ASoC: sun8i-codec: Set up clock tree at probe time
authorSamuel Holland <samuel@sholland.org>
Thu, 1 Oct 2020 02:11:24 +0000 (21:11 -0500)
committerMark Brown <broonie@kernel.org>
Mon, 5 Oct 2020 13:16:14 +0000 (14:16 +0100)
The sun8i codec is effectively an on-die variant of the X-Powers AC100
codec. The AC100 can derive its clocks from either of two I2S master
clocks or an internal PLL. For the on-die variant, Allwinner replaced
the codec's own PLL with a connection to SoC's existing PLL_AUDIO, and
they connected both I2S MCLK inputs to the same source -- which happens
to be an integer divider from the same PLL_AUDIO.

So there's actually no clocking flexibility. To run SYSCLK at the
required rate, it must be run straight from the PLL. The only choice is
whether it goes through AIF1CLK or AIF2CLK. Since both run at the same
rate, the only effect of that choice is which field in SYS_SR_CTRL
(AIF1_FS or AIF2_FS) controls the system sample rate.

Since AIFnCLK is required to bring up the corresponding DAI, and AIF1
(connected to the CPU) is used most often, let's use AIF1CLK as the
SYSCLK parent. That means we no longer need to set AIF2_FS.

Since this clock tree never changes, we can program it from the
component probe function, instead of using DAPM widgets. The DAPM
widgets unnecessarily change clock parents when the codec goes in/out
of idle and the supply widgets are powered up/down.

Signed-off-by: Samuel Holland <samuel@sholland.org>
Acked-by: Maxime Ripard <mripard@kernel.org>
Link: https://lore.kernel.org/r/20201001021148.15852-2-samuel@sholland.org
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/sunxi/sun8i-codec.c

index 178f6fb..407f0fe 100644 (file)
 
 #define SUN8I_SYSCLK_CTL                               0x00c
 #define SUN8I_SYSCLK_CTL_AIF1CLK_ENA                   11
-#define SUN8I_SYSCLK_CTL_AIF1CLK_SRC_PLL               9
-#define SUN8I_SYSCLK_CTL_AIF1CLK_SRC                   8
+#define SUN8I_SYSCLK_CTL_AIF1CLK_SRC_PLL               (0x2 << 8)
+#define SUN8I_SYSCLK_CTL_AIF2CLK_ENA                   7
+#define SUN8I_SYSCLK_CTL_AIF2CLK_SRC_PLL               (0x2 << 4)
 #define SUN8I_SYSCLK_CTL_SYSCLK_ENA                    3
 #define SUN8I_SYSCLK_CTL_SYSCLK_SRC                    0
+#define SUN8I_SYSCLK_CTL_SYSCLK_SRC_AIF1CLK            (0x0 << 0)
+#define SUN8I_SYSCLK_CTL_SYSCLK_SRC_AIF2CLK            (0x1 << 0)
 #define SUN8I_MOD_CLK_ENA                              0x010
 #define SUN8I_MOD_CLK_ENA_AIF1                         15
 #define SUN8I_MOD_CLK_ENA_ADC                          3
@@ -79,6 +82,8 @@
 #define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF2DACR                9
 #define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_ADCR            8
 
+#define SUN8I_SYSCLK_CTL_AIF1CLK_SRC_MASK      GENMASK(9, 8)
+#define SUN8I_SYSCLK_CTL_AIF2CLK_SRC_MASK      GENMASK(5, 4)
 #define SUN8I_SYS_SR_CTRL_AIF1_FS_MASK         GENMASK(15, 12)
 #define SUN8I_SYS_SR_CTRL_AIF2_FS_MASK         GENMASK(11, 8)
 #define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK  GENMASK(12, 9)
@@ -323,9 +328,6 @@ static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
        regmap_update_bits(scodec->regmap, SUN8I_SYS_SR_CTRL,
                           SUN8I_SYS_SR_CTRL_AIF1_FS_MASK,
                           sample_rate << SUN8I_SYS_SR_CTRL_AIF1_FS);
-       regmap_update_bits(scodec->regmap, SUN8I_SYS_SR_CTRL,
-                          SUN8I_SYS_SR_CTRL_AIF2_FS_MASK,
-                          sample_rate << SUN8I_SYS_SR_CTRL_AIF2_FS);
 
        return 0;
 }
@@ -366,8 +368,16 @@ static const struct snd_kcontrol_new sun8i_input_mixer_controls[] = {
 };
 
 static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = {
+       /* System Clocks */
        SND_SOC_DAPM_CLOCK_SUPPLY("mod"),
 
+       SND_SOC_DAPM_SUPPLY("AIF1CLK",
+                           SUN8I_SYSCLK_CTL,
+                           SUN8I_SYSCLK_CTL_AIF1CLK_ENA, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("SYSCLK",
+                           SUN8I_SYSCLK_CTL,
+                           SUN8I_SYSCLK_CTL_SYSCLK_ENA, 0, NULL, 0),
+
        /* Digital parts of the DACs and ADC */
        SND_SOC_DAPM_SUPPLY("DAC", SUN8I_DAC_DIG_CTRL, SUN8I_DAC_DIG_CTRL_ENDA,
                            0, NULL, 0),
@@ -415,16 +425,6 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = {
                            SUN8I_MOD_CLK_ENA_DAC, 0, NULL, 0),
        SND_SOC_DAPM_SUPPLY("MODCLK ADC", SUN8I_MOD_CLK_ENA,
                            SUN8I_MOD_CLK_ENA_ADC, 0, NULL, 0),
-       SND_SOC_DAPM_SUPPLY("AIF1", SUN8I_SYSCLK_CTL,
-                           SUN8I_SYSCLK_CTL_AIF1CLK_ENA, 0, NULL, 0),
-       SND_SOC_DAPM_SUPPLY("SYSCLK", SUN8I_SYSCLK_CTL,
-                           SUN8I_SYSCLK_CTL_SYSCLK_ENA, 0, NULL, 0),
-
-       SND_SOC_DAPM_SUPPLY("AIF1 PLL", SUN8I_SYSCLK_CTL,
-                           SUN8I_SYSCLK_CTL_AIF1CLK_SRC_PLL, 0, NULL, 0),
-       /* Inversion as 0=AIF1, 1=AIF2 */
-       SND_SOC_DAPM_SUPPLY("SYSCLK AIF1", SUN8I_SYSCLK_CTL,
-                           SUN8I_SYSCLK_CTL_SYSCLK_SRC, 1, NULL, 0),
 
        /* Module reset */
        SND_SOC_DAPM_SUPPLY("RST AIF1", SUN8I_MOD_RST_CTL,
@@ -437,12 +437,11 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = {
 
 static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = {
        /* Clock Routes */
-       { "AIF1", NULL, "mod" },
+       { "AIF1CLK", NULL, "mod" },
 
-       { "AIF1", NULL, "SYSCLK AIF1" },
-       { "AIF1 PLL", NULL, "AIF1" },
-       { "SYSCLK", NULL, "AIF1 PLL" },
+       { "SYSCLK", NULL, "AIF1CLK" },
 
+       { "RST AIF1", NULL, "AIF1CLK" },
        { "RST AIF1", NULL, "SYSCLK" },
        { "MODCLK AIF1", NULL, "RST AIF1" },
        { "AIF1 AD0L", NULL, "MODCLK AIF1" },
@@ -524,6 +523,23 @@ static int sun8i_codec_component_probe(struct snd_soc_component *component)
                        return ret;
        }
 
+       /*
+        * AIF1CLK and AIF2CLK share a pair of clock parents: PLL_AUDIO ("mod")
+        * and MCLK (from the CPU DAI connected to AIF1). MCLK's parent is also
+        * PLL_AUDIO, so using it adds no additional flexibility. Use PLL_AUDIO
+        * directly to simplify the clock tree.
+        */
+       regmap_update_bits(scodec->regmap, SUN8I_SYSCLK_CTL,
+                          SUN8I_SYSCLK_CTL_AIF1CLK_SRC_MASK |
+                          SUN8I_SYSCLK_CTL_AIF2CLK_SRC_MASK,
+                          SUN8I_SYSCLK_CTL_AIF1CLK_SRC_PLL |
+                          SUN8I_SYSCLK_CTL_AIF2CLK_SRC_PLL);
+
+       /* Use AIF1CLK as the SYSCLK parent since AIF1 is used most often. */
+       regmap_update_bits(scodec->regmap, SUN8I_SYSCLK_CTL,
+                          BIT(SUN8I_SYSCLK_CTL_SYSCLK_SRC),
+                          SUN8I_SYSCLK_CTL_SYSCLK_SRC_AIF1CLK);
+
        return 0;
 }