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>
This commit is contained in:
Shengjiu Wang 2020-09-08 17:24:18 +08:00
parent a38e0a4edd
commit ca44520dbf
2 changed files with 58 additions and 21 deletions

View File

@ -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,

View File

@ -31,6 +31,7 @@ struct fsl_esai_mix {
struct workqueue_struct *mix_wq;
struct work_struct work;
struct snd_pcm_substream *substream;
dma_cookie_t cookie;
u32 buffer_read_offset;
u32 buffer_write_offset;
u32 buffer_bytes;