aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/dma/pl330.c1
-rw-r--r--include/linux/dmaengine.h28
-rw-r--r--sound/soc/adi/axi-i2s.c3
-rw-r--r--sound/soc/adi/axi-spdif.c3
-rw-r--r--sound/soc/samsung/dmaengine.c1
-rw-r--r--sound/soc/soc-generic-dmaengine-pcm.c66
6 files changed, 73 insertions, 29 deletions
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index 536632f6479c..c90edecee463 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -2884,6 +2884,7 @@ static int pl330_dma_device_slave_caps(struct dma_chan *dchan,
2884 caps->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); 2884 caps->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
2885 caps->cmd_pause = false; 2885 caps->cmd_pause = false;
2886 caps->cmd_terminate = true; 2886 caps->cmd_terminate = true;
2887 caps->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
2887 2888
2888 return 0; 2889 return 0;
2889} 2890}
diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
index ed92b30a02fd..ba5f96db0754 100644
--- a/include/linux/dmaengine.h
+++ b/include/linux/dmaengine.h
@@ -364,6 +364,32 @@ struct dma_slave_config {
364 unsigned int slave_id; 364 unsigned int slave_id;
365}; 365};
366 366
367/**
368 * enum dma_residue_granularity - Granularity of the reported transfer residue
369 * @DMA_RESIDUE_GRANULARITY_DESCRIPTOR: Residue reporting is not support. The
370 * DMA channel is only able to tell whether a descriptor has been completed or
371 * not, which means residue reporting is not supported by this channel. The
372 * residue field of the dma_tx_state field will always be 0.
373 * @DMA_RESIDUE_GRANULARITY_SEGMENT: Residue is updated after each successfully
374 * completed segment of the transfer (For cyclic transfers this is after each
375 * period). This is typically implemented by having the hardware generate an
376 * interrupt after each transferred segment and then the drivers updates the
377 * outstanding residue by the size of the segment. Another possibility is if
378 * the hardware supports scatter-gather and the segment descriptor has a field
379 * which gets set after the segment has been completed. The driver then counts
380 * the number of segments without the flag set to compute the residue.
381 * @DMA_RESIDUE_GRANULARITY_BURST: Residue is updated after each transferred
382 * burst. This is typically only supported if the hardware has a progress
383 * register of some sort (E.g. a register with the current read/write address
384 * or a register with the amount of bursts/beats/bytes that have been
385 * transferred or still need to be transferred).
386 */
387enum dma_residue_granularity {
388 DMA_RESIDUE_GRANULARITY_DESCRIPTOR = 0,
389 DMA_RESIDUE_GRANULARITY_SEGMENT = 1,
390 DMA_RESIDUE_GRANULARITY_BURST = 2,
391};
392
367/* struct dma_slave_caps - expose capabilities of a slave channel only 393/* struct dma_slave_caps - expose capabilities of a slave channel only
368 * 394 *
369 * @src_addr_widths: bit mask of src addr widths the channel supports 395 * @src_addr_widths: bit mask of src addr widths the channel supports
@@ -374,6 +400,7 @@ struct dma_slave_config {
374 * should be checked by controller as well 400 * should be checked by controller as well
375 * @cmd_pause: true, if pause and thereby resume is supported 401 * @cmd_pause: true, if pause and thereby resume is supported
376 * @cmd_terminate: true, if terminate cmd is supported 402 * @cmd_terminate: true, if terminate cmd is supported
403 * @residue_granularity: granularity of the reported transfer residue
377 */ 404 */
378struct dma_slave_caps { 405struct dma_slave_caps {
379 u32 src_addr_widths; 406 u32 src_addr_widths;
@@ -381,6 +408,7 @@ struct dma_slave_caps {
381 u32 directions; 408 u32 directions;
382 bool cmd_pause; 409 bool cmd_pause;
383 bool cmd_terminate; 410 bool cmd_terminate;
411 enum dma_residue_granularity residue_granularity;
384}; 412};
385 413
386static inline const char *dma_chan_name(struct dma_chan *chan) 414static inline const char *dma_chan_name(struct dma_chan *chan)
diff --git a/sound/soc/adi/axi-i2s.c b/sound/soc/adi/axi-i2s.c
index 7f91a86dd734..6058c1fd5070 100644
--- a/sound/soc/adi/axi-i2s.c
+++ b/sound/soc/adi/axi-i2s.c
@@ -236,8 +236,7 @@ static int axi_i2s_probe(struct platform_device *pdev)
236 if (ret) 236 if (ret)
237 goto err_clk_disable; 237 goto err_clk_disable;
238 238
239 ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 239 ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
240 SND_DMAENGINE_PCM_FLAG_NO_RESIDUE);
241 if (ret) 240 if (ret)
242 goto err_clk_disable; 241 goto err_clk_disable;
243 242
diff --git a/sound/soc/adi/axi-spdif.c b/sound/soc/adi/axi-spdif.c
index 8db7a9920695..198e3a4640f6 100644
--- a/sound/soc/adi/axi-spdif.c
+++ b/sound/soc/adi/axi-spdif.c
@@ -229,8 +229,7 @@ static int axi_spdif_probe(struct platform_device *pdev)
229 if (ret) 229 if (ret)
230 goto err_clk_disable; 230 goto err_clk_disable;
231 231
232 ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 232 ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
233 SND_DMAENGINE_PCM_FLAG_NO_RESIDUE);
234 if (ret) 233 if (ret)
235 goto err_clk_disable; 234 goto err_clk_disable;
236 235
diff --git a/sound/soc/samsung/dmaengine.c b/sound/soc/samsung/dmaengine.c
index 3be479d51b9b..750ce5808d9f 100644
--- a/sound/soc/samsung/dmaengine.c
+++ b/sound/soc/samsung/dmaengine.c
@@ -68,7 +68,6 @@ int samsung_asoc_dma_platform_register(struct device *dev)
68{ 68{
69 return snd_dmaengine_pcm_register(dev, &samsung_dmaengine_pcm_config, 69 return snd_dmaengine_pcm_register(dev, &samsung_dmaengine_pcm_config,
70 SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME | 70 SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME |
71 SND_DMAENGINE_PCM_FLAG_NO_RESIDUE |
72 SND_DMAENGINE_PCM_FLAG_COMPAT); 71 SND_DMAENGINE_PCM_FLAG_COMPAT);
73} 72}
74EXPORT_SYMBOL_GPL(samsung_asoc_dma_platform_register); 73EXPORT_SYMBOL_GPL(samsung_asoc_dma_platform_register);
diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c
index 2a6c569d991f..560a7787d8a7 100644
--- a/sound/soc/soc-generic-dmaengine-pcm.c
+++ b/sound/soc/soc-generic-dmaengine-pcm.c
@@ -144,6 +144,8 @@ static int dmaengine_pcm_set_runtime_hwparams(struct snd_pcm_substream *substrea
144 if (ret == 0) { 144 if (ret == 0) {
145 if (dma_caps.cmd_pause) 145 if (dma_caps.cmd_pause)
146 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;
147 } 149 }
148 150
149 return snd_soc_set_runtime_hwparams(substream, &hw); 151 return snd_soc_set_runtime_hwparams(substream, &hw);
@@ -187,6 +189,21 @@ static struct dma_chan *dmaengine_pcm_compat_request_channel(
187 dma_data->filter_data); 189 dma_data->filter_data);
188} 190}
189 191
192static bool dmaengine_pcm_can_report_residue(struct dma_chan *chan)
193{
194 struct dma_slave_caps dma_caps;
195 int ret;
196
197 ret = dma_get_slave_caps(chan, &dma_caps);
198 if (ret != 0)
199 return true;
200
201 if (dma_caps.residue_granularity == DMA_RESIDUE_GRANULARITY_DESCRIPTOR)
202 return false;
203
204 return true;
205}
206
190static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd) 207static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd)
191{ 208{
192 struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform); 209 struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform);
@@ -239,6 +256,16 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd)
239 max_buffer_size); 256 max_buffer_size);
240 if (ret) 257 if (ret)
241 goto err_free; 258 goto err_free;
259
260 /*
261 * This will only return false if we know for sure that at least
262 * one channel does not support residue reporting. If the DMA
263 * driver does not implement the slave_caps API we rely having
264 * the NO_RESIDUE flag set manually in case residue reporting is
265 * not supported.
266 */
267 if (!dmaengine_pcm_can_report_residue(pcm->chan[i]))
268 pcm->flags |= SND_DMAENGINE_PCM_FLAG_NO_RESIDUE;
242 } 269 }
243 270
244 return 0; 271 return 0;
@@ -248,6 +275,18 @@ err_free:
248 return ret; 275 return ret;
249} 276}
250 277
278static snd_pcm_uframes_t dmaengine_pcm_pointer(
279 struct snd_pcm_substream *substream)
280{
281 struct snd_soc_pcm_runtime *rtd = substream->private_data;
282 struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform);
283
284 if (pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE)
285 return snd_dmaengine_pcm_pointer_no_residue(substream);
286 else
287 return snd_dmaengine_pcm_pointer(substream);
288}
289
251static const struct snd_pcm_ops dmaengine_pcm_ops = { 290static const struct snd_pcm_ops dmaengine_pcm_ops = {
252 .open = dmaengine_pcm_open, 291 .open = dmaengine_pcm_open,
253 .close = snd_dmaengine_pcm_close, 292 .close = snd_dmaengine_pcm_close,
@@ -255,7 +294,7 @@ static const struct snd_pcm_ops dmaengine_pcm_ops = {
255 .hw_params = dmaengine_pcm_hw_params, 294 .hw_params = dmaengine_pcm_hw_params,
256 .hw_free = snd_pcm_lib_free_pages, 295 .hw_free = snd_pcm_lib_free_pages,
257 .trigger = snd_dmaengine_pcm_trigger, 296 .trigger = snd_dmaengine_pcm_trigger,
258 .pointer = snd_dmaengine_pcm_pointer, 297 .pointer = dmaengine_pcm_pointer,
259}; 298};
260 299
261static const struct snd_soc_platform_driver dmaengine_pcm_platform = { 300static const struct snd_soc_platform_driver dmaengine_pcm_platform = {
@@ -265,23 +304,6 @@ static const struct snd_soc_platform_driver dmaengine_pcm_platform = {
265 .probe_order = SND_SOC_COMP_ORDER_LATE, 304 .probe_order = SND_SOC_COMP_ORDER_LATE,
266}; 305};
267 306
268static const struct snd_pcm_ops dmaengine_no_residue_pcm_ops = {
269 .open = dmaengine_pcm_open,
270 .close = snd_dmaengine_pcm_close,
271 .ioctl = snd_pcm_lib_ioctl,
272 .hw_params = dmaengine_pcm_hw_params,
273 .hw_free = snd_pcm_lib_free_pages,
274 .trigger = snd_dmaengine_pcm_trigger,
275 .pointer = snd_dmaengine_pcm_pointer_no_residue,
276};
277
278static const struct snd_soc_platform_driver dmaengine_no_residue_pcm_platform = {
279 .ops = &dmaengine_no_residue_pcm_ops,
280 .pcm_new = dmaengine_pcm_new,
281 .pcm_free = dmaengine_pcm_free,
282 .probe_order = SND_SOC_COMP_ORDER_LATE,
283};
284
285static const char * const dmaengine_pcm_dma_channel_names[] = { 307static const char * const dmaengine_pcm_dma_channel_names[] = {
286 [SNDRV_PCM_STREAM_PLAYBACK] = "tx", 308 [SNDRV_PCM_STREAM_PLAYBACK] = "tx",
287 [SNDRV_PCM_STREAM_CAPTURE] = "rx", 309 [SNDRV_PCM_STREAM_CAPTURE] = "rx",
@@ -374,12 +396,8 @@ int snd_dmaengine_pcm_register(struct device *dev,
374 if (ret) 396 if (ret)
375 goto err_free_dma; 397 goto err_free_dma;
376 398
377 if (flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE) 399 ret = snd_soc_add_platform(dev, &pcm->platform,
378 ret = snd_soc_add_platform(dev, &pcm->platform, 400 &dmaengine_pcm_platform);
379 &dmaengine_no_residue_pcm_platform);
380 else
381 ret = snd_soc_add_platform(dev, &pcm->platform,
382 &dmaengine_pcm_platform);
383 if (ret) 401 if (ret)
384 goto err_free_dma; 402 goto err_free_dma;
385 403