diff options
author | Peter Ujfalusi <peter.ujfalusi@ti.com> | 2012-09-14 08:05:58 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2012-09-22 11:13:02 -0400 |
commit | 946cc36ae550ea52adee0f42ac5034a34b5393be (patch) | |
tree | 9b2df993e42e844b8a1f4f95ad1c88b6c5c395ba /sound/soc/omap | |
parent | bcd6da7bfd1bc3e9d5f8887967ec3d550ce56c70 (diff) |
ASoC: omap-pcm: Convert to use dmaengine
Original author: Russell King <rmk+kernel@arm.linux.org.uk>
Switch the omap-pcm to use dmaengine.
Certain features are not supported by after dmaengine conversion:
1. No period wakeup mode
DMA engine has no way to communicate this information through
standard channels.
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
Tested-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/omap')
-rw-r--r-- | sound/soc/omap/Kconfig | 3 | ||||
-rw-r--r-- | sound/soc/omap/omap-pcm.c | 269 |
2 files changed, 61 insertions, 211 deletions
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index 2c484a52ef92..7048137f9a33 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig | |||
@@ -1,6 +1,7 @@ | |||
1 | config SND_OMAP_SOC | 1 | config SND_OMAP_SOC |
2 | tristate "SoC Audio for the Texas Instruments OMAP chips" | 2 | tristate "SoC Audio for the Texas Instruments OMAP chips" |
3 | depends on ARCH_OMAP | 3 | depends on ARCH_OMAP && DMA_OMAP |
4 | select SND_SOC_DMAENGINE_PCM | ||
4 | 5 | ||
5 | config SND_OMAP_SOC_DMIC | 6 | config SND_OMAP_SOC_DMIC |
6 | tristate | 7 | tristate |
diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c index 74da4b7209d0..a2636f6b8362 100644 --- a/sound/soc/omap/omap-pcm.c +++ b/sound/soc/omap/omap-pcm.c | |||
@@ -25,12 +25,13 @@ | |||
25 | #include <linux/dma-mapping.h> | 25 | #include <linux/dma-mapping.h> |
26 | #include <linux/slab.h> | 26 | #include <linux/slab.h> |
27 | #include <linux/module.h> | 27 | #include <linux/module.h> |
28 | #include <linux/omap-dma.h> | ||
28 | #include <sound/core.h> | 29 | #include <sound/core.h> |
29 | #include <sound/pcm.h> | 30 | #include <sound/pcm.h> |
30 | #include <sound/pcm_params.h> | 31 | #include <sound/pcm_params.h> |
32 | #include <sound/dmaengine_pcm.h> | ||
31 | #include <sound/soc.h> | 33 | #include <sound/soc.h> |
32 | 34 | ||
33 | #include <plat/dma.h> | ||
34 | #include "omap-pcm.h" | 35 | #include "omap-pcm.h" |
35 | 36 | ||
36 | static const struct snd_pcm_hardware omap_pcm_hardware = { | 37 | static const struct snd_pcm_hardware omap_pcm_hardware = { |
@@ -49,61 +50,34 @@ static const struct snd_pcm_hardware omap_pcm_hardware = { | |||
49 | .buffer_bytes_max = 128 * 1024, | 50 | .buffer_bytes_max = 128 * 1024, |
50 | }; | 51 | }; |
51 | 52 | ||
52 | struct omap_runtime_data { | 53 | static int omap_pcm_get_dma_buswidth(int num_bits) |
53 | spinlock_t lock; | ||
54 | struct omap_pcm_dma_data *dma_data; | ||
55 | int dma_ch; | ||
56 | int period_index; | ||
57 | }; | ||
58 | |||
59 | static void omap_pcm_dma_irq(int ch, u16 stat, void *data) | ||
60 | { | 54 | { |
61 | struct snd_pcm_substream *substream = data; | 55 | int buswidth; |
62 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
63 | struct omap_runtime_data *prtd = runtime->private_data; | ||
64 | unsigned long flags; | ||
65 | |||
66 | if ((cpu_is_omap1510())) { | ||
67 | /* | ||
68 | * OMAP1510 doesn't fully support DMA progress counter | ||
69 | * and there is no software emulation implemented yet, | ||
70 | * so have to maintain our own progress counters | ||
71 | * that can be used by omap_pcm_pointer() instead. | ||
72 | */ | ||
73 | spin_lock_irqsave(&prtd->lock, flags); | ||
74 | if ((stat == OMAP_DMA_LAST_IRQ) && | ||
75 | (prtd->period_index == runtime->periods - 1)) { | ||
76 | /* we are in sync, do nothing */ | ||
77 | spin_unlock_irqrestore(&prtd->lock, flags); | ||
78 | return; | ||
79 | } | ||
80 | if (prtd->period_index >= 0) { | ||
81 | if (stat & OMAP_DMA_BLOCK_IRQ) { | ||
82 | /* end of buffer reached, loop back */ | ||
83 | prtd->period_index = 0; | ||
84 | } else if (stat & OMAP_DMA_LAST_IRQ) { | ||
85 | /* update the counter for the last period */ | ||
86 | prtd->period_index = runtime->periods - 1; | ||
87 | } else if (++prtd->period_index >= runtime->periods) { | ||
88 | /* end of buffer missed? loop back */ | ||
89 | prtd->period_index = 0; | ||
90 | } | ||
91 | } | ||
92 | spin_unlock_irqrestore(&prtd->lock, flags); | ||
93 | } | ||
94 | 56 | ||
95 | snd_pcm_period_elapsed(substream); | 57 | switch (num_bits) { |
58 | case 16: | ||
59 | buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES; | ||
60 | break; | ||
61 | case 32: | ||
62 | buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES; | ||
63 | break; | ||
64 | default: | ||
65 | buswidth = -EINVAL; | ||
66 | break; | ||
67 | } | ||
68 | return buswidth; | ||
96 | } | 69 | } |
97 | 70 | ||
71 | |||
98 | /* this may get called several times by oss emulation */ | 72 | /* this may get called several times by oss emulation */ |
99 | static int omap_pcm_hw_params(struct snd_pcm_substream *substream, | 73 | static int omap_pcm_hw_params(struct snd_pcm_substream *substream, |
100 | struct snd_pcm_hw_params *params) | 74 | struct snd_pcm_hw_params *params) |
101 | { | 75 | { |
102 | struct snd_pcm_runtime *runtime = substream->runtime; | 76 | struct snd_pcm_runtime *runtime = substream->runtime; |
103 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 77 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
104 | struct omap_runtime_data *prtd = runtime->private_data; | ||
105 | struct omap_pcm_dma_data *dma_data; | 78 | struct omap_pcm_dma_data *dma_data; |
106 | 79 | struct dma_slave_config config; | |
80 | struct dma_chan *chan; | ||
107 | int err = 0; | 81 | int err = 0; |
108 | 82 | ||
109 | dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); | 83 | dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); |
@@ -116,195 +90,78 @@ static int omap_pcm_hw_params(struct snd_pcm_substream *substream, | |||
116 | snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); | 90 | snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); |
117 | runtime->dma_bytes = params_buffer_bytes(params); | 91 | runtime->dma_bytes = params_buffer_bytes(params); |
118 | 92 | ||
119 | if (prtd->dma_data) | 93 | chan = snd_dmaengine_pcm_get_chan(substream); |
120 | return 0; | 94 | if (!chan) |
121 | prtd->dma_data = dma_data; | 95 | return -EINVAL; |
122 | err = omap_request_dma(dma_data->dma_req, dma_data->name, | ||
123 | omap_pcm_dma_irq, substream, &prtd->dma_ch); | ||
124 | if (!err) { | ||
125 | /* | ||
126 | * Link channel with itself so DMA doesn't need any | ||
127 | * reprogramming while looping the buffer | ||
128 | */ | ||
129 | omap_dma_link_lch(prtd->dma_ch, prtd->dma_ch); | ||
130 | } | ||
131 | |||
132 | return err; | ||
133 | } | ||
134 | |||
135 | static int omap_pcm_hw_free(struct snd_pcm_substream *substream) | ||
136 | { | ||
137 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
138 | struct omap_runtime_data *prtd = runtime->private_data; | ||
139 | |||
140 | if (prtd->dma_data == NULL) | ||
141 | return 0; | ||
142 | 96 | ||
143 | omap_dma_unlink_lch(prtd->dma_ch, prtd->dma_ch); | 97 | /* fills in addr_width and direction */ |
144 | omap_free_dma(prtd->dma_ch); | 98 | err = snd_hwparams_to_dma_slave_config(substream, params, &config); |
145 | prtd->dma_data = NULL; | 99 | if (err) |
100 | return err; | ||
146 | 101 | ||
147 | snd_pcm_set_runtime_buffer(substream, NULL); | 102 | /* Override the *_dma addr_width if requested by the DAI driver */ |
103 | if (dma_data->data_type) { | ||
104 | int buswidth = omap_pcm_get_dma_buswidth(dma_data->data_type); | ||
148 | 105 | ||
149 | return 0; | 106 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
150 | } | 107 | config.dst_addr_width = buswidth; |
108 | else | ||
109 | config.src_addr_width = buswidth; | ||
110 | } | ||
151 | 111 | ||
152 | static int omap_pcm_get_dma_type(int num_bits) | 112 | config.src_addr = dma_data->port_addr; |
153 | { | 113 | config.dst_addr = dma_data->port_addr; |
154 | int data_type; | 114 | config.src_maxburst = dma_data->packet_size; |
115 | config.dst_maxburst = dma_data->packet_size; | ||
155 | 116 | ||
156 | switch (num_bits) { | 117 | return dmaengine_slave_config(chan, &config); |
157 | case 16: | ||
158 | data_type = OMAP_DMA_DATA_TYPE_S16; | ||
159 | break; | ||
160 | case 32: | ||
161 | data_type = OMAP_DMA_DATA_TYPE_S32; | ||
162 | break; | ||
163 | default: | ||
164 | data_type = -EINVAL; | ||
165 | break; | ||
166 | } | ||
167 | return data_type; | ||
168 | } | 118 | } |
169 | 119 | ||
170 | static int omap_pcm_prepare(struct snd_pcm_substream *substream) | 120 | static int omap_pcm_hw_free(struct snd_pcm_substream *substream) |
171 | { | 121 | { |
172 | struct snd_pcm_runtime *runtime = substream->runtime; | 122 | snd_pcm_set_runtime_buffer(substream, NULL); |
173 | struct omap_runtime_data *prtd = runtime->private_data; | ||
174 | struct omap_pcm_dma_data *dma_data = prtd->dma_data; | ||
175 | struct omap_dma_channel_params dma_params; | ||
176 | int bytes; | ||
177 | |||
178 | /* return if this is a bufferless transfer e.g. | ||
179 | * codec <--> BT codec or GSM modem -- lg FIXME */ | ||
180 | if (!prtd->dma_data) | ||
181 | return 0; | ||
182 | |||
183 | memset(&dma_params, 0, sizeof(dma_params)); | ||
184 | |||
185 | if (dma_data->data_type) | ||
186 | dma_params.data_type = omap_pcm_get_dma_type( | ||
187 | dma_data->data_type); | ||
188 | else | ||
189 | dma_params.data_type = omap_pcm_get_dma_type( | ||
190 | snd_pcm_format_physical_width(runtime->format)); | ||
191 | |||
192 | if (dma_params.data_type < 0) | ||
193 | return dma_params.data_type; | ||
194 | |||
195 | dma_params.trigger = dma_data->dma_req; | ||
196 | |||
197 | if (dma_data->packet_size) | ||
198 | dma_params.sync_mode = OMAP_DMA_SYNC_PACKET; | ||
199 | else | ||
200 | dma_params.sync_mode = OMAP_DMA_SYNC_ELEMENT; | ||
201 | |||
202 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
203 | dma_params.src_amode = OMAP_DMA_AMODE_POST_INC; | ||
204 | dma_params.dst_amode = OMAP_DMA_AMODE_CONSTANT; | ||
205 | dma_params.src_or_dst_synch = OMAP_DMA_DST_SYNC; | ||
206 | dma_params.src_start = runtime->dma_addr; | ||
207 | dma_params.dst_start = dma_data->port_addr; | ||
208 | dma_params.dst_port = OMAP_DMA_PORT_MPUI; | ||
209 | dma_params.dst_fi = dma_data->packet_size; | ||
210 | } else { | ||
211 | dma_params.src_amode = OMAP_DMA_AMODE_CONSTANT; | ||
212 | dma_params.dst_amode = OMAP_DMA_AMODE_POST_INC; | ||
213 | dma_params.src_or_dst_synch = OMAP_DMA_SRC_SYNC; | ||
214 | dma_params.src_start = dma_data->port_addr; | ||
215 | dma_params.dst_start = runtime->dma_addr; | ||
216 | dma_params.src_port = OMAP_DMA_PORT_MPUI; | ||
217 | dma_params.src_fi = dma_data->packet_size; | ||
218 | } | ||
219 | /* | ||
220 | * Set DMA transfer frame size equal to ALSA period size and frame | ||
221 | * count as no. of ALSA periods. Then with DMA frame interrupt enabled, | ||
222 | * we can transfer the whole ALSA buffer with single DMA transfer but | ||
223 | * still can get an interrupt at each period bounary | ||
224 | */ | ||
225 | bytes = snd_pcm_lib_period_bytes(substream); | ||
226 | dma_params.elem_count = bytes >> dma_params.data_type; | ||
227 | dma_params.frame_count = runtime->periods; | ||
228 | omap_set_dma_params(prtd->dma_ch, &dma_params); | ||
229 | |||
230 | if ((cpu_is_omap1510())) | ||
231 | omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ | | ||
232 | OMAP_DMA_LAST_IRQ | OMAP_DMA_BLOCK_IRQ); | ||
233 | else if (!substream->runtime->no_period_wakeup) | ||
234 | omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ); | ||
235 | else { | ||
236 | /* | ||
237 | * No period wakeup: | ||
238 | * we need to disable BLOCK_IRQ, which is enabled by the omap | ||
239 | * dma core at request dma time. | ||
240 | */ | ||
241 | omap_disable_dma_irq(prtd->dma_ch, OMAP_DMA_BLOCK_IRQ); | ||
242 | } | ||
243 | |||
244 | if (!(cpu_class_is_omap1())) { | ||
245 | omap_set_dma_src_burst_mode(prtd->dma_ch, | ||
246 | OMAP_DMA_DATA_BURST_16); | ||
247 | omap_set_dma_dest_burst_mode(prtd->dma_ch, | ||
248 | OMAP_DMA_DATA_BURST_16); | ||
249 | } | ||
250 | |||
251 | return 0; | 123 | return 0; |
252 | } | 124 | } |
253 | 125 | ||
254 | static int omap_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | 126 | static int omap_pcm_trigger(struct snd_pcm_substream *substream, int cmd) |
255 | { | 127 | { |
256 | struct snd_pcm_runtime *runtime = substream->runtime; | 128 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
257 | struct omap_runtime_data *prtd = runtime->private_data; | 129 | struct omap_pcm_dma_data *dma_data; |
258 | struct omap_pcm_dma_data *dma_data = prtd->dma_data; | ||
259 | unsigned long flags; | ||
260 | int ret = 0; | 130 | int ret = 0; |
261 | 131 | ||
262 | spin_lock_irqsave(&prtd->lock, flags); | 132 | dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); |
133 | |||
263 | switch (cmd) { | 134 | switch (cmd) { |
264 | case SNDRV_PCM_TRIGGER_START: | 135 | case SNDRV_PCM_TRIGGER_START: |
265 | case SNDRV_PCM_TRIGGER_RESUME: | 136 | case SNDRV_PCM_TRIGGER_RESUME: |
266 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | 137 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
267 | prtd->period_index = 0; | ||
268 | /* Configure McBSP internal buffer usage */ | 138 | /* Configure McBSP internal buffer usage */ |
269 | if (dma_data->set_threshold) | 139 | if (dma_data->set_threshold) |
270 | dma_data->set_threshold(substream); | 140 | dma_data->set_threshold(substream); |
271 | |||
272 | omap_start_dma(prtd->dma_ch); | ||
273 | break; | 141 | break; |
274 | 142 | ||
275 | case SNDRV_PCM_TRIGGER_STOP: | 143 | case SNDRV_PCM_TRIGGER_STOP: |
276 | case SNDRV_PCM_TRIGGER_SUSPEND: | 144 | case SNDRV_PCM_TRIGGER_SUSPEND: |
277 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | 145 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
278 | prtd->period_index = -1; | ||
279 | omap_stop_dma(prtd->dma_ch); | ||
280 | break; | 146 | break; |
281 | default: | 147 | default: |
282 | ret = -EINVAL; | 148 | ret = -EINVAL; |
283 | } | 149 | } |
284 | spin_unlock_irqrestore(&prtd->lock, flags); | 150 | |
151 | if (ret == 0) | ||
152 | ret = snd_dmaengine_pcm_trigger(substream, cmd); | ||
285 | 153 | ||
286 | return ret; | 154 | return ret; |
287 | } | 155 | } |
288 | 156 | ||
289 | static snd_pcm_uframes_t omap_pcm_pointer(struct snd_pcm_substream *substream) | 157 | static snd_pcm_uframes_t omap_pcm_pointer(struct snd_pcm_substream *substream) |
290 | { | 158 | { |
291 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
292 | struct omap_runtime_data *prtd = runtime->private_data; | ||
293 | dma_addr_t ptr; | ||
294 | snd_pcm_uframes_t offset; | 159 | snd_pcm_uframes_t offset; |
295 | 160 | ||
296 | if (cpu_is_omap1510()) { | 161 | if (cpu_is_omap1510()) |
297 | offset = prtd->period_index * runtime->period_size; | 162 | offset = snd_dmaengine_pcm_pointer_no_residue(substream); |
298 | } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { | 163 | else |
299 | ptr = omap_get_dma_dst_pos(prtd->dma_ch); | 164 | offset = snd_dmaengine_pcm_pointer(substream); |
300 | offset = bytes_to_frames(runtime, ptr - runtime->dma_addr); | ||
301 | } else { | ||
302 | ptr = omap_get_dma_src_pos(prtd->dma_ch); | ||
303 | offset = bytes_to_frames(runtime, ptr - runtime->dma_addr); | ||
304 | } | ||
305 | |||
306 | if (offset >= runtime->buffer_size) | ||
307 | offset = 0; | ||
308 | 165 | ||
309 | return offset; | 166 | return offset; |
310 | } | 167 | } |
@@ -312,7 +169,8 @@ static snd_pcm_uframes_t omap_pcm_pointer(struct snd_pcm_substream *substream) | |||
312 | static int omap_pcm_open(struct snd_pcm_substream *substream) | 169 | static int omap_pcm_open(struct snd_pcm_substream *substream) |
313 | { | 170 | { |
314 | struct snd_pcm_runtime *runtime = substream->runtime; | 171 | struct snd_pcm_runtime *runtime = substream->runtime; |
315 | struct omap_runtime_data *prtd; | 172 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
173 | struct omap_pcm_dma_data *dma_data; | ||
316 | int ret; | 174 | int ret; |
317 | 175 | ||
318 | snd_soc_set_runtime_hwparams(substream, &omap_pcm_hardware); | 176 | snd_soc_set_runtime_hwparams(substream, &omap_pcm_hardware); |
@@ -321,25 +179,17 @@ static int omap_pcm_open(struct snd_pcm_substream *substream) | |||
321 | ret = snd_pcm_hw_constraint_integer(runtime, | 179 | ret = snd_pcm_hw_constraint_integer(runtime, |
322 | SNDRV_PCM_HW_PARAM_PERIODS); | 180 | SNDRV_PCM_HW_PARAM_PERIODS); |
323 | if (ret < 0) | 181 | if (ret < 0) |
324 | goto out; | 182 | return ret; |
325 | |||
326 | prtd = kzalloc(sizeof(*prtd), GFP_KERNEL); | ||
327 | if (prtd == NULL) { | ||
328 | ret = -ENOMEM; | ||
329 | goto out; | ||
330 | } | ||
331 | spin_lock_init(&prtd->lock); | ||
332 | runtime->private_data = prtd; | ||
333 | 183 | ||
334 | out: | 184 | dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); |
185 | ret = snd_dmaengine_pcm_open(substream, omap_dma_filter_fn, | ||
186 | &dma_data->dma_req); | ||
335 | return ret; | 187 | return ret; |
336 | } | 188 | } |
337 | 189 | ||
338 | static int omap_pcm_close(struct snd_pcm_substream *substream) | 190 | static int omap_pcm_close(struct snd_pcm_substream *substream) |
339 | { | 191 | { |
340 | struct snd_pcm_runtime *runtime = substream->runtime; | 192 | snd_dmaengine_pcm_close(substream); |
341 | |||
342 | kfree(runtime->private_data); | ||
343 | return 0; | 193 | return 0; |
344 | } | 194 | } |
345 | 195 | ||
@@ -360,7 +210,6 @@ static struct snd_pcm_ops omap_pcm_ops = { | |||
360 | .ioctl = snd_pcm_lib_ioctl, | 210 | .ioctl = snd_pcm_lib_ioctl, |
361 | .hw_params = omap_pcm_hw_params, | 211 | .hw_params = omap_pcm_hw_params, |
362 | .hw_free = omap_pcm_hw_free, | 212 | .hw_free = omap_pcm_hw_free, |
363 | .prepare = omap_pcm_prepare, | ||
364 | .trigger = omap_pcm_trigger, | 213 | .trigger = omap_pcm_trigger, |
365 | .pointer = omap_pcm_pointer, | 214 | .pointer = omap_pcm_pointer, |
366 | .mmap = omap_pcm_mmap, | 215 | .mmap = omap_pcm_mmap, |