From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
[ Upstream commit 25226abc1affd4bf4f6dd415d475b76e7a273fa8 ]
MSIOF has TXRST/RXRST to reset FIFO, but it shouldn't be used during SYNC signal was asserted, because it will be cause of HW issue.
When MSIOF is used as Sound driver, this driver is assuming it is used as clock consumer mode (= Codec is clock provider). This means, it can't control SYNC signal by itself.
We need to use SW reset (= reset_control_xxx()) instead of TXRST/RXRST.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com Tested-by: Yusuke Goda yusuke.goda.sx@renesas.com Link: https://patch.msgid.link/87cy7fyuug.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown broonie@kernel.org Signed-off-by: Sasha Levin sashal@kernel.org ---
LLM Generated explanations, may be completely bogus:
YES — The change should go to stable. The MSIOF audio block needs its FIFOs cleared between runs, but the on-IP `TXRST/RXRST` bits cannot be touched while SYNC is asserted, which is exactly the situation for clock-consumer audio links; the original driver therefore has no safe way to recover from prior transfers and will hit the documented HW issue. The patch switches the driver to the module reset line, gated by a local user-count, so the FIFOs are flushed without touching the fragile in-block resets.
Key pieces: - `sound/soc/renesas/rcar/msiof.c:112-121` adds a reset-controller handle plus a stream user counter so the module reset can be shared by playback and capture safely. - `msiof_hw_start()` now drops the module reset on the first active stream (`sound/soc/renesas/rcar/msiof.c:189-207`), guaranteeing the hardware starts from a clean state without poking the unsafe FIFO reset bits. - `msiof_hw_stop()` balances that by re-asserting the reset once the last stream stops (`sound/soc/renesas/rcar/msiof.c:316-319`), ensuring the block is quiesced even if the codec keeps SYNC running. - `msiof_probe()` acquires the reset line and asserts it at probe (`sound/soc/renesas/rcar/msiof.c:587-599`), aligning the power-on state with the new sequencing.
The change is confined to the new MSIOF sound driver, depends only on the already-required DT `resets` property (verified in the binding), and doesn’t alter wider ASoC infrastructure. Given it fixes a real hardware malfunction and carries low regression risk, it is a solid stable backport candidate.
sound/soc/renesas/rcar/msiof.c | 39 +++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-)
diff --git a/sound/soc/renesas/rcar/msiof.c b/sound/soc/renesas/rcar/msiof.c index 7a9ecc73231a8..3a1a6496637dd 100644 --- a/sound/soc/renesas/rcar/msiof.c +++ b/sound/soc/renesas/rcar/msiof.c @@ -24,12 +24,25 @@ * Clock/Frame Consumer Mode. */
+/* + * [NOTE-RESET] + * + * MSIOF has TXRST/RXRST to reset FIFO, but it shouldn't be used during SYNC signal was asserted, + * because it will be cause of HW issue. + * + * When MSIOF is used as Sound driver, this driver is assuming it is used as clock consumer mode + * (= Codec is clock provider). This means, it can't control SYNC signal by itself. + * + * We need to use SW reset (= reset_control_xxx()) instead of TXRST/RXRST. + */ + #include <linux/module.h> #include <linux/of.h> #include <linux/of_dma.h> #include <linux/of_graph.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/reset.h> #include <linux/spi/sh_msiof.h> #include <sound/dmaengine_pcm.h> #include <sound/soc.h> @@ -61,10 +74,13 @@ struct msiof_priv { struct device *dev; struct snd_pcm_substream *substream[SNDRV_PCM_STREAM_LAST + 1]; + struct reset_control *reset; spinlock_t lock; void __iomem *base; resource_size_t phy_addr;
+ int count; + /* for error */ int err_syc[SNDRV_PCM_STREAM_LAST + 1]; int err_ovf[SNDRV_PCM_STREAM_LAST + 1]; @@ -126,6 +142,16 @@ static int msiof_hw_start(struct snd_soc_component *component, * RX: Fig 109.15 */
+ /* + * Use reset_control_xx() instead of TXRST/RXRST. + * see + * [NOTE-RESET] + */ + if (!priv->count) + reset_control_deassert(priv->reset); + + priv->count++; + /* reset errors */ priv->err_syc[substream->stream] = priv->err_ovf[substream->stream] = @@ -144,7 +170,6 @@ static int msiof_hw_start(struct snd_soc_component *component, val = FIELD_PREP(SIMDR2_BITLEN1, width - 1); msiof_write(priv, SITMDR2, val | FIELD_PREP(SIMDR2_GRP, 1)); msiof_write(priv, SITMDR3, val); - } /* SIRMDRx */ else { @@ -217,6 +242,11 @@ static int msiof_hw_stop(struct snd_soc_component *component, priv->err_ovf[substream->stream], priv->err_udf[substream->stream]);
+ priv->count--; + + if (!priv->count) + reset_control_assert(priv->reset); + return 0; }
@@ -493,12 +523,19 @@ static int msiof_probe(struct platform_device *pdev) if (IS_ERR(priv->base)) return PTR_ERR(priv->base);
+ priv->reset = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(priv->reset)) + return PTR_ERR(priv->reset); + + reset_control_assert(priv->reset); + ret = devm_request_irq(dev, irq, msiof_interrupt, 0, dev_name(dev), priv); if (ret) return ret;
priv->dev = dev; priv->phy_addr = res->start; + priv->count = 0;
spin_lock_init(&priv->lock); platform_set_drvdata(pdev, priv);