diff options
| -rw-r--r-- | drivers/dma/pl330.c | 1 | ||||
| -rw-r--r-- | include/linux/dmaengine.h | 28 | ||||
| -rw-r--r-- | sound/soc/adi/axi-i2s.c | 3 | ||||
| -rw-r--r-- | sound/soc/adi/axi-spdif.c | 3 | ||||
| -rw-r--r-- | sound/soc/samsung/dmaengine.c | 1 | ||||
| -rw-r--r-- | sound/soc/soc-generic-dmaengine-pcm.c | 66 |
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 | */ | ||
| 387 | enum 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 | */ |
| 378 | struct dma_slave_caps { | 405 | struct 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 | ||
| 386 | static inline const char *dma_chan_name(struct dma_chan *chan) | 414 | static 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 | } |
| 74 | EXPORT_SYMBOL_GPL(samsung_asoc_dma_platform_register); | 73 | EXPORT_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 | ||
| 192 | static 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 | |||
| 190 | static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd) | 207 | static 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 | ||
| 278 | static 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 | |||
| 251 | static const struct snd_pcm_ops dmaengine_pcm_ops = { | 290 | static 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 | ||
| 261 | static const struct snd_soc_platform_driver dmaengine_pcm_platform = { | 300 | static 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 | ||
| 268 | static 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 | |||
| 278 | static 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 | |||
| 285 | static const char * const dmaengine_pcm_dma_channel_names[] = { | 307 | static 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 | ||
