[alsa-devel] [PATCH v5 00/15] ASoC: sun4i-i2s: Updates to the driver
From: Marcus Cooper codekipper@gmail.com
Hi All,
here is a patch series to add some improvements to the sun4i-i2s driver found whilst getting slave clocking and hdmi audio working on the newer SoCs. As the LibreELEC project is progressing extremely well then there has been some activity getting H6 SoC support with surround sound working and these changes are also included.
The functionality included with the new patch set has been extended to cover more sample resolutions, multi-lane data output for HDMI audio and some bug fixes that have been discovered along the way. I have changed some of the original reg fields into function calls as this made it easier to setup for multi-channel audio especially across different SoCs.
I can see more usage of the tdm property since I last attempted to push these patches and the examples currently in mainline sort of the opposite to what I'm trying to achieve. When we first started looking at the i2s driver, the codecs that we were using allowed for the frame width to be determined based on the sampling resolution but in most use cases it seems that a fixed width is required(my highest priority should be to get HDMI audio support in). We're using the tdm property to override the old way to calculate the frame width. What I've seen in what has already been mainlined is that the i2s driver has a frame width that is fixed to 32 bits and this can be overridden using the tdm property.
My test branch for this can be found at https://github.com/codekipper/linux-sunxi/commits/upstream-i2s , I've been using a Pine64 to test with; validating the new SoC block with HDMI audio and ensuring that I've not broken the old block by making sure that the audio codec still works. If we able to get the first three patches delivered then that is enough for HDMI audio support on the newer SoCs(H3, A64 etc).
I still need to investigate the FIFO syncing issues which i've not had a chance to change or address the concerns that broonie and wens brought up. This change has been moved to the top of the patch stack. I would also like to make the multi-channel audio and audio mapping more configurable via the device tree. Currently what is implemented suites our current needs.
BR, CK
--- v5 changes compared to v4 are: - removed delivered patches. - Added more details to commit messages. - replaced some reg fields with function calls. - Added DSP_A and DSP_B support for H3 and later SoCs. - Added support for the Allwinner H6.
v4 changes compared to v3 are: - Moved patches around so that the more controversial of patches are at the top of the stack. - Added more details to commit messages. - Fixed 20bit audio PCM format to use 4 bytes. - Reduced number of flags used to indicate a new SoC.
v3 changes compared to v2 are: - added back slave mode changes - added back the use of tdm properties - changes to regmap and caching - removed loopback functionality - fixes to the channel offset mask
v2 changes compared to v1 are: - removed slave mode changes which didn't set mclk and bclk div. - removed use of tdm and now use a dedicated property. - fix commit message to better explain reason for sign extending - add divider calculations for newer SoCs. - add support for multi-lane i2s data output. - add support for 20, 24 and 32 bit samples. - add loopback property so blocks can be tested without a codec.
--- Jernej Skrabec (3): clk: sunxi-ng: h6: Allow I2S to change parent rate dt-bindings: ASoC: sun4i-i2s: Add H6 compatible ASoC: sun4i-i2s: Add support for H6 I2S
Marcus Cooper (12): ASoC: sun4i-i2s: Add regmap field to sign extend sample ASoC: sun4i-i2s: Add set_tdm_slot functionality ASoC: sun4i-i2s: Correct divider calculations ASoC: sun4i-i2s: Support more formats on newer SoCs ASoC: sun4i-i2s: Add functions for RX and TX channel offsets ASoC: sun4i-i2s: Add functions for RX and TX channel enables ASoC: sun4i-i2s: Add functions for RX and TX channel selects ASoC: sun4i-i2s: Add functions for channel mapping ASoC: sun4i-i2s: Add multi-lane functionality ASoC: sun4i-i2s: Add multichannel functionality ASoc: sun4i-i2s: Add 20, 24 and 32 bit support ASoC: sun4i-i2s: Adjust regmap settings
.../sound/allwinner,sun4i-a10-i2s.yaml | 2 + drivers/clk/sunxi-ng/ccu-sun50i-h6.c | 8 +- sound/soc/sunxi/sun4i-i2s.c | 690 ++++++++++++++---- 3 files changed, 542 insertions(+), 158 deletions(-)
From: Marcus Cooper codekipper@gmail.com
On the newer SoCs such as the H3 and A64 this is set by default to transfer a 0 after each sample in each slot. However the A10 and A20 SoCs that this driver was developed on had a default setting where it padded the audio gain with zeros.
This isn't a problem whilst we have only support for 16bit audio but with larger sample resolution rates in the pipeline then SEXT bits should be cleared so that they also pad at the LSB. Without this the audio gets distorted.
Signed-off-by: Marcus Cooper codekipper@gmail.com --- sound/soc/sunxi/sun4i-i2s.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index 793457394efe..8201334a059b 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -135,6 +135,7 @@ 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_fmt_sext: regmap field to set the sign extension. * @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. @@ -159,6 +160,7 @@ 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_fmt_sext; struct reg_field field_txchanmap; struct reg_field field_rxchanmap; struct reg_field field_txchansel; @@ -186,6 +188,7 @@ 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_fmt_sext; struct regmap_field *field_txchanmap; struct regmap_field *field_rxchanmap; struct regmap_field *field_txchansel; @@ -345,6 +348,9 @@ static int sun4i_i2s_set_clk_rate(struct snd_soc_dai *dai, SUN8I_I2S_FMT0_LRCK_PERIOD_MASK, SUN8I_I2S_FMT0_LRCK_PERIOD(32));
+ /* Set sign extension to pad out LSB with 0 */ + regmap_field_write(i2s->field_fmt_sext, 0); + return 0; }
@@ -917,6 +923,7 @@ 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_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG, 8, 8), .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), @@ -936,6 +943,7 @@ 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_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG, 8, 8), .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), @@ -979,6 +987,7 @@ 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_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_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), @@ -998,6 +1007,7 @@ 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_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG, 8, 8), .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), @@ -1045,6 +1055,12 @@ 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_fmt_sext = + devm_regmap_field_alloc(dev, i2s->regmap, + i2s->variant->field_fmt_sext); + if (IS_ERR(i2s->field_fmt_sext)) + return PTR_ERR(i2s->field_fmt_sext); + i2s->field_txchanmap = devm_regmap_field_alloc(dev, i2s->regmap, i2s->variant->field_txchanmap);
Hi,
On Wed, Aug 14, 2019 at 08:08:40AM +0200, codekipper@gmail.com wrote:
From: Marcus Cooper codekipper@gmail.com
On the newer SoCs such as the H3 and A64 this is set by default to transfer a 0 after each sample in each slot. However the A10 and A20 SoCs that this driver was developed on had a default setting where it padded the audio gain with zeros.
This isn't a problem whilst we have only support for 16bit audio but with larger sample resolution rates in the pipeline then SEXT bits should be cleared so that they also pad at the LSB. Without this the audio gets distorted.
Signed-off-by: Marcus Cooper codekipper@gmail.com
sound/soc/sunxi/sun4i-i2s.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index 793457394efe..8201334a059b 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -135,6 +135,7 @@ 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_fmt_sext: regmap field to set the sign extension.
- @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.
@@ -159,6 +160,7 @@ 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_fmt_sext; struct reg_field field_txchanmap; struct reg_field field_rxchanmap; struct reg_field field_txchansel;
@@ -186,6 +188,7 @@ 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_fmt_sext; struct regmap_field *field_txchanmap; struct regmap_field *field_rxchanmap; struct regmap_field *field_txchansel;
@@ -345,6 +348,9 @@ static int sun4i_i2s_set_clk_rate(struct snd_soc_dai *dai, SUN8I_I2S_FMT0_LRCK_PERIOD_MASK, SUN8I_I2S_FMT0_LRCK_PERIOD(32));
- /* Set sign extension to pad out LSB with 0 */
- regmap_field_write(i2s->field_fmt_sext, 0);
- return 0;
}
@@ -917,6 +923,7 @@ 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_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG, 8, 8), .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),
@@ -936,6 +943,7 @@ 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_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG, 8, 8), .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),
@@ -979,6 +987,7 @@ 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_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_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),
@@ -998,6 +1007,7 @@ 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_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG, 8, 8), .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),
You're missing the A83t here
Maxime
-- Maxime Ripard, Bootlin Embedded Linux and Kernel engineering https://bootlin.com
On Wed, 14 Aug 2019 at 13:08, Maxime Ripard maxime.ripard@bootlin.com wrote:
Hi,
On Wed, Aug 14, 2019 at 08:08:40AM +0200, codekipper@gmail.com wrote:
From: Marcus Cooper codekipper@gmail.com
On the newer SoCs such as the H3 and A64 this is set by default to transfer a 0 after each sample in each slot. However the A10 and A20 SoCs that this driver was developed on had a default setting where it padded the audio gain with zeros.
This isn't a problem whilst we have only support for 16bit audio but with larger sample resolution rates in the pipeline then SEXT bits should be cleared so that they also pad at the LSB. Without this the audio gets distorted.
Signed-off-by: Marcus Cooper codekipper@gmail.com
sound/soc/sunxi/sun4i-i2s.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index 793457394efe..8201334a059b 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -135,6 +135,7 @@ 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_fmt_sext: regmap field to set the sign extension.
- @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.
@@ -159,6 +160,7 @@ 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_fmt_sext; struct reg_field field_txchanmap; struct reg_field field_rxchanmap; struct reg_field field_txchansel;
@@ -186,6 +188,7 @@ 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_fmt_sext; struct regmap_field *field_txchanmap; struct regmap_field *field_rxchanmap; struct regmap_field *field_txchansel;
@@ -345,6 +348,9 @@ static int sun4i_i2s_set_clk_rate(struct snd_soc_dai *dai, SUN8I_I2S_FMT0_LRCK_PERIOD_MASK, SUN8I_I2S_FMT0_LRCK_PERIOD(32));
/* Set sign extension to pad out LSB with 0 */
regmap_field_write(i2s->field_fmt_sext, 0);
return 0;
}
@@ -917,6 +923,7 @@ 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_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG, 8, 8), .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),
@@ -936,6 +943,7 @@ 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_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG, 8, 8), .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),
@@ -979,6 +987,7 @@ 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_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_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),
@@ -998,6 +1007,7 @@ 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_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG, 8, 8), .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),
You're missing the A83t here
ARRGGGHHHHH...ACK...thanks, CK
Maxime
-- Maxime Ripard, Bootlin Embedded Linux and Kernel engineering https://bootlin.com
From: Marcus Cooper codekipper@gmail.com
Codecs without a control connection such as i2s based HDMI audio and the Pine64 DAC require a different amount of bit clocks per frame than what is calculated by the sample width. Use the tdm slot bindings to provide this mechanism.
Signed-off-by: Marcus Cooper codekipper@gmail.com --- sound/soc/sunxi/sun4i-i2s.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-)
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index 8201334a059b..7c37b6291df0 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -195,6 +195,9 @@ struct sun4i_i2s { struct regmap_field *field_rxchansel;
const struct sun4i_i2s_quirks *variant; + + unsigned int tdm_slots; + unsigned int slot_width; };
struct sun4i_i2s_clk_div { @@ -346,7 +349,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(word_size));
/* Set sign extension to pad out LSB with 0 */ regmap_field_write(i2s->field_fmt_sext, 0); @@ -450,7 +453,8 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, regmap_field_write(i2s->field_fmt_sr, sr);
return sun4i_i2s_set_clk_rate(dai, params_rate(params), - params_width(params)); + i2s->tdm_slots ? + i2s->slot_width : params_width(params)); }
static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) @@ -693,10 +697,25 @@ static int sun4i_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, return 0; }
+static int sun4i_i2s_set_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, + unsigned int rx_mask, + int slots, int width) +{ + struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai); + + i2s->tdm_slots = slots; + + i2s->slot_width = width; + + return 0; +} + static const struct snd_soc_dai_ops sun4i_i2s_dai_ops = { .hw_params = sun4i_i2s_hw_params, .set_fmt = sun4i_i2s_set_fmt, .set_sysclk = sun4i_i2s_set_sysclk, + .set_tdm_slot = sun4i_i2s_set_dai_tdm_slot, .trigger = sun4i_i2s_trigger, };
Hi,
On Wed, Aug 14, 2019 at 08:08:41AM +0200, codekipper@gmail.com wrote:
From: Marcus Cooper codekipper@gmail.com
Codecs without a control connection such as i2s based HDMI audio and the Pine64 DAC require a different amount of bit clocks per frame than what is calculated by the sample width. Use the tdm slot bindings to provide this mechanism.
Signed-off-by: Marcus Cooper codekipper@gmail.com
sound/soc/sunxi/sun4i-i2s.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-)
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index 8201334a059b..7c37b6291df0 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -195,6 +195,9 @@ struct sun4i_i2s { struct regmap_field *field_rxchansel;
const struct sun4i_i2s_quirks *variant;
- unsigned int tdm_slots;
- unsigned int slot_width;
};
struct sun4i_i2s_clk_div { @@ -346,7 +349,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(word_size));
/* Set sign extension to pad out LSB with 0 */ regmap_field_write(i2s->field_fmt_sext, 0);
@@ -450,7 +453,8 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, regmap_field_write(i2s->field_fmt_sr, sr);
return sun4i_i2s_set_clk_rate(dai, params_rate(params),
params_width(params));
i2s->tdm_slots ?
i2s->slot_width : params_width(params));
This is slightly more complicated than that.
On the H3 (and all related ones), the CHAN_CFG_TX_SLOT_NUM and _RX_SLOT_NUM fields in the CHAN_CFG register need to be set to the number of slots as well.
Maxime
-- Maxime Ripard, Bootlin Embedded Linux and Kernel engineering https://bootlin.com
On Wed, 14 Aug 2019 at 13:08, Maxime Ripard maxime.ripard@bootlin.com wrote:
Hi,
On Wed, Aug 14, 2019 at 08:08:41AM +0200, codekipper@gmail.com wrote:
From: Marcus Cooper codekipper@gmail.com
Codecs without a control connection such as i2s based HDMI audio and the Pine64 DAC require a different amount of bit clocks per frame than what is calculated by the sample width. Use the tdm slot bindings to provide this mechanism.
Signed-off-by: Marcus Cooper codekipper@gmail.com
sound/soc/sunxi/sun4i-i2s.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-)
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index 8201334a059b..7c37b6291df0 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -195,6 +195,9 @@ struct sun4i_i2s { struct regmap_field *field_rxchansel;
const struct sun4i_i2s_quirks *variant;
unsigned int tdm_slots;
unsigned int slot_width;
};
struct sun4i_i2s_clk_div { @@ -346,7 +349,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(word_size)); /* Set sign extension to pad out LSB with 0 */ regmap_field_write(i2s->field_fmt_sext, 0);
@@ -450,7 +453,8 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, regmap_field_write(i2s->field_fmt_sr, sr);
return sun4i_i2s_set_clk_rate(dai, params_rate(params),
params_width(params));
i2s->tdm_slots ?
i2s->slot_width : params_width(params));
This is slightly more complicated than that.
At this point we're only supporting 2 channels with fixed slot settings. I've added a comment to state that we're using the tdm_slot at the moment as an indicator to override the slot width. Do you think that is enough for now?.
Thanks, CK
On the H3 (and all related ones), the CHAN_CFG_TX_SLOT_NUM and _RX_SLOT_NUM fields in the CHAN_CFG register need to be set to the number of slots as well.
Maxime
-- Maxime Ripard, Bootlin Embedded Linux and Kernel engineering https://bootlin.com
On Wed, Aug 14, 2019 at 08:08:41AM +0200, codekipper@gmail.com wrote:
From: Marcus Cooper codekipper@gmail.com
Codecs without a control connection such as i2s based HDMI audio and the Pine64 DAC require a different amount of bit clocks per frame than
This isn't a universal property of CODECs without a control, and it's something that CODECs with control can require too.
return sun4i_i2s_set_clk_rate(dai, params_rate(params),
params_width(params));
i2s->tdm_slots ?
i2s->slot_width : params_width(params));
Please write normal conditional statements unless there's a strong reason to do otherwise, it makes things more legible.
+static int sun4i_i2s_set_dai_tdm_slot(struct snd_soc_dai *dai,
unsigned int tx_mask,
unsigned int rx_mask,
int slots, int width)
+{
- struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
- i2s->tdm_slots = slots;
- i2s->slot_width = width;
- return 0;
+}
No validation of the parameters here?
On Wed, 14 Aug 2019 at 11:30, Mark Brown broonie@kernel.org wrote:
On Wed, Aug 14, 2019 at 08:08:41AM +0200, codekipper@gmail.com wrote:
From: Marcus Cooper codekipper@gmail.com
Codecs without a control connection such as i2s based HDMI audio and the Pine64 DAC require a different amount of bit clocks per frame than
This isn't a universal property of CODECs without a control, and it's something that CODECs with control can require too.
ACK
return sun4i_i2s_set_clk_rate(dai, params_rate(params),
params_width(params));
i2s->tdm_slots ?
i2s->slot_width : params_width(params));
Please write normal conditional statements unless there's a strong reason to do otherwise, it makes things more legible.
ACK
+static int sun4i_i2s_set_dai_tdm_slot(struct snd_soc_dai *dai,
unsigned int tx_mask,
unsigned int rx_mask,
int slots, int width)
+{
struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
i2s->tdm_slots = slots;
i2s->slot_width = width;
return 0;
+}
No validation of the parameters here?
ACK Thanks, CK
From: Marcus Cooper codekipper@gmail.com
The clock division circuitry is different on the H3 and later SoCs. The division of bclk is now based on pll2.
Signed-off-by: Marcus Cooper codekipper@gmail.com --- sound/soc/sunxi/sun4i-i2s.c | 73 +++++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 24 deletions(-)
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index 7c37b6291df0..34f31439ae7b 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -127,8 +127,6 @@ struct sun4i_i2s; * @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. - * @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. @@ -150,8 +148,6 @@ struct sun4i_i2s_quirks { bool has_chsel_offset; 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; @@ -212,7 +208,25 @@ static const struct sun4i_i2s_clk_div sun4i_i2s_bclk_div[] = { { .div = 8, .val = 3 }, { .div = 12, .val = 4 }, { .div = 16, .val = 5 }, - /* TODO - extend divide ratio supported by newer SoCs */ +}; + +static const struct sun4i_i2s_clk_div sun8i_i2s_clk_div[] = { + { .div = 0, .val = 0 }, + { .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 const struct sun4i_i2s_clk_div sun4i_i2s_mclk_div[] = { @@ -224,21 +238,21 @@ static const struct sun4i_i2s_clk_div sun4i_i2s_mclk_div[] = { { .div = 12, .val = 5 }, { .div = 16, .val = 6 }, { .div = 24, .val = 7 }, - /* TODO - extend divide ratio supported by newer SoCs */ };
static int sun4i_i2s_get_bclk_div(struct sun4i_i2s *i2s, unsigned int oversample_rate, - unsigned int word_size) + unsigned int word_size, + const struct sun4i_i2s_clk_div *bdiv, + unsigned int size) { int div = oversample_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 < size; i++) { if (bdiv->div == div) return bdiv->val; + bdiv++; }
return -EINVAL; @@ -247,16 +261,17 @@ static int sun4i_i2s_get_bclk_div(struct sun4i_i2s *i2s, static int sun4i_i2s_get_mclk_div(struct sun4i_i2s *i2s, unsigned int oversample_rate, unsigned int module_rate, - unsigned int sampling_rate) + unsigned int sampling_rate, + const struct sun4i_i2s_clk_div *mdiv, + unsigned int size) { int div = module_rate / sampling_rate / oversample_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 < size; i++) { if (mdiv->div == div) return mdiv->val; + mdiv++; }
return -EINVAL; @@ -321,24 +336,36 @@ static int sun4i_i2s_set_clk_rate(struct snd_soc_dai *dai, return -EINVAL; }
- bclk_div = sun4i_i2s_get_bclk_div(i2s, oversample_rate, - word_size); + if (i2s->variant->has_fmt_set_lrck_period) + bclk_div = sun4i_i2s_get_bclk_div(i2s, clk_rate / rate, + word_size, + sun8i_i2s_clk_div, + ARRAY_SIZE(sun8i_i2s_clk_div)); + else + bclk_div = sun4i_i2s_get_bclk_div(i2s, oversample_rate, + word_size, + sun4i_i2s_bclk_div, + ARRAY_SIZE(sun4i_i2s_bclk_div)); if (bclk_div < 0) { dev_err(dai->dev, "Unsupported BCLK divider: %d\n", bclk_div); return -EINVAL; }
- mclk_div = sun4i_i2s_get_mclk_div(i2s, oversample_rate, - clk_rate, rate); + if (i2s->variant->has_fmt_set_lrck_period) + mclk_div = sun4i_i2s_get_mclk_div(i2s, oversample_rate, + clk_rate, rate, + sun8i_i2s_clk_div, + ARRAY_SIZE(sun8i_i2s_clk_div)); + else + mclk_div = sun4i_i2s_get_mclk_div(i2s, oversample_rate, + clk_rate, rate, + sun4i_i2s_mclk_div, + ARRAY_SIZE(sun4i_i2s_mclk_div)); if (mclk_div < 0) { dev_err(dai->dev, "Unsupported MCLK divider: %d\n", mclk_div); 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)); @@ -994,8 +1021,6 @@ 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, .has_chcfg = true, .has_chsel_tx_chen = true,
Hi,
(I just noticed this, but can you update my mail address, it's not @free-electrons for quite a while, you probably want to change your scripts to use mripard@kernel.org)
On Wed, Aug 14, 2019 at 08:08:42AM +0200, codekipper@gmail.com wrote:
From: Marcus Cooper codekipper@gmail.com
The clock division circuitry is different on the H3 and later SoCs. The division of bclk is now based on pll2.
Signed-off-by: Marcus Cooper codekipper@gmail.com
sound/soc/sunxi/sun4i-i2s.c | 73 +++++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 24 deletions(-)
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index 7c37b6291df0..34f31439ae7b 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -127,8 +127,6 @@ struct sun4i_i2s;
- @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.
- @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.
@@ -150,8 +148,6 @@ struct sun4i_i2s_quirks { bool has_chsel_offset; 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;
@@ -212,7 +208,25 @@ static const struct sun4i_i2s_clk_div sun4i_i2s_bclk_div[] = { { .div = 8, .val = 3 }, { .div = 12, .val = 4 }, { .div = 16, .val = 5 },
- /* TODO - extend divide ratio supported by newer SoCs */
+};
+static const struct sun4i_i2s_clk_div sun8i_i2s_clk_div[] = {
- { .div = 0, .val = 0 },
Having a divider of 0 seems like a bad idea.
- { .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 const struct sun4i_i2s_clk_div sun4i_i2s_mclk_div[] = { @@ -224,21 +238,21 @@ static const struct sun4i_i2s_clk_div sun4i_i2s_mclk_div[] = { { .div = 12, .val = 5 }, { .div = 16, .val = 6 }, { .div = 24, .val = 7 },
- /* TODO - extend divide ratio supported by newer SoCs */
};
static int sun4i_i2s_get_bclk_div(struct sun4i_i2s *i2s, unsigned int oversample_rate,
unsigned int word_size)
unsigned int word_size,
const struct sun4i_i2s_clk_div *bdiv,
unsigned int size)
Wouldn't it be simpler to just have the divider list in the variant structure? It would avoid having to refactor all the functions, and it's not like it's really going to change from one call to another anyway.
Maxime
-- Maxime Ripard, Bootlin Embedded Linux and Kernel engineering https://bootlin.com
On Wed, Aug 14, 2019 at 08:08:42AM +0200, codekipper@gmail.com wrote:
- if (i2s->variant->has_fmt_set_lrck_period)
bclk_div = sun4i_i2s_get_bclk_div(i2s, clk_rate / rate,
word_size,
sun8i_i2s_clk_div,
ARRAY_SIZE(sun8i_i2s_clk_div));
- else
bclk_div = sun4i_i2s_get_bclk_div(i2s, oversample_rate,
word_size,
sun4i_i2s_bclk_div,
ARRAY_SIZE(sun4i_i2s_bclk_div));
Are we sure there'll never be any new variants which would make a switch statement for the variant work better?
From: Marcus Cooper codekipper@gmail.com
There is a need to support more formats on the newer SoCs(H3 and later). Extend the formats supported to include DSP_A and DSP_B modes.
Signed-off-by: Marcus Cooper codekipper@gmail.com --- sound/soc/sunxi/sun4i-i2s.c | 87 +++++++++++++++++++++++++++---------- 1 file changed, 63 insertions(+), 24 deletions(-)
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index 34f31439ae7b..3553c17318b0 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -27,6 +27,8 @@ #define SUN4I_I2S_CTRL_MODE_MASK BIT(5) #define SUN4I_I2S_CTRL_MODE_SLAVE (1 << 5) #define SUN4I_I2S_CTRL_MODE_MASTER (0 << 5) +#define SUN4I_I2S_CTRL_PCM BIT(4) +#define SUN4I_I2S_CTRL_LOOP BIT(3) #define SUN4I_I2S_CTRL_TX_EN BIT(2) #define SUN4I_I2S_CTRL_RX_EN BIT(1) #define SUN4I_I2S_CTRL_GL_EN BIT(0) @@ -91,6 +93,9 @@ /* Defines required for sun8i-h3 support */ #define SUN8I_I2S_CTRL_BCLK_OUT BIT(18) #define SUN8I_I2S_CTRL_LRCK_OUT BIT(17) +#define SUN8I_I2S_CTRL_MODE_RIGHT_J (2 << 0) +#define SUN8I_I2S_CTRL_MODE_I2S_LEFT_J (1 << 0) +#define SUN8I_I2S_CTRL_MODE_PCM (0 << 0)
#define SUN8I_I2S_FMT0_LRCK_PERIOD_MASK GENMASK(17, 8) #define SUN8I_I2S_FMT0_LRCK_PERIOD(period) ((period - 1) << 8) @@ -164,6 +169,7 @@ struct sun4i_i2s_quirks {
s8 (*get_sr)(const struct sun4i_i2s *, int); s8 (*get_wss)(const struct sun4i_i2s *, int); + int (*set_format)(struct sun4i_i2s *, unsigned int); };
struct sun4i_i2s { @@ -194,6 +200,7 @@ struct sun4i_i2s {
unsigned int tdm_slots; unsigned int slot_width; + unsigned int offset; };
struct sun4i_i2s_clk_div { @@ -484,19 +491,14 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, i2s->slot_width : params_width(params)); }
-static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +static int sun4i_i2s_set_format(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; @@ -505,32 +507,64 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 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_field_write(i2s->field_fmt_mode, val); + + return 0; +} + +static int sun8i_i2s_set_format(struct sun4i_i2s *i2s, unsigned int fmt) +{ + u32 val;
- regmap_update_bits(i2s->regmap, SUN8I_I2S_RX_CHAN_SEL_REG, - SUN8I_I2S_TX_CHAN_OFFSET_MASK, - SUN8I_I2S_TX_CHAN_OFFSET(offset)); + /* DAI Mode */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + i2s->offset = 1; + case SND_SOC_DAIFMT_LEFT_J: + val = SUN8I_I2S_CTRL_MODE_I2S_LEFT_J; + break; + case SND_SOC_DAIFMT_RIGHT_J: + val = SUN8I_I2S_CTRL_MODE_RIGHT_J; + break; + case SND_SOC_DAIFMT_DSP_A: + i2s->offset = 1; + case SND_SOC_DAIFMT_DSP_B: + val = SUN8I_I2S_CTRL_MODE_PCM; + break; + + default: + return -EINVAL; }
+ /* + * bclk offset determines whether i2s or LJ if in i2s mode and + * DSP_A or DSP_B if in PCM mode. + */ + i2s->variant->set_txchanoffset(i2s, 0); + i2s->variant->set_rxchanoffset(i2s); + regmap_field_write(i2s->field_fmt_mode, 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 val; + u32 bclk_polarity = SUN4I_I2S_FMT0_POLARITY_NORMAL; + u32 lrclk_polarity = SUN4I_I2S_FMT0_POLARITY_NORMAL; + + /* Set DAI Mode */ + if (i2s->variant->set_format(i2s, fmt) != 0) { + dev_err(dai->dev, "Unsupported format: %d\n", + fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + /* DAI clock polarity */ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_IB_IF: @@ -976,6 +1010,7 @@ static const struct sun4i_i2s_quirks sun4i_a10_i2s_quirks = { .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_format = sun4i_i2s_set_format, };
static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = { @@ -996,6 +1031,7 @@ static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = { .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_format = sun4i_i2s_set_format, };
static const struct sun4i_i2s_quirks sun8i_a83t_i2s_quirks = { @@ -1015,6 +1051,7 @@ static const struct sun4i_i2s_quirks sun8i_a83t_i2s_quirks = { .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_format = sun4i_i2s_set_format, };
static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = { @@ -1038,6 +1075,7 @@ static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = { .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_format = sun8i_i2s_set_format, };
static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = { @@ -1058,6 +1096,7 @@ static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = { .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_format = sun4i_i2s_set_format, };
static int sun4i_i2s_init_regmap_fields(struct device *dev,
On Wed, Aug 14, 2019 at 08:08:43AM +0200, codekipper@gmail.com wrote:
From: Marcus Cooper codekipper@gmail.com
There is a need to support more formats on the newer SoCs(H3 and later). Extend the formats supported to include DSP_A and DSP_B modes.
Signed-off-by: Marcus Cooper codekipper@gmail.com
sound/soc/sunxi/sun4i-i2s.c | 87 +++++++++++++++++++++++++++---------- 1 file changed, 63 insertions(+), 24 deletions(-)
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index 34f31439ae7b..3553c17318b0 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -27,6 +27,8 @@ #define SUN4I_I2S_CTRL_MODE_MASK BIT(5) #define SUN4I_I2S_CTRL_MODE_SLAVE (1 << 5) #define SUN4I_I2S_CTRL_MODE_MASTER (0 << 5) +#define SUN4I_I2S_CTRL_PCM BIT(4) +#define SUN4I_I2S_CTRL_LOOP BIT(3) #define SUN4I_I2S_CTRL_TX_EN BIT(2) #define SUN4I_I2S_CTRL_RX_EN BIT(1) #define SUN4I_I2S_CTRL_GL_EN BIT(0) @@ -91,6 +93,9 @@ /* Defines required for sun8i-h3 support */ #define SUN8I_I2S_CTRL_BCLK_OUT BIT(18) #define SUN8I_I2S_CTRL_LRCK_OUT BIT(17) +#define SUN8I_I2S_CTRL_MODE_RIGHT_J (2 << 0) +#define SUN8I_I2S_CTRL_MODE_I2S_LEFT_J (1 << 0) +#define SUN8I_I2S_CTRL_MODE_PCM (0 << 0)
#define SUN8I_I2S_FMT0_LRCK_PERIOD_MASK GENMASK(17, 8) #define SUN8I_I2S_FMT0_LRCK_PERIOD(period) ((period - 1) << 8) @@ -164,6 +169,7 @@ struct sun4i_i2s_quirks {
s8 (*get_sr)(const struct sun4i_i2s *, int); s8 (*get_wss)(const struct sun4i_i2s *, int);
- int (*set_format)(struct sun4i_i2s *, unsigned int);
};
struct sun4i_i2s { @@ -194,6 +200,7 @@ struct sun4i_i2s {
unsigned int tdm_slots; unsigned int slot_width;
- unsigned int offset;
};
struct sun4i_i2s_clk_div { @@ -484,19 +491,14 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, i2s->slot_width : params_width(params)); }
-static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +static int sun4i_i2s_set_format(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;
@@ -505,32 +507,64 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 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_field_write(i2s->field_fmt_mode, val);
- return 0;
+}
+static int sun8i_i2s_set_format(struct sun4i_i2s *i2s, unsigned int fmt) +{
- u32 val;
regmap_update_bits(i2s->regmap, SUN8I_I2S_RX_CHAN_SEL_REG,
SUN8I_I2S_TX_CHAN_OFFSET_MASK,
SUN8I_I2S_TX_CHAN_OFFSET(offset));
/* DAI Mode */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
i2s->offset = 1;
case SND_SOC_DAIFMT_LEFT_J:
val = SUN8I_I2S_CTRL_MODE_I2S_LEFT_J;
break;
case SND_SOC_DAIFMT_RIGHT_J:
val = SUN8I_I2S_CTRL_MODE_RIGHT_J;
break;
case SND_SOC_DAIFMT_DSP_A:
i2s->offset = 1;
case SND_SOC_DAIFMT_DSP_B:
val = SUN8I_I2S_CTRL_MODE_PCM;
break;
default:
return -EINVAL;
}
/*
* bclk offset determines whether i2s or LJ if in i2s mode and
* DSP_A or DSP_B if in PCM mode.
*/
i2s->variant->set_txchanoffset(i2s, 0);
i2s->variant->set_rxchanoffset(i2s);
regmap_field_write(i2s->field_fmt_mode, val);
It's a bit more complicated in the sun8i case. The LRCK period also needs to be changed when in PCM / DSP_* mode since it changes from a number of periods for one channel to a number of periods for all the channels.
I have patches that still need a bit of rework and take care of all of that, I'll try to post them by the end of the week
Maxime
-- Maxime Ripard, Bootlin Embedded Linux and Kernel engineering https://bootlin.com
On Wed, 14 Aug 2019 at 13:08, Maxime Ripard maxime.ripard@bootlin.com wrote:
On Wed, Aug 14, 2019 at 08:08:43AM +0200, codekipper@gmail.com wrote:
From: Marcus Cooper codekipper@gmail.com
There is a need to support more formats on the newer SoCs(H3 and later). Extend the formats supported to include DSP_A and DSP_B modes.
Signed-off-by: Marcus Cooper codekipper@gmail.com
sound/soc/sunxi/sun4i-i2s.c | 87 +++++++++++++++++++++++++++---------- 1 file changed, 63 insertions(+), 24 deletions(-)
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index 34f31439ae7b..3553c17318b0 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -27,6 +27,8 @@ #define SUN4I_I2S_CTRL_MODE_MASK BIT(5) #define SUN4I_I2S_CTRL_MODE_SLAVE (1 << 5) #define SUN4I_I2S_CTRL_MODE_MASTER (0 << 5) +#define SUN4I_I2S_CTRL_PCM BIT(4) +#define SUN4I_I2S_CTRL_LOOP BIT(3) #define SUN4I_I2S_CTRL_TX_EN BIT(2) #define SUN4I_I2S_CTRL_RX_EN BIT(1) #define SUN4I_I2S_CTRL_GL_EN BIT(0) @@ -91,6 +93,9 @@ /* Defines required for sun8i-h3 support */ #define SUN8I_I2S_CTRL_BCLK_OUT BIT(18) #define SUN8I_I2S_CTRL_LRCK_OUT BIT(17) +#define SUN8I_I2S_CTRL_MODE_RIGHT_J (2 << 0) +#define SUN8I_I2S_CTRL_MODE_I2S_LEFT_J (1 << 0) +#define SUN8I_I2S_CTRL_MODE_PCM (0 << 0)
#define SUN8I_I2S_FMT0_LRCK_PERIOD_MASK GENMASK(17, 8) #define SUN8I_I2S_FMT0_LRCK_PERIOD(period) ((period - 1) << 8) @@ -164,6 +169,7 @@ struct sun4i_i2s_quirks {
s8 (*get_sr)(const struct sun4i_i2s *, int); s8 (*get_wss)(const struct sun4i_i2s *, int);
int (*set_format)(struct sun4i_i2s *, unsigned int);
};
struct sun4i_i2s { @@ -194,6 +200,7 @@ struct sun4i_i2s {
unsigned int tdm_slots; unsigned int slot_width;
unsigned int offset;
};
struct sun4i_i2s_clk_div { @@ -484,19 +491,14 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, i2s->slot_width : params_width(params)); }
-static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +static int sun4i_i2s_set_format(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;
@@ -505,32 +507,64 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 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_field_write(i2s->field_fmt_mode, val);
return 0;
+}
+static int sun8i_i2s_set_format(struct sun4i_i2s *i2s, unsigned int fmt) +{
u32 val;
regmap_update_bits(i2s->regmap, SUN8I_I2S_RX_CHAN_SEL_REG,
SUN8I_I2S_TX_CHAN_OFFSET_MASK,
SUN8I_I2S_TX_CHAN_OFFSET(offset));
/* DAI Mode */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
i2s->offset = 1;
case SND_SOC_DAIFMT_LEFT_J:
val = SUN8I_I2S_CTRL_MODE_I2S_LEFT_J;
break;
case SND_SOC_DAIFMT_RIGHT_J:
val = SUN8I_I2S_CTRL_MODE_RIGHT_J;
break;
case SND_SOC_DAIFMT_DSP_A:
i2s->offset = 1;
case SND_SOC_DAIFMT_DSP_B:
val = SUN8I_I2S_CTRL_MODE_PCM;
break;
default:
return -EINVAL; }
/*
* bclk offset determines whether i2s or LJ if in i2s mode and
* DSP_A or DSP_B if in PCM mode.
*/
i2s->variant->set_txchanoffset(i2s, 0);
i2s->variant->set_rxchanoffset(i2s);
regmap_field_write(i2s->field_fmt_mode, val);
It's a bit more complicated in the sun8i case. The LRCK period also needs to be changed when in PCM / DSP_* mode since it changes from a number of periods for one channel to a number of periods for all the channels.
Yeah I was thinking that but I don't have any hardware to test this with (been helping out someone trying to connect to a modem).
I have patches that still need a bit of rework and take care of all of that, I'll try to post them by the end of the week
This patch could be dropped for now or at least we just keep the offset parts in. BR, CK
Maxime
-- Maxime Ripard, Bootlin Embedded Linux and Kernel engineering https://bootlin.com
From: Marcus Cooper codekipper@gmail.com
Newer SoCs like the H6 have the channel offset bits in a different position to what is on the H3. As we will eventually add multi- channel support then create function calls as opposed to regmap fields to add support for different devices.
Signed-off-by: Marcus Cooper codekipper@gmail.com --- sound/soc/sunxi/sun4i-i2s.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-)
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index 3553c17318b0..4a748747ccd7 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -112,7 +112,7 @@ #define SUN8I_I2S_TX_CHAN_MAP_REG 0x44 #define SUN8I_I2S_TX_CHAN_SEL_REG 0x34 #define SUN8I_I2S_TX_CHAN_OFFSET_MASK GENMASK(13, 12) -#define SUN8I_I2S_TX_CHAN_OFFSET(offset) (offset << 12) +#define SUN8I_I2S_TX_CHAN_OFFSET(offset) ((offset) << 12) #define SUN8I_I2S_TX_CHAN_EN_MASK GENMASK(11, 4) #define SUN8I_I2S_TX_CHAN_EN(num_chan) (((1 << num_chan) - 1) << 4)
@@ -170,6 +170,8 @@ struct sun4i_i2s_quirks { s8 (*get_sr)(const struct sun4i_i2s *, int); s8 (*get_wss)(const struct sun4i_i2s *, int); int (*set_format)(struct sun4i_i2s *, unsigned int); + void (*set_txchanoffset)(const struct sun4i_i2s *, int); + void (*set_rxchanoffset)(const struct sun4i_i2s *); };
struct sun4i_i2s { @@ -424,6 +426,24 @@ static s8 sun8i_i2s_get_sr_wss(const struct sun4i_i2s *i2s, int width) return (width - 8) / 4 + 1; }
+static void sun8i_i2s_set_txchanoffset(const struct sun4i_i2s *i2s, int output) +{ + if (output >= 0 && output < 4) { + regmap_update_bits(i2s->regmap, + SUN8I_I2S_TX_CHAN_SEL_REG + (output * 4), + SUN8I_I2S_TX_CHAN_OFFSET_MASK, + SUN8I_I2S_TX_CHAN_OFFSET(i2s->offset)); + } +} + +static void sun8i_i2s_set_rxchanoffset(const struct sun4i_i2s *i2s) +{ + regmap_update_bits(i2s->regmap, + SUN8I_I2S_RX_CHAN_SEL_REG, + SUN8I_I2S_TX_CHAN_OFFSET_MASK, + SUN8I_I2S_TX_CHAN_OFFSET(i2s->offset)); +} + static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -1076,6 +1096,8 @@ static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = { .get_sr = sun8i_i2s_get_sr_wss, .get_wss = sun8i_i2s_get_sr_wss, .set_format = sun8i_i2s_set_format, + .set_txchanoffset = sun8i_i2s_set_txchanoffset, + .set_rxchanoffset = sun8i_i2s_set_rxchanoffset, };
static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = {
From: Marcus Cooper codekipper@gmail.com
Newer SoCs like the H6 have the channel enable bits in a different position to what is on the H3. As we will eventually add multi- channel support then create function calls as opposed to regmap fields to add support for different devices.
Signed-off-by: Marcus Cooper codekipper@gmail.com --- sound/soc/sunxi/sun4i-i2s.c | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-)
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index 4a748747ccd7..ad2ff83deeb7 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -172,6 +172,8 @@ struct sun4i_i2s_quirks { int (*set_format)(struct sun4i_i2s *, unsigned int); void (*set_txchanoffset)(const struct sun4i_i2s *, int); void (*set_rxchanoffset)(const struct sun4i_i2s *); + void (*set_txchanen)(const struct sun4i_i2s *, int, int); + void (*set_rxchanen)(const struct sun4i_i2s *, int); };
struct sun4i_i2s { @@ -444,6 +446,25 @@ static void sun8i_i2s_set_rxchanoffset(const struct sun4i_i2s *i2s) SUN8I_I2S_TX_CHAN_OFFSET(i2s->offset)); }
+static void sun8i_i2s_set_txchanen(const struct sun4i_i2s *i2s, int output, + int channel) +{ + if (output >= 0 && output < 4) { + regmap_update_bits(i2s->regmap, + SUN8I_I2S_TX_CHAN_SEL_REG + (output * 4), + SUN8I_I2S_TX_CHAN_EN_MASK, + SUN8I_I2S_TX_CHAN_EN(channel)); + } +} + +static void sun8i_i2s_set_rxchanen(const struct sun4i_i2s *i2s, int channel) +{ + regmap_update_bits(i2s->regmap, + SUN8I_I2S_RX_CHAN_SEL_REG, + SUN8I_I2S_TX_CHAN_EN_MASK, + SUN8I_I2S_TX_CHAN_EN(channel)); +} + static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -479,10 +500,11 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, regmap_field_write(i2s->field_rxchansel, SUN4I_I2S_CHAN_SEL(params_channels(params)));
- 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)); + if (i2s->variant->set_txchanen) + i2s->variant->set_txchanen(i2s, 0, channels); + + if (i2s->variant->set_rxchanen) + i2s->variant->set_rxchanen(i2s, channels);
switch (params_physical_width(params)) { case 16: @@ -1098,6 +1120,8 @@ static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = { .set_format = sun8i_i2s_set_format, .set_txchanoffset = sun8i_i2s_set_txchanoffset, .set_rxchanoffset = sun8i_i2s_set_rxchanoffset, + .set_txchanen = sun8i_i2s_set_txchanen, + .set_rxchanen = sun8i_i2s_set_rxchanen, };
static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = {
From: Marcus Cooper codekipper@gmail.com
Newer SoCs like the H6 have the channel select bits in a different positions than what is on the H3. As we will eventually add multi- channel support then create function calls as opposed to regmap fields to add support for different devices.
Signed-off-by: Marcus Cooper codekipper@gmail.com --- sound/soc/sunxi/sun4i-i2s.c | 87 ++++++++++++++++++++++--------------- 1 file changed, 53 insertions(+), 34 deletions(-)
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index ad2ff83deeb7..0b98adde0717 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -115,6 +115,8 @@ #define SUN8I_I2S_TX_CHAN_OFFSET(offset) ((offset) << 12) #define SUN8I_I2S_TX_CHAN_EN_MASK GENMASK(11, 4) #define SUN8I_I2S_TX_CHAN_EN(num_chan) (((1 << num_chan) - 1) << 4) +#define SUN8I_I2S_TX_CHAN_SEL_MASK GENMASK(2, 0) +#define SUN8I_I2S_TX_CHAN_SEL(chan) (chan - 1)
#define SUN8I_I2S_RX_CHAN_SEL_REG 0x54 #define SUN8I_I2S_RX_CHAN_MAP_REG 0x58 @@ -141,8 +143,6 @@ struct sun4i_i2s; * @field_fmt_sext: regmap field to set the sign extension. * @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; @@ -164,8 +164,6 @@ struct sun4i_i2s_quirks { struct reg_field field_fmt_sext; 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); @@ -174,6 +172,8 @@ struct sun4i_i2s_quirks { void (*set_rxchanoffset)(const struct sun4i_i2s *); void (*set_txchanen)(const struct sun4i_i2s *, int, int); void (*set_rxchanen)(const struct sun4i_i2s *, int); + void (*set_txchansel)(const struct sun4i_i2s *, int, int); + void (*set_rxchansel)(const struct sun4i_i2s *, int); };
struct sun4i_i2s { @@ -197,8 +197,6 @@ struct sun4i_i2s { struct regmap_field *field_fmt_sext; 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;
@@ -465,6 +463,42 @@ static void sun8i_i2s_set_rxchanen(const struct sun4i_i2s *i2s, int channel) SUN8I_I2S_TX_CHAN_EN(channel)); }
+static void sun4i_i2s_set_txchansel(const struct sun4i_i2s *i2s, int output, + int channel) +{ + /* Configure the channels */ + regmap_write(i2s->regmap, + SUN4I_I2S_TX_CHAN_SEL_REG, + SUN4I_I2S_CHAN_SEL(channel)); +} + +static void sun8i_i2s_set_txchansel(const struct sun4i_i2s *i2s, int output, + int channel) +{ + if (output >= 0 && output < 4) { + regmap_update_bits(i2s->regmap, + SUN8I_I2S_TX_CHAN_SEL_REG + (output * 4), + SUN8I_I2S_TX_CHAN_SEL_MASK, + SUN8I_I2S_TX_CHAN_SEL(channel)); + } +} + +static void sun4i_i2s_set_rxchansel(const struct sun4i_i2s *i2s, int channel) +{ + /* Configure the channels */ + regmap_write(i2s->regmap, + SUN4I_I2S_RX_CHAN_SEL_REG, + SUN4I_I2S_CHAN_SEL(channel)); +} + +static void sun8i_i2s_set_rxchansel(const struct sun4i_i2s *i2s, int channel) +{ + regmap_update_bits(i2s->regmap, + SUN8I_I2S_RX_CHAN_SEL_REG, + SUN8I_I2S_TX_CHAN_SEL_MASK, + SUN8I_I2S_TX_CHAN_SEL(channel)); +} + static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -494,11 +528,8 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, regmap_field_write(i2s->field_rxchanmap, 0x00003210);
/* Configure the channels */ - regmap_field_write(i2s->field_txchansel, - SUN4I_I2S_CHAN_SEL(params_channels(params))); - - regmap_field_write(i2s->field_rxchansel, - SUN4I_I2S_CHAN_SEL(params_channels(params))); + i2s->variant->set_txchansel(i2s, 0, channels); + i2s->variant->set_rxchansel(i2s, channels);
if (i2s->variant->set_txchanen) i2s->variant->set_txchanen(i2s, 0, channels); @@ -1048,11 +1079,11 @@ static const struct sun4i_i2s_quirks sun4i_a10_i2s_quirks = { .field_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG, 8, 8), .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_format = sun4i_i2s_set_format, + .set_txchansel = sun4i_i2s_set_txchansel, + .set_rxchansel = sun4i_i2s_set_rxchansel, };
static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = { @@ -1069,11 +1100,11 @@ static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = { .field_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG, 8, 8), .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_format = sun4i_i2s_set_format, + .set_txchansel = sun4i_i2s_set_txchansel, + .set_rxchansel = sun4i_i2s_set_rxchansel, };
static const struct sun4i_i2s_quirks sun8i_a83t_i2s_quirks = { @@ -1089,11 +1120,11 @@ static const struct sun4i_i2s_quirks sun8i_a83t_i2s_quirks = { .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_format = sun4i_i2s_set_format, + .set_txchansel = sun4i_i2s_set_txchansel, + .set_rxchansel = sun4i_i2s_set_rxchansel, };
static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = { @@ -1113,8 +1144,6 @@ static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = { .field_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_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_format = sun8i_i2s_set_format, @@ -1122,6 +1151,8 @@ static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = { .set_rxchanoffset = sun8i_i2s_set_rxchanoffset, .set_txchanen = sun8i_i2s_set_txchanen, .set_rxchanen = sun8i_i2s_set_rxchanen, + .set_txchansel = sun8i_i2s_set_txchansel, + .set_rxchansel = sun8i_i2s_set_rxchansel, };
static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = { @@ -1138,11 +1169,11 @@ static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = { .field_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG, 8, 8), .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_format = sun4i_i2s_set_format, + .set_txchansel = sun4i_i2s_set_txchansel, + .set_rxchansel = sun4i_i2s_set_rxchansel, };
static int sun4i_i2s_init_regmap_fields(struct device *dev, @@ -1199,19 +1230,7 @@ static int sun4i_i2s_init_regmap_fields(struct device *dev, 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 PTR_ERR_OR_ZERO(i2s->field_rxchanmap); }
static int sun4i_i2s_probe(struct platform_device *pdev)
From: Marcus Cooper codekipper@gmail.com
As we will eventually add multi-channel audio support to the i2s then create function calls as opposed to regmap fields to add support for different devices.
Signed-off-by: Marcus Cooper codekipper@gmail.com --- sound/soc/sunxi/sun4i-i2s.c | 71 +++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 31 deletions(-)
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index 0b98adde0717..6de3cb41aaf6 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -141,8 +141,6 @@ struct sun4i_i2s; * @field_fmt_lrclk: regmap field to set frame polarity. * @field_fmt_mode: regmap field to set the operational mode. * @field_fmt_sext: regmap field to set the sign extension. - * @field_txchanmap: location of the tx channel mapping register. - * @field_rxchanmap: location of the rx channel mapping register. */ struct sun4i_i2s_quirks { bool has_reset; @@ -162,8 +160,6 @@ struct sun4i_i2s_quirks { struct reg_field field_fmt_lrclk; struct reg_field field_fmt_mode; struct reg_field field_fmt_sext; - struct reg_field field_txchanmap; - struct reg_field field_rxchanmap;
s8 (*get_sr)(const struct sun4i_i2s *, int); s8 (*get_wss)(const struct sun4i_i2s *, int); @@ -174,6 +170,8 @@ struct sun4i_i2s_quirks { void (*set_rxchanen)(const struct sun4i_i2s *, int); void (*set_txchansel)(const struct sun4i_i2s *, int, int); void (*set_rxchansel)(const struct sun4i_i2s *, int); + void (*set_txchanmap)(const struct sun4i_i2s *, int, int); + void (*set_rxchanmap)(const struct sun4i_i2s *, int); };
struct sun4i_i2s { @@ -195,8 +193,6 @@ struct sun4i_i2s { struct regmap_field *field_fmt_lrclk; struct regmap_field *field_fmt_mode; struct regmap_field *field_fmt_sext; - struct regmap_field *field_txchanmap; - struct regmap_field *field_rxchanmap;
const struct sun4i_i2s_quirks *variant;
@@ -499,6 +495,31 @@ static void sun8i_i2s_set_rxchansel(const struct sun4i_i2s *i2s, int channel) SUN8I_I2S_TX_CHAN_SEL(channel)); }
+static void sun4i_i2s_set_txchanmap(const struct sun4i_i2s *i2s, int output, + int channel) +{ + regmap_write(i2s->regmap, SUN4I_I2S_TX_CHAN_MAP_REG, channel); +} + +static void sun8i_i2s_set_txchanmap(const struct sun4i_i2s *i2s, int output, + int channel) +{ + if (output >= 0 && output < 4) { + regmap_write(i2s->regmap, + SUN8I_I2S_TX_CHAN_MAP_REG + (output * 4), channel); + } +} + +static void sun4i_i2s_set_rxchanmap(const struct sun4i_i2s *i2s, int channel) +{ + regmap_write(i2s->regmap, SUN4I_I2S_RX_CHAN_MAP_REG, channel); +} + +static void sun8i_i2s_set_rxchanmap(const struct sun4i_i2s *i2s, int channel) +{ + regmap_write(i2s->regmap, SUN8I_I2S_RX_CHAN_MAP_REG, channel); +} + static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -524,8 +545,8 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, }
/* Map the channels for playback and capture */ - regmap_field_write(i2s->field_txchanmap, 0x76543210); - regmap_field_write(i2s->field_rxchanmap, 0x00003210); + i2s->variant->set_txchanmap(i2s, 0, 0x76543210); + i2s->variant->set_rxchanmap(i2s, 0x00003210);
/* Configure the channels */ i2s->variant->set_txchansel(i2s, 0, channels); @@ -1077,13 +1098,13 @@ static const struct sun4i_i2s_quirks sun4i_a10_i2s_quirks = { .has_slave_select_bit = true, .field_fmt_mode = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1), .field_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG, 8, 8), - .field_txchanmap = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31), - .field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31), .get_sr = sun4i_i2s_get_sr, .get_wss = sun4i_i2s_get_wss, .set_format = sun4i_i2s_set_format, .set_txchansel = sun4i_i2s_set_txchansel, .set_rxchansel = sun4i_i2s_set_rxchansel, + .set_txchanmap = sun4i_i2s_set_txchanmap, + .set_rxchanmap = sun4i_i2s_set_rxchanmap, };
static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = { @@ -1098,13 +1119,13 @@ static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = { .has_slave_select_bit = true, .field_fmt_mode = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1), .field_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG, 8, 8), - .field_txchanmap = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31), - .field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31), .get_sr = sun4i_i2s_get_sr, .get_wss = sun4i_i2s_get_wss, .set_format = sun4i_i2s_set_format, .set_txchansel = sun4i_i2s_set_txchansel, .set_rxchansel = sun4i_i2s_set_rxchansel, + .set_txchanmap = sun4i_i2s_set_txchanmap, + .set_rxchanmap = sun4i_i2s_set_rxchanmap, };
static const struct sun4i_i2s_quirks sun8i_a83t_i2s_quirks = { @@ -1118,13 +1139,13 @@ 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), .get_sr = sun8i_i2s_get_sr_wss, .get_wss = sun8i_i2s_get_sr_wss, .set_format = sun4i_i2s_set_format, .set_txchansel = sun4i_i2s_set_txchansel, .set_rxchansel = sun4i_i2s_set_rxchansel, + .set_txchanmap = sun4i_i2s_set_txchanmap, + .set_rxchanmap = sun4i_i2s_set_rxchanmap, };
static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = { @@ -1142,8 +1163,6 @@ static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = { .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 19, 19), .field_fmt_mode = REG_FIELD(SUN4I_I2S_CTRL_REG, 4, 5), .field_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_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), .get_sr = sun8i_i2s_get_sr_wss, .get_wss = sun8i_i2s_get_sr_wss, .set_format = sun8i_i2s_set_format, @@ -1153,6 +1172,8 @@ static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = { .set_rxchanen = sun8i_i2s_set_rxchanen, .set_txchansel = sun8i_i2s_set_txchansel, .set_rxchansel = sun8i_i2s_set_rxchansel, + .set_txchanmap = sun8i_i2s_set_txchanmap, + .set_rxchanmap = sun8i_i2s_set_rxchanmap, };
static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = { @@ -1167,13 +1188,13 @@ static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = { .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7), .field_fmt_mode = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1), .field_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG, 8, 8), - .field_txchanmap = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31), - .field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31), .get_sr = sun4i_i2s_get_sr, .get_wss = sun4i_i2s_get_wss, .set_format = sun4i_i2s_set_format, .set_txchansel = sun4i_i2s_set_txchansel, .set_rxchansel = sun4i_i2s_set_rxchansel, + .set_txchanmap = sun4i_i2s_set_txchanmap, + .set_rxchanmap = sun4i_i2s_set_rxchanmap, };
static int sun4i_i2s_init_regmap_fields(struct device *dev, @@ -1218,19 +1239,7 @@ static int sun4i_i2s_init_regmap_fields(struct device *dev, i2s->field_fmt_sext = devm_regmap_field_alloc(dev, i2s->regmap, i2s->variant->field_fmt_sext); - if (IS_ERR(i2s->field_fmt_sext)) - return PTR_ERR(i2s->field_fmt_sext); - - 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); - return PTR_ERR_OR_ZERO(i2s->field_rxchanmap); + return PTR_ERR_OR_ZERO(i2s->field_fmt_sext); }
static int sun4i_i2s_probe(struct platform_device *pdev)
From: Jernej Skrabec jernej.skrabec@siol.net
I2S doesn't work if parent rate couldn't be change. Difference between wanted and actual rate is too big.
Fix this by adding CLK_SET_RATE_PARENT flag to I2S clocks.
Signed-off-by: Jernej Skrabec jernej.skrabec@siol.net --- drivers/clk/sunxi-ng/ccu-sun50i-h6.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-h6.c b/drivers/clk/sunxi-ng/ccu-sun50i-h6.c index aebef4af9861..d89353a3cdec 100644 --- a/drivers/clk/sunxi-ng/ccu-sun50i-h6.c +++ b/drivers/clk/sunxi-ng/ccu-sun50i-h6.c @@ -505,7 +505,7 @@ static struct ccu_div i2s3_clk = { .hw.init = CLK_HW_INIT_PARENTS("i2s3", audio_parents, &ccu_div_ops, - 0), + CLK_SET_RATE_PARENT), }, };
@@ -518,7 +518,7 @@ static struct ccu_div i2s0_clk = { .hw.init = CLK_HW_INIT_PARENTS("i2s0", audio_parents, &ccu_div_ops, - 0), + CLK_SET_RATE_PARENT), }, };
@@ -531,7 +531,7 @@ static struct ccu_div i2s1_clk = { .hw.init = CLK_HW_INIT_PARENTS("i2s1", audio_parents, &ccu_div_ops, - 0), + CLK_SET_RATE_PARENT), }, };
@@ -544,7 +544,7 @@ static struct ccu_div i2s2_clk = { .hw.init = CLK_HW_INIT_PARENTS("i2s2", audio_parents, &ccu_div_ops, - 0), + CLK_SET_RATE_PARENT), }, };
On Wed, Aug 14, 2019 at 2:09 PM codekipper@gmail.com wrote:
From: Jernej Skrabec jernej.skrabec@siol.net
I2S doesn't work if parent rate couldn't be change. Difference between wanted and actual rate is too big.
Fix this by adding CLK_SET_RATE_PARENT flag to I2S clocks.
Signed-off-by: Jernej Skrabec jernej.skrabec@siol.net
This lacks your SoB. Please reply and I can add it when applying.
ChenYu
Thanks....I've added to my next patch series but if you could add it when applying that would be great. BR, CK
On Wed, 21 Aug 2019 at 06:07, Chen-Yu Tsai wens@csie.org wrote:
On Wed, Aug 14, 2019 at 2:09 PM codekipper@gmail.com wrote:
From: Jernej Skrabec jernej.skrabec@siol.net
I2S doesn't work if parent rate couldn't be change. Difference between wanted and actual rate is too big.
Fix this by adding CLK_SET_RATE_PARENT flag to I2S clocks.
Signed-off-by: Jernej Skrabec jernej.skrabec@siol.net
This lacks your SoB. Please reply and I can add it when applying.
ChenYu
On Wed, Aug 21, 2019 at 1:52 PM Code Kipper codekipper@gmail.com wrote:
Thanks....I've added to my next patch series but if you could add it when applying that would be great.
Please reply with an explicit SoB to put it on the record.
ChenYu
BR, CK
On Wed, 21 Aug 2019 at 06:07, Chen-Yu Tsai wens@csie.org wrote:
On Wed, Aug 14, 2019 at 2:09 PM codekipper@gmail.com wrote:
From: Jernej Skrabec jernej.skrabec@siol.net
I2S doesn't work if parent rate couldn't be change. Difference between wanted and actual rate is too big.
Fix this by adding CLK_SET_RATE_PARENT flag to I2S clocks.
Signed-off-by: Jernej Skrabec jernej.skrabec@siol.net
This lacks your SoB. Please reply and I can add it when applying.
ChenYu
-- You received this message because you are subscribed to the Google Groups "linux-sunxi" group. To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe@googlegroups.com. To view this discussion on the web, visit https://groups.google.com/d/msgid/linux-sunxi/CAEKpxBnxf%3Diejk887A7qFkzt3BX....
On Wed, 14 Aug 2019 at 08:09, codekipper@gmail.com wrote:
From: Jernej Skrabec jernej.skrabec@siol.net
I2S doesn't work if parent rate couldn't be change. Difference between wanted and actual rate is too big.
Fix this by adding CLK_SET_RATE_PARENT flag to I2S clocks.
Signed-off-by: Jernej Skrabec jernej.skrabec@siol.net
Signed-off-by: Marcus Cooper codekipper@gmail.com
drivers/clk/sunxi-ng/ccu-sun50i-h6.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-h6.c b/drivers/clk/sunxi-ng/ccu-sun50i-h6.c index aebef4af9861..d89353a3cdec 100644 --- a/drivers/clk/sunxi-ng/ccu-sun50i-h6.c +++ b/drivers/clk/sunxi-ng/ccu-sun50i-h6.c @@ -505,7 +505,7 @@ static struct ccu_div i2s3_clk = { .hw.init = CLK_HW_INIT_PARENTS("i2s3", audio_parents, &ccu_div_ops,
0),
CLK_SET_RATE_PARENT), },
};
@@ -518,7 +518,7 @@ static struct ccu_div i2s0_clk = { .hw.init = CLK_HW_INIT_PARENTS("i2s0", audio_parents, &ccu_div_ops,
0),
CLK_SET_RATE_PARENT), },
};
@@ -531,7 +531,7 @@ static struct ccu_div i2s1_clk = { .hw.init = CLK_HW_INIT_PARENTS("i2s1", audio_parents, &ccu_div_ops,
0),
CLK_SET_RATE_PARENT), },
};
@@ -544,7 +544,7 @@ static struct ccu_div i2s2_clk = { .hw.init = CLK_HW_INIT_PARENTS("i2s2", audio_parents, &ccu_div_ops,
0),
CLK_SET_RATE_PARENT), },
};
-- 2.22.0
On Wed, Aug 21, 2019 at 5:19 PM Code Kipper codekipper@gmail.com wrote:
On Wed, 14 Aug 2019 at 08:09, codekipper@gmail.com wrote:
From: Jernej Skrabec jernej.skrabec@siol.net
I2S doesn't work if parent rate couldn't be change. Difference between wanted and actual rate is too big.
Fix this by adding CLK_SET_RATE_PARENT flag to I2S clocks.
Signed-off-by: Jernej Skrabec jernej.skrabec@siol.net
Signed-off-by: Marcus Cooper codekipper@gmail.com
Applied for 5.4
From: Jernej Skrabec jernej.skrabec@siol.net
H6 I2S is very similar to H3, except that it supports up to 16 channels and thus few registers have fields on different position.
Signed-off-by: Jernej Skrabec jernej.skrabec@siol.net --- .../devicetree/bindings/sound/allwinner,sun4i-a10-i2s.yaml | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-i2s.yaml b/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-i2s.yaml index eb3992138eec..6928d0a1dcc8 100644 --- a/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-i2s.yaml +++ b/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-i2s.yaml @@ -24,6 +24,7 @@ properties: - items: - const: allwinner,sun50i-a64-i2s - const: allwinner,sun8i-h3-i2s + - const: allwinner,sun50i-h6-i2s
reg: maxItems: 1 @@ -59,6 +60,7 @@ allOf: - allwinner,sun8i-a83t-i2s - allwinner,sun8i-h3-i2s - allwinner,sun50i-a64-codec-i2s + - allwinner,sun50i-h6-i2s
then: required:
From: Jernej Skrabec jernej.skrabec@siol.net
H6 I2S is very similar to that in H3, except it supports up to 16 channels.
Signed-off-by: Jernej Skrabec jernej.skrabec@siol.net --- sound/soc/sunxi/sun4i-i2s.c | 148 ++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+)
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index 6de3cb41aaf6..a8d98696fe7c 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -121,6 +121,21 @@ #define SUN8I_I2S_RX_CHAN_SEL_REG 0x54 #define SUN8I_I2S_RX_CHAN_MAP_REG 0x58
+/* Defines required for sun50i-h6 support */ +#define SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET_MASK GENMASK(21, 20) +#define SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET(offset) ((offset) << 20) +#define SUN50I_H6_I2S_TX_CHAN_SEL_MASK GENMASK(19, 16) +#define SUN50I_H6_I2S_TX_CHAN_SEL(chan) ((chan - 1) << 16) +#define SUN50I_H6_I2S_TX_CHAN_EN_MASK GENMASK(15, 0) +#define SUN50I_H6_I2S_TX_CHAN_EN(num_chan) (((1 << num_chan) - 1)) + +#define SUN50I_H6_I2S_TX_CHAN_MAP0_REG 0x44 +#define SUN50I_H6_I2S_TX_CHAN_MAP1_REG 0x48 + +#define SUN50I_H6_I2S_RX_CHAN_SEL_REG 0x64 +#define SUN50I_H6_I2S_RX_CHAN_MAP0_REG 0x68 +#define SUN50I_H6_I2S_RX_CHAN_MAP1_REG 0x6C + struct sun4i_i2s;
/** @@ -440,6 +455,25 @@ static void sun8i_i2s_set_rxchanoffset(const struct sun4i_i2s *i2s) SUN8I_I2S_TX_CHAN_OFFSET(i2s->offset)); }
+static void sun50i_h6_i2s_set_txchanoffset(const struct sun4i_i2s *i2s, int output) +{ + if (output >= 0 && output < 4) { + regmap_update_bits(i2s->regmap, + SUN8I_I2S_TX_CHAN_SEL_REG + (output * 4), + SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET_MASK, + SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET(i2s->offset)); + } + +} + +static void sun50i_h6_i2s_set_rxchanoffset(const struct sun4i_i2s *i2s) +{ + regmap_update_bits(i2s->regmap, + SUN50I_H6_I2S_RX_CHAN_SEL_REG, + SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET_MASK, + SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET(i2s->offset)); +} + static void sun8i_i2s_set_txchanen(const struct sun4i_i2s *i2s, int output, int channel) { @@ -459,6 +493,26 @@ static void sun8i_i2s_set_rxchanen(const struct sun4i_i2s *i2s, int channel) SUN8I_I2S_TX_CHAN_EN(channel)); }
+ +static void sun50i_h6_i2s_set_txchanen(const struct sun4i_i2s *i2s, int output, + int channel) +{ + if (output >= 0 && output < 4) { + regmap_update_bits(i2s->regmap, + SUN8I_I2S_TX_CHAN_SEL_REG + (output * 4), + SUN50I_H6_I2S_TX_CHAN_EN_MASK, + SUN50I_H6_I2S_TX_CHAN_EN(channel)); + } +} + +static void sun50i_h6_i2s_set_rxchanen(const struct sun4i_i2s *i2s, int channel) +{ + regmap_update_bits(i2s->regmap, + SUN50I_H6_I2S_RX_CHAN_SEL_REG, + SUN50I_H6_I2S_TX_CHAN_EN_MASK, + SUN50I_H6_I2S_TX_CHAN_EN(channel)); +} + static void sun4i_i2s_set_txchansel(const struct sun4i_i2s *i2s, int output, int channel) { @@ -495,6 +549,25 @@ static void sun8i_i2s_set_rxchansel(const struct sun4i_i2s *i2s, int channel) SUN8I_I2S_TX_CHAN_SEL(channel)); }
+static void sun50i_h6_i2s_set_txchansel(const struct sun4i_i2s *i2s, int output, + int channel) +{ + if (output >= 0 && output < 4) { + regmap_update_bits(i2s->regmap, + SUN8I_I2S_TX_CHAN_SEL_REG + (output * 4), + SUN50I_H6_I2S_TX_CHAN_SEL_MASK, + SUN50I_H6_I2S_TX_CHAN_SEL(channel)); + } +} + +static void sun50i_h6_i2s_set_rxchansel(const struct sun4i_i2s *i2s, int channel) +{ + regmap_update_bits(i2s->regmap, + SUN50I_H6_I2S_RX_CHAN_SEL_REG, + SUN50I_H6_I2S_TX_CHAN_SEL_MASK, + SUN50I_H6_I2S_TX_CHAN_SEL(channel)); +} + static void sun4i_i2s_set_txchanmap(const struct sun4i_i2s *i2s, int output, int channel) { @@ -520,6 +593,20 @@ static void sun8i_i2s_set_rxchanmap(const struct sun4i_i2s *i2s, int channel) regmap_write(i2s->regmap, SUN8I_I2S_RX_CHAN_MAP_REG, channel); }
+static void sun50i_h6_i2s_set_txchanmap(const struct sun4i_i2s *i2s, int output, + int channel) +{ + if (output >= 0 && output < 4) { + regmap_write(i2s->regmap, + SUN50I_H6_I2S_TX_CHAN_MAP1_REG + (output * 8), channel); + } +} + +static void sun50i_h6_i2s_set_rxchanmap(const struct sun4i_i2s *i2s, int channel) +{ + regmap_write(i2s->regmap, SUN50I_H6_I2S_RX_CHAN_MAP1_REG, channel); +} + static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -996,6 +1083,22 @@ static const struct reg_default sun8i_i2s_reg_defaults[] = { { SUN8I_I2S_RX_CHAN_MAP_REG, 0x00000000 }, };
+static const struct reg_default sun50i_i2s_reg_defaults[] = { + { SUN4I_I2S_CTRL_REG, 0x00060000 }, + { SUN4I_I2S_FMT0_REG, 0x00000033 }, + { SUN4I_I2S_FMT1_REG, 0x00000030 }, + { SUN4I_I2S_FIFO_CTRL_REG, 0x000400f0 }, + { SUN4I_I2S_DMA_INT_CTRL_REG, 0x00000000 }, + { SUN4I_I2S_CLK_DIV_REG, 0x00000000 }, + { SUN8I_I2S_CHAN_CFG_REG, 0x00000000 }, + { SUN8I_I2S_TX_CHAN_SEL_REG, 0x00000000 }, + { SUN50I_H6_I2S_TX_CHAN_MAP0_REG, 0x00000000 }, + { SUN50I_H6_I2S_TX_CHAN_MAP1_REG, 0x00000000 }, + { SUN50I_H6_I2S_RX_CHAN_SEL_REG, 0x00000000 }, + { SUN50I_H6_I2S_RX_CHAN_MAP0_REG, 0x00000000 }, + { SUN50I_H6_I2S_RX_CHAN_MAP1_REG, 0x00000000 }, +}; + static const struct regmap_config sun4i_i2s_regmap_config = { .reg_bits = 32, .reg_stride = 4, @@ -1023,6 +1126,19 @@ static const struct regmap_config sun8i_i2s_regmap_config = { .volatile_reg = sun8i_i2s_volatile_reg, };
+static const struct regmap_config sun50i_i2s_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = SUN50I_H6_I2S_RX_CHAN_MAP1_REG, + .cache_type = REGCACHE_FLAT, + .reg_defaults = sun50i_i2s_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(sun50i_i2s_reg_defaults), + .writeable_reg = sun4i_i2s_wr_reg, + .readable_reg = sun8i_i2s_rd_reg, + .volatile_reg = sun8i_i2s_volatile_reg, +}; + static int sun4i_i2s_runtime_resume(struct device *dev) { struct sun4i_i2s *i2s = dev_get_drvdata(dev); @@ -1197,6 +1313,34 @@ static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = { .set_rxchanmap = sun4i_i2s_set_rxchanmap, };
+static const struct sun4i_i2s_quirks sun50i_h6_i2s_quirks = { + .has_reset = true, + .reg_offset_txdata = SUN8I_I2S_FIFO_TX_REG, + .sun4i_i2s_regmap = &sun50i_i2s_regmap_config, + .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), + .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), + .field_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG, 4, 5), + .get_sr = sun8i_i2s_get_sr_wss, + .get_wss = sun8i_i2s_get_sr_wss, + .set_format = sun8i_i2s_set_format, + .set_txchanoffset = sun50i_h6_i2s_set_txchanoffset, + .set_rxchanoffset = sun50i_h6_i2s_set_rxchanoffset, + .set_txchanen = sun50i_h6_i2s_set_txchanen, + .set_rxchanen = sun50i_h6_i2s_set_rxchanen, + .set_txchansel = sun50i_h6_i2s_set_txchansel, + .set_rxchansel = sun50i_h6_i2s_set_rxchansel, + .set_txchanmap = sun50i_h6_i2s_set_txchanmap, + .set_rxchanmap = sun50i_h6_i2s_set_rxchanmap, +}; + static int sun4i_i2s_init_regmap_fields(struct device *dev, struct sun4i_i2s *i2s) { @@ -1389,6 +1533,10 @@ static const struct of_device_id sun4i_i2s_match[] = { .compatible = "allwinner,sun50i-a64-codec-i2s", .data = &sun50i_a64_codec_i2s_quirks, }, + { + .compatible = "allwinner,sun50i-h6-i2s", + .data = &sun50i_h6_i2s_quirks, + }, {} }; MODULE_DEVICE_TABLE(of, sun4i_i2s_match);
Hi!
Dne sreda, 14. avgust 2019 ob 08:08:50 CEST je codekipper@gmail.com napisal(a):
From: Jernej Skrabec jernej.skrabec@siol.net
H6 I2S is very similar to that in H3, except it supports up to 16 channels.
Signed-off-by: Jernej Skrabec jernej.skrabec@siol.net
Your Signed-off-by is missing here and on all other patches made originally by me.
Best regards, Jernej
sound/soc/sunxi/sun4i-i2s.c | 148 ++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+)
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index 6de3cb41aaf6..a8d98696fe7c 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -121,6 +121,21 @@ #define SUN8I_I2S_RX_CHAN_SEL_REG 0x54 #define SUN8I_I2S_RX_CHAN_MAP_REG 0x58
+/* Defines required for sun50i-h6 support */ +#define SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET_MASK GENMASK(21, 20) +#define SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET(offset) ((offset) << 20) +#define SUN50I_H6_I2S_TX_CHAN_SEL_MASK GENMASK(19, 16) +#define SUN50I_H6_I2S_TX_CHAN_SEL(chan) ((chan - 1) << 16) +#define SUN50I_H6_I2S_TX_CHAN_EN_MASK GENMASK(15, 0) +#define SUN50I_H6_I2S_TX_CHAN_EN(num_chan) (((1 << num_chan) - 1))
+#define SUN50I_H6_I2S_TX_CHAN_MAP0_REG 0x44 +#define SUN50I_H6_I2S_TX_CHAN_MAP1_REG 0x48
+#define SUN50I_H6_I2S_RX_CHAN_SEL_REG 0x64 +#define SUN50I_H6_I2S_RX_CHAN_MAP0_REG 0x68 +#define SUN50I_H6_I2S_RX_CHAN_MAP1_REG 0x6C
struct sun4i_i2s;
/** @@ -440,6 +455,25 @@ static void sun8i_i2s_set_rxchanoffset(const struct sun4i_i2s *i2s) SUN8I_I2S_TX_CHAN_OFFSET(i2s->offset)); }
+static void sun50i_h6_i2s_set_txchanoffset(const struct sun4i_i2s *i2s, int output) +{
- if (output >= 0 && output < 4) {
regmap_update_bits(i2s->regmap,
SUN8I_I2S_TX_CHAN_SEL_REG +
(output * 4),
SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET_MASK,
SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET(i2s->offset));
- }
+}
+static void sun50i_h6_i2s_set_rxchanoffset(const struct sun4i_i2s *i2s) +{
- regmap_update_bits(i2s->regmap,
SUN50I_H6_I2S_RX_CHAN_SEL_REG,
SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET_MASK,
SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET(i2s-
offset)); +}
static void sun8i_i2s_set_txchanen(const struct sun4i_i2s *i2s, int output, int channel) { @@ -459,6 +493,26 @@ static void sun8i_i2s_set_rxchanen(const struct sun4i_i2s *i2s, int channel) SUN8I_I2S_TX_CHAN_EN(channel)); }
+static void sun50i_h6_i2s_set_txchanen(const struct sun4i_i2s *i2s, int output, + int channel) +{
- if (output >= 0 && output < 4) {
regmap_update_bits(i2s->regmap,
SUN8I_I2S_TX_CHAN_SEL_REG +
(output * 4),
SUN50I_H6_I2S_TX_CHAN_EN_MASK,
SUN50I_H6_I2S_TX_CHAN_EN(channel));
- }
+}
+static void sun50i_h6_i2s_set_rxchanen(const struct sun4i_i2s *i2s, int channel) +{
- regmap_update_bits(i2s->regmap,
SUN50I_H6_I2S_RX_CHAN_SEL_REG,
SUN50I_H6_I2S_TX_CHAN_EN_MASK,
SUN50I_H6_I2S_TX_CHAN_EN(channel));
+}
static void sun4i_i2s_set_txchansel(const struct sun4i_i2s *i2s, int output, int channel) { @@ -495,6 +549,25 @@ static void sun8i_i2s_set_rxchansel(const struct sun4i_i2s *i2s, int channel) SUN8I_I2S_TX_CHAN_SEL(channel)); }
+static void sun50i_h6_i2s_set_txchansel(const struct sun4i_i2s *i2s, int output, + int channel) +{
- if (output >= 0 && output < 4) {
regmap_update_bits(i2s->regmap,
SUN8I_I2S_TX_CHAN_SEL_REG +
(output * 4),
SUN50I_H6_I2S_TX_CHAN_SEL_MASK,
SUN50I_H6_I2S_TX_CHAN_SEL(channel));
- }
+}
+static void sun50i_h6_i2s_set_rxchansel(const struct sun4i_i2s *i2s, int channel) +{
- regmap_update_bits(i2s->regmap,
SUN50I_H6_I2S_RX_CHAN_SEL_REG,
SUN50I_H6_I2S_TX_CHAN_SEL_MASK,
SUN50I_H6_I2S_TX_CHAN_SEL(channel));
+}
static void sun4i_i2s_set_txchanmap(const struct sun4i_i2s *i2s, int output, int channel) { @@ -520,6 +593,20 @@ static void sun8i_i2s_set_rxchanmap(const struct sun4i_i2s *i2s, int channel) regmap_write(i2s->regmap, SUN8I_I2S_RX_CHAN_MAP_REG, channel); }
+static void sun50i_h6_i2s_set_txchanmap(const struct sun4i_i2s *i2s, int output, + int channel) +{
- if (output >= 0 && output < 4) {
regmap_write(i2s->regmap,
SUN50I_H6_I2S_TX_CHAN_MAP1_REG + (output
* 8), channel);
- }
+}
+static void sun50i_h6_i2s_set_rxchanmap(const struct sun4i_i2s *i2s, int channel) +{
- regmap_write(i2s->regmap, SUN50I_H6_I2S_RX_CHAN_MAP1_REG, channel);
+}
static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -996,6 +1083,22 @@ static const struct reg_default sun8i_i2s_reg_defaults[] = { { SUN8I_I2S_RX_CHAN_MAP_REG, 0x00000000 }, };
+static const struct reg_default sun50i_i2s_reg_defaults[] = {
- { SUN4I_I2S_CTRL_REG, 0x00060000 },
- { SUN4I_I2S_FMT0_REG, 0x00000033 },
- { SUN4I_I2S_FMT1_REG, 0x00000030 },
- { SUN4I_I2S_FIFO_CTRL_REG, 0x000400f0 },
- { SUN4I_I2S_DMA_INT_CTRL_REG, 0x00000000 },
- { SUN4I_I2S_CLK_DIV_REG, 0x00000000 },
- { SUN8I_I2S_CHAN_CFG_REG, 0x00000000 },
- { SUN8I_I2S_TX_CHAN_SEL_REG, 0x00000000 },
- { SUN50I_H6_I2S_TX_CHAN_MAP0_REG, 0x00000000 },
- { SUN50I_H6_I2S_TX_CHAN_MAP1_REG, 0x00000000 },
- { SUN50I_H6_I2S_RX_CHAN_SEL_REG, 0x00000000 },
- { SUN50I_H6_I2S_RX_CHAN_MAP0_REG, 0x00000000 },
- { SUN50I_H6_I2S_RX_CHAN_MAP1_REG, 0x00000000 },
+};
static const struct regmap_config sun4i_i2s_regmap_config = { .reg_bits = 32, .reg_stride = 4, @@ -1023,6 +1126,19 @@ static const struct regmap_config sun8i_i2s_regmap_config = { .volatile_reg = sun8i_i2s_volatile_reg, };
+static const struct regmap_config sun50i_i2s_regmap_config = {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .max_register = SUN50I_H6_I2S_RX_CHAN_MAP1_REG,
- .cache_type = REGCACHE_FLAT,
- .reg_defaults = sun50i_i2s_reg_defaults,
- .num_reg_defaults = ARRAY_SIZE(sun50i_i2s_reg_defaults),
- .writeable_reg = sun4i_i2s_wr_reg,
- .readable_reg = sun8i_i2s_rd_reg,
- .volatile_reg = sun8i_i2s_volatile_reg,
+};
static int sun4i_i2s_runtime_resume(struct device *dev) { struct sun4i_i2s *i2s = dev_get_drvdata(dev); @@ -1197,6 +1313,34 @@ static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = { .set_rxchanmap =
sun4i_i2s_set_rxchanmap,
};
+static const struct sun4i_i2s_quirks sun50i_h6_i2s_quirks = {
- .has_reset = true,
- .reg_offset_txdata = SUN8I_I2S_FIFO_TX_REG,
- .sun4i_i2s_regmap = &sun50i_i2s_regmap_config,
- .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),
- .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),
- .field_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG,
4, 5),
- .get_sr = sun8i_i2s_get_sr_wss,
- .get_wss = sun8i_i2s_get_sr_wss,
- .set_format = sun8i_i2s_set_format,
- .set_txchanoffset = sun50i_h6_i2s_set_txchanoffset,
- .set_rxchanoffset = sun50i_h6_i2s_set_rxchanoffset,
- .set_txchanen = sun50i_h6_i2s_set_txchanen,
- .set_rxchanen = sun50i_h6_i2s_set_rxchanen,
- .set_txchansel = sun50i_h6_i2s_set_txchansel,
- .set_rxchansel = sun50i_h6_i2s_set_rxchansel,
- .set_txchanmap = sun50i_h6_i2s_set_txchanmap,
- .set_rxchanmap = sun50i_h6_i2s_set_rxchanmap,
+};
static int sun4i_i2s_init_regmap_fields(struct device *dev, struct sun4i_i2s *i2s) { @@ -1389,6 +1533,10 @@ static const struct of_device_id sun4i_i2s_match[] = { .compatible = "allwinner,sun50i-a64-codec-i2s", .data = &sun50i_a64_codec_i2s_quirks, },
- {
.compatible = "allwinner,sun50i-h6-i2s",
.data = &sun50i_h6_i2s_quirks,
- }, {}
}; MODULE_DEVICE_TABLE(of, sun4i_i2s_match);
On Wed, 14 Aug 2019 at 09:57, Jernej Škrabec jernej.skrabec@siol.net wrote:
Hi!
Dne sreda, 14. avgust 2019 ob 08:08:50 CEST je codekipper@gmail.com napisal(a):
From: Jernej Skrabec jernej.skrabec@siol.net
H6 I2S is very similar to that in H3, except it supports up to 16 channels.
Signed-off-by: Jernej Skrabec jernej.skrabec@siol.net
Your Signed-off-by is missing here and on all other patches made originally by me.
ACK
Best regards, Jernej
sound/soc/sunxi/sun4i-i2s.c | 148 ++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+)
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index 6de3cb41aaf6..a8d98696fe7c 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -121,6 +121,21 @@ #define SUN8I_I2S_RX_CHAN_SEL_REG 0x54 #define SUN8I_I2S_RX_CHAN_MAP_REG 0x58
+/* Defines required for sun50i-h6 support */ +#define SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET_MASK GENMASK(21, 20) +#define SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET(offset) ((offset) << 20) +#define SUN50I_H6_I2S_TX_CHAN_SEL_MASK GENMASK(19, 16) +#define SUN50I_H6_I2S_TX_CHAN_SEL(chan) ((chan - 1) << 16) +#define SUN50I_H6_I2S_TX_CHAN_EN_MASK GENMASK(15, 0) +#define SUN50I_H6_I2S_TX_CHAN_EN(num_chan) (((1 << num_chan) - 1))
+#define SUN50I_H6_I2S_TX_CHAN_MAP0_REG 0x44 +#define SUN50I_H6_I2S_TX_CHAN_MAP1_REG 0x48
+#define SUN50I_H6_I2S_RX_CHAN_SEL_REG 0x64 +#define SUN50I_H6_I2S_RX_CHAN_MAP0_REG 0x68 +#define SUN50I_H6_I2S_RX_CHAN_MAP1_REG 0x6C
struct sun4i_i2s;
/** @@ -440,6 +455,25 @@ static void sun8i_i2s_set_rxchanoffset(const struct sun4i_i2s *i2s) SUN8I_I2S_TX_CHAN_OFFSET(i2s->offset)); }
+static void sun50i_h6_i2s_set_txchanoffset(const struct sun4i_i2s *i2s, int output) +{
if (output >= 0 && output < 4) {
regmap_update_bits(i2s->regmap,
SUN8I_I2S_TX_CHAN_SEL_REG +
(output * 4),
SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET_MASK,
SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET(i2s->offset));
}
+}
+static void sun50i_h6_i2s_set_rxchanoffset(const struct sun4i_i2s *i2s) +{
regmap_update_bits(i2s->regmap,
SUN50I_H6_I2S_RX_CHAN_SEL_REG,
SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET_MASK,
SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET(i2s-
offset)); +}
static void sun8i_i2s_set_txchanen(const struct sun4i_i2s *i2s, int output, int channel) { @@ -459,6 +493,26 @@ static void sun8i_i2s_set_rxchanen(const struct sun4i_i2s *i2s, int channel) SUN8I_I2S_TX_CHAN_EN(channel)); }
+static void sun50i_h6_i2s_set_txchanen(const struct sun4i_i2s *i2s, int output, + int channel) +{
if (output >= 0 && output < 4) {
regmap_update_bits(i2s->regmap,
SUN8I_I2S_TX_CHAN_SEL_REG +
(output * 4),
SUN50I_H6_I2S_TX_CHAN_EN_MASK,
SUN50I_H6_I2S_TX_CHAN_EN(channel));
}
+}
+static void sun50i_h6_i2s_set_rxchanen(const struct sun4i_i2s *i2s, int channel) +{
regmap_update_bits(i2s->regmap,
SUN50I_H6_I2S_RX_CHAN_SEL_REG,
SUN50I_H6_I2S_TX_CHAN_EN_MASK,
SUN50I_H6_I2S_TX_CHAN_EN(channel));
+}
static void sun4i_i2s_set_txchansel(const struct sun4i_i2s *i2s, int output, int channel) { @@ -495,6 +549,25 @@ static void sun8i_i2s_set_rxchansel(const struct sun4i_i2s *i2s, int channel) SUN8I_I2S_TX_CHAN_SEL(channel)); }
+static void sun50i_h6_i2s_set_txchansel(const struct sun4i_i2s *i2s, int output, + int channel) +{
if (output >= 0 && output < 4) {
regmap_update_bits(i2s->regmap,
SUN8I_I2S_TX_CHAN_SEL_REG +
(output * 4),
SUN50I_H6_I2S_TX_CHAN_SEL_MASK,
SUN50I_H6_I2S_TX_CHAN_SEL(channel));
}
+}
+static void sun50i_h6_i2s_set_rxchansel(const struct sun4i_i2s *i2s, int channel) +{
regmap_update_bits(i2s->regmap,
SUN50I_H6_I2S_RX_CHAN_SEL_REG,
SUN50I_H6_I2S_TX_CHAN_SEL_MASK,
SUN50I_H6_I2S_TX_CHAN_SEL(channel));
+}
static void sun4i_i2s_set_txchanmap(const struct sun4i_i2s *i2s, int output, int channel) { @@ -520,6 +593,20 @@ static void sun8i_i2s_set_rxchanmap(const struct sun4i_i2s *i2s, int channel) regmap_write(i2s->regmap, SUN8I_I2S_RX_CHAN_MAP_REG, channel); }
+static void sun50i_h6_i2s_set_txchanmap(const struct sun4i_i2s *i2s, int output, + int channel) +{
if (output >= 0 && output < 4) {
regmap_write(i2s->regmap,
SUN50I_H6_I2S_TX_CHAN_MAP1_REG + (output
- 8), channel);
}
+}
+static void sun50i_h6_i2s_set_rxchanmap(const struct sun4i_i2s *i2s, int channel) +{
regmap_write(i2s->regmap, SUN50I_H6_I2S_RX_CHAN_MAP1_REG, channel);
+}
static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -996,6 +1083,22 @@ static const struct reg_default sun8i_i2s_reg_defaults[] = { { SUN8I_I2S_RX_CHAN_MAP_REG, 0x00000000 }, };
+static const struct reg_default sun50i_i2s_reg_defaults[] = {
{ SUN4I_I2S_CTRL_REG, 0x00060000 },
{ SUN4I_I2S_FMT0_REG, 0x00000033 },
{ SUN4I_I2S_FMT1_REG, 0x00000030 },
{ SUN4I_I2S_FIFO_CTRL_REG, 0x000400f0 },
{ SUN4I_I2S_DMA_INT_CTRL_REG, 0x00000000 },
{ SUN4I_I2S_CLK_DIV_REG, 0x00000000 },
{ SUN8I_I2S_CHAN_CFG_REG, 0x00000000 },
{ SUN8I_I2S_TX_CHAN_SEL_REG, 0x00000000 },
{ SUN50I_H6_I2S_TX_CHAN_MAP0_REG, 0x00000000 },
{ SUN50I_H6_I2S_TX_CHAN_MAP1_REG, 0x00000000 },
{ SUN50I_H6_I2S_RX_CHAN_SEL_REG, 0x00000000 },
{ SUN50I_H6_I2S_RX_CHAN_MAP0_REG, 0x00000000 },
{ SUN50I_H6_I2S_RX_CHAN_MAP1_REG, 0x00000000 },
+};
static const struct regmap_config sun4i_i2s_regmap_config = { .reg_bits = 32, .reg_stride = 4, @@ -1023,6 +1126,19 @@ static const struct regmap_config sun8i_i2s_regmap_config = { .volatile_reg = sun8i_i2s_volatile_reg, };
+static const struct regmap_config sun50i_i2s_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = SUN50I_H6_I2S_RX_CHAN_MAP1_REG,
.cache_type = REGCACHE_FLAT,
.reg_defaults = sun50i_i2s_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(sun50i_i2s_reg_defaults),
.writeable_reg = sun4i_i2s_wr_reg,
.readable_reg = sun8i_i2s_rd_reg,
.volatile_reg = sun8i_i2s_volatile_reg,
+};
static int sun4i_i2s_runtime_resume(struct device *dev) { struct sun4i_i2s *i2s = dev_get_drvdata(dev); @@ -1197,6 +1313,34 @@ static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = { .set_rxchanmap =
sun4i_i2s_set_rxchanmap,
};
+static const struct sun4i_i2s_quirks sun50i_h6_i2s_quirks = {
.has_reset = true,
.reg_offset_txdata = SUN8I_I2S_FIFO_TX_REG,
.sun4i_i2s_regmap = &sun50i_i2s_regmap_config,
.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),
.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),
.field_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG,
4, 5),
.get_sr = sun8i_i2s_get_sr_wss,
.get_wss = sun8i_i2s_get_sr_wss,
.set_format = sun8i_i2s_set_format,
.set_txchanoffset = sun50i_h6_i2s_set_txchanoffset,
.set_rxchanoffset = sun50i_h6_i2s_set_rxchanoffset,
.set_txchanen = sun50i_h6_i2s_set_txchanen,
.set_rxchanen = sun50i_h6_i2s_set_rxchanen,
.set_txchansel = sun50i_h6_i2s_set_txchansel,
.set_rxchansel = sun50i_h6_i2s_set_rxchansel,
.set_txchanmap = sun50i_h6_i2s_set_txchanmap,
.set_rxchanmap = sun50i_h6_i2s_set_rxchanmap,
+};
static int sun4i_i2s_init_regmap_fields(struct device *dev, struct sun4i_i2s *i2s) { @@ -1389,6 +1533,10 @@ static const struct of_device_id sun4i_i2s_match[] = { .compatible = "allwinner,sun50i-a64-codec-i2s", .data = &sun50i_a64_codec_i2s_quirks, },
{
.compatible = "allwinner,sun50i-h6-i2s",
.data = &sun50i_h6_i2s_quirks,
}, {}
}; MODULE_DEVICE_TABLE(of, sun4i_i2s_match);
From: Marcus Cooper codekipper@gmail.com
The i2s block supports multi-lane i2s output however this functionality is only possible in earlier SoCs where the pins are exposed and for the i2s block used for HDMI audio on the later SoCs.
To enable this functionality, an optional property has been added to the bindings.
Signed-off-by: Marcus Cooper codekipper@gmail.com --- sound/soc/sunxi/sun4i-i2s.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-)
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index a8d98696fe7c..a020c3b372a8 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -23,7 +23,7 @@
#define SUN4I_I2S_CTRL_REG 0x00 #define SUN4I_I2S_CTRL_SDO_EN_MASK GENMASK(11, 8) -#define SUN4I_I2S_CTRL_SDO_EN(sdo) BIT(8 + (sdo)) +#define SUN4I_I2S_CTRL_SDO_EN(lines) (((1 << lines) - 1) << 8) #define SUN4I_I2S_CTRL_MODE_MASK BIT(5) #define SUN4I_I2S_CTRL_MODE_SLAVE (1 << 5) #define SUN4I_I2S_CTRL_MODE_MASTER (0 << 5) @@ -614,6 +614,7 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai); int sr, wss, channels; u32 width; + int lines;
channels = params_channels(params); if (channels != 2) { @@ -622,6 +623,13 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, return -EINVAL; }
+ lines = (channels + 1) / 2; + + /* Enable the required output lines */ + regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG, + SUN4I_I2S_CTRL_SDO_EN_MASK, + SUN4I_I2S_CTRL_SDO_EN(lines)); + if (i2s->variant->has_chcfg) { regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG, SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM_MASK, @@ -1389,9 +1397,10 @@ static int sun4i_i2s_init_regmap_fields(struct device *dev, static int sun4i_i2s_probe(struct platform_device *pdev) { struct sun4i_i2s *i2s; + struct snd_soc_dai_driver *soc_dai; struct resource *res; void __iomem *regs; - int irq, ret; + int irq, ret, val;
i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL); if (!i2s) @@ -1456,6 +1465,19 @@ static int sun4i_i2s_probe(struct platform_device *pdev) i2s->capture_dma_data.addr = res->start + SUN4I_I2S_FIFO_RX_REG; i2s->capture_dma_data.maxburst = 8;
+ soc_dai = devm_kmemdup(&pdev->dev, &sun4i_i2s_dai, + sizeof(*soc_dai), GFP_KERNEL); + if (!soc_dai) { + ret = -ENOMEM; + goto err_pm_disable; + } + + if (!of_property_read_u32(pdev->dev.of_node, + "allwinner,playback-channels", &val)) { + if (val >= 2 && val <= 8) + soc_dai->playback.channels_max = val; + } + pm_runtime_enable(&pdev->dev); if (!pm_runtime_enabled(&pdev->dev)) { ret = sun4i_i2s_runtime_resume(&pdev->dev); @@ -1465,7 +1487,7 @@ static int sun4i_i2s_probe(struct platform_device *pdev)
ret = devm_snd_soc_register_component(&pdev->dev, &sun4i_i2s_component, - &sun4i_i2s_dai, 1); + soc_dai, 1); if (ret) { dev_err(&pdev->dev, "Could not register DAI\n"); goto err_suspend;
On Wed, Aug 14, 2019 at 08:08:51AM +0200, codekipper@gmail.com wrote:
From: Marcus Cooper codekipper@gmail.com
The i2s block supports multi-lane i2s output however this functionality is only possible in earlier SoCs where the pins are exposed and for the i2s block used for HDMI audio on the later SoCs.
To enable this functionality, an optional property has been added to the bindings.
Signed-off-by: Marcus Cooper codekipper@gmail.com
Wasn't the plan to support only stereo for now?
Either way, that property should be documented.
Maxime -- Maxime Ripard, Bootlin Embedded Linux and Kernel engineering https://bootlin.com
On Wed, 14 Aug 2019 at 13:08, Maxime Ripard maxime.ripard@bootlin.com wrote:
On Wed, Aug 14, 2019 at 08:08:51AM +0200, codekipper@gmail.com wrote:
From: Marcus Cooper codekipper@gmail.com
The i2s block supports multi-lane i2s output however this functionality is only possible in earlier SoCs where the pins are exposed and for the i2s block used for HDMI audio on the later SoCs.
To enable this functionality, an optional property has been added to the bindings.
Signed-off-by: Marcus Cooper codekipper@gmail.com
Wasn't the plan to support only stereo for now?
Stereo HDMI can be introduced on the H3 and later if we get the first three patches merged. Post those patches is the work to get multi-channel working.
Either way, that property should be documented.
I can do this...but I'm thinking we should bang our heads together to find a solution that we all agree on...especially if we're considering multi-channel tdm support. Thanks, CK
Maxime
Maxime Ripard, Bootlin Embedded Linux and Kernel engineering https://bootlin.com
Hi!
Dne sreda, 14. avgust 2019 ob 08:08:51 CEST je codekipper@gmail.com napisal(a):
From: Marcus Cooper codekipper@gmail.com
The i2s block supports multi-lane i2s output however this functionality is only possible in earlier SoCs where the pins are exposed and for the i2s block used for HDMI audio on the later SoCs.
To enable this functionality, an optional property has been added to the bindings.
Signed-off-by: Marcus Cooper codekipper@gmail.com
sound/soc/sunxi/sun4i-i2s.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-)
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index a8d98696fe7c..a020c3b372a8 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -23,7 +23,7 @@
#define SUN4I_I2S_CTRL_REG 0x00 #define SUN4I_I2S_CTRL_SDO_EN_MASK GENMASK(11, 8) -#define SUN4I_I2S_CTRL_SDO_EN(sdo) BIT(8 +
(sdo))
+#define SUN4I_I2S_CTRL_SDO_EN(lines) (((1 << lines) - 1)
<< 8)
#define SUN4I_I2S_CTRL_MODE_MASK BIT(5) #define SUN4I_I2S_CTRL_MODE_SLAVE (1 << 5) #define SUN4I_I2S_CTRL_MODE_MASTER (0 << 5) @@ -614,6 +614,7 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai); int sr, wss, channels; u32 width;
int lines;
channels = params_channels(params); if (channels != 2) {
@@ -622,6 +623,13 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, return -EINVAL; }
- lines = (channels + 1) / 2;
- /* Enable the required output lines */
- regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
SUN4I_I2S_CTRL_SDO_EN_MASK,
SUN4I_I2S_CTRL_SDO_EN(lines));
As Maxime said before, this doesn't work for TDM. Maybe we can skip this for now, until we agree on method how to describe channel allocation?
- if (i2s->variant->has_chcfg) { regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG,
SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM_MASK,
@@ -1389,9 +1397,10 @@ static int sun4i_i2s_init_regmap_fields(struct device *dev, static int sun4i_i2s_probe(struct platform_device *pdev) { struct sun4i_i2s *i2s;
- struct snd_soc_dai_driver *soc_dai; struct resource *res; void __iomem *regs;
- int irq, ret;
int irq, ret, val;
i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL); if (!i2s)
@@ -1456,6 +1465,19 @@ static int sun4i_i2s_probe(struct platform_device *pdev) i2s->capture_dma_data.addr = res->start + SUN4I_I2S_FIFO_RX_REG; i2s->capture_dma_data.maxburst = 8;
- soc_dai = devm_kmemdup(&pdev->dev, &sun4i_i2s_dai,
sizeof(*soc_dai), GFP_KERNEL);
- if (!soc_dai) {
ret = -ENOMEM;
goto err_pm_disable;
- }
- if (!of_property_read_u32(pdev->dev.of_node,
"allwinner,playback-channels",
&val)) {
if (val >= 2 && val <= 8)
soc_dai->playback.channels_max = val;
- }
Rather than inventing new DT properties, I would rather have multiple snd_soc_dai_driver structures, depending on capabilities of that particular I2S block. That way we avoid some boilerplate code as can be seen here and it's IMO more transparent.
In this case, I would make another snd_soc_dai_driver struct for H3, which has channel_max property set to 8 and from patch 14, additional supported formats.
Best regards, Jernej
pm_runtime_enable(&pdev->dev); if (!pm_runtime_enabled(&pdev->dev)) { ret = sun4i_i2s_runtime_resume(&pdev->dev); @@ -1465,7 +1487,7 @@ static int sun4i_i2s_probe(struct platform_device *pdev)
ret = devm_snd_soc_register_component(&pdev->dev,
&sun4i_i2s_component,
&sun4i_i2s_dai,
1);
if (ret) { dev_err(&pdev->dev, "Could not register DAI\n"); goto err_suspend;soc_dai, 1);
From: Marcus Cooper codekipper@gmail.com
The i2s block can be used to pass PCM data over multiple channels and is sometimes used for the audio side of an HDMI connection.
Signed-off-by: Marcus Cooper codekipper@gmail.com --- sound/soc/sunxi/sun4i-i2s.c | 93 +++++++++++++++++++++++++------------ 1 file changed, 63 insertions(+), 30 deletions(-)
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index a020c3b372a8..a71969167053 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -617,41 +617,74 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, int lines;
channels = params_channels(params); - if (channels != 2) { - dev_err(dai->dev, "Unsupported number of channels: %d\n", - channels); - return -EINVAL; - } - - lines = (channels + 1) / 2; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if ((channels > dai->driver->playback.channels_max) || + (channels < dai->driver->playback.channels_min)) { + dev_err(dai->dev, "Unsupported number of channels: %d\n", + channels); + return -EINVAL; + }
- /* Enable the required output lines */ - regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG, - SUN4I_I2S_CTRL_SDO_EN_MASK, - SUN4I_I2S_CTRL_SDO_EN(lines)); - - 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)); - } + lines = (channels + 1) / 2;
- /* Map the channels for playback and capture */ - i2s->variant->set_txchanmap(i2s, 0, 0x76543210); - i2s->variant->set_rxchanmap(i2s, 0x00003210); + /* Enable the required output lines */ + regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG, + SUN4I_I2S_CTRL_SDO_EN_MASK, + SUN4I_I2S_CTRL_SDO_EN(lines)); + + i2s->variant->set_txchanmap(i2s, 0, 0x10); + i2s->variant->set_txchansel(i2s, 0, channels > 1 ? 2:1); + + if (i2s->variant->set_txchanen) + i2s->variant->set_txchanen(i2s, 0, 2); + + 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)); + + if (channels > 2) { + i2s->variant->set_txchanmap(i2s, 1, 0x32); + i2s->variant->set_txchanoffset(i2s, 1); + i2s->variant->set_txchansel(i2s, 1, + channels > 3 ? 2:1); + i2s->variant->set_txchanen(i2s, 1, 2); + } + if (channels > 4) { + i2s->variant->set_txchanmap(i2s, 2, 0x54); + i2s->variant->set_txchanoffset(i2s, 2); + i2s->variant->set_txchansel(i2s, 2, + channels > 5 ? 2:1); + i2s->variant->set_txchanen(i2s, 2, 2); + } + if (channels > 6) { + i2s->variant->set_txchanmap(i2s, 3, 0x76); + i2s->variant->set_txchanoffset(i2s, 3); + i2s->variant->set_txchansel(i2s, 3, + channels > 6 ? 2:1); + i2s->variant->set_txchanen(i2s, 3, 2); + } + } + } else { + if ((channels > dai->driver->capture.channels_max) || + (channels < dai->driver->capture.channels_min)) { + dev_err(dai->dev, "Unsupported number of channels: %d\n", + channels); + return -EINVAL; + }
- /* Configure the channels */ - i2s->variant->set_txchansel(i2s, 0, channels); - i2s->variant->set_rxchansel(i2s, channels); + /* Map the channels for capture */ + i2s->variant->set_rxchanmap(i2s, 0x10); + i2s->variant->set_rxchansel(i2s, channels);
- if (i2s->variant->set_txchanen) - i2s->variant->set_txchanen(i2s, 0, channels); + if (i2s->variant->set_rxchanen) + i2s->variant->set_rxchanen(i2s, channels);
- if (i2s->variant->set_rxchanen) - i2s->variant->set_rxchanen(i2s, channels); + if (i2s->variant->has_chcfg) + 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)); + }
switch (params_physical_width(params)) { case 16:
From: Marcus Cooper codekipper@gmail.com
Extend the functionality of the driver to include support of 20 and 24 bits per sample for the earlier SoCs.
Newer SoCs can also handle 32bit samples.
Signed-off-by: Marcus Cooper codekipper@gmail.com --- sound/soc/sunxi/sun4i-i2s.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-)
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index a71969167053..d3c8789f70bb 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -690,6 +690,11 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, case 16: width = DMA_SLAVE_BUSWIDTH_2_BYTES; break; + case 20: + case 24: + case 32: + width = DMA_SLAVE_BUSWIDTH_4_BYTES; + break; default: dev_err(dai->dev, "Unsupported physical sample width: %d\n", params_physical_width(params)); @@ -1015,6 +1020,13 @@ static int sun4i_i2s_dai_probe(struct snd_soc_dai *dai) return 0; }
+#define SUN4I_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +#define SUN8I_FORMATS (SUN4I_FORMATS | \ + SNDRV_PCM_FMTBIT_S32_LE) + static struct snd_soc_dai_driver sun4i_i2s_dai = { .probe = sun4i_i2s_dai_probe, .capture = { @@ -1022,14 +1034,14 @@ static struct snd_soc_dai_driver sun4i_i2s_dai = { .channels_min = 2, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_192000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .formats = SUN4I_FORMATS, }, .playback = { .stream_name = "Playback", .channels_min = 2, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_192000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .formats = SUN4I_FORMATS, }, .ops = &sun4i_i2s_dai_ops, .symmetric_rates = 1, @@ -1505,6 +1517,11 @@ static int sun4i_i2s_probe(struct platform_device *pdev) goto err_pm_disable; }
+ if (i2s->variant->has_fmt_set_lrck_period) { + soc_dai->playback.formats = SUN8I_FORMATS; + soc_dai->capture.formats = SUN8I_FORMATS; + } + if (!of_property_read_u32(pdev->dev.of_node, "allwinner,playback-channels", &val)) { if (val >= 2 && val <= 8)
Hi!
Dne sreda, 14. avgust 2019 ob 08:08:53 CEST je codekipper@gmail.com napisal(a):
From: Marcus Cooper codekipper@gmail.com
Extend the functionality of the driver to include support of 20 and 24 bits per sample for the earlier SoCs.
Newer SoCs can also handle 32bit samples.
Signed-off-by: Marcus Cooper codekipper@gmail.com
sound/soc/sunxi/sun4i-i2s.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-)
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index a71969167053..d3c8789f70bb 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -690,6 +690,11 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, case 16: width = DMA_SLAVE_BUSWIDTH_2_BYTES; break;
- case 20:
- case 24:
- case 32:
params_physical_width() returns 32 also for 20 and 24-bit formats, so drop 20 and 24.
Best regards, Jernej
width = DMA_SLAVE_BUSWIDTH_4_BYTES;
default: dev_err(dai->dev, "Unsupported physical sample width:break;
%d\n",
params_physical_width(params));
@@ -1015,6 +1020,13 @@ static int sun4i_i2s_dai_probe(struct snd_soc_dai *dai) return 0; }
+#define SUN4I_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S20_LE | \
SNDRV_PCM_FMTBIT_S24_LE)
+#define SUN8I_FORMATS (SUN4I_FORMATS | \
SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_driver sun4i_i2s_dai = { .probe = sun4i_i2s_dai_probe, .capture = { @@ -1022,14 +1034,14 @@ static struct snd_soc_dai_driver sun4i_i2s_dai = { .channels_min = 2, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
}, .playback = { .stream_name = "Playback", .channels_min = 2, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_192000,.formats = SUN4I_FORMATS,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
}, .ops = &sun4i_i2s_dai_ops, .symmetric_rates = 1,.formats = SUN4I_FORMATS,
@@ -1505,6 +1517,11 @@ static int sun4i_i2s_probe(struct platform_device *pdev) goto err_pm_disable; }
- if (i2s->variant->has_fmt_set_lrck_period) {
soc_dai->playback.formats = SUN8I_FORMATS;
soc_dai->capture.formats = SUN8I_FORMATS;
- }
- if (!of_property_read_u32(pdev->dev.of_node, "allwinner,playback-channels",
&val)) {
if (val >= 2 && val <= 8)
On Wed, 14 Aug 2019 at 10:28, Jernej Škrabec jernej.skrabec@gmail.com wrote:
Hi!
Dne sreda, 14. avgust 2019 ob 08:08:53 CEST je codekipper@gmail.com napisal(a):
From: Marcus Cooper codekipper@gmail.com
Extend the functionality of the driver to include support of 20 and 24 bits per sample for the earlier SoCs.
Newer SoCs can also handle 32bit samples.
Signed-off-by: Marcus Cooper codekipper@gmail.com
sound/soc/sunxi/sun4i-i2s.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-)
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index a71969167053..d3c8789f70bb 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -690,6 +690,11 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, case 16: width = DMA_SLAVE_BUSWIDTH_2_BYTES; break;
case 20:
case 24:
case 32:
params_physical_width() returns 32 also for 20 and 24-bit formats, so drop 20 and 24.
ACK
Best regards, Jernej
width = DMA_SLAVE_BUSWIDTH_4_BYTES;
break; default: dev_err(dai->dev, "Unsupported physical sample width:
%d\n",
params_physical_width(params));
@@ -1015,6 +1020,13 @@ static int sun4i_i2s_dai_probe(struct snd_soc_dai *dai) return 0; }
+#define SUN4I_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S20_LE | \
SNDRV_PCM_FMTBIT_S24_LE)
+#define SUN8I_FORMATS (SUN4I_FORMATS | \
SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_driver sun4i_i2s_dai = { .probe = sun4i_i2s_dai_probe, .capture = { @@ -1022,14 +1034,14 @@ static struct snd_soc_dai_driver sun4i_i2s_dai = { .channels_min = 2, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.formats = SUN4I_FORMATS, }, .playback = { .stream_name = "Playback", .channels_min = 2, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.formats = SUN4I_FORMATS, }, .ops = &sun4i_i2s_dai_ops, .symmetric_rates = 1,
@@ -1505,6 +1517,11 @@ static int sun4i_i2s_probe(struct platform_device *pdev) goto err_pm_disable; }
if (i2s->variant->has_fmt_set_lrck_period) {
soc_dai->playback.formats = SUN8I_FORMATS;
soc_dai->capture.formats = SUN8I_FORMATS;
}
if (!of_property_read_u32(pdev->dev.of_node, "allwinner,playback-channels",
&val)) {
if (val >= 2 && val <= 8)
From: Marcus Cooper codekipper@gmail.com
Bypass the regmap cache when flushing the i2s FIFOs and modify the tables to reflect this.
Signed-off-by: Marcus Cooper codekipper@gmail.com --- sound/soc/sunxi/sun4i-i2s.c | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-)
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index d3c8789f70bb..ecfc1ed79379 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -876,9 +876,11 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) static void sun4i_i2s_start_capture(struct sun4i_i2s *i2s) { /* Flush RX FIFO */ + regcache_cache_bypass(i2s->regmap, true); regmap_update_bits(i2s->regmap, SUN4I_I2S_FIFO_CTRL_REG, SUN4I_I2S_FIFO_CTRL_FLUSH_RX, SUN4I_I2S_FIFO_CTRL_FLUSH_RX); + regcache_cache_bypass(i2s->regmap, false);
/* Clear RX counter */ regmap_write(i2s->regmap, SUN4I_I2S_RX_CNT_REG, 0); @@ -897,9 +899,11 @@ static void sun4i_i2s_start_capture(struct sun4i_i2s *i2s) static void sun4i_i2s_start_playback(struct sun4i_i2s *i2s) { /* Flush TX FIFO */ + regcache_cache_bypass(i2s->regmap, true); regmap_update_bits(i2s->regmap, SUN4I_I2S_FIFO_CTRL_REG, SUN4I_I2S_FIFO_CTRL_FLUSH_TX, SUN4I_I2S_FIFO_CTRL_FLUSH_TX); + regcache_cache_bypass(i2s->regmap, false);
/* Clear TX counter */ regmap_write(i2s->regmap, SUN4I_I2S_TX_CNT_REG, 0); @@ -1053,13 +1057,7 @@ static const struct snd_soc_component_driver sun4i_i2s_component = {
static bool sun4i_i2s_rd_reg(struct device *dev, unsigned int reg) { - switch (reg) { - case SUN4I_I2S_FIFO_TX_REG: - return false; - - default: - return true; - } + return true; }
static bool sun4i_i2s_wr_reg(struct device *dev, unsigned int reg) @@ -1078,6 +1076,8 @@ static bool sun4i_i2s_volatile_reg(struct device *dev, unsigned int reg) { switch (reg) { case SUN4I_I2S_FIFO_RX_REG: + case SUN4I_I2S_FIFO_TX_REG: + case SUN4I_I2S_FIFO_STA_REG: case SUN4I_I2S_INT_STA_REG: case SUN4I_I2S_RX_CNT_REG: case SUN4I_I2S_TX_CNT_REG: @@ -1088,23 +1088,12 @@ static bool sun4i_i2s_volatile_reg(struct device *dev, unsigned int reg) } }
-static bool sun8i_i2s_rd_reg(struct device *dev, unsigned int reg) -{ - switch (reg) { - case SUN8I_I2S_FIFO_TX_REG: - return false; - - default: - return true; - } -} - static bool sun8i_i2s_volatile_reg(struct device *dev, unsigned int reg) { if (reg == SUN8I_I2S_INT_STA_REG) return true; if (reg == SUN8I_I2S_FIFO_TX_REG) - return false; + return true;
return sun4i_i2s_volatile_reg(dev, reg); } @@ -1175,7 +1164,7 @@ static const struct regmap_config sun8i_i2s_regmap_config = { .reg_defaults = sun8i_i2s_reg_defaults, .num_reg_defaults = ARRAY_SIZE(sun8i_i2s_reg_defaults), .writeable_reg = sun4i_i2s_wr_reg, - .readable_reg = sun8i_i2s_rd_reg, + .readable_reg = sun4i_i2s_rd_reg, .volatile_reg = sun8i_i2s_volatile_reg, };
@@ -1188,7 +1177,7 @@ static const struct regmap_config sun50i_i2s_regmap_config = { .reg_defaults = sun50i_i2s_reg_defaults, .num_reg_defaults = ARRAY_SIZE(sun50i_i2s_reg_defaults), .writeable_reg = sun4i_i2s_wr_reg, - .readable_reg = sun8i_i2s_rd_reg, + .readable_reg = sun4i_i2s_rd_reg, .volatile_reg = sun8i_i2s_volatile_reg, };
On Wed, Aug 14, 2019 at 08:08:54AM +0200, codekipper@gmail.com wrote:
From: Marcus Cooper codekipper@gmail.com
Bypass the regmap cache when flushing the i2s FIFOs and modify the tables to reflect this.
Signed-off-by: Marcus Cooper codekipper@gmail.com
This patch looks like it's fixing something while the commit log doesn't mention what is being fixed.
Having some context here would be great.
Maxime
-- Maxime Ripard, Bootlin Embedded Linux and Kernel engineering https://bootlin.com
Dne sreda, 14. avgust 2019 ob 09:20:07 CEST je Maxime Ripard napisal(a):
On Wed, Aug 14, 2019 at 08:08:54AM +0200, codekipper@gmail.com wrote:
From: Marcus Cooper codekipper@gmail.com
Bypass the regmap cache when flushing the i2s FIFOs and modify the tables to reflect this.
Signed-off-by: Marcus Cooper codekipper@gmail.com
This patch looks like it's fixing something while the commit log doesn't mention what is being fixed.
Main issue addressed here is that SUN4I_I2S_FIFO_CTRL_REG has two self-clear registers (SUN4I_I2S_FIFO_CTRL_FLUSH_RX and SUN4I_I2S_FIFO_CTRL_FLUSH_TX) and thus it should be marked as volatile.
Best regards, Jernej
Having some context here would be great.
Maxime
-- Maxime Ripard, Bootlin Embedded Linux and Kernel engineering https://bootlin.com
Hi!
Dne sreda, 14. avgust 2019 ob 08:08:54 CEST je codekipper@gmail.com napisal(a):
From: Marcus Cooper codekipper@gmail.com
Bypass the regmap cache when flushing the i2s FIFOs and modify the tables to reflect this.
Signed-off-by: Marcus Cooper codekipper@gmail.com
sound/soc/sunxi/sun4i-i2s.c | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-)
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index d3c8789f70bb..ecfc1ed79379 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -876,9 +876,11 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) static void sun4i_i2s_start_capture(struct sun4i_i2s *i2s) { /* Flush RX FIFO */
- regcache_cache_bypass(i2s->regmap, true); regmap_update_bits(i2s->regmap, SUN4I_I2S_FIFO_CTRL_REG, SUN4I_I2S_FIFO_CTRL_FLUSH_RX, SUN4I_I2S_FIFO_CTRL_FLUSH_RX);
- regcache_cache_bypass(i2s->regmap, false);
Did you try with regmap_write_bits() instead? This function will unconditionally write bits so it's nicer solution, because you don't have to use regcache_cache_bypass().
/* Clear RX counter */ regmap_write(i2s->regmap, SUN4I_I2S_RX_CNT_REG, 0); @@ -897,9 +899,11 @@ static void sun4i_i2s_start_capture(struct sun4i_i2s *i2s) static void sun4i_i2s_start_playback(struct sun4i_i2s *i2s) { /* Flush TX FIFO */
- regcache_cache_bypass(i2s->regmap, true); regmap_update_bits(i2s->regmap, SUN4I_I2S_FIFO_CTRL_REG, SUN4I_I2S_FIFO_CTRL_FLUSH_TX, SUN4I_I2S_FIFO_CTRL_FLUSH_TX);
- regcache_cache_bypass(i2s->regmap, false);
Ditto.
/* Clear TX counter */ regmap_write(i2s->regmap, SUN4I_I2S_TX_CNT_REG, 0); @@ -1053,13 +1057,7 @@ static const struct snd_soc_component_driver sun4i_i2s_component = {
static bool sun4i_i2s_rd_reg(struct device *dev, unsigned int reg) {
- switch (reg) {
- case SUN4I_I2S_FIFO_TX_REG:
return false;
- default:
return true;
- }
- return true;
Why did you change this? Manual mentions that SUN4I_I2S_FIFO_TX_REG is write- only register. Even if it can be read, then it's better to remove whole function, which will automatically mean that all registers can be read.
}
static bool sun4i_i2s_wr_reg(struct device *dev, unsigned int reg) @@ -1078,6 +1076,8 @@ static bool sun4i_i2s_volatile_reg(struct device *dev, unsigned int reg) { switch (reg) { case SUN4I_I2S_FIFO_RX_REG:
- case SUN4I_I2S_FIFO_TX_REG:
- case SUN4I_I2S_FIFO_STA_REG: case SUN4I_I2S_INT_STA_REG: case SUN4I_I2S_RX_CNT_REG: case SUN4I_I2S_TX_CNT_REG:
SUN4I_I2S_FIFO_CTRL_REG should be put here, because it has two bits which returns to 0 immediately after they are set to 1.
Best regards, Jernej
@@ -1088,23 +1088,12 @@ static bool sun4i_i2s_volatile_reg(struct device *dev, unsigned int reg) } }
-static bool sun8i_i2s_rd_reg(struct device *dev, unsigned int reg) -{
- switch (reg) {
- case SUN8I_I2S_FIFO_TX_REG:
return false;
- default:
return true;
- }
-}
static bool sun8i_i2s_volatile_reg(struct device *dev, unsigned int reg) { if (reg == SUN8I_I2S_INT_STA_REG) return true; if (reg == SUN8I_I2S_FIFO_TX_REG)
return false;
return true;
return sun4i_i2s_volatile_reg(dev, reg);
} @@ -1175,7 +1164,7 @@ static const struct regmap_config sun8i_i2s_regmap_config = { .reg_defaults = sun8i_i2s_reg_defaults, .num_reg_defaults = ARRAY_SIZE(sun8i_i2s_reg_defaults), .writeable_reg = sun4i_i2s_wr_reg,
- .readable_reg = sun8i_i2s_rd_reg,
- .readable_reg = sun4i_i2s_rd_reg, .volatile_reg = sun8i_i2s_volatile_reg,
};
@@ -1188,7 +1177,7 @@ static const struct regmap_config sun50i_i2s_regmap_config = { .reg_defaults = sun50i_i2s_reg_defaults, .num_reg_defaults = ARRAY_SIZE(sun50i_i2s_reg_defaults), .writeable_reg = sun4i_i2s_wr_reg,
- .readable_reg = sun8i_i2s_rd_reg,
- .readable_reg = sun4i_i2s_rd_reg, .volatile_reg = sun8i_i2s_volatile_reg,
};
On Wed, 14 Aug 2019 at 10:38, Jernej Škrabec jernej.skrabec@gmail.com wrote:
Hi!
Dne sreda, 14. avgust 2019 ob 08:08:54 CEST je codekipper@gmail.com napisal(a):
From: Marcus Cooper codekipper@gmail.com
Bypass the regmap cache when flushing the i2s FIFOs and modify the tables to reflect this.
Signed-off-by: Marcus Cooper codekipper@gmail.com
sound/soc/sunxi/sun4i-i2s.c | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-)
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index d3c8789f70bb..ecfc1ed79379 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -876,9 +876,11 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) static void sun4i_i2s_start_capture(struct sun4i_i2s *i2s) { /* Flush RX FIFO */
regcache_cache_bypass(i2s->regmap, true); regmap_update_bits(i2s->regmap, SUN4I_I2S_FIFO_CTRL_REG, SUN4I_I2S_FIFO_CTRL_FLUSH_RX, SUN4I_I2S_FIFO_CTRL_FLUSH_RX);
regcache_cache_bypass(i2s->regmap, false);
Did you try with regmap_write_bits() instead? This function will unconditionally write bits so it's nicer solution, because you don't have to use regcache_cache_bypass().
I didn't....with all the rework I've avoided messing with this change. Now that the dust has settled, I can go back to look at this. Thanks, CK
/* Clear RX counter */ regmap_write(i2s->regmap, SUN4I_I2S_RX_CNT_REG, 0);
@@ -897,9 +899,11 @@ static void sun4i_i2s_start_capture(struct sun4i_i2s *i2s) static void sun4i_i2s_start_playback(struct sun4i_i2s *i2s) { /* Flush TX FIFO */
regcache_cache_bypass(i2s->regmap, true); regmap_update_bits(i2s->regmap, SUN4I_I2S_FIFO_CTRL_REG, SUN4I_I2S_FIFO_CTRL_FLUSH_TX, SUN4I_I2S_FIFO_CTRL_FLUSH_TX);
regcache_cache_bypass(i2s->regmap, false);
Ditto.
/* Clear TX counter */ regmap_write(i2s->regmap, SUN4I_I2S_TX_CNT_REG, 0);
@@ -1053,13 +1057,7 @@ static const struct snd_soc_component_driver sun4i_i2s_component = {
static bool sun4i_i2s_rd_reg(struct device *dev, unsigned int reg) {
switch (reg) {
case SUN4I_I2S_FIFO_TX_REG:
return false;
default:
return true;
}
return true;
Why did you change this? Manual mentions that SUN4I_I2S_FIFO_TX_REG is write- only register. Even if it can be read, then it's better to remove whole function, which will automatically mean that all registers can be read.
}
static bool sun4i_i2s_wr_reg(struct device *dev, unsigned int reg) @@ -1078,6 +1076,8 @@ static bool sun4i_i2s_volatile_reg(struct device *dev, unsigned int reg) { switch (reg) { case SUN4I_I2S_FIFO_RX_REG:
case SUN4I_I2S_FIFO_TX_REG:
case SUN4I_I2S_FIFO_STA_REG: case SUN4I_I2S_INT_STA_REG: case SUN4I_I2S_RX_CNT_REG: case SUN4I_I2S_TX_CNT_REG:
SUN4I_I2S_FIFO_CTRL_REG should be put here, because it has two bits which returns to 0 immediately after they are set to 1.
Best regards, Jernej
@@ -1088,23 +1088,12 @@ static bool sun4i_i2s_volatile_reg(struct device *dev, unsigned int reg) } }
-static bool sun8i_i2s_rd_reg(struct device *dev, unsigned int reg) -{
switch (reg) {
case SUN8I_I2S_FIFO_TX_REG:
return false;
default:
return true;
}
-}
static bool sun8i_i2s_volatile_reg(struct device *dev, unsigned int reg) { if (reg == SUN8I_I2S_INT_STA_REG) return true; if (reg == SUN8I_I2S_FIFO_TX_REG)
return false;
return true; return sun4i_i2s_volatile_reg(dev, reg);
} @@ -1175,7 +1164,7 @@ static const struct regmap_config sun8i_i2s_regmap_config = { .reg_defaults = sun8i_i2s_reg_defaults, .num_reg_defaults = ARRAY_SIZE(sun8i_i2s_reg_defaults), .writeable_reg = sun4i_i2s_wr_reg,
.readable_reg = sun8i_i2s_rd_reg,
.readable_reg = sun4i_i2s_rd_reg, .volatile_reg = sun8i_i2s_volatile_reg,
};
@@ -1188,7 +1177,7 @@ static const struct regmap_config sun50i_i2s_regmap_config = { .reg_defaults = sun50i_i2s_reg_defaults, .num_reg_defaults = ARRAY_SIZE(sun50i_i2s_reg_defaults), .writeable_reg = sun4i_i2s_wr_reg,
.readable_reg = sun8i_i2s_rd_reg,
.readable_reg = sun4i_i2s_rd_reg, .volatile_reg = sun8i_i2s_volatile_reg,
};
On Wed, Aug 14, 2019 at 2:09 PM codekipper@gmail.com wrote:
From: Marcus Cooper codekipper@gmail.com
Bypass the regmap cache when flushing the i2s FIFOs and modify the tables to reflect this.
Signed-off-by: Marcus Cooper codekipper@gmail.com
sound/soc/sunxi/sun4i-i2s.c | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-)
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index d3c8789f70bb..ecfc1ed79379 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -876,9 +876,11 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) static void sun4i_i2s_start_capture(struct sun4i_i2s *i2s) { /* Flush RX FIFO */
regcache_cache_bypass(i2s->regmap, true); regmap_update_bits(i2s->regmap, SUN4I_I2S_FIFO_CTRL_REG, SUN4I_I2S_FIFO_CTRL_FLUSH_RX, SUN4I_I2S_FIFO_CTRL_FLUSH_RX);
regcache_cache_bypass(i2s->regmap, false); /* Clear RX counter */ regmap_write(i2s->regmap, SUN4I_I2S_RX_CNT_REG, 0);
@@ -897,9 +899,11 @@ static void sun4i_i2s_start_capture(struct sun4i_i2s *i2s) static void sun4i_i2s_start_playback(struct sun4i_i2s *i2s) { /* Flush TX FIFO */
regcache_cache_bypass(i2s->regmap, true); regmap_update_bits(i2s->regmap, SUN4I_I2S_FIFO_CTRL_REG, SUN4I_I2S_FIFO_CTRL_FLUSH_TX, SUN4I_I2S_FIFO_CTRL_FLUSH_TX);
regcache_cache_bypass(i2s->regmap, false); /* Clear TX counter */ regmap_write(i2s->regmap, SUN4I_I2S_TX_CNT_REG, 0);
@@ -1053,13 +1057,7 @@ static const struct snd_soc_component_driver sun4i_i2s_component = {
static bool sun4i_i2s_rd_reg(struct device *dev, unsigned int reg) {
switch (reg) {
case SUN4I_I2S_FIFO_TX_REG:
return false;
default:
return true;
}
return true;
The commit log needs to explain why this is relevant. And I'm not sure why one would read back the TX FIFO. Also, if it's always true, just drop the callback.
ChenYu
}
static bool sun4i_i2s_wr_reg(struct device *dev, unsigned int reg) @@ -1078,6 +1076,8 @@ static bool sun4i_i2s_volatile_reg(struct device *dev, unsigned int reg) { switch (reg) { case SUN4I_I2S_FIFO_RX_REG:
case SUN4I_I2S_FIFO_TX_REG:
case SUN4I_I2S_FIFO_STA_REG: case SUN4I_I2S_INT_STA_REG: case SUN4I_I2S_RX_CNT_REG: case SUN4I_I2S_TX_CNT_REG:
@@ -1088,23 +1088,12 @@ static bool sun4i_i2s_volatile_reg(struct device *dev, unsigned int reg) } }
-static bool sun8i_i2s_rd_reg(struct device *dev, unsigned int reg) -{
switch (reg) {
case SUN8I_I2S_FIFO_TX_REG:
return false;
default:
return true;
}
-}
static bool sun8i_i2s_volatile_reg(struct device *dev, unsigned int reg) { if (reg == SUN8I_I2S_INT_STA_REG) return true; if (reg == SUN8I_I2S_FIFO_TX_REG)
return false;
return true; return sun4i_i2s_volatile_reg(dev, reg);
} @@ -1175,7 +1164,7 @@ static const struct regmap_config sun8i_i2s_regmap_config = { .reg_defaults = sun8i_i2s_reg_defaults, .num_reg_defaults = ARRAY_SIZE(sun8i_i2s_reg_defaults), .writeable_reg = sun4i_i2s_wr_reg,
.readable_reg = sun8i_i2s_rd_reg,
.readable_reg = sun4i_i2s_rd_reg, .volatile_reg = sun8i_i2s_volatile_reg,
};
@@ -1188,7 +1177,7 @@ static const struct regmap_config sun50i_i2s_regmap_config = { .reg_defaults = sun50i_i2s_reg_defaults, .num_reg_defaults = ARRAY_SIZE(sun50i_i2s_reg_defaults), .writeable_reg = sun4i_i2s_wr_reg,
.readable_reg = sun8i_i2s_rd_reg,
.readable_reg = sun4i_i2s_rd_reg, .volatile_reg = sun8i_i2s_volatile_reg,
};
-- 2.22.0
participants (7)
-
Chen-Yu Tsai
-
Code Kipper
-
codekipper@gmail.com
-
Jernej Škrabec
-
Jernej Škrabec
-
Mark Brown
-
Maxime Ripard