]> git.proxmox.com Git - mirror_ubuntu-focal-kernel.git/blobdiff - sound/soc/sunxi/sun8i-codec.c
Merge remote-tracking branches 'asoc/topic/ac97', 'asoc/topic/ac97-mfd', 'asoc/topic...
[mirror_ubuntu-focal-kernel.git] / sound / soc / sunxi / sun8i-codec.c
index abfb710df7cbceb54c9fc60ab7502f088d630874..50a9e077f01b2372f1fe20d4ebd1179e208fa69d 100644 (file)
@@ -73,6 +73,7 @@
 #define SUN8I_SYS_SR_CTRL_AIF2_FS_MASK         GENMASK(11, 8)
 #define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_MASK  GENMASK(5, 4)
 #define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK  GENMASK(8, 6)
+#define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK  GENMASK(12, 9)
 
 struct sun8i_codec {
        struct device   *dev;
@@ -170,11 +171,11 @@ static int sun8i_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 
        /* clock masters */
        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
-       case SND_SOC_DAIFMT_CBS_CFS: /* DAI Slave */
-               value = 0x0; /* Codec Master */
+       case SND_SOC_DAIFMT_CBS_CFS: /* Codec slave, DAI master */
+               value = 0x1;
                break;
-       case SND_SOC_DAIFMT_CBM_CFM: /* DAI Master */
-               value = 0x1; /* Codec Slave */
+       case SND_SOC_DAIFMT_CBM_CFM: /* Codec Master, DAI slave */
+               value = 0x0;
                break;
        default:
                return -EINVAL;
@@ -226,12 +227,57 @@ static int sun8i_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
        return 0;
 }
 
+struct sun8i_codec_clk_div {
+       u8      div;
+       u8      val;
+};
+
+static const struct sun8i_codec_clk_div sun8i_codec_bclk_div[] = {
+       { .div = 1,     .val = 0 },
+       { .div = 2,     .val = 1 },
+       { .div = 4,     .val = 2 },
+       { .div = 6,     .val = 3 },
+       { .div = 8,     .val = 4 },
+       { .div = 12,    .val = 5 },
+       { .div = 16,    .val = 6 },
+       { .div = 24,    .val = 7 },
+       { .div = 32,    .val = 8 },
+       { .div = 48,    .val = 9 },
+       { .div = 64,    .val = 10 },
+       { .div = 96,    .val = 11 },
+       { .div = 128,   .val = 12 },
+       { .div = 192,   .val = 13 },
+};
+
+static u8 sun8i_codec_get_bclk_div(struct sun8i_codec *scodec,
+                                  unsigned int rate,
+                                  unsigned int word_size)
+{
+       unsigned long clk_rate = clk_get_rate(scodec->clk_module);
+       unsigned int div = clk_rate / rate / word_size / 2;
+       unsigned int best_val = 0, best_diff = ~0;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(sun8i_codec_bclk_div); i++) {
+               const struct sun8i_codec_clk_div *bdiv = &sun8i_codec_bclk_div[i];
+               unsigned int diff = abs(bdiv->div - div);
+
+               if (diff < best_diff) {
+                       best_diff = diff;
+                       best_val = bdiv->val;
+               }
+       }
+
+       return best_val;
+}
+
 static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
                                 struct snd_pcm_hw_params *params,
                                 struct snd_soc_dai *dai)
 {
        struct sun8i_codec *scodec = snd_soc_codec_get_drvdata(dai->codec);
        int sample_rate;
+       u8 bclk_div;
 
        /*
         * The CPU DAI handles only a sample of 16 bits. Configure the
@@ -241,6 +287,11 @@ static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
                           SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_MASK,
                           SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_16);
 
+       bclk_div = sun8i_codec_get_bclk_div(scodec, params_rate(params), 16);
+       regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
+                          SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK,
+                          bclk_div << SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV);
+
        regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
                           SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK,
                           SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_16);