From patchwork Mon Sep 3 16:59:53 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Russell King X-Patchwork-Id: 1400621 Return-Path: X-Original-To: patchwork-linux-omap@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by patchwork1.kernel.org (Postfix) with ESMTP id E03A4402E1 for ; Mon, 3 Sep 2012 17:00:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932369Ab2ICRAD (ORCPT ); Mon, 3 Sep 2012 13:00:03 -0400 Received: from caramon.arm.linux.org.uk ([78.32.30.218]:51006 "EHLO caramon.arm.linux.org.uk" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932362Ab2ICQ77 (ORCPT ); Mon, 3 Sep 2012 12:59:59 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=arm.linux.org.uk; s=caramon; h=Date:Sender:Message-Id:Subject:Cc:Cc:To:From:References:In-Reply-To; bh=tgFftMwCAt7/8BhYtEwrUtCChViPp5FWPorSIi1r3jI=; b=cdfNOl3gCaYkaK8yETSshfsZlS3fSMQ+cCGi/dGCn0T55iWptUUb2MtVxPqQajvCO8jNMDgC3M3G59om9XpBsIllzdGQZc3szww+btMrNschZ+dsreW3eES+eni2PhkPLqpdwISvwsh4t0Gtz8PEw5FJ3hrd8bV352kxtLlbLX0=; Received: from e0022681537dd.dyn.arm.linux.org.uk ([2002:4e20:1eda:1:222:68ff:fe15:37dd]:38495 helo=rmk-PC.arm.linux.org.uk) by caramon.arm.linux.org.uk with esmtpsa (TLSv1:AES256-SHA:256) (Exim 4.76) (envelope-from ) id 1T8a0A-0007ac-0I; Mon, 03 Sep 2012 17:59:54 +0100 Received: from rmk by rmk-PC.arm.linux.org.uk with local (Exim 4.76) (envelope-from ) id 1T8a09-0002vz-Bl; Mon, 03 Sep 2012 17:59:53 +0100 In-Reply-To: <20120903165832.GA31511@n2100.arm.linux.org.uk> References: <20120903165832.GA31511@n2100.arm.linux.org.uk> From: Russell King To: linux-arm-kernel@lists.infradead.org, linux-omap@vger.kernel.org Cc: Santosh Shilimkar Cc: Peter Ujfalusi , Jarkko Nikula , Liam Girdwood , Mark Brown , Jaroslav Kysela , Takashi Iwai , alsa-devel@alsa-project.org Subject: [RFC 3/3] ASoC: first stab at converting OMAP PCM driver to use dmaengine Message-Id: Date: Mon, 03 Sep 2012 17:59:53 +0100 Sender: linux-omap-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org Note that certain features of the original driver are not supported: 1. Non-packet mode sync 2. No period wakeup mode DMA engine has no way to communicate this information through standard channels. 3. Pause 4. Resume OMAP DMA engine backend does not support pausing and resuming an in-progress transfer. It is unclear from the specs what effect clearing the enable bit has on the DMA position of a destination synchronized transfer, and whether the transfer can be restarted from the exact point that it was paused (or whether the data in the FIFO read from memory is simply discarded.) Signed-off-by: Russell King Tested-by: Janusz Krzysztofik --- sound/soc/omap/Kconfig | 1 + sound/soc/omap/omap-pcm.c | 173 ++++++++++++-------------------------------- 2 files changed, 48 insertions(+), 126 deletions(-) diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index 57a2fa7..8a92d5b 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig @@ -1,6 +1,7 @@ config SND_OMAP_SOC tristate "SoC Audio for the Texas Instruments OMAP chips" depends on ARCH_OMAP + select SND_SOC_DMAENGINE_PCM config SND_OMAP_SOC_DMIC tristate diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c index f0feb06..50ae048 100644 --- a/sound/soc/omap/omap-pcm.c +++ b/sound/soc/omap/omap-pcm.c @@ -25,21 +25,23 @@ #include #include #include +#include #include +#include #include #include #include -#include +#include /* needed just for OMAP_DMA_SYNC_PACKET */ #include "omap-pcm.h" static const struct snd_pcm_hardware omap_pcm_hardware = { .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_INTERLEAVED /* | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | - SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP */, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, .period_bytes_min = 32, @@ -50,51 +52,9 @@ static const struct snd_pcm_hardware omap_pcm_hardware = { }; struct omap_runtime_data { - spinlock_t lock; struct omap_pcm_dma_data *dma_data; - int dma_ch; - int period_index; }; -static void omap_pcm_dma_irq(int ch, u16 stat, void *data) -{ - struct snd_pcm_substream *substream = data; - struct snd_pcm_runtime *runtime = substream->runtime; - struct omap_runtime_data *prtd = runtime->private_data; - unsigned long flags; - - if ((cpu_is_omap1510())) { - /* - * OMAP1510 doesn't fully support DMA progress counter - * and there is no software emulation implemented yet, - * so have to maintain our own progress counters - * that can be used by omap_pcm_pointer() instead. - */ - spin_lock_irqsave(&prtd->lock, flags); - if ((stat == OMAP_DMA_LAST_IRQ) && - (prtd->period_index == runtime->periods - 1)) { - /* we are in sync, do nothing */ - spin_unlock_irqrestore(&prtd->lock, flags); - return; - } - if (prtd->period_index >= 0) { - if (stat & OMAP_DMA_BLOCK_IRQ) { - /* end of buffer reached, loop back */ - prtd->period_index = 0; - } else if (stat & OMAP_DMA_LAST_IRQ) { - /* update the counter for the last period */ - prtd->period_index = runtime->periods - 1; - } else if (++prtd->period_index >= runtime->periods) { - /* end of buffer missed? loop back */ - prtd->period_index = 0; - } - } - spin_unlock_irqrestore(&prtd->lock, flags); - } - - snd_pcm_period_elapsed(substream); -} - /* this may get called several times by oss emulation */ static int omap_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) @@ -103,6 +63,9 @@ static int omap_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct omap_runtime_data *prtd = runtime->private_data; struct omap_pcm_dma_data *dma_data; + struct dma_slave_config config; + struct dma_chan *chan; + unsigned req; int err = 0; @@ -119,17 +82,36 @@ static int omap_pcm_hw_params(struct snd_pcm_substream *substream, if (prtd->dma_data) return 0; prtd->dma_data = dma_data; - err = omap_request_dma(dma_data->dma_req, dma_data->name, - omap_pcm_dma_irq, substream, &prtd->dma_ch); - if (!err) { - /* - * Link channel with itself so DMA doesn't need any - * reprogramming while looping the buffer - */ - omap_dma_link_lch(prtd->dma_ch, prtd->dma_ch); + + /* + * This is the only parameter we don't handle with DMA + * engine - so we insist on OMAP_DMA_SYNC_PACKET here. + */ + if (dma_data->sync_mode != OMAP_DMA_SYNC_PACKET) { + pr_warn("ALSA: omap-dma: DMA using non-packet mode?\n"); + return -EINVAL; } - return err; + req = dma_data->dma_req; + err = snd_dmaengine_pcm_open(substream, omap_dma_filter_fn, &req); + if (err) + return err; + + chan = snd_dmaengine_pcm_get_chan(substream); + if (!chan) + return -EINVAL; + + /* fills in addr_width and direction */ + err = snd_hwparams_to_dma_slave_config(substream, params, &config); + if (err) + return err; + + config.src_addr = dma_data->port_addr; + config.dst_addr = dma_data->port_addr; + config.src_maxburst = dma_data->packet_size; + config.dst_maxburst = dma_data->packet_size; + + return dmaengine_slave_config(chan, &config); } static int omap_pcm_hw_free(struct snd_pcm_substream *substream) @@ -140,8 +122,6 @@ static int omap_pcm_hw_free(struct snd_pcm_substream *substream) if (prtd->dma_data == NULL) return 0; - omap_dma_unlink_lch(prtd->dma_ch, prtd->dma_ch); - omap_free_dma(prtd->dma_ch); prtd->dma_data = NULL; snd_pcm_set_runtime_buffer(substream, NULL); @@ -151,48 +131,12 @@ static int omap_pcm_hw_free(struct snd_pcm_substream *substream) static int omap_pcm_prepare(struct snd_pcm_substream *substream) { - struct snd_pcm_runtime *runtime = substream->runtime; - struct omap_runtime_data *prtd = runtime->private_data; - struct omap_pcm_dma_data *dma_data = prtd->dma_data; - struct omap_dma_channel_params dma_params; - int bytes; - - /* return if this is a bufferless transfer e.g. - * codec <--> BT codec or GSM modem -- lg FIXME */ - if (!prtd->dma_data) - return 0; - - memset(&dma_params, 0, sizeof(dma_params)); - dma_params.data_type = dma_data->data_type; - dma_params.trigger = dma_data->dma_req; - dma_params.sync_mode = dma_data->sync_mode; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - dma_params.src_amode = OMAP_DMA_AMODE_POST_INC; - dma_params.dst_amode = OMAP_DMA_AMODE_CONSTANT; - dma_params.src_or_dst_synch = OMAP_DMA_DST_SYNC; - dma_params.src_start = runtime->dma_addr; - dma_params.dst_start = dma_data->port_addr; - dma_params.dst_port = OMAP_DMA_PORT_MPUI; - dma_params.dst_fi = dma_data->packet_size; - } else { - dma_params.src_amode = OMAP_DMA_AMODE_CONSTANT; - dma_params.dst_amode = OMAP_DMA_AMODE_POST_INC; - dma_params.src_or_dst_synch = OMAP_DMA_SRC_SYNC; - dma_params.src_start = dma_data->port_addr; - dma_params.dst_start = runtime->dma_addr; - dma_params.src_port = OMAP_DMA_PORT_MPUI; - dma_params.src_fi = dma_data->packet_size; - } +#if 0 /* - * Set DMA transfer frame size equal to ALSA period size and frame - * count as no. of ALSA periods. Then with DMA frame interrupt enabled, - * we can transfer the whole ALSA buffer with single DMA transfer but - * still can get an interrupt at each period bounary + * These remains serve to document the bits which + * DMA engine doesn't handle. */ - bytes = snd_pcm_lib_period_bytes(substream); - dma_params.elem_count = bytes >> dma_data->data_type; - dma_params.frame_count = runtime->periods; - omap_set_dma_params(prtd->dma_ch, &dma_params); + dma_params.sync_mode = dma_data->sync_mode; if ((cpu_is_omap1510())) omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ | @@ -207,14 +151,7 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream) */ omap_disable_dma_irq(prtd->dma_ch, OMAP_DMA_BLOCK_IRQ); } - - if (!(cpu_class_is_omap1())) { - omap_set_dma_src_burst_mode(prtd->dma_ch, - OMAP_DMA_DATA_BURST_16); - omap_set_dma_dest_burst_mode(prtd->dma_ch, - OMAP_DMA_DATA_BURST_16); - } - +#endif return 0; } @@ -223,56 +160,41 @@ static int omap_pcm_trigger(struct snd_pcm_substream *substream, int cmd) struct snd_pcm_runtime *runtime = substream->runtime; struct omap_runtime_data *prtd = runtime->private_data; struct omap_pcm_dma_data *dma_data = prtd->dma_data; - unsigned long flags; int ret = 0; - spin_lock_irqsave(&prtd->lock, flags); switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - prtd->period_index = 0; /* Configure McBSP internal buffer usage */ if (dma_data->set_threshold) dma_data->set_threshold(substream); - - omap_start_dma(prtd->dma_ch); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - prtd->period_index = -1; - omap_stop_dma(prtd->dma_ch); break; default: ret = -EINVAL; } - spin_unlock_irqrestore(&prtd->lock, flags); + + if (ret == 0) + ret = snd_dmaengine_pcm_trigger(substream, cmd); return ret; } static snd_pcm_uframes_t omap_pcm_pointer(struct snd_pcm_substream *substream) { - struct snd_pcm_runtime *runtime = substream->runtime; - struct omap_runtime_data *prtd = runtime->private_data; - dma_addr_t ptr; snd_pcm_uframes_t offset; if (cpu_is_omap1510()) { - offset = prtd->period_index * runtime->period_size; - } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - ptr = omap_get_dma_dst_pos(prtd->dma_ch); - offset = bytes_to_frames(runtime, ptr - runtime->dma_addr); + offset = snd_dmaengine_pcm_pointer_no_residue(substream); } else { - ptr = omap_get_dma_src_pos(prtd->dma_ch); - offset = bytes_to_frames(runtime, ptr - runtime->dma_addr); + offset = snd_dmaengine_pcm_pointer(substream); } - if (offset >= runtime->buffer_size) - offset = 0; - return offset; } @@ -295,7 +217,6 @@ static int omap_pcm_open(struct snd_pcm_substream *substream) ret = -ENOMEM; goto out; } - spin_lock_init(&prtd->lock); runtime->private_data = prtd; out: @@ -307,7 +228,7 @@ static int omap_pcm_close(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; kfree(runtime->private_data); - return 0; + return snd_dmaengine_pcm_close(substream); } static int omap_pcm_mmap(struct snd_pcm_substream *substream,