aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/sound/dmaengine_pcm.h70
-rw-r--r--include/sound/soc.h4
-rw-r--r--sound/soc/Kconfig4
-rw-r--r--sound/soc/Makefile4
-rw-r--r--sound/soc/atmel/atmel-pcm-dma.c6
-rw-r--r--sound/soc/cirrus/ep93xx-pcm.c5
-rw-r--r--sound/soc/fsl/Kconfig2
-rw-r--r--sound/soc/fsl/fsl_ssi.c19
-rw-r--r--sound/soc/fsl/imx-pcm-dma.c76
-rw-r--r--sound/soc/fsl/imx-pcm.c6
-rw-r--r--sound/soc/fsl/imx-pcm.h5
-rw-r--r--sound/soc/fsl/imx-ssi.c22
-rw-r--r--sound/soc/mxs/Kconfig2
-rw-r--r--sound/soc/mxs/mxs-pcm.c139
-rw-r--r--sound/soc/mxs/mxs-saif.c3
-rw-r--r--sound/soc/omap/omap-pcm.c7
-rw-r--r--sound/soc/pxa/mmp-pcm.c5
-rw-r--r--sound/soc/soc-core.c85
-rw-r--r--sound/soc/soc-dmaengine-pcm.c82
-rw-r--r--sound/soc/soc-generic-dmaengine-pcm.c300
-rw-r--r--sound/soc/spear/spear_pcm.c5
-rw-r--r--sound/soc/tegra/Kconfig2
-rw-r--r--sound/soc/tegra/tegra_pcm.c171
-rw-r--r--sound/soc/ux500/Kconfig2
-rw-r--r--sound/soc/ux500/ux500_pcm.c159
25 files changed, 605 insertions, 580 deletions
diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h
index 95620428a59b..f11c35cd5532 100644
--- a/include/sound/dmaengine_pcm.h
+++ b/include/sound/dmaengine_pcm.h
@@ -16,6 +16,7 @@
16#define __SOUND_DMAENGINE_PCM_H__ 16#define __SOUND_DMAENGINE_PCM_H__
17 17
18#include <sound/pcm.h> 18#include <sound/pcm.h>
19#include <sound/soc.h>
19#include <linux/dmaengine.h> 20#include <linux/dmaengine.h>
20 21
21/** 22/**
@@ -39,9 +40,15 @@ snd_pcm_uframes_t snd_dmaengine_pcm_pointer(struct snd_pcm_substream *substream)
39snd_pcm_uframes_t snd_dmaengine_pcm_pointer_no_residue(struct snd_pcm_substream *substream); 40snd_pcm_uframes_t snd_dmaengine_pcm_pointer_no_residue(struct snd_pcm_substream *substream);
40 41
41int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream, 42int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream,
42 dma_filter_fn filter_fn, void *filter_data); 43 struct dma_chan *chan);
43int snd_dmaengine_pcm_close(struct snd_pcm_substream *substream); 44int snd_dmaengine_pcm_close(struct snd_pcm_substream *substream);
44 45
46int snd_dmaengine_pcm_open_request_chan(struct snd_pcm_substream *substream,
47 dma_filter_fn filter_fn, void *filter_data);
48int snd_dmaengine_pcm_close_release_chan(struct snd_pcm_substream *substream);
49
50struct dma_chan *snd_dmaengine_pcm_request_channel(dma_filter_fn filter_fn,
51 void *filter_data);
45struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream); 52struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream);
46 53
47/** 54/**
@@ -68,4 +75,65 @@ void snd_dmaengine_pcm_set_config_from_dai_data(
68 const struct snd_dmaengine_dai_dma_data *dma_data, 75 const struct snd_dmaengine_dai_dma_data *dma_data,
69 struct dma_slave_config *config); 76 struct dma_slave_config *config);
70 77
78
79/*
80 * Try to request the DMA channel using compat_request_channel or
81 * compat_filter_fn if it couldn't be requested through devicetree.
82 */
83#define SND_DMAENGINE_PCM_FLAG_COMPAT BIT(0)
84/*
85 * Don't try to request the DMA channels through devicetree. This flag only
86 * makes sense if SND_DMAENGINE_PCM_FLAG_COMPAT is set as well.
87 */
88#define SND_DMAENGINE_PCM_FLAG_NO_DT BIT(1)
89/*
90 * The platforms dmaengine driver does not support reporting the amount of
91 * bytes that are still left to transfer.
92 */
93#define SND_DMAENGINE_PCM_FLAG_NO_RESIDUE BIT(2)
94/*
95 * The PCM is half duplex and the DMA channel is shared between capture and
96 * playback.
97 */
98#define SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX BIT(3)
99
100/**
101 * struct snd_dmaengine_pcm_config - Configuration data for dmaengine based PCM
102 * @prepare_slave_config: Callback used to fill in the DMA slave_config for a
103 * PCM substream. Will be called from the PCM drivers hwparams callback.
104 * @compat_request_channel: Callback to request a DMA channel for platforms
105 * which do not use devicetree.
106 * @compat_filter_fn: Will be used as the filter function when requesting a
107 * channel for platforms which do not use devicetree. The filter parameter
108 * will be the DAI's DMA data.
109 * @pcm_hardware: snd_pcm_hardware struct to be used for the PCM.
110 * @prealloc_buffer_size: Size of the preallocated audio buffer.
111 *
112 * Note: If both compat_request_channel and compat_filter_fn are set
113 * compat_request_channel will be used to request the channel and
114 * compat_filter_fn will be ignored. Otherwise the channel will be requested
115 * using dma_request_channel with compat_filter_fn as the filter function.
116 */
117struct snd_dmaengine_pcm_config {
118 int (*prepare_slave_config)(struct snd_pcm_substream *substream,
119 struct snd_pcm_hw_params *params,
120 struct dma_slave_config *slave_config);
121 struct dma_chan *(*compat_request_channel)(
122 struct snd_soc_pcm_runtime *rtd,
123 struct snd_pcm_substream *substream);
124 dma_filter_fn compat_filter_fn;
125
126 const struct snd_pcm_hardware *pcm_hardware;
127 unsigned int prealloc_buffer_size;
128};
129
130int snd_dmaengine_pcm_register(struct device *dev,
131 const struct snd_dmaengine_pcm_config *config,
132 unsigned int flags);
133void snd_dmaengine_pcm_unregister(struct device *dev);
134
135int snd_dmaengine_pcm_prepare_slave_config(struct snd_pcm_substream *substream,
136 struct snd_pcm_hw_params *params,
137 struct dma_slave_config *slave_config);
138
71#endif 139#endif
diff --git a/include/sound/soc.h b/include/sound/soc.h
index 9eb0e4db4835..85c15226103b 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -375,6 +375,10 @@ int snd_soc_poweroff(struct device *dev);
375int snd_soc_register_platform(struct device *dev, 375int snd_soc_register_platform(struct device *dev,
376 const struct snd_soc_platform_driver *platform_drv); 376 const struct snd_soc_platform_driver *platform_drv);
377void snd_soc_unregister_platform(struct device *dev); 377void snd_soc_unregister_platform(struct device *dev);
378int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform,
379 const struct snd_soc_platform_driver *platform_drv);
380void snd_soc_remove_platform(struct snd_soc_platform *platform);
381struct snd_soc_platform *snd_soc_lookup_platform(struct device *dev);
378int snd_soc_register_codec(struct device *dev, 382int snd_soc_register_codec(struct device *dev,
379 const struct snd_soc_codec_driver *codec_drv, 383 const struct snd_soc_codec_driver *codec_drv,
380 struct snd_soc_dai_driver *dai_drv, int num_dai); 384 struct snd_soc_dai_driver *dai_drv, int num_dai);
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 5da8ca7aee05..9e675c76436c 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -29,6 +29,10 @@ config SND_SOC_AC97_BUS
29config SND_SOC_DMAENGINE_PCM 29config SND_SOC_DMAENGINE_PCM
30 bool 30 bool
31 31
32config SND_SOC_GENERIC_DMAENGINE_PCM
33 bool
34 select SND_SOC_DMAENGINE_PCM
35
32# All the supported SoCs 36# All the supported SoCs
33source "sound/soc/atmel/Kconfig" 37source "sound/soc/atmel/Kconfig"
34source "sound/soc/au1x/Kconfig" 38source "sound/soc/au1x/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 99f32f7c0692..197b6ae54c8d 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -5,6 +5,10 @@ ifneq ($(CONFIG_SND_SOC_DMAENGINE_PCM),)
5snd-soc-core-objs += soc-dmaengine-pcm.o 5snd-soc-core-objs += soc-dmaengine-pcm.o
6endif 6endif
7 7
8ifneq ($(CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM),)
9snd-soc-core-objs += soc-generic-dmaengine-pcm.o
10endif
11
8obj-$(CONFIG_SND_SOC) += snd-soc-core.o 12obj-$(CONFIG_SND_SOC) += snd-soc-core.o
9obj-$(CONFIG_SND_SOC) += codecs/ 13obj-$(CONFIG_SND_SOC) += codecs/
10obj-$(CONFIG_SND_SOC) += generic/ 14obj-$(CONFIG_SND_SOC) += generic/
diff --git a/sound/soc/atmel/atmel-pcm-dma.c b/sound/soc/atmel/atmel-pcm-dma.c
index bb07989762d5..1d38fd0bc4e2 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,
155 if (ssc->pdev) 155 if (ssc->pdev)
156 sdata = ssc->pdev->dev.platform_data; 156 sdata = ssc->pdev->dev.platform_data;
157 157
158 ret = snd_dmaengine_pcm_open(substream, filter, sdata); 158 ret = snd_dmaengine_pcm_open_request_chan(substream, filter, sdata);
159 if (ret) { 159 if (ret) {
160 pr_err("atmel-pcm: dmaengine pcm open failed\n"); 160 pr_err("atmel-pcm: dmaengine pcm open failed\n");
161 return -EINVAL; 161 return -EINVAL;
@@ -171,7 +171,7 @@ static int atmel_pcm_hw_params(struct snd_pcm_substream *substream,
171 171
172 return 0; 172 return 0;
173err: 173err:
174 snd_dmaengine_pcm_close(substream); 174 snd_dmaengine_pcm_close_release_chan(substream);
175 return ret; 175 return ret;
176} 176}
177 177
@@ -197,7 +197,7 @@ static int atmel_pcm_open(struct snd_pcm_substream *substream)
197 197
198static struct snd_pcm_ops atmel_pcm_ops = { 198static struct snd_pcm_ops atmel_pcm_ops = {
199 .open = atmel_pcm_open, 199 .open = atmel_pcm_open,
200 .close = snd_dmaengine_pcm_close, 200 .close = snd_dmaengine_pcm_close_release_chan,
201 .ioctl = snd_pcm_lib_ioctl, 201 .ioctl = snd_pcm_lib_ioctl,
202 .hw_params = atmel_pcm_hw_params, 202 .hw_params = atmel_pcm_hw_params,
203 .prepare = atmel_pcm_dma_prepare, 203 .prepare = atmel_pcm_dma_prepare,
diff --git a/sound/soc/cirrus/ep93xx-pcm.c b/sound/soc/cirrus/ep93xx-pcm.c
index 298946f790eb..488032690378 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)
69 69
70 snd_soc_set_runtime_hwparams(substream, &ep93xx_pcm_hardware); 70 snd_soc_set_runtime_hwparams(substream, &ep93xx_pcm_hardware);
71 71
72 return snd_dmaengine_pcm_open(substream, ep93xx_pcm_dma_filter, 72 return snd_dmaengine_pcm_open_request_chan(substream,
73 ep93xx_pcm_dma_filter,
73 snd_soc_dai_get_dma_data(rtd->cpu_dai, substream)); 74 snd_soc_dai_get_dma_data(rtd->cpu_dai, substream));
74} 75}
75 76
@@ -100,7 +101,7 @@ static int ep93xx_pcm_mmap(struct snd_pcm_substream *substream,
100 101
101static struct snd_pcm_ops ep93xx_pcm_ops = { 102static struct snd_pcm_ops ep93xx_pcm_ops = {
102 .open = ep93xx_pcm_open, 103 .open = ep93xx_pcm_open,
103 .close = snd_dmaengine_pcm_close, 104 .close = snd_dmaengine_pcm_close_release_chan,
104 .ioctl = snd_pcm_lib_ioctl, 105 .ioctl = snd_pcm_lib_ioctl,
105 .hw_params = ep93xx_pcm_hw_params, 106 .hw_params = ep93xx_pcm_hw_params,
106 .hw_free = ep93xx_pcm_hw_free, 107 .hw_free = ep93xx_pcm_hw_free,
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 3b98159d9645..3843a18d4e56 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -118,7 +118,7 @@ config SND_SOC_IMX_PCM_FIQ
118 118
119config SND_SOC_IMX_PCM_DMA 119config SND_SOC_IMX_PCM_DMA
120 bool 120 bool
121 select SND_SOC_DMAENGINE_PCM 121 select SND_SOC_GENERIC_DMAENGINE_PCM
122 select SND_SOC_IMX_PCM 122 select SND_SOC_IMX_PCM
123 123
124config SND_SOC_IMX_AUDMUX 124config SND_SOC_IMX_AUDMUX
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index 42366d776f62..0f0bed6def9e 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,
425 ssi_private->second_stream = substream; 425 ssi_private->second_stream = substream;
426 } 426 }
427 427
428 if (ssi_private->ssi_on_imx)
429 snd_soc_dai_set_dma_data(dai, substream,
430 (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
431 &ssi_private->dma_params_tx :
432 &ssi_private->dma_params_rx);
433
434 return 0; 428 return 0;
435} 429}
436 430
@@ -552,6 +546,18 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
552 } 546 }
553} 547}
554 548
549static int fsl_ssi_dai_probe(struct snd_soc_dai *dai)
550{
551 struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(dai);
552
553 if (ssi_private->ssi_on_imx) {
554 dai->playback_dma_data = &ssi_private->dma_params_tx;
555 dai->capture_dma_data = &ssi_private->dma_params_rx;
556 }
557
558 return 0;
559}
560
555static const struct snd_soc_dai_ops fsl_ssi_dai_ops = { 561static const struct snd_soc_dai_ops fsl_ssi_dai_ops = {
556 .startup = fsl_ssi_startup, 562 .startup = fsl_ssi_startup,
557 .hw_params = fsl_ssi_hw_params, 563 .hw_params = fsl_ssi_hw_params,
@@ -561,6 +567,7 @@ static const struct snd_soc_dai_ops fsl_ssi_dai_ops = {
561 567
562/* Template for the CPU dai driver structure */ 568/* Template for the CPU dai driver structure */
563static struct snd_soc_dai_driver fsl_ssi_dai_template = { 569static struct snd_soc_dai_driver fsl_ssi_dai_template = {
570 .probe = fsl_ssi_dai_probe,
564 .playback = { 571 .playback = {
565 /* The SSI does not support monaural audio. */ 572 /* The SSI does not support monaural audio. */
566 .channels_min = 2, 573 .channels_min = 2,
diff --git a/sound/soc/fsl/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c
index ee838c8a3b11..c246fb514930 100644
--- a/sound/soc/fsl/imx-pcm-dma.c
+++ b/sound/soc/fsl/imx-pcm-dma.c
@@ -11,22 +11,12 @@
11 * Free Software Foundation; either version 2 of the License, or (at your 11 * Free Software Foundation; either version 2 of the License, or (at your
12 * option) any later version. 12 * option) any later version.
13 */ 13 */
14#include <linux/clk.h>
15#include <linux/delay.h>
16#include <linux/device.h>
17#include <linux/dma-mapping.h>
18#include <linux/init.h>
19#include <linux/interrupt.h>
20#include <linux/module.h>
21#include <linux/platform_device.h> 14#include <linux/platform_device.h>
22#include <linux/slab.h>
23#include <linux/dmaengine.h> 15#include <linux/dmaengine.h>
24#include <linux/types.h> 16#include <linux/types.h>
25 17
26#include <sound/core.h> 18#include <sound/core.h>
27#include <sound/initval.h>
28#include <sound/pcm.h> 19#include <sound/pcm.h>
29#include <sound/pcm_params.h>
30#include <sound/soc.h> 20#include <sound/soc.h>
31#include <sound/dmaengine_pcm.h> 21#include <sound/dmaengine_pcm.h>
32 22
@@ -44,32 +34,7 @@ static bool filter(struct dma_chan *chan, void *param)
44 return true; 34 return true;
45} 35}
46 36
47static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream, 37static const struct snd_pcm_hardware imx_pcm_hardware = {
48 struct snd_pcm_hw_params *params)
49{
50 struct snd_soc_pcm_runtime *rtd = substream->private_data;
51 struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
52 struct dma_slave_config slave_config;
53 int ret;
54
55 ret = snd_hwparams_to_dma_slave_config(substream, params, &slave_config);
56 if (ret)
57 return ret;
58
59 snd_dmaengine_pcm_set_config_from_dai_data(substream,
60 snd_soc_dai_get_dma_data(rtd->cpu_dai, substream),
61 &slave_config);
62
63 ret = dmaengine_slave_config(chan, &slave_config);
64 if (ret)
65 return ret;
66
67 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
68
69 return 0;
70}
71
72static struct snd_pcm_hardware snd_imx_hardware = {
73 .info = SNDRV_PCM_INFO_INTERLEAVED | 38 .info = SNDRV_PCM_INFO_INTERLEAVED |
74 SNDRV_PCM_INFO_BLOCK_TRANSFER | 39 SNDRV_PCM_INFO_BLOCK_TRANSFER |
75 SNDRV_PCM_INFO_MMAP | 40 SNDRV_PCM_INFO_MMAP |
@@ -88,33 +53,22 @@ static struct snd_pcm_hardware snd_imx_hardware = {
88 .fifo_size = 0, 53 .fifo_size = 0,
89}; 54};
90 55
91static int snd_imx_open(struct snd_pcm_substream *substream) 56static const struct snd_dmaengine_pcm_config imx_dmaengine_pcm_config = {
92{ 57 .pcm_hardware = &imx_pcm_hardware,
93 struct snd_soc_pcm_runtime *rtd = substream->private_data; 58 .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
94 59 .compat_filter_fn = filter,
95 snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware); 60 .prealloc_buffer_size = IMX_SSI_DMABUF_SIZE,
96
97 return snd_dmaengine_pcm_open(substream, filter,
98 snd_soc_dai_get_dma_data(rtd->cpu_dai, substream));
99}
100
101static struct snd_pcm_ops imx_pcm_ops = {
102 .open = snd_imx_open,
103 .close = snd_dmaengine_pcm_close,
104 .ioctl = snd_pcm_lib_ioctl,
105 .hw_params = snd_imx_pcm_hw_params,
106 .trigger = snd_dmaengine_pcm_trigger,
107 .pointer = snd_dmaengine_pcm_pointer_no_residue,
108 .mmap = snd_imx_pcm_mmap,
109};
110
111static struct snd_soc_platform_driver imx_soc_platform_mx2 = {
112 .ops = &imx_pcm_ops,
113 .pcm_new = imx_pcm_new,
114 .pcm_free = imx_pcm_free,
115}; 61};
116 62
117int imx_pcm_dma_init(struct platform_device *pdev) 63int imx_pcm_dma_init(struct platform_device *pdev)
118{ 64{
119 return snd_soc_register_platform(&pdev->dev, &imx_soc_platform_mx2); 65 return snd_dmaengine_pcm_register(&pdev->dev, &imx_dmaengine_pcm_config,
66 SND_DMAENGINE_PCM_FLAG_NO_RESIDUE |
67 SND_DMAENGINE_PCM_FLAG_NO_DT |
68 SND_DMAENGINE_PCM_FLAG_COMPAT);
69}
70
71void imx_pcm_dma_exit(struct platform_device *pdev)
72{
73 snd_dmaengine_pcm_unregister(&pdev->dev);
120} 74}
diff --git a/sound/soc/fsl/imx-pcm.c b/sound/soc/fsl/imx-pcm.c
index 0d0625bfcb65..c49896442d8e 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)
114 114
115static int imx_pcm_remove(struct platform_device *pdev) 115static int imx_pcm_remove(struct platform_device *pdev)
116{ 116{
117 snd_soc_unregister_platform(&pdev->dev); 117 if (strcmp(pdev->id_entry->name, "imx-fiq-pcm-audio") == 0)
118 snd_soc_unregister_platform(&pdev->dev);
119 else
120 imx_pcm_dma_exit(pdev);
121
118 return 0; 122 return 0;
119} 123}
120 124
diff --git a/sound/soc/fsl/imx-pcm.h b/sound/soc/fsl/imx-pcm.h
index be9cc64a208b..b7fa0d75c687 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);
39 39
40#ifdef CONFIG_SND_SOC_IMX_PCM_DMA 40#ifdef CONFIG_SND_SOC_IMX_PCM_DMA
41int imx_pcm_dma_init(struct platform_device *pdev); 41int imx_pcm_dma_init(struct platform_device *pdev);
42void imx_pcm_dma_exit(struct platform_device *pdev);
42#else 43#else
43static inline int imx_pcm_dma_init(struct platform_device *pdev) 44static inline int imx_pcm_dma_init(struct platform_device *pdev)
44{ 45{
45 return -ENODEV; 46 return -ENODEV;
46} 47}
48
49static inline void imx_pcm_dma_exit(struct platform_device *pdev)
50{
51}
47#endif 52#endif
48 53
49#ifdef CONFIG_SND_SOC_IMX_PCM_FIQ 54#ifdef CONFIG_SND_SOC_IMX_PCM_FIQ
diff --git a/sound/soc/fsl/imx-ssi.c b/sound/soc/fsl/imx-ssi.c
index 4ce2d608b37a..902fab02b851 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,
232 return 0; 232 return 0;
233} 233}
234 234
235static int imx_ssi_startup(struct snd_pcm_substream *substream,
236 struct snd_soc_dai *cpu_dai)
237{
238 struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai);
239 struct snd_dmaengine_dai_dma_data *dma_data;
240
241 /* Tx/Rx config */
242 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
243 dma_data = &ssi->dma_params_tx;
244 else
245 dma_data = &ssi->dma_params_rx;
246
247 snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data);
248
249 return 0;
250}
251
252/* 235/*
253 * Should only be called when port is inactive (i.e. SSIEN = 0), 236 * Should only be called when port is inactive (i.e. SSIEN = 0),
254 * although can be called multiple times by upper layers. 237 * 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,
353} 336}
354 337
355static const struct snd_soc_dai_ops imx_ssi_pcm_dai_ops = { 338static const struct snd_soc_dai_ops imx_ssi_pcm_dai_ops = {
356 .startup = imx_ssi_startup,
357 .hw_params = imx_ssi_hw_params, 339 .hw_params = imx_ssi_hw_params,
358 .set_fmt = imx_ssi_set_dai_fmt, 340 .set_fmt = imx_ssi_set_dai_fmt,
359 .set_clkdiv = imx_ssi_set_dai_clkdiv, 341 .set_clkdiv = imx_ssi_set_dai_clkdiv,
@@ -373,6 +355,10 @@ static int imx_ssi_dai_probe(struct snd_soc_dai *dai)
373 SSI_SFCSR_RFWM0(ssi->dma_params_rx.maxburst); 355 SSI_SFCSR_RFWM0(ssi->dma_params_rx.maxburst);
374 writel(val, ssi->base + SSI_SFCSR); 356 writel(val, ssi->base + SSI_SFCSR);
375 357
358 /* Tx/Rx config */
359 dai->playback_dma_data = &ssi->dma_params_tx;
360 dai->capture_dma_data = &ssi->dma_params_rx;
361
376 return 0; 362 return 0;
377} 363}
378 364
diff --git a/sound/soc/mxs/Kconfig b/sound/soc/mxs/Kconfig
index b6fa77678d97..78d321cbe8b4 100644
--- a/sound/soc/mxs/Kconfig
+++ b/sound/soc/mxs/Kconfig
@@ -1,7 +1,7 @@
1menuconfig SND_MXS_SOC 1menuconfig SND_MXS_SOC
2 tristate "SoC Audio for Freescale MXS CPUs" 2 tristate "SoC Audio for Freescale MXS CPUs"
3 depends on ARCH_MXS 3 depends on ARCH_MXS
4 select SND_SOC_DMAENGINE_PCM 4 select SND_SOC_GENERIC_DMAENGINE_PCM
5 help 5 help
6 Say Y or M if you want to add support for codecs attached to 6 Say Y or M if you want to add support for codecs attached to
7 the MXS SAIF interface. 7 the MXS SAIF interface.
diff --git a/sound/soc/mxs/mxs-pcm.c b/sound/soc/mxs/mxs-pcm.c
index ebbef8597554..b41fffc056fb 100644
--- a/sound/soc/mxs/mxs-pcm.c
+++ b/sound/soc/mxs/mxs-pcm.c
@@ -18,32 +18,24 @@
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */ 19 */
20 20
21#include <linux/clk.h>
22#include <linux/delay.h>
23#include <linux/device.h> 21#include <linux/device.h>
24#include <linux/dma-mapping.h>
25#include <linux/init.h> 22#include <linux/init.h>
26#include <linux/interrupt.h>
27#include <linux/module.h> 23#include <linux/module.h>
28#include <linux/platform_device.h>
29#include <linux/slab.h>
30#include <linux/dmaengine.h>
31 24
32#include <sound/core.h> 25#include <sound/core.h>
33#include <sound/initval.h>
34#include <sound/pcm.h> 26#include <sound/pcm.h>
35#include <sound/pcm_params.h>
36#include <sound/soc.h> 27#include <sound/soc.h>
37#include <sound/dmaengine_pcm.h> 28#include <sound/dmaengine_pcm.h>
38 29
39#include "mxs-pcm.h" 30#include "mxs-pcm.h"
40 31
41static struct snd_pcm_hardware snd_mxs_hardware = { 32static const struct snd_pcm_hardware snd_mxs_hardware = {
42 .info = SNDRV_PCM_INFO_MMAP | 33 .info = SNDRV_PCM_INFO_MMAP |
43 SNDRV_PCM_INFO_MMAP_VALID | 34 SNDRV_PCM_INFO_MMAP_VALID |
44 SNDRV_PCM_INFO_PAUSE | 35 SNDRV_PCM_INFO_PAUSE |
45 SNDRV_PCM_INFO_RESUME | 36 SNDRV_PCM_INFO_RESUME |
46 SNDRV_PCM_INFO_INTERLEAVED, 37 SNDRV_PCM_INFO_INTERLEAVED |
38 SNDRV_PCM_INFO_HALF_DUPLEX,
47 .formats = SNDRV_PCM_FMTBIT_S16_LE | 39 .formats = SNDRV_PCM_FMTBIT_S16_LE |
48 SNDRV_PCM_FMTBIT_S20_3LE | 40 SNDRV_PCM_FMTBIT_S20_3LE |
49 SNDRV_PCM_FMTBIT_S24_LE, 41 SNDRV_PCM_FMTBIT_S24_LE,
@@ -55,7 +47,6 @@ static struct snd_pcm_hardware snd_mxs_hardware = {
55 .periods_max = 52, 47 .periods_max = 52,
56 .buffer_bytes_max = 64 * 1024, 48 .buffer_bytes_max = 64 * 1024,
57 .fifo_size = 32, 49 .fifo_size = 32,
58
59}; 50};
60 51
61static bool filter(struct dma_chan *chan, void *param) 52static bool filter(struct dma_chan *chan, void *param)
@@ -73,129 +64,25 @@ static bool filter(struct dma_chan *chan, void *param)
73 return true; 64 return true;
74} 65}
75 66
76static int snd_mxs_pcm_hw_params(struct snd_pcm_substream *substream, 67static const struct snd_dmaengine_pcm_config mxs_dmaengine_pcm_config = {
77 struct snd_pcm_hw_params *params) 68 .pcm_hardware = &snd_mxs_hardware,
78{ 69 .compat_filter_fn = filter,
79 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); 70 .prealloc_buffer_size = 64 * 1024,
80
81 return 0;
82}
83
84static int snd_mxs_open(struct snd_pcm_substream *substream)
85{
86 struct snd_soc_pcm_runtime *rtd = substream->private_data;
87
88 snd_soc_set_runtime_hwparams(substream, &snd_mxs_hardware);
89
90 return snd_dmaengine_pcm_open(substream, filter,
91 snd_soc_dai_get_dma_data(rtd->cpu_dai, substream));
92}
93
94static int snd_mxs_pcm_mmap(struct snd_pcm_substream *substream,
95 struct vm_area_struct *vma)
96{
97 struct snd_pcm_runtime *runtime = substream->runtime;
98
99 return dma_mmap_writecombine(substream->pcm->card->dev, vma,
100 runtime->dma_area,
101 runtime->dma_addr,
102 runtime->dma_bytes);
103}
104
105static struct snd_pcm_ops mxs_pcm_ops = {
106 .open = snd_mxs_open,
107 .close = snd_dmaengine_pcm_close,
108 .ioctl = snd_pcm_lib_ioctl,
109 .hw_params = snd_mxs_pcm_hw_params,
110 .trigger = snd_dmaengine_pcm_trigger,
111 .pointer = snd_dmaengine_pcm_pointer_no_residue,
112 .mmap = snd_mxs_pcm_mmap,
113};
114
115static int mxs_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
116{
117 struct snd_pcm_substream *substream = pcm->streams[stream].substream;
118 struct snd_dma_buffer *buf = &substream->dma_buffer;
119 size_t size = snd_mxs_hardware.buffer_bytes_max;
120
121 buf->dev.type = SNDRV_DMA_TYPE_DEV;
122 buf->dev.dev = pcm->card->dev;
123 buf->private_data = NULL;
124 buf->area = dma_alloc_writecombine(pcm->card->dev, size,
125 &buf->addr, GFP_KERNEL);
126 if (!buf->area)
127 return -ENOMEM;
128 buf->bytes = size;
129
130 return 0;
131}
132
133static u64 mxs_pcm_dmamask = DMA_BIT_MASK(32);
134static int mxs_pcm_new(struct snd_soc_pcm_runtime *rtd)
135{
136 struct snd_card *card = rtd->card->snd_card;
137 struct snd_pcm *pcm = rtd->pcm;
138 int ret = 0;
139
140 if (!card->dev->dma_mask)
141 card->dev->dma_mask = &mxs_pcm_dmamask;
142 if (!card->dev->coherent_dma_mask)
143 card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
144
145 if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
146 ret = mxs_pcm_preallocate_dma_buffer(pcm,
147 SNDRV_PCM_STREAM_PLAYBACK);
148 if (ret)
149 goto out;
150 }
151
152 if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
153 ret = mxs_pcm_preallocate_dma_buffer(pcm,
154 SNDRV_PCM_STREAM_CAPTURE);
155 if (ret)
156 goto out;
157 }
158
159out:
160 return ret;
161}
162
163static void mxs_pcm_free(struct snd_pcm *pcm)
164{
165 struct snd_pcm_substream *substream;
166 struct snd_dma_buffer *buf;
167 int stream;
168
169 for (stream = 0; stream < 2; stream++) {
170 substream = pcm->streams[stream].substream;
171 if (!substream)
172 continue;
173
174 buf = &substream->dma_buffer;
175 if (!buf->area)
176 continue;
177
178 dma_free_writecombine(pcm->card->dev, buf->bytes,
179 buf->area, buf->addr);
180 buf->area = NULL;
181 }
182}
183
184static struct snd_soc_platform_driver mxs_soc_platform = {
185 .ops = &mxs_pcm_ops,
186 .pcm_new = mxs_pcm_new,
187 .pcm_free = mxs_pcm_free,
188}; 71};
189 72
190int mxs_pcm_platform_register(struct device *dev) 73int mxs_pcm_platform_register(struct device *dev)
191{ 74{
192 return snd_soc_register_platform(dev, &mxs_soc_platform); 75 return snd_dmaengine_pcm_register(dev, &mxs_dmaengine_pcm_config,
76 SND_DMAENGINE_PCM_FLAG_NO_RESIDUE |
77 SND_DMAENGINE_PCM_FLAG_NO_DT |
78 SND_DMAENGINE_PCM_FLAG_COMPAT |
79 SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX);
193} 80}
194EXPORT_SYMBOL_GPL(mxs_pcm_platform_register); 81EXPORT_SYMBOL_GPL(mxs_pcm_platform_register);
195 82
196void mxs_pcm_platform_unregister(struct device *dev) 83void mxs_pcm_platform_unregister(struct device *dev)
197{ 84{
198 snd_soc_unregister_platform(dev); 85 snd_dmaengine_pcm_unregister(dev);
199} 86}
200EXPORT_SYMBOL_GPL(mxs_pcm_platform_unregister); 87EXPORT_SYMBOL_GPL(mxs_pcm_platform_unregister);
201 88
diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c
index abf4ddf4ed89..b563141a6543 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,
369 struct snd_soc_dai *cpu_dai) 369 struct snd_soc_dai *cpu_dai)
370{ 370{
371 struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai); 371 struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai);
372 snd_soc_dai_set_dma_data(cpu_dai, substream, &saif->dma_param);
373 372
374 /* clear error status to 0 for each re-open */ 373 /* clear error status to 0 for each re-open */
375 saif->fifo_underrun = 0; 374 saif->fifo_underrun = 0;
@@ -605,6 +604,8 @@ static int mxs_saif_dai_probe(struct snd_soc_dai *dai)
605 struct mxs_saif *saif = dev_get_drvdata(dai->dev); 604 struct mxs_saif *saif = dev_get_drvdata(dai->dev);
606 605
607 snd_soc_dai_set_drvdata(dai, saif); 606 snd_soc_dai_set_drvdata(dai, saif);
607 dai->playback_dma_data = &saif->dma_param;
608 dai->capture_dma_data = &saif->dma_param;
608 609
609 return 0; 610 return 0;
610} 611}
diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c
index c8e272f9c2de..c28e042f2208 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)
118 118
119 dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); 119 dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
120 120
121 return snd_dmaengine_pcm_open(substream, omap_dma_filter_fn, 121 return snd_dmaengine_pcm_open_request_chan(substream,
122 dma_data->filter_data); 122 omap_dma_filter_fn,
123 dma_data->filter_data);
123} 124}
124 125
125static int omap_pcm_mmap(struct snd_pcm_substream *substream, 126static int omap_pcm_mmap(struct snd_pcm_substream *substream,
@@ -135,7 +136,7 @@ static int omap_pcm_mmap(struct snd_pcm_substream *substream,
135 136
136static struct snd_pcm_ops omap_pcm_ops = { 137static struct snd_pcm_ops omap_pcm_ops = {
137 .open = omap_pcm_open, 138 .open = omap_pcm_open,
138 .close = snd_dmaengine_pcm_close, 139 .close = snd_dmaengine_pcm_close_release_chan,
139 .ioctl = snd_pcm_lib_ioctl, 140 .ioctl = snd_pcm_lib_ioctl,
140 .hw_params = omap_pcm_hw_params, 141 .hw_params = omap_pcm_hw_params,
141 .hw_free = omap_pcm_hw_free, 142 .hw_free = omap_pcm_hw_free,
diff --git a/sound/soc/pxa/mmp-pcm.c b/sound/soc/pxa/mmp-pcm.c
index 6c3980252bf6..349930015264 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)
131 dma_data.dma_res = r; 131 dma_data.dma_res = r;
132 dma_data.ssp_id = cpu_dai->id; 132 dma_data.ssp_id = cpu_dai->id;
133 133
134 return snd_dmaengine_pcm_open(substream, filter, &dma_data); 134 return snd_dmaengine_pcm_open_request_chan(substream, filter,
135 &dma_data);
135} 136}
136 137
137static int mmp_pcm_mmap(struct snd_pcm_substream *substream, 138static int mmp_pcm_mmap(struct snd_pcm_substream *substream,
@@ -148,7 +149,7 @@ static int mmp_pcm_mmap(struct snd_pcm_substream *substream,
148 149
149struct snd_pcm_ops mmp_pcm_ops = { 150struct snd_pcm_ops mmp_pcm_ops = {
150 .open = mmp_pcm_open, 151 .open = mmp_pcm_open,
151 .close = snd_dmaengine_pcm_close, 152 .close = snd_dmaengine_pcm_close_release_chan,
152 .ioctl = snd_pcm_lib_ioctl, 153 .ioctl = snd_pcm_lib_ioctl,
153 .hw_params = mmp_pcm_hw_params, 154 .hw_params = mmp_pcm_hw_params,
154 .trigger = snd_dmaengine_pcm_trigger, 155 .trigger = snd_dmaengine_pcm_trigger,
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 7bf21a1035ea..d56bbea6e75e 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)
3900} 3900}
3901 3901
3902/** 3902/**
3903 * snd_soc_register_platform - Register a platform with the ASoC core 3903 * snd_soc_add_platform - Add a platform to the ASoC core
3904 * 3904 * @dev: The parent device for the platform
3905 * @platform: platform to register 3905 * @platform: The platform to add
3906 * @platform_driver: The driver for the platform
3906 */ 3907 */
3907int snd_soc_register_platform(struct device *dev, 3908int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform,
3908 const struct snd_soc_platform_driver *platform_drv) 3909 const struct snd_soc_platform_driver *platform_drv)
3909{ 3910{
3910 struct snd_soc_platform *platform;
3911
3912 dev_dbg(dev, "ASoC: platform register %s\n", dev_name(dev));
3913
3914 platform = kzalloc(sizeof(struct snd_soc_platform), GFP_KERNEL);
3915 if (platform == NULL)
3916 return -ENOMEM;
3917
3918 /* create platform component name */ 3911 /* create platform component name */
3919 platform->name = fmt_single_name(dev, &platform->id); 3912 platform->name = fmt_single_name(dev, &platform->id);
3920 if (platform->name == NULL) { 3913 if (platform->name == NULL) {
@@ -3937,30 +3930,76 @@ int snd_soc_register_platform(struct device *dev,
3937 3930
3938 return 0; 3931 return 0;
3939} 3932}
3940EXPORT_SYMBOL_GPL(snd_soc_register_platform); 3933EXPORT_SYMBOL_GPL(snd_soc_add_platform);
3941 3934
3942/** 3935/**
3943 * snd_soc_unregister_platform - Unregister a platform from the ASoC core 3936 * snd_soc_register_platform - Register a platform with the ASoC core
3944 * 3937 *
3945 * @platform: platform to unregister 3938 * @platform: platform to register
3946 */ 3939 */
3947void snd_soc_unregister_platform(struct device *dev) 3940int snd_soc_register_platform(struct device *dev,
3941 const struct snd_soc_platform_driver *platform_drv)
3948{ 3942{
3949 struct snd_soc_platform *platform; 3943 struct snd_soc_platform *platform;
3944 int ret;
3950 3945
3951 list_for_each_entry(platform, &platform_list, list) { 3946 dev_dbg(dev, "ASoC: platform register %s\n", dev_name(dev));
3952 if (dev == platform->dev)
3953 goto found;
3954 }
3955 return;
3956 3947
3957found: 3948 platform = kzalloc(sizeof(struct snd_soc_platform), GFP_KERNEL);
3949 if (platform == NULL)
3950 return -ENOMEM;
3951
3952 ret = snd_soc_add_platform(dev, platform, platform_drv);
3953 if (ret)
3954 kfree(platform);
3955
3956 return ret;
3957}
3958EXPORT_SYMBOL_GPL(snd_soc_register_platform);
3959
3960/**
3961 * snd_soc_remove_platform - Remove a platform from the ASoC core
3962 * @platform: the platform to remove
3963 */
3964void snd_soc_remove_platform(struct snd_soc_platform *platform)
3965{
3958 mutex_lock(&client_mutex); 3966 mutex_lock(&client_mutex);
3959 list_del(&platform->list); 3967 list_del(&platform->list);
3960 mutex_unlock(&client_mutex); 3968 mutex_unlock(&client_mutex);
3961 3969
3962 dev_dbg(dev, "ASoC: Unregistered platform '%s'\n", platform->name); 3970 dev_dbg(platform->dev, "ASoC: Unregistered platform '%s'\n",
3971 platform->name);
3963 kfree(platform->name); 3972 kfree(platform->name);
3973}
3974EXPORT_SYMBOL_GPL(snd_soc_remove_platform);
3975
3976struct snd_soc_platform *snd_soc_lookup_platform(struct device *dev)
3977{
3978 struct snd_soc_platform *platform;
3979
3980 list_for_each_entry(platform, &platform_list, list) {
3981 if (dev == platform->dev)
3982 return platform;
3983 }
3984
3985 return NULL;
3986}
3987EXPORT_SYMBOL_GPL(snd_soc_lookup_platform);
3988
3989/**
3990 * snd_soc_unregister_platform - Unregister a platform from the ASoC core
3991 *
3992 * @platform: platform to unregister
3993 */
3994void snd_soc_unregister_platform(struct device *dev)
3995{
3996 struct snd_soc_platform *platform;
3997
3998 platform = snd_soc_lookup_platform(dev);
3999 if (!platform)
4000 return;
4001
4002 snd_soc_remove_platform(platform);
3964 kfree(platform); 4003 kfree(platform);
3965} 4004}
3966EXPORT_SYMBOL_GPL(snd_soc_unregister_platform); 4005EXPORT_SYMBOL_GPL(snd_soc_unregister_platform);
diff --git a/sound/soc/soc-dmaengine-pcm.c b/sound/soc/soc-dmaengine-pcm.c
index a9a300acb506..aa924d9b7986 100644
--- a/sound/soc/soc-dmaengine-pcm.c
+++ b/sound/soc/soc-dmaengine-pcm.c
@@ -254,44 +254,48 @@ snd_pcm_uframes_t snd_dmaengine_pcm_pointer(struct snd_pcm_substream *substream)
254} 254}
255EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer); 255EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer);
256 256
257static int dmaengine_pcm_request_channel(struct dmaengine_pcm_runtime_data *prtd, 257/**
258 dma_filter_fn filter_fn, void *filter_data) 258 * snd_dmaengine_pcm_request_channel - Request channel for the dmaengine PCM
259 * @filter_fn: Filter function used to request the DMA channel
260 * @filter_data: Data passed to the DMA filter function
261 *
262 * Returns NULL or the requested DMA channel.
263 *
264 * This function request a DMA channel for usage with dmaengine PCM.
265 */
266struct dma_chan *snd_dmaengine_pcm_request_channel(dma_filter_fn filter_fn,
267 void *filter_data)
259{ 268{
260 dma_cap_mask_t mask; 269 dma_cap_mask_t mask;
261 270
262 dma_cap_zero(mask); 271 dma_cap_zero(mask);
263 dma_cap_set(DMA_SLAVE, mask); 272 dma_cap_set(DMA_SLAVE, mask);
264 dma_cap_set(DMA_CYCLIC, mask); 273 dma_cap_set(DMA_CYCLIC, mask);
265 prtd->dma_chan = dma_request_channel(mask, filter_fn, filter_data);
266
267 if (!prtd->dma_chan)
268 return -ENXIO;
269 274
270 return 0; 275 return dma_request_channel(mask, filter_fn, filter_data);
271} 276}
277EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_request_channel);
272 278
273/** 279/**
274 * snd_dmaengine_pcm_open - Open a dmaengine based PCM substream 280 * snd_dmaengine_pcm_open - Open a dmaengine based PCM substream
275 * @substream: PCM substream 281 * @substream: PCM substream
276 * @filter_fn: Filter function used to request the DMA channel 282 * @chan: DMA channel to use for data transfers
277 * @filter_data: Data passed to the DMA filter function
278 * 283 *
279 * Returns 0 on success, a negative error code otherwise. 284 * Returns 0 on success, a negative error code otherwise.
280 * 285 *
281 * This function will request a DMA channel using the passed filter function and 286 * The function should usually be called from the pcm open callback. Note that
282 * data. The function should usually be called from the pcm open callback. 287 * this function will use private_data field of the substream's runtime. So it
283 * 288 * is not availabe to your pcm driver implementation.
284 * Note that this function will use private_data field of the substream's
285 * runtime. So it is not availabe to your pcm driver implementation. If you need
286 * to keep additional data attached to a substream use
287 * snd_dmaengine_pcm_{set,get}_data.
288 */ 289 */
289int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream, 290int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream,
290 dma_filter_fn filter_fn, void *filter_data) 291 struct dma_chan *chan)
291{ 292{
292 struct dmaengine_pcm_runtime_data *prtd; 293 struct dmaengine_pcm_runtime_data *prtd;
293 int ret; 294 int ret;
294 295
296 if (!chan)
297 return -ENXIO;
298
295 ret = snd_pcm_hw_constraint_integer(substream->runtime, 299 ret = snd_pcm_hw_constraint_integer(substream->runtime,
296 SNDRV_PCM_HW_PARAM_PERIODS); 300 SNDRV_PCM_HW_PARAM_PERIODS);
297 if (ret < 0) 301 if (ret < 0)
@@ -301,11 +305,7 @@ int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream,
301 if (!prtd) 305 if (!prtd)
302 return -ENOMEM; 306 return -ENOMEM;
303 307
304 ret = dmaengine_pcm_request_channel(prtd, filter_fn, filter_data); 308 prtd->dma_chan = chan;
305 if (ret < 0) {
306 kfree(prtd);
307 return ret;
308 }
309 309
310 substream->runtime->private_data = prtd; 310 substream->runtime->private_data = prtd;
311 311
@@ -314,6 +314,27 @@ int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream,
314EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open); 314EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open);
315 315
316/** 316/**
317 * snd_dmaengine_pcm_open_request_chan - Open a dmaengine based PCM substream and request channel
318 * @substream: PCM substream
319 * @filter_fn: Filter function used to request the DMA channel
320 * @filter_data: Data passed to the DMA filter function
321 *
322 * Returns 0 on success, a negative error code otherwise.
323 *
324 * This function will request a DMA channel using the passed filter function and
325 * data. The function should usually be called from the pcm open callback. Note
326 * that this function will use private_data field of the substream's runtime. So
327 * it is not availabe to your pcm driver implementation.
328 */
329int snd_dmaengine_pcm_open_request_chan(struct snd_pcm_substream *substream,
330 dma_filter_fn filter_fn, void *filter_data)
331{
332 return snd_dmaengine_pcm_open(substream,
333 snd_dmaengine_pcm_request_channel(filter_fn, filter_data));
334}
335EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open_request_chan);
336
337/**
317 * snd_dmaengine_pcm_close - Close a dmaengine based PCM substream 338 * snd_dmaengine_pcm_close - Close a dmaengine based PCM substream
318 * @substream: PCM substream 339 * @substream: PCM substream
319 */ 340 */
@@ -321,11 +342,26 @@ int snd_dmaengine_pcm_close(struct snd_pcm_substream *substream)
321{ 342{
322 struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); 343 struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
323 344
324 dma_release_channel(prtd->dma_chan);
325 kfree(prtd); 345 kfree(prtd);
326 346
327 return 0; 347 return 0;
328} 348}
329EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close); 349EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close);
330 350
351/**
352 * snd_dmaengine_pcm_release_chan_close - Close a dmaengine based PCM substream and release channel
353 * @substream: PCM substream
354 *
355 * Releases the DMA channel associated with the PCM substream.
356 */
357int snd_dmaengine_pcm_close_release_chan(struct snd_pcm_substream *substream)
358{
359 struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
360
361 dma_release_channel(prtd->dma_chan);
362
363 return snd_dmaengine_pcm_close(substream);
364}
365EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close_release_chan);
366
331MODULE_LICENSE("GPL"); 367MODULE_LICENSE("GPL");
diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c
new file mode 100644
index 000000000000..e29ec3cd84b1
--- /dev/null
+++ b/sound/soc/soc-generic-dmaengine-pcm.c
@@ -0,0 +1,300 @@
1/*
2 * Copyright (C) 2013, Analog Devices Inc.
3 * Author: Lars-Peter Clausen <lars@metafoo.de>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version.
9 *
10 * You should have received a copy of the GNU General Public License along
11 * with this program; if not, write to the Free Software Foundation, Inc.,
12 * 675 Mass Ave, Cambridge, MA 02139, USA.
13 *
14 */
15#include <linux/module.h>
16#include <linux/init.h>
17#include <linux/dmaengine.h>
18#include <linux/slab.h>
19#include <sound/pcm.h>
20#include <sound/pcm_params.h>
21#include <sound/soc.h>
22#include <linux/dma-mapping.h>
23#include <linux/of.h>
24
25#include <sound/dmaengine_pcm.h>
26
27struct dmaengine_pcm {
28 struct dma_chan *chan[SNDRV_PCM_STREAM_CAPTURE + 1];
29 const struct snd_dmaengine_pcm_config *config;
30 struct snd_soc_platform platform;
31 unsigned int flags;
32};
33
34static struct dmaengine_pcm *soc_platform_to_pcm(struct snd_soc_platform *p)
35{
36 return container_of(p, struct dmaengine_pcm, platform);
37}
38
39/**
40 * snd_dmaengine_pcm_prepare_slave_config() - Generic prepare_slave_config callback
41 * @substream: PCM substream
42 * @params: hw_params
43 * @slave_config: DMA slave config to prepare
44 *
45 * This function can be used as a generic prepare_slave_config callback for
46 * platforms which make use of the snd_dmaengine_dai_dma_data struct for their
47 * DAI DMA data. Internally the function will first call
48 * snd_hwparams_to_dma_slave_config to fill in the slave config based on the
49 * hw_params, followed by snd_dmaengine_set_config_from_dai_data to fill in the
50 * remaining fields based on the DAI DMA data.
51 */
52int snd_dmaengine_pcm_prepare_slave_config(struct snd_pcm_substream *substream,
53 struct snd_pcm_hw_params *params, struct dma_slave_config *slave_config)
54{
55 struct snd_soc_pcm_runtime *rtd = substream->private_data;
56 struct snd_dmaengine_dai_dma_data *dma_data;
57 int ret;
58
59 dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
60
61 ret = snd_hwparams_to_dma_slave_config(substream, params, slave_config);
62 if (ret)
63 return ret;
64
65 snd_dmaengine_pcm_set_config_from_dai_data(substream, dma_data,
66 slave_config);
67
68 return 0;
69}
70EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_prepare_slave_config);
71
72static int dmaengine_pcm_hw_params(struct snd_pcm_substream *substream,
73 struct snd_pcm_hw_params *params)
74{
75 struct snd_soc_pcm_runtime *rtd = substream->private_data;
76 struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform);
77 struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
78 struct dma_slave_config slave_config;
79 int ret;
80
81 if (pcm->config->prepare_slave_config) {
82 ret = pcm->config->prepare_slave_config(substream, params,
83 &slave_config);
84 if (ret)
85 return ret;
86
87 ret = dmaengine_slave_config(chan, &slave_config);
88 if (ret)
89 return ret;
90 }
91
92 return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
93}
94
95static int dmaengine_pcm_open(struct snd_pcm_substream *substream)
96{
97 struct snd_soc_pcm_runtime *rtd = substream->private_data;
98 struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform);
99 struct dma_chan *chan = pcm->chan[substream->stream];
100 int ret;
101
102 ret = snd_soc_set_runtime_hwparams(substream,
103 pcm->config->pcm_hardware);
104 if (ret)
105 return ret;
106
107 return snd_dmaengine_pcm_open(substream, chan);
108}
109
110static struct device *dmaengine_dma_dev(struct dmaengine_pcm *pcm,
111 struct snd_pcm_substream *substream)
112{
113 if (!pcm->chan[substream->stream])
114 return NULL;
115
116 return pcm->chan[substream->stream]->device->dev;
117}
118
119static void dmaengine_pcm_free(struct snd_pcm *pcm)
120{
121 snd_pcm_lib_preallocate_free_for_all(pcm);
122}
123
124static struct dma_chan *dmaengine_pcm_compat_request_channel(
125 struct snd_soc_pcm_runtime *rtd,
126 struct snd_pcm_substream *substream)
127{
128 struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform);
129
130 if ((pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) && pcm->chan[0])
131 return pcm->chan[0];
132
133 if (pcm->config->compat_request_channel)
134 return pcm->config->compat_request_channel(rtd, substream);
135
136 return snd_dmaengine_pcm_request_channel(pcm->config->compat_filter_fn,
137 snd_soc_dai_get_dma_data(rtd->cpu_dai, substream));
138}
139
140static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd)
141{
142 struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform);
143 const struct snd_dmaengine_pcm_config *config = pcm->config;
144 struct snd_pcm_substream *substream;
145 unsigned int i;
146 int ret;
147
148 for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) {
149 substream = rtd->pcm->streams[i].substream;
150 if (!substream)
151 continue;
152
153 if (!pcm->chan[i] && (pcm->flags & SND_DMAENGINE_PCM_FLAG_COMPAT)) {
154 pcm->chan[i] = dmaengine_pcm_compat_request_channel(rtd,
155 substream);
156 }
157
158 if (!pcm->chan[i]) {
159 dev_err(rtd->platform->dev,
160 "Missing dma channel for stream: %d\n", i);
161 ret = -EINVAL;
162 goto err_free;
163 }
164
165 ret = snd_pcm_lib_preallocate_pages(substream,
166 SNDRV_DMA_TYPE_DEV,
167 dmaengine_dma_dev(pcm, substream),
168 config->prealloc_buffer_size,
169 config->pcm_hardware->buffer_bytes_max);
170 if (ret)
171 goto err_free;
172 }
173
174 return 0;
175
176err_free:
177 dmaengine_pcm_free(rtd->pcm);
178 return ret;
179}
180
181static const struct snd_pcm_ops dmaengine_pcm_ops = {
182 .open = dmaengine_pcm_open,
183 .close = snd_dmaengine_pcm_close,
184 .ioctl = snd_pcm_lib_ioctl,
185 .hw_params = dmaengine_pcm_hw_params,
186 .hw_free = snd_pcm_lib_free_pages,
187 .trigger = snd_dmaengine_pcm_trigger,
188 .pointer = snd_dmaengine_pcm_pointer,
189};
190
191static const struct snd_soc_platform_driver dmaengine_pcm_platform = {
192 .ops = &dmaengine_pcm_ops,
193 .pcm_new = dmaengine_pcm_new,
194 .pcm_free = dmaengine_pcm_free,
195 .probe_order = SND_SOC_COMP_ORDER_LATE,
196};
197
198static const struct snd_pcm_ops dmaengine_no_residue_pcm_ops = {
199 .open = dmaengine_pcm_open,
200 .close = snd_dmaengine_pcm_close,
201 .ioctl = snd_pcm_lib_ioctl,
202 .hw_params = dmaengine_pcm_hw_params,
203 .hw_free = snd_pcm_lib_free_pages,
204 .trigger = snd_dmaengine_pcm_trigger,
205 .pointer = snd_dmaengine_pcm_pointer_no_residue,
206};
207
208static const struct snd_soc_platform_driver dmaengine_no_residue_pcm_platform = {
209 .ops = &dmaengine_no_residue_pcm_ops,
210 .pcm_new = dmaengine_pcm_new,
211 .pcm_free = dmaengine_pcm_free,
212 .probe_order = SND_SOC_COMP_ORDER_LATE,
213};
214
215static const char * const dmaengine_pcm_dma_channel_names[] = {
216 [SNDRV_PCM_STREAM_PLAYBACK] = "tx",
217 [SNDRV_PCM_STREAM_CAPTURE] = "rx",
218};
219
220static void dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm,
221 struct device *dev)
222{
223 unsigned int i;
224
225 if ((pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_DT) || !dev->of_node)
226 return;
227
228 if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) {
229 pcm->chan[0] = dma_request_slave_channel(dev, "rx-tx");
230 pcm->chan[1] = pcm->chan[0];
231 } else {
232 for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) {
233 pcm->chan[i] = dma_request_slave_channel(dev,
234 dmaengine_pcm_dma_channel_names[i]);
235 }
236 }
237}
238
239/**
240 * snd_dmaengine_pcm_register - Register a dmaengine based PCM device
241 * @dev: The parent device for the PCM device
242 * @config: Platform specific PCM configuration
243 * @flags: Platform specific quirks
244 */
245int snd_dmaengine_pcm_register(struct device *dev,
246 const struct snd_dmaengine_pcm_config *config, unsigned int flags)
247{
248 struct dmaengine_pcm *pcm;
249
250 pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
251 if (!pcm)
252 return -ENOMEM;
253
254 pcm->config = config;
255 pcm->flags = flags;
256
257 dmaengine_pcm_request_chan_of(pcm, dev);
258
259 if (flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE)
260 return snd_soc_add_platform(dev, &pcm->platform,
261 &dmaengine_no_residue_pcm_platform);
262 else
263 return snd_soc_add_platform(dev, &pcm->platform,
264 &dmaengine_pcm_platform);
265}
266EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_register);
267
268/**
269 * snd_dmaengine_pcm_unregister - Removes a dmaengine based PCM device
270 * @dev: Parent device the PCM was register with
271 *
272 * Removes a dmaengine based PCM device previously registered with
273 * snd_dmaengine_pcm_register.
274 */
275void snd_dmaengine_pcm_unregister(struct device *dev)
276{
277 struct snd_soc_platform *platform;
278 struct dmaengine_pcm *pcm;
279 unsigned int i;
280
281 platform = snd_soc_lookup_platform(dev);
282 if (!platform)
283 return;
284
285 pcm = soc_platform_to_pcm(platform);
286
287 for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) {
288 if (pcm->chan[i]) {
289 dma_release_channel(pcm->chan[i]);
290 if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX)
291 break;
292 }
293 }
294
295 snd_soc_remove_platform(platform);
296 kfree(pcm);
297}
298EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_unregister);
299
300MODULE_LICENSE("GPL");
diff --git a/sound/soc/spear/spear_pcm.c b/sound/soc/spear/spear_pcm.c
index bfbcc1fcfe6f..2fbd4899d8ef 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)
64 if (ret) 64 if (ret)
65 return ret; 65 return ret;
66 66
67 return snd_dmaengine_pcm_open(substream, dma_data->filter, dma_data) 67 return snd_dmaengine_pcm_open_request_chan(substream, dma_data->filter,
68 dma_data);
68} 69}
69 70
70static int spear_pcm_mmap(struct snd_pcm_substream *substream, 71static int spear_pcm_mmap(struct snd_pcm_substream *substream,
@@ -79,7 +80,7 @@ static int spear_pcm_mmap(struct snd_pcm_substream *substream,
79 80
80static struct snd_pcm_ops spear_pcm_ops = { 81static struct snd_pcm_ops spear_pcm_ops = {
81 .open = spear_pcm_open, 82 .open = spear_pcm_open,
82 .close = snd_dmaengine_pcm_close, 83 .close = snd_dmaengine_pcm_close_release_chan,
83 .ioctl = snd_pcm_lib_ioctl, 84 .ioctl = snd_pcm_lib_ioctl,
84 .hw_params = spear_pcm_hw_params, 85 .hw_params = spear_pcm_hw_params,
85 .hw_free = spear_pcm_hw_free, 86 .hw_free = spear_pcm_hw_free,
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig
index dbc27ce1d4de..b1c9d573da05 100644
--- a/sound/soc/tegra/Kconfig
+++ b/sound/soc/tegra/Kconfig
@@ -2,7 +2,7 @@ config SND_SOC_TEGRA
2 tristate "SoC Audio for the Tegra System-on-Chip" 2 tristate "SoC Audio for the Tegra System-on-Chip"
3 depends on ARCH_TEGRA && TEGRA20_APB_DMA 3 depends on ARCH_TEGRA && TEGRA20_APB_DMA
4 select REGMAP_MMIO 4 select REGMAP_MMIO
5 select SND_SOC_DMAENGINE_PCM if TEGRA20_APB_DMA 5 select SND_SOC_GENERIC_DMAENGINE_PCM if TEGRA20_APB_DMA
6 help 6 help
7 Say Y or M here if you want support for SoC audio on Tegra. 7 Say Y or M here if you want support for SoC audio on Tegra.
8 8
diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c
index 6d1c70c3d753..f056f632557c 100644
--- a/sound/soc/tegra/tegra_pcm.c
+++ b/sound/soc/tegra/tegra_pcm.c
@@ -29,9 +29,7 @@
29 * 29 *
30 */ 30 */
31 31
32#include <linux/dma-mapping.h>
33#include <linux/module.h> 32#include <linux/module.h>
34#include <linux/slab.h>
35#include <sound/core.h> 33#include <sound/core.h>
36#include <sound/pcm.h> 34#include <sound/pcm.h>
37#include <sound/pcm_params.h> 35#include <sound/pcm_params.h>
@@ -55,175 +53,24 @@ static const struct snd_pcm_hardware tegra_pcm_hardware = {
55 .fifo_size = 4, 53 .fifo_size = 4,
56}; 54};
57 55
58static int tegra_pcm_open(struct snd_pcm_substream *substream) 56static const struct snd_dmaengine_pcm_config tegra_dmaengine_pcm_config = {
59{ 57 .pcm_hardware = &tegra_pcm_hardware,
60 struct snd_soc_pcm_runtime *rtd = substream->private_data; 58 .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
61 struct device *dev = rtd->platform->dev; 59 .compat_filter_fn = NULL,
62 int ret; 60 .prealloc_buffer_size = PAGE_SIZE * 8,
63
64 /* Set HW params now that initialization is complete */
65 snd_soc_set_runtime_hwparams(substream, &tegra_pcm_hardware);
66
67 ret = snd_dmaengine_pcm_open(substream, NULL, NULL);
68 if (ret) {
69 dev_err(dev, "dmaengine pcm open failed with err %d\n", ret);
70 return ret;
71 }
72
73 return 0;
74}
75
76static int tegra_pcm_hw_params(struct snd_pcm_substream *substream,
77 struct snd_pcm_hw_params *params)
78{
79 struct snd_soc_pcm_runtime *rtd = substream->private_data;
80 struct device *dev = rtd->platform->dev;
81 struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
82 struct dma_slave_config slave_config;
83 int ret;
84
85 ret = snd_hwparams_to_dma_slave_config(substream, params,
86 &slave_config);
87 if (ret) {
88 dev_err(dev, "hw params config failed with err %d\n", ret);
89 return ret;
90 }
91
92 snd_dmaengine_pcm_set_config_from_dai_data(substream,
93 snd_soc_dai_get_dma_data(rtd->cpu_dai, substream),
94 &slave_config);
95
96 ret = dmaengine_slave_config(chan, &slave_config);
97 if (ret < 0) {
98 dev_err(dev, "dma slave config failed with err %d\n", ret);
99 return ret;
100 }
101
102 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
103 return 0;
104}
105
106static int tegra_pcm_hw_free(struct snd_pcm_substream *substream)
107{
108 snd_pcm_set_runtime_buffer(substream, NULL);
109 return 0;
110}
111
112static int tegra_pcm_mmap(struct snd_pcm_substream *substream,
113 struct vm_area_struct *vma)
114{
115 struct snd_pcm_runtime *runtime = substream->runtime;
116
117 return dma_mmap_writecombine(substream->pcm->card->dev, vma,
118 runtime->dma_area,
119 runtime->dma_addr,
120 runtime->dma_bytes);
121}
122
123static struct snd_pcm_ops tegra_pcm_ops = {
124 .open = tegra_pcm_open,
125 .close = snd_dmaengine_pcm_close,
126 .ioctl = snd_pcm_lib_ioctl,
127 .hw_params = tegra_pcm_hw_params,
128 .hw_free = tegra_pcm_hw_free,
129 .trigger = snd_dmaengine_pcm_trigger,
130 .pointer = snd_dmaengine_pcm_pointer,
131 .mmap = tegra_pcm_mmap,
132};
133
134static int tegra_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
135{
136 struct snd_pcm_substream *substream = pcm->streams[stream].substream;
137 struct snd_dma_buffer *buf = &substream->dma_buffer;
138 size_t size = tegra_pcm_hardware.buffer_bytes_max;
139
140 buf->area = dma_alloc_writecombine(pcm->card->dev, size,
141 &buf->addr, GFP_KERNEL);
142 if (!buf->area)
143 return -ENOMEM;
144
145 buf->dev.type = SNDRV_DMA_TYPE_DEV;
146 buf->dev.dev = pcm->card->dev;
147 buf->private_data = NULL;
148 buf->bytes = size;
149
150 return 0;
151}
152
153static void tegra_pcm_deallocate_dma_buffer(struct snd_pcm *pcm, int stream)
154{
155 struct snd_pcm_substream *substream;
156 struct snd_dma_buffer *buf;
157
158 substream = pcm->streams[stream].substream;
159 if (!substream)
160 return;
161
162 buf = &substream->dma_buffer;
163 if (!buf->area)
164 return;
165
166 dma_free_writecombine(pcm->card->dev, buf->bytes,
167 buf->area, buf->addr);
168 buf->area = NULL;
169}
170
171static u64 tegra_dma_mask = DMA_BIT_MASK(32);
172
173static int tegra_pcm_new(struct snd_soc_pcm_runtime *rtd)
174{
175 struct snd_card *card = rtd->card->snd_card;
176 struct snd_pcm *pcm = rtd->pcm;
177 int ret = 0;
178
179 if (!card->dev->dma_mask)
180 card->dev->dma_mask = &tegra_dma_mask;
181 if (!card->dev->coherent_dma_mask)
182 card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
183
184 if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
185 ret = tegra_pcm_preallocate_dma_buffer(pcm,
186 SNDRV_PCM_STREAM_PLAYBACK);
187 if (ret)
188 goto err;
189 }
190
191 if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
192 ret = tegra_pcm_preallocate_dma_buffer(pcm,
193 SNDRV_PCM_STREAM_CAPTURE);
194 if (ret)
195 goto err_free_play;
196 }
197
198 return 0;
199
200err_free_play:
201 tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK);
202err:
203 return ret;
204}
205
206static void tegra_pcm_free(struct snd_pcm *pcm)
207{
208 tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE);
209 tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK);
210}
211
212static struct snd_soc_platform_driver tegra_pcm_platform = {
213 .ops = &tegra_pcm_ops,
214 .pcm_new = tegra_pcm_new,
215 .pcm_free = tegra_pcm_free,
216}; 61};
217 62
218int tegra_pcm_platform_register(struct device *dev) 63int tegra_pcm_platform_register(struct device *dev)
219{ 64{
220 return snd_soc_register_platform(dev, &tegra_pcm_platform); 65 return snd_dmaengine_pcm_register(dev, &tegra_dmaengine_pcm_config,
66 SND_DMAENGINE_PCM_FLAG_NO_DT |
67 SND_DMAENGINE_PCM_FLAG_COMPAT);
221} 68}
222EXPORT_SYMBOL_GPL(tegra_pcm_platform_register); 69EXPORT_SYMBOL_GPL(tegra_pcm_platform_register);
223 70
224void tegra_pcm_platform_unregister(struct device *dev) 71void tegra_pcm_platform_unregister(struct device *dev)
225{ 72{
226 snd_soc_unregister_platform(dev); 73 return snd_dmaengine_pcm_unregister(dev);
227} 74}
228EXPORT_SYMBOL_GPL(tegra_pcm_platform_unregister); 75EXPORT_SYMBOL_GPL(tegra_pcm_platform_unregister);
229 76
diff --git a/sound/soc/ux500/Kconfig b/sound/soc/ux500/Kconfig
index 069330d82be5..c73c5907eb11 100644
--- a/sound/soc/ux500/Kconfig
+++ b/sound/soc/ux500/Kconfig
@@ -16,7 +16,7 @@ config SND_SOC_UX500_PLAT_MSP_I2S
16config SND_SOC_UX500_PLAT_DMA 16config SND_SOC_UX500_PLAT_DMA
17 tristate "Platform - DB8500 (DMA)" 17 tristate "Platform - DB8500 (DMA)"
18 depends on SND_SOC_UX500 18 depends on SND_SOC_UX500
19 select SND_SOC_DMAENGINE_PCM 19 select SND_SOC_GENERIC_DMAENGINE_PCM
20 help 20 help
21 Say Y if you want to enable the Ux500 platform-driver. 21 Say Y if you want to enable the Ux500 platform-driver.
22 22
diff --git a/sound/soc/ux500/ux500_pcm.c b/sound/soc/ux500/ux500_pcm.c
index 09b5364e5095..b6e5ae277299 100644
--- a/sound/soc/ux500/ux500_pcm.c
+++ b/sound/soc/ux500/ux500_pcm.c
@@ -40,7 +40,7 @@
40#define UX500_PLATFORM_PERIODS_MAX 48 40#define UX500_PLATFORM_PERIODS_MAX 48
41#define UX500_PLATFORM_BUFFER_BYTES_MAX (2048 * PAGE_SIZE) 41#define UX500_PLATFORM_BUFFER_BYTES_MAX (2048 * PAGE_SIZE)
42 42
43static struct snd_pcm_hardware ux500_pcm_hw = { 43static const struct snd_pcm_hardware ux500_pcm_hw = {
44 .info = SNDRV_PCM_INFO_INTERLEAVED | 44 .info = SNDRV_PCM_INFO_INTERLEAVED |
45 SNDRV_PCM_INFO_MMAP | 45 SNDRV_PCM_INFO_MMAP |
46 SNDRV_PCM_INFO_RESUME | 46 SNDRV_PCM_INFO_RESUME |
@@ -61,43 +61,23 @@ static struct snd_pcm_hardware ux500_pcm_hw = {
61 .periods_max = UX500_PLATFORM_PERIODS_MAX, 61 .periods_max = UX500_PLATFORM_PERIODS_MAX,
62}; 62};
63 63
64static void ux500_pcm_dma_hw_free(struct device *dev, 64static struct dma_chan *ux500_pcm_request_chan(struct snd_soc_pcm_runtime *rtd,
65 struct snd_pcm_substream *substream) 65 struct snd_pcm_substream *substream)
66{ 66{
67 struct snd_pcm_runtime *runtime = substream->runtime;
68 struct snd_dma_buffer *buf = runtime->dma_buffer_p;
69
70 if (runtime->dma_area == NULL)
71 return;
72
73 if (buf != &substream->dma_buffer) {
74 dma_free_coherent(buf->dev.dev, buf->bytes, buf->area,
75 buf->addr);
76 kfree(runtime->dma_buffer_p);
77 }
78
79 snd_pcm_set_runtime_buffer(substream, NULL);
80}
81
82static int ux500_pcm_open(struct snd_pcm_substream *substream)
83{
84 struct snd_soc_pcm_runtime *rtd = substream->private_data;
85 struct snd_soc_dai *dai = rtd->cpu_dai; 67 struct snd_soc_dai *dai = rtd->cpu_dai;
86 struct device *dev = dai->dev; 68 struct device *dev = dai->dev;
87 int ret;
88 struct ux500_msp_dma_params *dma_params;
89 u16 per_data_width, mem_data_width; 69 u16 per_data_width, mem_data_width;
90 struct stedma40_chan_cfg *dma_cfg; 70 struct stedma40_chan_cfg *dma_cfg;
71 struct ux500_msp_dma_params *dma_params;
91 72
92 dev_dbg(dev, "%s: MSP %d (%s): Enter.\n", __func__, dai->id, 73 dev_dbg(dev, "%s: MSP %d (%s): Enter.\n", __func__, dai->id,
93 snd_pcm_stream_str(substream)); 74 snd_pcm_stream_str(substream));
94 75
95 dev_dbg(dev, "%s: Set runtime hwparams.\n", __func__); 76 dma_params = snd_soc_dai_get_dma_data(dai, substream);
96 snd_soc_set_runtime_hwparams(substream, &ux500_pcm_hw); 77 dma_cfg = dma_params->dma_cfg;
97 78
98 mem_data_width = STEDMA40_HALFWORD_WIDTH; 79 mem_data_width = STEDMA40_HALFWORD_WIDTH;
99 80
100 dma_params = snd_soc_dai_get_dma_data(dai, substream);
101 switch (dma_params->data_size) { 81 switch (dma_params->data_size) {
102 case 32: 82 case 32:
103 per_data_width = STEDMA40_WORD_WIDTH; 83 per_data_width = STEDMA40_WORD_WIDTH;
@@ -110,13 +90,8 @@ static int ux500_pcm_open(struct snd_pcm_substream *substream)
110 break; 90 break;
111 default: 91 default:
112 per_data_width = STEDMA40_WORD_WIDTH; 92 per_data_width = STEDMA40_WORD_WIDTH;
113 dev_warn(rtd->platform->dev,
114 "%s: Unknown data-size (%d)! Assuming 32 bits.\n",
115 __func__, dma_params->data_size);
116 } 93 }
117 94
118 dma_cfg = dma_params->dma_cfg;
119
120 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 95 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
121 dma_cfg->src_info.data_width = mem_data_width; 96 dma_cfg->src_info.data_width = mem_data_width;
122 dma_cfg->dst_info.data_width = per_data_width; 97 dma_cfg->dst_info.data_width = per_data_width;
@@ -125,123 +100,24 @@ static int ux500_pcm_open(struct snd_pcm_substream *substream)
125 dma_cfg->dst_info.data_width = mem_data_width; 100 dma_cfg->dst_info.data_width = mem_data_width;
126 } 101 }
127 102
128 103 return snd_dmaengine_pcm_request_channel(stedma40_filter, dma_cfg);
129 ret = snd_dmaengine_pcm_open(substream, stedma40_filter, dma_cfg);
130 if (ret) {
131 dev_dbg(dai->dev,
132 "%s: ERROR: snd_dmaengine_pcm_open failed (%d)!\n",
133 __func__, ret);
134 return ret;
135 }
136
137 return 0;
138} 104}
139 105
140static int ux500_pcm_hw_params(struct snd_pcm_substream *substream, 106static const struct snd_dmaengine_pcm_config ux500_dmaengine_pcm_config = {
141 struct snd_pcm_hw_params *hw_params) 107 .pcm_hardware = &ux500_pcm_hw,
142{ 108 .compat_request_channel = ux500_pcm_request_chan,
143 struct snd_pcm_runtime *runtime = substream->runtime; 109 .prealloc_buffer_size = 128 * 1024,
144 struct snd_dma_buffer *buf = runtime->dma_buffer_p;
145 struct snd_soc_pcm_runtime *rtd = substream->private_data;
146 int ret = 0;
147 int size;
148
149 dev_dbg(rtd->platform->dev, "%s: Enter\n", __func__);
150
151 size = params_buffer_bytes(hw_params);
152
153 if (buf) {
154 if (buf->bytes >= size)
155 goto out;
156 ux500_pcm_dma_hw_free(NULL, substream);
157 }
158
159 if (substream->dma_buffer.area != NULL &&
160 substream->dma_buffer.bytes >= size) {
161 buf = &substream->dma_buffer;
162 } else {
163 buf = kmalloc(sizeof(struct snd_dma_buffer), GFP_KERNEL);
164 if (!buf)
165 goto nomem;
166
167 buf->dev.type = SNDRV_DMA_TYPE_DEV;
168 buf->dev.dev = NULL;
169 buf->area = dma_alloc_coherent(NULL, size, &buf->addr,
170 GFP_KERNEL);
171 buf->bytes = size;
172 buf->private_data = NULL;
173
174 if (!buf->area)
175 goto free;
176 }
177 snd_pcm_set_runtime_buffer(substream, buf);
178 ret = 1;
179 out:
180 runtime->dma_bytes = size;
181 return ret;
182
183 free:
184 kfree(buf);
185 nomem:
186 return -ENOMEM;
187}
188
189static int ux500_pcm_hw_free(struct snd_pcm_substream *substream)
190{
191 struct snd_soc_pcm_runtime *rtd = substream->private_data;
192
193 dev_dbg(rtd->platform->dev, "%s: Enter\n", __func__);
194
195 ux500_pcm_dma_hw_free(NULL, substream);
196
197 return 0;
198}
199
200static int ux500_pcm_mmap(struct snd_pcm_substream *substream,
201 struct vm_area_struct *vma)
202{
203 struct snd_pcm_runtime *runtime = substream->runtime;
204 struct snd_soc_pcm_runtime *rtd = substream->private_data;
205
206 dev_dbg(rtd->platform->dev, "%s: Enter.\n", __func__);
207
208 return dma_mmap_coherent(NULL, vma, runtime->dma_area,
209 runtime->dma_addr, runtime->dma_bytes);
210}
211
212static struct snd_pcm_ops ux500_pcm_ops = {
213 .open = ux500_pcm_open,
214 .close = snd_dmaengine_pcm_close,
215 .ioctl = snd_pcm_lib_ioctl,
216 .hw_params = ux500_pcm_hw_params,
217 .hw_free = ux500_pcm_hw_free,
218 .trigger = snd_dmaengine_pcm_trigger,
219 .pointer = snd_dmaengine_pcm_pointer_no_residue,
220 .mmap = ux500_pcm_mmap
221};
222
223int ux500_pcm_new(struct snd_soc_pcm_runtime *rtd)
224{
225 struct snd_pcm *pcm = rtd->pcm;
226
227 dev_dbg(rtd->platform->dev, "%s: Enter (id = '%s').\n", __func__,
228 pcm->id);
229
230 pcm->info_flags = 0;
231
232 return 0;
233}
234
235static struct snd_soc_platform_driver ux500_pcm_soc_drv = {
236 .ops = &ux500_pcm_ops,
237 .pcm_new = ux500_pcm_new,
238}; 110};
239 111
240int ux500_pcm_register_platform(struct platform_device *pdev) 112int ux500_pcm_register_platform(struct platform_device *pdev)
241{ 113{
242 int ret; 114 int ret;
243 115
244 ret = snd_soc_register_platform(&pdev->dev, &ux500_pcm_soc_drv); 116 ret = snd_dmaengine_pcm_register(&pdev->dev,
117 &ux500_dmaengine_pcm_config,
118 SND_DMAENGINE_PCM_FLAG_NO_RESIDUE |
119 SND_DMAENGINE_PCM_FLAG_COMPAT |
120 SND_DMAENGINE_PCM_FLAG_NO_DT);
245 if (ret < 0) { 121 if (ret < 0) {
246 dev_err(&pdev->dev, 122 dev_err(&pdev->dev,
247 "%s: ERROR: Failed to register platform '%s' (%d)!\n", 123 "%s: ERROR: Failed to register platform '%s' (%d)!\n",
@@ -255,8 +131,7 @@ EXPORT_SYMBOL_GPL(ux500_pcm_register_platform);
255 131
256int ux500_pcm_unregister_platform(struct platform_device *pdev) 132int ux500_pcm_unregister_platform(struct platform_device *pdev)
257{ 133{
258 snd_soc_unregister_platform(&pdev->dev); 134 snd_dmaengine_pcm_unregister(&pdev->dev);
259
260 return 0; 135 return 0;
261} 136}
262EXPORT_SYMBOL_GPL(ux500_pcm_unregister_platform); 137EXPORT_SYMBOL_GPL(ux500_pcm_unregister_platform);