[alsa-devel] [PATCH v2 0/4] ASoC: sun4i-i2s: Number of fixes and TDM Support
From: Maxime Ripard maxime.ripard@bootlin.com
Hi,
This series aims at fixing a number of issues in the current i2s driver, mostly related to the i2s master support and the A83t support. It also uses that occasion to cleanup a few things and simplify the driver. Finally, it builds on those fixes and cleanups to introduce TDM and DSP formats support.
Let me know what you think, Maxime
Changes from v1: - Removed patches applied - Refactor a bit the call to sun4i_i2s_set_clk_rate - Fix build issue - Add an extra patch to cleanup sun4i_i2s_hw_params
Maxime Ripard (4): ASoC: sun4i-i2s: Use the physical / slot width for the clocks ASoC: sun4i-i2s: Use the actual format width instead of an hardcoded one ASoC: sun4i-i2s: Replace call to params_width by local variable ASoC: sun4i-i2s: Add support for DSP formats
sound/soc/sunxi/sun4i-i2s.c | 58 ++++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 15 deletions(-)
base-commit: 137befe19f310400a8b20fd8a4ce8c4141aafde0
From: Maxime Ripard maxime.ripard@bootlin.com
The clock dividers function has been using the word size to compute the clock rate at which it's supposed to be running, but the proper formula would be to use the physical width and / or slot width in TDM.
It doesn't make any difference at the moment since all the formats supported have the same sample width and physical width, but it's not going to last forever.
Fixes: 7d2993811a1e ("ASoC: sun4i-i2s: Add support for H3") Signed-off-by: Maxime Ripard maxime.ripard@bootlin.com --- sound/soc/sunxi/sun4i-i2s.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index 8326b8cfa569..cdc3fa60ff33 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -290,7 +290,7 @@ static bool sun4i_i2s_oversample_is_valid(unsigned int oversample) static int sun4i_i2s_set_clk_rate(struct snd_soc_dai *dai, unsigned int rate, unsigned int slots, - unsigned int word_size) + unsigned int slot_width) { struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai); unsigned int oversample_rate, clk_rate, bclk_parent_rate; @@ -337,7 +337,7 @@ static int sun4i_i2s_set_clk_rate(struct snd_soc_dai *dai,
bclk_parent_rate = i2s->variant->get_bclk_parent_rate(i2s); bclk_div = sun4i_i2s_get_bclk_div(i2s, bclk_parent_rate, - rate, slots, word_size); + rate, slots, slot_width); if (bclk_div < 0) { dev_err(dai->dev, "Unsupported BCLK divider: %d\n", bclk_div); return -EINVAL; @@ -458,6 +458,7 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, { struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai); unsigned int word_size = params_width(params); + unsigned int slot_width = params_physical_width(params); unsigned int channels = params_channels(params); unsigned int slots = channels; int ret, sr, wss; @@ -467,7 +468,7 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, slots = i2s->slots;
if (i2s->slot_width) - word_size = i2s->slot_width; + slot_width = i2s->slot_width;
ret = i2s->variant->set_chan_cfg(i2s, params); if (ret < 0) { @@ -490,14 +491,15 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, if (sr < 0) return -EINVAL;
- wss = i2s->variant->get_wss(i2s, word_size); + wss = i2s->variant->get_wss(i2s, slot_width); if (wss < 0) return -EINVAL;
regmap_field_write(i2s->field_fmt_wss, wss); regmap_field_write(i2s->field_fmt_sr, sr);
- return sun4i_i2s_set_clk_rate(dai, params_rate(params), slots, word_size); + return sun4i_i2s_set_clk_rate(dai, params_rate(params), + slots, slot_width); }
static int sun4i_i2s_set_soc_fmt(const struct sun4i_i2s *i2s,
From: Maxime Ripard maxime.ripard@bootlin.com
The LRCK period field in the FMT0 register holds the number of LRCK period for one channel in I2S mode.
This has been hardcoded to 32, while it really should be the physical width of the format, which creates an improper clock when using a 16bit format, with the i2s controller as LRCK master.
Fixes: 7d2993811a1e ("ASoC: sun4i-i2s: Add support for H3") Signed-off-by: Maxime Ripard maxime.ripard@bootlin.com --- sound/soc/sunxi/sun4i-i2s.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index cdc3fa60ff33..9ef784b8867c 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -359,7 +359,7 @@ static int sun4i_i2s_set_clk_rate(struct snd_soc_dai *dai, 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(32)); + SUN8I_I2S_FMT0_LRCK_PERIOD(slot_width));
return 0; }
From: Maxime Ripard maxime.ripard@bootlin.com
The sun4i_i2s_hw_params function already has a variable holding the value returned by params_width, so let's just use that variable instead of calling params_width multiple times.
Signed-off-by: Maxime Ripard maxime.ripard@bootlin.com --- sound/soc/sunxi/sun4i-i2s.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index 9ef784b8867c..69162af9fd65 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -487,7 +487,7 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, } i2s->playback_dma_data.addr_width = width;
- sr = i2s->variant->get_sr(i2s, params_width(params)); + sr = i2s->variant->get_sr(i2s, word_size); if (sr < 0) return -EINVAL;
From: Maxime Ripard maxime.ripard@bootlin.com
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 --- sound/soc/sunxi/sun4i-i2s.c | 44 ++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 9 deletions(-)
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index 69162af9fd65..57bf2a33753e 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -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),
participants (1)
-
Maxime Ripard