Commit 7cfa7b54 authored by Mark Brown's avatar Mark Brown

Merge remote-tracking branch 'asoc/topic/dma' into for-tiwai

parents 99896f71 d70e861a
...@@ -2884,6 +2884,7 @@ static int pl330_dma_device_slave_caps(struct dma_chan *dchan, ...@@ -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->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
caps->cmd_pause = false; caps->cmd_pause = false;
caps->cmd_terminate = true; caps->cmd_terminate = true;
caps->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
return 0; return 0;
} }
......
...@@ -364,6 +364,32 @@ struct dma_slave_config { ...@@ -364,6 +364,32 @@ struct dma_slave_config {
unsigned int slave_id; 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 /* struct dma_slave_caps - expose capabilities of a slave channel only
* *
* @src_addr_widths: bit mask of src addr widths the channel supports * @src_addr_widths: bit mask of src addr widths the channel supports
...@@ -374,6 +400,7 @@ struct dma_slave_config { ...@@ -374,6 +400,7 @@ struct dma_slave_config {
* should be checked by controller as well * should be checked by controller as well
* @cmd_pause: true, if pause and thereby resume is supported * @cmd_pause: true, if pause and thereby resume is supported
* @cmd_terminate: true, if terminate cmd is supported * @cmd_terminate: true, if terminate cmd is supported
* @residue_granularity: granularity of the reported transfer residue
*/ */
struct dma_slave_caps { struct dma_slave_caps {
u32 src_addr_widths; u32 src_addr_widths;
...@@ -381,6 +408,7 @@ struct dma_slave_caps { ...@@ -381,6 +408,7 @@ struct dma_slave_caps {
u32 directions; u32 directions;
bool cmd_pause; bool cmd_pause;
bool cmd_terminate; bool cmd_terminate;
enum dma_residue_granularity residue_granularity;
}; };
static inline const char *dma_chan_name(struct dma_chan *chan) static inline const char *dma_chan_name(struct dma_chan *chan)
......
...@@ -236,8 +236,7 @@ static int axi_i2s_probe(struct platform_device *pdev) ...@@ -236,8 +236,7 @@ static int axi_i2s_probe(struct platform_device *pdev)
if (ret) if (ret)
goto err_clk_disable; goto err_clk_disable;
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
SND_DMAENGINE_PCM_FLAG_NO_RESIDUE);
if (ret) if (ret)
goto err_clk_disable; goto err_clk_disable;
......
...@@ -229,8 +229,7 @@ static int axi_spdif_probe(struct platform_device *pdev) ...@@ -229,8 +229,7 @@ static int axi_spdif_probe(struct platform_device *pdev)
if (ret) if (ret)
goto err_clk_disable; goto err_clk_disable;
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
SND_DMAENGINE_PCM_FLAG_NO_RESIDUE);
if (ret) if (ret)
goto err_clk_disable; goto err_clk_disable;
......
...@@ -68,7 +68,6 @@ int samsung_asoc_dma_platform_register(struct device *dev) ...@@ -68,7 +68,6 @@ int samsung_asoc_dma_platform_register(struct device *dev)
{ {
return snd_dmaengine_pcm_register(dev, &samsung_dmaengine_pcm_config, return snd_dmaengine_pcm_register(dev, &samsung_dmaengine_pcm_config,
SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME | SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME |
SND_DMAENGINE_PCM_FLAG_NO_RESIDUE |
SND_DMAENGINE_PCM_FLAG_COMPAT); SND_DMAENGINE_PCM_FLAG_COMPAT);
} }
EXPORT_SYMBOL_GPL(samsung_asoc_dma_platform_register); EXPORT_SYMBOL_GPL(samsung_asoc_dma_platform_register);
......
...@@ -144,6 +144,8 @@ static int dmaengine_pcm_set_runtime_hwparams(struct snd_pcm_substream *substrea ...@@ -144,6 +144,8 @@ static int dmaengine_pcm_set_runtime_hwparams(struct snd_pcm_substream *substrea
if (ret == 0) { if (ret == 0) {
if (dma_caps.cmd_pause) if (dma_caps.cmd_pause)
hw.info |= SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME; 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); return snd_soc_set_runtime_hwparams(substream, &hw);
...@@ -187,6 +189,21 @@ static struct dma_chan *dmaengine_pcm_compat_request_channel( ...@@ -187,6 +189,21 @@ static struct dma_chan *dmaengine_pcm_compat_request_channel(
dma_data->filter_data); 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) static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd)
{ {
struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform); 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,6 +256,16 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd)
max_buffer_size); max_buffer_size);
if (ret) if (ret)
goto err_free; 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; return 0;
...@@ -248,6 +275,18 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd) ...@@ -248,6 +275,18 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd)
return ret; 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 = { static const struct snd_pcm_ops dmaengine_pcm_ops = {
.open = dmaengine_pcm_open, .open = dmaengine_pcm_open,
.close = snd_dmaengine_pcm_close, .close = snd_dmaengine_pcm_close,
...@@ -255,7 +294,7 @@ static const struct snd_pcm_ops dmaengine_pcm_ops = { ...@@ -255,7 +294,7 @@ static const struct snd_pcm_ops dmaengine_pcm_ops = {
.hw_params = dmaengine_pcm_hw_params, .hw_params = dmaengine_pcm_hw_params,
.hw_free = snd_pcm_lib_free_pages, .hw_free = snd_pcm_lib_free_pages,
.trigger = snd_dmaengine_pcm_trigger, .trigger = snd_dmaengine_pcm_trigger,
.pointer = snd_dmaengine_pcm_pointer, .pointer = dmaengine_pcm_pointer,
}; };
static const struct snd_soc_platform_driver dmaengine_pcm_platform = { 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,23 +304,6 @@ static const struct snd_soc_platform_driver dmaengine_pcm_platform = {
.probe_order = SND_SOC_COMP_ORDER_LATE, .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[] = { static const char * const dmaengine_pcm_dma_channel_names[] = {
[SNDRV_PCM_STREAM_PLAYBACK] = "tx", [SNDRV_PCM_STREAM_PLAYBACK] = "tx",
[SNDRV_PCM_STREAM_CAPTURE] = "rx", [SNDRV_PCM_STREAM_CAPTURE] = "rx",
...@@ -374,12 +396,8 @@ int snd_dmaengine_pcm_register(struct device *dev, ...@@ -374,12 +396,8 @@ int snd_dmaengine_pcm_register(struct device *dev,
if (ret) if (ret)
goto err_free_dma; goto err_free_dma;
if (flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE) ret = snd_soc_add_platform(dev, &pcm->platform,
ret = snd_soc_add_platform(dev, &pcm->platform, &dmaengine_pcm_platform);
&dmaengine_no_residue_pcm_platform);
else
ret = snd_soc_add_platform(dev, &pcm->platform,
&dmaengine_pcm_platform);
if (ret) if (ret)
goto err_free_dma; goto err_free_dma;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment