From ca44520dbf4b494db4f3b38d498a179165dd431d Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Tue, 8 Sep 2020 17:24:18 +0800 Subject: MLK-24753-2: ASoC: fsl_esai_mix: get pointer from dma driver directly Previously update pointer in mix function according to dma callback, which is not accurate for the callback maybe triggerred very late, that cause the read pointer and write pointer point to same period, then there is noise in output. So get pointer directly from dma driver to avoid such issue. Signed-off-by: Shengjiu Wang Reviewed-by: Peng Zhang --- sound/soc/fsl/fsl_esai_mix.c | 78 ++++++++++++++++++++++++++++++++------------ 1 file changed, 57 insertions(+), 21 deletions(-) (limited to 'sound/soc/fsl/fsl_esai_mix.c') diff --git a/sound/soc/fsl/fsl_esai_mix.c b/sound/soc/fsl/fsl_esai_mix.c index f7dab64f108a..9b603f9a6e6f 100644 --- a/sound/soc/fsl/fsl_esai_mix.c +++ b/sound/soc/fsl/fsl_esai_mix.c @@ -71,6 +71,53 @@ int fsl_esai_mix_close(struct snd_pcm_substream *substream, return 0; } +static int fsl_esai_mix_pointer(struct fsl_esai_mix *mix) +{ + struct dma_tx_state state; + enum dma_status status; + unsigned int buf_size; + unsigned int pos = 0; + + status = dmaengine_tx_status(mix->chan, mix->cookie, &state); + if (status == DMA_IN_PROGRESS || status == DMA_PAUSED) { + buf_size = mix->buffer_bytes; + if (state.residue > 0 && state.residue <= buf_size) + pos = buf_size - state.residue; + } + + return pos; +} + +static int fsl_esai_tx_avail(struct fsl_esai_mix *mix) +{ + int avail; + + mix->buffer_read_offset = fsl_esai_mix_pointer(mix); + + avail = mix->buffer_bytes - mix->buffer_write_offset + mix->buffer_read_offset; + if (avail < 0) + avail += mix->buffer_bytes; + else if (avail > mix->buffer_bytes) + avail -= mix->buffer_bytes; + + return avail; +} + +static int fsl_esai_rx_avail(struct fsl_esai_mix *mix) +{ + int avail; + + mix->buffer_write_offset = fsl_esai_mix_pointer(mix); + + avail = mix->buffer_bytes - mix->buffer_read_offset + mix->buffer_write_offset; + if (avail < 0) + avail += mix->buffer_bytes; + else if (avail > mix->buffer_bytes) + avail = avail - mix->buffer_bytes; + + return avail; +} + static void fsl_esai_mix_buffer_from_fe_tx(struct snd_pcm_substream *substream, bool elapse) { bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; @@ -115,9 +162,9 @@ static void fsl_esai_mix_buffer_from_fe_tx(struct snd_pcm_substream *substream, } spin_unlock_irqrestore(&rtd->card->dpcm_lock, flags); - avail = mix->buffer_bytes - mix->buffer_write_offset + mix->buffer_read_offset; - if (avail >= mix->buffer_bytes) - avail = avail - mix->buffer_bytes; + avail = fsl_esai_tx_avail(mix); + if (avail >= mix->buffer_bytes && elapse) + dev_err(rtd->cpu_dai->dev, "mix underrun\n"); while (avail >= mix->period_bytes) { size = mix->period_bytes; @@ -166,7 +213,7 @@ static void fsl_esai_mix_buffer_from_fe_tx(struct snd_pcm_substream *substream, } mix->buffer_write_offset += size; - if (mix->buffer_write_offset > mix->buffer_bytes) + if (mix->buffer_write_offset >= mix->buffer_bytes) mix->buffer_write_offset -= mix->buffer_bytes; avail -= mix->period_bytes; @@ -216,9 +263,9 @@ static void fsl_esai_split_buffer_from_be_rx(struct snd_pcm_substream *substream } spin_unlock_irqrestore(&rtd->card->dpcm_lock, flags); - avail = mix->buffer_bytes - mix->buffer_read_offset + mix->buffer_write_offset; - if (avail >= mix->buffer_bytes) - avail = avail - mix->buffer_bytes; + avail = fsl_esai_rx_avail(mix); + if (avail >= mix->buffer_bytes && elapse) + dev_err(rtd->cpu_dai->dev, "mix overrun\n"); while (avail >= mix->period_bytes) { size = mix->period_bytes; @@ -261,7 +308,7 @@ static void fsl_esai_split_buffer_from_be_rx(struct snd_pcm_substream *substream } mix->buffer_read_offset += size; - if (mix->buffer_read_offset > mix->buffer_bytes) + if (mix->buffer_read_offset >= mix->buffer_bytes) mix->buffer_read_offset -= mix->buffer_bytes; avail -= mix->period_bytes; @@ -296,16 +343,6 @@ static void fsl_esai_mix_dma_complete(void *arg) mix->substream = substream; - if (tx) { - mix->buffer_read_offset = mix->buffer_read_offset + mix->period_bytes; - if (mix->buffer_read_offset > mix->buffer_bytes) - mix->buffer_read_offset -= mix->buffer_bytes; - } else { - mix->buffer_write_offset = mix->buffer_write_offset + mix->period_bytes; - if (mix->buffer_write_offset > mix->buffer_bytes) - mix->buffer_write_offset -= mix->buffer_bytes; - } - queue_work(mix->mix_wq, &mix->work); } @@ -329,11 +366,11 @@ static int fsl_esai_mix_prepare_and_submit(struct snd_pcm_substream *substream, desc->callback = fsl_esai_mix_dma_complete; desc->callback_param = substream; - dmaengine_submit(desc); + mix->cookie = dmaengine_submit(desc); /* Mix the tx buffer */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - mix->buffer_read_offset = mix->buffer_bytes; + mix->buffer_read_offset = 0; mix->buffer_write_offset = 0; fsl_esai_mix_buffer_from_fe_tx(substream, false); } else { @@ -413,7 +450,6 @@ int fsl_esai_mix_probe(struct device *dev, struct fsl_esai_mix *mix_rx, struct f mix_rx->channels = 4; mix_rx->buffer_bytes = 2048 * mix_rx->client_cnt * 4; mix_rx->period_bytes = 2048 * mix_rx->client_cnt; - ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, IMX_SSI_DMABUF_SIZE * mix_rx->client_cnt, -- cgit v1.2.3