diff options
Diffstat (limited to 'sound/soc/soc-generic-dmaengine-pcm.c')
-rw-r--r-- | sound/soc/soc-generic-dmaengine-pcm.c | 131 |
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 | |||
195 | static 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 | ||
187 | static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd) | 210 | static 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 | ||
281 | static 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 | |||
248 | static const struct snd_pcm_ops dmaengine_pcm_ops = { | 293 | static 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 | ||
258 | static const struct snd_soc_platform_driver dmaengine_pcm_platform = { | 303 | static 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 | ||
265 | static 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 | |||
275 | static 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 | |||
282 | static const char * const dmaengine_pcm_dma_channel_names[] = { | 310 | static 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 | ||
287 | static void dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm, | 315 | static 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 | ||
308 | static void dmaengine_pcm_release_chan(struct dmaengine_pcm *pcm) | 365 | static 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 | ||