From d8f006825ac57e34f7ed5e63a1e16d889dc1508e Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 30 Sep 2020 21:11:24 -0500 Subject: ASoC: sun8i-codec: Set up clock tree at probe time 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 Acked-by: Maxime Ripard Link: https://lore.kernel.org/r/20201001021148.15852-2-samuel@sholland.org Signed-off-by: Mark Brown --- sound/soc/sunxi/sun8i-codec.c | 54 ++++++++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 19 deletions(-) (limited to 'sound') diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c index 178f6fb31fd4..407f0fedc4ed 100644 --- a/sound/soc/sunxi/sun8i-codec.c +++ b/sound/soc/sunxi/sun8i-codec.c @@ -24,10 +24,13 @@ #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; } -- cgit v1.2.3