ASoC: meson: axg-toddr: fix channel order on g12 platforms
authorJerome Brunet <jbrunet@baylibre.com>
Fri, 28 Aug 2020 15:14:38 +0000 (17:14 +0200)
committerMark Brown <broonie@kernel.org>
Fri, 28 Aug 2020 18:02:54 +0000 (19:02 +0100)
On g12 and following platforms, The first channel of record with more than
2 channels ends being placed randomly on an even channel of the output.

On these SoCs, a bit was added to force the first channel to be placed at
the beginning of the output. Apparently the behavior if the bit is not set
is not easily predictable. According to the documentation, this bit is not
present on the axg series.

Set the bit on g12 and fix the problem.

Fixes: a3c23a8ad4dc ("ASoC: meson: axg-toddr: add g12a support")
Reported-by: Nicolas Belin <nbelin@baylibre.com>
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Link: https://lore.kernel.org/r/20200828151438.350974-1-jbrunet@baylibre.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/meson/axg-toddr.c

index e711abc..d6adf7e 100644 (file)
@@ -18,6 +18,7 @@
 #define CTRL0_TODDR_SEL_RESAMPLE       BIT(30)
 #define CTRL0_TODDR_EXT_SIGNED         BIT(29)
 #define CTRL0_TODDR_PP_MODE            BIT(28)
+#define CTRL0_TODDR_SYNC_CH            BIT(27)
 #define CTRL0_TODDR_TYPE_MASK          GENMASK(15, 13)
 #define CTRL0_TODDR_TYPE(x)            ((x) << 13)
 #define CTRL0_TODDR_MSB_POS_MASK       GENMASK(12, 8)
@@ -189,10 +190,31 @@ static const struct axg_fifo_match_data axg_toddr_match_data = {
        .dai_drv                = &axg_toddr_dai_drv
 };
 
+static int g12a_toddr_dai_startup(struct snd_pcm_substream *substream,
+                                struct snd_soc_dai *dai)
+{
+       struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai);
+       int ret;
+
+       ret = axg_toddr_dai_startup(substream, dai);
+       if (ret)
+               return ret;
+
+       /*
+        * Make sure the first channel ends up in the at beginning of the output
+        * As weird as it looks, without this the first channel may be misplaced
+        * in memory, with a random shift of 2 channels.
+        */
+       regmap_update_bits(fifo->map, FIFO_CTRL0, CTRL0_TODDR_SYNC_CH,
+                          CTRL0_TODDR_SYNC_CH);
+
+       return 0;
+}
+
 static const struct snd_soc_dai_ops g12a_toddr_ops = {
        .prepare        = g12a_toddr_dai_prepare,
        .hw_params      = axg_toddr_dai_hw_params,
-       .startup        = axg_toddr_dai_startup,
+       .startup        = g12a_toddr_dai_startup,
        .shutdown       = axg_toddr_dai_shutdown,
 };