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, caps->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); caps->cmd_pause = false; caps->cmd_terminate = true; + caps->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR; return 0; } 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 { unsigned int slave_id; }; +/** + * enum dma_residue_granularity - Granularity of the reported transfer residue + * @DMA_RESIDUE_GRANULARITY_DESCRIPTOR: Residue reporting is not support. The + * DMA channel is only able to tell whether a descriptor has been completed or + * not, which means residue reporting is not supported by this channel. The + * residue field of the dma_tx_state field will always be 0. + * @DMA_RESIDUE_GRANULARITY_SEGMENT: Residue is updated after each successfully + * completed segment of the transfer (For cyclic transfers this is after each + * period). This is typically implemented by having the hardware generate an + * interrupt after each transferred segment and then the drivers updates the + * outstanding residue by the size of the segment. Another possibility is if + * the hardware supports scatter-gather and the segment descriptor has a field + * which gets set after the segment has been completed. The driver then counts + * the number of segments without the flag set to compute the residue. + * @DMA_RESIDUE_GRANULARITY_BURST: Residue is updated after each transferred + * burst. This is typically only supported if the hardware has a progress + * register of some sort (E.g. a register with the current read/write address + * or a register with the amount of bursts/beats/bytes that have been + * transferred or still need to be transferred). + */ +enum dma_residue_granularity { + DMA_RESIDUE_GRANULARITY_DESCRIPTOR = 0, + DMA_RESIDUE_GRANULARITY_SEGMENT = 1, + DMA_RESIDUE_GRANULARITY_BURST = 2, +}; + /* struct dma_slave_caps - expose capabilities of a slave channel only * * @src_addr_widths: bit mask of src addr widths the channel supports @@ -374,6 +400,7 @@ struct dma_slave_config { * should be checked by controller as well * @cmd_pause: true, if pause and thereby resume is supported * @cmd_terminate: true, if terminate cmd is supported + * @residue_granularity: granularity of the reported transfer residue */ struct dma_slave_caps { u32 src_addr_widths; @@ -381,6 +408,7 @@ struct dma_slave_caps { u32 directions; bool cmd_pause; bool cmd_terminate; + enum dma_residue_granularity residue_granularity; }; 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) if (ret) goto err_clk_disable; - ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, - SND_DMAENGINE_PCM_FLAG_NO_RESIDUE); + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); if (ret) goto err_clk_disable; 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) if (ret) goto err_clk_disable; - ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, - SND_DMAENGINE_PCM_FLAG_NO_RESIDUE); + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); if (ret) goto err_clk_disable; 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) { return snd_dmaengine_pcm_register(dev, &samsung_dmaengine_pcm_config, SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME | - SND_DMAENGINE_PCM_FLAG_NO_RESIDUE | SND_DMAENGINE_PCM_FLAG_COMPAT); } 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 if (ret == 0) { if (dma_caps.cmd_pause) hw.info |= SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME; + if (dma_caps.residue_granularity <= DMA_RESIDUE_GRANULARITY_SEGMENT) + hw.info |= SNDRV_PCM_INFO_BATCH; } return snd_soc_set_runtime_hwparams(substream, &hw); @@ -187,6 +189,21 @@ static struct dma_chan *dmaengine_pcm_compat_request_channel( dma_data->filter_data); } +static bool dmaengine_pcm_can_report_residue(struct dma_chan *chan) +{ + struct dma_slave_caps dma_caps; + int ret; + + ret = dma_get_slave_caps(chan, &dma_caps); + if (ret != 0) + return true; + + if (dma_caps.residue_granularity == DMA_RESIDUE_GRANULARITY_DESCRIPTOR) + return false; + + return true; +} + static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd) { 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) max_buffer_size); if (ret) goto err_free; + + /* + * This will only return false if we know for sure that at least + * one channel does not support residue reporting. If the DMA + * driver does not implement the slave_caps API we rely having + * the NO_RESIDUE flag set manually in case residue reporting is + * not supported. + */ + if (!dmaengine_pcm_can_report_residue(pcm->chan[i])) + pcm->flags |= SND_DMAENGINE_PCM_FLAG_NO_RESIDUE; } return 0; @@ -248,6 +275,18 @@ err_free: return ret; } +static snd_pcm_uframes_t dmaengine_pcm_pointer( + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform); + + if (pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE) + return snd_dmaengine_pcm_pointer_no_residue(substream); + else + return snd_dmaengine_pcm_pointer(substream); +} + static const struct snd_pcm_ops dmaengine_pcm_ops = { .open = dmaengine_pcm_open, .close = snd_dmaengine_pcm_close, @@ -255,7 +294,7 @@ static const struct snd_pcm_ops dmaengine_pcm_ops = { .hw_params = dmaengine_pcm_hw_params, .hw_free = snd_pcm_lib_free_pages, .trigger = snd_dmaengine_pcm_trigger, - .pointer = snd_dmaengine_pcm_pointer, + .pointer = dmaengine_pcm_pointer, }; static const struct snd_soc_platform_driver dmaengine_pcm_platform = { @@ -265,23 +304,6 @@ static const struct snd_soc_platform_driver dmaengine_pcm_platform = { .probe_order = SND_SOC_COMP_ORDER_LATE, }; -static const struct snd_pcm_ops dmaengine_no_residue_pcm_ops = { - .open = dmaengine_pcm_open, - .close = snd_dmaengine_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = dmaengine_pcm_hw_params, - .hw_free = snd_pcm_lib_free_pages, - .trigger = snd_dmaengine_pcm_trigger, - .pointer = snd_dmaengine_pcm_pointer_no_residue, -}; - -static const struct snd_soc_platform_driver dmaengine_no_residue_pcm_platform = { - .ops = &dmaengine_no_residue_pcm_ops, - .pcm_new = dmaengine_pcm_new, - .pcm_free = dmaengine_pcm_free, - .probe_order = SND_SOC_COMP_ORDER_LATE, -}; - static const char * const dmaengine_pcm_dma_channel_names[] = { [SNDRV_PCM_STREAM_PLAYBACK] = "tx", [SNDRV_PCM_STREAM_CAPTURE] = "rx", @@ -374,12 +396,8 @@ int snd_dmaengine_pcm_register(struct device *dev, if (ret) goto err_free_dma; - if (flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE) - ret = snd_soc_add_platform(dev, &pcm->platform, - &dmaengine_no_residue_pcm_platform); - else - ret = snd_soc_add_platform(dev, &pcm->platform, - &dmaengine_pcm_platform); + ret = snd_soc_add_platform(dev, &pcm->platform, + &dmaengine_pcm_platform); if (ret) goto err_free_dma; |