ASoC: sun4i-i2s: Add support for DSP formats
authorMaxime Ripard <maxime.ripard@bootlin.com>
Wed, 21 Aug 2019 13:06:56 +0000 (15:06 +0200)
committerMark Brown <broonie@kernel.org>
Wed, 21 Aug 2019 13:17:15 +0000 (14:17 +0100)
In addition to the I2S format, the controller also supports the DSP_*
formats.

This requires some extra care on the LRCK period calculation, since the
controller, with the PCM formats, require that the value set is no longer
the periods of LRCK for a single channel, but for all of them.

Let's add the code to deal with this, and support the DSP_A and DSP_B
formats.

Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
Link: https://lore.kernel.org/r/5562db1ac8759f12b1b87c3258223eed629ef771.1566392800.git-series.maxime.ripard@bootlin.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/sunxi/sun4i-i2s.c

index 69162af9fd65f7c04bdc03d628d9907a01100686..57bf2a33753e460e846020bed0e7b7efc4a898ad 100644 (file)
@@ -130,7 +130,6 @@ struct sun4i_i2s;
  * struct sun4i_i2s_quirks - Differences between SoC variants.
  *
  * @has_reset: SoC needs reset deasserted.
- * @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.
  * @field_clkdiv_mclk_en: regmap field to enable mclk output.
@@ -139,7 +138,6 @@ struct sun4i_i2s;
  */
 struct sun4i_i2s_quirks {
        bool                            has_reset;
-       bool                            has_fmt_set_lrck_period;
        unsigned int                    reg_offset_txdata;      /* TX FIFO */
        const struct regmap_config      *sun4i_i2s_regmap;
 
@@ -167,6 +165,7 @@ struct sun4i_i2s {
        struct regmap   *regmap;
        struct reset_control *rst;
 
+       unsigned int    format;
        unsigned int    mclk_freq;
        unsigned int    slots;
        unsigned int    slot_width;
@@ -355,12 +354,6 @@ static int sun4i_i2s_set_clk_rate(struct snd_soc_dai *dai,
 
        regmap_field_write(i2s->field_clkdiv_mclk_en, 1);
 
-       /* Set sync period */
-       if (i2s->variant->has_fmt_set_lrck_period)
-               regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT0_REG,
-                                  SUN8I_I2S_FMT0_LRCK_PERIOD_MASK,
-                                  SUN8I_I2S_FMT0_LRCK_PERIOD(slot_width));
-
        return 0;
 }
 
@@ -422,6 +415,7 @@ static int sun8i_i2s_set_chan_cfg(const struct sun4i_i2s *i2s,
 {
        unsigned int channels = params_channels(params);
        unsigned int slots = channels;
+       unsigned int lrck_period;
 
        if (i2s->slots)
                slots = i2s->slots;
@@ -445,6 +439,26 @@ static int sun8i_i2s_set_chan_cfg(const struct sun4i_i2s *i2s,
                           SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM_MASK,
                           SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM(channels));
 
+       switch (i2s->format & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_DSP_A:
+       case SND_SOC_DAIFMT_DSP_B:
+       case SND_SOC_DAIFMT_LEFT_J:
+       case SND_SOC_DAIFMT_RIGHT_J:
+               lrck_period = params_physical_width(params) * slots;
+               break;
+
+       case SND_SOC_DAIFMT_I2S:
+               lrck_period = params_physical_width(params);
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT0_REG,
+                          SUN8I_I2S_FMT0_LRCK_PERIOD_MASK,
+                          SUN8I_I2S_FMT0_LRCK_PERIOD(lrck_period));
+
        regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG,
                           SUN8I_I2S_TX_CHAN_EN_MASK,
                           SUN8I_I2S_TX_CHAN_EN(channels));
@@ -616,6 +630,16 @@ static int sun8i_i2s_set_soc_fmt(const struct sun4i_i2s *i2s,
 
        /* DAI Mode */
        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_DSP_A:
+               mode = SUN8I_I2S_CTRL_MODE_PCM;
+               offset = 1;
+               break;
+
+       case SND_SOC_DAIFMT_DSP_B:
+               mode = SUN8I_I2S_CTRL_MODE_PCM;
+               offset = 0;
+               break;
+
        case SND_SOC_DAIFMT_I2S:
                mode = SUN8I_I2S_CTRL_MODE_LEFT;
                offset = 1;
@@ -684,6 +708,9 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
                           SUN4I_I2S_FIFO_CTRL_RX_MODE_MASK,
                           SUN4I_I2S_FIFO_CTRL_TX_MODE(1) |
                           SUN4I_I2S_FIFO_CTRL_RX_MODE(1));
+
+       i2s->format = fmt;
+
        return 0;
 }
 
@@ -1074,7 +1101,6 @@ static const struct sun4i_i2s_quirks sun8i_a83t_i2s_quirks = {
        .has_reset              = true,
        .reg_offset_txdata      = SUN8I_I2S_FIFO_TX_REG,
        .sun4i_i2s_regmap       = &sun4i_i2s_regmap_config,
-       .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),