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.c131
1 files changed, 93 insertions, 38 deletions
diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c
index 41949af3baae..5bace124ef43 100644
--- a/sound/soc/soc-generic-dmaengine-pcm.c
+++ b/sound/soc/soc-generic-dmaengine-pcm.c
@@ -137,10 +137,15 @@ 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)
143 hw.info |= SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME; 146 hw.info |= SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME;
147 if (dma_caps.residue_granularity <= DMA_RESIDUE_GRANULARITY_SEGMENT)
148 hw.info |= SNDRV_PCM_INFO_BATCH;
144 } 149 }
145 150
146 return snd_soc_set_runtime_hwparams(substream, &hw); 151 return snd_soc_set_runtime_hwparams(substream, &hw);
@@ -171,17 +176,35 @@ static struct dma_chan *dmaengine_pcm_compat_request_channel(
171{ 176{
172 struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform); 177 struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform);
173 struct snd_dmaengine_dai_dma_data *dma_data; 178 struct snd_dmaengine_dai_dma_data *dma_data;
179 dma_filter_fn fn = NULL;
174 180
175 dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); 181 dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
176 182
177 if ((pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) && pcm->chan[0]) 183 if ((pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) && pcm->chan[0])
178 return pcm->chan[0]; 184 return pcm->chan[0];
179 185
180 if (pcm->config->compat_request_channel) 186 if (pcm->config && pcm->config->compat_request_channel)
181 return pcm->config->compat_request_channel(rtd, substream); 187 return pcm->config->compat_request_channel(rtd, substream);
182 188
183 return snd_dmaengine_pcm_request_channel(pcm->config->compat_filter_fn, 189 if (pcm->config)
184 dma_data->filter_data); 190 fn = pcm->config->compat_filter_fn;
191
192 return snd_dmaengine_pcm_request_channel(fn, dma_data->filter_data);
193}
194
195static bool dmaengine_pcm_can_report_residue(struct dma_chan *chan)
196{
197 struct dma_slave_caps dma_caps;
198 int ret;
199
200 ret = dma_get_slave_caps(chan, &dma_caps);
201 if (ret != 0)
202 return true;
203
204 if (dma_caps.residue_granularity == DMA_RESIDUE_GRANULARITY_DESCRIPTOR)
205 return false;
206
207 return true;
185} 208}
186 209
187static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd) 210static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd)
@@ -236,6 +259,16 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd)
236 max_buffer_size); 259 max_buffer_size);
237 if (ret) 260 if (ret)
238 goto err_free; 261 goto err_free;
262
263 /*
264 * This will only return false if we know for sure that at least
265 * one channel does not support residue reporting. If the DMA
266 * driver does not implement the slave_caps API we rely having
267 * the NO_RESIDUE flag set manually in case residue reporting is
268 * not supported.
269 */
270 if (!dmaengine_pcm_can_report_residue(pcm->chan[i]))
271 pcm->flags |= SND_DMAENGINE_PCM_FLAG_NO_RESIDUE;
239 } 272 }
240 273
241 return 0; 274 return 0;
@@ -245,6 +278,18 @@ err_free:
245 return ret; 278 return ret;
246} 279}
247 280
281static snd_pcm_uframes_t dmaengine_pcm_pointer(
282 struct snd_pcm_substream *substream)
283{
284 struct snd_soc_pcm_runtime *rtd = substream->private_data;
285 struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform);
286
287 if (pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE)
288 return snd_dmaengine_pcm_pointer_no_residue(substream);
289 else
290 return snd_dmaengine_pcm_pointer(substream);
291}
292
248static const struct snd_pcm_ops dmaengine_pcm_ops = { 293static const struct snd_pcm_ops dmaengine_pcm_ops = {
249 .open = dmaengine_pcm_open, 294 .open = dmaengine_pcm_open,
250 .close = snd_dmaengine_pcm_close, 295 .close = snd_dmaengine_pcm_close,
@@ -252,7 +297,7 @@ static const struct snd_pcm_ops dmaengine_pcm_ops = {
252 .hw_params = dmaengine_pcm_hw_params, 297 .hw_params = dmaengine_pcm_hw_params,
253 .hw_free = snd_pcm_lib_free_pages, 298 .hw_free = snd_pcm_lib_free_pages,
254 .trigger = snd_dmaengine_pcm_trigger, 299 .trigger = snd_dmaengine_pcm_trigger,
255 .pointer = snd_dmaengine_pcm_pointer, 300 .pointer = dmaengine_pcm_pointer,
256}; 301};
257 302
258static const struct snd_soc_platform_driver dmaengine_pcm_platform = { 303static const struct snd_soc_platform_driver dmaengine_pcm_platform = {
@@ -262,47 +307,59 @@ static const struct snd_soc_platform_driver dmaengine_pcm_platform = {
262 .probe_order = SND_SOC_COMP_ORDER_LATE, 307 .probe_order = SND_SOC_COMP_ORDER_LATE,
263}; 308};
264 309
265static const struct snd_pcm_ops dmaengine_no_residue_pcm_ops = {
266 .open = dmaengine_pcm_open,
267 .close = snd_dmaengine_pcm_close,
268 .ioctl = snd_pcm_lib_ioctl,
269 .hw_params = dmaengine_pcm_hw_params,
270 .hw_free = snd_pcm_lib_free_pages,
271 .trigger = snd_dmaengine_pcm_trigger,
272 .pointer = snd_dmaengine_pcm_pointer_no_residue,
273};
274
275static const struct snd_soc_platform_driver dmaengine_no_residue_pcm_platform = {
276 .ops = &dmaengine_no_residue_pcm_ops,
277 .pcm_new = dmaengine_pcm_new,
278 .pcm_free = dmaengine_pcm_free,
279 .probe_order = SND_SOC_COMP_ORDER_LATE,
280};
281
282static const char * const dmaengine_pcm_dma_channel_names[] = { 310static const char * const dmaengine_pcm_dma_channel_names[] = {
283 [SNDRV_PCM_STREAM_PLAYBACK] = "tx", 311 [SNDRV_PCM_STREAM_PLAYBACK] = "tx",
284 [SNDRV_PCM_STREAM_CAPTURE] = "rx", 312 [SNDRV_PCM_STREAM_CAPTURE] = "rx",
285}; 313};
286 314
287static void dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm, 315static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm,
288 struct device *dev) 316 struct device *dev, const struct snd_dmaengine_pcm_config *config)
289{ 317{
290 unsigned int i; 318 unsigned int i;
319 const char *name;
320 struct dma_chan *chan;
291 321
292 if ((pcm->flags & (SND_DMAENGINE_PCM_FLAG_NO_DT | 322 if ((pcm->flags & (SND_DMAENGINE_PCM_FLAG_NO_DT |
293 SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME)) || 323 SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME)) ||
294 !dev->of_node) 324 !dev->of_node)
295 return; 325 return 0;
326
327 if (config && config->dma_dev) {
328 /*
329 * If this warning is seen, it probably means that your Linux
330 * device structure does not match your HW device structure.
331 * It would be best to refactor the Linux device structure to
332 * correctly match the HW structure.
333 */
334 dev_warn(dev, "DMA channels sourced from device %s",
335 dev_name(config->dma_dev));
336 dev = config->dma_dev;
337 }
296 338
297 if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) { 339 for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE;
298 pcm->chan[0] = dma_request_slave_channel(dev, "rx-tx"); 340 i++) {
299 pcm->chan[1] = pcm->chan[0]; 341 if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX)
300 } else { 342 name = "rx-tx";
301 for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) { 343 else
302 pcm->chan[i] = dma_request_slave_channel(dev, 344 name = dmaengine_pcm_dma_channel_names[i];
303 dmaengine_pcm_dma_channel_names[i]); 345 if (config && config->chan_names[i])
346 name = config->chan_names[i];
347 chan = dma_request_slave_channel_reason(dev, name);
348 if (IS_ERR(chan)) {
349 if (PTR_ERR(chan) == -EPROBE_DEFER)
350 return -EPROBE_DEFER;
351 pcm->chan[i] = NULL;
352 } else {
353 pcm->chan[i] = chan;
304 } 354 }
355 if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX)
356 break;
305 } 357 }
358
359 if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX)
360 pcm->chan[1] = pcm->chan[0];
361
362 return 0;
306} 363}
307 364
308static void dmaengine_pcm_release_chan(struct dmaengine_pcm *pcm) 365static void dmaengine_pcm_release_chan(struct dmaengine_pcm *pcm)
@@ -338,14 +395,12 @@ int snd_dmaengine_pcm_register(struct device *dev,
338 pcm->config = config; 395 pcm->config = config;
339 pcm->flags = flags; 396 pcm->flags = flags;
340 397
341 dmaengine_pcm_request_chan_of(pcm, dev); 398 ret = dmaengine_pcm_request_chan_of(pcm, dev, config);
399 if (ret)
400 goto err_free_dma;
342 401
343 if (flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE) 402 ret = snd_soc_add_platform(dev, &pcm->platform,
344 ret = snd_soc_add_platform(dev, &pcm->platform, 403 &dmaengine_pcm_platform);
345 &dmaengine_no_residue_pcm_platform);
346 else
347 ret = snd_soc_add_platform(dev, &pcm->platform,
348 &dmaengine_pcm_platform);
349 if (ret) 404 if (ret)
350 goto err_free_dma; 405 goto err_free_dma;
351 406