ASoC: apple: mca: Fix SERDES reset sequence
authorMartin Povišer <povik+lin@cutebit.org>
Fri, 24 Feb 2023 15:33:01 +0000 (16:33 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 11 Mar 2023 12:55:30 +0000 (13:55 +0100)
[ Upstream commit d8b3e396088d787771f19fd3b7949e080dc31d6f ]

Fix the reset sequence of reads and writes that we invoke from within
the early trigger. It looks like there never was a SERDES_CONF_SOME_RST
bit that should be involved in the reset sequence, and its presence in
the driver code is a mistake from earlier.

Instead, the reset sequence should go as follows: We should switch the
the SERDES unit's SYNC_SEL mux to the value of 7 (so outside the range
of 1...6 representing cluster's SYNCGEN units), then raise the RST bit
in SERDES_STATUS and wait for it to clear.

Properly resetting the SERDES unit fixes frame desynchronization hazard
in case of long frames (longer than 4 used slots). The desynchronization
manifests itself by rotating the PCM channels.

Fixes: 3df5d0d97289 ("ASoC: apple: mca: Start new platform driver")
Signed-off-by: Martin Povišer <povik+lin@cutebit.org>
Link: https://lore.kernel.org/r/20230224153302.45365-2-povik+lin@cutebit.org
Signed-off-by: Mark Brown <broonie@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
sound/soc/apple/mca.c

index 9cceeb2..aea08c7 100644 (file)
 #define SERDES_CONF_UNK3       BIT(14)
 #define SERDES_CONF_NO_DATA_FEEDBACK   BIT(15)
 #define SERDES_CONF_SYNC_SEL   GENMASK(18, 16)
-#define SERDES_CONF_SOME_RST   BIT(19)
 #define REG_TX_SERDES_BITSTART 0x08
 #define REG_RX_SERDES_BITSTART 0x0c
 #define REG_TX_SERDES_SLOTMASK 0x0c
@@ -203,15 +202,24 @@ static void mca_fe_early_trigger(struct snd_pcm_substream *substream, int cmd,
        case SNDRV_PCM_TRIGGER_START:
        case SNDRV_PCM_TRIGGER_RESUME:
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               mca_modify(cl, serdes_conf, SERDES_CONF_SYNC_SEL,
+                          FIELD_PREP(SERDES_CONF_SYNC_SEL, 0));
+               mca_modify(cl, serdes_conf, SERDES_CONF_SYNC_SEL,
+                          FIELD_PREP(SERDES_CONF_SYNC_SEL, 7));
                mca_modify(cl, serdes_unit + REG_SERDES_STATUS,
                           SERDES_STATUS_EN | SERDES_STATUS_RST,
                           SERDES_STATUS_RST);
-               mca_modify(cl, serdes_conf, SERDES_CONF_SOME_RST,
-                          SERDES_CONF_SOME_RST);
-               readl_relaxed(cl->base + serdes_conf);
-               mca_modify(cl, serdes_conf, SERDES_STATUS_RST, 0);
+               /*
+                * Experiments suggest that it takes at most ~1 us
+                * for the bit to clear, so wait 2 us for good measure.
+                */
+               udelay(2);
                WARN_ON(readl_relaxed(cl->base + serdes_unit + REG_SERDES_STATUS) &
                        SERDES_STATUS_RST);
+               mca_modify(cl, serdes_conf, SERDES_CONF_SYNC_SEL,
+                          FIELD_PREP(SERDES_CONF_SYNC_SEL, 0));
+               mca_modify(cl, serdes_conf, SERDES_CONF_SYNC_SEL,
+                          FIELD_PREP(SERDES_CONF_SYNC_SEL, cl->no + 1));
                break;
        default:
                break;