* [PATCH V4 0/2] recover the channel swap after xrun @ 2019-07-11 10:49 shengjiu.wang 2019-07-11 10:49 ` [PATCH V4 1/2] ASoC: fsl_esai: Wrap some operations to be functions shengjiu.wang 2019-07-11 10:49 ` [PATCH V4 2/2] ASoC: fsl_esai: recover the channel swap after xrun shengjiu.wang 0 siblings, 2 replies; 6+ messages in thread From: shengjiu.wang @ 2019-07-11 10:49 UTC (permalink / raw) To: timur, nicoleotsuka, Xiubo.Lee, festevam, lgirdwood, perex, tiwai, broonie, alsa-devel Cc: linuxppc-dev, linux-kernel From: Shengjiu Wang <shengjiu.wang@nxp.com> recover the channel swap after xrun Shengjiu Wang (2): ASoC: fsl_esai: Wrap some operations to be functions ASoC: fsl_esai: recover the channel swap after xrun sound/soc/fsl/fsl_esai.c | 266 ++++++++++++++++++++++++++++----------- 1 file changed, 193 insertions(+), 73 deletions(-) Changes in v4 -add more comments and remove read saisr Changes in V3 - update code sytle. Changes in v2 - add one patch for wrap operations to functions. - fix some coding style issue -- 2.21.0 ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH V4 1/2] ASoC: fsl_esai: Wrap some operations to be functions 2019-07-11 10:49 [PATCH V4 0/2] recover the channel swap after xrun shengjiu.wang @ 2019-07-11 10:49 ` shengjiu.wang 2019-07-22 12:22 ` Applied "ASoC: fsl_esai: Wrap some operations to be functions" to the asoc tree Mark Brown 2019-07-11 10:49 ` [PATCH V4 2/2] ASoC: fsl_esai: recover the channel swap after xrun shengjiu.wang 1 sibling, 1 reply; 6+ messages in thread From: shengjiu.wang @ 2019-07-11 10:49 UTC (permalink / raw) To: timur, nicoleotsuka, Xiubo.Lee, festevam, lgirdwood, perex, tiwai, broonie, alsa-devel Cc: linuxppc-dev, linux-kernel From: Shengjiu Wang <shengjiu.wang@nxp.com> Extract the operation to be functions, to improve the readability. In this patch, fsl_esai_hw_init, fsl_esai_register_restore, fsl_esai_trigger_start and fsl_esai_trigger_stop are extracted. Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com> Acked-by: Nicolin Chen <nicoleotsuka@gmail.com> --- sound/soc/fsl/fsl_esai.c | 192 ++++++++++++++++++++++++--------------- 1 file changed, 119 insertions(+), 73 deletions(-) diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index 10d2210c91ef..ab460d6d7432 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -35,6 +35,7 @@ * @fifo_depth: depth of tx/rx FIFO * @slot_width: width of each DAI slot * @slots: number of slots + * @channels: channel num for tx or rx * @hck_rate: clock rate of desired HCKx clock * @sck_rate: clock rate of desired SCKx clock * @hck_dir: the direction of HCKx pads @@ -57,6 +58,7 @@ struct fsl_esai { u32 slots; u32 tx_mask; u32 rx_mask; + u32 channels[2]; u32 hck_rate[2]; u32 sck_rate[2]; bool hck_dir[2]; @@ -543,64 +545,132 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream, return 0; } -static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, - struct snd_soc_dai *dai) +static int fsl_esai_hw_init(struct fsl_esai *esai_priv) { - struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); - bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; - u8 i, channels = substream->runtime->channels; + struct platform_device *pdev = esai_priv->pdev; + int ret; + + /* Reset ESAI unit */ + ret = regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR, + ESAI_ECR_ESAIEN_MASK | ESAI_ECR_ERST_MASK, + ESAI_ECR_ESAIEN | ESAI_ECR_ERST); + if (ret) { + dev_err(&pdev->dev, "failed to reset ESAI: %d\n", ret); + return ret; + } + + /* + * We need to enable ESAI so as to access some of its registers. + * Otherwise, we would fail to dump regmap from user space. + */ + ret = regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR, + ESAI_ECR_ESAIEN_MASK | ESAI_ECR_ERST_MASK, + ESAI_ECR_ESAIEN); + if (ret) { + dev_err(&pdev->dev, "failed to enable ESAI: %d\n", ret); + return ret; + } + + regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC, + ESAI_PRRC_PDC_MASK, 0); + regmap_update_bits(esai_priv->regmap, REG_ESAI_PCRC, + ESAI_PCRC_PC_MASK, 0); + + return 0; +} + +static int fsl_esai_register_restore(struct fsl_esai *esai_priv) +{ + int ret; + + /* FIFO reset for safety */ + regmap_update_bits(esai_priv->regmap, REG_ESAI_TFCR, + ESAI_xFCR_xFR, ESAI_xFCR_xFR); + regmap_update_bits(esai_priv->regmap, REG_ESAI_RFCR, + ESAI_xFCR_xFR, ESAI_xFCR_xFR); + + regcache_mark_dirty(esai_priv->regmap); + ret = regcache_sync(esai_priv->regmap); + if (ret) + return ret; + + /* FIFO reset done */ + regmap_update_bits(esai_priv->regmap, REG_ESAI_TFCR, ESAI_xFCR_xFR, 0); + regmap_update_bits(esai_priv->regmap, REG_ESAI_RFCR, ESAI_xFCR_xFR, 0); + + return 0; +} + +static void fsl_esai_trigger_start(struct fsl_esai *esai_priv, bool tx) +{ + u8 i, channels = esai_priv->channels[tx]; u32 pins = DIV_ROUND_UP(channels, esai_priv->slots); u32 mask; + regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), + ESAI_xFCR_xFEN_MASK, ESAI_xFCR_xFEN); + + /* Write initial words reqiured by ESAI as normal procedure */ + for (i = 0; tx && i < channels; i++) + 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), + tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK, + 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)); +} + +static void fsl_esai_trigger_stop(struct fsl_esai *esai_priv, bool tx) +{ + regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), + 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 */ + regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), + ESAI_xFCR_xFR | ESAI_xFCR_xFEN, ESAI_xFCR_xFR); + regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), + ESAI_xFCR_xFR, 0); +} + +static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + + esai_priv->channels[tx] = substream->runtime->channels; + switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), - ESAI_xFCR_xFEN_MASK, ESAI_xFCR_xFEN); - - /* Write initial words reqiured by ESAI as normal procedure */ - for (i = 0; tx && i < channels; i++) - 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), - tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK, - 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)); - + fsl_esai_trigger_start(esai_priv, tx); break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), - 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 */ - regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), - ESAI_xFCR_xFR | ESAI_xFCR_xFEN, ESAI_xFCR_xFR); - regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), - ESAI_xFCR_xFR, 0); + fsl_esai_trigger_stop(esai_priv, tx); break; default: return -EINVAL; @@ -866,22 +936,9 @@ static int fsl_esai_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, esai_priv); - /* Reset ESAI unit */ - ret = regmap_write(esai_priv->regmap, REG_ESAI_ECR, ESAI_ECR_ERST); - if (ret) { - dev_err(&pdev->dev, "failed to reset ESAI: %d\n", ret); + ret = fsl_esai_hw_init(esai_priv); + if (ret) return ret; - } - - /* - * We need to enable ESAI so as to access some of its registers. - * Otherwise, we would fail to dump regmap from user space. - */ - ret = regmap_write(esai_priv->regmap, REG_ESAI_ECR, ESAI_ECR_ESAIEN); - if (ret) { - dev_err(&pdev->dev, "failed to enable ESAI: %d\n", ret); - return ret; - } esai_priv->tx_mask = 0xFFFFFFFF; esai_priv->rx_mask = 0xFFFFFFFF; @@ -955,20 +1012,10 @@ static int fsl_esai_runtime_resume(struct device *dev) regcache_cache_only(esai->regmap, false); - /* FIFO reset for safety */ - regmap_update_bits(esai->regmap, REG_ESAI_TFCR, - ESAI_xFCR_xFR, ESAI_xFCR_xFR); - regmap_update_bits(esai->regmap, REG_ESAI_RFCR, - ESAI_xFCR_xFR, ESAI_xFCR_xFR); - - ret = regcache_sync(esai->regmap); + ret = fsl_esai_register_restore(esai); if (ret) goto err_regcache_sync; - /* FIFO reset done */ - regmap_update_bits(esai->regmap, REG_ESAI_TFCR, ESAI_xFCR_xFR, 0); - regmap_update_bits(esai->regmap, REG_ESAI_RFCR, ESAI_xFCR_xFR, 0); - return 0; err_regcache_sync: @@ -991,7 +1038,6 @@ static int fsl_esai_runtime_suspend(struct device *dev) struct fsl_esai *esai = dev_get_drvdata(dev); regcache_cache_only(esai->regmap, true); - regcache_mark_dirty(esai->regmap); if (!IS_ERR(esai->fsysclk)) clk_disable_unprepare(esai->fsysclk); -- 2.21.0 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Applied "ASoC: fsl_esai: Wrap some operations to be functions" to the asoc tree 2019-07-11 10:49 ` [PATCH V4 1/2] ASoC: fsl_esai: Wrap some operations to be functions shengjiu.wang @ 2019-07-22 12:22 ` Mark Brown 0 siblings, 0 replies; 6+ messages in thread From: Mark Brown @ 2019-07-22 12:22 UTC (permalink / raw) To: Shengjiu Wang Cc: alsa-devel, broonie, festevam, lgirdwood, linux-kernel, linuxppc-dev, Mark Brown, nicoleotsuka, Nicolin Chen, perex, timur, tiwai, Xiubo.Lee The patch ASoC: fsl_esai: Wrap some operations to be functions has been applied to the asoc tree at https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-5.4 All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted. You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed. If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced. Please add any relevant lists and maintainers to the CCs when replying to this mail. Thanks, Mark From 5be6155b50bbf7083b4bfa219e4ce6d1491f42f0 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang <shengjiu.wang@nxp.com> Date: Thu, 11 Jul 2019 18:49:45 +0800 Subject: [PATCH] ASoC: fsl_esai: Wrap some operations to be functions Extract the operation to be functions, to improve the readability. In this patch, fsl_esai_hw_init, fsl_esai_register_restore, fsl_esai_trigger_start and fsl_esai_trigger_stop are extracted. Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com> Acked-by: Nicolin Chen <nicoleotsuka@gmail.com> Link: https://lore.kernel.org/r/804d7e75ae7e06a913479912b578b3538ca7cd3f.1562842206.git.shengjiu.wang@nxp.com Signed-off-by: Mark Brown <broonie@kernel.org> --- sound/soc/fsl/fsl_esai.c | 188 ++++++++++++++++++++++++--------------- 1 file changed, 117 insertions(+), 71 deletions(-) diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index 10d2210c91ef..ab460d6d7432 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -35,6 +35,7 @@ * @fifo_depth: depth of tx/rx FIFO * @slot_width: width of each DAI slot * @slots: number of slots + * @channels: channel num for tx or rx * @hck_rate: clock rate of desired HCKx clock * @sck_rate: clock rate of desired SCKx clock * @hck_dir: the direction of HCKx pads @@ -57,6 +58,7 @@ struct fsl_esai { u32 slots; u32 tx_mask; u32 rx_mask; + u32 channels[2]; u32 hck_rate[2]; u32 sck_rate[2]; bool hck_dir[2]; @@ -543,64 +545,132 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream, return 0; } +static int fsl_esai_hw_init(struct fsl_esai *esai_priv) +{ + struct platform_device *pdev = esai_priv->pdev; + int ret; + + /* Reset ESAI unit */ + ret = regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR, + ESAI_ECR_ESAIEN_MASK | ESAI_ECR_ERST_MASK, + ESAI_ECR_ESAIEN | ESAI_ECR_ERST); + if (ret) { + dev_err(&pdev->dev, "failed to reset ESAI: %d\n", ret); + return ret; + } + + /* + * We need to enable ESAI so as to access some of its registers. + * Otherwise, we would fail to dump regmap from user space. + */ + ret = regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR, + ESAI_ECR_ESAIEN_MASK | ESAI_ECR_ERST_MASK, + ESAI_ECR_ESAIEN); + if (ret) { + dev_err(&pdev->dev, "failed to enable ESAI: %d\n", ret); + return ret; + } + + regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC, + ESAI_PRRC_PDC_MASK, 0); + regmap_update_bits(esai_priv->regmap, REG_ESAI_PCRC, + ESAI_PCRC_PC_MASK, 0); + + return 0; +} + +static int fsl_esai_register_restore(struct fsl_esai *esai_priv) +{ + int ret; + + /* FIFO reset for safety */ + regmap_update_bits(esai_priv->regmap, REG_ESAI_TFCR, + ESAI_xFCR_xFR, ESAI_xFCR_xFR); + regmap_update_bits(esai_priv->regmap, REG_ESAI_RFCR, + ESAI_xFCR_xFR, ESAI_xFCR_xFR); + + regcache_mark_dirty(esai_priv->regmap); + ret = regcache_sync(esai_priv->regmap); + if (ret) + return ret; + + /* FIFO reset done */ + regmap_update_bits(esai_priv->regmap, REG_ESAI_TFCR, ESAI_xFCR_xFR, 0); + regmap_update_bits(esai_priv->regmap, REG_ESAI_RFCR, ESAI_xFCR_xFR, 0); + + return 0; +} + +static void fsl_esai_trigger_start(struct fsl_esai *esai_priv, bool tx) +{ + u8 i, channels = esai_priv->channels[tx]; + u32 pins = DIV_ROUND_UP(channels, esai_priv->slots); + u32 mask; + + regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), + ESAI_xFCR_xFEN_MASK, ESAI_xFCR_xFEN); + + /* Write initial words reqiured by ESAI as normal procedure */ + for (i = 0; tx && i < channels; i++) + 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), + tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK, + 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)); +} + +static void fsl_esai_trigger_stop(struct fsl_esai *esai_priv, bool tx) +{ + regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), + 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 */ + regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), + ESAI_xFCR_xFR | ESAI_xFCR_xFEN, ESAI_xFCR_xFR); + regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), + ESAI_xFCR_xFR, 0); +} + static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; - u8 i, channels = substream->runtime->channels; - u32 pins = DIV_ROUND_UP(channels, esai_priv->slots); - u32 mask; + + esai_priv->channels[tx] = substream->runtime->channels; switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), - ESAI_xFCR_xFEN_MASK, ESAI_xFCR_xFEN); - - /* Write initial words reqiured by ESAI as normal procedure */ - for (i = 0; tx && i < channels; i++) - 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), - tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK, - 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)); - + fsl_esai_trigger_start(esai_priv, tx); break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), - 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 */ - regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), - ESAI_xFCR_xFR | ESAI_xFCR_xFEN, ESAI_xFCR_xFR); - regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), - ESAI_xFCR_xFR, 0); + fsl_esai_trigger_stop(esai_priv, tx); break; default: return -EINVAL; @@ -866,22 +936,9 @@ static int fsl_esai_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, esai_priv); - /* Reset ESAI unit */ - ret = regmap_write(esai_priv->regmap, REG_ESAI_ECR, ESAI_ECR_ERST); - if (ret) { - dev_err(&pdev->dev, "failed to reset ESAI: %d\n", ret); - return ret; - } - - /* - * We need to enable ESAI so as to access some of its registers. - * Otherwise, we would fail to dump regmap from user space. - */ - ret = regmap_write(esai_priv->regmap, REG_ESAI_ECR, ESAI_ECR_ESAIEN); - if (ret) { - dev_err(&pdev->dev, "failed to enable ESAI: %d\n", ret); + ret = fsl_esai_hw_init(esai_priv); + if (ret) return ret; - } esai_priv->tx_mask = 0xFFFFFFFF; esai_priv->rx_mask = 0xFFFFFFFF; @@ -955,20 +1012,10 @@ static int fsl_esai_runtime_resume(struct device *dev) regcache_cache_only(esai->regmap, false); - /* FIFO reset for safety */ - regmap_update_bits(esai->regmap, REG_ESAI_TFCR, - ESAI_xFCR_xFR, ESAI_xFCR_xFR); - regmap_update_bits(esai->regmap, REG_ESAI_RFCR, - ESAI_xFCR_xFR, ESAI_xFCR_xFR); - - ret = regcache_sync(esai->regmap); + ret = fsl_esai_register_restore(esai); if (ret) goto err_regcache_sync; - /* FIFO reset done */ - regmap_update_bits(esai->regmap, REG_ESAI_TFCR, ESAI_xFCR_xFR, 0); - regmap_update_bits(esai->regmap, REG_ESAI_RFCR, ESAI_xFCR_xFR, 0); - return 0; err_regcache_sync: @@ -991,7 +1038,6 @@ static int fsl_esai_runtime_suspend(struct device *dev) struct fsl_esai *esai = dev_get_drvdata(dev); regcache_cache_only(esai->regmap, true); - regcache_mark_dirty(esai->regmap); if (!IS_ERR(esai->fsysclk)) clk_disable_unprepare(esai->fsysclk); -- 2.20.1 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH V4 2/2] ASoC: fsl_esai: recover the channel swap after xrun 2019-07-11 10:49 [PATCH V4 0/2] recover the channel swap after xrun shengjiu.wang 2019-07-11 10:49 ` [PATCH V4 1/2] ASoC: fsl_esai: Wrap some operations to be functions shengjiu.wang @ 2019-07-11 10:49 ` shengjiu.wang 2019-07-11 16:54 ` Nicolin Chen 2019-07-23 11:29 ` Applied "ASoC: fsl_esai: recover the channel swap after xrun" to the asoc tree Mark Brown 1 sibling, 2 replies; 6+ messages in thread From: shengjiu.wang @ 2019-07-11 10:49 UTC (permalink / raw) To: timur, nicoleotsuka, Xiubo.Lee, festevam, lgirdwood, perex, tiwai, broonie, alsa-devel Cc: linuxppc-dev, linux-kernel From: Shengjiu Wang <shengjiu.wang@nxp.com> There is chip errata ERR008000, the reference doc is (https://www.nxp.com/docs/en/errata/IMX6DQCE.pdf), The issue is "While using ESAI transmit or receive and an underrun/overrun happens, channel swap may occur. The only recovery mechanism is to reset the ESAI." This issue exist in imx3/imx5/imx6(partial) series. In this commit add a tasklet to handle reset of ESAI after xrun happens to recover the channel swap. Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com> --- sound/soc/fsl/fsl_esai.c | 74 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index ab460d6d7432..4ce8ac769244 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -32,6 +32,7 @@ * @extalclk: esai clock source to derive HCK, SCK and FS * @fsysclk: system clock source to derive HCK, SCK and FS * @spbaclk: SPBA clock (optional, depending on SoC design) + * @task: tasklet to handle the reset operation * @fifo_depth: depth of tx/rx FIFO * @slot_width: width of each DAI slot * @slots: number of slots @@ -42,6 +43,7 @@ * @sck_div: if using PSR/PM dividers for SCKx clock * @slave_mode: if fully using DAI slave mode * @synchronous: if using tx/rx synchronous mode + * @reset_at_xrun: flags for enable reset operaton * @name: driver name */ struct fsl_esai { @@ -53,6 +55,7 @@ struct fsl_esai { struct clk *extalclk; struct clk *fsysclk; struct clk *spbaclk; + struct tasklet_struct task; u32 fifo_depth; u32 slot_width; u32 slots; @@ -65,6 +68,7 @@ struct fsl_esai { bool sck_div[2]; bool slave_mode; bool synchronous; + bool reset_at_xrun; char name[32]; }; @@ -73,8 +77,16 @@ static irqreturn_t esai_isr(int irq, void *devid) struct fsl_esai *esai_priv = (struct fsl_esai *)devid; struct platform_device *pdev = esai_priv->pdev; u32 esr; + u32 saisr; regmap_read(esai_priv->regmap, REG_ESAI_ESR, &esr); + regmap_read(esai_priv->regmap, REG_ESAI_SAISR, &saisr); + + if ((saisr & (ESAI_SAISR_TUE | ESAI_SAISR_ROE)) && + esai_priv->reset_at_xrun) { + dev_dbg(&pdev->dev, "reset module for xrun\n"); + tasklet_schedule(&esai_priv->task); + } if (esr & ESAI_ESR_TINIT_MASK) dev_dbg(&pdev->dev, "isr: Transmission Initialized\n"); @@ -635,10 +647,17 @@ static void fsl_esai_trigger_start(struct fsl_esai *esai_priv, bool 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)); + + /* Enable Exception interrupt */ + regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), + ESAI_xCR_xEIE_MASK, ESAI_xCR_xEIE); } static void fsl_esai_trigger_stop(struct fsl_esai *esai_priv, bool tx) { + regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), + ESAI_xCR_xEIE_MASK, 0); + regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK, 0); regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMA(tx), @@ -653,6 +672,51 @@ static void fsl_esai_trigger_stop(struct fsl_esai *esai_priv, bool tx) ESAI_xFCR_xFR, 0); } +static void fsl_esai_hw_reset(unsigned long arg) +{ + struct fsl_esai *esai_priv = (struct fsl_esai *)arg; + bool tx = true, rx = false, enabled[2]; + u32 tfcr, rfcr; + + /* Save the registers */ + regmap_read(esai_priv->regmap, REG_ESAI_TFCR, &tfcr); + regmap_read(esai_priv->regmap, REG_ESAI_RFCR, &rfcr); + enabled[tx] = tfcr & ESAI_xFCR_xFEN; + enabled[rx] = rfcr & ESAI_xFCR_xFEN; + + /* Stop the tx & rx */ + fsl_esai_trigger_stop(esai_priv, tx); + fsl_esai_trigger_stop(esai_priv, rx); + + /* Reset the esai, and ignore return value */ + fsl_esai_hw_init(esai_priv); + + /* Enforce ESAI personal resets for both TX and RX */ + regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR, + ESAI_xCR_xPR_MASK, ESAI_xCR_xPR); + regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR, + ESAI_xCR_xPR_MASK, ESAI_xCR_xPR); + + /* Restore registers by regcache_sync, and ignore return value */ + fsl_esai_register_restore(esai_priv); + + /* Remove ESAI personal resets by configuring PCRC and PRRC also */ + regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR, + ESAI_xCR_xPR_MASK, 0); + regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR, + ESAI_xCR_xPR_MASK, 0); + regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC, + ESAI_PRRC_PDC_MASK, ESAI_PRRC_PDC(ESAI_GPIO)); + regmap_update_bits(esai_priv->regmap, REG_ESAI_PCRC, + ESAI_PCRC_PC_MASK, ESAI_PCRC_PC(ESAI_GPIO)); + + /* Restart tx / rx, if they already enabled */ + if (enabled[tx]) + fsl_esai_trigger_start(esai_priv, tx); + if (enabled[rx]) + fsl_esai_trigger_start(esai_priv, rx); +} + static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { @@ -857,6 +921,10 @@ static int fsl_esai_probe(struct platform_device *pdev) esai_priv->pdev = pdev; snprintf(esai_priv->name, sizeof(esai_priv->name), "%pOFn", np); + if (of_device_is_compatible(np, "fsl,vf610-esai") || + of_device_is_compatible(np, "fsl,imx35-esai")) + esai_priv->reset_at_xrun = true; + /* Get the addresses and IRQ */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); regs = devm_ioremap_resource(&pdev->dev, res); @@ -956,6 +1024,9 @@ static int fsl_esai_probe(struct platform_device *pdev) return ret; } + tasklet_init(&esai_priv->task, fsl_esai_hw_reset, + (unsigned long)esai_priv); + pm_runtime_enable(&pdev->dev); regcache_cache_only(esai_priv->regmap, true); @@ -969,7 +1040,10 @@ static int fsl_esai_probe(struct platform_device *pdev) static int fsl_esai_remove(struct platform_device *pdev) { + struct fsl_esai *esai_priv = platform_get_drvdata(pdev); + pm_runtime_disable(&pdev->dev); + tasklet_kill(&esai_priv->task); return 0; } -- 2.21.0 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH V4 2/2] ASoC: fsl_esai: recover the channel swap after xrun 2019-07-11 10:49 ` [PATCH V4 2/2] ASoC: fsl_esai: recover the channel swap after xrun shengjiu.wang @ 2019-07-11 16:54 ` Nicolin Chen 2019-07-23 11:29 ` Applied "ASoC: fsl_esai: recover the channel swap after xrun" to the asoc tree Mark Brown 1 sibling, 0 replies; 6+ messages in thread From: Nicolin Chen @ 2019-07-11 16:54 UTC (permalink / raw) To: shengjiu.wang Cc: timur, Xiubo.Lee, festevam, lgirdwood, perex, tiwai, broonie, alsa-devel, linuxppc-dev, linux-kernel On Thu, Jul 11, 2019 at 06:49:46PM +0800, shengjiu.wang@nxp.com wrote: > From: Shengjiu Wang <shengjiu.wang@nxp.com> > > There is chip errata ERR008000, the reference doc is > (https://www.nxp.com/docs/en/errata/IMX6DQCE.pdf), > > The issue is "While using ESAI transmit or receive and > an underrun/overrun happens, channel swap may occur. > The only recovery mechanism is to reset the ESAI." > > This issue exist in imx3/imx5/imx6(partial) series. > > In this commit add a tasklet to handle reset of ESAI > after xrun happens to recover the channel swap. > > Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com> Acked-by: Nicolin Chen <nicoleotsuka@gmail.com> Thanks > --- > sound/soc/fsl/fsl_esai.c | 74 ++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 74 insertions(+) > > diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c > index ab460d6d7432..4ce8ac769244 100644 > --- a/sound/soc/fsl/fsl_esai.c > +++ b/sound/soc/fsl/fsl_esai.c > @@ -32,6 +32,7 @@ > * @extalclk: esai clock source to derive HCK, SCK and FS > * @fsysclk: system clock source to derive HCK, SCK and FS > * @spbaclk: SPBA clock (optional, depending on SoC design) > + * @task: tasklet to handle the reset operation > * @fifo_depth: depth of tx/rx FIFO > * @slot_width: width of each DAI slot > * @slots: number of slots > @@ -42,6 +43,7 @@ > * @sck_div: if using PSR/PM dividers for SCKx clock > * @slave_mode: if fully using DAI slave mode > * @synchronous: if using tx/rx synchronous mode > + * @reset_at_xrun: flags for enable reset operaton > * @name: driver name > */ > struct fsl_esai { > @@ -53,6 +55,7 @@ struct fsl_esai { > struct clk *extalclk; > struct clk *fsysclk; > struct clk *spbaclk; > + struct tasklet_struct task; > u32 fifo_depth; > u32 slot_width; > u32 slots; > @@ -65,6 +68,7 @@ struct fsl_esai { > bool sck_div[2]; > bool slave_mode; > bool synchronous; > + bool reset_at_xrun; > char name[32]; > }; > > @@ -73,8 +77,16 @@ static irqreturn_t esai_isr(int irq, void *devid) > struct fsl_esai *esai_priv = (struct fsl_esai *)devid; > struct platform_device *pdev = esai_priv->pdev; > u32 esr; > + u32 saisr; > > regmap_read(esai_priv->regmap, REG_ESAI_ESR, &esr); > + regmap_read(esai_priv->regmap, REG_ESAI_SAISR, &saisr); > + > + if ((saisr & (ESAI_SAISR_TUE | ESAI_SAISR_ROE)) && > + esai_priv->reset_at_xrun) { > + dev_dbg(&pdev->dev, "reset module for xrun\n"); > + tasklet_schedule(&esai_priv->task); > + } > > if (esr & ESAI_ESR_TINIT_MASK) > dev_dbg(&pdev->dev, "isr: Transmission Initialized\n"); > @@ -635,10 +647,17 @@ static void fsl_esai_trigger_start(struct fsl_esai *esai_priv, bool 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)); > + > + /* Enable Exception interrupt */ > + regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), > + ESAI_xCR_xEIE_MASK, ESAI_xCR_xEIE); > } > > static void fsl_esai_trigger_stop(struct fsl_esai *esai_priv, bool tx) > { > + regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), > + ESAI_xCR_xEIE_MASK, 0); > + > regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), > tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK, 0); > regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMA(tx), > @@ -653,6 +672,51 @@ static void fsl_esai_trigger_stop(struct fsl_esai *esai_priv, bool tx) > ESAI_xFCR_xFR, 0); > } > > +static void fsl_esai_hw_reset(unsigned long arg) > +{ > + struct fsl_esai *esai_priv = (struct fsl_esai *)arg; > + bool tx = true, rx = false, enabled[2]; > + u32 tfcr, rfcr; > + > + /* Save the registers */ > + regmap_read(esai_priv->regmap, REG_ESAI_TFCR, &tfcr); > + regmap_read(esai_priv->regmap, REG_ESAI_RFCR, &rfcr); > + enabled[tx] = tfcr & ESAI_xFCR_xFEN; > + enabled[rx] = rfcr & ESAI_xFCR_xFEN; > + > + /* Stop the tx & rx */ > + fsl_esai_trigger_stop(esai_priv, tx); > + fsl_esai_trigger_stop(esai_priv, rx); > + > + /* Reset the esai, and ignore return value */ > + fsl_esai_hw_init(esai_priv); > + > + /* Enforce ESAI personal resets for both TX and RX */ > + regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR, > + ESAI_xCR_xPR_MASK, ESAI_xCR_xPR); > + regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR, > + ESAI_xCR_xPR_MASK, ESAI_xCR_xPR); > + > + /* Restore registers by regcache_sync, and ignore return value */ > + fsl_esai_register_restore(esai_priv); > + > + /* Remove ESAI personal resets by configuring PCRC and PRRC also */ > + regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR, > + ESAI_xCR_xPR_MASK, 0); > + regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR, > + ESAI_xCR_xPR_MASK, 0); > + regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC, > + ESAI_PRRC_PDC_MASK, ESAI_PRRC_PDC(ESAI_GPIO)); > + regmap_update_bits(esai_priv->regmap, REG_ESAI_PCRC, > + ESAI_PCRC_PC_MASK, ESAI_PCRC_PC(ESAI_GPIO)); > + > + /* Restart tx / rx, if they already enabled */ > + if (enabled[tx]) > + fsl_esai_trigger_start(esai_priv, tx); > + if (enabled[rx]) > + fsl_esai_trigger_start(esai_priv, rx); > +} > + > static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, > struct snd_soc_dai *dai) > { > @@ -857,6 +921,10 @@ static int fsl_esai_probe(struct platform_device *pdev) > esai_priv->pdev = pdev; > snprintf(esai_priv->name, sizeof(esai_priv->name), "%pOFn", np); > > + if (of_device_is_compatible(np, "fsl,vf610-esai") || > + of_device_is_compatible(np, "fsl,imx35-esai")) > + esai_priv->reset_at_xrun = true; > + > /* Get the addresses and IRQ */ > res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > regs = devm_ioremap_resource(&pdev->dev, res); > @@ -956,6 +1024,9 @@ static int fsl_esai_probe(struct platform_device *pdev) > return ret; > } > > + tasklet_init(&esai_priv->task, fsl_esai_hw_reset, > + (unsigned long)esai_priv); > + > pm_runtime_enable(&pdev->dev); > > regcache_cache_only(esai_priv->regmap, true); > @@ -969,7 +1040,10 @@ static int fsl_esai_probe(struct platform_device *pdev) > > static int fsl_esai_remove(struct platform_device *pdev) > { > + struct fsl_esai *esai_priv = platform_get_drvdata(pdev); > + > pm_runtime_disable(&pdev->dev); > + tasklet_kill(&esai_priv->task); > > return 0; > } > -- > 2.21.0 > ^ permalink raw reply [flat|nested] 6+ messages in thread
* Applied "ASoC: fsl_esai: recover the channel swap after xrun" to the asoc tree 2019-07-11 10:49 ` [PATCH V4 2/2] ASoC: fsl_esai: recover the channel swap after xrun shengjiu.wang 2019-07-11 16:54 ` Nicolin Chen @ 2019-07-23 11:29 ` Mark Brown 1 sibling, 0 replies; 6+ messages in thread From: Mark Brown @ 2019-07-23 11:29 UTC (permalink / raw) To: Shengjiu Wang Cc: alsa-devel, broonie, festevam, lgirdwood, linux-kernel, linuxppc-dev, Mark Brown, nicoleotsuka, Nicolin Chen, perex, timur, tiwai, Xiubo.Lee The patch ASoC: fsl_esai: recover the channel swap after xrun has been applied to the asoc tree at https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-5.4 All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted. You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed. If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced. Please add any relevant lists and maintainers to the CCs when replying to this mail. Thanks, Mark From 7ccafa2b3879612ab9ed88bb6664bd4085957186 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang <shengjiu.wang@nxp.com> Date: Thu, 11 Jul 2019 18:49:46 +0800 Subject: [PATCH] ASoC: fsl_esai: recover the channel swap after xrun There is chip errata ERR008000, the reference doc is (https://www.nxp.com/docs/en/errata/IMX6DQCE.pdf), The issue is "While using ESAI transmit or receive and an underrun/overrun happens, channel swap may occur. The only recovery mechanism is to reset the ESAI." This issue exist in imx3/imx5/imx6(partial) series. In this commit add a tasklet to handle reset of ESAI after xrun happens to recover the channel swap. Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com> Acked-by: Nicolin Chen <nicoleotsuka@gmail.com> Link: https://lore.kernel.org/r/326035cb99975361699d9ed748054b08bc06a341.1562842206.git.shengjiu.wang@nxp.com Signed-off-by: Mark Brown <broonie@kernel.org> --- sound/soc/fsl/fsl_esai.c | 74 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index ab460d6d7432..4ce8ac769244 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -32,6 +32,7 @@ * @extalclk: esai clock source to derive HCK, SCK and FS * @fsysclk: system clock source to derive HCK, SCK and FS * @spbaclk: SPBA clock (optional, depending on SoC design) + * @task: tasklet to handle the reset operation * @fifo_depth: depth of tx/rx FIFO * @slot_width: width of each DAI slot * @slots: number of slots @@ -42,6 +43,7 @@ * @sck_div: if using PSR/PM dividers for SCKx clock * @slave_mode: if fully using DAI slave mode * @synchronous: if using tx/rx synchronous mode + * @reset_at_xrun: flags for enable reset operaton * @name: driver name */ struct fsl_esai { @@ -53,6 +55,7 @@ struct fsl_esai { struct clk *extalclk; struct clk *fsysclk; struct clk *spbaclk; + struct tasklet_struct task; u32 fifo_depth; u32 slot_width; u32 slots; @@ -65,6 +68,7 @@ struct fsl_esai { bool sck_div[2]; bool slave_mode; bool synchronous; + bool reset_at_xrun; char name[32]; }; @@ -73,8 +77,16 @@ static irqreturn_t esai_isr(int irq, void *devid) struct fsl_esai *esai_priv = (struct fsl_esai *)devid; struct platform_device *pdev = esai_priv->pdev; u32 esr; + u32 saisr; regmap_read(esai_priv->regmap, REG_ESAI_ESR, &esr); + regmap_read(esai_priv->regmap, REG_ESAI_SAISR, &saisr); + + if ((saisr & (ESAI_SAISR_TUE | ESAI_SAISR_ROE)) && + esai_priv->reset_at_xrun) { + dev_dbg(&pdev->dev, "reset module for xrun\n"); + tasklet_schedule(&esai_priv->task); + } if (esr & ESAI_ESR_TINIT_MASK) dev_dbg(&pdev->dev, "isr: Transmission Initialized\n"); @@ -635,10 +647,17 @@ static void fsl_esai_trigger_start(struct fsl_esai *esai_priv, bool 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)); + + /* Enable Exception interrupt */ + regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), + ESAI_xCR_xEIE_MASK, ESAI_xCR_xEIE); } static void fsl_esai_trigger_stop(struct fsl_esai *esai_priv, bool tx) { + regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), + ESAI_xCR_xEIE_MASK, 0); + regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK, 0); regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMA(tx), @@ -653,6 +672,51 @@ static void fsl_esai_trigger_stop(struct fsl_esai *esai_priv, bool tx) ESAI_xFCR_xFR, 0); } +static void fsl_esai_hw_reset(unsigned long arg) +{ + struct fsl_esai *esai_priv = (struct fsl_esai *)arg; + bool tx = true, rx = false, enabled[2]; + u32 tfcr, rfcr; + + /* Save the registers */ + regmap_read(esai_priv->regmap, REG_ESAI_TFCR, &tfcr); + regmap_read(esai_priv->regmap, REG_ESAI_RFCR, &rfcr); + enabled[tx] = tfcr & ESAI_xFCR_xFEN; + enabled[rx] = rfcr & ESAI_xFCR_xFEN; + + /* Stop the tx & rx */ + fsl_esai_trigger_stop(esai_priv, tx); + fsl_esai_trigger_stop(esai_priv, rx); + + /* Reset the esai, and ignore return value */ + fsl_esai_hw_init(esai_priv); + + /* Enforce ESAI personal resets for both TX and RX */ + regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR, + ESAI_xCR_xPR_MASK, ESAI_xCR_xPR); + regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR, + ESAI_xCR_xPR_MASK, ESAI_xCR_xPR); + + /* Restore registers by regcache_sync, and ignore return value */ + fsl_esai_register_restore(esai_priv); + + /* Remove ESAI personal resets by configuring PCRC and PRRC also */ + regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR, + ESAI_xCR_xPR_MASK, 0); + regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR, + ESAI_xCR_xPR_MASK, 0); + regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC, + ESAI_PRRC_PDC_MASK, ESAI_PRRC_PDC(ESAI_GPIO)); + regmap_update_bits(esai_priv->regmap, REG_ESAI_PCRC, + ESAI_PCRC_PC_MASK, ESAI_PCRC_PC(ESAI_GPIO)); + + /* Restart tx / rx, if they already enabled */ + if (enabled[tx]) + fsl_esai_trigger_start(esai_priv, tx); + if (enabled[rx]) + fsl_esai_trigger_start(esai_priv, rx); +} + static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { @@ -857,6 +921,10 @@ static int fsl_esai_probe(struct platform_device *pdev) esai_priv->pdev = pdev; snprintf(esai_priv->name, sizeof(esai_priv->name), "%pOFn", np); + if (of_device_is_compatible(np, "fsl,vf610-esai") || + of_device_is_compatible(np, "fsl,imx35-esai")) + esai_priv->reset_at_xrun = true; + /* Get the addresses and IRQ */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); regs = devm_ioremap_resource(&pdev->dev, res); @@ -956,6 +1024,9 @@ static int fsl_esai_probe(struct platform_device *pdev) return ret; } + tasklet_init(&esai_priv->task, fsl_esai_hw_reset, + (unsigned long)esai_priv); + pm_runtime_enable(&pdev->dev); regcache_cache_only(esai_priv->regmap, true); @@ -969,7 +1040,10 @@ static int fsl_esai_probe(struct platform_device *pdev) static int fsl_esai_remove(struct platform_device *pdev) { + struct fsl_esai *esai_priv = platform_get_drvdata(pdev); + pm_runtime_disable(&pdev->dev); + tasklet_kill(&esai_priv->task); return 0; } -- 2.20.1 ^ permalink raw reply related [flat|nested] 6+ messages in thread
end of thread, other threads:[~2019-07-23 11:29 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2019-07-11 10:49 [PATCH V4 0/2] recover the channel swap after xrun shengjiu.wang 2019-07-11 10:49 ` [PATCH V4 1/2] ASoC: fsl_esai: Wrap some operations to be functions shengjiu.wang 2019-07-22 12:22 ` Applied "ASoC: fsl_esai: Wrap some operations to be functions" to the asoc tree Mark Brown 2019-07-11 10:49 ` [PATCH V4 2/2] ASoC: fsl_esai: recover the channel swap after xrun shengjiu.wang 2019-07-11 16:54 ` Nicolin Chen 2019-07-23 11:29 ` Applied "ASoC: fsl_esai: recover the channel swap after xrun" to the asoc tree Mark Brown
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox