Commit 0ff4e8c6 authored by S.j. Wang's avatar S.j. Wang Committed by Mark Brown

ASoC: fsl_esai: fix channel swap issue when stream starts

There is very low possibility ( < 0.1% ) that channel swap happened
in beginning when multi output/input pin is enabled. The issue is
that hardware can't send data to correct pin in the beginning with
the normal enable flow.

This is hardware issue, but there is no errata, the workaround flow
is that: Each time playback/recording, firstly clear the xSMA/xSMB,
then enable TE/RE, then enable xSMB and xSMA (xSMB must be enabled
before xSMA). Which is to use the xSMA as the trigger start register,
previously the xCR_TE or xCR_RE is the bit for starting.

Fixes commit 43d24e76 ("ASoC: fsl_esai: Add ESAI CPU DAI driver")
Cc: <stable@vger.kernel.org>
Reviewed-by: default avatarFabio Estevam <festevam@gmail.com>
Acked-by: default avatarNicolin Chen <nicoleotsuka@gmail.com>
Signed-off-by: default avatarShengjiu Wang <shengjiu.wang@nxp.com>
Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent 53f67a78
...@@ -54,6 +54,8 @@ struct fsl_esai { ...@@ -54,6 +54,8 @@ struct fsl_esai {
u32 fifo_depth; u32 fifo_depth;
u32 slot_width; u32 slot_width;
u32 slots; u32 slots;
u32 tx_mask;
u32 rx_mask;
u32 hck_rate[2]; u32 hck_rate[2];
u32 sck_rate[2]; u32 sck_rate[2];
bool hck_dir[2]; bool hck_dir[2];
...@@ -361,21 +363,13 @@ static int fsl_esai_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, ...@@ -361,21 +363,13 @@ static int fsl_esai_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask,
regmap_update_bits(esai_priv->regmap, REG_ESAI_TCCR, regmap_update_bits(esai_priv->regmap, REG_ESAI_TCCR,
ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(slots)); ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(slots));
regmap_update_bits(esai_priv->regmap, REG_ESAI_TSMA,
ESAI_xSMA_xS_MASK, ESAI_xSMA_xS(tx_mask));
regmap_update_bits(esai_priv->regmap, REG_ESAI_TSMB,
ESAI_xSMB_xS_MASK, ESAI_xSMB_xS(tx_mask));
regmap_update_bits(esai_priv->regmap, REG_ESAI_RCCR, regmap_update_bits(esai_priv->regmap, REG_ESAI_RCCR,
ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(slots)); ESAI_xCCR_xDC_MASK, ESAI_xCCR_xDC(slots));
regmap_update_bits(esai_priv->regmap, REG_ESAI_RSMA,
ESAI_xSMA_xS_MASK, ESAI_xSMA_xS(rx_mask));
regmap_update_bits(esai_priv->regmap, REG_ESAI_RSMB,
ESAI_xSMB_xS_MASK, ESAI_xSMB_xS(rx_mask));
esai_priv->slot_width = slot_width; esai_priv->slot_width = slot_width;
esai_priv->slots = slots; esai_priv->slots = slots;
esai_priv->tx_mask = tx_mask;
esai_priv->rx_mask = rx_mask;
return 0; return 0;
} }
...@@ -596,6 +590,7 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, ...@@ -596,6 +590,7 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd,
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
u8 i, channels = substream->runtime->channels; u8 i, channels = substream->runtime->channels;
u32 pins = DIV_ROUND_UP(channels, esai_priv->slots); u32 pins = DIV_ROUND_UP(channels, esai_priv->slots);
u32 mask;
switch (cmd) { switch (cmd) {
case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_START:
...@@ -608,15 +603,38 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, ...@@ -608,15 +603,38 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd,
for (i = 0; tx && i < channels; i++) for (i = 0; tx && i < channels; i++)
regmap_write(esai_priv->regmap, REG_ESAI_ETDR, 0x0); regmap_write(esai_priv->regmap, REG_ESAI_ETDR, 0x0);
/*
* When set the TE/RE in the end of enablement flow, there
* will be channel swap issue for multi data line case.
* In order to workaround this issue, we switch the bit
* enablement sequence to below sequence
* 1) clear the xSMB & xSMA: which is done in probe and
* stop state.
* 2) set TE/RE
* 3) set xSMB
* 4) set xSMA: xSMA is the last one in this flow, which
* will trigger esai to start.
*/
regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),
tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK, tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK,
tx ? ESAI_xCR_TE(pins) : ESAI_xCR_RE(pins)); tx ? ESAI_xCR_TE(pins) : ESAI_xCR_RE(pins));
mask = tx ? esai_priv->tx_mask : esai_priv->rx_mask;
regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMB(tx),
ESAI_xSMB_xS_MASK, ESAI_xSMB_xS(mask));
regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMA(tx),
ESAI_xSMA_xS_MASK, ESAI_xSMA_xS(mask));
break; break;
case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx),
tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK, 0); tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK, 0);
regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMA(tx),
ESAI_xSMA_xS_MASK, 0);
regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMB(tx),
ESAI_xSMB_xS_MASK, 0);
/* Disable and reset FIFO */ /* Disable and reset FIFO */
regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx),
...@@ -906,6 +924,15 @@ static int fsl_esai_probe(struct platform_device *pdev) ...@@ -906,6 +924,15 @@ static int fsl_esai_probe(struct platform_device *pdev)
return ret; return ret;
} }
esai_priv->tx_mask = 0xFFFFFFFF;
esai_priv->rx_mask = 0xFFFFFFFF;
/* Clear the TSMA, TSMB, RSMA, RSMB */
regmap_write(esai_priv->regmap, REG_ESAI_TSMA, 0);
regmap_write(esai_priv->regmap, REG_ESAI_TSMB, 0);
regmap_write(esai_priv->regmap, REG_ESAI_RSMA, 0);
regmap_write(esai_priv->regmap, REG_ESAI_RSMB, 0);
ret = devm_snd_soc_register_component(&pdev->dev, &fsl_esai_component, ret = devm_snd_soc_register_component(&pdev->dev, &fsl_esai_component,
&fsl_esai_dai, 1); &fsl_esai_dai, 1);
if (ret) { if (ret) {
......
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