Applied "ASoC: fsl_esai: recover the channel swap after xrun" to the asoc tree
Mark Brown
broonie at kernel.org
Tue Jul 23 21:29:11 AEST 2019
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 at 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 at nxp.com>
Acked-by: Nicolin Chen <nicoleotsuka at gmail.com>
Link: https://lore.kernel.org/r/326035cb99975361699d9ed748054b08bc06a341.1562842206.git.shengjiu.wang@nxp.com
Signed-off-by: Mark Brown <broonie at 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
More information about the Linuxppc-dev
mailing list