[alsa-devel] [PATCH 00/17] ASoC: Towards a generic dmaengine PCM driver
Hi,
This series adds a (almost) generic dmaengine PCM driver for ASoC. The tegra, spear, omap, imx, mxs, ep93xx and ux500 platforms are converted to use the generic driver instead of their own custom implementation. The driver builds on top of the dmaengine PCM library and adds the missing bits. The driver was primarily built for handling devicetree based platforms, but the series also adds support for compat platforms which don't use devicetree yet or have to support non-devicetree instantiation for backwards compatibility. Since non of the converted platforms have devicetree support yet (at least not using the generic DMA bindings) this series doesn't enable devicetree support for them either. But enabling support for them is very easy and can be done by removing the SND_DMAENGINE_PCM_NO_DT flag for these platforms.
For platforms using devicetree the DMA channels are requested early on before the ASoC platform driver is registered. This allows us, at least in theory, to use probe deferral if the DMA driver hasn't been probed yet. Unfortunately of_dma_request_slave_channel either returns a valid channel or NULL, so we can't distinguish between the case where there is no DMA channel assigned and the case where the DMA driver hasn't been probed yet. I plan to submit a patch which lets of_dma_request_slave_channel return a ERR_PTR, but for now the generic PCM dmaengine driver assumes that the DMA driver will always be probed before it. For the compat case the DMA channel gets requested from within the PCM driver's pcm_new callback. This requires some minor changes to some of the DAI drivers moving the initialization of the DAI DMA data pointer from the DAI startup callback to the DAI probe callback.
Unfortunately the driver is not fully generic yet and we still need some platform specific data to make it work, but the plan is to eventually make the driver fully generic in the future. The platform specific bits currently are: * The snd_pcm_hardware struct which contains device specific parameters like the maximum period size and maximum number of periods as well as whether the driver supports pause and resume. The idea is to get rid of the per platform snd_pcm_hardware struct by extending the dmaengine API to a point where we can query all necessary information from the dmaengine driver to fill in the snd_pcm_hardware struct at runtime. * The size of the pre-allocated audio buffer. All platforms seem use different sizes and since some of them might be sensitive to changes to the buffer size this series keeps the original size for now. But we should probably try to decide on a sane default for this and maybe allow ASoC board drivers to overwrite it somehow. * A callback to fill in the dma_slave_config. Currently this is either NULL (for platforms which don't need to configure the DMA channel at runtime) or uses the generic snd_dmaengine_pcm_prepare_slave_config(), which uses snd_hwparams_to_dma_slave_config() and snd_dmaengine_pcm_set_config_from_dai_data(). The later requires the platform to use the generic DMA DAI data struct though, hopefully we'll be able to convert all platforms to use this generic struct, and once that's done we can always use snd_dmaengine_pcm_prepare_slave_config() unconditionally. * For platforms not using devicetree platforms a callback to request the dma channel. This can either be a completely custom function which returns the channel or a filter function which will be passed to dma_request_channel. The route forward here probably is to let the DAI driver provide the filter function or implement a table based DMA channel lookup like we have for other subsystems (like clk or regulator) where the board file provides a mapping based on the device name.
Platforms which use the dmaengine pcm library but are not converted to the generic driver yet are atmel and mmp. mmp use a custom genalloc based memory allocator, we may add support for this to the generic driver at a later point. The atmel PCM driver is very interlocked with atmel SSC DAI driver, it'll probably require some serious refactoring before it can use the generic dmaengine PCM driver.
The driver uses snd_pcm_lib_preallocate_pages to preallocate the memory buffer. snd_pcm_lib_preallocate_pages internally uses dma_alloc_coherent, while most of the platforms converted in this series use dma_alloc_writecombine. Laxman Dewangan submitted a patch[1] a couple of months ago to add support for dma_alloc_writecombine to snd_pcm_lib_preallocate_pages, I always thought the patch had been merged, but it looks like I was mistaken. So if there are concerns about performance regression due to using coherent instead of writecombine mappings we'll have to postpone converting these platforms until Laxman's patch has been merged.
The compat path of this series hasn't been runtime tested yet and neither have the platforms which are converted in this series. I probably broke one or two of them by accident, so please test the patches.
Thanks, - Lars
[1] https://lkml.org/lkml/2012/7/2/94
Lars-Peter Clausen (17): ASoC: dmaengine-pcm: Make requesting the DMA channel at PCM open optional ASoC: Add snd_soc_{add,remove}_platform ASoC: Add a generic dmaengine_pcm driver ASoC: dmaengine_pcm: Add support for compat platforms ASoC: tegra: Use generic dmaengine PCM ASoC: spear: Setup dma data in DAI probe ASoC: spear: Use generic dmaengine PCM ASoC: dmaengine-pcm: Add support for platforms which can't report residue ASoC: mxs: Setup dma data in DAI probe ASoC: mxs: Use generic dmaengine PCM ASoC: imx: Setup dma data in DAI probe ASoC: imx: Use generic dmaengine PCM ASoC: omap: Setup dma data in DAI probe ASoC: omap: Use generic dmaengine PCM ASoC: ep93xx: Setup dma data in DAI probe ASoC: ep93xx: Use generic dmaengine PCM ASoC: ux500: Use generic dmaengine PCM
include/sound/dmaengine_pcm.h | 65 +++++++- include/sound/soc.h | 4 + sound/soc/Kconfig | 4 + sound/soc/Makefile | 4 + sound/soc/atmel/atmel-pcm-dma.c | 6 +- sound/soc/cirrus/Kconfig | 2 +- sound/soc/cirrus/ep93xx-ac97.c | 14 +- sound/soc/cirrus/ep93xx-i2s.c | 14 +- sound/soc/cirrus/ep93xx-pcm.c | 137 ++-------------- sound/soc/fsl/Kconfig | 2 +- sound/soc/fsl/fsl_ssi.c | 19 ++- sound/soc/fsl/imx-pcm-dma.c | 76 ++------- sound/soc/fsl/imx-pcm.c | 6 +- sound/soc/fsl/imx-pcm.h | 5 + sound/soc/fsl/imx-ssi.c | 22 +-- sound/soc/mxs/Kconfig | 2 +- sound/soc/mxs/mxs-pcm.c | 135 ++-------------- sound/soc/mxs/mxs-saif.c | 3 +- sound/soc/omap/Kconfig | 2 +- sound/soc/omap/omap-dmic.c | 4 +- sound/soc/omap/omap-hdmi.c | 12 +- sound/soc/omap/omap-mcbsp.c | 6 +- sound/soc/omap/omap-mcpdm.c | 7 +- sound/soc/omap/omap-pcm.c | 186 ++-------------------- sound/soc/pxa/mmp-pcm.c | 5 +- sound/soc/soc-core.c | 85 +++++++--- sound/soc/soc-dmaengine-pcm.c | 82 +++++++--- sound/soc/soc-generic-dmaengine-pcm.c | 284 ++++++++++++++++++++++++++++++++++ sound/soc/spear/spdif_in.c | 12 +- sound/soc/spear/spdif_out.c | 7 +- sound/soc/spear/spear_pcm.c | 151 ++---------------- sound/soc/tegra/Kconfig | 2 +- sound/soc/tegra/tegra_pcm.c | 171 ++------------------ sound/soc/ux500/Kconfig | 2 +- sound/soc/ux500/ux500_pcm.c | 159 ++----------------- 35 files changed, 651 insertions(+), 1046 deletions(-) create mode 100644 sound/soc/soc-generic-dmaengine-pcm.c
Refactor the dmaengine PCM library to allow the DMA channel to be requested before opening a PCM substream. snd_dmaengine_pcm_open() now expects a DMA channel instead of a filter function and filter parameter as its parameters. snd_dmaengine_pcm_close() is updated to not release the DMA channel. This allows a dmaengine based PCM driver to request its channels before the substream is opened.
The patch also introduces two new functions, snd_dmaengine_pcm_open_request_chan() and snd_dmaengine_pcm_close_release_chan(), which have the same signature and behaviour of the old snd_dmaengine_pcm_{open,close}() and internally use the new variants of these functions. All users of snd_dmaengine_pcm_{open,close}() are updated to use snd_dmaengine_pcm_open_request_chan() and snd_dmaengine_pcm_close_release_chan().
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- include/sound/dmaengine_pcm.h | 6 +++- sound/soc/atmel/atmel-pcm-dma.c | 6 ++-- sound/soc/cirrus/ep93xx-pcm.c | 5 +-- sound/soc/fsl/imx-pcm-dma.c | 2 +- sound/soc/mxs/mxs-pcm.c | 4 +-- sound/soc/omap/omap-pcm.c | 7 ++-- sound/soc/pxa/mmp-pcm.c | 5 +-- sound/soc/soc-dmaengine-pcm.c | 72 ++++++++++++++++++++++++++++------------- sound/soc/spear/spear_pcm.c | 5 +-- sound/soc/tegra/tegra_pcm.c | 4 +-- sound/soc/ux500/ux500_pcm.c | 6 ++-- 11 files changed, 78 insertions(+), 44 deletions(-)
diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h index 9562042..d015d67 100644 --- a/include/sound/dmaengine_pcm.h +++ b/include/sound/dmaengine_pcm.h @@ -39,9 +39,13 @@ snd_pcm_uframes_t snd_dmaengine_pcm_pointer(struct snd_pcm_substream *substream) snd_pcm_uframes_t snd_dmaengine_pcm_pointer_no_residue(struct snd_pcm_substream *substream);
int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream, - dma_filter_fn filter_fn, void *filter_data); + struct dma_chan *chan); int snd_dmaengine_pcm_close(struct snd_pcm_substream *substream);
+int snd_dmaengine_pcm_open_request_chan(struct snd_pcm_substream *substream, + dma_filter_fn filter_fn, void *filter_data); +int snd_dmaengine_pcm_close_release_chan(struct snd_pcm_substream *substream); + struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream);
/** diff --git a/sound/soc/atmel/atmel-pcm-dma.c b/sound/soc/atmel/atmel-pcm-dma.c index bb07989..1d38fd0 100644 --- a/sound/soc/atmel/atmel-pcm-dma.c +++ b/sound/soc/atmel/atmel-pcm-dma.c @@ -155,7 +155,7 @@ static int atmel_pcm_hw_params(struct snd_pcm_substream *substream, if (ssc->pdev) sdata = ssc->pdev->dev.platform_data;
- ret = snd_dmaengine_pcm_open(substream, filter, sdata); + ret = snd_dmaengine_pcm_open_request_chan(substream, filter, sdata); if (ret) { pr_err("atmel-pcm: dmaengine pcm open failed\n"); return -EINVAL; @@ -171,7 +171,7 @@ static int atmel_pcm_hw_params(struct snd_pcm_substream *substream,
return 0; err: - snd_dmaengine_pcm_close(substream); + snd_dmaengine_pcm_close_release_chan(substream); return ret; }
@@ -197,7 +197,7 @@ static int atmel_pcm_open(struct snd_pcm_substream *substream)
static struct snd_pcm_ops atmel_pcm_ops = { .open = atmel_pcm_open, - .close = snd_dmaengine_pcm_close, + .close = snd_dmaengine_pcm_close_release_chan, .ioctl = snd_pcm_lib_ioctl, .hw_params = atmel_pcm_hw_params, .prepare = atmel_pcm_dma_prepare, diff --git a/sound/soc/cirrus/ep93xx-pcm.c b/sound/soc/cirrus/ep93xx-pcm.c index 298946f..4880326 100644 --- a/sound/soc/cirrus/ep93xx-pcm.c +++ b/sound/soc/cirrus/ep93xx-pcm.c @@ -69,7 +69,8 @@ static int ep93xx_pcm_open(struct snd_pcm_substream *substream)
snd_soc_set_runtime_hwparams(substream, &ep93xx_pcm_hardware);
- return snd_dmaengine_pcm_open(substream, ep93xx_pcm_dma_filter, + return snd_dmaengine_pcm_open_request_chan(substream, + ep93xx_pcm_dma_filter, snd_soc_dai_get_dma_data(rtd->cpu_dai, substream)); }
@@ -100,7 +101,7 @@ static int ep93xx_pcm_mmap(struct snd_pcm_substream *substream,
static struct snd_pcm_ops ep93xx_pcm_ops = { .open = ep93xx_pcm_open, - .close = snd_dmaengine_pcm_close, + .close = snd_dmaengine_pcm_close_release_chan, .ioctl = snd_pcm_lib_ioctl, .hw_params = ep93xx_pcm_hw_params, .hw_free = ep93xx_pcm_hw_free, diff --git a/sound/soc/fsl/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c index ee838c8..c664782 100644 --- a/sound/soc/fsl/imx-pcm-dma.c +++ b/sound/soc/fsl/imx-pcm-dma.c @@ -100,7 +100,7 @@ static int snd_imx_open(struct snd_pcm_substream *substream)
static struct snd_pcm_ops imx_pcm_ops = { .open = snd_imx_open, - .close = snd_dmaengine_pcm_close, + .close = snd_dmaengine_pcm_close_release_chan, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_imx_pcm_hw_params, .trigger = snd_dmaengine_pcm_trigger, diff --git a/sound/soc/mxs/mxs-pcm.c b/sound/soc/mxs/mxs-pcm.c index ebbef85..7bceb16 100644 --- a/sound/soc/mxs/mxs-pcm.c +++ b/sound/soc/mxs/mxs-pcm.c @@ -87,7 +87,7 @@ static int snd_mxs_open(struct snd_pcm_substream *substream)
snd_soc_set_runtime_hwparams(substream, &snd_mxs_hardware);
- return snd_dmaengine_pcm_open(substream, filter, + return snd_dmaengine_pcm_open_request_chan(substream, filter, snd_soc_dai_get_dma_data(rtd->cpu_dai, substream)); }
@@ -104,7 +104,7 @@ static int snd_mxs_pcm_mmap(struct snd_pcm_substream *substream,
static struct snd_pcm_ops mxs_pcm_ops = { .open = snd_mxs_open, - .close = snd_dmaengine_pcm_close, + .close = snd_dmaengine_pcm_close_release_chan, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_mxs_pcm_hw_params, .trigger = snd_dmaengine_pcm_trigger, diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c index c8e272f..c28e042 100644 --- a/sound/soc/omap/omap-pcm.c +++ b/sound/soc/omap/omap-pcm.c @@ -118,8 +118,9 @@ static int omap_pcm_open(struct snd_pcm_substream *substream)
dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
- return snd_dmaengine_pcm_open(substream, omap_dma_filter_fn, - dma_data->filter_data); + return snd_dmaengine_pcm_open_request_chan(substream, + omap_dma_filter_fn, + dma_data->filter_data); }
static int omap_pcm_mmap(struct snd_pcm_substream *substream, @@ -135,7 +136,7 @@ static int omap_pcm_mmap(struct snd_pcm_substream *substream,
static struct snd_pcm_ops omap_pcm_ops = { .open = omap_pcm_open, - .close = snd_dmaengine_pcm_close, + .close = snd_dmaengine_pcm_close_release_chan, .ioctl = snd_pcm_lib_ioctl, .hw_params = omap_pcm_hw_params, .hw_free = omap_pcm_hw_free, diff --git a/sound/soc/pxa/mmp-pcm.c b/sound/soc/pxa/mmp-pcm.c index 6c39802..3499300 100644 --- a/sound/soc/pxa/mmp-pcm.c +++ b/sound/soc/pxa/mmp-pcm.c @@ -131,7 +131,8 @@ static int mmp_pcm_open(struct snd_pcm_substream *substream) dma_data.dma_res = r; dma_data.ssp_id = cpu_dai->id;
- return snd_dmaengine_pcm_open(substream, filter, &dma_data); + return snd_dmaengine_pcm_open_request_chan(substream, filter, + &dma_data); }
static int mmp_pcm_mmap(struct snd_pcm_substream *substream, @@ -148,7 +149,7 @@ static int mmp_pcm_mmap(struct snd_pcm_substream *substream,
struct snd_pcm_ops mmp_pcm_ops = { .open = mmp_pcm_open, - .close = snd_dmaengine_pcm_close, + .close = snd_dmaengine_pcm_close_release_chan, .ioctl = snd_pcm_lib_ioctl, .hw_params = mmp_pcm_hw_params, .trigger = snd_dmaengine_pcm_trigger, diff --git a/sound/soc/soc-dmaengine-pcm.c b/sound/soc/soc-dmaengine-pcm.c index a9a300a..b0420a7 100644 --- a/sound/soc/soc-dmaengine-pcm.c +++ b/sound/soc/soc-dmaengine-pcm.c @@ -254,44 +254,38 @@ snd_pcm_uframes_t snd_dmaengine_pcm_pointer(struct snd_pcm_substream *substream) } EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer);
-static int dmaengine_pcm_request_channel(struct dmaengine_pcm_runtime_data *prtd, - dma_filter_fn filter_fn, void *filter_data) +static struct dma_chan *dmaengine_pcm_request_channel(dma_filter_fn filter_fn, + void *filter_data) { dma_cap_mask_t mask;
dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); dma_cap_set(DMA_CYCLIC, mask); - prtd->dma_chan = dma_request_channel(mask, filter_fn, filter_data); - - if (!prtd->dma_chan) - return -ENXIO;
- return 0; + return dma_request_channel(mask, filter_fn, filter_data); }
/** * snd_dmaengine_pcm_open - Open a dmaengine based PCM substream * @substream: PCM substream - * @filter_fn: Filter function used to request the DMA channel - * @filter_data: Data passed to the DMA filter function + * @chan: DMA channel to use for data transfers * * Returns 0 on success, a negative error code otherwise. * - * This function will request a DMA channel using the passed filter function and - * data. The function should usually be called from the pcm open callback. - * - * Note that this function will use private_data field of the substream's - * runtime. So it is not availabe to your pcm driver implementation. If you need - * to keep additional data attached to a substream use - * snd_dmaengine_pcm_{set,get}_data. + * The function should usually be called from the pcm open callback. Note that + * this function will use private_data field of the substream's runtime. So it + * is not availabe to your pcm driver implementation. */ int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream, - dma_filter_fn filter_fn, void *filter_data) + struct dma_chan *chan) { struct dmaengine_pcm_runtime_data *prtd; int ret;
+ if (!chan) + return -ENXIO; + ret = snd_pcm_hw_constraint_integer(substream->runtime, SNDRV_PCM_HW_PARAM_PERIODS); if (ret < 0) @@ -301,11 +295,7 @@ int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream, if (!prtd) return -ENOMEM;
- ret = dmaengine_pcm_request_channel(prtd, filter_fn, filter_data); - if (ret < 0) { - kfree(prtd); - return ret; - } + prtd->dma_chan = chan;
substream->runtime->private_data = prtd;
@@ -314,6 +304,27 @@ int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream, EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open);
/** + * snd_dmaengine_pcm_open_request_chan - Open a dmaengine based PCM substream and request channel + * @substream: PCM substream + * @filter_fn: Filter function used to request the DMA channel + * @filter_data: Data passed to the DMA filter function + * + * Returns 0 on success, a negative error code otherwise. + * + * This function will request a DMA channel using the passed filter function and + * data. The function should usually be called from the pcm open callback. Note + * that this function will use private_data field of the substream's runtime. So + * it is not availabe to your pcm driver implementation. + */ +int snd_dmaengine_pcm_open_request_chan(struct snd_pcm_substream *substream, + dma_filter_fn filter_fn, void *filter_data) +{ + return snd_dmaengine_pcm_open(substream, + dmaengine_pcm_request_channel(filter_fn, filter_data)); +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open_request_chan); + +/** * snd_dmaengine_pcm_close - Close a dmaengine based PCM substream * @substream: PCM substream */ @@ -321,11 +332,26 @@ int snd_dmaengine_pcm_close(struct snd_pcm_substream *substream) { struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
- dma_release_channel(prtd->dma_chan); kfree(prtd);
return 0; } EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close);
+/** + * snd_dmaengine_pcm_release_chan_close - Close a dmaengine based PCM substream and release channel + * @substream: PCM substream + * + * Releases the DMA channel associated with the PCM substream. + */ +int snd_dmaengine_pcm_close_release_chan(struct snd_pcm_substream *substream) +{ + struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); + + dma_release_channel(prtd->dma_chan); + + return snd_dmaengine_pcm_close(substream); +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close_release_chan); + MODULE_LICENSE("GPL"); diff --git a/sound/soc/spear/spear_pcm.c b/sound/soc/spear/spear_pcm.c index bfbcc1f..2fbd489 100644 --- a/sound/soc/spear/spear_pcm.c +++ b/sound/soc/spear/spear_pcm.c @@ -64,7 +64,8 @@ static int spear_pcm_open(struct snd_pcm_substream *substream) if (ret) return ret;
- return snd_dmaengine_pcm_open(substream, dma_data->filter, dma_data) + return snd_dmaengine_pcm_open_request_chan(substream, dma_data->filter, + dma_data); }
static int spear_pcm_mmap(struct snd_pcm_substream *substream, @@ -79,7 +80,7 @@ static int spear_pcm_mmap(struct snd_pcm_substream *substream,
static struct snd_pcm_ops spear_pcm_ops = { .open = spear_pcm_open, - .close = snd_dmaengine_pcm_close, + .close = snd_dmaengine_pcm_close_release_chan, .ioctl = snd_pcm_lib_ioctl, .hw_params = spear_pcm_hw_params, .hw_free = spear_pcm_hw_free, diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c index 6d1c70c..f9f247c 100644 --- a/sound/soc/tegra/tegra_pcm.c +++ b/sound/soc/tegra/tegra_pcm.c @@ -64,7 +64,7 @@ static int tegra_pcm_open(struct snd_pcm_substream *substream) /* Set HW params now that initialization is complete */ snd_soc_set_runtime_hwparams(substream, &tegra_pcm_hardware);
- ret = snd_dmaengine_pcm_open(substream, NULL, NULL); + ret = snd_dmaengine_pcm_open_request_chan(substream, NULL, NULL); if (ret) { dev_err(dev, "dmaengine pcm open failed with err %d\n", ret); return ret; @@ -122,7 +122,7 @@ static int tegra_pcm_mmap(struct snd_pcm_substream *substream,
static struct snd_pcm_ops tegra_pcm_ops = { .open = tegra_pcm_open, - .close = snd_dmaengine_pcm_close, + .close = snd_dmaengine_pcm_close_release_chan, .ioctl = snd_pcm_lib_ioctl, .hw_params = tegra_pcm_hw_params, .hw_free = tegra_pcm_hw_free, diff --git a/sound/soc/ux500/ux500_pcm.c b/sound/soc/ux500/ux500_pcm.c index 09b5364..a7d4f04 100644 --- a/sound/soc/ux500/ux500_pcm.c +++ b/sound/soc/ux500/ux500_pcm.c @@ -125,8 +125,8 @@ static int ux500_pcm_open(struct snd_pcm_substream *substream) dma_cfg->dst_info.data_width = mem_data_width; }
- - ret = snd_dmaengine_pcm_open(substream, stedma40_filter, dma_cfg); + ret = snd_dmaengine_pcm_open_request_chan(substream, stedma40_filter, + dma_cfg); if (ret) { dev_dbg(dai->dev, "%s: ERROR: snd_dmaengine_pcm_open failed (%d)!\n", @@ -211,7 +211,7 @@ static int ux500_pcm_mmap(struct snd_pcm_substream *substream,
static struct snd_pcm_ops ux500_pcm_ops = { .open = ux500_pcm_open, - .close = snd_dmaengine_pcm_close, + .close = snd_dmaengine_pcm_close_release_chan, .ioctl = snd_pcm_lib_ioctl, .hw_params = ux500_pcm_hw_params, .hw_free = ux500_pcm_hw_free,
On Mon, Apr 15, 2013 at 07:19:48PM +0200, Lars-Peter Clausen wrote:
Refactor the dmaengine PCM library to allow the DMA channel to be requested before opening a PCM substream. snd_dmaengine_pcm_open() now expects a DMA channel instead of a filter function and filter parameter as its parameters. snd_dmaengine_pcm_close() is updated to not release the DMA channel. This allows a dmaengine based PCM driver to request its channels before the substream is opened.
Applied, thanks.
snd_soc_{add,remove}_platform are similar to snd_soc_register_platform and snd_soc_unregister_platform with the difference that they won't allocate and free the snd_soc_platform structure.
Also add snd_soc_lookup_platform which looks up a platform by the device it has been registered for.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- include/sound/soc.h | 4 +++ sound/soc/soc-core.c | 85 ++++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 66 insertions(+), 23 deletions(-)
diff --git a/include/sound/soc.h b/include/sound/soc.h index 9eb0e4d..85c1522 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -375,6 +375,10 @@ int snd_soc_poweroff(struct device *dev); int snd_soc_register_platform(struct device *dev, const struct snd_soc_platform_driver *platform_drv); void snd_soc_unregister_platform(struct device *dev); +int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform, + const struct snd_soc_platform_driver *platform_drv); +void snd_soc_remove_platform(struct snd_soc_platform *platform); +struct snd_soc_platform *snd_soc_lookup_platform(struct device *dev); int snd_soc_register_codec(struct device *dev, const struct snd_soc_codec_driver *codec_drv, struct snd_soc_dai_driver *dai_drv, int num_dai); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 7bf21a1..d56bbea 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3900,21 +3900,14 @@ static void snd_soc_unregister_dais(struct device *dev, size_t count) }
/** - * snd_soc_register_platform - Register a platform with the ASoC core - * - * @platform: platform to register + * snd_soc_add_platform - Add a platform to the ASoC core + * @dev: The parent device for the platform + * @platform: The platform to add + * @platform_driver: The driver for the platform */ -int snd_soc_register_platform(struct device *dev, +int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform, const struct snd_soc_platform_driver *platform_drv) { - struct snd_soc_platform *platform; - - dev_dbg(dev, "ASoC: platform register %s\n", dev_name(dev)); - - platform = kzalloc(sizeof(struct snd_soc_platform), GFP_KERNEL); - if (platform == NULL) - return -ENOMEM; - /* create platform component name */ platform->name = fmt_single_name(dev, &platform->id); if (platform->name == NULL) { @@ -3937,30 +3930,76 @@ int snd_soc_register_platform(struct device *dev,
return 0; } -EXPORT_SYMBOL_GPL(snd_soc_register_platform); +EXPORT_SYMBOL_GPL(snd_soc_add_platform);
/** - * snd_soc_unregister_platform - Unregister a platform from the ASoC core + * snd_soc_register_platform - Register a platform with the ASoC core * - * @platform: platform to unregister + * @platform: platform to register */ -void snd_soc_unregister_platform(struct device *dev) +int snd_soc_register_platform(struct device *dev, + const struct snd_soc_platform_driver *platform_drv) { struct snd_soc_platform *platform; + int ret;
- list_for_each_entry(platform, &platform_list, list) { - if (dev == platform->dev) - goto found; - } - return; + dev_dbg(dev, "ASoC: platform register %s\n", dev_name(dev));
-found: + platform = kzalloc(sizeof(struct snd_soc_platform), GFP_KERNEL); + if (platform == NULL) + return -ENOMEM; + + ret = snd_soc_add_platform(dev, platform, platform_drv); + if (ret) + kfree(platform); + + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_register_platform); + +/** + * snd_soc_remove_platform - Remove a platform from the ASoC core + * @platform: the platform to remove + */ +void snd_soc_remove_platform(struct snd_soc_platform *platform) +{ mutex_lock(&client_mutex); list_del(&platform->list); mutex_unlock(&client_mutex);
- dev_dbg(dev, "ASoC: Unregistered platform '%s'\n", platform->name); + dev_dbg(platform->dev, "ASoC: Unregistered platform '%s'\n", + platform->name); kfree(platform->name); +} +EXPORT_SYMBOL_GPL(snd_soc_remove_platform); + +struct snd_soc_platform *snd_soc_lookup_platform(struct device *dev) +{ + struct snd_soc_platform *platform; + + list_for_each_entry(platform, &platform_list, list) { + if (dev == platform->dev) + return platform; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(snd_soc_lookup_platform); + +/** + * snd_soc_unregister_platform - Unregister a platform from the ASoC core + * + * @platform: platform to unregister + */ +void snd_soc_unregister_platform(struct device *dev) +{ + struct snd_soc_platform *platform; + + platform = snd_soc_lookup_platform(dev); + if (!platform) + return; + + snd_soc_remove_platform(platform); kfree(platform); } EXPORT_SYMBOL_GPL(snd_soc_unregister_platform);
On Mon, Apr 15, 2013 at 07:19:49PM +0200, Lars-Peter Clausen wrote:
snd_soc_{add,remove}_platform are similar to snd_soc_register_platform and snd_soc_unregister_platform with the difference that they won't allocate and free the snd_soc_platform structure.
Also add snd_soc_lookup_platform which looks up a platform by the device it has been registered for.
Applied, thanks.
On Mon, Apr 15, 2013 at 07:19:49PM +0200, Lars-Peter Clausen wrote:
snd_soc_{add,remove}_platform are similar to snd_soc_register_platform and snd_soc_unregister_platform with the difference that they won't allocate and free the snd_soc_platform structure.
Applied, thanks. Seems like we're going to get a bit of refactoring still but I'd rather get this in for the merge window.
This patch adds a generic dmaengine PCM driver. It builds on top of the dmaengine PCM library and adds the missing pieces like DMA channel management, buffer management and channel configuration. It will be able to replace the majority of the existing platform specific dmaengine based PCM drivers. Devicetree is used to map the DMA channels to the PCM device.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- include/sound/dmaengine_pcm.h | 25 ++++ sound/soc/Kconfig | 4 + sound/soc/Makefile | 4 + sound/soc/soc-generic-dmaengine-pcm.c | 241 ++++++++++++++++++++++++++++++++++ 4 files changed, 274 insertions(+) create mode 100644 sound/soc/soc-generic-dmaengine-pcm.c
diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h index d015d67..e0bf24e 100644 --- a/include/sound/dmaengine_pcm.h +++ b/include/sound/dmaengine_pcm.h @@ -72,4 +72,29 @@ void snd_dmaengine_pcm_set_config_from_dai_data( const struct snd_dmaengine_dai_dma_data *dma_data, struct dma_slave_config *config);
+/** + * struct snd_dmaengine_pcm_config - Configuration data for dmaengine based PCM + * @prepare_slave_config: Callback used to fill in the DMA slave_config for a + * PCM substream. Will be called from the PCM drivers hwparams callback. + * @pcm_hardware: snd_pcm_hardware struct to be used for the PCM. + * @prealloc_buffer_size: Size of the preallocated audio buffer. + */ +struct snd_dmaengine_pcm_config { + int (*prepare_slave_config)(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct dma_slave_config *slave_config); + + const struct snd_pcm_hardware *pcm_hardware; + unsigned int prealloc_buffer_size; +}; + +int snd_dmaengine_pcm_register(struct device *dev, + const struct snd_dmaengine_pcm_config *config, + unsigned int flags); +void snd_dmaengine_pcm_unregister(struct device *dev); + +int snd_dmaengine_pcm_prepare_slave_config(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct dma_slave_config *slave_config); + #endif diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 5da8ca7..9e675c7 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -29,6 +29,10 @@ config SND_SOC_AC97_BUS config SND_SOC_DMAENGINE_PCM bool
+config SND_SOC_GENERIC_DMAENGINE_PCM + bool + select SND_SOC_DMAENGINE_PCM + # All the supported SoCs source "sound/soc/atmel/Kconfig" source "sound/soc/au1x/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 99f32f7..197b6ae 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -5,6 +5,10 @@ ifneq ($(CONFIG_SND_SOC_DMAENGINE_PCM),) snd-soc-core-objs += soc-dmaengine-pcm.o endif
+ifneq ($(CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM),) +snd-soc-core-objs += soc-generic-dmaengine-pcm.o +endif + obj-$(CONFIG_SND_SOC) += snd-soc-core.o obj-$(CONFIG_SND_SOC) += codecs/ obj-$(CONFIG_SND_SOC) += generic/ diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c new file mode 100644 index 0000000..acfc926 --- /dev/null +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2013, Analog Devices Inc. + * Author: Lars-Peter Clausen lars@metafoo.de + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/dmaengine.h> +#include <linux/slab.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <linux/dma-mapping.h> +#include <linux/of.h> +#include <linux/of_dma.h> + +#include <sound/dmaengine_pcm.h> + +struct dmaengine_pcm { + struct dma_chan *chan[SNDRV_PCM_STREAM_CAPTURE + 1]; + const struct snd_dmaengine_pcm_config *config; + struct snd_soc_platform platform; +}; + +static struct dmaengine_pcm *soc_platform_to_pcm(struct snd_soc_platform *p) +{ + return container_of(p, struct dmaengine_pcm, platform); +} + +/** + * snd_dmaengine_pcm_prepare_slave_config() - Generic prepare_slave_config callback + * @substream: PCM substream + * @params: hw_params + * @slave_config: DMA slave config to prepare + * + * This function can be used as a generic prepare_slave_config callback for + * platforms which make use of the snd_dmaengine_dai_dma_data struct for their + * DAI DMA data. Internally the function will first call + * snd_hwparams_to_dma_slave_config to fill in the slave config based on the + * hw_params, followed by snd_dmaengine_set_config_from_dai_data to fill in the + * remaining fields based on the DAI DMA data. + */ +int snd_dmaengine_pcm_prepare_slave_config(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct dma_slave_config *slave_config) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_dmaengine_dai_dma_data *dma_data; + int ret; + + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + + ret = snd_hwparams_to_dma_slave_config(substream, params, slave_config); + if (ret) + return ret; + + snd_dmaengine_pcm_set_config_from_dai_data(substream, dma_data, + slave_config); + + return 0; +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_prepare_slave_config); + +static int dmaengine_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform); + struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); + struct dma_slave_config slave_config; + int ret; + + if (pcm->config->prepare_slave_config) { + ret = pcm->config->prepare_slave_config(substream, params, + &slave_config); + if (ret) + return ret; + + ret = dmaengine_slave_config(chan, &slave_config); + if (ret) + return ret; + } + + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); +} + +static int dmaengine_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform); + struct dma_chan *chan = pcm->chan[substream->stream]; + int ret; + + ret = snd_soc_set_runtime_hwparams(substream, + pcm->config->pcm_hardware); + if (ret) + return ret; + + return snd_dmaengine_pcm_open(substream, chan); +} + +static struct device *dmaengine_dma_dev(struct dmaengine_pcm *pcm, + struct snd_pcm_substream *substream) +{ + if (!pcm->chan[substream->stream]) + return NULL; + + return pcm->chan[substream->stream]->device->dev; +} + +static void dmaengine_pcm_free(struct snd_pcm *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform); + const struct snd_dmaengine_pcm_config *config = pcm->config; + struct snd_pcm_substream *substream; + unsigned int i; + int ret; + + for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) { + substream = rtd->pcm->streams[i].substream; + if (!substream) + continue; + + if (!pcm->chan[i]) { + dev_err(rtd->platform->dev, + "Missing dma channel for stream: %d\n", i); + ret = -EINVAL; + goto err_free; + } + + ret = snd_pcm_lib_preallocate_pages(substream, + SNDRV_DMA_TYPE_DEV, + dmaengine_dma_dev(pcm, substream), + config->prealloc_buffer_size, + config->pcm_hardware->buffer_bytes_max); + if (ret) + goto err_free; + } + + return 0; + +err_free: + dmaengine_pcm_free(rtd->pcm); + return ret; +} + +static const struct snd_pcm_ops dmaengine_pcm_ops = { + .open = dmaengine_pcm_open, + .close = snd_dmaengine_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = dmaengine_pcm_hw_params, + .hw_free = snd_pcm_lib_free_pages, + .trigger = snd_dmaengine_pcm_trigger, + .pointer = snd_dmaengine_pcm_pointer, +}; + +static const struct snd_soc_platform_driver dmaengine_pcm_platform = { + .ops = &dmaengine_pcm_ops, + .pcm_new = dmaengine_pcm_new, + .pcm_free = dmaengine_pcm_free, +}; + +static const char * const dmaengine_pcm_dma_channel_names[] = { + [SNDRV_PCM_STREAM_PLAYBACK] = "tx", + [SNDRV_PCM_STREAM_CAPTURE] = "rx", +}; + +/** + * snd_dmaengine_pcm_register - Register a dmaengine based PCM device + * @dev: The parent device for the PCM device + * @config: Platform specific PCM configuration + * @flags: Platform specific quirks + */ +int snd_dmaengine_pcm_register(struct device *dev, + const struct snd_dmaengine_pcm_config *config, unsigned int flags) +{ + struct dmaengine_pcm *pcm; + unsigned int i; + + if (!dev->of_node) + return -EINVAL; + + pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + + pcm->config = config; + + for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) { + pcm->chan[i] = of_dma_request_slave_channel(dev->of_node, + dmaengine_pcm_dma_channel_names[i]); + } + + return snd_soc_add_platform(dev, &pcm->platform, + &dmaengine_pcm_platform); +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_register); + +/** + * snd_dmaengine_pcm_unregister - Removes a dmaengine based PCM device + * @dev: Parent device the PCM was register with + * + * Removes a dmaengine based PCM device previously registered with + * snd_dmaengine_pcm_register. + */ +void snd_dmaengine_pcm_unregister(struct device *dev) +{ + struct snd_soc_platform *platform; + struct dmaengine_pcm *pcm; + unsigned int i; + + platform = snd_soc_lookup_platform(dev); + if (!platform) + return; + + pcm = soc_platform_to_pcm(platform); + + for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) { + if (pcm->chan[i]) + dma_release_channel(pcm->chan[i]); + } + + snd_soc_remove_platform(platform); + kfree(pcm); +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_unregister); + +MODULE_LICENSE("GPL");
On 04/15/2013 11:19 AM, Lars-Peter Clausen wrote:
This patch adds a generic dmaengine PCM driver. It builds on top of the dmaengine PCM library and adds the missing pieces like DMA channel management, buffer management and channel configuration. It will be able to replace the majority of the existing platform specific dmaengine based PCM drivers.
Devicetree is used to map the DMA channels to the PCM device.
...
diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c
...
+static const char * const dmaengine_pcm_dma_channel_names[] = {
- [SNDRV_PCM_STREAM_PLAYBACK] = "tx",
- [SNDRV_PCM_STREAM_CAPTURE] = "rx",
+};
...
+int snd_dmaengine_pcm_register(struct device *dev,
- const struct snd_dmaengine_pcm_config *config, unsigned int flags)
...
- for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) {
pcm->chan[i] = of_dma_request_slave_channel(dev->of_node,
dmaengine_pcm_dma_channel_names[i]);
That all means that the Tegra patch won't work yet. Tegra currently uses a custom DT binding for DMA since we haven't fully converted everything over the very new DT DMA bindings. So, the patches for Tegra may well be fine /if/ staged in the correct order with some .dts file changes too.
I wonder if all the other platforms this series modifies already have the appropriate DMA properties set up? A very quick look implies that (at least some) imx and omap do, but I'm not sure about spear, ep93xx, ux500?
Perhaps Laxman can provide appropriate DT fixes for Tegra while I'm on vacation, and test out this series, if I don't get to it in the next 2 days before my vacation.
On 04/16/2013 01:15 AM, Stephen Warren wrote:
On 04/15/2013 11:19 AM, Lars-Peter Clausen wrote:
This patch adds a generic dmaengine PCM driver. It builds on top of the dmaengine PCM library and adds the missing pieces like DMA channel management, buffer management and channel configuration. It will be able to replace the majority of the existing platform specific dmaengine based PCM drivers.
Devicetree is used to map the DMA channels to the PCM device.
...
diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c
...
+static const char * const dmaengine_pcm_dma_channel_names[] = {
- [SNDRV_PCM_STREAM_PLAYBACK] = "tx",
- [SNDRV_PCM_STREAM_CAPTURE] = "rx",
+};
...
+int snd_dmaengine_pcm_register(struct device *dev,
- const struct snd_dmaengine_pcm_config *config, unsigned int flags)
...
- for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) {
pcm->chan[i] = of_dma_request_slave_channel(dev->of_node,
dmaengine_pcm_dma_channel_names[i]);
That all means that the Tegra patch won't work yet. Tegra currently uses a custom DT binding for DMA since we haven't fully converted everything over the very new DT DMA bindings. So, the patches for Tegra may well be fine /if/ staged in the correct order with some .dts file changes too.
I wonder if all the other platforms this series modifies already have the appropriate DMA properties set up? A very quick look implies that (at least some) imx and omap do, but I'm not sure about spear, ep93xx, ux500?
Perhaps Laxman can provide appropriate DT fixes for Tegra while I'm on vacation, and test out this series, if I don't get to it in the next 2 days before my vacation.
None of the converted platforms is changed to use DT yet, they all use the compat path. So there shouldn't be any functional change for them. Converting them to use the generic DT bindings can be done one by one later.
- Lars
On 04/15/2013 11:31 PM, Lars-Peter Clausen wrote:
On 04/16/2013 01:15 AM, Stephen Warren wrote:
On 04/15/2013 11:19 AM, Lars-Peter Clausen wrote:
This patch adds a generic dmaengine PCM driver. It builds on top of the dmaengine PCM library and adds the missing pieces like DMA channel management, buffer management and channel configuration. It will be able to replace the majority of the existing platform specific dmaengine based PCM drivers.
Devicetree is used to map the DMA channels to the PCM device.
...
diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c
...
+static const char * const dmaengine_pcm_dma_channel_names[] = {
- [SNDRV_PCM_STREAM_PLAYBACK] = "tx",
- [SNDRV_PCM_STREAM_CAPTURE] = "rx",
+};
...
+int snd_dmaengine_pcm_register(struct device *dev,
- const struct snd_dmaengine_pcm_config *config, unsigned int flags)
...
- for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) {
pcm->chan[i] = of_dma_request_slave_channel(dev->of_node,
dmaengine_pcm_dma_channel_names[i]);
That all means that the Tegra patch won't work yet. Tegra currently uses a custom DT binding for DMA since we haven't fully converted everything over the very new DT DMA bindings. So, the patches for Tegra may well be fine /if/ staged in the correct order with some .dts file changes too.
I wonder if all the other platforms this series modifies already have the appropriate DMA properties set up? A very quick look implies that (at least some) imx and omap do, but I'm not sure about spear, ep93xx, ux500?
Perhaps Laxman can provide appropriate DT fixes for Tegra while I'm on vacation, and test out this series, if I don't get to it in the next 2 days before my vacation.
None of the converted platforms is changed to use DT yet, they all use the compat path. So there shouldn't be any functional change for them. Converting them to use the generic DT bindings can be done one by one later.
Ah right. So, this series works fine on Tegra20 at least:
Tested-by: Stephen Warren swarren@nvidia.com
On Mon, Apr 15, 2013 at 07:19:50PM +0200, Lars-Peter Clausen wrote:
This patch adds a generic dmaengine PCM driver. It builds on top of the dmaengine PCM library and adds the missing pieces like DMA channel management, buffer management and channel configuration. It will be able to replace the majority of the existing platform specific dmaengine based PCM drivers. Devicetree is used to map the DMA channels to the PCM device.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de
include/sound/dmaengine_pcm.h | 25 ++++ sound/soc/Kconfig | 4 + sound/soc/Makefile | 4 + sound/soc/soc-generic-dmaengine-pcm.c | 241 ++++++++++++++++++++++++++++++++++ 4 files changed, 274 insertions(+) create mode 100644 sound/soc/soc-generic-dmaengine-pcm.c
diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h index d015d67..e0bf24e 100644 --- a/include/sound/dmaengine_pcm.h +++ b/include/sound/dmaengine_pcm.h @@ -72,4 +72,29 @@ void snd_dmaengine_pcm_set_config_from_dai_data( const struct snd_dmaengine_dai_dma_data *dma_data, struct dma_slave_config *config);
+/**
- struct snd_dmaengine_pcm_config - Configuration data for dmaengine based PCM
- @prepare_slave_config: Callback used to fill in the DMA slave_config for a
- PCM substream. Will be called from the PCM drivers hwparams callback.
- @pcm_hardware: snd_pcm_hardware struct to be used for the PCM.
- @prealloc_buffer_size: Size of the preallocated audio buffer.
- */
+struct snd_dmaengine_pcm_config {
- int (*prepare_slave_config)(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct dma_slave_config *slave_config);
- const struct snd_pcm_hardware *pcm_hardware;
- unsigned int prealloc_buffer_size;
+};
+int snd_dmaengine_pcm_register(struct device *dev,
- const struct snd_dmaengine_pcm_config *config,
- unsigned int flags);
+void snd_dmaengine_pcm_unregister(struct device *dev);
+int snd_dmaengine_pcm_prepare_slave_config(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct dma_slave_config *slave_config);
#endif diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 5da8ca7..9e675c7 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -29,6 +29,10 @@ config SND_SOC_AC97_BUS config SND_SOC_DMAENGINE_PCM bool
+config SND_SOC_GENERIC_DMAENGINE_PCM
- bool
- select SND_SOC_DMAENGINE_PCM
# All the supported SoCs source "sound/soc/atmel/Kconfig" source "sound/soc/au1x/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 99f32f7..197b6ae 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -5,6 +5,10 @@ ifneq ($(CONFIG_SND_SOC_DMAENGINE_PCM),) snd-soc-core-objs += soc-dmaengine-pcm.o endif
+ifneq ($(CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM),) +snd-soc-core-objs += soc-generic-dmaengine-pcm.o +endif
obj-$(CONFIG_SND_SOC) += snd-soc-core.o obj-$(CONFIG_SND_SOC) += codecs/ obj-$(CONFIG_SND_SOC) += generic/ diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c new file mode 100644 index 0000000..acfc926 --- /dev/null +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -0,0 +1,241 @@ +/*
- Copyright (C) 2013, Analog Devices Inc.
- Author: Lars-Peter Clausen lars@metafoo.de
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by the
- Free Software Foundation; either version 2 of the License, or (at your
- option) any later version.
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 675 Mass Ave, Cambridge, MA 02139, USA.
- */
+#include <linux/module.h> +#include <linux/init.h> +#include <linux/dmaengine.h> +#include <linux/slab.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <linux/dma-mapping.h> +#include <linux/of.h> +#include <linux/of_dma.h>
+#include <sound/dmaengine_pcm.h>
+struct dmaengine_pcm {
- struct dma_chan *chan[SNDRV_PCM_STREAM_CAPTURE + 1];
- const struct snd_dmaengine_pcm_config *config;
- struct snd_soc_platform platform;
+};
+static struct dmaengine_pcm *soc_platform_to_pcm(struct snd_soc_platform *p) +{
- return container_of(p, struct dmaengine_pcm, platform);
+}
+/**
- snd_dmaengine_pcm_prepare_slave_config() - Generic prepare_slave_config callback
- @substream: PCM substream
- @params: hw_params
- @slave_config: DMA slave config to prepare
- This function can be used as a generic prepare_slave_config callback for
- platforms which make use of the snd_dmaengine_dai_dma_data struct for their
- DAI DMA data. Internally the function will first call
- snd_hwparams_to_dma_slave_config to fill in the slave config based on the
- hw_params, followed by snd_dmaengine_set_config_from_dai_data to fill in the
- remaining fields based on the DAI DMA data.
- */
+int snd_dmaengine_pcm_prepare_slave_config(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params, struct dma_slave_config *slave_config)
+{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_dmaengine_dai_dma_data *dma_data;
- int ret;
- dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
- ret = snd_hwparams_to_dma_slave_config(substream, params, slave_config);
- if (ret)
return ret;
- snd_dmaengine_pcm_set_config_from_dai_data(substream, dma_data,
slave_config);
- return 0;
+} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_prepare_slave_config);
+static int dmaengine_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform);
- struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
- struct dma_slave_config slave_config;
- int ret;
- if (pcm->config->prepare_slave_config) {
ret = pcm->config->prepare_slave_config(substream, params,
&slave_config);
if (ret)
return ret;
ret = dmaengine_slave_config(chan, &slave_config);
if (ret)
return ret;
- }
- return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+}
+static int dmaengine_pcm_open(struct snd_pcm_substream *substream) +{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform);
- struct dma_chan *chan = pcm->chan[substream->stream];
- int ret;
- ret = snd_soc_set_runtime_hwparams(substream,
pcm->config->pcm_hardware);
- if (ret)
return ret;
- return snd_dmaengine_pcm_open(substream, chan);
+}
+static struct device *dmaengine_dma_dev(struct dmaengine_pcm *pcm,
- struct snd_pcm_substream *substream)
+{
- if (!pcm->chan[substream->stream])
return NULL;
- return pcm->chan[substream->stream]->device->dev;
+}
+static void dmaengine_pcm_free(struct snd_pcm *pcm) +{
- snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd) +{
- struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform);
- const struct snd_dmaengine_pcm_config *config = pcm->config;
- struct snd_pcm_substream *substream;
- unsigned int i;
- int ret;
- for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) {
substream = rtd->pcm->streams[i].substream;
if (!substream)
continue;
if (!pcm->chan[i]) {
dev_err(rtd->platform->dev,
"Missing dma channel for stream: %d\n", i);
ret = -EINVAL;
goto err_free;
}
ret = snd_pcm_lib_preallocate_pages(substream,
SNDRV_DMA_TYPE_DEV,
dmaengine_dma_dev(pcm, substream),
config->prealloc_buffer_size,
config->pcm_hardware->buffer_bytes_max);
if (ret)
goto err_free;
- }
- return 0;
+err_free:
- dmaengine_pcm_free(rtd->pcm);
- return ret;
+}
+static const struct snd_pcm_ops dmaengine_pcm_ops = {
- .open = dmaengine_pcm_open,
- .close = snd_dmaengine_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = dmaengine_pcm_hw_params,
- .hw_free = snd_pcm_lib_free_pages,
- .trigger = snd_dmaengine_pcm_trigger,
- .pointer = snd_dmaengine_pcm_pointer,
+};
+static const struct snd_soc_platform_driver dmaengine_pcm_platform = {
- .ops = &dmaengine_pcm_ops,
- .pcm_new = dmaengine_pcm_new,
- .pcm_free = dmaengine_pcm_free,
+};
+static const char * const dmaengine_pcm_dma_channel_names[] = {
- [SNDRV_PCM_STREAM_PLAYBACK] = "tx",
- [SNDRV_PCM_STREAM_CAPTURE] = "rx",
+};
+/**
- snd_dmaengine_pcm_register - Register a dmaengine based PCM device
- @dev: The parent device for the PCM device
- @config: Platform specific PCM configuration
- @flags: Platform specific quirks
- */
+int snd_dmaengine_pcm_register(struct device *dev,
- const struct snd_dmaengine_pcm_config *config, unsigned int flags)
+{
- struct dmaengine_pcm *pcm;
- unsigned int i;
- if (!dev->of_node)
return -EINVAL;
- pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
- if (!pcm)
return -ENOMEM;
- pcm->config = config;
- for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) {
pcm->chan[i] = of_dma_request_slave_channel(dev->of_node,
dmaengine_pcm_dma_channel_names[i]);
- }
Perhaps you should use dma_request_slave_channel here? Then you could also drop the of_dma.h include.
Regards,
Markus
- return snd_soc_add_platform(dev, &pcm->platform,
&dmaengine_pcm_platform);
+} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_register);
+/**
- snd_dmaengine_pcm_unregister - Removes a dmaengine based PCM device
- @dev: Parent device the PCM was register with
- Removes a dmaengine based PCM device previously registered with
- snd_dmaengine_pcm_register.
- */
+void snd_dmaengine_pcm_unregister(struct device *dev) +{
- struct snd_soc_platform *platform;
- struct dmaengine_pcm *pcm;
- unsigned int i;
- platform = snd_soc_lookup_platform(dev);
- if (!platform)
return;
- pcm = soc_platform_to_pcm(platform);
- for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) {
if (pcm->chan[i])
dma_release_channel(pcm->chan[i]);
- }
- snd_soc_remove_platform(platform);
- kfree(pcm);
+} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_unregister);
+MODULE_LICENSE("GPL");
1.8.0
+/**
- snd_dmaengine_pcm_register - Register a dmaengine based PCM device
- @dev: The parent device for the PCM device
- @config: Platform specific PCM configuration
- @flags: Platform specific quirks
- */
+int snd_dmaengine_pcm_register(struct device *dev,
- const struct snd_dmaengine_pcm_config *config, unsigned int flags)
+{
- struct dmaengine_pcm *pcm;
- unsigned int i;
- if (!dev->of_node)
return -EINVAL;
Why is this call Device Tree only?
Are devices which use platform data still supported?
- pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
- if (!pcm)
return -ENOMEM;
- pcm->config = config;
- for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) {
pcm->chan[i] = of_dma_request_slave_channel(dev->of_node,
dmaengine_pcm_dma_channel_names[i]);
- }
Perhaps you should use dma_request_slave_channel here? Then you could also drop the of_dma.h include.
Then how would Device Tree be supported?
On Tue, Apr 16, 2013 at 03:35:18PM +0100, Lee Jones wrote:
+/**
- snd_dmaengine_pcm_register - Register a dmaengine based PCM device
- @dev: The parent device for the PCM device
- @config: Platform specific PCM configuration
- @flags: Platform specific quirks
- */
+int snd_dmaengine_pcm_register(struct device *dev,
- const struct snd_dmaengine_pcm_config *config, unsigned int flags)
+{
- struct dmaengine_pcm *pcm;
- unsigned int i;
- if (!dev->of_node)
return -EINVAL;
Why is this call Device Tree only?
Are devices which use platform data still supported?
- pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
- if (!pcm)
return -ENOMEM;
- pcm->config = config;
- for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) {
pcm->chan[i] = of_dma_request_slave_channel(dev->of_node,
dmaengine_pcm_dma_channel_names[i]);
- }
Perhaps you should use dma_request_slave_channel here? Then you could also drop the of_dma.h include.
Then how would Device Tree be supported?
dma_request_slave_channel calls of_dma_request_slave_channel if dev->of_node exists. As far as I know dma_request_slave_channel is preferred for calls from outside the dmaengine core.
Regards,
Markus
On Tue, 16 Apr 2013, Markus Pargmann wrote:
On Tue, Apr 16, 2013 at 03:35:18PM +0100, Lee Jones wrote:
+/**
- snd_dmaengine_pcm_register - Register a dmaengine based PCM device
- @dev: The parent device for the PCM device
- @config: Platform specific PCM configuration
- @flags: Platform specific quirks
- */
+int snd_dmaengine_pcm_register(struct device *dev,
- const struct snd_dmaengine_pcm_config *config, unsigned int flags)
+{
- struct dmaengine_pcm *pcm;
- unsigned int i;
- if (!dev->of_node)
return -EINVAL;
Why is this call Device Tree only?
Are devices which use platform data still supported?
- pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
- if (!pcm)
return -ENOMEM;
- pcm->config = config;
- for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) {
pcm->chan[i] = of_dma_request_slave_channel(dev->of_node,
dmaengine_pcm_dma_channel_names[i]);
- }
Perhaps you should use dma_request_slave_channel here? Then you could also drop the of_dma.h include.
Then how would Device Tree be supported?
dma_request_slave_channel calls of_dma_request_slave_channel if dev->of_node exists. As far as I know dma_request_slave_channel is preferred for calls from outside the dmaengine core.
Ah yes, fair point. I was thinking of dma_request_channel().
I think my first question (above) is still valid though.
On 04/16/2013 04:35 PM, Lee Jones wrote:
+/**
- snd_dmaengine_pcm_register - Register a dmaengine based PCM device
- @dev: The parent device for the PCM device
- @config: Platform specific PCM configuration
- @flags: Platform specific quirks
- */
+int snd_dmaengine_pcm_register(struct device *dev,
- const struct snd_dmaengine_pcm_config *config, unsigned int flags)
+{
- struct dmaengine_pcm *pcm;
- unsigned int i;
- if (!dev->of_node)
return -EINVAL;
Why is this call Device Tree only?
Are devices which use platform data still supported?
The next patch (04/17) adds support for compat platforms.
- pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
- if (!pcm)
return -ENOMEM;
- pcm->config = config;
- for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) {
pcm->chan[i] = of_dma_request_slave_channel(dev->of_node,
dmaengine_pcm_dma_channel_names[i]);
- }
Perhaps you should use dma_request_slave_channel here? Then you could also drop the of_dma.h include.
Then how would Device Tree be supported?
On 04/15/2013 07:19 PM, Lars-Peter Clausen wrote:
This patch adds a generic dmaengine PCM driver. It builds on top of the dmaengine PCM library and adds the missing pieces like DMA channel management, buffer management and channel configuration. It will be able to replace the majority of the existing platform specific dmaengine based PCM drivers. Devicetree is used to map the DMA channels to the PCM device.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de
+static const char * const dmaengine_pcm_dma_channel_names[] = {
- [SNDRV_PCM_STREAM_PLAYBACK] = "tx",
- [SNDRV_PCM_STREAM_CAPTURE] = "rx",
+};
+/**
- snd_dmaengine_pcm_register - Register a dmaengine based PCM device
- @dev: The parent device for the PCM device
- @config: Platform specific PCM configuration
- @flags: Platform specific quirks
- */
+int snd_dmaengine_pcm_register(struct device *dev,
- const struct snd_dmaengine_pcm_config *config, unsigned int flags)
+{
- struct dmaengine_pcm *pcm;
- unsigned int i;
- if (!dev->of_node)
return -EINVAL;
- pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
- if (!pcm)
return -ENOMEM;
- pcm->config = config;
- for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) {
pcm->chan[i] = of_dma_request_slave_channel(dev->of_node,
dmaengine_pcm_dma_channel_names[i]);
Here we should have flexibility to provide custom DMA channel names. For OMAP4 AESS support (which I'm cleaning up right now for upstream) will need such a flexibility since we will have 8 DMA channels and none of them is dedicated tx or rx.
- }
- return snd_soc_add_platform(dev, &pcm->platform,
&dmaengine_pcm_platform);
+} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_register);
+/**
- snd_dmaengine_pcm_unregister - Removes a dmaengine based PCM device
- @dev: Parent device the PCM was register with
- Removes a dmaengine based PCM device previously registered with
- snd_dmaengine_pcm_register.
- */
+void snd_dmaengine_pcm_unregister(struct device *dev) +{
- struct snd_soc_platform *platform;
- struct dmaengine_pcm *pcm;
- unsigned int i;
- platform = snd_soc_lookup_platform(dev);
- if (!platform)
return;
- pcm = soc_platform_to_pcm(platform);
- for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) {
if (pcm->chan[i])
dma_release_channel(pcm->chan[i]);
- }
- snd_soc_remove_platform(platform);
- kfree(pcm);
+} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_unregister);
+MODULE_LICENSE("GPL");
On 04/17/2013 11:48 AM, Peter Ujfalusi wrote:
On 04/15/2013 07:19 PM, Lars-Peter Clausen wrote:
This patch adds a generic dmaengine PCM driver. It builds on top of the dmaengine PCM library and adds the missing pieces like DMA channel management, buffer management and channel configuration. It will be able to replace the majority of the existing platform specific dmaengine based PCM drivers. Devicetree is used to map the DMA channels to the PCM device.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de
+static const char * const dmaengine_pcm_dma_channel_names[] = {
- [SNDRV_PCM_STREAM_PLAYBACK] = "tx",
- [SNDRV_PCM_STREAM_CAPTURE] = "rx",
+};
+/**
- snd_dmaengine_pcm_register - Register a dmaengine based PCM device
- @dev: The parent device for the PCM device
- @config: Platform specific PCM configuration
- @flags: Platform specific quirks
- */
+int snd_dmaengine_pcm_register(struct device *dev,
- const struct snd_dmaengine_pcm_config *config, unsigned int flags)
+{
- struct dmaengine_pcm *pcm;
- unsigned int i;
- if (!dev->of_node)
return -EINVAL;
- pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
- if (!pcm)
return -ENOMEM;
- pcm->config = config;
- for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) {
pcm->chan[i] = of_dma_request_slave_channel(dev->of_node,
dmaengine_pcm_dma_channel_names[i]);
Here we should have flexibility to provide custom DMA channel names. For OMAP4 AESS support (which I'm cleaning up right now for upstream) will need such a flexibility since we will have 8 DMA channels and none of them is dedicated tx or rx.
This sounds like it needs more than just support for custom channel names. The generic driver currently expects that there is exactly one DMA channel for capture and one for playback (well and in case the DAI link is simplex only either one of them).
What are these 8 channels exactly used for?
- Lars
On 04/17/2013 12:48 PM, Lars-Peter Clausen wrote:
This sounds like it needs more than just support for custom channel names. The generic driver currently expects that there is exactly one DMA channel for capture and one for playback (well and in case the DAI link is simplex only either one of them).
What are these 8 channels exactly used for?
We have 7 FIFOs which can be configured either playback or capture. The last channel is used for debug purposes only right now. Basically we can dynamically configure the 8 DMA channel for playback/capture and the mixing of incoming streams done inside of the AESS. We have 7 DAIs and we have playback/capture on those (some only have playback some only capture some duplex). Right now we have kind of static DMA bindings for them, I'm working on a dynamic model right now.
So one IP (one driver) which can request 8 DMA and depending on the stream (DAI in use) we pick one of them to be used.
On 04/17/2013 03:06 PM, Peter Ujfalusi wrote:
On 04/17/2013 12:48 PM, Lars-Peter Clausen wrote:
This sounds like it needs more than just support for custom channel names. The generic driver currently expects that there is exactly one DMA channel for capture and one for playback (well and in case the DAI link is simplex only either one of them).
What are these 8 channels exactly used for?
We have 7 FIFOs which can be configured either playback or capture. The last channel is used for debug purposes only right now. Basically we can dynamically configure the 8 DMA channel for playback/capture and the mixing of incoming streams done inside of the AESS. We have 7 DAIs and we have playback/capture on those (some only have playback some only capture some duplex). Right now we have kind of static DMA bindings for them, I'm working on a dynamic model right now.
Hm, if you need to re-assign the DMA channels dynamically at runtime it maybe better to stick with a custom PCM driver. Should I drop OMAP from this series for now?
- Lars
Hi Lars,
On 04/17/2013 08:45 PM, Lars-Peter Clausen wrote:
On 04/17/2013 03:06 PM, Peter Ujfalusi wrote:
On 04/17/2013 12:48 PM, Lars-Peter Clausen wrote:
This sounds like it needs more than just support for custom channel names. The generic driver currently expects that there is exactly one DMA channel for capture and one for playback (well and in case the DAI link is simplex only either one of them).
What are these 8 channels exactly used for?
We have 7 FIFOs which can be configured either playback or capture. The last channel is used for debug purposes only right now. Basically we can dynamically configure the 8 DMA channel for playback/capture and the mixing of incoming streams done inside of the AESS. We have 7 DAIs and we have playback/capture on those (some only have playback some only capture some duplex). Right now we have kind of static DMA bindings for them, I'm working on a dynamic model right now.
Hm, if you need to re-assign the DMA channels dynamically at runtime it maybe better to stick with a custom PCM driver. Should I drop OMAP from this series for now?
I think this would the best for now. However I did tested the series and it worked with the code upstream. Later I can revisit this and probably have two PCM driver for OMAP, but I'm not sure about this right now, I will know more when I have rebased my AESS/ABE work on top of 3.10-rc1.
Can you drop the OMAP patches for now?
Thanks, Péter
On Mon, Apr 15, 2013 at 07:19:50PM +0200, Lars-Peter Clausen wrote:
This patch adds a generic dmaengine PCM driver. It builds on top of the dmaengine PCM library and adds the missing pieces like DMA channel management, buffer management and channel configuration. It will be able to replace the majority of the existing platform specific dmaengine based PCM drivers. Devicetree is used to map the DMA channels to the PCM device.
Applied, thanks - seems like it'll need some more work but let's make the merge window...
Add support for platforms which don't use devicetree yet or have to optionally support a non-devicetree way to request the DMA channel. The patch adds the compat_request_channel and compat_filter_fn callbacks to the snd_dmaengine_pcm_config struct. If the compat_request_channel is implemented it will be used to request the DMA channel. If not dma_request_channel with compat_filter_fn as the filter function will be used to request the channel.
The patch also exports the snd_dmaengine_pcm_request_chan() function, since compat platforms will want to use it to request their DMA channel.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- include/sound/dmaengine_pcm.h | 29 +++++++++++++++++++++++++++++ sound/soc/soc-dmaengine-pcm.c | 14 ++++++++++++-- sound/soc/soc-generic-dmaengine-pcm.c | 32 +++++++++++++++++++++++++++----- 3 files changed, 68 insertions(+), 7 deletions(-)
diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h index e0bf24e..1a7897a 100644 --- a/include/sound/dmaengine_pcm.h +++ b/include/sound/dmaengine_pcm.h @@ -16,6 +16,7 @@ #define __SOUND_DMAENGINE_PCM_H__
#include <sound/pcm.h> +#include <sound/soc.h> #include <linux/dmaengine.h>
/** @@ -46,6 +47,8 @@ int snd_dmaengine_pcm_open_request_chan(struct snd_pcm_substream *substream, dma_filter_fn filter_fn, void *filter_data); int snd_dmaengine_pcm_close_release_chan(struct snd_pcm_substream *substream);
+struct dma_chan *snd_dmaengine_pcm_request_channel(dma_filter_fn filter_fn, + void *filter_data); struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream);
/** @@ -72,17 +75,43 @@ void snd_dmaengine_pcm_set_config_from_dai_data( const struct snd_dmaengine_dai_dma_data *dma_data, struct dma_slave_config *config);
+ +/* + * Try to request the DMA channel using compat_request_channel or + * compat_filter_fn if it couldn't be requested through devicetree. + */ +#define SND_DMAENGINE_PCM_FLAG_COMPAT BIT(0) +/* + * Don't try to request the DMA channels through devicetree. This flag only + * makes sense if SND_DMAENGINE_PCM_FLAG_COMPAT is set as well. + */ +#define SND_DMAENGINE_PCM_FLAG_NO_DT BIT(1) + /** * struct snd_dmaengine_pcm_config - Configuration data for dmaengine based PCM * @prepare_slave_config: Callback used to fill in the DMA slave_config for a * PCM substream. Will be called from the PCM drivers hwparams callback. + * @compat_request_channel: Callback to request a DMA channel for platforms + * which do not use devicetree. + * @compat_filter_fn: Will be used as the filter function when requesting a + * channel for platforms which do not use devicetree. The filter parameter + * will be the DAI's DMA data. * @pcm_hardware: snd_pcm_hardware struct to be used for the PCM. * @prealloc_buffer_size: Size of the preallocated audio buffer. + * + * Note: If both compat_request_channel and compat_filter_fn are set + * compat_request_channel will be used to request the channel and + * compat_filter_fn will be ignored. Otherwise the channel will be requested + * using dma_request_channel with compat_filter_fn as the filter function. */ struct snd_dmaengine_pcm_config { int (*prepare_slave_config)(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct dma_slave_config *slave_config); + struct dma_chan *(*compat_request_channel)( + struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_substream *substream); + dma_filter_fn compat_filter_fn;
const struct snd_pcm_hardware *pcm_hardware; unsigned int prealloc_buffer_size; diff --git a/sound/soc/soc-dmaengine-pcm.c b/sound/soc/soc-dmaengine-pcm.c index b0420a7..aa924d9 100644 --- a/sound/soc/soc-dmaengine-pcm.c +++ b/sound/soc/soc-dmaengine-pcm.c @@ -254,7 +254,16 @@ snd_pcm_uframes_t snd_dmaengine_pcm_pointer(struct snd_pcm_substream *substream) } EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer);
-static struct dma_chan *dmaengine_pcm_request_channel(dma_filter_fn filter_fn, +/** + * snd_dmaengine_pcm_request_channel - Request channel for the dmaengine PCM + * @filter_fn: Filter function used to request the DMA channel + * @filter_data: Data passed to the DMA filter function + * + * Returns NULL or the requested DMA channel. + * + * This function request a DMA channel for usage with dmaengine PCM. + */ +struct dma_chan *snd_dmaengine_pcm_request_channel(dma_filter_fn filter_fn, void *filter_data) { dma_cap_mask_t mask; @@ -265,6 +274,7 @@ static struct dma_chan *dmaengine_pcm_request_channel(dma_filter_fn filter_fn,
return dma_request_channel(mask, filter_fn, filter_data); } +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_request_channel);
/** * snd_dmaengine_pcm_open - Open a dmaengine based PCM substream @@ -320,7 +330,7 @@ int snd_dmaengine_pcm_open_request_chan(struct snd_pcm_substream *substream, dma_filter_fn filter_fn, void *filter_data) { return snd_dmaengine_pcm_open(substream, - dmaengine_pcm_request_channel(filter_fn, filter_data)); + snd_dmaengine_pcm_request_channel(filter_fn, filter_data)); } EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open_request_chan);
diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index acfc926..d6e6380 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -29,6 +29,7 @@ struct dmaengine_pcm { struct dma_chan *chan[SNDRV_PCM_STREAM_CAPTURE + 1]; const struct snd_dmaengine_pcm_config *config; struct snd_soc_platform platform; + bool compat; };
static struct dmaengine_pcm *soc_platform_to_pcm(struct snd_soc_platform *p) @@ -121,6 +122,19 @@ static void dmaengine_pcm_free(struct snd_pcm *pcm) snd_pcm_lib_preallocate_free_for_all(pcm); }
+static struct dma_chan *dmaengine_pcm_compat_request_channel( + struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_substream *substream) +{ + struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform); + + if (pcm->config->compat_request_channel) + return pcm->config->compat_request_channel(rtd, substream); + + return snd_dmaengine_pcm_request_channel(pcm->config->compat_filter_fn, + snd_soc_dai_get_dma_data(rtd->cpu_dai, substream)); +} + static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd) { struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform); @@ -134,6 +148,11 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd) if (!substream) continue;
+ if (!pcm->chan[i] && pcm->compat) { + pcm->chan[i] = dmaengine_pcm_compat_request_channel(rtd, + substream); + } + if (!pcm->chan[i]) { dev_err(rtd->platform->dev, "Missing dma channel for stream: %d\n", i); @@ -171,6 +190,7 @@ static const struct snd_soc_platform_driver dmaengine_pcm_platform = { .ops = &dmaengine_pcm_ops, .pcm_new = dmaengine_pcm_new, .pcm_free = dmaengine_pcm_free, + .probe_order = SND_SOC_COMP_ORDER_LATE, };
static const char * const dmaengine_pcm_dma_channel_names[] = { @@ -190,18 +210,20 @@ int snd_dmaengine_pcm_register(struct device *dev, struct dmaengine_pcm *pcm; unsigned int i;
- if (!dev->of_node) - return -EINVAL; - pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); if (!pcm) return -ENOMEM;
pcm->config = config;
- for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) { - pcm->chan[i] = of_dma_request_slave_channel(dev->of_node, + if (flags & SND_DMAENGINE_PCM_FLAG_COMPAT) + pcm->compat = true; + + if (!(flags & SND_DMAENGINE_PCM_FLAG_NO_DT) && dev->of_node) { + for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) { + pcm->chan[i] = of_dma_request_slave_channel(dev->of_node, dmaengine_pcm_dma_channel_names[i]); + } }
return snd_soc_add_platform(dev, &pcm->platform,
On Mon, Apr 15, 2013 at 07:19:51PM +0200, Lars-Peter Clausen wrote:
Add support for platforms which don't use devicetree yet or have to optionally support a non-devicetree way to request the DMA channel. The patch adds the
Applied, thanks.
Use the generic dmaengine PCM driver instead of a custom implementation.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- sound/soc/tegra/Kconfig | 2 +- sound/soc/tegra/tegra_pcm.c | 171 +++----------------------------------------- 2 files changed, 10 insertions(+), 163 deletions(-)
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index dbc27ce..b1c9d57 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -2,7 +2,7 @@ config SND_SOC_TEGRA tristate "SoC Audio for the Tegra System-on-Chip" depends on ARCH_TEGRA && TEGRA20_APB_DMA select REGMAP_MMIO - select SND_SOC_DMAENGINE_PCM if TEGRA20_APB_DMA + select SND_SOC_GENERIC_DMAENGINE_PCM if TEGRA20_APB_DMA help Say Y or M here if you want support for SoC audio on Tegra.
diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c index f9f247c..f056f63 100644 --- a/sound/soc/tegra/tegra_pcm.c +++ b/sound/soc/tegra/tegra_pcm.c @@ -29,9 +29,7 @@ * */
-#include <linux/dma-mapping.h> #include <linux/module.h> -#include <linux/slab.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -55,175 +53,24 @@ static const struct snd_pcm_hardware tegra_pcm_hardware = { .fifo_size = 4, };
-static int tegra_pcm_open(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct device *dev = rtd->platform->dev; - int ret; - - /* Set HW params now that initialization is complete */ - snd_soc_set_runtime_hwparams(substream, &tegra_pcm_hardware); - - ret = snd_dmaengine_pcm_open_request_chan(substream, NULL, NULL); - if (ret) { - dev_err(dev, "dmaengine pcm open failed with err %d\n", ret); - return ret; - } - - return 0; -} - -static int tegra_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct device *dev = rtd->platform->dev; - struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); - struct dma_slave_config slave_config; - int ret; - - ret = snd_hwparams_to_dma_slave_config(substream, params, - &slave_config); - if (ret) { - dev_err(dev, "hw params config failed with err %d\n", ret); - return ret; - } - - snd_dmaengine_pcm_set_config_from_dai_data(substream, - snd_soc_dai_get_dma_data(rtd->cpu_dai, substream), - &slave_config); - - ret = dmaengine_slave_config(chan, &slave_config); - if (ret < 0) { - dev_err(dev, "dma slave config failed with err %d\n", ret); - return ret; - } - - snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); - return 0; -} - -static int tegra_pcm_hw_free(struct snd_pcm_substream *substream) -{ - snd_pcm_set_runtime_buffer(substream, NULL); - return 0; -} - -static int tegra_pcm_mmap(struct snd_pcm_substream *substream, - struct vm_area_struct *vma) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - - return dma_mmap_writecombine(substream->pcm->card->dev, vma, - runtime->dma_area, - runtime->dma_addr, - runtime->dma_bytes); -} - -static struct snd_pcm_ops tegra_pcm_ops = { - .open = tegra_pcm_open, - .close = snd_dmaengine_pcm_close_release_chan, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = tegra_pcm_hw_params, - .hw_free = tegra_pcm_hw_free, - .trigger = snd_dmaengine_pcm_trigger, - .pointer = snd_dmaengine_pcm_pointer, - .mmap = tegra_pcm_mmap, -}; - -static int tegra_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) -{ - struct snd_pcm_substream *substream = pcm->streams[stream].substream; - struct snd_dma_buffer *buf = &substream->dma_buffer; - size_t size = tegra_pcm_hardware.buffer_bytes_max; - - buf->area = dma_alloc_writecombine(pcm->card->dev, size, - &buf->addr, GFP_KERNEL); - if (!buf->area) - return -ENOMEM; - - buf->dev.type = SNDRV_DMA_TYPE_DEV; - buf->dev.dev = pcm->card->dev; - buf->private_data = NULL; - buf->bytes = size; - - return 0; -} - -static void tegra_pcm_deallocate_dma_buffer(struct snd_pcm *pcm, int stream) -{ - struct snd_pcm_substream *substream; - struct snd_dma_buffer *buf; - - substream = pcm->streams[stream].substream; - if (!substream) - return; - - buf = &substream->dma_buffer; - if (!buf->area) - return; - - dma_free_writecombine(pcm->card->dev, buf->bytes, - buf->area, buf->addr); - buf->area = NULL; -} - -static u64 tegra_dma_mask = DMA_BIT_MASK(32); - -static int tegra_pcm_new(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_card *card = rtd->card->snd_card; - struct snd_pcm *pcm = rtd->pcm; - int ret = 0; - - if (!card->dev->dma_mask) - card->dev->dma_mask = &tegra_dma_mask; - if (!card->dev->coherent_dma_mask) - card->dev->coherent_dma_mask = DMA_BIT_MASK(32); - - if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { - ret = tegra_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_PLAYBACK); - if (ret) - goto err; - } - - if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { - ret = tegra_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_CAPTURE); - if (ret) - goto err_free_play; - } - - return 0; - -err_free_play: - tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK); -err: - return ret; -} - -static void tegra_pcm_free(struct snd_pcm *pcm) -{ - tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE); - tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK); -} - -static struct snd_soc_platform_driver tegra_pcm_platform = { - .ops = &tegra_pcm_ops, - .pcm_new = tegra_pcm_new, - .pcm_free = tegra_pcm_free, +static const struct snd_dmaengine_pcm_config tegra_dmaengine_pcm_config = { + .pcm_hardware = &tegra_pcm_hardware, + .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, + .compat_filter_fn = NULL, + .prealloc_buffer_size = PAGE_SIZE * 8, };
int tegra_pcm_platform_register(struct device *dev) { - return snd_soc_register_platform(dev, &tegra_pcm_platform); + return snd_dmaengine_pcm_register(dev, &tegra_dmaengine_pcm_config, + SND_DMAENGINE_PCM_FLAG_NO_DT | + SND_DMAENGINE_PCM_FLAG_COMPAT); } EXPORT_SYMBOL_GPL(tegra_pcm_platform_register);
void tegra_pcm_platform_unregister(struct device *dev) { - snd_soc_unregister_platform(dev); + return snd_dmaengine_pcm_unregister(dev); } EXPORT_SYMBOL_GPL(tegra_pcm_platform_unregister);
This allows us to access the DAI DMA data when we create the PCM. We'll use this when converting spear to generic DMA engine PCM driver.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- sound/soc/spear/spdif_in.c | 12 ++++-------- sound/soc/spear/spdif_out.c | 7 ++++--- 2 files changed, 8 insertions(+), 11 deletions(-)
diff --git a/sound/soc/spear/spdif_in.c b/sound/soc/spear/spdif_in.c index 14d57e8..82c8387 100644 --- a/sound/soc/spear/spdif_in.c +++ b/sound/soc/spear/spdif_in.c @@ -49,15 +49,12 @@ static void spdif_in_configure(struct spdif_in_dev *host) writel(0xF, host->io_base + SPDIF_IN_IRQ_MASK); }
-static int spdif_in_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *cpu_dai) +static int spdif_in_dai_probe(struct snd_soc_dai *dai) { - struct spdif_in_dev *host = snd_soc_dai_get_drvdata(cpu_dai); + struct spdif_in_dev *host = snd_soc_dai_get_drvdata(dai);
- if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) - return -EINVAL; + dai->capture_dma_data = &host->dma_params;
- snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)&host->dma_params); return 0; }
@@ -70,7 +67,6 @@ static void spdif_in_shutdown(struct snd_pcm_substream *substream, return;
writel(0x0, host->io_base + SPDIF_IN_IRQ_MASK); - snd_soc_dai_set_dma_data(dai, substream, NULL); }
static void spdif_in_format(struct spdif_in_dev *host, u32 format) @@ -151,13 +147,13 @@ static int spdif_in_trigger(struct snd_pcm_substream *substream, int cmd, }
static struct snd_soc_dai_ops spdif_in_dai_ops = { - .startup = spdif_in_startup, .shutdown = spdif_in_shutdown, .trigger = spdif_in_trigger, .hw_params = spdif_in_hw_params, };
struct snd_soc_dai_driver spdif_in_dai = { + .probe = spdif_in_dai_probe, .capture = { .channels_min = 2, .channels_max = 2, diff --git a/sound/soc/spear/spdif_out.c b/sound/soc/spear/spdif_out.c index 1e3c3dd..12b4f2f 100644 --- a/sound/soc/spear/spdif_out.c +++ b/sound/soc/spear/spdif_out.c @@ -62,8 +62,6 @@ static int spdif_out_startup(struct snd_pcm_substream *substream, if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) return -EINVAL;
- snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)&host->dma_params); - ret = clk_enable(host->clk); if (ret) return ret; @@ -84,7 +82,6 @@ static void spdif_out_shutdown(struct snd_pcm_substream *substream,
clk_disable(host->clk); host->running = false; - snd_soc_dai_set_dma_data(dai, substream, NULL); }
static void spdif_out_clock(struct spdif_out_dev *host, u32 core_freq, @@ -245,6 +242,10 @@ static const struct snd_kcontrol_new spdif_out_controls[] = {
int spdif_soc_dai_probe(struct snd_soc_dai *dai) { + struct spdif_out_dev *host = snd_soc_dai_get_drvdata(dai); + + dai->playback_dma_data = &host->dma_params; + return snd_soc_add_dai_controls(dai, spdif_out_controls, ARRAY_SIZE(spdif_out_controls)); }
Use the generic dmaengine PCM driver instead of a custom implementation.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- sound/soc/spear/spear_pcm.c | 152 +++++--------------------------------------- 1 file changed, 15 insertions(+), 137 deletions(-)
diff --git a/sound/soc/spear/spear_pcm.c b/sound/soc/spear/spear_pcm.c index 2fbd489..4707f2b 100644 --- a/sound/soc/spear/spear_pcm.c +++ b/sound/soc/spear/spear_pcm.c @@ -13,19 +13,13 @@
#include <linux/module.h> #include <linux/dmaengine.h> -#include <linux/dma-mapping.h> -#include <linux/init.h> #include <linux/platform_device.h> -#include <linux/scatterlist.h> -#include <linux/slab.h> -#include <sound/core.h> #include <sound/dmaengine_pcm.h> #include <sound/pcm.h> -#include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/spear_dma.h>
-static struct snd_pcm_hardware spear_pcm_hardware = { +static const struct snd_pcm_hardware spear_pcm_hardware = { .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), @@ -37,149 +31,33 @@ static struct snd_pcm_hardware spear_pcm_hardware = { .fifo_size = 0, /* fifo size in bytes */ };
-static int spear_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +static struct dma_chan *spear_pcm_request_chan(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_substream *substream) { - snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + struct spear_dma_data *dma_data;
- return 0; -} - -static int spear_pcm_hw_free(struct snd_pcm_substream *substream) -{ - snd_pcm_set_runtime_buffer(substream, NULL); - - return 0; -} - -static int spear_pcm_open(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - - struct spear_dma_data *dma_data = (struct spear_dma_data *) - snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); - int ret; - - ret = snd_soc_set_runtime_hwparams(substream, &spear_pcm_hardware); - if (ret) - return ret; + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
- return snd_dmaengine_pcm_open_request_chan(substream, dma_data->filter, - dma_data); + return snd_dmaengine_pcm_request_channel(dma_data->filter, dma_data); }
-static int spear_pcm_mmap(struct snd_pcm_substream *substream, - struct vm_area_struct *vma) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - - return dma_mmap_writecombine(substream->pcm->card->dev, vma, - runtime->dma_area, runtime->dma_addr, - runtime->dma_bytes); -} - -static struct snd_pcm_ops spear_pcm_ops = { - .open = spear_pcm_open, - .close = snd_dmaengine_pcm_close_release_chan, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = spear_pcm_hw_params, - .hw_free = spear_pcm_hw_free, - .trigger = snd_dmaengine_pcm_trigger, - .pointer = snd_dmaengine_pcm_pointer, - .mmap = spear_pcm_mmap, -}; - -static int -spear_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; - - buf->dev.type = SNDRV_DMA_TYPE_DEV; - buf->dev.dev = pcm->card->dev; - buf->private_data = NULL; - - buf->area = dma_alloc_writecombine(pcm->card->dev, size, - &buf->addr, GFP_KERNEL); - if (!buf->area) - return -ENOMEM; - - dev_info(buf->dev.dev, - " preallocate_dma_buffer: area=%p, addr=%p, size=%d\n", - (void *)buf->area, (void *)buf->addr, size); - - buf->bytes = size; - return 0; -} - -static void spear_pcm_free(struct snd_pcm *pcm) -{ - struct snd_pcm_substream *substream; - struct snd_dma_buffer *buf; - int stream; - - for (stream = 0; stream < 2; stream++) { - substream = pcm->streams[stream].substream; - if (!substream) - continue; - - buf = &substream->dma_buffer; - if (!buf || !buf->area) - continue; - - dma_free_writecombine(pcm->card->dev, buf->bytes, - buf->area, buf->addr); - buf->area = NULL; - } -} - -static u64 spear_pcm_dmamask = DMA_BIT_MASK(32); - -static int spear_pcm_new(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_card *card = rtd->card->snd_card; - int ret; - - if (!card->dev->dma_mask) - card->dev->dma_mask = &spear_pcm_dmamask; - if (!card->dev->coherent_dma_mask) - card->dev->coherent_dma_mask = DMA_BIT_MASK(32); - - if (rtd->cpu_dai->driver->playback.channels_min) { - ret = spear_pcm_preallocate_dma_buffer(rtd->pcm, - SNDRV_PCM_STREAM_PLAYBACK, - spear_pcm_hardware.buffer_bytes_max); - if (ret) - return ret; - } - - if (rtd->cpu_dai->driver->capture.channels_min) { - ret = spear_pcm_preallocate_dma_buffer(rtd->pcm, - SNDRV_PCM_STREAM_CAPTURE, - spear_pcm_hardware.buffer_bytes_max); - if (ret) - return ret; - } - - return 0; -} - -static struct snd_soc_platform_driver spear_soc_platform = { - .ops = &spear_pcm_ops, - .pcm_new = spear_pcm_new, - .pcm_free = spear_pcm_free, +static const struct snd_dmaengine_pcm_config spear_dmaengine_pcm_config = { + .pcm_hardware = &spear_pcm_hardware, + .compat_request_channel = spear_pcm_request_chan, + .prealloc_buffer_size = 16 * 1024, };
static int spear_soc_platform_probe(struct platform_device *pdev) { - return snd_soc_register_platform(&pdev->dev, &spear_soc_platform); + return snd_dmaengine_pcm_register(&pdev->dev, + &spear_dmaengine_pcm_config, + SND_DMAENGINE_PCM_FLAG_NO_DT | + SND_DMAENGINE_PCM_FLAG_COMPAT); }
static int spear_soc_platform_remove(struct platform_device *pdev) { - snd_soc_unregister_platform(&pdev->dev); - + snd_dmaengine_pcm_unregister(&pdev->dev); return 0; }
Unfortunately there are still quite a few platforms with a dmaengine driver which do not support reporting the number of bytes left to transfer. If we want to support these platforms in the generic dmaengine PCM driver we have.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- include/sound/dmaengine_pcm.h | 5 +++++ sound/soc/soc-generic-dmaengine-pcm.c | 23 ++++++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-)
diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h index 1a7897a..e705286 100644 --- a/include/sound/dmaengine_pcm.h +++ b/include/sound/dmaengine_pcm.h @@ -86,6 +86,11 @@ void snd_dmaengine_pcm_set_config_from_dai_data( * makes sense if SND_DMAENGINE_PCM_FLAG_COMPAT is set as well. */ #define SND_DMAENGINE_PCM_FLAG_NO_DT BIT(1) +/* + * The platforms dmaengine driver does not support reporting the ammount of + * bytes that are still left to transfer. + */ +#define SND_DMAENGINE_PCM_FLAG_NO_RESIDUE BIT(2)
/** * struct snd_dmaengine_pcm_config - Configuration data for dmaengine based PCM diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index d6e6380..ae0c37e 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -193,6 +193,23 @@ static const struct snd_soc_platform_driver dmaengine_pcm_platform = { .probe_order = SND_SOC_COMP_ORDER_LATE, };
+static const struct snd_pcm_ops dmaengine_no_residue_pcm_ops = { + .open = dmaengine_pcm_open, + .close = snd_dmaengine_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = dmaengine_pcm_hw_params, + .hw_free = snd_pcm_lib_free_pages, + .trigger = snd_dmaengine_pcm_trigger, + .pointer = snd_dmaengine_pcm_pointer_no_residue, +}; + +static const struct snd_soc_platform_driver dmaengine_no_residue_pcm_platform = { + .ops = &dmaengine_no_residue_pcm_ops, + .pcm_new = dmaengine_pcm_new, + .pcm_free = dmaengine_pcm_free, + .probe_order = SND_SOC_COMP_ORDER_LATE, +}; + static const char * const dmaengine_pcm_dma_channel_names[] = { [SNDRV_PCM_STREAM_PLAYBACK] = "tx", [SNDRV_PCM_STREAM_CAPTURE] = "rx", @@ -226,7 +243,11 @@ int snd_dmaengine_pcm_register(struct device *dev, } }
- return snd_soc_add_platform(dev, &pcm->platform, + if (flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE) + return snd_soc_add_platform(dev, &pcm->platform, + &dmaengine_no_residue_pcm_platform); + else + return snd_soc_add_platform(dev, &pcm->platform, &dmaengine_pcm_platform); } EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_register);
This allows us to access the DAI DMA data when we create the PCM. We'll use this when converting mxs to generic DMA engine PCM driver.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- sound/soc/mxs/mxs-saif.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c index abf4ddf..b563141 100644 --- a/sound/soc/mxs/mxs-saif.c +++ b/sound/soc/mxs/mxs-saif.c @@ -369,7 +369,6 @@ static int mxs_saif_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai); - snd_soc_dai_set_dma_data(cpu_dai, substream, &saif->dma_param);
/* clear error status to 0 for each re-open */ saif->fifo_underrun = 0; @@ -605,6 +604,8 @@ static int mxs_saif_dai_probe(struct snd_soc_dai *dai) struct mxs_saif *saif = dev_get_drvdata(dai->dev);
snd_soc_dai_set_drvdata(dai, saif); + dai->playback_dma_data = &saif->dma_param; + dai->capture_dma_data = &saif->dma_param;
return 0; }
Use the generic dmaengine PCM driver instead of a custom implementation.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- sound/soc/mxs/Kconfig | 2 +- sound/soc/mxs/mxs-pcm.c | 135 ++++-------------------------------------------- 2 files changed, 11 insertions(+), 126 deletions(-)
diff --git a/sound/soc/mxs/Kconfig b/sound/soc/mxs/Kconfig index b6fa776..78d321c 100644 --- a/sound/soc/mxs/Kconfig +++ b/sound/soc/mxs/Kconfig @@ -1,7 +1,7 @@ menuconfig SND_MXS_SOC tristate "SoC Audio for Freescale MXS CPUs" depends on ARCH_MXS - select SND_SOC_DMAENGINE_PCM + select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for codecs attached to the MXS SAIF interface. diff --git a/sound/soc/mxs/mxs-pcm.c b/sound/soc/mxs/mxs-pcm.c index 7bceb16..d798de5 100644 --- a/sound/soc/mxs/mxs-pcm.c +++ b/sound/soc/mxs/mxs-pcm.c @@ -18,27 +18,18 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
-#include <linux/clk.h> -#include <linux/delay.h> #include <linux/device.h> -#include <linux/dma-mapping.h> #include <linux/init.h> -#include <linux/interrupt.h> #include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/slab.h> -#include <linux/dmaengine.h>
#include <sound/core.h> -#include <sound/initval.h> #include <sound/pcm.h> -#include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/dmaengine_pcm.h>
#include "mxs-pcm.h"
-static struct snd_pcm_hardware snd_mxs_hardware = { +static const struct snd_pcm_hardware snd_mxs_hardware = { .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE | @@ -55,7 +46,6 @@ static struct snd_pcm_hardware snd_mxs_hardware = { .periods_max = 52, .buffer_bytes_max = 64 * 1024, .fifo_size = 32, - };
static bool filter(struct dma_chan *chan, void *param) @@ -73,129 +63,24 @@ static bool filter(struct dma_chan *chan, void *param) return true; }
-static int snd_mxs_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); - - return 0; -} - -static int snd_mxs_open(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - - snd_soc_set_runtime_hwparams(substream, &snd_mxs_hardware); - - return snd_dmaengine_pcm_open_request_chan(substream, filter, - snd_soc_dai_get_dma_data(rtd->cpu_dai, substream)); -} - -static int snd_mxs_pcm_mmap(struct snd_pcm_substream *substream, - struct vm_area_struct *vma) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - - return dma_mmap_writecombine(substream->pcm->card->dev, vma, - runtime->dma_area, - runtime->dma_addr, - runtime->dma_bytes); -} - -static struct snd_pcm_ops mxs_pcm_ops = { - .open = snd_mxs_open, - .close = snd_dmaengine_pcm_close_release_chan, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_mxs_pcm_hw_params, - .trigger = snd_dmaengine_pcm_trigger, - .pointer = snd_dmaengine_pcm_pointer_no_residue, - .mmap = snd_mxs_pcm_mmap, -}; - -static int mxs_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) -{ - struct snd_pcm_substream *substream = pcm->streams[stream].substream; - struct snd_dma_buffer *buf = &substream->dma_buffer; - size_t size = snd_mxs_hardware.buffer_bytes_max; - - buf->dev.type = SNDRV_DMA_TYPE_DEV; - buf->dev.dev = pcm->card->dev; - buf->private_data = NULL; - buf->area = dma_alloc_writecombine(pcm->card->dev, size, - &buf->addr, GFP_KERNEL); - if (!buf->area) - return -ENOMEM; - buf->bytes = size; - - return 0; -} - -static u64 mxs_pcm_dmamask = DMA_BIT_MASK(32); -static int mxs_pcm_new(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_card *card = rtd->card->snd_card; - struct snd_pcm *pcm = rtd->pcm; - int ret = 0; - - if (!card->dev->dma_mask) - card->dev->dma_mask = &mxs_pcm_dmamask; - if (!card->dev->coherent_dma_mask) - card->dev->coherent_dma_mask = DMA_BIT_MASK(32); - - if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { - ret = mxs_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_PLAYBACK); - if (ret) - goto out; - } - - if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { - ret = mxs_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_CAPTURE); - if (ret) - goto out; - } - -out: - return ret; -} - -static void mxs_pcm_free(struct snd_pcm *pcm) -{ - struct snd_pcm_substream *substream; - struct snd_dma_buffer *buf; - int stream; - - for (stream = 0; stream < 2; stream++) { - substream = pcm->streams[stream].substream; - if (!substream) - continue; - - buf = &substream->dma_buffer; - if (!buf->area) - continue; - - dma_free_writecombine(pcm->card->dev, buf->bytes, - buf->area, buf->addr); - buf->area = NULL; - } -} - -static struct snd_soc_platform_driver mxs_soc_platform = { - .ops = &mxs_pcm_ops, - .pcm_new = mxs_pcm_new, - .pcm_free = mxs_pcm_free, +static const struct snd_dmaengine_pcm_config mxs_dmaengine_pcm_config = { + .pcm_hardware = &snd_mxs_hardware, + .compat_filter_fn = filter, + .prealloc_buffer_size = 64 * 1024, };
int mxs_pcm_platform_register(struct device *dev) { - return snd_soc_register_platform(dev, &mxs_soc_platform); + return snd_dmaengine_pcm_register(dev, &mxs_dmaengine_pcm_config, + SND_DMAENGINE_PCM_FLAG_NO_RESIDUE | + SND_DMAENGINE_PCM_FLAG_NO_DT | + SND_DMAENGINE_PCM_FLAG_COMPAT); } EXPORT_SYMBOL_GPL(mxs_pcm_platform_register);
void mxs_pcm_platform_unregister(struct device *dev) { - snd_soc_unregister_platform(dev); + snd_dmaengine_pcm_unregister(dev); } EXPORT_SYMBOL_GPL(mxs_pcm_platform_unregister);
On Mon, Apr 15, 2013 at 07:19:57PM +0200, Lars-Peter Clausen wrote:
Use the generic dmaengine PCM driver instead of a custom implementation.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de
sound/soc/mxs/Kconfig | 2 +- sound/soc/mxs/mxs-pcm.c | 135 ++++-------------------------------------------- 2 files changed, 11 insertions(+), 126 deletions(-)
After applying the patch, mxs audio is broken as below.
[ 2.003200] mxs-saif 80042000.saif: Missing dma channel for stream: 1 [ 2.010220] mmc0: new high speed SDHC card at address e624 [ 2.016453] mxs-saif 80042000.saif: ASoC: pcm constructor failed: -22 [ 2.022964] mxs-sgtl5000 sound.12: ASoC: can't create pcm HiFi Playback :-22 [ 2.030310] mxs-sgtl5000 sound.12: ASoC: failed to instantiate card -22
It's caused by that dmaengine_pcm_new() will call dmaengine_pcm_compat_request_channel() twice to request the same mxs-dma channel, and the second call will fail.
Shawn
On 04/17/2013 10:43 AM, Shawn Guo wrote:
On Mon, Apr 15, 2013 at 07:19:57PM +0200, Lars-Peter Clausen wrote:
Use the generic dmaengine PCM driver instead of a custom implementation.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de
sound/soc/mxs/Kconfig | 2 +- sound/soc/mxs/mxs-pcm.c | 135 ++++-------------------------------------------- 2 files changed, 11 insertions(+), 126 deletions(-)
After applying the patch, mxs audio is broken as below.
[ 2.003200] mxs-saif 80042000.saif: Missing dma channel for stream: 1 [ 2.010220] mmc0: new high speed SDHC card at address e624 [ 2.016453] mxs-saif 80042000.saif: ASoC: pcm constructor failed: -22 [ 2.022964] mxs-sgtl5000 sound.12: ASoC: can't create pcm HiFi Playback :-22 [ 2.030310] mxs-sgtl5000 sound.12: ASoC: failed to instantiate card -22
It's caused by that dmaengine_pcm_new() will call dmaengine_pcm_compat_request_channel() twice to request the same mxs-dma channel, and the second call will fail.
Shawn
Can you see whether the patch below fixes it?
diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c index b563141..675c348 100644 --- a/sound/soc/mxs/mxs-saif.c +++ b/sound/soc/mxs/mxs-saif.c @@ -611,21 +611,28 @@ static int mxs_saif_dai_probe(struct snd_soc_dai *dai) }
static struct snd_soc_dai_driver mxs_saif_dai = { - .name = "mxs-saif", - .probe = mxs_saif_dai_probe, - .playback = { - .channels_min = 2, - .channels_max = 2, - .rates = MXS_SAIF_RATES, - .formats = MXS_SAIF_FORMATS, + { + .name = "mxs-saif", + .probe = mxs_saif_dai_probe, + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = MXS_SAIF_RATES, + .formats = MXS_SAIF_FORMATS, + }, + .ops = &mxs_saif_dai_ops, }, - .capture = { - .channels_min = 2, - .channels_max = 2, - .rates = MXS_SAIF_RATES, - .formats = MXS_SAIF_FORMATS, + { + .name = "mxs-saif", + .probe = mxs_saif_dai_probe, + .capture = { + .channels_min = 2, + .channels_max = 2, + .rates = MXS_SAIF_RATES, + .formats = MXS_SAIF_FORMATS, + }, + .ops = &mxs_saif_dai_ops, }, - .ops = &mxs_saif_dai_ops, };
static const struct snd_soc_component_driver mxs_saif_component = { @@ -769,7 +776,7 @@ static int mxs_saif_probe(struct platform_device *pdev) platform_set_drvdata(pdev, saif);
ret = snd_soc_register_component(&pdev->dev, &mxs_saif_component, - &mxs_saif_dai, 1); + &mxs_saif_dai[saif->id], 1); if (ret) { dev_err(&pdev->dev, "register DAI failed\n"); return ret;
On Wed, Apr 17, 2013 at 11:28:11AM +0200, Lars-Peter Clausen wrote:
Can you see whether the patch below fixes it?
No, it does not fix the problem.
Shawn
diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c index b563141..675c348 100644 --- a/sound/soc/mxs/mxs-saif.c +++ b/sound/soc/mxs/mxs-saif.c @@ -611,21 +611,28 @@ static int mxs_saif_dai_probe(struct snd_soc_dai *dai) }
static struct snd_soc_dai_driver mxs_saif_dai = {
- .name = "mxs-saif",
- .probe = mxs_saif_dai_probe,
- .playback = {
.channels_min = 2,
.channels_max = 2,
.rates = MXS_SAIF_RATES,
.formats = MXS_SAIF_FORMATS,
- {
.name = "mxs-saif",
.probe = mxs_saif_dai_probe,
.playback = {
.channels_min = 2,
.channels_max = 2,
.rates = MXS_SAIF_RATES,
.formats = MXS_SAIF_FORMATS,
},
},.ops = &mxs_saif_dai_ops,
- .capture = {
.channels_min = 2,
.channels_max = 2,
.rates = MXS_SAIF_RATES,
.formats = MXS_SAIF_FORMATS,
- {
.name = "mxs-saif",
.probe = mxs_saif_dai_probe,
.capture = {
.channels_min = 2,
.channels_max = 2,
.rates = MXS_SAIF_RATES,
.formats = MXS_SAIF_FORMATS,
},
},.ops = &mxs_saif_dai_ops,
- .ops = &mxs_saif_dai_ops,
};
static const struct snd_soc_component_driver mxs_saif_component = { @@ -769,7 +776,7 @@ static int mxs_saif_probe(struct platform_device *pdev) platform_set_drvdata(pdev, saif);
ret = snd_soc_register_component(&pdev->dev, &mxs_saif_component,
&mxs_saif_dai, 1);
if (ret) { dev_err(&pdev->dev, "register DAI failed\n"); return ret;&mxs_saif_dai[saif->id], 1);
On 04/17/2013 03:50 PM, Shawn Guo wrote:
On Wed, Apr 17, 2013 at 11:28:11AM +0200, Lars-Peter Clausen wrote:
Can you see whether the patch below fixes it?
No, it does not fix the problem.
Shawn
Could you try the other patch plus this one:
--- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -2018,9 +2018,11 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) if (cpu_dai->driver->capture.channels_min) capture = 1; } else { - if (codec_dai->driver->playback.channels_min) + if (codec_dai->driver->playback.channels_min && + cpu_dai->driver->playback.channels_min) playback = 1; - if (codec_dai->driver->capture.channels_min) + if (codec_dai->driver->capture.channels_min && + cpu_dai->driver->capture.channels_min) capture = 1; }
diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c index b563141..675c348 100644 --- a/sound/soc/mxs/mxs-saif.c +++ b/sound/soc/mxs/mxs-saif.c @@ -611,21 +611,28 @@ static int mxs_saif_dai_probe(struct snd_soc_dai *dai) }
static struct snd_soc_dai_driver mxs_saif_dai = {
- .name = "mxs-saif",
- .probe = mxs_saif_dai_probe,
- .playback = {
.channels_min = 2,
.channels_max = 2,
.rates = MXS_SAIF_RATES,
.formats = MXS_SAIF_FORMATS,
- {
.name = "mxs-saif",
.probe = mxs_saif_dai_probe,
.playback = {
.channels_min = 2,
.channels_max = 2,
.rates = MXS_SAIF_RATES,
.formats = MXS_SAIF_FORMATS,
},
},.ops = &mxs_saif_dai_ops,
- .capture = {
.channels_min = 2,
.channels_max = 2,
.rates = MXS_SAIF_RATES,
.formats = MXS_SAIF_FORMATS,
- {
.name = "mxs-saif",
.probe = mxs_saif_dai_probe,
.capture = {
.channels_min = 2,
.channels_max = 2,
.rates = MXS_SAIF_RATES,
.formats = MXS_SAIF_FORMATS,
},
},.ops = &mxs_saif_dai_ops,
- .ops = &mxs_saif_dai_ops,
};
static const struct snd_soc_component_driver mxs_saif_component = { @@ -769,7 +776,7 @@ static int mxs_saif_probe(struct platform_device *pdev) platform_set_drvdata(pdev, saif);
ret = snd_soc_register_component(&pdev->dev, &mxs_saif_component,
&mxs_saif_dai, 1);
if (ret) { dev_err(&pdev->dev, "register DAI failed\n"); return ret;&mxs_saif_dai[saif->id], 1);
On Thu, Apr 18, 2013 at 11:04:38AM +0200, Lars-Peter Clausen wrote:
On 04/17/2013 03:50 PM, Shawn Guo wrote:
On Wed, Apr 17, 2013 at 11:28:11AM +0200, Lars-Peter Clausen wrote:
Can you see whether the patch below fixes it?
No, it does not fix the problem.
Shawn
Could you try the other patch plus this one:
Yes, it works now. Thanks for the fixing, Lars.
One small correction below on the first patch.
--- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -2018,9 +2018,11 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) if (cpu_dai->driver->capture.channels_min) capture = 1; } else {
if (codec_dai->driver->playback.channels_min)
if (codec_dai->driver->playback.channels_min &&
cpu_dai->driver->playback.channels_min) playback = 1;
if (codec_dai->driver->capture.channels_min)
if (codec_dai->driver->capture.channels_min &&
}cpu_dai->driver->capture.channels_min) capture = 1;
diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c index b563141..675c348 100644 --- a/sound/soc/mxs/mxs-saif.c +++ b/sound/soc/mxs/mxs-saif.c @@ -611,21 +611,28 @@ static int mxs_saif_dai_probe(struct snd_soc_dai *dai) }
static struct snd_soc_dai_driver mxs_saif_dai = {
It should be mxs_saif_dai[] now.
Shawn
- .name = "mxs-saif",
- .probe = mxs_saif_dai_probe,
- .playback = {
.channels_min = 2,
.channels_max = 2,
.rates = MXS_SAIF_RATES,
.formats = MXS_SAIF_FORMATS,
- {
.name = "mxs-saif",
.probe = mxs_saif_dai_probe,
.playback = {
.channels_min = 2,
.channels_max = 2,
.rates = MXS_SAIF_RATES,
.formats = MXS_SAIF_FORMATS,
},
},.ops = &mxs_saif_dai_ops,
- .capture = {
.channels_min = 2,
.channels_max = 2,
.rates = MXS_SAIF_RATES,
.formats = MXS_SAIF_FORMATS,
- {
.name = "mxs-saif",
.probe = mxs_saif_dai_probe,
.capture = {
.channels_min = 2,
.channels_max = 2,
.rates = MXS_SAIF_RATES,
.formats = MXS_SAIF_FORMATS,
},
},.ops = &mxs_saif_dai_ops,
- .ops = &mxs_saif_dai_ops,
};
static const struct snd_soc_component_driver mxs_saif_component = { @@ -769,7 +776,7 @@ static int mxs_saif_probe(struct platform_device *pdev) platform_set_drvdata(pdev, saif);
ret = snd_soc_register_component(&pdev->dev, &mxs_saif_component,
&mxs_saif_dai, 1);
if (ret) { dev_err(&pdev->dev, "register DAI failed\n"); return ret;&mxs_saif_dai[saif->id], 1);
On 04/18/2013 05:00 PM, Shawn Guo wrote:
On Thu, Apr 18, 2013 at 11:04:38AM +0200, Lars-Peter Clausen wrote:
On 04/17/2013 03:50 PM, Shawn Guo wrote:
On Wed, Apr 17, 2013 at 11:28:11AM +0200, Lars-Peter Clausen wrote:
Can you see whether the patch below fixes it?
No, it does not fix the problem.
Shawn
Could you try the other patch plus this one:
Yes, it works now. Thanks for the fixing, Lars.
One small correction below on the first patch.
Ok, thanks for testing. Will resent it with the two other patches later today.
- Lars
On 04/18/2013 05:04 PM, Lars-Peter Clausen wrote:
On 04/18/2013 05:00 PM, Shawn Guo wrote:
On Thu, Apr 18, 2013 at 11:04:38AM +0200, Lars-Peter Clausen wrote:
On 04/17/2013 03:50 PM, Shawn Guo wrote:
On Wed, Apr 17, 2013 at 11:28:11AM +0200, Lars-Peter Clausen wrote:
Can you see whether the patch below fixes it?
No, it does not fix the problem.
Shawn
Could you try the other patch plus this one:
Yes, it works now. Thanks for the fixing, Lars.
One small correction below on the first patch.
Ok, thanks for testing. Will resent it with the two other patches later today.
- Lars
Hm, this is actually a bit more tricky. I missed that the direction of the SAIF is runtime programmable and not fixed. And while your probably won't switch between transmit and receive dynamically, whether a SAIF is used as transmitter or receiver is board specific. So I guess we need to add a devicetree property to describe this.
- Lars
On Wed, Apr 17, 2013 at 04:43:06PM +0800, Shawn Guo wrote:
On Mon, Apr 15, 2013 at 07:19:57PM +0200, Lars-Peter Clausen wrote:
Use the generic dmaengine PCM driver instead of a custom implementation.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de
sound/soc/mxs/Kconfig | 2 +- sound/soc/mxs/mxs-pcm.c | 135 ++++-------------------------------------------- 2 files changed, 11 insertions(+), 126 deletions(-)
After applying the patch, mxs audio is broken as below.
[ 2.003200] mxs-saif 80042000.saif: Missing dma channel for stream: 1 [ 2.010220] mmc0: new high speed SDHC card at address e624 [ 2.016453] mxs-saif 80042000.saif: ASoC: pcm constructor failed: -22 [ 2.022964] mxs-sgtl5000 sound.12: ASoC: can't create pcm HiFi Playback :-22 [ 2.030310] mxs-sgtl5000 sound.12: ASoC: failed to instantiate card -22
It's caused by that dmaengine_pcm_new() will call dmaengine_pcm_compat_request_channel() twice to request the same mxs-dma channel, and the second call will fail.
For purpose of debugging, the mxs audio can just work as before with the changes below.
Shawn
diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index ae0c37e..7d41fdd 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -148,6 +148,9 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd) if (!substream) continue;
+ if (i == SNDRV_PCM_STREAM_CAPTURE) + pcm->chan[i] = pcm->chan[SNDRV_PCM_STREAM_PLAYBACK]; + if (!pcm->chan[i] && pcm->compat) { pcm->chan[i] = dmaengine_pcm_compat_request_channel(rtd, substream);
This allows us to access the DAI DMA data when we create the PCM. We'll use this when converting imx to generic DMA engine PCM driver.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- sound/soc/fsl/fsl_ssi.c | 19 +++++++++++++------ sound/soc/fsl/imx-ssi.c | 22 ++++------------------ 2 files changed, 17 insertions(+), 24 deletions(-)
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 42366d7..0f0bed6 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -425,12 +425,6 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, ssi_private->second_stream = substream; }
- if (ssi_private->ssi_on_imx) - snd_soc_dai_set_dma_data(dai, substream, - (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? - &ssi_private->dma_params_tx : - &ssi_private->dma_params_rx); - return 0; }
@@ -552,6 +546,18 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream, } }
+static int fsl_ssi_dai_probe(struct snd_soc_dai *dai) +{ + struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(dai); + + if (ssi_private->ssi_on_imx) { + dai->playback_dma_data = &ssi_private->dma_params_tx; + dai->capture_dma_data = &ssi_private->dma_params_rx; + } + + return 0; +} + static const struct snd_soc_dai_ops fsl_ssi_dai_ops = { .startup = fsl_ssi_startup, .hw_params = fsl_ssi_hw_params, @@ -561,6 +567,7 @@ static const struct snd_soc_dai_ops fsl_ssi_dai_ops = {
/* Template for the CPU dai driver structure */ static struct snd_soc_dai_driver fsl_ssi_dai_template = { + .probe = fsl_ssi_dai_probe, .playback = { /* The SSI does not support monaural audio. */ .channels_min = 2, diff --git a/sound/soc/fsl/imx-ssi.c b/sound/soc/fsl/imx-ssi.c index 4ce2d60..902fab0 100644 --- a/sound/soc/fsl/imx-ssi.c +++ b/sound/soc/fsl/imx-ssi.c @@ -232,23 +232,6 @@ static int imx_ssi_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, return 0; }
-static int imx_ssi_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *cpu_dai) -{ - struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai); - struct snd_dmaengine_dai_dma_data *dma_data; - - /* Tx/Rx config */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - dma_data = &ssi->dma_params_tx; - else - dma_data = &ssi->dma_params_rx; - - snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); - - return 0; -} - /* * Should only be called when port is inactive (i.e. SSIEN = 0), * although can be called multiple times by upper layers. @@ -353,7 +336,6 @@ static int imx_ssi_trigger(struct snd_pcm_substream *substream, int cmd, }
static const struct snd_soc_dai_ops imx_ssi_pcm_dai_ops = { - .startup = imx_ssi_startup, .hw_params = imx_ssi_hw_params, .set_fmt = imx_ssi_set_dai_fmt, .set_clkdiv = imx_ssi_set_dai_clkdiv, @@ -373,6 +355,10 @@ static int imx_ssi_dai_probe(struct snd_soc_dai *dai) SSI_SFCSR_RFWM0(ssi->dma_params_rx.maxburst); writel(val, ssi->base + SSI_SFCSR);
+ /* Tx/Rx config */ + dai->playback_dma_data = &ssi->dma_params_tx; + dai->capture_dma_data = &ssi->dma_params_rx; + return 0; }
Use the generic dmaengine PCM driver instead of a custom implementation.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- sound/soc/fsl/Kconfig | 2 +- sound/soc/fsl/imx-pcm-dma.c | 76 +++++++++------------------------------------ sound/soc/fsl/imx-pcm.c | 6 +++- sound/soc/fsl/imx-pcm.h | 5 +++ 4 files changed, 26 insertions(+), 63 deletions(-)
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 3b98159..3843a18 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -118,7 +118,7 @@ config SND_SOC_IMX_PCM_FIQ
config SND_SOC_IMX_PCM_DMA bool - select SND_SOC_DMAENGINE_PCM + select SND_SOC_GENERIC_DMAENGINE_PCM select SND_SOC_IMX_PCM
config SND_SOC_IMX_AUDMUX diff --git a/sound/soc/fsl/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c index c664782..c246fb5 100644 --- a/sound/soc/fsl/imx-pcm-dma.c +++ b/sound/soc/fsl/imx-pcm-dma.c @@ -11,22 +11,12 @@ * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. */ -#include <linux/clk.h> -#include <linux/delay.h> -#include <linux/device.h> -#include <linux/dma-mapping.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/module.h> #include <linux/platform_device.h> -#include <linux/slab.h> #include <linux/dmaengine.h> #include <linux/types.h>
#include <sound/core.h> -#include <sound/initval.h> #include <sound/pcm.h> -#include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/dmaengine_pcm.h>
@@ -44,32 +34,7 @@ static bool filter(struct dma_chan *chan, void *param) return true; }
-static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); - struct dma_slave_config slave_config; - int ret; - - ret = snd_hwparams_to_dma_slave_config(substream, params, &slave_config); - if (ret) - return ret; - - snd_dmaengine_pcm_set_config_from_dai_data(substream, - snd_soc_dai_get_dma_data(rtd->cpu_dai, substream), - &slave_config); - - ret = dmaengine_slave_config(chan, &slave_config); - if (ret) - return ret; - - snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); - - return 0; -} - -static struct snd_pcm_hardware snd_imx_hardware = { +static const struct snd_pcm_hardware imx_pcm_hardware = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | @@ -88,33 +53,22 @@ static struct snd_pcm_hardware snd_imx_hardware = { .fifo_size = 0, };
-static int snd_imx_open(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - - snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware); - - return snd_dmaengine_pcm_open(substream, filter, - snd_soc_dai_get_dma_data(rtd->cpu_dai, substream)); -} - -static struct snd_pcm_ops imx_pcm_ops = { - .open = snd_imx_open, - .close = snd_dmaengine_pcm_close_release_chan, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_imx_pcm_hw_params, - .trigger = snd_dmaengine_pcm_trigger, - .pointer = snd_dmaengine_pcm_pointer_no_residue, - .mmap = snd_imx_pcm_mmap, -}; - -static struct snd_soc_platform_driver imx_soc_platform_mx2 = { - .ops = &imx_pcm_ops, - .pcm_new = imx_pcm_new, - .pcm_free = imx_pcm_free, +static const struct snd_dmaengine_pcm_config imx_dmaengine_pcm_config = { + .pcm_hardware = &imx_pcm_hardware, + .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, + .compat_filter_fn = filter, + .prealloc_buffer_size = IMX_SSI_DMABUF_SIZE, };
int imx_pcm_dma_init(struct platform_device *pdev) { - return snd_soc_register_platform(&pdev->dev, &imx_soc_platform_mx2); + return snd_dmaengine_pcm_register(&pdev->dev, &imx_dmaengine_pcm_config, + SND_DMAENGINE_PCM_FLAG_NO_RESIDUE | + SND_DMAENGINE_PCM_FLAG_NO_DT | + SND_DMAENGINE_PCM_FLAG_COMPAT); +} + +void imx_pcm_dma_exit(struct platform_device *pdev) +{ + snd_dmaengine_pcm_unregister(&pdev->dev); } diff --git a/sound/soc/fsl/imx-pcm.c b/sound/soc/fsl/imx-pcm.c index 0d0625b..c498964 100644 --- a/sound/soc/fsl/imx-pcm.c +++ b/sound/soc/fsl/imx-pcm.c @@ -114,7 +114,11 @@ static int imx_pcm_probe(struct platform_device *pdev)
static int imx_pcm_remove(struct platform_device *pdev) { - snd_soc_unregister_platform(&pdev->dev); + if (strcmp(pdev->id_entry->name, "imx-fiq-pcm-audio") == 0) + snd_soc_unregister_platform(&pdev->dev); + else + imx_pcm_dma_exit(pdev); + return 0; }
diff --git a/sound/soc/fsl/imx-pcm.h b/sound/soc/fsl/imx-pcm.h index be9cc64..b7fa0d7 100644 --- a/sound/soc/fsl/imx-pcm.h +++ b/sound/soc/fsl/imx-pcm.h @@ -39,11 +39,16 @@ void imx_pcm_free(struct snd_pcm *pcm);
#ifdef CONFIG_SND_SOC_IMX_PCM_DMA int imx_pcm_dma_init(struct platform_device *pdev); +void imx_pcm_dma_exit(struct platform_device *pdev); #else static inline int imx_pcm_dma_init(struct platform_device *pdev) { return -ENODEV; } + +static inline void imx_pcm_dma_exit(struct platform_device *pdev) +{ +} #endif
#ifdef CONFIG_SND_SOC_IMX_PCM_FIQ
This allows us to access the DAI DMA data when we create the PCM. We'll use this when converting omap to generic DMA engine PCM driver.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- sound/soc/omap/omap-dmic.c | 4 +++- sound/soc/omap/omap-hdmi.c | 12 ++++++++++-- sound/soc/omap/omap-mcbsp.c | 6 +++--- sound/soc/omap/omap-mcpdm.c | 7 ++++--- 4 files changed, 20 insertions(+), 9 deletions(-)
diff --git a/sound/soc/omap/omap-dmic.c b/sound/soc/omap/omap-dmic.c index 2ad0370..257b5e5 100644 --- a/sound/soc/omap/omap-dmic.c +++ b/sound/soc/omap/omap-dmic.c @@ -114,7 +114,6 @@ static int omap_dmic_dai_startup(struct snd_pcm_substream *substream,
mutex_unlock(&dmic->mutex);
- snd_soc_dai_set_dma_data(dai, substream, &dmic->dma_data); return ret; }
@@ -418,6 +417,9 @@ static int omap_dmic_probe(struct snd_soc_dai *dai)
/* Configure DMIC threshold value */ dmic->threshold = OMAP_DMIC_THRES_MAX - 3; + + dai->playback_dma_data = &dmic->dma_data; + return 0; }
diff --git a/sound/soc/omap/omap-hdmi.c b/sound/soc/omap/omap-hdmi.c index ced3b88..d5c5cf4 100644 --- a/sound/soc/omap/omap-hdmi.c +++ b/sound/soc/omap/omap-hdmi.c @@ -69,8 +69,6 @@ static int omap_hdmi_dai_startup(struct snd_pcm_substream *substream, return -ENODEV; }
- snd_soc_dai_set_dma_data(dai, substream, &priv->dma_data); - return 0; }
@@ -250,7 +248,17 @@ static const struct snd_soc_dai_ops omap_hdmi_dai_ops = { .shutdown = omap_hdmi_dai_shutdown, };
+static int omap_hdmi_dai_probe(struct snd_soc_dai *dai) +{ + struct hdmi_priv *priv = snd_soc_dai_get_drvdata(dai); + + dai->playback_dma_data = &priv->dma_data; + + return 0; +} + static struct snd_soc_dai_driver omap_hdmi_dai = { + .probe = omap_hdmi_dai_probe, .playback = { .channels_min = 2, .channels_max = 8, diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index eadbfb6..dd74933 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c @@ -149,9 +149,6 @@ 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; }
@@ -554,6 +551,9 @@ static int omap_mcbsp_probe(struct snd_soc_dai *dai)
pm_runtime_enable(mcbsp->dev);
+ dai->playback_dma_data = &mcbsp->dma_data[0]; + dai->capture_dma_data = &mcbsp->dma_data[1]; + return 0; }
diff --git a/sound/soc/omap/omap-mcpdm.c b/sound/soc/omap/omap-mcpdm.c index eb05c7e..d8817a6 100644 --- a/sound/soc/omap/omap-mcpdm.c +++ b/sound/soc/omap/omap-mcpdm.c @@ -266,9 +266,6 @@ static int omap_mcpdm_dai_startup(struct snd_pcm_substream *substream, } mutex_unlock(&mcpdm->mutex);
- snd_soc_dai_set_dma_data(dai, substream, - &mcpdm->dma_data[substream->stream]); - return 0; }
@@ -407,6 +404,10 @@ static int omap_mcpdm_probe(struct snd_soc_dai *dai) mcpdm->config[SNDRV_PCM_STREAM_PLAYBACK].threshold = 2; mcpdm->config[SNDRV_PCM_STREAM_CAPTURE].threshold = MCPDM_UP_THRES_MAX - 3; + + dai->playback_dma_data = &mcpdm->dma_data[0]; + dai->capture_dma_data = &mcpdm->dma_data[1]; + return ret; }
Use the generic dmaengine PCM driver instead of a custom implementation.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- sound/soc/omap/Kconfig | 2 +- sound/soc/omap/omap-pcm.c | 187 ++++------------------------------------------ 2 files changed, 17 insertions(+), 172 deletions(-)
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index 60259f2..fbb006b 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig @@ -1,7 +1,7 @@ config SND_OMAP_SOC tristate "SoC Audio for the Texas Instruments OMAP chips" depends on ARCH_OMAP && DMA_OMAP - select SND_SOC_DMAENGINE_PCM + select SND_SOC_GENERIC_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 c28e042..87dbc7b 100644 --- a/sound/soc/omap/omap-pcm.c +++ b/sound/soc/omap/omap-pcm.c @@ -22,13 +22,10 @@ * */
-#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>
@@ -54,187 +51,35 @@ static const struct snd_pcm_hardware omap_pcm_hardware = { .buffer_bytes_max = 128 * 1024, };
-/* 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) +static bool omap_pcm_filter_fn(struct dma_chan *chan, void *params) { - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct omap_pcm_dma_data *dma_data; - struct dma_slave_config config; - struct dma_chan *chan; - int err = 0; + struct snd_dmaengine_dai_dma_data *dma_data = params;
- dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); - - /* return if this is a bufferless transfer e.g. - * codec <--> BT codec or GSM modem -- lg FIXME */ - if (!dma_data) - return 0; - - snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); - runtime->dma_bytes = params_buffer_bytes(params); - - 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; - - snd_dmaengine_pcm_set_config_from_dai_data(substream, - snd_soc_dai_get_dma_data(rtd->cpu_dai, substream), - &config); - - return dmaengine_slave_config(chan, &config); -} - -static int omap_pcm_hw_free(struct snd_pcm_substream *substream) -{ - snd_pcm_set_runtime_buffer(substream, NULL); - return 0; -} - -static snd_pcm_uframes_t omap_pcm_pointer(struct snd_pcm_substream *substream) -{ - snd_pcm_uframes_t offset; - - if (pcm_omap1510()) - offset = snd_dmaengine_pcm_pointer_no_residue(substream); - else - offset = snd_dmaengine_pcm_pointer(substream); - - return offset; -} - -static int omap_pcm_open(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_dmaengine_dai_dma_data *dma_data; - - snd_soc_set_runtime_hwparams(substream, &omap_pcm_hardware); - - dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); - - return snd_dmaengine_pcm_open_request_chan(substream, - omap_dma_filter_fn, - dma_data->filter_data); -} - -static int omap_pcm_mmap(struct snd_pcm_substream *substream, - struct vm_area_struct *vma) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - - return dma_mmap_writecombine(substream->pcm->card->dev, vma, - runtime->dma_area, - runtime->dma_addr, - runtime->dma_bytes); + return omap_dma_filter_fn(chan, dma_data->filter_data); }
-static struct snd_pcm_ops omap_pcm_ops = { - .open = omap_pcm_open, - .close = snd_dmaengine_pcm_close_release_chan, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = omap_pcm_hw_params, - .hw_free = omap_pcm_hw_free, - .trigger = snd_dmaengine_pcm_trigger, - .pointer = omap_pcm_pointer, - .mmap = omap_pcm_mmap, +static const struct snd_dmaengine_pcm_config omap_dmaengine_pcm_config = { + .pcm_hardware = &omap_pcm_hardware, + .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, + .compat_filter_fn = omap_pcm_filter_fn, + .prealloc_buffer_size = 128 * 1024, };
-static u64 omap_pcm_dmamask = DMA_BIT_MASK(64); - -static int omap_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, - int stream) -{ - struct snd_pcm_substream *substream = pcm->streams[stream].substream; - struct snd_dma_buffer *buf = &substream->dma_buffer; - size_t size = omap_pcm_hardware.buffer_bytes_max; - - buf->dev.type = SNDRV_DMA_TYPE_DEV; - buf->dev.dev = pcm->card->dev; - buf->private_data = NULL; - buf->area = dma_alloc_writecombine(pcm->card->dev, size, - &buf->addr, GFP_KERNEL); - if (!buf->area) - return -ENOMEM; - - buf->bytes = size; - return 0; -} - -static void omap_pcm_free_dma_buffers(struct snd_pcm *pcm) -{ - struct snd_pcm_substream *substream; - struct snd_dma_buffer *buf; - int stream; - - for (stream = 0; stream < 2; stream++) { - substream = pcm->streams[stream].substream; - if (!substream) - continue; - - buf = &substream->dma_buffer; - if (!buf->area) - continue; - - dma_free_writecombine(pcm->card->dev, buf->bytes, - buf->area, buf->addr); - buf->area = NULL; - } -} - -static int omap_pcm_new(struct snd_soc_pcm_runtime *rtd) +static int omap_pcm_probe(struct platform_device *pdev) { - struct snd_card *card = rtd->card->snd_card; - struct snd_pcm *pcm = rtd->pcm; - int ret = 0; - - if (!card->dev->dma_mask) - card->dev->dma_mask = &omap_pcm_dmamask; - if (!card->dev->coherent_dma_mask) - card->dev->coherent_dma_mask = DMA_BIT_MASK(64); - - if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { - ret = omap_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_PLAYBACK); - if (ret) - goto out; - } - - if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { - ret = omap_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_CAPTURE); - if (ret) - goto out; - } - -out: - /* free preallocated buffers in case of error */ - if (ret) - omap_pcm_free_dma_buffers(pcm); + unsigned int flags = SND_DMAENGINE_PCM_FLAG_NO_DT | + SND_DMAENGINE_PCM_FLAG_COMPAT;
- return ret; -} - -static struct snd_soc_platform_driver omap_soc_platform = { - .ops = &omap_pcm_ops, - .pcm_new = omap_pcm_new, - .pcm_free = omap_pcm_free_dma_buffers, -}; + if (pcm_omap1510()) + flags |= SND_DMAENGINE_PCM_FLAG_NO_RESIDUE;
-static int omap_pcm_probe(struct platform_device *pdev) -{ - return snd_soc_register_platform(&pdev->dev, - &omap_soc_platform); + return snd_dmaengine_pcm_register(&pdev->dev, + &omap_dmaengine_pcm_config, flags); }
static int omap_pcm_remove(struct platform_device *pdev) { - snd_soc_unregister_platform(&pdev->dev); + snd_dmaengine_pcm_unregister(&pdev->dev); return 0; }
This allows us to access the DAI DMA data when we create the PCM. We'll use this when converting ep39xx to generic DMA engine PCM driver.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- sound/soc/cirrus/ep93xx-ac97.c | 14 ++++---------- sound/soc/cirrus/ep93xx-i2s.c | 14 ++++---------- 2 files changed, 8 insertions(+), 20 deletions(-)
diff --git a/sound/soc/cirrus/ep93xx-ac97.c b/sound/soc/cirrus/ep93xx-ac97.c index 7798fbd..ef65432 100644 --- a/sound/soc/cirrus/ep93xx-ac97.c +++ b/sound/soc/cirrus/ep93xx-ac97.c @@ -314,22 +314,15 @@ static int ep93xx_ac97_trigger(struct snd_pcm_substream *substream, return 0; }
-static int ep93xx_ac97_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) +static int ep93xx_ac97_dai_probe(struct snd_soc_dai *dai) { - struct ep93xx_dma_data *dma_data; + dai->playback_dma_data = &ep93xx_ac97_pcm_out; + dai->capture_dma_data = &ep93xx_ac97_pcm_in;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - dma_data = &ep93xx_ac97_pcm_out; - else - dma_data = &ep93xx_ac97_pcm_in; - - snd_soc_dai_set_dma_data(dai, substream, dma_data); return 0; }
static const struct snd_soc_dai_ops ep93xx_ac97_dai_ops = { - .startup = ep93xx_ac97_startup, .trigger = ep93xx_ac97_trigger, };
@@ -337,6 +330,7 @@ static struct snd_soc_dai_driver ep93xx_ac97_dai = { .name = "ep93xx-ac97", .id = 0, .ac97_control = 1, + .probe = ep93xx_ac97_dai_probe, .playback = { .stream_name = "AC97 Playback", .channels_min = 2, diff --git a/sound/soc/cirrus/ep93xx-i2s.c b/sound/soc/cirrus/ep93xx-i2s.c index 5c1102e..453a89c 100644 --- a/sound/soc/cirrus/ep93xx-i2s.c +++ b/sound/soc/cirrus/ep93xx-i2s.c @@ -60,7 +60,6 @@ struct ep93xx_i2s_info { struct clk *mclk; struct clk *sclk; struct clk *lrclk; - struct ep93xx_dma_data *dma_data; void __iomem *regs; };
@@ -139,15 +138,11 @@ static void ep93xx_i2s_disable(struct ep93xx_i2s_info *info, int stream) } }
-static int ep93xx_i2s_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) +static int ep93xx_i2s_dai_probe(struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai); - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + dai->playback_dma_data = ep93xx_i2s_dma_data[SNDRV_PCM_STREAM_PLAYBACK]; + dai->capute_dma_data = ep93xx_i2s_dma_data[SNDRV_PCM_STREAM_CAPTURE]
- snd_soc_dai_set_dma_data(cpu_dai, substream, - &info->dma_data[substream->stream]); return 0; }
@@ -338,7 +333,6 @@ static int ep93xx_i2s_resume(struct snd_soc_dai *dai) #endif
static const struct snd_soc_dai_ops ep93xx_i2s_dai_ops = { - .startup = ep93xx_i2s_startup, .shutdown = ep93xx_i2s_shutdown, .hw_params = ep93xx_i2s_hw_params, .set_sysclk = ep93xx_i2s_set_sysclk, @@ -349,6 +343,7 @@ static const struct snd_soc_dai_ops ep93xx_i2s_dai_ops = {
static struct snd_soc_dai_driver ep93xx_i2s_dai = { .symmetric_rates= 1, + .probe = ep93xx_i2s_dai_probe, .suspend = ep93xx_i2s_suspend, .resume = ep93xx_i2s_resume, .playback = { @@ -407,7 +402,6 @@ static int ep93xx_i2s_probe(struct platform_device *pdev) }
dev_set_drvdata(&pdev->dev, info); - info->dma_data = ep93xx_i2s_dma_data;
err = snd_soc_register_component(&pdev->dev, &ep93xx_i2s_component, &ep93xx_i2s_dai, 1);
Use the generic dmaengine PCM driver instead of a custom implementation.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- sound/soc/cirrus/Kconfig | 2 +- sound/soc/cirrus/ep93xx-i2s.c | 4 +- sound/soc/cirrus/ep93xx-pcm.c | 138 ++++-------------------------------------- 3 files changed, 14 insertions(+), 130 deletions(-)
diff --git a/sound/soc/cirrus/Kconfig b/sound/soc/cirrus/Kconfig index 88143db..2c20f01 100644 --- a/sound/soc/cirrus/Kconfig +++ b/sound/soc/cirrus/Kconfig @@ -1,7 +1,7 @@ config SND_EP93XX_SOC tristate "SoC Audio support for the Cirrus Logic EP93xx series" depends on ARCH_EP93XX && SND_SOC - select SND_SOC_DMAENGINE_PCM + select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for codecs attached to the EP93xx I2S or AC97 interfaces. diff --git a/sound/soc/cirrus/ep93xx-i2s.c b/sound/soc/cirrus/ep93xx-i2s.c index 453a89c..ef731e6 100644 --- a/sound/soc/cirrus/ep93xx-i2s.c +++ b/sound/soc/cirrus/ep93xx-i2s.c @@ -140,8 +140,8 @@ static void ep93xx_i2s_disable(struct ep93xx_i2s_info *info, int stream)
static int ep93xx_i2s_dai_probe(struct snd_soc_dai *dai) { - dai->playback_dma_data = ep93xx_i2s_dma_data[SNDRV_PCM_STREAM_PLAYBACK]; - dai->capute_dma_data = ep93xx_i2s_dma_data[SNDRV_PCM_STREAM_CAPTURE] + dai->playback_dma_data = &ep93xx_i2s_dma_data[SNDRV_PCM_STREAM_PLAYBACK]; + dai->capture_dma_data = &ep93xx_i2s_dma_data[SNDRV_PCM_STREAM_CAPTURE];
return 0; } diff --git a/sound/soc/cirrus/ep93xx-pcm.c b/sound/soc/cirrus/ep93xx-pcm.c index 4880326..0e9f56e 100644 --- a/sound/soc/cirrus/ep93xx-pcm.c +++ b/sound/soc/cirrus/ep93xx-pcm.c @@ -14,20 +14,14 @@
#include <linux/module.h> #include <linux/init.h> -#include <linux/device.h> -#include <linux/slab.h> +#include <linux/platform_device.h> #include <linux/dmaengine.h> -#include <linux/dma-mapping.h>
-#include <sound/core.h> #include <sound/pcm.h> -#include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/dmaengine_pcm.h>
#include <linux/platform_data/dma-ep93xx.h> -#include <mach/hardware.h> -#include <mach/ep93xx-regs.h>
static const struct snd_pcm_hardware ep93xx_pcm_hardware = { .info = (SNDRV_PCM_INFO_MMAP | @@ -63,134 +57,24 @@ static bool ep93xx_pcm_dma_filter(struct dma_chan *chan, void *filter_param) return false; }
-static int ep93xx_pcm_open(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - - snd_soc_set_runtime_hwparams(substream, &ep93xx_pcm_hardware); - - return snd_dmaengine_pcm_open_request_chan(substream, - ep93xx_pcm_dma_filter, - snd_soc_dai_get_dma_data(rtd->cpu_dai, substream)); -} - -static int ep93xx_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); - - return 0; -} - -static int ep93xx_pcm_hw_free(struct snd_pcm_substream *substream) -{ - snd_pcm_set_runtime_buffer(substream, NULL); - return 0; -} - -static int ep93xx_pcm_mmap(struct snd_pcm_substream *substream, - struct vm_area_struct *vma) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - - return dma_mmap_writecombine(substream->pcm->card->dev, vma, - runtime->dma_area, - runtime->dma_addr, - runtime->dma_bytes); -} - -static struct snd_pcm_ops ep93xx_pcm_ops = { - .open = ep93xx_pcm_open, - .close = snd_dmaengine_pcm_close_release_chan, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = ep93xx_pcm_hw_params, - .hw_free = ep93xx_pcm_hw_free, - .trigger = snd_dmaengine_pcm_trigger, - .pointer = snd_dmaengine_pcm_pointer_no_residue, - .mmap = ep93xx_pcm_mmap, -}; - -static int ep93xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) -{ - struct snd_pcm_substream *substream = pcm->streams[stream].substream; - struct snd_dma_buffer *buf = &substream->dma_buffer; - size_t size = ep93xx_pcm_hardware.buffer_bytes_max; - - buf->dev.type = SNDRV_DMA_TYPE_DEV; - buf->dev.dev = pcm->card->dev; - buf->private_data = NULL; - buf->area = dma_alloc_writecombine(pcm->card->dev, size, - &buf->addr, GFP_KERNEL); - buf->bytes = size; - - return (buf->area == NULL) ? -ENOMEM : 0; -} - -static void ep93xx_pcm_free_dma_buffers(struct snd_pcm *pcm) -{ - struct snd_pcm_substream *substream; - struct snd_dma_buffer *buf; - int stream; - - for (stream = 0; stream < 2; stream++) { - substream = pcm->streams[stream].substream; - if (!substream) - continue; - - buf = &substream->dma_buffer; - if (!buf->area) - continue; - - dma_free_writecombine(pcm->card->dev, buf->bytes, buf->area, - buf->addr); - buf->area = NULL; - } -} - -static u64 ep93xx_pcm_dmamask = DMA_BIT_MASK(32); - -static int ep93xx_pcm_new(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_card *card = rtd->card->snd_card; - struct snd_pcm *pcm = rtd->pcm; - int ret = 0; - - if (!card->dev->dma_mask) - card->dev->dma_mask = &ep93xx_pcm_dmamask; - if (!card->dev->coherent_dma_mask) - card->dev->coherent_dma_mask = DMA_BIT_MASK(32); - - if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { - ret = ep93xx_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_PLAYBACK); - if (ret) - return ret; - } - - if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { - ret = ep93xx_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_CAPTURE); - if (ret) - return ret; - } - - return 0; -} - -static struct snd_soc_platform_driver ep93xx_soc_platform = { - .ops = &ep93xx_pcm_ops, - .pcm_new = &ep93xx_pcm_new, - .pcm_free = &ep93xx_pcm_free_dma_buffers, +static const struct snd_dmaengine_pcm_config ep93xx_dmaengine_pcm_config = { + .pcm_hardware = &ep93xx_pcm_hardware, + .compat_filter_fn = ep93xx_pcm_dma_filter, + .prealloc_buffer_size = 131072, };
static int ep93xx_soc_platform_probe(struct platform_device *pdev) { - return snd_soc_register_platform(&pdev->dev, &ep93xx_soc_platform); + return snd_dmaengine_pcm_register(&pdev->dev, + &ep93xx_dmaengine_pcm_config, + SND_DMAENGINE_PCM_FLAG_NO_RESIDUE | + SND_DMAENGINE_PCM_FLAG_NO_DT | + SND_DMAENGINE_PCM_FLAG_COMPAT); }
static int ep93xx_soc_platform_remove(struct platform_device *pdev) { - snd_soc_unregister_platform(&pdev->dev); + snd_dmaengine_pcm_unregister(&pdev->dev); return 0; }
Use the generic dmaengine PCM driver instead of a custom implemention. There is a minor functional change, the ux500 PCM driver did not preallocate the audio buffer, while the generic dmaengine PCM driver will do this.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- include/sound/dmaengine_pcm.h | 2 +- sound/soc/ux500/Kconfig | 2 +- sound/soc/ux500/ux500_pcm.c | 159 +++++------------------------------------- 3 files changed, 19 insertions(+), 144 deletions(-)
diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h index e705286..b1d1150 100644 --- a/include/sound/dmaengine_pcm.h +++ b/include/sound/dmaengine_pcm.h @@ -87,7 +87,7 @@ void snd_dmaengine_pcm_set_config_from_dai_data( */ #define SND_DMAENGINE_PCM_FLAG_NO_DT BIT(1) /* - * The platforms dmaengine driver does not support reporting the ammount of + * The platforms dmaengine driver does not support reporting the amount of * bytes that are still left to transfer. */ #define SND_DMAENGINE_PCM_FLAG_NO_RESIDUE BIT(2) diff --git a/sound/soc/ux500/Kconfig b/sound/soc/ux500/Kconfig index 069330d..c73c590 100644 --- a/sound/soc/ux500/Kconfig +++ b/sound/soc/ux500/Kconfig @@ -16,7 +16,7 @@ config SND_SOC_UX500_PLAT_MSP_I2S config SND_SOC_UX500_PLAT_DMA tristate "Platform - DB8500 (DMA)" depends on SND_SOC_UX500 - select SND_SOC_DMAENGINE_PCM + select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y if you want to enable the Ux500 platform-driver.
diff --git a/sound/soc/ux500/ux500_pcm.c b/sound/soc/ux500/ux500_pcm.c index a7d4f04..b6e5ae2 100644 --- a/sound/soc/ux500/ux500_pcm.c +++ b/sound/soc/ux500/ux500_pcm.c @@ -40,7 +40,7 @@ #define UX500_PLATFORM_PERIODS_MAX 48 #define UX500_PLATFORM_BUFFER_BYTES_MAX (2048 * PAGE_SIZE)
-static struct snd_pcm_hardware ux500_pcm_hw = { +static const struct snd_pcm_hardware ux500_pcm_hw = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_RESUME | @@ -61,43 +61,23 @@ static struct snd_pcm_hardware ux500_pcm_hw = { .periods_max = UX500_PLATFORM_PERIODS_MAX, };
-static void ux500_pcm_dma_hw_free(struct device *dev, - struct snd_pcm_substream *substream) +static struct dma_chan *ux500_pcm_request_chan(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_substream *substream) { - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_dma_buffer *buf = runtime->dma_buffer_p; - - if (runtime->dma_area == NULL) - return; - - if (buf != &substream->dma_buffer) { - dma_free_coherent(buf->dev.dev, buf->bytes, buf->area, - buf->addr); - kfree(runtime->dma_buffer_p); - } - - snd_pcm_set_runtime_buffer(substream, NULL); -} - -static int ux500_pcm_open(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *dai = rtd->cpu_dai; struct device *dev = dai->dev; - int ret; - struct ux500_msp_dma_params *dma_params; u16 per_data_width, mem_data_width; struct stedma40_chan_cfg *dma_cfg; + struct ux500_msp_dma_params *dma_params;
dev_dbg(dev, "%s: MSP %d (%s): Enter.\n", __func__, dai->id, snd_pcm_stream_str(substream));
- dev_dbg(dev, "%s: Set runtime hwparams.\n", __func__); - snd_soc_set_runtime_hwparams(substream, &ux500_pcm_hw); + dma_params = snd_soc_dai_get_dma_data(dai, substream); + dma_cfg = dma_params->dma_cfg;
mem_data_width = STEDMA40_HALFWORD_WIDTH;
- dma_params = snd_soc_dai_get_dma_data(dai, substream); switch (dma_params->data_size) { case 32: per_data_width = STEDMA40_WORD_WIDTH; @@ -110,13 +90,8 @@ static int ux500_pcm_open(struct snd_pcm_substream *substream) break; default: per_data_width = STEDMA40_WORD_WIDTH; - dev_warn(rtd->platform->dev, - "%s: Unknown data-size (%d)! Assuming 32 bits.\n", - __func__, dma_params->data_size); }
- dma_cfg = dma_params->dma_cfg; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { dma_cfg->src_info.data_width = mem_data_width; dma_cfg->dst_info.data_width = per_data_width; @@ -125,123 +100,24 @@ static int ux500_pcm_open(struct snd_pcm_substream *substream) dma_cfg->dst_info.data_width = mem_data_width; }
- ret = snd_dmaengine_pcm_open_request_chan(substream, stedma40_filter, - dma_cfg); - if (ret) { - dev_dbg(dai->dev, - "%s: ERROR: snd_dmaengine_pcm_open failed (%d)!\n", - __func__, ret); - return ret; - } - - return 0; -} - -static int ux500_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_dma_buffer *buf = runtime->dma_buffer_p; - struct snd_soc_pcm_runtime *rtd = substream->private_data; - int ret = 0; - int size; - - dev_dbg(rtd->platform->dev, "%s: Enter\n", __func__); - - size = params_buffer_bytes(hw_params); - - if (buf) { - if (buf->bytes >= size) - goto out; - ux500_pcm_dma_hw_free(NULL, substream); - } - - if (substream->dma_buffer.area != NULL && - substream->dma_buffer.bytes >= size) { - buf = &substream->dma_buffer; - } else { - buf = kmalloc(sizeof(struct snd_dma_buffer), GFP_KERNEL); - if (!buf) - goto nomem; - - buf->dev.type = SNDRV_DMA_TYPE_DEV; - buf->dev.dev = NULL; - buf->area = dma_alloc_coherent(NULL, size, &buf->addr, - GFP_KERNEL); - buf->bytes = size; - buf->private_data = NULL; - - if (!buf->area) - goto free; - } - snd_pcm_set_runtime_buffer(substream, buf); - ret = 1; - out: - runtime->dma_bytes = size; - return ret; - - free: - kfree(buf); - nomem: - return -ENOMEM; -} - -static int ux500_pcm_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - - dev_dbg(rtd->platform->dev, "%s: Enter\n", __func__); - - ux500_pcm_dma_hw_free(NULL, substream); - - return 0; + return snd_dmaengine_pcm_request_channel(stedma40_filter, dma_cfg); }
-static int ux500_pcm_mmap(struct snd_pcm_substream *substream, - struct vm_area_struct *vma) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *rtd = substream->private_data; - - dev_dbg(rtd->platform->dev, "%s: Enter.\n", __func__); - - return dma_mmap_coherent(NULL, vma, runtime->dma_area, - runtime->dma_addr, runtime->dma_bytes); -} - -static struct snd_pcm_ops ux500_pcm_ops = { - .open = ux500_pcm_open, - .close = snd_dmaengine_pcm_close_release_chan, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = ux500_pcm_hw_params, - .hw_free = ux500_pcm_hw_free, - .trigger = snd_dmaengine_pcm_trigger, - .pointer = snd_dmaengine_pcm_pointer_no_residue, - .mmap = ux500_pcm_mmap -}; - -int ux500_pcm_new(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_pcm *pcm = rtd->pcm; - - dev_dbg(rtd->platform->dev, "%s: Enter (id = '%s').\n", __func__, - pcm->id); - - pcm->info_flags = 0; - - return 0; -} - -static struct snd_soc_platform_driver ux500_pcm_soc_drv = { - .ops = &ux500_pcm_ops, - .pcm_new = ux500_pcm_new, +static const struct snd_dmaengine_pcm_config ux500_dmaengine_pcm_config = { + .pcm_hardware = &ux500_pcm_hw, + .compat_request_channel = ux500_pcm_request_chan, + .prealloc_buffer_size = 128 * 1024, };
int ux500_pcm_register_platform(struct platform_device *pdev) { int ret;
- ret = snd_soc_register_platform(&pdev->dev, &ux500_pcm_soc_drv); + ret = snd_dmaengine_pcm_register(&pdev->dev, + &ux500_dmaengine_pcm_config, + SND_DMAENGINE_PCM_FLAG_NO_RESIDUE | + SND_DMAENGINE_PCM_FLAG_COMPAT | + SND_DMAENGINE_PCM_FLAG_NO_DT); if (ret < 0) { dev_err(&pdev->dev, "%s: ERROR: Failed to register platform '%s' (%d)!\n", @@ -255,8 +131,7 @@ EXPORT_SYMBOL_GPL(ux500_pcm_register_platform);
int ux500_pcm_unregister_platform(struct platform_device *pdev) { - snd_soc_unregister_platform(&pdev->dev); - + snd_dmaengine_pcm_unregister(&pdev->dev); return 0; } EXPORT_SYMBOL_GPL(ux500_pcm_unregister_platform);
On Mon, 15 Apr 2013, Lars-Peter Clausen wrote:
Use the generic dmaengine PCM driver instead of a custom implemention. There is a minor functional change, the ux500 PCM driver did not preallocate the audio buffer, while the generic dmaengine PCM driver will do this.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de
include/sound/dmaengine_pcm.h | 2 +- sound/soc/ux500/Kconfig | 2 +- sound/soc/ux500/ux500_pcm.c | 159 +++++------------------------------------- 3 files changed, 19 insertions(+), 144 deletions(-)
By rights you really need Ola's approval on this, but because:
a) ... this removes a hell of a lot of old code, and b) ... using the new DMA APIs _is_ the correct thing to do, and c) ... this aids me in getting the D40 DMA controller up-to-scratch, and d) ... I have patches which rely on these changes, and e) ... the driver is already pretty broken
... you can have my: Acked-by: Lee Jones lee.jones@linaro.org
On Mon, Apr 15, 2013 at 07:20:04PM +0200, Lars-Peter Clausen wrote:
Use the generic dmaengine PCM driver instead of a custom implemention. There is a minor functional change, the ux500 PCM driver did not preallocate the audio buffer, while the generic dmaengine PCM driver will do this.
Applied, thanks.
On Mon, Apr 15, 2013 at 07:19:47PM +0200, Lars-Peter Clausen wrote:
This series adds a (almost) generic dmaengine PCM driver for ASoC. The tegra, spear, omap, imx, mxs, ep93xx and ux500 platforms are converted to use the generic driver instead of their own custom implementation. The driver builds on top of the dmaengine PCM library and adds the missing bits. The driver was
This all looks pretty good modulo testing and so on.
- The size of the pre-allocated audio buffer. All platforms seem use different sizes and since some of them might be sensitive to changes to the buffer size this series keeps the original size for now. But we should probably try to decide on a sane default for this and maybe allow ASoC board drivers to overwrite it somehow.
Or the platform for that matter of course.
The driver uses snd_pcm_lib_preallocate_pages to preallocate the memory buffer. snd_pcm_lib_preallocate_pages internally uses dma_alloc_coherent, while most of the platforms converted in this series use dma_alloc_writecombine. Laxman Dewangan submitted a patch[1] a couple of months ago to add support for dma_alloc_writecombine to snd_pcm_lib_preallocate_pages, I always thought the patch had been merged, but it looks like I was mistaken. So if there are concerns about performance regression due to using coherent instead of writecombine mappings we'll have to postpone converting these platforms until Laxman's patch has been merged.
Can you resend that to Takashi, it sounds like if it's not been merged it's probably due to it getting dropped rather than any great objection?
On Mon, Apr 15, 2013 at 07:19:47PM +0200, Lars-Peter Clausen wrote:
The compat path of this series hasn't been runtime tested yet and neither have the platforms which are converted in this series. I probably broke one or two of them by accident, so please test the patches.
...
ASoC: imx: Setup dma data in DAI probe ASoC: imx: Use generic dmaengine PCM
Tested-by: Shawn Guo shawn.guo@linaro.org
participants (7)
-
Lars-Peter Clausen
-
Lee Jones
-
Mark Brown
-
Markus Pargmann
-
Peter Ujfalusi
-
Shawn Guo
-
Stephen Warren