From cd3ff762991ebf309e089ed5a87a8965f175f46a Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Sun, 14 Apr 2013 13:42:52 +0200 Subject: ASoC: fsl-ssi: Add SACNT definitions Add definitions for AC97 control register. Signed-off-by: Markus Pargmann Acked-by: Timur Tabi Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_ssi.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/fsl/fsl_ssi.h b/sound/soc/fsl/fsl_ssi.h index 2173000..e6b9a69 100644 --- a/sound/soc/fsl/fsl_ssi.h +++ b/sound/soc/fsl/fsl_ssi.h @@ -196,5 +196,13 @@ struct ccsr_ssi { #define CCSR_SSI_SOR_WAIT(x) (((x) & 3) << CCSR_SSI_SOR_WAIT_SHIFT) #define CCSR_SSI_SOR_SYNRST 0x00000001 +#define CCSR_SSI_SACNT_FRDIV(x) (((x) & 0x3f) << 5) +#define CCSR_SSI_SACNT_WR 0x00000010 +#define CCSR_SSI_SACNT_RD 0x00000008 +#define CCSR_SSI_SACNT_RDWR_MASK 0x00000018 +#define CCSR_SSI_SACNT_TIF 0x00000004 +#define CCSR_SSI_SACNT_FV 0x00000002 +#define CCSR_SSI_SACNT_AC97EN 0x00000001 + #endif -- cgit v1.1 From 7c1c1d4a7b4ca1266057a3632d27450f5575caf9 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 15 Apr 2013 19:19:48 +0200 Subject: ASoC: dmaengine-pcm: Make requesting the DMA channel at PCM open optional 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 Tested-by: Stephen Warren Tested-by: Shawn Guo Signed-off-by: Mark Brown --- 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 ++-- 10 files changed, 73 insertions(+), 43 deletions(-) (limited to 'sound/soc') 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..34993001 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 db75d72..ba66a3f 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 f91d08b..32d0811 100644 --- a/sound/soc/tegra/tegra_pcm.c +++ b/sound/soc/tegra/tegra_pcm.c @@ -66,7 +66,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; @@ -144,7 +144,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, -- cgit v1.1 From 71a45cda444f9c47bd63516cf4c24fb6d1ccb151 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 15 Apr 2013 19:19:49 +0200 Subject: ASoC: Add snd_soc_{add, remove}_platform 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 Tested-by: Stephen Warren Tested-by: Shawn Guo Signed-off-by: Mark Brown --- sound/soc/soc-core.c | 85 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 62 insertions(+), 23 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 4d24b5e..0e64af1 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3901,21 +3901,14 @@ void snd_soc_unregister_dais(struct device *dev, size_t count) EXPORT_SYMBOL_GPL(snd_soc_unregister_dais); /** - * 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) { @@ -3938,30 +3931,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); -- cgit v1.1 From 28c4468b00a1e55e08cc20117de968f7c6275441 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 15 Apr 2013 19:19:50 +0200 Subject: ASoC: Add a generic dmaengine_pcm driver 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 Tested-by: Stephen Warren Tested-by: Shawn Guo Signed-off-by: Mark Brown --- sound/soc/Kconfig | 4 + sound/soc/Makefile | 4 + sound/soc/soc-generic-dmaengine-pcm.c | 241 ++++++++++++++++++++++++++++++++++ 3 files changed, 249 insertions(+) create mode 100644 sound/soc/soc-generic-dmaengine-pcm.c (limited to 'sound/soc') 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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +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"); -- cgit v1.1 From c999836d37c6c1125e856f68877ae13952baa61a Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 15 Apr 2013 19:19:51 +0200 Subject: ASoC: dmaengine_pcm: Add support for compat platforms 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 Tested-by: Stephen Warren Tested-by: Shawn Guo Signed-off-by: Mark Brown --- sound/soc/soc-dmaengine-pcm.c | 14 ++++++++++++-- sound/soc/soc-generic-dmaengine-pcm.c | 32 +++++++++++++++++++++++++++----- 2 files changed, 39 insertions(+), 7 deletions(-) (limited to 'sound/soc') 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, -- cgit v1.1 From 11a8576a0a3f153784fc4fca28e6fcee83531135 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 15 Apr 2013 19:19:52 +0200 Subject: ASoC: tegra: Use generic dmaengine PCM Use the generic dmaengine PCM driver instead of a custom implementation. Signed-off-by: Lars-Peter Clausen Tested-by: Stephen Warren Signed-off-by: Mark Brown --- sound/soc/tegra/Kconfig | 2 +- sound/soc/tegra/tegra_pcm.c | 171 +++----------------------------------------- 2 files changed, 10 insertions(+), 163 deletions(-) (limited to 'sound/soc') 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 #include -#include #include #include #include @@ -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); -- cgit v1.1 From 610f780050090db1af024bd060f819478a656cd0 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 15 Apr 2013 19:19:55 +0200 Subject: ASoC: dmaengine-pcm: Add support for platforms which can't report residue 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 Tested-by: Stephen Warren Tested-by: Shawn Guo Signed-off-by: Mark Brown --- sound/soc/soc-generic-dmaengine-pcm.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) (limited to 'sound/soc') 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); -- cgit v1.1 From fc8ba7f94d84e1ccd351e41c778e36710834334e Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 15 Apr 2013 19:19:58 +0200 Subject: ASoC: imx: Setup dma data in DAI probe 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 Tested-by: Shawn Guo Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_ssi.c | 19 +++++++++++++------ sound/soc/fsl/imx-ssi.c | 22 ++++------------------ 2 files changed, 17 insertions(+), 24 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 8a7eac4..ab27ffa 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 7c84eb1..9128b7b 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; } -- cgit v1.1 From adaa3229fbb88532e0d460afad779efbfb92ffeb Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 15 Apr 2013 19:19:59 +0200 Subject: ASoC: imx: Use generic dmaengine PCM Use the generic dmaengine PCM driver instead of a custom implementation. Signed-off-by: Lars-Peter Clausen Tested-by: Shawn Guo Signed-off-by: Mark Brown --- 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(-) (limited to 'sound/soc') 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 -#include -#include -#include -#include -#include -#include #include -#include #include #include #include -#include #include -#include #include #include @@ -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 -- cgit v1.1 From 60b6f1a1e578a5f8a245d4b7622ebf2bad7798cb Mon Sep 17 00:00:00 2001 From: Stas Sergeev Date: Wed, 17 Apr 2013 16:34:06 +0400 Subject: ASoC: define playback and capture streams in dummy codec This patch adds a playback and capture streams to the dummy codec DAI configuration. Most permissive set of sampling rates and formats is used. This patch is needed for playback and capturing on a codec-less systems, as otherwise the PCM device nodes are not even created. Signed-off-by: Stas Sergeev Signed-off-by: Mark Brown --- sound/soc/soc-utils.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c index fe4541d..4b3be6c 100644 --- a/sound/soc/soc-utils.c +++ b/sound/soc/soc-utils.c @@ -90,8 +90,33 @@ static struct snd_soc_platform_driver dummy_platform = { }; static struct snd_soc_codec_driver dummy_codec; + +#define STUB_RATES SNDRV_PCM_RATE_8000_192000 +#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_U8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_U16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_U24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE | \ + SNDRV_PCM_FMTBIT_U32_LE | \ + SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE) static struct snd_soc_dai_driver dummy_dai = { .name = "snd-soc-dummy-dai", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 384, + .rates = STUB_RATES, + .formats = STUB_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 384, + .rates = STUB_RATES, + .formats = STUB_FORMATS, + }, }; static int snd_soc_dummy_probe(struct platform_device *pdev) -- cgit v1.1 From 98682063549bedd6e2d2b6b7222f150c6fbce68c Mon Sep 17 00:00:00 2001 From: Dylan Reid Date: Tue, 16 Apr 2013 20:02:34 -0700 Subject: ASoC: max98088: Fix logging of hardware revision. The hardware revision of the codec is based at 0x40. Subtract that before convering to ASCII. The same as it is done for 98095. Signed-off-by: Dylan Reid Signed-off-by: Mark Brown Cc: stable@vger.kernel.org --- sound/soc/codecs/max98088.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c index 3a7b7fd..3eeada5 100644 --- a/sound/soc/codecs/max98088.c +++ b/sound/soc/codecs/max98088.c @@ -2024,7 +2024,7 @@ static int max98088_probe(struct snd_soc_codec *codec) ret); goto err_access; } - dev_info(codec->dev, "revision %c\n", ret + 'A'); + dev_info(codec->dev, "revision %c\n", ret - 0x40 + 'A'); snd_soc_write(codec, M98088_REG_51_PWR_SYS, M98088_PWRSV); -- cgit v1.1 From 22f38f792ec53e2a93be13ecb609bbe911ed8ff9 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 15 Apr 2013 19:20:04 +0200 Subject: ASoC: ux500: Use generic dmaengine PCM 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 Acked-by: Lee Jones Signed-off-by: Mark Brown --- sound/soc/ux500/Kconfig | 2 +- sound/soc/ux500/ux500_pcm.c | 159 +++++--------------------------------------- 2 files changed, 18 insertions(+), 143 deletions(-) (limited to 'sound/soc') 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); -- cgit v1.1