OSDN Git Service

ASoC: sun4i-i2s: Move the format configuration to a callback
authorMaxime Ripard <maxime.ripard@bootlin.com>
Mon, 19 Aug 2019 19:25:12 +0000 (21:25 +0200)
committerMark Brown <broonie@kernel.org>
Tue, 20 Aug 2019 17:30:18 +0000 (18:30 +0100)
The two main generations of our I2S controller require a slightly different
format configuration, mostly because of a quite different register layout
and some additional registers being needed on the newer generation.

This used to be controlled through a bunch of booleans, however this proved
to be quite impractical, especially since a bunch of SoCs forgot to set
those parameters and therefore were broken from that point of view.

Fixes: 21faaea1343f ("ASoC: sun4i-i2s: Add support for A83T")
Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
Link: https://lore.kernel.org/r/dc818644c3e40734e7a97247c994b1fca1c3c047.1566242458.git-series.maxime.ripard@bootlin.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/sunxi/sun4i-i2s.c

index 42e45c9..93ea627 100644 (file)
 #define SUN8I_I2S_CTRL_BCLK_OUT                        BIT(18)
 #define SUN8I_I2S_CTRL_LRCK_OUT                        BIT(17)
 
+#define SUN8I_I2S_CTRL_MODE_MASK               GENMASK(5, 4)
+#define SUN8I_I2S_CTRL_MODE_RIGHT              (2 << 4)
+#define SUN8I_I2S_CTRL_MODE_LEFT               (1 << 4)
+#define SUN8I_I2S_CTRL_MODE_PCM                        (0 << 4)
+
 #define SUN8I_I2S_FMT0_LRCK_PERIOD_MASK                GENMASK(17, 8)
 #define SUN8I_I2S_FMT0_LRCK_PERIOD(period)     ((period - 1) << 8)
 
@@ -121,9 +126,7 @@ struct sun4i_i2s;
  * struct sun4i_i2s_quirks - Differences between SoC variants.
  *
  * @has_reset: SoC needs reset deasserted.
- * @has_slave_select_bit: SoC has a bit to enable slave mode.
  * @has_fmt_set_lrck_period: SoC requires lrclk period to be set.
- * @has_chsel_offset: SoC uses offset for selecting dai operational mode.
  * @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.
@@ -133,13 +136,10 @@ struct sun4i_i2s;
  * @field_fmt_sr: regmap field to set sample resolution.
  * @field_fmt_bclk: regmap field to set clk polarity.
  * @field_fmt_lrclk: regmap field to set frame polarity.
- * @field_fmt_mode: regmap field to set the operational mode.
  */
 struct sun4i_i2s_quirks {
        bool                            has_reset;
-       bool                            has_slave_select_bit;
        bool                            has_fmt_set_lrck_period;
-       bool                            has_chsel_offset;
        unsigned int                    reg_offset_txdata;      /* TX FIFO */
        const struct regmap_config      *sun4i_i2s_regmap;
        unsigned int                    mclk_offset;
@@ -151,12 +151,12 @@ struct sun4i_i2s_quirks {
        struct reg_field                field_fmt_sr;
        struct reg_field                field_fmt_bclk;
        struct reg_field                field_fmt_lrclk;
-       struct reg_field                field_fmt_mode;
 
        s8      (*get_sr)(const struct sun4i_i2s *, int);
        s8      (*get_wss)(const struct sun4i_i2s *, int);
        int     (*set_chan_cfg)(const struct sun4i_i2s *,
                                const struct snd_pcm_hw_params *);
+       int     (*set_fmt)(const struct sun4i_i2s *, unsigned int);
 };
 
 struct sun4i_i2s {
@@ -176,7 +176,6 @@ struct sun4i_i2s {
        struct regmap_field     *field_fmt_sr;
        struct regmap_field     *field_fmt_bclk;
        struct regmap_field     *field_fmt_lrclk;
-       struct regmap_field     *field_fmt_mode;
 
        const struct sun4i_i2s_quirks   *variant;
 };
@@ -465,52 +464,117 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
                                      params_width(params));
 }
 
-static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+static int sun4i_i2s_set_soc_fmt(const struct sun4i_i2s *i2s,
+                                unsigned int fmt)
 {
-       struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
        u32 val;
-       u32 offset = 0;
-       u32 bclk_polarity = SUN4I_I2S_FMT0_POLARITY_NORMAL;
-       u32 lrclk_polarity = SUN4I_I2S_FMT0_POLARITY_NORMAL;
 
        /* DAI Mode */
        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
        case SND_SOC_DAIFMT_I2S:
                val = SUN4I_I2S_FMT0_FMT_I2S;
-               offset = 1;
                break;
+
        case SND_SOC_DAIFMT_LEFT_J:
                val = SUN4I_I2S_FMT0_FMT_LEFT_J;
                break;
+
        case SND_SOC_DAIFMT_RIGHT_J:
                val = SUN4I_I2S_FMT0_FMT_RIGHT_J;
                break;
+
        default:
-               dev_err(dai->dev, "Unsupported format: %d\n",
-                       fmt & SND_SOC_DAIFMT_FORMAT_MASK);
                return -EINVAL;
        }
 
-       if (i2s->variant->has_chsel_offset) {
-               /*
-                * offset being set indicates that we're connected to an i2s
-                * device, however offset is only used on the sun8i block and
-                * i2s shares the same setting with the LJ format. Increment
-                * val so that the bit to value to write is correct.
-                */
-               if (offset > 0)
-                       val++;
-               /* blck offset determines whether i2s or LJ */
-               regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG,
-                                  SUN8I_I2S_TX_CHAN_OFFSET_MASK,
-                                  SUN8I_I2S_TX_CHAN_OFFSET(offset));
-
-               regmap_update_bits(i2s->regmap, SUN8I_I2S_RX_CHAN_SEL_REG,
-                                  SUN8I_I2S_TX_CHAN_OFFSET_MASK,
-                                  SUN8I_I2S_TX_CHAN_OFFSET(offset));
+       regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT0_REG,
+                          SUN4I_I2S_FMT0_FMT_MASK, val);
+
+       /* DAI clock master masks */
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFS:
+               /* BCLK and LRCLK master */
+               val = SUN4I_I2S_CTRL_MODE_MASTER;
+               break;
+
+       case SND_SOC_DAIFMT_CBM_CFM:
+               /* BCLK and LRCLK slave */
+               val = SUN4I_I2S_CTRL_MODE_SLAVE;
+               break;
+
+       default:
+               return -EINVAL;
        }
+       regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
+                          SUN4I_I2S_CTRL_MODE_MASK, val);
+       return 0;
+}
 
-       regmap_field_write(i2s->field_fmt_mode, val);
+static int sun8i_i2s_set_soc_fmt(const struct sun4i_i2s *i2s,
+                                unsigned int fmt)
+{
+       u32 mode, val;
+       u8 offset;
+
+       /* DAI Mode */
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               mode = SUN8I_I2S_CTRL_MODE_LEFT;
+               offset = 1;
+               break;
+
+       case SND_SOC_DAIFMT_LEFT_J:
+               mode = SUN8I_I2S_CTRL_MODE_LEFT;
+               offset = 0;
+               break;
+
+       case SND_SOC_DAIFMT_RIGHT_J:
+               mode = SUN8I_I2S_CTRL_MODE_RIGHT;
+               offset = 0;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
+                          SUN8I_I2S_CTRL_MODE_MASK, mode);
+       regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG,
+                          SUN8I_I2S_TX_CHAN_OFFSET_MASK,
+                          SUN8I_I2S_TX_CHAN_OFFSET(offset));
+       regmap_update_bits(i2s->regmap, SUN8I_I2S_RX_CHAN_SEL_REG,
+                          SUN8I_I2S_TX_CHAN_OFFSET_MASK,
+                          SUN8I_I2S_TX_CHAN_OFFSET(offset));
+
+       /* DAI clock master masks */
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFS:
+               /* BCLK and LRCLK master */
+               val = SUN8I_I2S_CTRL_BCLK_OUT | SUN8I_I2S_CTRL_LRCK_OUT;
+               break;
+
+       case SND_SOC_DAIFMT_CBM_CFM:
+               /* BCLK and LRCLK slave */
+               val = 0;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
+                          SUN8I_I2S_CTRL_BCLK_OUT | SUN8I_I2S_CTRL_LRCK_OUT,
+                          val);
+
+       return 0;
+}
+
+static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+       struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+       u32 bclk_polarity = SUN4I_I2S_FMT0_POLARITY_NORMAL;
+       u32 lrclk_polarity = SUN4I_I2S_FMT0_POLARITY_NORMAL;
+       int ret;
 
        /* DAI clock polarity */
        switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
@@ -538,50 +602,10 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
        regmap_field_write(i2s->field_fmt_bclk, bclk_polarity);
        regmap_field_write(i2s->field_fmt_lrclk, lrclk_polarity);
 
-       if (i2s->variant->has_slave_select_bit) {
-               /* DAI clock master masks */
-               switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
-               case SND_SOC_DAIFMT_CBS_CFS:
-                       /* BCLK and LRCLK master */
-                       val = SUN4I_I2S_CTRL_MODE_MASTER;
-                       break;
-               case SND_SOC_DAIFMT_CBM_CFM:
-                       /* BCLK and LRCLK slave */
-                       val = SUN4I_I2S_CTRL_MODE_SLAVE;
-                       break;
-               default:
-                       dev_err(dai->dev, "Unsupported slave setting: %d\n",
-                               fmt & SND_SOC_DAIFMT_MASTER_MASK);
-                       return -EINVAL;
-               }
-               regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
-                                  SUN4I_I2S_CTRL_MODE_MASK,
-                                  val);
-       } else {
-               /*
-                * The newer i2s block does not have a slave select bit,
-                * instead the clk pins are configured as inputs.
-                */
-               /* DAI clock master masks */
-               switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
-               case SND_SOC_DAIFMT_CBS_CFS:
-                       /* BCLK and LRCLK master */
-                       val = SUN8I_I2S_CTRL_BCLK_OUT |
-                               SUN8I_I2S_CTRL_LRCK_OUT;
-                       break;
-               case SND_SOC_DAIFMT_CBM_CFM:
-                       /* BCLK and LRCLK slave */
-                       val = 0;
-                       break;
-               default:
-                       dev_err(dai->dev, "Unsupported slave setting: %d\n",
-                               fmt & SND_SOC_DAIFMT_MASTER_MASK);
-                       return -EINVAL;
-               }
-               regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
-                                  SUN8I_I2S_CTRL_BCLK_OUT |
-                                  SUN8I_I2S_CTRL_LRCK_OUT,
-                                  val);
+       ret = i2s->variant->set_fmt(i2s, fmt);
+       if (ret) {
+               dev_err(dai->dev, "Unsupported format configuration\n");
+               return ret;
        }
 
        /* Set significant bits in our FIFOs */
@@ -933,11 +957,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),
-       .has_slave_select_bit   = true,
-       .field_fmt_mode         = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1),
        .get_sr                 = sun4i_i2s_get_sr,
        .get_wss                = sun4i_i2s_get_wss,
        .set_chan_cfg           = sun4i_i2s_set_chan_cfg,
+       .set_fmt                = sun4i_i2s_set_soc_fmt,
 };
 
 static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = {
@@ -949,11 +972,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),
-       .has_slave_select_bit   = true,
-       .field_fmt_mode         = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1),
        .get_sr                 = sun4i_i2s_get_sr,
        .get_wss                = sun4i_i2s_get_wss,
        .set_chan_cfg           = sun4i_i2s_set_chan_cfg,
+       .set_fmt                = sun4i_i2s_set_soc_fmt,
 };
 
 static const struct sun4i_i2s_quirks sun8i_a83t_i2s_quirks = {
@@ -965,11 +987,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),
-       .has_slave_select_bit   = true,
-       .field_fmt_mode         = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1),
        .get_sr                 = sun8i_i2s_get_sr_wss,
        .get_wss                = sun8i_i2s_get_sr_wss,
        .set_chan_cfg           = sun8i_i2s_set_chan_cfg,
+       .set_fmt                = sun8i_i2s_set_soc_fmt,
 };
 
 static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = {
@@ -979,32 +1000,30 @@ static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = {
        .mclk_offset            = 1,
        .bclk_offset            = 2,
        .has_fmt_set_lrck_period = true,
-       .has_chsel_offset       = 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),
-       .field_fmt_mode         = REG_FIELD(SUN4I_I2S_CTRL_REG, 4, 5),
        .get_sr                 = sun8i_i2s_get_sr_wss,
        .get_wss                = sun8i_i2s_get_sr_wss,
        .set_chan_cfg           = sun8i_i2s_set_chan_cfg,
+       .set_fmt                = sun8i_i2s_set_soc_fmt,
 };
 
 static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = {
        .has_reset              = true,
        .reg_offset_txdata      = SUN8I_I2S_FIFO_TX_REG,
        .sun4i_i2s_regmap       = &sun4i_i2s_regmap_config,
-       .has_slave_select_bit   = true,
        .field_clkdiv_mclk_en   = REG_FIELD(SUN4I_I2S_CLK_DIV_REG, 7, 7),
        .field_fmt_wss          = REG_FIELD(SUN4I_I2S_FMT0_REG, 2, 3),
        .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),
-       .field_fmt_mode         = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1),
        .get_sr                 = sun4i_i2s_get_sr,
        .get_wss                = sun4i_i2s_get_wss,
        .set_chan_cfg           = sun4i_i2s_set_chan_cfg,
+       .set_fmt                = sun4i_i2s_set_soc_fmt,
 };
 
 static int sun4i_i2s_init_regmap_fields(struct device *dev,
@@ -1040,12 +1059,6 @@ static int sun4i_i2s_init_regmap_fields(struct device *dev,
        if (IS_ERR(i2s->field_fmt_lrclk))
                return PTR_ERR(i2s->field_fmt_lrclk);
 
-       i2s->field_fmt_mode =
-                       devm_regmap_field_alloc(dev, i2s->regmap,
-                                               i2s->variant->field_fmt_mode);
-       if (IS_ERR(i2s->field_fmt_mode))
-               return PTR_ERR(i2s->field_fmt_mode);
-
        return 0;
 }