ASoC: sun4i-i2s: Fix the MCLK and BCLK dividers on newer SoCs
authorMaxime Ripard <maxime.ripard@bootlin.com>
Mon, 19 Aug 2019 19:25:16 +0000 (21:25 +0200)
committerMark Brown <broonie@kernel.org>
Tue, 20 Aug 2019 17:31:42 +0000 (18:31 +0100)
From: Marcus Cooper <codekipper@gmail.com>

The clock division dividers have changed between the older (A10/A31) and
newer (H3, A64, etc) SoCs.

While this was addressed through an offset on some SoCs, it was missing
some dividers as well, so the support wasn't perfect. Let's introduce a
pointer in the quirk structure for the divider calculation functions to use
so we can have the proper range now.

Signed-off-by: Marcus Cooper <codekipper@gmail.com>
[Maxime: Fix the commit log, use a field in the quirk structure]
Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
Link: https://lore.kernel.org/r/0e5b4abf06cd3202354315201c6af44caeb20236.1566242458.git-series.maxime.ripard@bootlin.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/sunxi/sun4i-i2s.c

index acfcdb2..0a7f1d0 100644 (file)
@@ -129,8 +129,6 @@ struct sun4i_i2s;
  * @has_fmt_set_lrck_period: SoC requires lrclk period to be set.
  * @reg_offset_txdata: offset of the tx fifo.
  * @sun4i_i2s_regmap: regmap config to use.
- * @mclk_offset: Value by which mclkdiv needs to be adjusted.
- * @bclk_offset: Value by which bclkdiv needs to be adjusted.
  * @field_clkdiv_mclk_en: regmap field to enable mclk output.
  * @field_fmt_wss: regmap field to set word select size.
  * @field_fmt_sr: regmap field to set sample resolution.
@@ -142,8 +140,6 @@ struct sun4i_i2s_quirks {
        bool                            has_fmt_set_lrck_period;
        unsigned int                    reg_offset_txdata;      /* TX FIFO */
        const struct regmap_config      *sun4i_i2s_regmap;
-       unsigned int                    mclk_offset;
-       unsigned int                    bclk_offset;
 
        /* Register fields for i2s */
        struct reg_field                field_clkdiv_mclk_en;
@@ -152,6 +148,11 @@ struct sun4i_i2s_quirks {
        struct reg_field                field_fmt_bclk;
        struct reg_field                field_fmt_lrclk;
 
+       const struct sun4i_i2s_clk_div  *bclk_dividers;
+       unsigned int                    num_bclk_dividers;
+       const struct sun4i_i2s_clk_div  *mclk_dividers;
+       unsigned int                    num_mclk_dividers;
+
        unsigned long (*get_bclk_parent_rate)(const struct sun4i_i2s *);
        s8      (*get_sr)(const struct sun4i_i2s *, int);
        s8      (*get_wss)(const struct sun4i_i2s *, int);
@@ -208,6 +209,24 @@ static const struct sun4i_i2s_clk_div sun4i_i2s_mclk_div[] = {
        /* TODO - extend divide ratio supported by newer SoCs */
 };
 
+static const struct sun4i_i2s_clk_div sun8i_i2s_clk_div[] = {
+       { .div = 1, .val = 1 },
+       { .div = 2, .val = 2 },
+       { .div = 4, .val = 3 },
+       { .div = 6, .val = 4 },
+       { .div = 8, .val = 5 },
+       { .div = 12, .val = 6 },
+       { .div = 16, .val = 7 },
+       { .div = 24, .val = 8 },
+       { .div = 32, .val = 9 },
+       { .div = 48, .val = 10 },
+       { .div = 64, .val = 11 },
+       { .div = 96, .val = 12 },
+       { .div = 128, .val = 13 },
+       { .div = 176, .val = 14 },
+       { .div = 192, .val = 15 },
+};
+
 static unsigned long sun4i_i2s_get_bclk_parent_rate(const struct sun4i_i2s *i2s)
 {
        return i2s->mclk_freq;
@@ -223,11 +242,12 @@ static int sun4i_i2s_get_bclk_div(struct sun4i_i2s *i2s,
                                  unsigned int sampling_rate,
                                  unsigned int word_size)
 {
+       const struct sun4i_i2s_clk_div *dividers = i2s->variant->bclk_dividers;
        int div = parent_rate / sampling_rate / word_size / 2;
        int i;
 
-       for (i = 0; i < ARRAY_SIZE(sun4i_i2s_bclk_div); i++) {
-               const struct sun4i_i2s_clk_div *bdiv = &sun4i_i2s_bclk_div[i];
+       for (i = 0; i < i2s->variant->num_bclk_dividers; i++) {
+               const struct sun4i_i2s_clk_div *bdiv = &dividers[i];
 
                if (bdiv->div == div)
                        return bdiv->val;
@@ -240,11 +260,12 @@ static int sun4i_i2s_get_mclk_div(struct sun4i_i2s *i2s,
                                  unsigned long parent_rate,
                                  unsigned long mclk_rate)
 {
+       const struct sun4i_i2s_clk_div *dividers = i2s->variant->mclk_dividers;
        int div = parent_rate / mclk_rate;
        int i;
 
-       for (i = 0; i < ARRAY_SIZE(sun4i_i2s_mclk_div); i++) {
-               const struct sun4i_i2s_clk_div *mdiv = &sun4i_i2s_mclk_div[i];
+       for (i = 0; i < i2s->variant->num_mclk_dividers; i++) {
+               const struct sun4i_i2s_clk_div *mdiv = &dividers[i];
 
                if (mdiv->div == div)
                        return mdiv->val;
@@ -326,10 +347,6 @@ static int sun4i_i2s_set_clk_rate(struct snd_soc_dai *dai,
                return -EINVAL;
        }
 
-       /* Adjust the clock division values if needed */
-       bclk_div += i2s->variant->bclk_offset;
-       mclk_div += i2s->variant->mclk_offset;
-
        regmap_write(i2s->regmap, SUN4I_I2S_CLK_DIV_REG,
                     SUN4I_I2S_CLK_DIV_BCLK(bclk_div) |
                     SUN4I_I2S_CLK_DIV_MCLK(mclk_div));
@@ -969,6 +986,10 @@ static const struct sun4i_i2s_quirks sun4i_a10_i2s_quirks = {
        .field_fmt_sr           = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 5),
        .field_fmt_bclk         = REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6),
        .field_fmt_lrclk        = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
+       .bclk_dividers          = sun4i_i2s_bclk_div,
+       .num_bclk_dividers      = ARRAY_SIZE(sun4i_i2s_bclk_div),
+       .mclk_dividers          = sun4i_i2s_mclk_div,
+       .num_mclk_dividers      = ARRAY_SIZE(sun4i_i2s_mclk_div),
        .get_bclk_parent_rate   = sun4i_i2s_get_bclk_parent_rate,
        .get_sr                 = sun4i_i2s_get_sr,
        .get_wss                = sun4i_i2s_get_wss,
@@ -985,6 +1006,10 @@ static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = {
        .field_fmt_sr           = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 5),
        .field_fmt_bclk         = REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6),
        .field_fmt_lrclk        = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
+       .bclk_dividers          = sun4i_i2s_bclk_div,
+       .num_bclk_dividers      = ARRAY_SIZE(sun4i_i2s_bclk_div),
+       .mclk_dividers          = sun4i_i2s_mclk_div,
+       .num_mclk_dividers      = ARRAY_SIZE(sun4i_i2s_mclk_div),
        .get_bclk_parent_rate   = sun4i_i2s_get_bclk_parent_rate,
        .get_sr                 = sun4i_i2s_get_sr,
        .get_wss                = sun4i_i2s_get_wss,
@@ -1001,6 +1026,10 @@ static const struct sun4i_i2s_quirks sun8i_a83t_i2s_quirks = {
        .field_fmt_sr           = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 5),
        .field_fmt_bclk         = REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6),
        .field_fmt_lrclk        = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
+       .bclk_dividers          = sun8i_i2s_clk_div,
+       .num_bclk_dividers      = ARRAY_SIZE(sun8i_i2s_clk_div),
+       .mclk_dividers          = sun8i_i2s_clk_div,
+       .num_mclk_dividers      = ARRAY_SIZE(sun8i_i2s_clk_div),
        .get_bclk_parent_rate   = sun8i_i2s_get_bclk_parent_rate,
        .get_sr                 = sun8i_i2s_get_sr_wss,
        .get_wss                = sun8i_i2s_get_sr_wss,
@@ -1012,14 +1041,16 @@ static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = {
        .has_reset              = true,
        .reg_offset_txdata      = SUN8I_I2S_FIFO_TX_REG,
        .sun4i_i2s_regmap       = &sun8i_i2s_regmap_config,
-       .mclk_offset            = 1,
-       .bclk_offset            = 2,
        .has_fmt_set_lrck_period = true,
        .field_clkdiv_mclk_en   = REG_FIELD(SUN4I_I2S_CLK_DIV_REG, 8, 8),
        .field_fmt_wss          = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 2),
        .field_fmt_sr           = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 6),
        .field_fmt_bclk         = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
        .field_fmt_lrclk        = REG_FIELD(SUN4I_I2S_FMT0_REG, 19, 19),
+       .bclk_dividers          = sun8i_i2s_clk_div,
+       .num_bclk_dividers      = ARRAY_SIZE(sun8i_i2s_clk_div),
+       .mclk_dividers          = sun8i_i2s_clk_div,
+       .num_mclk_dividers      = ARRAY_SIZE(sun8i_i2s_clk_div),
        .get_bclk_parent_rate   = sun8i_i2s_get_bclk_parent_rate,
        .get_sr                 = sun8i_i2s_get_sr_wss,
        .get_wss                = sun8i_i2s_get_sr_wss,
@@ -1036,6 +1067,10 @@ static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = {
        .field_fmt_sr           = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 5),
        .field_fmt_bclk         = REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6),
        .field_fmt_lrclk        = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
+       .bclk_dividers          = sun4i_i2s_bclk_div,
+       .num_bclk_dividers      = ARRAY_SIZE(sun4i_i2s_bclk_div),
+       .mclk_dividers          = sun4i_i2s_mclk_div,
+       .num_mclk_dividers      = ARRAY_SIZE(sun4i_i2s_mclk_div),
        .get_bclk_parent_rate   = sun4i_i2s_get_bclk_parent_rate,
        .get_sr                 = sun4i_i2s_get_sr,
        .get_wss                = sun4i_i2s_get_wss,