[alsa-devel] [PATCH v3 00/15] ASoC: OMAP: Convert to use dmaengine
Hello,
Changes since v2: - As it has been discussed the no_wakeup parameter has been replaced with flags for the dmaengine APIs
Changes since v1: - Support for pause/resume for OMAP audio via dmaengine - dmaengine: support for NO_PERIOD_WAKEUP in cyclic mode - OMAP to keep supporting NO_PERIOD_WAKEUP for audio - Other plaforms can also try to enable this mode since we have now generic interface to do so.
This series will switch the OMAP audio to use dmaengine. The final patch which does the switch was based on Russell King's earlier patch.
The first 10 patch is to prepare the OMAP audio drivers for a smooth change to dmaengine: - sDMA FRAME sync mode is removed and replaced with PACKET mode - dai drivers no longer need to configure sDMA sync mode - dai drivers does not need to specify the DMA word length - with the exception of the omap-hdmi driver which requires 32bit word length regardless of the audio format in use - the McPDM driver used (to my surprise) hackish way of getting the DMA channel and address - via defines from some header files
After the conversion OMAP audio support should have the same features as before, no regressions expected.
I have tested the series on: - BeagleBoard (audio via McBSP): - aplay/arecord. In element mode and in threshold mode with different period sizes - mplayer -ao alsa: for direct ALSA access - mplayer -ao pulse: via PulseAudio to test NO_PERIOD_WAKEUP feature - OMAP4 Blaze (audio via McPDM and DMIC) - aplay/arecord - mplayer -ao alsa: for direct ALSA access - mplayer -ao pulse: via PulseAudio to test NO_PERIOD_WAKEUP feature
The patches has been generated against: git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-3.7
Janusz: Can you retest this series on OMAP1 to be sure I have not broken it? Ricardo: Can you test the omap-hmdi if it is still working?
Regards, Peter --- Peter Ujfalusi (15): dmaengine: omap: Support for element mode in cyclic DMA dmaengine: omap: Add support for pause/resume in cyclic dma mode dmaengine: Add flags parameter to dmaengine_prep_dma_cyclic() dmaengine: Pass flags via device_prep_dma_cyclic() callback dmaengine: omap-dma: Add support to suppress interrupts in cyclic mode ASoC: omap-mcbsp: Use sDMA packet mode instead of frame mode ASoC: omap-pcm: Select sDMA synchronization based on packet_size ASoC: OMAP: Remove sync_mode from omap_pcm_dma_data struct ASoC: omap-pcm: Prepare to configure the DMA data_type based on stream properties ARM: OMAP4: hwmod_data: Add resource names to McPDM memory ranges ASoC: omap-mcpdm: Use platform_get_resource_* to get resources ASoC: OMAP: mcbsp, mcpdm, dmic: Let omap-pcm to pick the dma_type ASoC: omap-pcm, omap-dmic: Change the use of omap_pcm_dma_data->data_type ASoC: OMAP: mcbsp, mcpdm, dmic, hdmi: Set dma_data at startup time ASoC: omap-pcm: Convert to use dmaengine
arch/arm/mach-omap2/omap_hwmod_44xx_data.c | 2 + drivers/dma/at_hdmac.c | 3 +- drivers/dma/ep93xx_dma.c | 4 +- drivers/dma/imx-dma.c | 2 +- drivers/dma/imx-sdma.c | 2 +- drivers/dma/mmp_tdma.c | 2 +- drivers/dma/mxs-dma.c | 2 +- drivers/dma/omap-dma.c | 45 ++++-- drivers/dma/pl330.c | 2 +- drivers/dma/sa11x0-dma.c | 2 +- drivers/dma/sirf-dma.c | 2 +- drivers/dma/ste_dma40.c | 3 +- drivers/dma/tegra20-apb-dma.c | 2 +- include/linux/dmaengine.h | 7 +- sound/soc/omap/Kconfig | 3 +- sound/soc/omap/omap-dmic.c | 9 +- sound/soc/omap/omap-hdmi.c | 17 ++- sound/soc/omap/omap-mcbsp.c | 60 +++----- sound/soc/omap/omap-mcpdm.c | 40 +++-- sound/soc/omap/omap-pcm.c | 236 ++++++++--------------------- sound/soc/omap/omap-pcm.h | 4 +- sound/soc/soc-dmaengine-pcm.c | 6 +- 22 files changed, 186 insertions(+), 269 deletions(-)
When src_maxburst/dst_maxburst is set to 0 by the users of cyclic DMA (mostly audio) indicates that we should configure the omap DMA to element sync mode instead of packet mode.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com CC: Russell King rmk+kernel@arm.linux.org.uk --- drivers/dma/omap-dma.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/dma/omap-dma.c b/drivers/dma/omap-dma.c index ae05618..b77a40d 100644 --- a/drivers/dma/omap-dma.c +++ b/drivers/dma/omap-dma.c @@ -413,7 +413,10 @@ static struct dma_async_tx_descriptor *omap_dma_prep_dma_cyclic( d->dev_addr = dev_addr; d->fi = burst; d->es = es; - d->sync_mode = OMAP_DMA_SYNC_PACKET; + if (burst) + d->sync_mode = OMAP_DMA_SYNC_PACKET; + else + d->sync_mode = OMAP_DMA_SYNC_ELEMENT; d->sync_type = sync_type; d->periph_port = OMAP_DMA_PORT_MPUI; d->sg[0].addr = buf_addr;
The audio stack used omap_stop_dma/omap_start_dma to pause/resume the DMA. This method has been used for years on OMAP based products. We only allow pause/resume when the DMA has been configured in cyclic mode which is used by the audio stack.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com CC: Russell King rmk+kernel@arm.linux.org.uk --- drivers/dma/omap-dma.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-)
diff --git a/drivers/dma/omap-dma.c b/drivers/dma/omap-dma.c index b77a40d..71d7869 100644 --- a/drivers/dma/omap-dma.c +++ b/drivers/dma/omap-dma.c @@ -34,6 +34,7 @@ struct omap_chan { struct dma_slave_config cfg; unsigned dma_sig; bool cyclic; + bool paused;
int dma_ch; struct omap_desc *desc; @@ -470,11 +471,14 @@ static int omap_dma_terminate_all(struct omap_chan *c) */ if (c->desc) { c->desc = NULL; - omap_stop_dma(c->dma_ch); + /* Avoid stopping the dma twice */ + if (!c->paused) + omap_stop_dma(c->dma_ch); }
if (c->cyclic) { c->cyclic = false; + c->paused = false; omap_dma_unlink_lch(c->dma_ch, c->dma_ch); }
@@ -487,14 +491,30 @@ static int omap_dma_terminate_all(struct omap_chan *c)
static int omap_dma_pause(struct omap_chan *c) { - /* FIXME: not supported by platform private API */ - return -EINVAL; + /* Pause/Resume only allowed with cyclic mode */ + if (!c->cyclic) + return -EINVAL; + + if (!c->paused) { + omap_stop_dma(c->dma_ch); + c->paused = true; + } + + return 0; }
static int omap_dma_resume(struct omap_chan *c) { - /* FIXME: not supported by platform private API */ - return -EINVAL; + /* Pause/Resume only allowed with cyclic mode */ + if (!c->cyclic) + return -EINVAL; + + if (c->paused) { + omap_start_dma(c->dma_ch); + c->paused = false; + } + + return 0; }
static int omap_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
On Fri, 2012-09-14 at 15:05 +0300, Peter Ujfalusi wrote:
/* FIXME: not supported by platform private API */
return -EINVAL;
/* Pause/Resume only allowed with cyclic mode */
if (!c->cyclic)
return -EINVAL;
This is not a dma restriction right?
Hi Vinod,
On 09/17/2012 06:13 AM, Vinod Koul wrote:
On Fri, 2012-09-14 at 15:05 +0300, Peter Ujfalusi wrote:
/* FIXME: not supported by platform private API */
return -EINVAL;
/* Pause/Resume only allowed with cyclic mode */
if (!c->cyclic)
return -EINVAL;
This is not a dma restriction right?
The pause/resume operation has been only used by audio. This might work with non cyclic modes as well but it has never been used/tested so to be safe I have added this restriction.
On Mon, Sep 17, 2012 at 08:43:57AM +0530, Vinod Koul wrote:
On Fri, 2012-09-14 at 15:05 +0300, Peter Ujfalusi wrote:
/* FIXME: not supported by platform private API */
return -EINVAL;
/* Pause/Resume only allowed with cyclic mode */
if (!c->cyclic)
return -EINVAL;
This is not a dma restriction right?
This is needed because this is not a real "pause" - it is a stop, which causes the contents of the DMA engine's FIFOs to be discarded - which means a subsequent resume will loose data.
On Fri, 2012-09-14 at 15:05 +0300, Peter Ujfalusi wrote:
The audio stack used omap_stop_dma/omap_start_dma to pause/resume the DMA. This method has been used for years on OMAP based products. We only allow pause/resume when the DMA has been configured in cyclic mode which is used by the audio stack.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com CC: Russell King rmk+kernel@arm.linux.org.uk
Acked-by: Vinod Koul vinod.koul@linux.intel.com
With this parameter added to dmaengine_prep_dma_cyclic() the API will be in sync with other dmaengine_prep_*() functions. The dmaengine_prep_dma_cyclic() function primarily used by audio for cyclic transfer required by ALSA, we use the from audio to ask dma drivers to suppress interrupts (if DMA_PREP_INTERRUPT is cleared) when it is supported on the platform.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com CC: Lars-Peter Clausen lars@metafoo.de --- include/linux/dmaengine.h | 3 ++- sound/soc/soc-dmaengine-pcm.c | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 9c02a45..2abcac5 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -653,7 +653,8 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_rio_sg(
static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_cyclic( struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, - size_t period_len, enum dma_transfer_direction dir) + size_t period_len, enum dma_transfer_direction dir, + unsigned long flags) { return chan->device->device_prep_dma_cyclic(chan, buf_addr, buf_len, period_len, dir, NULL); diff --git a/sound/soc/soc-dmaengine-pcm.c b/sound/soc/soc-dmaengine-pcm.c index 5df529e..bbc1257 100644 --- a/sound/soc/soc-dmaengine-pcm.c +++ b/sound/soc/soc-dmaengine-pcm.c @@ -140,14 +140,18 @@ static int dmaengine_pcm_prepare_and_submit(struct snd_pcm_substream *substream) struct dma_chan *chan = prtd->dma_chan; struct dma_async_tx_descriptor *desc; enum dma_transfer_direction direction; + unsigned long flags = DMA_CTRL_ACK;
direction = snd_pcm_substream_to_dma_direction(substream);
+ if (!substream->runtime->no_period_wakeup) + flags |= DMA_PREP_INTERRUPT; + prtd->pos = 0; desc = dmaengine_prep_dma_cyclic(chan, substream->runtime->dma_addr, snd_pcm_lib_buffer_bytes(substream), - snd_pcm_lib_period_bytes(substream), direction); + snd_pcm_lib_period_bytes(substream), direction, flags);
if (!desc) return -ENOMEM;
On Fri, 2012-09-14 at 15:05 +0300, Peter Ujfalusi wrote:
With this parameter added to dmaengine_prep_dma_cyclic() the API will be in sync with other dmaengine_prep_*() functions. The dmaengine_prep_dma_cyclic() function primarily used by audio for cyclic transfer required by ALSA, we use the from audio to ask dma drivers to suppress interrupts (if DMA_PREP_INTERRUPT is cleared) when it is supported on the platform.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com CC: Lars-Peter Clausen lars@metafoo.de
Acked-by: Vinod Koul vinod.koul@linux.intel.com
On Fri, Sep 14, 2012 at 03:05:46PM +0300, Peter Ujfalusi wrote:
With this parameter added to dmaengine_prep_dma_cyclic() the API will be in sync with other dmaengine_prep_*() functions. The dmaengine_prep_dma_cyclic() function primarily used by audio for cyclic transfer required by ALSA, we use the from audio to ask dma drivers to suppress interrupts (if DMA_PREP_INTERRUPT is cleared) when it is supported on the platform.
Are you sure this was generated against for-3.7? There's fuzz against dmaengine.h and git can't find the blobs to do resolution. Anyway, I applied this and the rest of the series.
Change the parameter list of device_prep_dma_cyclic() so the DMA drivers can receive the flags coming from clients. This feature can be used during audio operation to disable all audio related interrupts when the DMA_PREP_INTERRUPT is cleared from the flags.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com CC: Nicolas Ferre nicolas.ferre@atmel.com CC: Barry Song baohua.song@csr.com CC: Srinidhi Kasagar srinidhi.kasagar@stericsson.com CC: Russell King - ARM Linux linux@arm.linux.org.uk CC: Vista Silicon javier.martin@vista-silicon.com CC: Zhangfei Gao zhangfei.gao@marvell.com CC: Shawn Guo shawn.guo@linaro.org CC: Laxman Dewangan ldewangan@nvidia.com --- drivers/dma/at_hdmac.c | 3 ++- drivers/dma/ep93xx_dma.c | 4 +++- drivers/dma/imx-dma.c | 2 +- drivers/dma/imx-sdma.c | 2 +- drivers/dma/mmp_tdma.c | 2 +- drivers/dma/mxs-dma.c | 2 +- drivers/dma/omap-dma.c | 3 ++- drivers/dma/pl330.c | 2 +- drivers/dma/sa11x0-dma.c | 2 +- drivers/dma/sirf-dma.c | 2 +- drivers/dma/ste_dma40.c | 3 ++- drivers/dma/tegra20-apb-dma.c | 2 +- include/linux/dmaengine.h | 4 ++-- 13 files changed, 19 insertions(+), 14 deletions(-)
diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 3934fcc..7e5f6b6 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -841,12 +841,13 @@ atc_dma_cyclic_fill_desc(struct dma_chan *chan, struct at_desc *desc, * @buf_len: total number of bytes for the entire buffer * @period_len: number of bytes for each period * @direction: transfer direction, to or from device + * @flags: tx descriptor status flags * @context: transfer context (ignored) */ static struct dma_async_tx_descriptor * atc_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, size_t period_len, enum dma_transfer_direction direction, - void *context) + unsigned long flags, void *context) { struct at_dma_chan *atchan = to_at_dma_chan(chan); struct at_dma_slave *atslave = chan->private; diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c index c64917e..493735b 100644 --- a/drivers/dma/ep93xx_dma.c +++ b/drivers/dma/ep93xx_dma.c @@ -1120,6 +1120,7 @@ fail: * @buf_len: length of the buffer (in bytes) * @period_len: lenght of a single period * @dir: direction of the operation + * @flags: tx descriptor status flags * @context: operation context (ignored) * * Prepares a descriptor for cyclic DMA operation. This means that once the @@ -1133,7 +1134,8 @@ fail: static struct dma_async_tx_descriptor * ep93xx_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len, size_t period_len, - enum dma_transfer_direction dir, void *context) + enum dma_transfer_direction dir, unsigned long flags, + void *context) { struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan); struct ep93xx_dma_desc *desc, *first; diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index 5084975..41b4376 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -801,7 +801,7 @@ static struct dma_async_tx_descriptor *imxdma_prep_slave_sg( static struct dma_async_tx_descriptor *imxdma_prep_dma_cyclic( struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len, size_t period_len, enum dma_transfer_direction direction, - void *context) + unsigned long flags, void *context) { struct imxdma_channel *imxdmac = to_imxdma_chan(chan); struct imxdma_engine *imxdma = imxdmac->imxdma; diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 1dc2a4a..2c5fd3e 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -1012,7 +1012,7 @@ err_out: static struct dma_async_tx_descriptor *sdma_prep_dma_cyclic( struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len, size_t period_len, enum dma_transfer_direction direction, - void *context) + unsigned long flags, void *context) { struct sdma_channel *sdmac = to_sdma_chan(chan); struct sdma_engine *sdma = sdmac->sdma; diff --git a/drivers/dma/mmp_tdma.c b/drivers/dma/mmp_tdma.c index 8a15cf2..6d52bd4 100644 --- a/drivers/dma/mmp_tdma.c +++ b/drivers/dma/mmp_tdma.c @@ -358,7 +358,7 @@ struct mmp_tdma_desc *mmp_tdma_alloc_descriptor(struct mmp_tdma_chan *tdmac) static struct dma_async_tx_descriptor *mmp_tdma_prep_dma_cyclic( struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len, size_t period_len, enum dma_transfer_direction direction, - void *context) + unsigned long flags, void *context) { struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan); struct mmp_tdma_desc *desc; diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index 7f41b25..734a4eb 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -531,7 +531,7 @@ err_out: static struct dma_async_tx_descriptor *mxs_dma_prep_dma_cyclic( struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len, size_t period_len, enum dma_transfer_direction direction, - void *context) + unsigned long flags, void *context) { struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan); struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; diff --git a/drivers/dma/omap-dma.c b/drivers/dma/omap-dma.c index 71d7869..4d2650f 100644 --- a/drivers/dma/omap-dma.c +++ b/drivers/dma/omap-dma.c @@ -366,7 +366,8 @@ static struct dma_async_tx_descriptor *omap_dma_prep_slave_sg(
static struct dma_async_tx_descriptor *omap_dma_prep_dma_cyclic( struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, - size_t period_len, enum dma_transfer_direction dir, void *context) + size_t period_len, enum dma_transfer_direction dir, unsigned long flags, + void *context) { struct omap_chan *c = to_omap_dma_chan(chan); enum dma_slave_buswidth dev_width; diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index e4feba6..0035645 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -2683,7 +2683,7 @@ static inline int get_burst_len(struct dma_pl330_desc *desc, size_t len) static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic( struct dma_chan *chan, dma_addr_t dma_addr, size_t len, size_t period_len, enum dma_transfer_direction direction, - void *context) + unsigned long flags, void *context) { struct dma_pl330_desc *desc; struct dma_pl330_chan *pch = to_pchan(chan); diff --git a/drivers/dma/sa11x0-dma.c b/drivers/dma/sa11x0-dma.c index f5a7360..b893159 100644 --- a/drivers/dma/sa11x0-dma.c +++ b/drivers/dma/sa11x0-dma.c @@ -614,7 +614,7 @@ static struct dma_async_tx_descriptor *sa11x0_dma_prep_slave_sg(
static struct dma_async_tx_descriptor *sa11x0_dma_prep_dma_cyclic( struct dma_chan *chan, dma_addr_t addr, size_t size, size_t period, - enum dma_transfer_direction dir, void *context) + enum dma_transfer_direction dir, unsigned long flags, void *context) { struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); struct sa11x0_dma_desc *txd; diff --git a/drivers/dma/sirf-dma.c b/drivers/dma/sirf-dma.c index 434ad31..3eed8b3 100644 --- a/drivers/dma/sirf-dma.c +++ b/drivers/dma/sirf-dma.c @@ -489,7 +489,7 @@ err_dir: static struct dma_async_tx_descriptor * sirfsoc_dma_prep_cyclic(struct dma_chan *chan, dma_addr_t addr, size_t buf_len, size_t period_len, - enum dma_transfer_direction direction, void *context) + enum dma_transfer_direction direction, unsigned long flags, void *context) { struct sirfsoc_dma_chan *schan = dma_chan_to_sirfsoc_dma_chan(chan); struct sirfsoc_dma_desc *sdesc = NULL; diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 000d309..eee8d9b 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -2347,7 +2347,8 @@ static struct dma_async_tx_descriptor *d40_prep_slave_sg(struct dma_chan *chan, static struct dma_async_tx_descriptor * dma40_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len, size_t period_len, - enum dma_transfer_direction direction, void *context) + enum dma_transfer_direction direction, unsigned long flags, + void *context) { unsigned int periods = buf_len / period_len; struct dma_async_tx_descriptor *txd; diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index 24acd71..b42b6ff 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -990,7 +990,7 @@ static struct dma_async_tx_descriptor *tegra_dma_prep_slave_sg( struct dma_async_tx_descriptor *tegra_dma_prep_dma_cyclic( struct dma_chan *dc, dma_addr_t buf_addr, size_t buf_len, size_t period_len, enum dma_transfer_direction direction, - void *context) + unsigned long flags, void *context) { struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc); struct tegra_dma_desc *dma_desc = NULL; diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 2abcac5..d3201e4 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -591,7 +591,7 @@ struct dma_device { struct dma_async_tx_descriptor *(*device_prep_dma_cyclic)( struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, size_t period_len, enum dma_transfer_direction direction, - void *context); + unsigned long flags, void *context); struct dma_async_tx_descriptor *(*device_prep_interleaved_dma)( struct dma_chan *chan, struct dma_interleaved_template *xt, unsigned long flags); @@ -657,7 +657,7 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_cyclic( unsigned long flags) { return chan->device->device_prep_dma_cyclic(chan, buf_addr, buf_len, - period_len, dir, NULL); + period_len, dir, flags, NULL); }
static inline int dmaengine_terminate_all(struct dma_chan *chan)
On Fri, 2012-09-14 at 15:05 +0300, Peter Ujfalusi wrote:
Change the parameter list of device_prep_dma_cyclic() so the DMA drivers can receive the flags coming from clients. This feature can be used during audio operation to disable all audio related interrupts when the DMA_PREP_INTERRUPT is cleared from the flags.
Acked-by: Vinod Koul vinod.koul@linux.intel.com
On 09/14/2012 02:05 PM, Peter Ujfalusi :
Change the parameter list of device_prep_dma_cyclic() so the DMA drivers can receive the flags coming from clients. This feature can be used during audio operation to disable all audio related interrupts when the DMA_PREP_INTERRUPT is cleared from the flags.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com CC: Nicolas Ferre nicolas.ferre@atmel.com CC: Barry Song baohua.song@csr.com CC: Srinidhi Kasagar srinidhi.kasagar@stericsson.com CC: Russell King - ARM Linux linux@arm.linux.org.uk CC: Vista Silicon javier.martin@vista-silicon.com CC: Zhangfei Gao zhangfei.gao@marvell.com CC: Shawn Guo shawn.guo@linaro.org CC: Laxman Dewangan ldewangan@nvidia.com
drivers/dma/at_hdmac.c | 3 ++-
For Atmel's driver:
Acked-by: Nicolas Ferre nicolas.ferre@atmel.com
drivers/dma/ep93xx_dma.c | 4 +++- drivers/dma/imx-dma.c | 2 +- drivers/dma/imx-sdma.c | 2 +- drivers/dma/mmp_tdma.c | 2 +- drivers/dma/mxs-dma.c | 2 +- drivers/dma/omap-dma.c | 3 ++- drivers/dma/pl330.c | 2 +- drivers/dma/sa11x0-dma.c | 2 +- drivers/dma/sirf-dma.c | 2 +- drivers/dma/ste_dma40.c | 3 ++- drivers/dma/tegra20-apb-dma.c | 2 +- include/linux/dmaengine.h | 4 ++-- 13 files changed, 19 insertions(+), 14 deletions(-)
diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 3934fcc..7e5f6b6 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -841,12 +841,13 @@ atc_dma_cyclic_fill_desc(struct dma_chan *chan, struct at_desc *desc,
- @buf_len: total number of bytes for the entire buffer
- @period_len: number of bytes for each period
- @direction: transfer direction, to or from device
*/
- @flags: tx descriptor status flags
- @context: transfer context (ignored)
static struct dma_async_tx_descriptor * atc_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, size_t period_len, enum dma_transfer_direction direction,
void *context)
unsigned long flags, void *context)
{ struct at_dma_chan *atchan = to_at_dma_chan(chan); struct at_dma_slave *atslave = chan->private; diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c index c64917e..493735b 100644 --- a/drivers/dma/ep93xx_dma.c +++ b/drivers/dma/ep93xx_dma.c @@ -1120,6 +1120,7 @@ fail:
- @buf_len: length of the buffer (in bytes)
- @period_len: lenght of a single period
- @dir: direction of the operation
- @flags: tx descriptor status flags
- @context: operation context (ignored)
- Prepares a descriptor for cyclic DMA operation. This means that once the
@@ -1133,7 +1134,8 @@ fail: static struct dma_async_tx_descriptor * ep93xx_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len, size_t period_len,
enum dma_transfer_direction dir, void *context)
enum dma_transfer_direction dir, unsigned long flags,
void *context)
{ struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan); struct ep93xx_dma_desc *desc, *first; diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index 5084975..41b4376 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -801,7 +801,7 @@ static struct dma_async_tx_descriptor *imxdma_prep_slave_sg( static struct dma_async_tx_descriptor *imxdma_prep_dma_cyclic( struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len, size_t period_len, enum dma_transfer_direction direction,
void *context)
unsigned long flags, void *context)
{ struct imxdma_channel *imxdmac = to_imxdma_chan(chan); struct imxdma_engine *imxdma = imxdmac->imxdma; diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 1dc2a4a..2c5fd3e 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -1012,7 +1012,7 @@ err_out: static struct dma_async_tx_descriptor *sdma_prep_dma_cyclic( struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len, size_t period_len, enum dma_transfer_direction direction,
void *context)
unsigned long flags, void *context)
{ struct sdma_channel *sdmac = to_sdma_chan(chan); struct sdma_engine *sdma = sdmac->sdma; diff --git a/drivers/dma/mmp_tdma.c b/drivers/dma/mmp_tdma.c index 8a15cf2..6d52bd4 100644 --- a/drivers/dma/mmp_tdma.c +++ b/drivers/dma/mmp_tdma.c @@ -358,7 +358,7 @@ struct mmp_tdma_desc *mmp_tdma_alloc_descriptor(struct mmp_tdma_chan *tdmac) static struct dma_async_tx_descriptor *mmp_tdma_prep_dma_cyclic( struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len, size_t period_len, enum dma_transfer_direction direction,
void *context)
unsigned long flags, void *context)
{ struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan); struct mmp_tdma_desc *desc; diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index 7f41b25..734a4eb 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -531,7 +531,7 @@ err_out: static struct dma_async_tx_descriptor *mxs_dma_prep_dma_cyclic( struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len, size_t period_len, enum dma_transfer_direction direction,
void *context)
unsigned long flags, void *context)
{ struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan); struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; diff --git a/drivers/dma/omap-dma.c b/drivers/dma/omap-dma.c index 71d7869..4d2650f 100644 --- a/drivers/dma/omap-dma.c +++ b/drivers/dma/omap-dma.c @@ -366,7 +366,8 @@ static struct dma_async_tx_descriptor *omap_dma_prep_slave_sg(
static struct dma_async_tx_descriptor *omap_dma_prep_dma_cyclic( struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
- size_t period_len, enum dma_transfer_direction dir, void *context)
- size_t period_len, enum dma_transfer_direction dir, unsigned long flags,
- void *context)
{ struct omap_chan *c = to_omap_dma_chan(chan); enum dma_slave_buswidth dev_width; diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index e4feba6..0035645 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -2683,7 +2683,7 @@ static inline int get_burst_len(struct dma_pl330_desc *desc, size_t len) static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic( struct dma_chan *chan, dma_addr_t dma_addr, size_t len, size_t period_len, enum dma_transfer_direction direction,
void *context)
unsigned long flags, void *context)
{ struct dma_pl330_desc *desc; struct dma_pl330_chan *pch = to_pchan(chan); diff --git a/drivers/dma/sa11x0-dma.c b/drivers/dma/sa11x0-dma.c index f5a7360..b893159 100644 --- a/drivers/dma/sa11x0-dma.c +++ b/drivers/dma/sa11x0-dma.c @@ -614,7 +614,7 @@ static struct dma_async_tx_descriptor *sa11x0_dma_prep_slave_sg(
static struct dma_async_tx_descriptor *sa11x0_dma_prep_dma_cyclic( struct dma_chan *chan, dma_addr_t addr, size_t size, size_t period,
- enum dma_transfer_direction dir, void *context)
- enum dma_transfer_direction dir, unsigned long flags, void *context)
{ struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan); struct sa11x0_dma_desc *txd; diff --git a/drivers/dma/sirf-dma.c b/drivers/dma/sirf-dma.c index 434ad31..3eed8b3 100644 --- a/drivers/dma/sirf-dma.c +++ b/drivers/dma/sirf-dma.c @@ -489,7 +489,7 @@ err_dir: static struct dma_async_tx_descriptor * sirfsoc_dma_prep_cyclic(struct dma_chan *chan, dma_addr_t addr, size_t buf_len, size_t period_len,
- enum dma_transfer_direction direction, void *context)
- enum dma_transfer_direction direction, unsigned long flags, void *context)
{ struct sirfsoc_dma_chan *schan = dma_chan_to_sirfsoc_dma_chan(chan); struct sirfsoc_dma_desc *sdesc = NULL; diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 000d309..eee8d9b 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -2347,7 +2347,8 @@ static struct dma_async_tx_descriptor *d40_prep_slave_sg(struct dma_chan *chan, static struct dma_async_tx_descriptor * dma40_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len, size_t period_len,
enum dma_transfer_direction direction, void *context)
enum dma_transfer_direction direction, unsigned long flags,
void *context)
{ unsigned int periods = buf_len / period_len; struct dma_async_tx_descriptor *txd; diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index 24acd71..b42b6ff 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -990,7 +990,7 @@ static struct dma_async_tx_descriptor *tegra_dma_prep_slave_sg( struct dma_async_tx_descriptor *tegra_dma_prep_dma_cyclic( struct dma_chan *dc, dma_addr_t buf_addr, size_t buf_len, size_t period_len, enum dma_transfer_direction direction,
- void *context)
- unsigned long flags, void *context)
{ struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc); struct tegra_dma_desc *dma_desc = NULL; diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 2abcac5..d3201e4 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -591,7 +591,7 @@ struct dma_device { struct dma_async_tx_descriptor *(*device_prep_dma_cyclic)( struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, size_t period_len, enum dma_transfer_direction direction,
void *context);
struct dma_async_tx_descriptor *(*device_prep_interleaved_dma)( struct dma_chan *chan, struct dma_interleaved_template *xt, unsigned long flags);unsigned long flags, void *context);
@@ -657,7 +657,7 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_cyclic( unsigned long flags) { return chan->device->device_prep_dma_cyclic(chan, buf_addr, buf_len,
period_len, dir, NULL);
period_len, dir, flags, NULL);
}
static inline int dmaengine_terminate_all(struct dma_chan *chan)
On Fri, Sep 14, 2012 at 03:05:47PM +0300, Peter Ujfalusi wrote:
Change the parameter list of device_prep_dma_cyclic() so the DMA drivers can receive the flags coming from clients. This feature can be used during audio operation to disable all audio related interrupts when the DMA_PREP_INTERRUPT is cleared from the flags.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com CC: Nicolas Ferre nicolas.ferre@atmel.com CC: Barry Song baohua.song@csr.com CC: Srinidhi Kasagar srinidhi.kasagar@stericsson.com CC: Russell King - ARM Linux linux@arm.linux.org.uk CC: Vista Silicon javier.martin@vista-silicon.com CC: Zhangfei Gao zhangfei.gao@marvell.com CC: Shawn Guo shawn.guo@linaro.org CC: Laxman Dewangan ldewangan@nvidia.com
...
drivers/dma/imx-dma.c | 2 +- drivers/dma/imx-sdma.c | 2 +-
...
drivers/dma/mxs-dma.c | 2 +-
Acked-by: Shawn Guo shawn.guo@linaro.org
When requested (DMA_PREP_INTERRUPT is cleared in flags) disable all DMA interrupts for the channel. In this mode user space does not expect periodic reports from kernel about the progress of the audio stream. PulseAudio for example support this type of mode.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com CC: Russell King rmk+kernel@arm.linux.org.uk --- drivers/dma/omap-dma.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/drivers/dma/omap-dma.c b/drivers/dma/omap-dma.c index 4d2650f..f59f244 100644 --- a/drivers/dma/omap-dma.c +++ b/drivers/dma/omap-dma.c @@ -429,7 +429,10 @@ static struct dma_async_tx_descriptor *omap_dma_prep_dma_cyclic( if (!c->cyclic) { c->cyclic = true; omap_dma_link_lch(c->dma_ch, c->dma_ch); - omap_enable_dma_irq(c->dma_ch, OMAP_DMA_FRAME_IRQ); + + if (flags & DMA_PREP_INTERRUPT) + omap_enable_dma_irq(c->dma_ch, OMAP_DMA_FRAME_IRQ); + omap_disable_dma_irq(c->dma_ch, OMAP_DMA_BLOCK_IRQ); }
@@ -438,7 +441,7 @@ static struct dma_async_tx_descriptor *omap_dma_prep_dma_cyclic( omap_set_dma_dest_burst_mode(c->dma_ch, OMAP_DMA_DATA_BURST_16); }
- return vchan_tx_prep(&c->vc, &d->vd, DMA_CTRL_ACK | DMA_PREP_INTERRUPT); + return vchan_tx_prep(&c->vc, &d->vd, flags); }
static int omap_dma_slave_config(struct omap_chan *c, struct dma_slave_config *cfg)
On Fri, 2012-09-14 at 15:05 +0300, Peter Ujfalusi wrote:
When requested (DMA_PREP_INTERRUPT is cleared in flags) disable all DMA interrupts for the channel. In this mode user space does not expect periodic reports from kernel about the progress of the audio stream. PulseAudio for example support this type of mode.
Acked-by: Vinod Koul vinod.koul@linux.intel.com
When McBSP is configured in threshold mode we can use sDMA packet mode in all cases.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- sound/soc/omap/omap-mcbsp.c | 47 ++++++++++++++++----------------------------- 1 file changed, 17 insertions(+), 30 deletions(-)
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index 2e91a86..fe3debc 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c @@ -81,9 +81,6 @@ static void omap_mcbsp_set_threshold(struct snd_pcm_substream *substream) */ if (dma_data->packet_size) words = dma_data->packet_size; - else if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) - words = snd_pcm_lib_period_bytes(substream) / - (mcbsp->wlen / 8); else words = 1;
@@ -251,6 +248,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, dma_data->set_threshold = omap_mcbsp_set_threshold; if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) { int period_words, max_thrsh; + int divider = 0;
period_words = params_period_bytes(params) / (wlen / 8); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) @@ -258,34 +256,23 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, else max_thrsh = mcbsp->max_rx_thres; /* - * If the period contains less or equal number of words, - * we are using the original threshold mode setup: - * McBSP threshold = sDMA frame size = period_size - * Otherwise we switch to sDMA packet mode: - * McBSP threshold = sDMA packet size - * sDMA frame size = period size + * Use sDMA packet mode if McBSP is in threshold mode: + * If period words less than the FIFO size the packet + * size is set to the number of period words, otherwise + * Look for the biggest threshold value which divides + * the period size evenly. */ - if (period_words > max_thrsh) { - int divider = 0; - - /* - * Look for the biggest threshold value, which - * divides the period size evenly. - */ - divider = period_words / max_thrsh; - if (period_words % max_thrsh) - divider++; - while (period_words % divider && - divider < period_words) - divider++; - if (divider == period_words) - return -EINVAL; - - pkt_size = period_words / divider; - sync_mode = OMAP_DMA_SYNC_PACKET; - } else { - sync_mode = OMAP_DMA_SYNC_FRAME; - } + divider = period_words / max_thrsh; + if (period_words % max_thrsh) + divider++; + while (period_words % divider && + divider < period_words) + divider++; + if (divider == period_words) + return -EINVAL; + + pkt_size = period_words / divider; + sync_mode = OMAP_DMA_SYNC_PACKET; } else if (channels > 1) { /* Use packet mode for non mono streams */ pkt_size = channels;
Since we only have element or packet synchronization we can use the dma_data->packet_size to select the desired mode: if packet_size is 0 we use ELEMENT mode if packet_size is not 0 we use PACKET mode for sDMA synchronization.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- sound/soc/omap/omap-pcm.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c index f0feb06..02eeb2e 100644 --- a/sound/soc/omap/omap-pcm.c +++ b/sound/soc/omap/omap-pcm.c @@ -165,7 +165,12 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream) 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 (dma_data->packet_size) + dma_params.sync_mode = OMAP_DMA_SYNC_PACKET; + else + dma_params.sync_mode = OMAP_DMA_SYNC_ELEMENT; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { dma_params.src_amode = OMAP_DMA_AMODE_POST_INC; dma_params.dst_amode = OMAP_DMA_AMODE_CONSTANT;
The omap-pcm platform driver no longer needs this parameter to select between ELEMENT and PACKET mode. The selection is based on the configured packet_size.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- sound/soc/omap/omap-dmic.c | 1 - sound/soc/omap/omap-hdmi.c | 1 - sound/soc/omap/omap-mcbsp.c | 5 +---- sound/soc/omap/omap-mcpdm.c | 2 -- sound/soc/omap/omap-pcm.h | 1 - 5 files changed, 1 insertion(+), 9 deletions(-)
diff --git a/sound/soc/omap/omap-dmic.c b/sound/soc/omap/omap-dmic.c index 75f5dca..60b7b8c 100644 --- a/sound/soc/omap/omap-dmic.c +++ b/sound/soc/omap/omap-dmic.c @@ -64,7 +64,6 @@ struct omap_dmic { static struct omap_pcm_dma_data omap_dmic_dai_dma_params = { .name = "DMIC capture", .data_type = OMAP_DMA_DATA_TYPE_S32, - .sync_mode = OMAP_DMA_SYNC_PACKET, };
static inline void omap_dmic_write(struct omap_dmic *dmic, u16 reg, u32 val) diff --git a/sound/soc/omap/omap-hdmi.c b/sound/soc/omap/omap-hdmi.c index a08245d..b194646 100644 --- a/sound/soc/omap/omap-hdmi.c +++ b/sound/soc/omap/omap-hdmi.c @@ -290,7 +290,6 @@ static __devinit int omap_hdmi_probe(struct platform_device *pdev)
hdmi_data->dma_params.dma_req = hdmi_rsrc->start; hdmi_data->dma_params.name = "HDMI playback"; - hdmi_data->dma_params.sync_mode = OMAP_DMA_SYNC_PACKET;
/* * TODO: We assume that there is only one DSS HDMI device. Future diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index fe3debc..5b3bacc 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c @@ -225,7 +225,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs; struct omap_pcm_dma_data *dma_data; - int wlen, channels, wpf, sync_mode = OMAP_DMA_SYNC_ELEMENT; + int wlen, channels, wpf; int pkt_size = 0; unsigned int format, div, framesize, master;
@@ -272,15 +272,12 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, return -EINVAL;
pkt_size = period_words / divider; - sync_mode = OMAP_DMA_SYNC_PACKET; } else if (channels > 1) { /* Use packet mode for non mono streams */ pkt_size = channels; - sync_mode = OMAP_DMA_SYNC_PACKET; } }
- dma_data->sync_mode = sync_mode; dma_data->packet_size = pkt_size;
snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); diff --git a/sound/soc/omap/omap-mcpdm.c b/sound/soc/omap/omap-mcpdm.c index f7babb3..baf92da 100644 --- a/sound/soc/omap/omap-mcpdm.c +++ b/sound/soc/omap/omap-mcpdm.c @@ -73,14 +73,12 @@ static struct omap_pcm_dma_data omap_mcpdm_dai_dma_params[] = { .name = "Audio playback", .dma_req = OMAP44XX_DMA_MCPDM_DL, .data_type = OMAP_DMA_DATA_TYPE_S32, - .sync_mode = OMAP_DMA_SYNC_PACKET, .port_addr = OMAP44XX_MCPDM_L3_BASE + MCPDM_REG_DN_DATA, }, { .name = "Audio capture", .dma_req = OMAP44XX_DMA_MCPDM_UP, .data_type = OMAP_DMA_DATA_TYPE_S32, - .sync_mode = OMAP_DMA_SYNC_PACKET, .port_addr = OMAP44XX_MCPDM_L3_BASE + MCPDM_REG_UP_DATA, }, }; diff --git a/sound/soc/omap/omap-pcm.h b/sound/soc/omap/omap-pcm.h index b92248c..1bf47e4 100644 --- a/sound/soc/omap/omap-pcm.h +++ b/sound/soc/omap/omap-pcm.h @@ -33,7 +33,6 @@ struct omap_pcm_dma_data { unsigned long port_addr; /* transmit/receive register */ void (*set_threshold)(struct snd_pcm_substream *substream); int data_type; /* data type 8,16,32 */ - int sync_mode; /* DMA sync mode */ int packet_size; /* packet size only in PACKET mode */ };
Based on the format of the stream the omap-pcm can decide alone what data type should be used with by the sDMA. Keep the possibility for OMAP dai drivers to tell omap-pcm if they want to use different data type. This is needed for the omap-hdmi for example which needs 32bit data type even if the stream format is S16_LE.
The check if (dma_data->data_type) is safe at the moment since omap-pcm does not support 8bit samples (OMAP_DMA_DATA_TYPE_S8 == 0x00).
The next step is to redefine the meaning of dma_data->data_type to unblock this limitation.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- sound/soc/omap/omap-pcm.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-)
diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c index 02eeb2e..4c13a5f 100644 --- a/sound/soc/omap/omap-pcm.c +++ b/sound/soc/omap/omap-pcm.c @@ -149,6 +149,24 @@ static int omap_pcm_hw_free(struct snd_pcm_substream *substream) return 0; }
+static int omap_pcm_get_dma_type(int num_bits) +{ + int data_type; + + switch (num_bits) { + case 16: + data_type = OMAP_DMA_DATA_TYPE_S16; + break; + case 32: + data_type = OMAP_DMA_DATA_TYPE_S32; + break; + default: + data_type = -EINVAL; + break; + } + return data_type; +} + static int omap_pcm_prepare(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -163,7 +181,16 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream) return 0;
memset(&dma_params, 0, sizeof(dma_params)); - dma_params.data_type = dma_data->data_type; + + if (dma_data->data_type) + dma_params.data_type = dma_data->data_type; + else + dma_params.data_type = omap_pcm_get_dma_type( + snd_pcm_format_physical_width(runtime->format)); + + if (dma_params.data_type < 0) + return dma_params.data_type; + dma_params.trigger = dma_data->dma_req;
if (dma_data->packet_size) @@ -195,7 +222,7 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream) * still can get an interrupt at each period bounary */ bytes = snd_pcm_lib_period_bytes(substream); - dma_params.elem_count = bytes >> dma_data->data_type; + dma_params.elem_count = bytes >> dma_params.data_type; dma_params.frame_count = runtime->periods; omap_set_dma_params(prtd->dma_ch, &dma_params);
To help the driver to get the correct memory range to access McPDM registers.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- arch/arm/mach-omap2/omap_hwmod_44xx_data.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c index 242aee4..f65251e 100644 --- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c @@ -5059,6 +5059,7 @@ static struct omap_hwmod_ocp_if omap44xx_l4_per__mcbsp4 = {
static struct omap_hwmod_addr_space omap44xx_mcpdm_addrs[] = { { + .name = "mpu", .pa_start = 0x40132000, .pa_end = 0x4013207f, .flags = ADDR_TYPE_RT @@ -5077,6 +5078,7 @@ static struct omap_hwmod_ocp_if omap44xx_l4_abe__mcpdm = {
static struct omap_hwmod_addr_space omap44xx_mcpdm_dma_addrs[] = { { + .name = "dma", .pa_start = 0x49032000, .pa_end = 0x4903207f, .flags = ADDR_TYPE_RT
Get the needed resources in a correct way and avoid using defines for them.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- sound/soc/omap/omap-mcpdm.c | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-)
diff --git a/sound/soc/omap/omap-mcpdm.c b/sound/soc/omap/omap-mcpdm.c index baf92da..f90d5de 100644 --- a/sound/soc/omap/omap-mcpdm.c +++ b/sound/soc/omap/omap-mcpdm.c @@ -71,15 +71,11 @@ struct omap_mcpdm { static struct omap_pcm_dma_data omap_mcpdm_dai_dma_params[] = { { .name = "Audio playback", - .dma_req = OMAP44XX_DMA_MCPDM_DL, .data_type = OMAP_DMA_DATA_TYPE_S32, - .port_addr = OMAP44XX_MCPDM_L3_BASE + MCPDM_REG_DN_DATA, }, { .name = "Audio capture", - .dma_req = OMAP44XX_DMA_MCPDM_UP, .data_type = OMAP_DMA_DATA_TYPE_S32, - .port_addr = OMAP44XX_MCPDM_L3_BASE + MCPDM_REG_UP_DATA, }, };
@@ -452,10 +448,33 @@ static __devinit int asoc_mcpdm_probe(struct platform_device *pdev)
mutex_init(&mcpdm->mutex);
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma"); + if (res == NULL) + return -ENOMEM; + + omap_mcpdm_dai_dma_params[0].port_addr = res->start + MCPDM_REG_DN_DATA; + omap_mcpdm_dai_dma_params[1].port_addr = res->start + MCPDM_REG_UP_DATA; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) return -ENOMEM;
+ res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "dn_link"); + if (!res) + return -ENODEV; + + omap_mcpdm_dai_dma_params[0].dma_req = res->start; + + res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "up_link"); + if (!res) + return -ENODEV; + + omap_mcpdm_dai_dma_params[1].dma_req = res->start; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu"); + if (res == NULL) + return -ENOMEM; + if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res), "McPDM")) return -EBUSY;
omap-pcm can figure out the correct dma_type based on the stream's format. In this way we can get rid of the plat/dma.h include from these drivers.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- sound/soc/omap/omap-dmic.c | 2 -- sound/soc/omap/omap-mcbsp.c | 3 --- sound/soc/omap/omap-mcpdm.c | 3 --- 3 files changed, 8 deletions(-)
diff --git a/sound/soc/omap/omap-dmic.c b/sound/soc/omap/omap-dmic.c index 60b7b8c..df0ff24 100644 --- a/sound/soc/omap/omap-dmic.c +++ b/sound/soc/omap/omap-dmic.c @@ -33,7 +33,6 @@ #include <linux/slab.h> #include <linux/pm_runtime.h> #include <linux/of_device.h> -#include <plat/dma.h>
#include <sound/core.h> #include <sound/pcm.h> @@ -63,7 +62,6 @@ struct omap_dmic { */ static struct omap_pcm_dma_data omap_dmic_dai_dma_params = { .name = "DMIC capture", - .data_type = OMAP_DMA_DATA_TYPE_S32, };
static inline void omap_dmic_write(struct omap_dmic *dmic, u16 reg, u32 val) diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index 5b3bacc..a230646 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c @@ -34,7 +34,6 @@ #include <sound/initval.h> #include <sound/soc.h>
-#include <plat/dma.h> #include <plat/mcbsp.h> #include "mcbsp.h" #include "omap-mcbsp.h" @@ -234,11 +233,9 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: - dma_data->data_type = OMAP_DMA_DATA_TYPE_S16; wlen = 16; break; case SNDRV_PCM_FORMAT_S32_LE: - dma_data->data_type = OMAP_DMA_DATA_TYPE_S32; wlen = 32; break; default: diff --git a/sound/soc/omap/omap-mcpdm.c b/sound/soc/omap/omap-mcpdm.c index f90d5de..84743d4 100644 --- a/sound/soc/omap/omap-mcpdm.c +++ b/sound/soc/omap/omap-mcpdm.c @@ -40,7 +40,6 @@ #include <sound/pcm_params.h> #include <sound/soc.h>
-#include <plat/dma.h> #include <plat/omap_hwmod.h> #include "omap-mcpdm.h" #include "omap-pcm.h" @@ -71,11 +70,9 @@ struct omap_mcpdm { static struct omap_pcm_dma_data omap_mcpdm_dai_dma_params[] = { { .name = "Audio playback", - .data_type = OMAP_DMA_DATA_TYPE_S32, }, { .name = "Audio capture", - .data_type = OMAP_DMA_DATA_TYPE_S32, }, };
Instead of the OMAP DMA data type definition the data_type will be used to specify the number of bits the DMA word should be configured or 0 in case when based on the stream's format the omap-pcm can decide the needed DMA word size. This feature is needed for the omap-hdmi where the sDMA need to be configured for 32bit word type regardless of the audio format used.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- sound/soc/omap/omap-hdmi.c | 3 +-- sound/soc/omap/omap-pcm.c | 3 ++- sound/soc/omap/omap-pcm.h | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/sound/soc/omap/omap-hdmi.c b/sound/soc/omap/omap-hdmi.c index b194646..0951767 100644 --- a/sound/soc/omap/omap-hdmi.c +++ b/sound/soc/omap/omap-hdmi.c @@ -34,7 +34,6 @@ #include <sound/asoundef.h> #include <video/omapdss.h>
-#include <plat/dma.h> #include "omap-pcm.h" #include "omap-hdmi.h"
@@ -100,7 +99,7 @@ static int omap_hdmi_dai_hw_params(struct snd_pcm_substream *substream, return -EINVAL; }
- priv->dma_params.data_type = OMAP_DMA_DATA_TYPE_S32; + priv->dma_params.data_type = 32;
snd_soc_dai_set_dma_data(dai, substream, &priv->dma_params); diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c index 4c13a5f..74da4b7 100644 --- a/sound/soc/omap/omap-pcm.c +++ b/sound/soc/omap/omap-pcm.c @@ -183,7 +183,8 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream) memset(&dma_params, 0, sizeof(dma_params));
if (dma_data->data_type) - dma_params.data_type = dma_data->data_type; + dma_params.data_type = omap_pcm_get_dma_type( + dma_data->data_type); else dma_params.data_type = omap_pcm_get_dma_type( snd_pcm_format_physical_width(runtime->format)); diff --git a/sound/soc/omap/omap-pcm.h b/sound/soc/omap/omap-pcm.h index 1bf47e4..cabe74c 100644 --- a/sound/soc/omap/omap-pcm.h +++ b/sound/soc/omap/omap-pcm.h @@ -32,7 +32,8 @@ struct omap_pcm_dma_data { int dma_req; /* DMA request line */ unsigned long port_addr; /* transmit/receive register */ void (*set_threshold)(struct snd_pcm_substream *substream); - int data_type; /* data type 8,16,32 */ + int data_type; /* 8, 16, 32 (bits) or 0 to let omap-pcm + * to decide the sDMA data type */ int packet_size; /* packet size only in PACKET mode */ };
Set the dma_data for the stream (snd_soc_dai_set_dma_data) at dai_startup time so omap-pcm will have access to the needed information regarding to the DMA channel earlier. This is needed for the clean dmaengine support.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- sound/soc/omap/omap-dmic.c | 6 ++++-- sound/soc/omap/omap-hdmi.c | 15 +++++++++------ sound/soc/omap/omap-mcbsp.c | 7 ++++--- sound/soc/omap/omap-mcpdm.c | 8 ++++---- 4 files changed, 21 insertions(+), 15 deletions(-)
diff --git a/sound/soc/omap/omap-dmic.c b/sound/soc/omap/omap-dmic.c index df0ff24..68f2cd1 100644 --- a/sound/soc/omap/omap-dmic.c +++ b/sound/soc/omap/omap-dmic.c @@ -118,6 +118,7 @@ static int omap_dmic_dai_startup(struct snd_pcm_substream *substream,
mutex_unlock(&dmic->mutex);
+ snd_soc_dai_set_dma_data(dai, substream, &omap_dmic_dai_dma_params); return ret; }
@@ -202,6 +203,7 @@ static int omap_dmic_dai_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai); + struct omap_pcm_dma_data *dma_data; int channels;
dmic->clk_div = omap_dmic_select_divider(dmic, params_rate(params)); @@ -227,8 +229,8 @@ static int omap_dmic_dai_hw_params(struct snd_pcm_substream *substream, }
/* packet size is threshold * channels */ - omap_dmic_dai_dma_params.packet_size = dmic->threshold * channels; - snd_soc_dai_set_dma_data(dai, substream, &omap_dmic_dai_dma_params); + dma_data = snd_soc_dai_get_dma_data(dai, substream); + dma_data->packet_size = dmic->threshold * channels;
return 0; } diff --git a/sound/soc/omap/omap-hdmi.c b/sound/soc/omap/omap-hdmi.c index 0951767..f59c69f 100644 --- a/sound/soc/omap/omap-hdmi.c +++ b/sound/soc/omap/omap-hdmi.c @@ -67,6 +67,9 @@ static int omap_hdmi_dai_startup(struct snd_pcm_substream *substream, dev_err(dai->dev, "audio not supported\n"); return -ENODEV; } + + snd_soc_dai_set_dma_data(dai, substream, &priv->dma_params); + return 0; }
@@ -85,24 +88,24 @@ static int omap_hdmi_dai_hw_params(struct snd_pcm_substream *substream, struct hdmi_priv *priv = snd_soc_dai_get_drvdata(dai); struct snd_aes_iec958 *iec = &priv->iec; struct snd_cea_861_aud_if *cea = &priv->cea; + struct omap_pcm_dma_data *dma_data; int err = 0;
+ dma_data = snd_soc_dai_get_dma_data(dai, substream); + switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: - priv->dma_params.packet_size = 16; + dma_data->packet_size = 16; break; case SNDRV_PCM_FORMAT_S24_LE: - priv->dma_params.packet_size = 32; + dma_data->packet_size = 32; break; default: dev_err(dai->dev, "format not supported!\n"); return -EINVAL; }
- priv->dma_params.data_type = 32; - - snd_soc_dai_set_dma_data(dai, substream, - &priv->dma_params); + dma_data->data_type = 32;
/* * fill the IEC-60958 channel status word diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index a230646..fef2f59 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c @@ -151,6 +151,9 @@ static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2); }
+ snd_soc_dai_set_dma_data(cpu_dai, substream, + &mcbsp->dma_data[substream->stream]); + return err; }
@@ -228,7 +231,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, int pkt_size = 0; unsigned int format, div, framesize, master;
- dma_data = &mcbsp->dma_data[substream->stream]; + dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream); channels = params_channels(params);
switch (params_format(params)) { @@ -277,8 +280,6 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
dma_data->packet_size = pkt_size;
- snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); - if (mcbsp->configured) { /* McBSP already configured by another stream */ return 0; diff --git a/sound/soc/omap/omap-mcpdm.c b/sound/soc/omap/omap-mcpdm.c index 84743d4..7755650 100644 --- a/sound/soc/omap/omap-mcpdm.c +++ b/sound/soc/omap/omap-mcpdm.c @@ -267,9 +267,11 @@ static int omap_mcpdm_dai_startup(struct snd_pcm_substream *substream, } omap_mcpdm_open_streams(mcpdm); } - mutex_unlock(&mcpdm->mutex);
+ snd_soc_dai_set_dma_data(dai, substream, + &omap_mcpdm_dai_dma_params[substream->stream]); + return 0; }
@@ -324,7 +326,7 @@ static int omap_mcpdm_dai_hw_params(struct snd_pcm_substream *substream, return -EINVAL; }
- dma_data = &omap_mcpdm_dai_dma_params[stream]; + dma_data = snd_soc_dai_get_dma_data(dai, substream);
/* Configure McPDM channels, and DMA packet size */ if (stream == SNDRV_PCM_STREAM_PLAYBACK) { @@ -336,8 +338,6 @@ static int omap_mcpdm_dai_hw_params(struct snd_pcm_substream *substream, dma_data->packet_size = mcpdm->up_threshold * channels; }
- snd_soc_dai_set_dma_data(dai, substream, dma_data); - return 0; }
Original author: Russell King rmk+kernel@arm.linux.org.uk
Switch the omap-pcm to use dmaengine. Certain features are not supported by after dmaengine conversion: 1. No period wakeup mode DMA engine has no way to communicate this information through standard channels.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com CC: Russell King rmk+kernel@arm.linux.org.uk --- sound/soc/omap/Kconfig | 3 +- sound/soc/omap/omap-pcm.c | 269 ++++++++++------------------------------------ 2 files changed, 61 insertions(+), 211 deletions(-)
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index 2c484a5..7048137 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 + depends on ARCH_OMAP && DMA_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 74da4b7..a2636f6 100644 --- a/sound/soc/omap/omap-pcm.c +++ b/sound/soc/omap/omap-pcm.c @@ -25,12 +25,13 @@ #include <linux/dma-mapping.h> #include <linux/slab.h> #include <linux/module.h> +#include <linux/omap-dma.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> +#include <sound/dmaengine_pcm.h> #include <sound/soc.h>
-#include <plat/dma.h> #include "omap-pcm.h"
static const struct snd_pcm_hardware omap_pcm_hardware = { @@ -49,61 +50,34 @@ static const struct snd_pcm_hardware omap_pcm_hardware = { .buffer_bytes_max = 128 * 1024, };
-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) +static int omap_pcm_get_dma_buswidth(int num_bits) { - 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); - } + int buswidth;
- snd_pcm_period_elapsed(substream); + switch (num_bits) { + case 16: + buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES; + break; + case 32: + buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES; + break; + default: + buswidth = -EINVAL; + break; + } + return buswidth; }
+ /* 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) { struct snd_pcm_runtime *runtime = substream->runtime; 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; int err = 0;
dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); @@ -116,195 +90,78 @@ static int omap_pcm_hw_params(struct snd_pcm_substream *substream, snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); runtime->dma_bytes = params_buffer_bytes(params);
- 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); - } - - return err; -} - -static int omap_pcm_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct omap_runtime_data *prtd = runtime->private_data; - - if (prtd->dma_data == NULL) - return 0; + chan = snd_dmaengine_pcm_get_chan(substream); + if (!chan) + return -EINVAL;
- omap_dma_unlink_lch(prtd->dma_ch, prtd->dma_ch); - omap_free_dma(prtd->dma_ch); - prtd->dma_data = NULL; + /* fills in addr_width and direction */ + err = snd_hwparams_to_dma_slave_config(substream, params, &config); + if (err) + return err;
- snd_pcm_set_runtime_buffer(substream, NULL); + /* Override the *_dma addr_width if requested by the DAI driver */ + if (dma_data->data_type) { + int buswidth = omap_pcm_get_dma_buswidth(dma_data->data_type);
- return 0; -} + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + config.dst_addr_width = buswidth; + else + config.src_addr_width = buswidth; + }
-static int omap_pcm_get_dma_type(int num_bits) -{ - int data_type; + 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;
- switch (num_bits) { - case 16: - data_type = OMAP_DMA_DATA_TYPE_S16; - break; - case 32: - data_type = OMAP_DMA_DATA_TYPE_S32; - break; - default: - data_type = -EINVAL; - break; - } - return data_type; + return dmaengine_slave_config(chan, &config); }
-static int omap_pcm_prepare(struct snd_pcm_substream *substream) +static int omap_pcm_hw_free(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)); - - if (dma_data->data_type) - dma_params.data_type = omap_pcm_get_dma_type( - dma_data->data_type); - else - dma_params.data_type = omap_pcm_get_dma_type( - snd_pcm_format_physical_width(runtime->format)); - - if (dma_params.data_type < 0) - return dma_params.data_type; - - dma_params.trigger = dma_data->dma_req; - - if (dma_data->packet_size) - dma_params.sync_mode = OMAP_DMA_SYNC_PACKET; - else - dma_params.sync_mode = OMAP_DMA_SYNC_ELEMENT; - - 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; - } - /* - * 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 - */ - bytes = snd_pcm_lib_period_bytes(substream); - dma_params.elem_count = bytes >> dma_params.data_type; - dma_params.frame_count = runtime->periods; - omap_set_dma_params(prtd->dma_ch, &dma_params); - - if ((cpu_is_omap1510())) - omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ | - OMAP_DMA_LAST_IRQ | OMAP_DMA_BLOCK_IRQ); - else if (!substream->runtime->no_period_wakeup) - omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ); - else { - /* - * No period wakeup: - * we need to disable BLOCK_IRQ, which is enabled by the omap - * dma core at request dma time. - */ - 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); - } - + snd_pcm_set_runtime_buffer(substream, NULL); return 0; }
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; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct omap_pcm_dma_data *dma_data; int ret = 0;
- spin_lock_irqsave(&prtd->lock, flags); + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + 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); - } else { - ptr = omap_get_dma_src_pos(prtd->dma_ch); - offset = bytes_to_frames(runtime, ptr - runtime->dma_addr); - } - - if (offset >= runtime->buffer_size) - offset = 0; + if (cpu_is_omap1510()) + offset = snd_dmaengine_pcm_pointer_no_residue(substream); + else + offset = snd_dmaengine_pcm_pointer(substream);
return offset; } @@ -312,7 +169,8 @@ static snd_pcm_uframes_t omap_pcm_pointer(struct snd_pcm_substream *substream) static int omap_pcm_open(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; - struct omap_runtime_data *prtd; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct omap_pcm_dma_data *dma_data; int ret;
snd_soc_set_runtime_hwparams(substream, &omap_pcm_hardware); @@ -321,25 +179,17 @@ static int omap_pcm_open(struct snd_pcm_substream *substream) ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); if (ret < 0) - goto out; - - prtd = kzalloc(sizeof(*prtd), GFP_KERNEL); - if (prtd == NULL) { - ret = -ENOMEM; - goto out; - } - spin_lock_init(&prtd->lock); - runtime->private_data = prtd; + return ret;
-out: + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + ret = snd_dmaengine_pcm_open(substream, omap_dma_filter_fn, + &dma_data->dma_req); return ret; }
static int omap_pcm_close(struct snd_pcm_substream *substream) { - struct snd_pcm_runtime *runtime = substream->runtime; - - kfree(runtime->private_data); + snd_dmaengine_pcm_close(substream); return 0; }
@@ -360,7 +210,6 @@ static struct snd_pcm_ops omap_pcm_ops = { .ioctl = snd_pcm_lib_ioctl, .hw_params = omap_pcm_hw_params, .hw_free = omap_pcm_hw_free, - .prepare = omap_pcm_prepare, .trigger = omap_pcm_trigger, .pointer = omap_pcm_pointer, .mmap = omap_pcm_mmap,
Dnia piątek, 14 września 2012 15:05:43 Peter Ujfalusi pisze:
Hello,
Changes since v2:
- As it has been discussed the no_wakeup parameter has been replaced with flags for the dmaengine APIs
Changes since v1:
- Support for pause/resume for OMAP audio via dmaengine
- dmaengine: support for NO_PERIOD_WAKEUP in cyclic mode
- OMAP to keep supporting NO_PERIOD_WAKEUP for audio
- Other plaforms can also try to enable this mode since we have now generic interface to do so.
This series will switch the OMAP audio to use dmaengine. The final patch which does the switch was based on Russell King's earlier patch.
The first 10 patch is to prepare the OMAP audio drivers for a smooth change to dmaengine:
- sDMA FRAME sync mode is removed and replaced with PACKET mode
- dai drivers no longer need to configure sDMA sync mode
- dai drivers does not need to specify the DMA word length - with the exception of the omap-hdmi driver which requires 32bit word length regardless of the audio format in use
- the McPDM driver used (to my surprise) hackish way of getting the DMA channel and address - via defines from some header files
After the conversion OMAP audio support should have the same features as before, no regressions expected.
I have tested the series on:
- BeagleBoard (audio via McBSP):
- aplay/arecord. In element mode and in threshold mode with different period sizes
- mplayer -ao alsa: for direct ALSA access
- mplayer -ao pulse: via PulseAudio to test NO_PERIOD_WAKEUP feature
- OMAP4 Blaze (audio via McPDM and DMIC)
- aplay/arecord
- mplayer -ao alsa: for direct ALSA access
- mplayer -ao pulse: via PulseAudio to test NO_PERIOD_WAKEUP feature
The patches has been generated against: git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-3.7
Janusz: Can you retest this series on OMAP1 to be sure I have not broken it?
Hi Peter, It looks like you haven't :-).
For OMAP1: Tested-by: Janusz Krzysztofik jkrzyszt@tis.icnet.pl
Thanks, Janusz
On Fri, 2012-09-14 at 15:05 +0300, Peter Ujfalusi wrote:
Hello,
dmaengine parts look good to me. How do you want to get this merged, dmaengine or ASoC tree?
Changes since v2:
- As it has been discussed the no_wakeup parameter has been replaced with flags for the dmaengine APIs
Changes since v1:
- Support for pause/resume for OMAP audio via dmaengine
- dmaengine: support for NO_PERIOD_WAKEUP in cyclic mode
- OMAP to keep supporting NO_PERIOD_WAKEUP for audio
- Other plaforms can also try to enable this mode since we have now generic interface to do so.
This series will switch the OMAP audio to use dmaengine. The final patch which does the switch was based on Russell King's earlier patch.
The first 10 patch is to prepare the OMAP audio drivers for a smooth change to dmaengine:
- sDMA FRAME sync mode is removed and replaced with PACKET mode
- dai drivers no longer need to configure sDMA sync mode
- dai drivers does not need to specify the DMA word length - with the exception of the omap-hdmi driver which requires 32bit word length regardless of the audio format in use
- the McPDM driver used (to my surprise) hackish way of getting the DMA channel and address - via defines from some header files
After the conversion OMAP audio support should have the same features as before, no regressions expected.
I have tested the series on:
- BeagleBoard (audio via McBSP):
- aplay/arecord. In element mode and in threshold mode with different period sizes
- mplayer -ao alsa: for direct ALSA access
- mplayer -ao pulse: via PulseAudio to test NO_PERIOD_WAKEUP feature
- OMAP4 Blaze (audio via McPDM and DMIC)
- aplay/arecord
- mplayer -ao alsa: for direct ALSA access
- mplayer -ao pulse: via PulseAudio to test NO_PERIOD_WAKEUP feature
The patches has been generated against: git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-3.7
Janusz: Can you retest this series on OMAP1 to be sure I have not broken it? Ricardo: Can you test the omap-hmdi if it is still working?
Regards, Peter
Peter Ujfalusi (15): dmaengine: omap: Support for element mode in cyclic DMA dmaengine: omap: Add support for pause/resume in cyclic dma mode dmaengine: Add flags parameter to dmaengine_prep_dma_cyclic() dmaengine: Pass flags via device_prep_dma_cyclic() callback dmaengine: omap-dma: Add support to suppress interrupts in cyclic mode ASoC: omap-mcbsp: Use sDMA packet mode instead of frame mode ASoC: omap-pcm: Select sDMA synchronization based on packet_size ASoC: OMAP: Remove sync_mode from omap_pcm_dma_data struct ASoC: omap-pcm: Prepare to configure the DMA data_type based on stream properties ARM: OMAP4: hwmod_data: Add resource names to McPDM memory ranges ASoC: omap-mcpdm: Use platform_get_resource_* to get resources ASoC: OMAP: mcbsp, mcpdm, dmic: Let omap-pcm to pick the dma_type ASoC: omap-pcm, omap-dmic: Change the use of omap_pcm_dma_data->data_type ASoC: OMAP: mcbsp, mcpdm, dmic, hdmi: Set dma_data at startup time ASoC: omap-pcm: Convert to use dmaengine
arch/arm/mach-omap2/omap_hwmod_44xx_data.c | 2 + drivers/dma/at_hdmac.c | 3 +- drivers/dma/ep93xx_dma.c | 4 +- drivers/dma/imx-dma.c | 2 +- drivers/dma/imx-sdma.c | 2 +- drivers/dma/mmp_tdma.c | 2 +- drivers/dma/mxs-dma.c | 2 +- drivers/dma/omap-dma.c | 45 ++++-- drivers/dma/pl330.c | 2 +- drivers/dma/sa11x0-dma.c | 2 +- drivers/dma/sirf-dma.c | 2 +- drivers/dma/ste_dma40.c | 3 +- drivers/dma/tegra20-apb-dma.c | 2 +- include/linux/dmaengine.h | 7 +- sound/soc/omap/Kconfig | 3 +- sound/soc/omap/omap-dmic.c | 9 +- sound/soc/omap/omap-hdmi.c | 17 ++- sound/soc/omap/omap-mcbsp.c | 60 +++----- sound/soc/omap/omap-mcpdm.c | 40 +++-- sound/soc/omap/omap-pcm.c | 236 ++++++++--------------------- sound/soc/omap/omap-pcm.h | 4 +- sound/soc/soc-dmaengine-pcm.c | 6 +- 22 files changed, 186 insertions(+), 269 deletions(-)
Hi Vinod,
On 09/17/2012 06:17 AM, Vinod Koul wrote:
On Fri, 2012-09-14 at 15:05 +0300, Peter Ujfalusi wrote:
Hello,
dmaengine parts look good to me. How do you want to get this merged, dmaengine or ASoC tree?
Thank you, I would prefer it to go via ASoC if it is not a problem. It is possible to separate the dmaengine core related patches and the patches for the OMAP drivers but I think it is better to keep them together so we can have them in a same tree for testing it further.
Regards, Péter
On Mon, 2012-09-17 at 11:44 +0300, Peter Ujfalusi wrote:
Hi Vinod,
On 09/17/2012 06:17 AM, Vinod Koul wrote:
On Fri, 2012-09-14 at 15:05 +0300, Peter Ujfalusi wrote:
Hello,
dmaengine parts look good to me. How do you want to get this merged, dmaengine or ASoC tree?
Thank you, I would prefer it to go via ASoC if it is not a problem. It is possible to separate the dmaengine core related patches and the patches for the OMAP drivers but I think it is better to keep them together so we can have them in a same tree for testing it further.
Sure I think this is better way to do.
Hi Peter, Mark,
On 09/14/2012 07:05 AM, Peter Ujfalusi wrote:
Hello,
Changes since v2:
- As it has been discussed the no_wakeup parameter has been replaced with flags for the dmaengine APIs
Changes since v1:
- Support for pause/resume for OMAP audio via dmaengine
- dmaengine: support for NO_PERIOD_WAKEUP in cyclic mode
- OMAP to keep supporting NO_PERIOD_WAKEUP for audio
- Other plaforms can also try to enable this mode since we have now generic interface to do so.
This series will switch the OMAP audio to use dmaengine. The final patch which does the switch was based on Russell King's earlier patch.
The first 10 patch is to prepare the OMAP audio drivers for a smooth change to dmaengine:
- sDMA FRAME sync mode is removed and replaced with PACKET mode
- dai drivers no longer need to configure sDMA sync mode
- dai drivers does not need to specify the DMA word length - with the exception of the omap-hdmi driver which requires 32bit word length regardless of the audio format in use
- the McPDM driver used (to my surprise) hackish way of getting the DMA channel and address - via defines from some header files
After the conversion OMAP audio support should have the same features as before, no regressions expected.
I have tested the series on:
- BeagleBoard (audio via McBSP):
- aplay/arecord. In element mode and in threshold mode with different period sizes
- mplayer -ao alsa: for direct ALSA access
- mplayer -ao pulse: via PulseAudio to test NO_PERIOD_WAKEUP feature
- OMAP4 Blaze (audio via McPDM and DMIC)
- aplay/arecord
- mplayer -ao alsa: for direct ALSA access
- mplayer -ao pulse: via PulseAudio to test NO_PERIOD_WAKEUP feature
The patches has been generated against: git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-3.7
Janusz: Can you retest this series on OMAP1 to be sure I have not broken it? Ricardo: Can you test the omap-hmdi if it is still working?
I tested these changes from Mark's for-3.7 branch. HDMI audio is working fine after applying patch 03. Such patch is missing in Mark's branch and this causes a build break with omap2plus_defconfig. Not sure if this is expected, Most likely not.
Tested-by: Ricardo Neri ricardo.neri@ti.com
Ricardo
participants (8)
-
Janusz Krzysztofik
-
Mark Brown
-
Nicolas Ferre
-
Peter Ujfalusi
-
Ricardo Neri
-
Russell King - ARM Linux
-
Shawn Guo
-
Vinod Koul