From patchwork Mon Aug 31 23:31:45 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Troy Kisky X-Patchwork-Id: 44967 Received: from devils.ext.ti.com (devils.ext.ti.com [198.47.26.153]) by demeter.kernel.org (8.14.2/8.14.2) with ESMTP id n7VNXgF9019741 for ; Mon, 31 Aug 2009 23:33:42 GMT Received: from dlep35.itg.ti.com ([157.170.170.118]) by devils.ext.ti.com (8.13.7/8.13.7) with ESMTP id n7VNVoGX018635; Mon, 31 Aug 2009 18:31:55 -0500 Received: from linux.omap.com (localhost [127.0.0.1]) by dlep35.itg.ti.com (8.13.7/8.13.7) with ESMTP id n7VNVlgi002985; Mon, 31 Aug 2009 18:31:47 -0500 (CDT) Received: from linux.omap.com (localhost [127.0.0.1]) by linux.omap.com (Postfix) with ESMTP id 910A180626; Mon, 31 Aug 2009 18:31:47 -0500 (CDT) X-Original-To: davinci-linux-open-source@linux.davincidsp.com Delivered-To: davinci-linux-open-source@linux.davincidsp.com Received: from dflp53.itg.ti.com (dflp53.itg.ti.com [128.247.5.6]) by linux.omap.com (Postfix) with ESMTP id 65EDD80627 for ; Mon, 31 Aug 2009 18:31:46 -0500 (CDT) Received: from red.ext.ti.com (localhost [127.0.0.1]) by dflp53.itg.ti.com (8.13.8/8.13.8) with ESMTP id n7VNVjTn002506 for ; Mon, 31 Aug 2009 18:31:45 -0500 (CDT) Received: from mail127-tx2-R.bigfish.com (mail-tx2.bigfish.com [65.55.88.113]) by red.ext.ti.com (8.13.7/8.13.7) with ESMTP id n7VNVewj020452 for ; Mon, 31 Aug 2009 18:31:45 -0500 Received: from mail127-tx2 (localhost.localdomain [127.0.0.1]) by mail127-tx2-R.bigfish.com (Postfix) with ESMTP id A9BAB150822D for ; Mon, 31 Aug 2009 23:31:40 +0000 (UTC) X-SpamScore: -3 X-BigFish: vps-3(zz655Nc8kzz1202hzzz2dh247h64h) X-Spam-TCS-SCL: 3:0 X-MS-Exchange-Organization-Antispam-Report: OrigIP: 63.231.195.113; Service: EHS Received: by mail127-tx2 (MessageSwitch) id 1251761494739092_2266; Mon, 31 Aug 2009 23:31:34 +0000 (UCT) Received: from mpls-qmqp-02.inet.qwest.net (mpls-qmqp-02.inet.qwest.net [63.231.195.113]) by mail127-tx2.bigfish.com (Postfix) with ESMTP id 6952F1C0050 for ; Mon, 31 Aug 2009 23:31:34 +0000 (UTC) Received: from localhost (unknown [67.42.45.38]) by mpls-qmqp-02.inet.qwest.net (Postfix) with ESMTP id 3DDD653BD32; Mon, 31 Aug 2009 23:31:29 +0000 (UTC) Received: by localhost (Postfix, from userid 1002) id 301F75883EF; Mon, 31 Aug 2009 16:31:46 -0700 (MST) From: Troy Kisky To: davinci-linux-open-source@linux.davincidsp.com Date: Mon, 31 Aug 2009 16:31:45 -0700 Message-Id: <1251761505-11353-3-git-send-email-troy.kisky@boundarydevices.com> X-Mailer: git-send-email 1.5.6.3 In-Reply-To: <1251761505-11353-2-git-send-email-troy.kisky@boundarydevices.com> References: <1251761505-11353-1-git-send-email-troy.kisky@boundarydevices.com> <1251761505-11353-2-git-send-email-troy.kisky@boundarydevices.com> Cc: alsa-devel@alsa-project.org, broonie@sirena.org.uk Subject: [PATCH 3/3] ASoC: DaVinci: pcm, fix underrun by using sram X-BeenThere: davinci-linux-open-source@linux.davincidsp.com X-Mailman-Version: 2.1.4 Precedence: list List-Id: davinci-linux-open-source.linux.davincidsp.com List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: davinci-linux-open-source-bounces@linux.davincidsp.com Errors-To: davinci-linux-open-source-bounces@linux.davincidsp.com Fix underruns by using dma to copy 1st to sram in a ping/pong buffer style and then copying from the sram to the ASP. This also has the advantage of tolerating very long interrupt latency on dma completion. Signed-off-by: Troy Kisky --- arch/arm/mach-davinci/include/mach/asp.h | 2 + sound/soc/davinci/davinci-i2s.c | 5 +- sound/soc/davinci/davinci-pcm.c | 513 +++++++++++++++++++++++++++--- sound/soc/davinci/davinci-pcm.h | 1 + 4 files changed, 474 insertions(+), 47 deletions(-) diff --git a/arch/arm/mach-davinci/include/mach/asp.h b/arch/arm/mach-davinci/include/mach/asp.h index a3d2aa1..f0f3b27 100644 --- a/arch/arm/mach-davinci/include/mach/asp.h +++ b/arch/arm/mach-davinci/include/mach/asp.h @@ -57,6 +57,8 @@ struct snd_platform_data { * when compared to previous behavior. */ unsigned disable_channel_combine:1; + unsigned sram_size_playback; + unsigned sram_size_capture; /* McASP specific fields */ int tdm_slots; diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c index 081b2d4..863313f 100644 --- a/sound/soc/davinci/davinci-i2s.c +++ b/sound/soc/davinci/davinci-i2s.c @@ -559,8 +559,11 @@ static int davinci_i2s_probe(struct platform_device *pdev) ret = -ENOMEM; goto err_release_region; } - if (pdata) + if (pdata) { dev->disable_channel_combine = pdata->disable_channel_combine; + davinci_i2s_pcm_out.sram_size = pdata->sram_size_playback; + davinci_i2s_pcm_in.sram_size = pdata->sram_size_capture; + } dev->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(dev->clk)) { ret = -ENODEV; diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c index a96655c..2ce0a8e 100644 --- a/sound/soc/davinci/davinci-pcm.c +++ b/sound/soc/davinci/davinci-pcm.c @@ -3,6 +3,7 @@ * * Author: Vladimir Barinov, * Copyright: (C) 2007 MontaVista Software, Inc., + * added SRAM ping/pong (C) 2008 Troy Kisky * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -23,10 +24,51 @@ #include #include +#include #include "davinci-pcm.h" -static struct snd_pcm_hardware davinci_pcm_hardware = { +#ifdef DEBUG +static void print_buf_info(int lch, char *name) +{ + struct edmacc_param p; + if (lch < 0) + return; + edma_read_slot(lch, &p); + printk(KERN_DEBUG "%s: 0x%x, opt=%x, src=%x, a_b_cnt=%x dst=%x\n", + name, lch, p.opt, p.src, p.a_b_cnt, p.dst); + printk(KERN_DEBUG " src_dst_bidx=%x link_bcntrld=%x src_dst_cidx=%x ccnt=%x\n", + p.src_dst_bidx, p.link_bcntrld, p.src_dst_cidx, p.ccnt); +} +#else +static void print_buf_info(int lch, char *name) +{ +} +#endif + +static struct snd_pcm_hardware pcm_hardware_playback = { + .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE), + .formats = (SNDRV_PCM_FMTBIT_S16_LE), + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_KNOT), + .rate_min = 8000, + .rate_max = 96000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 128 * 1024, + .period_bytes_min = 32, + .period_bytes_max = 8 * 1024, + .periods_min = 16, + .periods_max = 255, + .fifo_size = 0, +}; + +static struct snd_pcm_hardware pcm_hardware_capture = { .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE), @@ -48,14 +90,58 @@ static struct snd_pcm_hardware davinci_pcm_hardware = { .fifo_size = 0, }; +/* + * How ping/pong works.... + * + * Playback: + * ram_params - copys 2*ping_size from start of SDRAM to iram, + * links to ram_link_lch2 + * ram_link_lch2 - copys rest of SDRAM to iram in ping_size units, + * links to ram_link_lch + * ram_link_lch - copys entire SDRAM to iram in ping_size uints, + * links to self + * + * asp_params - same as asp_link_lch[0] + * asp_link_lch[0] - copys from lower half of iram to asp port + * links to asp_link_lch[1], triggers iram copy event on completion + * asp_link_lch[1] - copys from upper half of iram to asp port + * links to asp_link_lch[0], triggers iram copy event on completion + * triggers interrupt only needed to let upper SOC levels update position + * in stream on completion + * + * When playback is started: + * ram_params started + * asp_params started + * + * Capture: + * ram_params - same as ram_link_lch, + * links to ram_link_lch + * ram_link_lch - same as playback + * links to self + * + * asp_params - same as playback + * asp_link_lch[0] - same as playback + * asp_link_lch[1] - same as playback + * + * When capture is started: + * asp_params started + */ struct davinci_runtime_data { spinlock_t lock; int period; /* current DMA period */ int asp_master_lch; /* Master DMA channel */ int asp_link_lch[2]; /* asp parameter link channel, ping/pong */ struct davinci_pcm_dma_params *params; /* DMA params */ + int ram_master_lch; + int ram_link_lch; + int ram_link_lch2; + struct edmacc_param asp_params; + struct edmacc_param ram_params; }; +/* + * Not used with ping/pong + */ static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream) { struct davinci_runtime_data *prtd = substream->runtime->private_data; @@ -109,48 +195,286 @@ static void davinci_pcm_dma_irq(unsigned lch, u16 ch_status, void *data) struct snd_pcm_substream *substream = data; struct davinci_runtime_data *prtd = substream->runtime->private_data; + print_buf_info(prtd->ram_master_lch, "i ram_master_lch"); pr_debug("davinci_pcm: lch=%d, status=0x%x\n", lch, ch_status); if (unlikely(ch_status != DMA_COMPLETE)) return; if (snd_pcm_running(substream)) { + if (prtd->ram_master_lch < 0) { + /* No ping/pong must fix up link dma data*/ + spin_lock(&prtd->lock); + davinci_pcm_enqueue_dma(substream); + spin_unlock(&prtd->lock); + } snd_pcm_period_elapsed(substream); + } +} - spin_lock(&prtd->lock); - davinci_pcm_enqueue_dma(substream); - spin_unlock(&prtd->lock); +static int allocate_sram(struct snd_pcm_substream *substream, unsigned size, + struct snd_pcm_hardware *ppcm) +{ + struct snd_dma_buffer *buf = &substream->dma_buffer; + struct snd_dma_buffer *iram_dma = NULL; + dma_addr_t iram_phys = 0; + void *iram_virt = NULL; + + if (buf->private_data || !size) + return 0; + + ppcm->period_bytes_max = size; + iram_virt = sram_alloc(size, &iram_phys); + if (!iram_virt) + goto exit1; + iram_dma = kzalloc(sizeof(*iram_dma), GFP_KERNEL); + if (!iram_dma) + goto exit2; + iram_dma->area = iram_virt; + iram_dma->addr = iram_phys; + memset(iram_dma->area, 0, size); + iram_dma->bytes = size; + buf->private_data = iram_dma; + return 0; +exit2: + if (iram_virt) + sram_free(iram_virt, size); +exit1: + return -ENOMEM; +} + +/* + * Only used with ping/pong. + * This is called after runtime->dma_addr, period_bytes and data_type are valid + */ +static int ping_pong_dma_setup(struct snd_pcm_substream *substream) +{ + unsigned short ram_src_cidx, ram_dst_cidx; + struct snd_pcm_runtime *runtime = substream->runtime; + struct davinci_runtime_data *prtd = runtime->private_data; + struct snd_dma_buffer *iram_dma = + (struct snd_dma_buffer *)substream->dma_buffer.private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct davinci_pcm_dma_params *dma_data = rtd->dai->cpu_dai->dma_data; + unsigned int data_type = dma_data->data_type; + unsigned int acnt = dma_data->acnt; + /* divide by 2 for ping/pong */ + unsigned int ping_size = snd_pcm_lib_period_bytes(substream) >> 1; + int lch = prtd->asp_link_lch[1]; + if ((data_type == 0) || (data_type > 4)) { + printk(KERN_ERR "%s: data_type=%i\n", __func__, data_type); + return -EINVAL; } + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + dma_addr_t asp_src_pong = iram_dma->addr + ping_size; + ram_src_cidx = ping_size; + ram_dst_cidx = -ping_size; + edma_set_src(lch, asp_src_pong, INCR, W8BIT); + + lch = prtd->asp_link_lch[0]; + edma_set_src_index(lch, data_type, 0); + lch = prtd->asp_link_lch[1]; + edma_set_src_index(lch, data_type, 0); + + lch = prtd->ram_link_lch; + edma_set_src(lch, runtime->dma_addr, INCR, W32BIT); + } else { + dma_addr_t asp_dst_pong = iram_dma->addr + ping_size; + ram_src_cidx = -ping_size; + ram_dst_cidx = ping_size; + edma_set_dest(lch, asp_dst_pong, INCR, W8BIT); + + lch = prtd->asp_link_lch[0]; + edma_set_dest_index(lch, data_type, 0); + lch = prtd->asp_link_lch[1]; + edma_set_dest_index(lch, data_type, 0); + + lch = prtd->ram_link_lch; + edma_set_dest(lch, runtime->dma_addr, INCR, W32BIT); + } + + lch = prtd->asp_link_lch[0]; + edma_set_transfer_params(lch, acnt, + ping_size/data_type, 1, 0, ASYNC); + lch = prtd->asp_link_lch[1]; + edma_set_transfer_params(lch, acnt, + ping_size/data_type, 1, 0, ASYNC); + + + lch = prtd->ram_link_lch; + edma_set_src_index(lch, ping_size, ram_src_cidx); + edma_set_dest_index(lch, ping_size, ram_dst_cidx); + edma_set_transfer_params(lch, ping_size, 2, + runtime->periods, 2, ASYNC); + + /* init master params */ + edma_read_slot(prtd->asp_link_lch[0], &prtd->asp_params); + edma_read_slot(prtd->ram_link_lch, &prtd->ram_params); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + struct edmacc_param p_ram; + /* Copy entire iram buffer before playback started */ + prtd->ram_params.a_b_cnt = (1 << 16) | (ping_size << 1); + /* 0 dst_bidx */ + prtd->ram_params.src_dst_bidx = (ping_size << 1); + /* 0 dst_cidx */ + prtd->ram_params.src_dst_cidx = (ping_size << 1); + prtd->ram_params.ccnt = 1; + + /* Skip 1st period */ + edma_read_slot(prtd->ram_link_lch, &p_ram); + p_ram.src += (ping_size << 1); + p_ram.ccnt -= 1; + edma_write_slot(prtd->ram_link_lch2, &p_ram); + /* + * When 1st started, ram -> iram dma channel will fill the + * entire iram. Then, whenever a ping/pong asp buffer finishes, + * 1/2 iram will be filled. + */ + prtd->ram_params.link_bcntrld = + EDMA_CHAN_SLOT(prtd->ram_link_lch2) << 5; + } + return 0; +} + +/* 1 asp tx or rx channel using 2 parameter channels + * 1 ram to/from iram channel using 1 parameter channel + * + * Playback + * ram copy channel kicks off first, + * 1st ram copy of entire iram buffer completion kicks off asp channel + * asp tcc always kicks off ram copy of 1/2 iram buffer + * + * Record + * asp channel starts, tcc kicks off ram copy + */ +static int request_ping_pong(struct snd_pcm_substream *substream, + struct davinci_runtime_data *prtd, + struct snd_dma_buffer *iram_dma) +{ + dma_addr_t asp_src_ping; + dma_addr_t asp_dst_ping; + int lch; + struct davinci_pcm_dma_params *dma_data = prtd->params; + + /* Request ram master channel */ + lch = prtd->ram_master_lch = edma_alloc_channel(EDMA_CHANNEL_ANY, + davinci_pcm_dma_irq, substream, + EVENTQ_1); + if (lch < 0) + goto exit1; + + /* Request ram link channel */ + lch = prtd->ram_link_lch = edma_alloc_slot( + EDMA_CTLR(prtd->ram_master_lch), EDMA_SLOT_ANY); + if (lch < 0) + goto exit2; + + lch = prtd->asp_link_lch[1] = edma_alloc_slot( + EDMA_CTLR(prtd->asp_master_lch), EDMA_SLOT_ANY); + if (lch < 0) + goto exit3; + + prtd->ram_link_lch2 = -1; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + lch = prtd->ram_link_lch2 = edma_alloc_slot( + EDMA_CTLR(prtd->ram_master_lch), EDMA_SLOT_ANY); + if (lch < 0) + goto exit4; + } + /* circle ping-pong buffers */ + edma_link(prtd->asp_link_lch[0], prtd->asp_link_lch[1]); + edma_link(prtd->asp_link_lch[1], prtd->asp_link_lch[0]); + /* circle ram buffers */ + edma_link(prtd->ram_link_lch, prtd->ram_link_lch); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + asp_src_ping = iram_dma->addr; + asp_dst_ping = dma_data->dma_addr; /* fifo */ + } else { + asp_src_ping = dma_data->dma_addr; /* fifo */ + asp_dst_ping = iram_dma->addr; + } + /* ping */ + lch = prtd->asp_link_lch[0]; + edma_set_src(lch, asp_src_ping, INCR, W16BIT); + edma_set_dest(lch, asp_dst_ping, INCR, W16BIT); + edma_set_src_index(lch, 0, 0); + edma_set_dest_index(lch, 0, 0); + + edma_read_slot(lch, &prtd->asp_params); + prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f) | TCINTEN); + prtd->asp_params.opt |= TCCHEN | EDMA_TCC(prtd->ram_master_lch & 0x3f); + edma_write_slot(lch, &prtd->asp_params); + + /* pong */ + lch = prtd->asp_link_lch[1]; + edma_set_src(lch, asp_src_ping, INCR, W16BIT); + edma_set_dest(lch, asp_dst_ping, INCR, W16BIT); + edma_set_src_index(lch, 0, 0); + edma_set_dest_index(lch, 0, 0); + + edma_read_slot(lch, &prtd->asp_params); + prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f)); + /* interrupt after every pong completion */ + prtd->asp_params.opt |= TCINTEN | TCCHEN | + EDMA_TCC(EDMA_CHAN_SLOT(prtd->ram_master_lch)); + edma_write_slot(lch, &prtd->asp_params); + + /* ram */ + lch = prtd->ram_link_lch; + edma_set_src(lch, iram_dma->addr, INCR, W32BIT); + edma_set_dest(lch, iram_dma->addr, INCR, W32BIT); + pr_debug("%s: audio dma channels/slots in use for ram:%u %u %u," + "for asp:%u %u %u\n", __func__, + prtd->ram_master_lch, prtd->ram_link_lch, prtd->ram_link_lch2, + prtd->asp_master_lch, prtd->asp_link_lch[0], + prtd->asp_link_lch[1]); + return 0; +exit4: + edma_free_channel(prtd->asp_link_lch[1]); + prtd->asp_link_lch[1] = -1; +exit3: + edma_free_channel(prtd->ram_link_lch); + prtd->ram_link_lch = -1; +exit2: + edma_free_channel(prtd->ram_master_lch); + prtd->ram_master_lch = -1; +exit1: + return lch; } static int davinci_pcm_dma_request(struct snd_pcm_substream *substream) { + struct snd_dma_buffer *iram_dma; struct davinci_runtime_data *prtd = substream->runtime->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct davinci_pcm_dma_params *dma_data = rtd->dai->cpu_dai->dma_data; - struct edmacc_param p_ram; - int ret; + int lch; if (!dma_data) return -ENODEV; prtd->params = dma_data; - /* Request master DMA channel */ - ret = edma_alloc_channel(prtd->params->channel, - davinci_pcm_dma_irq, substream, - EVENTQ_0); - if (ret < 0) - return ret; - prtd->asp_master_lch = ret; - - /* Request parameter RAM reload slot */ - ret = edma_alloc_slot(EDMA_CTLR(prtd->asp_master_lch), EDMA_SLOT_ANY); - if (ret < 0) { - edma_free_channel(prtd->asp_master_lch); - return ret; + /* Request asp master DMA channel */ + lch = prtd->asp_master_lch = edma_alloc_channel(dma_data->channel, + davinci_pcm_dma_irq, substream, EVENTQ_0); + if (lch < 0) + goto exit1; + + /* Request asp link channels */ + lch = prtd->asp_link_lch[0] = edma_alloc_slot( + EDMA_CTLR(prtd->asp_master_lch), EDMA_SLOT_ANY); + if (lch < 0) + goto exit2; + + iram_dma = (struct snd_dma_buffer *)substream->dma_buffer.private_data; + if (iram_dma) { + if (request_ping_pong(substream, prtd, iram_dma) == 0) + return 0; + printk(KERN_WARNING "%s: dma channel allocation failed," + "not using sram\n", __func__); } - prtd->asp_link_lch[0] = ret; /* Issue transfer completion IRQ when the channel completes a * transfer, then always reload from the same slot (by a kind @@ -161,12 +485,17 @@ static int davinci_pcm_dma_request(struct snd_pcm_substream *substream) * the buffer and its length (ccnt) ... use it as a template * so davinci_pcm_enqueue_dma() takes less time in IRQ. */ - edma_read_slot(prtd->asp_link_lch[0], &p_ram); - p_ram.opt |= TCINTEN | EDMA_TCC(EDMA_CHAN_SLOT(prtd->asp_master_lch)); - p_ram.link_bcntrld = EDMA_CHAN_SLOT(prtd->asp_link_lch[0]) << 5; - edma_write_slot(prtd->asp_link_lch[0], &p_ram); - + edma_read_slot(lch, &prtd->asp_params); + prtd->asp_params.opt |= TCINTEN | + EDMA_TCC(EDMA_CHAN_SLOT(prtd->asp_master_lch)); + prtd->asp_params.link_bcntrld = EDMA_CHAN_SLOT(lch) << 5; + edma_write_slot(lch, &prtd->asp_params); return 0; +exit2: + edma_free_channel(prtd->asp_master_lch); + prtd->asp_master_lch = -1; +exit1: + return lch; } static int davinci_pcm_trigger(struct snd_pcm_substream *substream, int cmd) @@ -180,12 +509,12 @@ static int davinci_pcm_trigger(struct snd_pcm_substream *substream, int cmd) case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - edma_start(prtd->asp_master_lch); + edma_resume(prtd->asp_master_lch); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - edma_stop(prtd->asp_master_lch); + edma_pause(prtd->asp_master_lch); break; default: ret = -EINVAL; @@ -200,14 +529,35 @@ static int davinci_pcm_trigger(struct snd_pcm_substream *substream, int cmd) static int davinci_pcm_prepare(struct snd_pcm_substream *substream) { struct davinci_runtime_data *prtd = substream->runtime->private_data; - struct edmacc_param temp; + if (prtd->ram_master_lch >= 0) { + int ret = ping_pong_dma_setup(substream); + if (ret < 0) + return ret; + + edma_write_slot(prtd->ram_master_lch, &prtd->ram_params); + edma_write_slot(prtd->asp_master_lch, &prtd->asp_params); + + print_buf_info(prtd->ram_master_lch, "ram_master_lch"); + print_buf_info(prtd->ram_link_lch, "ram_link_lch"); + print_buf_info(prtd->ram_link_lch2, "ram_link_lch2"); + print_buf_info(prtd->asp_master_lch, "asp_master_lch"); + print_buf_info(prtd->asp_link_lch[0], "asp_link_lch[0]"); + print_buf_info(prtd->asp_link_lch[1], "asp_link_lch[1]"); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + /* copy 1st iram buffer */ + edma_start(prtd->ram_master_lch); + } + edma_start(prtd->asp_master_lch); + return 0; + } prtd->period = 0; davinci_pcm_enqueue_dma(substream); /* Copy self-linked parameter RAM entry into master channel */ - edma_read_slot(prtd->asp_link_lch[0], &temp); - edma_write_slot(prtd->asp_master_lch, &temp); + edma_read_slot(prtd->asp_link_lch[0], &prtd->asp_params); + edma_write_slot(prtd->asp_master_lch, &prtd->asp_params); davinci_pcm_enqueue_dma(substream); return 0; @@ -223,13 +573,46 @@ davinci_pcm_pointer(struct snd_pcm_substream *substream) dma_addr_t asp_src, asp_dst; spin_lock(&prtd->lock); - - edma_get_position(prtd->asp_master_lch, &asp_src, &asp_dst); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - asp_count = asp_src - runtime->dma_addr; - else - asp_count = asp_dst - runtime->dma_addr; - + if (prtd->ram_master_lch >= 0) { + int ram_count; + int mod_ram; + dma_addr_t ram_src, ram_dst; + unsigned int period_size = snd_pcm_lib_period_bytes(substream); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + /* reading ram before asp should be safe + * as long as the asp transfers less than a ping size + * of bytes between the 2 reads + */ + edma_get_position(prtd->ram_master_lch, + &ram_src, &ram_dst); + edma_get_position(prtd->asp_master_lch, + &asp_src, &asp_dst); + asp_count = asp_src - prtd->asp_params.src; + ram_count = ram_src - prtd->ram_params.src; + mod_ram = ram_count % period_size; + mod_ram -= asp_count; + if (mod_ram < 0) + mod_ram += period_size; + else if (mod_ram == 0) { + if (snd_pcm_running(substream)) + mod_ram += period_size; + } + ram_count -= mod_ram; + if (ram_count < 0) + ram_count += period_size * runtime->periods; + } else { + edma_get_position(prtd->ram_master_lch, + &ram_src, &ram_dst); + ram_count = ram_dst - prtd->ram_params.dst; + } + asp_count = ram_count; + } else { + edma_get_position(prtd->asp_master_lch, &asp_src, &asp_dst); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + asp_count = asp_src - runtime->dma_addr; + else + asp_count = asp_dst - runtime->dma_addr; + } spin_unlock(&prtd->lock); offset = bytes_to_frames(runtime, asp_count); @@ -241,11 +624,17 @@ davinci_pcm_pointer(struct snd_pcm_substream *substream) static int davinci_pcm_open(struct snd_pcm_substream *substream) { + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct davinci_pcm_dma_params *dma_data = rtd->dai->cpu_dai->dma_data; struct snd_pcm_runtime *runtime = substream->runtime; struct davinci_runtime_data *prtd; + struct snd_pcm_hardware *ppcm; int ret = 0; - snd_soc_set_runtime_hwparams(substream, &davinci_pcm_hardware); + ppcm = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + &pcm_hardware_playback : &pcm_hardware_capture; + allocate_sram(substream, dma_data->sram_size, ppcm); + snd_soc_set_runtime_hwparams(substream, ppcm); /* ensure that buffer size is a multiple of period size */ ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); @@ -257,6 +646,11 @@ static int davinci_pcm_open(struct snd_pcm_substream *substream) return -ENOMEM; spin_lock_init(&prtd->lock); + prtd->asp_master_lch = -1; + prtd->asp_link_lch[0] = prtd->asp_link_lch[1] = -1; + prtd->ram_master_lch = -1; + prtd->ram_link_lch = -1; + prtd->ram_link_lch2 = -1; runtime->private_data = prtd; @@ -274,10 +668,29 @@ static int davinci_pcm_close(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct davinci_runtime_data *prtd = runtime->private_data; - edma_unlink(prtd->asp_link_lch[0]); - - edma_free_slot(prtd->asp_link_lch[0]); - edma_free_channel(prtd->asp_master_lch); + if (prtd->ram_master_lch >= 0) + edma_stop(prtd->ram_master_lch); + if (prtd->asp_master_lch >= 0) + edma_stop(prtd->asp_master_lch); + if (prtd->asp_link_lch[0] >= 0) + edma_unlink(prtd->asp_link_lch[0]); + if (prtd->asp_link_lch[1] >= 0) + edma_unlink(prtd->asp_link_lch[1]); + if (prtd->ram_link_lch >= 0) + edma_unlink(prtd->ram_link_lch); + + if (prtd->asp_link_lch[0] >= 0) + edma_free_slot(prtd->asp_link_lch[0]); + if (prtd->asp_link_lch[1] >= 0) + edma_free_slot(prtd->asp_link_lch[1]); + if (prtd->asp_master_lch >= 0) + edma_free_channel(prtd->asp_master_lch); + if (prtd->ram_link_lch >= 0) + edma_free_slot(prtd->ram_link_lch); + if (prtd->ram_link_lch2 >= 0) + edma_free_slot(prtd->ram_link_lch2); + if (prtd->ram_master_lch >= 0) + edma_free_channel(prtd->ram_master_lch); kfree(prtd); @@ -319,11 +732,11 @@ static struct snd_pcm_ops davinci_pcm_ops = { .mmap = davinci_pcm_mmap, }; -static int davinci_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) +static int davinci_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream, + size_t size) { struct snd_pcm_substream *substream = pcm->streams[stream].substream; struct snd_dma_buffer *buf = &substream->dma_buffer; - size_t size = davinci_pcm_hardware.buffer_bytes_max; buf->dev.type = SNDRV_DMA_TYPE_DEV; buf->dev.dev = pcm->card->dev; @@ -348,6 +761,7 @@ static void davinci_pcm_free(struct snd_pcm *pcm) int stream; for (stream = 0; stream < 2; stream++) { + struct snd_dma_buffer *iram_dma; substream = pcm->streams[stream].substream; if (!substream) continue; @@ -359,6 +773,11 @@ static void davinci_pcm_free(struct snd_pcm *pcm) dma_free_writecombine(pcm->card->dev, buf->bytes, buf->area, buf->addr); buf->area = NULL; + iram_dma = (struct snd_dma_buffer *)buf->private_data; + if (iram_dma) { + sram_free(iram_dma->area, iram_dma->bytes); + kfree(iram_dma); + } } } @@ -376,14 +795,16 @@ static int davinci_pcm_new(struct snd_card *card, if (dai->playback.channels_min) { ret = davinci_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_PLAYBACK); + SNDRV_PCM_STREAM_PLAYBACK, + pcm_hardware_playback.buffer_bytes_max); if (ret) return ret; } if (dai->capture.channels_min) { ret = davinci_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_CAPTURE); + SNDRV_PCM_STREAM_CAPTURE, + pcm_hardware_capture.buffer_bytes_max); if (ret) return ret; } diff --git a/sound/soc/davinci/davinci-pcm.h b/sound/soc/davinci/davinci-pcm.h index 63d9625..7caba3c 100644 --- a/sound/soc/davinci/davinci-pcm.h +++ b/sound/soc/davinci/davinci-pcm.h @@ -21,6 +21,7 @@ struct davinci_pcm_dma_params { int channel; /* sync dma channel ID */ unsigned short acnt; dma_addr_t dma_addr; /* device physical address for DMA */ + unsigned sram_size; enum dma_event_q eventq_no; /* event queue number */ unsigned char data_type; /* xfer data type */ unsigned char convert_mono_stereo;