diff options
Diffstat (limited to 'sound/soc/soc-generic-dmaengine-pcm.c')
-rw-r--r-- | sound/soc/soc-generic-dmaengine-pcm.c | 94 |
1 files changed, 72 insertions, 22 deletions
diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index cbc9c96ce1f4..2a6c569d991f 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c | |||
@@ -137,6 +137,9 @@ static int dmaengine_pcm_set_runtime_hwparams(struct snd_pcm_substream *substrea | |||
137 | hw.buffer_bytes_max = SIZE_MAX; | 137 | hw.buffer_bytes_max = SIZE_MAX; |
138 | hw.fifo_size = dma_data->fifo_size; | 138 | hw.fifo_size = dma_data->fifo_size; |
139 | 139 | ||
140 | if (pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE) | ||
141 | hw.info |= SNDRV_PCM_INFO_BATCH; | ||
142 | |||
140 | ret = dma_get_slave_caps(chan, &dma_caps); | 143 | ret = dma_get_slave_caps(chan, &dma_caps); |
141 | if (ret == 0) { | 144 | if (ret == 0) { |
142 | if (dma_caps.cmd_pause) | 145 | if (dma_caps.cmd_pause) |
@@ -284,24 +287,67 @@ static const char * const dmaengine_pcm_dma_channel_names[] = { | |||
284 | [SNDRV_PCM_STREAM_CAPTURE] = "rx", | 287 | [SNDRV_PCM_STREAM_CAPTURE] = "rx", |
285 | }; | 288 | }; |
286 | 289 | ||
287 | static void dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm, | 290 | static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm, |
288 | struct device *dev) | 291 | struct device *dev, const struct snd_dmaengine_pcm_config *config) |
289 | { | 292 | { |
290 | unsigned int i; | 293 | unsigned int i; |
294 | const char *name; | ||
295 | struct dma_chan *chan; | ||
291 | 296 | ||
292 | if ((pcm->flags & (SND_DMAENGINE_PCM_FLAG_NO_DT | | 297 | if ((pcm->flags & (SND_DMAENGINE_PCM_FLAG_NO_DT | |
293 | SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME)) || | 298 | SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME)) || |
294 | !dev->of_node) | 299 | !dev->of_node) |
295 | return; | 300 | return 0; |
301 | |||
302 | if (config && config->dma_dev) { | ||
303 | /* | ||
304 | * If this warning is seen, it probably means that your Linux | ||
305 | * device structure does not match your HW device structure. | ||
306 | * It would be best to refactor the Linux device structure to | ||
307 | * correctly match the HW structure. | ||
308 | */ | ||
309 | dev_warn(dev, "DMA channels sourced from device %s", | ||
310 | dev_name(config->dma_dev)); | ||
311 | dev = config->dma_dev; | ||
312 | } | ||
296 | 313 | ||
297 | if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) { | 314 | for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; |
298 | pcm->chan[0] = dma_request_slave_channel(dev, "rx-tx"); | 315 | i++) { |
299 | pcm->chan[1] = pcm->chan[0]; | 316 | if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) |
300 | } else { | 317 | name = "rx-tx"; |
301 | for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) { | 318 | else |
302 | pcm->chan[i] = dma_request_slave_channel(dev, | 319 | name = dmaengine_pcm_dma_channel_names[i]; |
303 | dmaengine_pcm_dma_channel_names[i]); | 320 | if (config && config->chan_names[i]) |
321 | name = config->chan_names[i]; | ||
322 | chan = dma_request_slave_channel_reason(dev, name); | ||
323 | if (IS_ERR(chan)) { | ||
324 | if (PTR_ERR(chan) == -EPROBE_DEFER) | ||
325 | return -EPROBE_DEFER; | ||
326 | pcm->chan[i] = NULL; | ||
327 | } else { | ||
328 | pcm->chan[i] = chan; | ||
304 | } | 329 | } |
330 | if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) | ||
331 | break; | ||
332 | } | ||
333 | |||
334 | if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) | ||
335 | pcm->chan[1] = pcm->chan[0]; | ||
336 | |||
337 | return 0; | ||
338 | } | ||
339 | |||
340 | static void dmaengine_pcm_release_chan(struct dmaengine_pcm *pcm) | ||
341 | { | ||
342 | unsigned int i; | ||
343 | |||
344 | for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; | ||
345 | i++) { | ||
346 | if (!pcm->chan[i]) | ||
347 | continue; | ||
348 | dma_release_channel(pcm->chan[i]); | ||
349 | if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) | ||
350 | break; | ||
305 | } | 351 | } |
306 | } | 352 | } |
307 | 353 | ||
@@ -315,6 +361,7 @@ int snd_dmaengine_pcm_register(struct device *dev, | |||
315 | const struct snd_dmaengine_pcm_config *config, unsigned int flags) | 361 | const struct snd_dmaengine_pcm_config *config, unsigned int flags) |
316 | { | 362 | { |
317 | struct dmaengine_pcm *pcm; | 363 | struct dmaengine_pcm *pcm; |
364 | int ret; | ||
318 | 365 | ||
319 | pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); | 366 | pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); |
320 | if (!pcm) | 367 | if (!pcm) |
@@ -323,14 +370,25 @@ int snd_dmaengine_pcm_register(struct device *dev, | |||
323 | pcm->config = config; | 370 | pcm->config = config; |
324 | pcm->flags = flags; | 371 | pcm->flags = flags; |
325 | 372 | ||
326 | dmaengine_pcm_request_chan_of(pcm, dev); | 373 | ret = dmaengine_pcm_request_chan_of(pcm, dev, config); |
374 | if (ret) | ||
375 | goto err_free_dma; | ||
327 | 376 | ||
328 | if (flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE) | 377 | if (flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE) |
329 | return snd_soc_add_platform(dev, &pcm->platform, | 378 | ret = snd_soc_add_platform(dev, &pcm->platform, |
330 | &dmaengine_no_residue_pcm_platform); | 379 | &dmaengine_no_residue_pcm_platform); |
331 | else | 380 | else |
332 | return snd_soc_add_platform(dev, &pcm->platform, | 381 | ret = snd_soc_add_platform(dev, &pcm->platform, |
333 | &dmaengine_pcm_platform); | 382 | &dmaengine_pcm_platform); |
383 | if (ret) | ||
384 | goto err_free_dma; | ||
385 | |||
386 | return 0; | ||
387 | |||
388 | err_free_dma: | ||
389 | dmaengine_pcm_release_chan(pcm); | ||
390 | kfree(pcm); | ||
391 | return ret; | ||
334 | } | 392 | } |
335 | EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_register); | 393 | EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_register); |
336 | 394 | ||
@@ -345,7 +403,6 @@ void snd_dmaengine_pcm_unregister(struct device *dev) | |||
345 | { | 403 | { |
346 | struct snd_soc_platform *platform; | 404 | struct snd_soc_platform *platform; |
347 | struct dmaengine_pcm *pcm; | 405 | struct dmaengine_pcm *pcm; |
348 | unsigned int i; | ||
349 | 406 | ||
350 | platform = snd_soc_lookup_platform(dev); | 407 | platform = snd_soc_lookup_platform(dev); |
351 | if (!platform) | 408 | if (!platform) |
@@ -353,15 +410,8 @@ void snd_dmaengine_pcm_unregister(struct device *dev) | |||
353 | 410 | ||
354 | pcm = soc_platform_to_pcm(platform); | 411 | pcm = soc_platform_to_pcm(platform); |
355 | 412 | ||
356 | for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) { | ||
357 | if (pcm->chan[i]) { | ||
358 | dma_release_channel(pcm->chan[i]); | ||
359 | if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) | ||
360 | break; | ||
361 | } | ||
362 | } | ||
363 | |||
364 | snd_soc_remove_platform(platform); | 413 | snd_soc_remove_platform(platform); |
414 | dmaengine_pcm_release_chan(pcm); | ||
365 | kfree(pcm); | 415 | kfree(pcm); |
366 | } | 416 | } |
367 | EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_unregister); | 417 | EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_unregister); |