ASoC: sun4i-i2s: Move the channel configuration to a callback
authorMaxime Ripard <maxime.ripard@bootlin.com>
Mon, 19 Aug 2019 19:25:11 +0000 (21:25 +0200)
committerMark Brown <broonie@kernel.org>
Tue, 20 Aug 2019 17:30:00 +0000 (18:30 +0100)
The two main generations of our I2S controller require a slightly different
channel 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/6414463de69584e8227fa495b13aa5f4798e1f0e.1566242458.git-series.maxime.ripard@bootlin.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/sunxi/sun4i-i2s.c

index 2c909c6..42e45c9 100644 (file)
@@ -80,6 +80,7 @@
 #define SUN4I_I2S_TX_CNT_REG           0x2c
 
 #define SUN4I_I2S_TX_CHAN_SEL_REG      0x30
+#define SUN4I_I2S_CHAN_SEL_MASK                        GENMASK(2, 0)
 #define SUN4I_I2S_CHAN_SEL(num_chan)           (((num_chan) - 1) << 0)
 
 #define SUN4I_I2S_TX_CHAN_MAP_REG      0x34
@@ -122,8 +123,6 @@ struct sun4i_i2s;
  * @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_chcfg: tx and rx slot number need to be set.
- * @has_chsel_tx_chen: SoC requires that the tx channels are enabled.
  * @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.
@@ -135,17 +134,11 @@ struct sun4i_i2s;
  * @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.
- * @field_txchanmap: location of the tx channel mapping register.
- * @field_rxchanmap: location of the rx channel mapping register.
- * @field_txchansel: location of the tx channel select bit fields.
- * @field_rxchansel: location of the rx channel select bit fields.
  */
 struct sun4i_i2s_quirks {
        bool                            has_reset;
        bool                            has_slave_select_bit;
        bool                            has_fmt_set_lrck_period;
-       bool                            has_chcfg;
-       bool                            has_chsel_tx_chen;
        bool                            has_chsel_offset;
        unsigned int                    reg_offset_txdata;      /* TX FIFO */
        const struct regmap_config      *sun4i_i2s_regmap;
@@ -159,13 +152,11 @@ struct sun4i_i2s_quirks {
        struct reg_field                field_fmt_bclk;
        struct reg_field                field_fmt_lrclk;
        struct reg_field                field_fmt_mode;
-       struct reg_field                field_txchanmap;
-       struct reg_field                field_rxchanmap;
-       struct reg_field                field_txchansel;
-       struct reg_field                field_rxchansel;
 
        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 *);
 };
 
 struct sun4i_i2s {
@@ -186,10 +177,6 @@ struct sun4i_i2s {
        struct regmap_field     *field_fmt_bclk;
        struct regmap_field     *field_fmt_lrclk;
        struct regmap_field     *field_fmt_mode;
-       struct regmap_field     *field_txchanmap;
-       struct regmap_field     *field_rxchanmap;
-       struct regmap_field     *field_txchansel;
-       struct regmap_field     *field_rxchansel;
 
        const struct sun4i_i2s_quirks   *variant;
 };
@@ -380,44 +367,77 @@ static s8 sun8i_i2s_get_sr_wss(const struct sun4i_i2s *i2s, int width)
        return (width - 8) / 4 + 1;
 }
 
-static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
-                              struct snd_pcm_hw_params *params,
-                              struct snd_soc_dai *dai)
+static int sun4i_i2s_set_chan_cfg(const struct sun4i_i2s *i2s,
+                                 const struct snd_pcm_hw_params *params)
 {
-       struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
-       int sr, wss, channels;
-       u32 width;
+       unsigned int channels = params_channels(params);
 
-       channels = params_channels(params);
-       if (channels != 2) {
-               dev_err(dai->dev, "Unsupported number of channels: %d\n",
-                       channels);
+       if (channels != 2)
                return -EINVAL;
-       }
 
-       if (i2s->variant->has_chcfg) {
-               regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG,
-                                  SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM_MASK,
-                                  SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM(channels));
-               regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG,
-                                  SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM_MASK,
-                                  SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM(channels));
-       }
+       /* Map the channels for playback and capture */
+       regmap_write(i2s->regmap, SUN4I_I2S_TX_CHAN_MAP_REG, 0x76543210);
+       regmap_write(i2s->regmap, SUN4I_I2S_RX_CHAN_MAP_REG, 0x00003210);
+
+       /* Configure the channels */
+       regmap_update_bits(i2s->regmap, SUN4I_I2S_TX_CHAN_SEL_REG,
+                          SUN4I_I2S_CHAN_SEL_MASK,
+                          SUN4I_I2S_CHAN_SEL(channels));
+       regmap_update_bits(i2s->regmap, SUN4I_I2S_RX_CHAN_SEL_REG,
+                          SUN4I_I2S_CHAN_SEL_MASK,
+                          SUN4I_I2S_CHAN_SEL(channels));
+
+       return 0;
+}
+
+static int sun8i_i2s_set_chan_cfg(const struct sun4i_i2s *i2s,
+                                 const struct snd_pcm_hw_params *params)
+{
+       unsigned int channels = params_channels(params);
+
+       if (channels != 2)
+               return -EINVAL;
 
        /* Map the channels for playback and capture */
-       regmap_field_write(i2s->field_txchanmap, 0x76543210);
-       regmap_field_write(i2s->field_rxchanmap, 0x00003210);
+       regmap_write(i2s->regmap, SUN8I_I2S_TX_CHAN_MAP_REG, 0x76543210);
+       regmap_write(i2s->regmap, SUN8I_I2S_RX_CHAN_MAP_REG, 0x76543210);
 
        /* Configure the channels */
-       regmap_field_write(i2s->field_txchansel,
+       regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG,
+                          SUN4I_I2S_CHAN_SEL_MASK,
                           SUN4I_I2S_CHAN_SEL(channels));
-       regmap_field_write(i2s->field_rxchansel,
+
+       regmap_update_bits(i2s->regmap, SUN8I_I2S_RX_CHAN_SEL_REG,
+                          SUN4I_I2S_CHAN_SEL_MASK,
                           SUN4I_I2S_CHAN_SEL(channels));
 
-       if (i2s->variant->has_chsel_tx_chen)
-               regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG,
-                                  SUN8I_I2S_TX_CHAN_EN_MASK,
-                                  SUN8I_I2S_TX_CHAN_EN(channels));
+       regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG,
+                          SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM_MASK,
+                          SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM(channels));
+       regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG,
+                          SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM_MASK,
+                          SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM(channels));
+
+       regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG,
+                          SUN8I_I2S_TX_CHAN_EN_MASK,
+                          SUN8I_I2S_TX_CHAN_EN(channels));
+
+       return 0;
+}
+
+static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
+                              struct snd_pcm_hw_params *params,
+                              struct snd_soc_dai *dai)
+{
+       struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+       int ret, sr, wss;
+       u32 width;
+
+       ret = i2s->variant->set_chan_cfg(i2s, params);
+       if (ret < 0) {
+               dev_err(dai->dev, "Invalid channel configuration\n");
+               return ret;
+       }
 
        switch (params_physical_width(params)) {
        case 16:
@@ -915,12 +935,9 @@ static const struct sun4i_i2s_quirks sun4i_a10_i2s_quirks = {
        .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),
-       .field_txchanmap        = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31),
-       .field_rxchanmap        = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31),
-       .field_txchansel        = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2),
-       .field_rxchansel        = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2),
        .get_sr                 = sun4i_i2s_get_sr,
        .get_wss                = sun4i_i2s_get_wss,
+       .set_chan_cfg           = sun4i_i2s_set_chan_cfg,
 };
 
 static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = {
@@ -934,12 +951,9 @@ static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = {
        .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),
-       .field_txchanmap        = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31),
-       .field_rxchanmap        = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31),
-       .field_txchansel        = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2),
-       .field_rxchansel        = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2),
        .get_sr                 = sun4i_i2s_get_sr,
        .get_wss                = sun4i_i2s_get_wss,
+       .set_chan_cfg           = sun4i_i2s_set_chan_cfg,
 };
 
 static const struct sun4i_i2s_quirks sun8i_a83t_i2s_quirks = {
@@ -953,12 +967,9 @@ static const struct sun4i_i2s_quirks sun8i_a83t_i2s_quirks = {
        .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),
-       .field_txchanmap        = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31),
-       .field_rxchanmap        = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31),
-       .field_txchansel        = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2),
-       .field_rxchansel        = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2),
        .get_sr                 = sun8i_i2s_get_sr_wss,
        .get_wss                = sun8i_i2s_get_sr_wss,
+       .set_chan_cfg           = sun8i_i2s_set_chan_cfg,
 };
 
 static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = {
@@ -968,8 +979,6 @@ static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = {
        .mclk_offset            = 1,
        .bclk_offset            = 2,
        .has_fmt_set_lrck_period = true,
-       .has_chcfg              = true,
-       .has_chsel_tx_chen      = 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),
@@ -977,12 +986,9 @@ static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = {
        .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),
-       .field_txchanmap        = REG_FIELD(SUN8I_I2S_TX_CHAN_MAP_REG, 0, 31),
-       .field_rxchanmap        = REG_FIELD(SUN8I_I2S_RX_CHAN_MAP_REG, 0, 31),
-       .field_txchansel        = REG_FIELD(SUN8I_I2S_TX_CHAN_SEL_REG, 0, 2),
-       .field_rxchansel        = REG_FIELD(SUN8I_I2S_RX_CHAN_SEL_REG, 0, 2),
        .get_sr                 = sun8i_i2s_get_sr_wss,
        .get_wss                = sun8i_i2s_get_sr_wss,
+       .set_chan_cfg           = sun8i_i2s_set_chan_cfg,
 };
 
 static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = {
@@ -996,12 +1002,9 @@ static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = {
        .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),
-       .field_txchanmap        = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31),
-       .field_rxchanmap        = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31),
-       .field_txchansel        = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2),
-       .field_rxchansel        = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2),
        .get_sr                 = sun4i_i2s_get_sr,
        .get_wss                = sun4i_i2s_get_wss,
+       .set_chan_cfg           = sun4i_i2s_set_chan_cfg,
 };
 
 static int sun4i_i2s_init_regmap_fields(struct device *dev,
@@ -1043,28 +1046,7 @@ static int sun4i_i2s_init_regmap_fields(struct device *dev,
        if (IS_ERR(i2s->field_fmt_mode))
                return PTR_ERR(i2s->field_fmt_mode);
 
-       i2s->field_txchanmap =
-                       devm_regmap_field_alloc(dev, i2s->regmap,
-                                               i2s->variant->field_txchanmap);
-       if (IS_ERR(i2s->field_txchanmap))
-               return PTR_ERR(i2s->field_txchanmap);
-
-       i2s->field_rxchanmap =
-                       devm_regmap_field_alloc(dev, i2s->regmap,
-                                               i2s->variant->field_rxchanmap);
-       if (IS_ERR(i2s->field_rxchanmap))
-               return PTR_ERR(i2s->field_rxchanmap);
-
-       i2s->field_txchansel =
-                       devm_regmap_field_alloc(dev, i2s->regmap,
-                                               i2s->variant->field_txchansel);
-       if (IS_ERR(i2s->field_txchansel))
-               return PTR_ERR(i2s->field_txchansel);
-
-       i2s->field_rxchansel =
-                       devm_regmap_field_alloc(dev, i2s->regmap,
-                                               i2s->variant->field_rxchansel);
-       return PTR_ERR_OR_ZERO(i2s->field_rxchansel);
+       return 0;
 }
 
 static int sun4i_i2s_probe(struct platform_device *pdev)