]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/commitdiff
ASoC: sun8i-codec: Protect the clock rate while streams are open
authorSamuel Holland <samuel@sholland.org>
Wed, 14 Oct 2020 06:19:36 +0000 (01:19 -0500)
committerMark Brown <broonie@kernel.org>
Mon, 26 Oct 2020 14:57:01 +0000 (14:57 +0000)
The codec's clock input is shared among all AIFs, and shared with other
audio-related hardware in the SoC, including I2S and SPDIF controllers.
To ensure sample rates selected by userspace or by codec2codec DAI links
are maintained, the clock rate must be protected while it is in use.

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

index 9cd7009cb5708216fdc6c2619398cd5f6cfe0b5b..69b9d85153357dfd5140a54b6629038c15482388 100644 (file)
@@ -14,6 +14,7 @@ config SND_SUN8I_CODEC
        tristate "Allwinner SUN8I audio codec"
        depends on OF
        depends on MACH_SUN8I || (ARM64 && ARCH_SUNXI) || COMPILE_TEST
+       select COMMON_CLK
        select REGMAP_MMIO
        help
          This option enables the digital part of the internal audio codec for
index 0e8b0ac31fed303e52d2725a6ed409c177b49231..253857e66f6fe302638b5701f2210ac2fe1d5ede 100644 (file)
@@ -421,6 +421,11 @@ static int sun8i_codec_get_lrck_div_order(unsigned int slots,
        return order_base_2(div);
 }
 
+static unsigned int sun8i_codec_get_sysclk_rate(unsigned int sample_rate)
+{
+       return sample_rate % 4000 ? 22579200 : 24576000;
+}
+
 static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
                                 struct snd_pcm_hw_params *params,
                                 struct snd_soc_dai *dai)
@@ -430,8 +435,8 @@ static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
        unsigned int sample_rate = params_rate(params);
        unsigned int slots = aif->slots ?: params_channels(params);
        unsigned int slot_width = aif->slot_width ?: params_width(params);
-       unsigned int sysclk_rate = clk_get_rate(scodec->clk_module);
-       int lrck_div_order, word_size;
+       unsigned int sysclk_rate = sun8i_codec_get_sysclk_rate(sample_rate);
+       int lrck_div_order, ret, word_size;
        u8 bclk_div;
 
        /* word size */
@@ -471,6 +476,24 @@ static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
                           SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK,
                           bclk_div << SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV);
 
+       /*
+        * SYSCLK rate
+        *
+        * Clock rate protection is reference counted; but hw_params may be
+        * called many times per substream, without matching calls to hw_free.
+        * Protect the clock rate once per AIF, on the first hw_params call
+        * for the first substream. clk_set_rate() will allow clock rate
+        * changes on subsequent calls if only one AIF has open streams.
+        */
+       ret = (aif->open_streams ? clk_set_rate : clk_set_rate_exclusive)(scodec->clk_module,
+                                                                         sysclk_rate);
+       if (ret == -EBUSY)
+               dev_err(dai->dev,
+                       "%s sample rate (%u Hz) conflicts with other audio streams\n",
+                       dai->name, sample_rate);
+       if (ret < 0)
+               return ret;
+
        if (!aif->open_streams)
                scodec->sysclk_refcnt++;
        scodec->sysclk_rate = sysclk_rate;
@@ -487,9 +510,11 @@ static int sun8i_codec_hw_free(struct snd_pcm_substream *substream,
        struct sun8i_codec *scodec = snd_soc_dai_get_drvdata(dai);
        struct sun8i_codec_aif *aif = &scodec->aifs[dai->id];
 
+       /* Drop references when the last substream for the AIF is freed. */
        if (aif->open_streams != BIT(substream->stream))
                goto done;
 
+       clk_rate_exclusive_put(scodec->clk_module);
        scodec->sysclk_refcnt--;
        aif->sample_rate = 0;