aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/soc-generic-dmaengine-pcm.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/soc-generic-dmaengine-pcm.c')
-rw-r--r--sound/soc/soc-generic-dmaengine-pcm.c94
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
287static void dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm, 290static 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
340static 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
388err_free_dma:
389 dmaengine_pcm_release_chan(pcm);
390 kfree(pcm);
391 return ret;
334} 392}
335EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_register); 393EXPORT_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}
367EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_unregister); 417EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_unregister);