summaryrefslogtreecommitdiff
path: root/sound/soc/fsl/fsl_esai_mix.c
diff options
context:
space:
mode:
authorShengjiu Wang <shengjiu.wang@nxp.com>2020-09-08 17:24:18 +0800
committerShengjiu Wang <shengjiu.wang@nxp.com>2020-09-10 13:47:52 +0800
commitca44520dbf4b494db4f3b38d498a179165dd431d (patch)
tree6817d1775a5263a875fe6b3c267403c0261e191d /sound/soc/fsl/fsl_esai_mix.c
parenta38e0a4eddab3196317501081ccc29276c6c3906 (diff)
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 <shengjiu.wang@nxp.com> Reviewed-by: Peng Zhang <peng.zhang_8@nxp.com>
Diffstat (limited to 'sound/soc/fsl/fsl_esai_mix.c')
-rw-r--r--sound/soc/fsl/fsl_esai_mix.c78
1 files changed, 57 insertions, 21 deletions
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,