From: Valerio Setti vsetti@baylibre.com
[ Upstream commit 4c4ed5e073a923fb3323022e1131cb51ad8df7a0 ]
According to I2S specs audio data is sampled on the rising edge of the clock and it can change on the falling one. When operating in normal mode this SoC behaves the opposite so a clock polarity inversion is required in this case.
This was tested on an OdroidC2 (Amlogic S905 SoC) board.
Signed-off-by: Valerio Setti vsetti@baylibre.com Reviewed-by: Jerome Brunet jbrunet@baylibre.com Tested-by: Jerome Brunet jbrunet@baylibre.com Link: https://patch.msgid.link/20251007-fix-i2s-polarity-v1-1-86704d9cda10@baylibr... Signed-off-by: Mark Brown broonie@kernel.org Signed-off-by: Sasha Levin sashal@kernel.org ---
LLM Generated explanations, may be completely bogus:
YES – this change fixes a long-standing functional bug with the Meson AIU I²S encoder and is low risk to backport. - The updated `aiu_encoder_i2s_set_fmt()` now inverts the bit clock whenever the DAI format requests the normal (non-inverted) polarity (`SND_SOC_DAIFMT_NB_*`), matching the hardware quirk described in the new comment (`sound/soc/meson/aiu-encoder-i2s.c:239-245`). Without this inversion, the CPU-side master drives BCLK so that data toggles on the rising edge, which makes all mainline DT users (e.g. Odroid C2) sample on the wrong edge and yields audible corruption. - Frames (`LRCLK`) are still handled exactly as before, and the fix only flips which `inv` combinations set `AIU_CLK_CTRL_AOCLK_INVERT`, so `IB_*` formats continue to work as they already matched the SoC’s “inverted” default. - The change is tightly scoped to one helper in the Meson AIU encoder driver, leaves register programming and clock sequencing untouched, and has been validated on real hardware per the commit log. - Mainline device trees for this DAI all rely on the default `NB_NF` format, so the bug is user-visible today; there are no dependency or API concerns blocking stable backporting.
Suggested follow-up: 1) Run a quick playback sanity test on an Odroid C2 (or any Meson GX board using the AIU encoder) after backporting to confirm audio becomes clean.
sound/soc/meson/aiu-encoder-i2s.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/sound/soc/meson/aiu-encoder-i2s.c b/sound/soc/meson/aiu-encoder-i2s.c index a0dd914c8ed13..3b4061508c180 100644 --- a/sound/soc/meson/aiu-encoder-i2s.c +++ b/sound/soc/meson/aiu-encoder-i2s.c @@ -236,8 +236,12 @@ static int aiu_encoder_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) inv == SND_SOC_DAIFMT_IB_IF) val |= AIU_CLK_CTRL_LRCLK_INVERT;
- if (inv == SND_SOC_DAIFMT_IB_NF || - inv == SND_SOC_DAIFMT_IB_IF) + /* + * The SoC changes data on the rising edge of the bitclock + * so an inversion of the bitclock is required in normal mode + */ + if (inv == SND_SOC_DAIFMT_NB_NF || + inv == SND_SOC_DAIFMT_NB_IF) val |= AIU_CLK_CTRL_AOCLK_INVERT;
/* Signal skew */ @@ -328,4 +332,3 @@ const struct snd_soc_dai_ops aiu_encoder_i2s_dai_ops = { .startup = aiu_encoder_i2s_startup, .shutdown = aiu_encoder_i2s_shutdown, }; -