ASoC: sun4i-i2s: Updates to the driver
Subsystems (5)
v5 cover ASoC: sun4i-i2s: Updates to the driver
From: Marcus Cooper <[email protected]>
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(-)
Signature · 1 line
2.22.0
PATCH v5 1/15 ASoC: sun4i-i2s: Add regmap field to sign extend sample
From: Marcus Cooper <[email protected]>
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.
--- sound/soc/sunxi/sun4i-i2s.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
sound/soc/sunxi/sun4i-i2s.c +16 -0
@@ -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);
Signature · 1 line
2.22.0
PATCH v5 2/15 ASoC: sun4i-i2s: Add set_tdm_slot functionality
In reply to [email protected]From: Marcus Cooper <[email protected]>
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.
--- sound/soc/sunxi/sun4i-i2s.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-)
sound/soc/sunxi/sun4i-i2s.c +21 -2
@@ -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, };
Signature · 1 line
2.22.0
PATCH v5 3/15 ASoC: sun4i-i2s: Correct divider calculations
In reply to [email protected]From: Marcus Cooper <[email protected]>
The clock division circuitry is different on the H3 and later SoCs. The division of bclk is now based on pll2.
--- sound/soc/sunxi/sun4i-i2s.c | 73 +++++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 24 deletions(-)
sound/soc/sunxi/sun4i-i2s.c +49 -24
@@ -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,
Signature · 1 line
2.22.0
PATCH v5 4/15 ASoC: sun4i-i2s: Support more formats on newer SoCs
In reply to [email protected]From: Marcus Cooper <[email protected]>
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.
--- sound/soc/sunxi/sun4i-i2s.c | 87 +++++++++++++++++++++++++++---------- 1 file changed, 63 insertions(+), 24 deletions(-)
sound/soc/sunxi/sun4i-i2s.c +63 -24
@@ -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,
Signature · 1 line
2.22.0
PATCH v5 5/15 ASoC: sun4i-i2s: Add functions for RX and TX channel offsets
In reply to [email protected]From: Marcus Cooper <[email protected]>
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.
--- sound/soc/sunxi/sun4i-i2s.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-)
sound/soc/sunxi/sun4i-i2s.c +23 -1
@@ -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 = {
Signature · 1 line
2.22.0
PATCH v5 6/15 ASoC: sun4i-i2s: Add functions for RX and TX channel enables
In reply to [email protected]From: Marcus Cooper <[email protected]>
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.
--- sound/soc/sunxi/sun4i-i2s.c | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-)
sound/soc/sunxi/sun4i-i2s.c +28 -4
@@ -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 = {
Signature · 1 line
2.22.0
PATCH v5 7/15 ASoC: sun4i-i2s: Add functions for RX and TX channel selects
In reply to [email protected]From: Marcus Cooper <[email protected]>
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.
--- sound/soc/sunxi/sun4i-i2s.c | 87 ++++++++++++++++++++++--------------- 1 file changed, 53 insertions(+), 34 deletions(-)
sound/soc/sunxi/sun4i-i2s.c +53 -34
@@ -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)
Signature · 1 line
2.22.0
PATCH v5 8/15 ASoC: sun4i-i2s: Add functions for channel mapping
In reply to [email protected]From: Marcus Cooper <[email protected]>
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.
--- sound/soc/sunxi/sun4i-i2s.c | 71 +++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 31 deletions(-)
sound/soc/sunxi/sun4i-i2s.c +40 -31
@@ -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)
Signature · 1 line
2.22.0
PATCH v5 9/15 clk: sunxi-ng: h6: Allow I2S to change parent rate
In reply to [email protected]From: Jernej Skrabec <[email protected]>
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.
--- drivers/clk/sunxi-ng/ccu-sun50i-h6.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/clk/sunxi-ng/ccu-sun50i-h6.c +4 -4
@@ -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), }, };
Signature · 1 line
2.22.0
PATCH v5 10/15 dt-bindings: ASoC: sun4i-i2s: Add H6 compatible
In reply to [email protected]From: Jernej Skrabec <[email protected]>
H6 I2S is very similar to H3, except that it supports up to 16 channels and thus few registers have fields on different position.
--- .../devicetree/bindings/sound/allwinner,sun4i-a10-i2s.yaml | 2 ++ 1 file changed, 2 insertions(+)
Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-i2s.yaml +2 -0
@@ -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:
Signature · 1 line
2.22.0
PATCH v5 11/15 ASoC: sun4i-i2s: Add support for H6 I2S
In reply to [email protected]From: Jernej Skrabec <[email protected]>
H6 I2S is very similar to that in H3, except it supports up to 16 channels.
--- sound/soc/sunxi/sun4i-i2s.c | 148 ++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+)
sound/soc/sunxi/sun4i-i2s.c +148 -0
@@ -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);
Signature · 1 line
2.22.0
PATCH v5 12/15 ASoC: sun4i-i2s: Add multi-lane functionality
In reply to [email protected]From: Marcus Cooper <[email protected]>
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.
--- sound/soc/sunxi/sun4i-i2s.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-)
sound/soc/sunxi/sun4i-i2s.c +25 -3
@@ -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;
Signature · 1 line
2.22.0
PATCH v5 13/15 ASoC: sun4i-i2s: Add multichannel functionality
In reply to [email protected]From: Marcus Cooper <[email protected]>
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.
--- sound/soc/sunxi/sun4i-i2s.c | 93 +++++++++++++++++++++++++------------ 1 file changed, 63 insertions(+), 30 deletions(-)
sound/soc/sunxi/sun4i-i2s.c +63 -30
@@ -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 :
Signature · 1 line
2.22.0
PATCH v5 14/15 ASoc: sun4i-i2s: Add 20, 24 and 32 bit support
In reply to [email protected]From: Marcus Cooper <[email protected]>
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.
--- sound/soc/sunxi/sun4i-i2s.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-)
sound/soc/sunxi/sun4i-i2s.c +19 -2
@@ -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 )
Signature · 1 line
2.22.0
PATCH v5 15/15 ASoC: sun4i-i2s: Adjust regmap settings
In reply to [email protected]From: Marcus Cooper <[email protected]>
Bypass the regmap cache when flushing the i2s FIFOs and modify the tables to reflect this.
--- sound/soc/sunxi/sun4i-i2s.c | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-)
sound/soc/sunxi/sun4i-i2s.c +10 -21
@@ -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, };
Signature · 1 line
2.22.0
Re: [PATCH v5 01/15] ASoC: sun4i-i2s: Add regmap field to sign extend sample
In reply to [email protected]Hi,
Quoted 80 lines · On Wed, Aug 14, 2019 at 08:08:40AM +0200, [email protected] wrote:
From: Marcus Cooper <[email protected]>
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 <[email protected]>--- sound/soc/sunxi/sun4i-i2s.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)sound/soc/sunxi/sun4i-i2s.c +9
-0@@ -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),
sound/soc/sunxi/sun4i-i2s.c static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = { · line 1013
Quoted 7 lines
sound/soc/sunxi/sun4i-i2s.c +1
-0@@ -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
Re: [PATCH v5 02/15] ASoC: sun4i-i2s: Add set_tdm_slot functionality
In reply to [email protected]Hi,
Quoted 37 lines · On Wed, Aug 14, 2019 at 08:08:41AM +0200…
From: Marcus Cooper <[email protected]>
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 <[email protected]>--- sound/soc/sunxi/sun4i-i2s.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-)sound/soc/sunxi/sun4i-i2s.c +4
-1@@ -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 */ sound/soc/sunxi/sun4i-i2s.c +0
-0regmap_field_write (i2s->field_fmt_sext,0 );
sound/soc/sunxi/sun4i-i2s.c static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, · line 457
Quoted 6 lines
sound/soc/sunxi/sun4i-i2s.c +2
-1@@ -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
Re: [PATCH v5 03/15] ASoC: sun4i-i2s: Correct divider calculations
In reply to [email protected]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 [email protected])
Quoted 33 lines · On Wed, Aug 14, 2019 at 08:08:42AM +0200…
From: Marcus Cooper <[email protected]>
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 <[email protected]>--- sound/soc/sunxi/sun4i-i2s.c | 73 +++++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 24 deletions(-)sound/soc/sunxi/sun4i-i2s.c +0
-4@@ -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;
sound/soc/sunxi/sun4i-i2s.c static const struct sun4i_i2s_clk_div sun4i_i2s_bclk_div[] = { · line 214
Quoted 8 lines
sound/soc/sunxi/sun4i-i2s.c +4
-1@@ -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.
Quoted 21 lines
sound/soc/sunxi/sun4i-i2s.c +15
-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 },
sound/soc/sunxi/sun4i-i2s.c static const struct sun4i_i2s_clk_div sun4i_i2s_mclk_div[] = { · line 247
Quoted 10 lines
sound/soc/sunxi/sun4i-i2s.c +3
-2{ .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
Re: [PATCH v5 04/15] ASoC: sun4i-i2s: Support more formats on newer SoCs
In reply to [email protected]Quoted 122 lines · On Wed, Aug 14, 2019 at 08:08:43AM +0200…
From: Marcus Cooper <[email protected]>
There is a need to support more formats on the newer SoCs(H3 and later). Extend the formats supported to include
DSP_AandDSP_Bmodes.Signed-off-by Marcus Cooper <[email protected]>--- sound/soc/sunxi/sun4i-i2s.c | 87 +++++++++++++++++++++++++++---------- 1 file changed, 63 insertions(+), 24 deletions(-)sound/soc/sunxi/sun4i-i2s.c +34
-24@@ -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 ;
sound/soc/sunxi/sun4i-i2s.c static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) · line 549
Quoted 10 lines
sound/soc/sunxi/sun4i-i2s.c +7
-0} +/* +* 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
Re: [PATCH v5 15/15] ASoC: sun4i-i2s: Adjust regmap settings
In reply to [email protected]Quoted 6 lines · On Wed, Aug 14, 2019 at 08:08:54AM +0200…
From: Marcus Cooper <[email protected]>
Bypass the regmap cache when flushing the i2s FIFOs and modify the tables to reflect this.
Signed-off-by Marcus Cooper <[email protected]>
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
Re: [PATCH v5 12/15] ASoC: sun4i-i2s: Add multi-lane functionality
In reply to [email protected]Quoted 10 lines · On Wed, Aug 14, 2019 at 08:08:51AM +0200…
From: Marcus Cooper <[email protected]>
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 <[email protected]>
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
Re: [PATCH v5 11/15] ASoC: sun4i-i2s: Add support for H6 I2S
In reply to [email protected]Hi!
Dne sreda, 14. avgust 2019 ob 08:08:50 CEST je [email protected] napisal(a):
Quoted 6 lines
From: Jernej Skrabec <[email protected]>
H6 I2S is very similar to that in H3, except it supports up to 16 channels.
Signed-off-by Jernej Skrabec <[email protected]>
Your Signed-off-by is missing here and on all other patches made originally by me.
Best regards,
Jernej
Quoted 31 lines
--- sound/soc/sunxi/sun4i-i2s.c | 148 ++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+)sound/soc/sunxi/sun4i-i2s.c +15
-0@@ -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 ;/**
sound/soc/sunxi/sun4i-i2s.c static void sun8i_i2s_set_rxchanoffset(const struct · line 462
Quoted 8 lines
sound/soc/sunxi/sun4i-i2s.c +4
-0@@ -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),
sound/soc/sunxi/sun4i-i2s.c static void sun8i_i2s_set_rxchanoffset(const struct · line 463
Quoted 1 line
sound/soc/sunxi/sun4i-i2s.c +1
-0+
SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET_MASK,
sound/soc/sunxi/sun4i-i2s.c static void sun8i_i2s_set_rxchanoffset(const struct · line 464
Quoted 1 line
sound/soc/sunxi/sun4i-i2s.c +1
-0+
SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET(i2s->offset));
Quoted 17 lines
sound/soc/sunxi/sun4i-i2s.c +12
-0+ } + +} + +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)
{
sound/soc/sunxi/sun4i-i2s.c static void sun8i_i2s_set_rxchanen(const struct · line 502
Quoted 10 lines
sound/soc/sunxi/sun4i-i2s.c +6
-0@@ -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),
sound/soc/sunxi/sun4i-i2s.c static void sun8i_i2s_set_rxchanen(const struct · line 504
Quoted 2 lines
sound/soc/sunxi/sun4i-i2s.c +2
-0+ SUN50I_H6_I2S_TX_CHAN_EN_MASK, +
SUN50I_H6_I2S_TX_CHAN_EN(channel));
Quoted 15 lines
sound/soc/sunxi/sun4i-i2s.c +10
-0+ } +} + +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, intoutput, int channel)
{
sound/soc/sunxi/sun4i-i2s.c static void sun8i_i2s_set_rxchansel(const struct · line 557
Quoted 9 lines
sound/soc/sunxi/sun4i-i2s.c +5
-0@@ -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),
sound/soc/sunxi/sun4i-i2s.c static void sun8i_i2s_set_rxchansel(const struct · line 559
Quoted 2 lines
sound/soc/sunxi/sun4i-i2s.c +2
-0+ SUN50I_H6_I2S_TX_CHAN_SEL_MASK, +
SUN50I_H6_I2S_TX_CHAN_SEL(channel));
Quoted 15 lines
sound/soc/sunxi/sun4i-i2s.c +10
-0+ } +} + +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, intoutput, int channel)
{
sound/soc/sunxi/sun4i-i2s.c static void sun8i_i2s_set_rxchanmap(const struct · line 602
Quoted 10 lines
sound/soc/sunxi/sun4i-i2s.c +5
-0@@ -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);
Quoted 55 lines
sound/soc/sunxi/sun4i-i2s.c +36
-0+ } +} + +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
Quoted 1 line
sun50i_a64_codec_i2s_quirks= { .set_rxchanmap =
sun4i_i2s_set_rxchanmap,
sound/soc/sunxi/sun4i-i2s.c static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = {
Quoted 12 lines
};
sound/soc/sunxi/sun4i-i2s.c +10
-0+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),
sound/soc/sunxi/sun4i-i2s.c
Quoted 2 lines
sound/soc/sunxi/sun4i-i2s.c +2
-0+ .field_fmt_sr = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 6), + .field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG,
7, 7),
sound/soc/sunxi/sun4i-i2s.c
Quoted 2 lines
sound/soc/sunxi/sun4i-i2s.c +2
-0+ .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 19, 19), + .field_fmt_mode = REG_FIELD(SUN4I_I2S_CTRL_REG, 4,
5),
sound/soc/sunxi/sun4i-i2s.c
Quoted 1 line
sound/soc/sunxi/sun4i-i2s.c +1
-0+ .field_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG,
4, 5),
Quoted 27 lines
sound/soc/sunxi/sun4i-i2s.c +17
-0+ .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);
Jernej Škrabec
maintainerRe: [linux-sunxi] [PATCH v5 12/15] ASoC: sun4i-i2s: Add multi-lane functionality
In reply to [email protected]Hi!
Dne sreda, 14. avgust 2019 ob 08:08:51 CEST je [email protected] napisal(a):
Quoted 19 lines
From: Marcus Cooper <[email protected]>
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 <[email protected]>--- sound/soc/sunxi/sun4i-i2s.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-)sound/soc/sunxi/sun4i-i2s.c +0
-0
sound/soc/sunxi/sun4i-i2s.c line 26
Quoted 4 lines
sound/soc/sunxi/sun4i-i2s.c +0
-1@@ -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))
sound/soc/sunxi/sun4i-i2s.c line 26
Quoted 1 line
sound/soc/sunxi/sun4i-i2s.c +1
-0+#define SUN4I_I2S_CTRL_SDO_EN (lines) (((1 << lines) - 1)
<< 8)
Quoted 12 lines
sound/soc/sunxi/sun4i-i2s.c +1
-0#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 ) {
sound/soc/sunxi/sun4i-i2s.c static int sun4i_i2s_hw_params(struct snd_pcm_substream · line 631
Quoted 9 lines
sound/soc/sunxi/sun4i-i2s.c +6
-0@@ -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?
sound/soc/sunxi/sun4i-i2s.c static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
Quoted 4 lines
sound/soc/sunxi/sun4i-i2s.c +1
-0+if (i2s->variant->has_chcfg) { regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG,
SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM_MASK,
Quoted 15 lines
sound/soc/sunxi/sun4i-i2s.c +2
-1@@ -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 ;
sound/soc/sunxi/sun4i-i2s.c static int sun4i_i2s_probe(struct platform_device · line 1476
Quoted 10 lines
sound/soc/sunxi/sun4i-i2s.c +9
-0+ 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)) {
sound/soc/sunxi/sun4i-i2s.c static int sun4i_i2s_probe(struct platform_device *pdev) · line 1480
Quoted 4 lines
sound/soc/sunxi/sun4i-i2s.c +4
-0+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
sound/soc/sunxi/sun4i-i2s.c static int sun4i_i2s_probe(struct platform_device · line 1490
Quoted 8 lines
sound/soc/sunxi/sun4i-i2s.c +0
-0pm_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,
sound/soc/sunxi/sun4i-i2s.c static int sun4i_i2s_probe(struct platform_device · line 1469
Quoted 1 line
sound/soc/sunxi/sun4i-i2s.c +0
-1- &sun4i_i2s_dai,
1);
Quoted 4 lines
sound/soc/sunxi/sun4i-i2s.c +1
-0+ soc_dai, 1);if (ret) {dev_err (&pdev->dev,"Could not register DAI\n" );goto
err_suspend;
Jernej Škrabec
maintainerRe: [linux-sunxi] [PATCH v5 14/15] ASoc: sun4i-i2s: Add 20, 24 and 32 bit support
In reply to [email protected]Hi!
Dne sreda, 14. avgust 2019 ob 08:08:53 CEST je [email protected] napisal(a):
Quoted 17 lines
From: Marcus Cooper <[email protected]>
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 <[email protected]>--- sound/soc/sunxi/sun4i-i2s.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-)sound/soc/sunxi/sun4i-i2s.c +0
-0
sound/soc/sunxi/sun4i-i2s.c static int sun4i_i2s_hw_params(struct snd_pcm_substream · line 695
Quoted 6 lines
sound/soc/sunxi/sun4i-i2s.c +3
-0@@ -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
sound/soc/sunxi/sun4i-i2s.c static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
Quoted 4 lines
sound/soc/sunxi/sun4i-i2s.c +2
-0+ width =DMA_SLAVE_BUSWIDTH_4_BYTES ; +break ;default : dev_err(dai->dev, "Unsupported physical sample width:
%d\n",
Quoted 33 lines
sound/soc/sunxi/sun4i-i2s.c +9
-2params_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,
sound/soc/sunxi/sun4i-i2s.c static int sun4i_i2s_probe(struct platform_device · line 1526
Quoted 10 lines
sound/soc/sunxi/sun4i-i2s.c +5
-0@@ -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)) {
Quoted 1 line
sound/soc/sunxi/sun4i-i2s.c +0
-0if (val >=2 && val <=8 )
Jernej Škrabec
maintainerRe: [linux-sunxi] [PATCH v5 15/15] ASoC: sun4i-i2s: Adjust regmap settings
In reply to [email protected]Hi!
Dne sreda, 14. avgust 2019 ob 08:08:54 CEST je [email protected] napisal(a):
Quoted 18 lines
From: Marcus Cooper <[email protected]>
Bypass the regmap cache when flushing the i2s FIFOs and modify the tables to reflect this.
Signed-off-by Marcus Cooper <[email protected]>--- sound/soc/sunxi/sun4i-i2s.c | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-)sound/soc/sunxi/sun4i-i2s.c +0
-0@@ -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)
{
sound/soc/sunxi/sun4i-i2s.c static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) · line 883
Quoted 6 lines
sound/soc/sunxi/sun4i-i2s.c +2
-0/* 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().
sound/soc/sunxi/sun4i-i2s.c static void sun4i_i2s_start_capture(struct sun4i_i2s · line 906
Quoted 12 lines
sound/soc/sunxi/sun4i-i2s.c +2
-0/* 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.
Quoted 6 lines
sound/soc/sunxi/sun4i-i2s.c +0
-0/* 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 = {
sound/soc/sunxi/sun4i-i2s.c static const struct snd_soc_component_driver · line 1061
Quoted 10 lines
sound/soc/sunxi/sun4i-i2s.c +1
-7static 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.
sound/soc/sunxi/sun4i-i2s.c static bool sun4i_i2s_volatile_reg(struct device *dev, · line 1083
Quoted 12 lines
sound/soc/sunxi/sun4i-i2s.c +0
-0}static bool
sun4i_i2s_wr_reg(struct device *dev, unsigned int reg)sound/soc/sunxi/sun4i-i2s.c +2
-0@@ -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
Quoted 42 lines
sound/soc/sunxi/sun4i-i2s.c +3
-14@@ -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, };
Re: [linux-sunxi] [PATCH v5 15/15] ASoC: sun4i-i2s: Adjust regmap settings
Quoted 25 lines · Jernej Škrabec
Hi!
Dne sreda, 14. avgust 2019 ob 08:08:54 CEST je [email protected] napisal(a):
Quoted 20 lines
From: Marcus Cooper <[email protected]>
Bypass the regmap cache when flushing the i2s FIFOs and modify the tables to reflect this.
Signed-off-by Marcus Cooper <[email protected]>--- sound/soc/sunxi/sun4i-i2s.c | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-)sound/soc/sunxi/sun4i-i2s.c +0
-0@@ -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);
Quoted 8 lines
Quoted 4 lines
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 useregcache_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
Quoted 103 lines
sound/soc/sunxi/sun4i-i2s.c static void sun4i_i2s_start_capture(struct sun4i_i2s · line 906
Quoted 13 lines
/* Clear RX counter */ regmap_write(i2s->regmap, SUN4I_I2S_RX_CNT_REG, 0);sound/soc/sunxi/sun4i-i2s.c +2
-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.
Quoted 6 lines
sound/soc/sunxi/sun4i-i2s.c +0
-0/* 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 = {sound/soc/sunxi/sun4i-i2s.c static const struct snd_soc_component_driver · line 1061
Quoted 10 lines
sound/soc/sunxi/sun4i-i2s.c +1
-7static 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_REGis 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.sound/soc/sunxi/sun4i-i2s.c static bool sun4i_i2s_volatile_reg(struct device *dev, · line 1083
Quoted 12 lines
sound/soc/sunxi/sun4i-i2s.c +0
-0}static bool
sun4i_i2s_wr_reg(struct device *dev, unsigned int reg)sound/soc/sunxi/sun4i-i2s.c +2
-0@@ -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_REGshould be put here, because it has two bits which returns to 0 immediately after they are set to 1.Best regards,
JernejQuoted 42 lines
sound/soc/sunxi/sun4i-i2s.c +3
-14@@ -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, };
Signature · 3 lines
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-/JYPxA39Uh5TLH3MbocFF+G/[email protected] To view this discussion on the web, visit https://groups.google.com/d/msgid/linux-sunxi/CAEKpxBnNzH3KANfaY7p0qv%3DXTAyHAk3YSWqFbt_hpWat%2BxSBxw%40mail.gmail.com.
Re: [linux-sunxi] [PATCH v5 14/15] ASoc: sun4i-i2s: Add 20, 24 and 32 bit support
In reply to Jernej ŠkrabecQuoted 17 lines · Jernej Škrabec
Hi!
Dne sreda, 14. avgust 2019 ob 08:08:53 CEST je [email protected] napisal(a):
Quoted 12 lines
From: Marcus Cooper <[email protected]>
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 <[email protected]>--- sound/soc/sunxi/sun4i-i2s.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-)
Quoted 14 lines
sound/soc/sunxi/sun4i-i2s.c static int sun4i_i2s_hw_params(struct snd_pcm_substream · line 695
Quoted 11 lines
sound/soc/sunxi/sun4i-i2s.c +3
-0@@ -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
Quoted 58 lines
Best regards,
Jernejsound/soc/sunxi/sun4i-i2s.c static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
Quoted 4 lines
sound/soc/sunxi/sun4i-i2s.c +2
-0+ width =DMA_SLAVE_BUSWIDTH_4_BYTES ; +break ;default : dev_err(dai->dev, "Unsupported physical sample width:%d\n",
Quoted 33 lines
sound/soc/sunxi/sun4i-i2s.c +9
-2params_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,sound/soc/sunxi/sun4i-i2s.c static int sun4i_i2s_probe(struct platform_device · line 1526
Quoted 10 lines
sound/soc/sunxi/sun4i-i2s.c +5
-0@@ -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)) {
Quoted 1 line
sound/soc/sunxi/sun4i-i2s.c +0
-0if (val >=2 && val <=8 )
Signature · 3 lines
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-/JYPxA39Uh5TLH3MbocFF+G/[email protected] To view this discussion on the web, visit https://groups.google.com/d/msgid/linux-sunxi/CAEKpxBmsLKt_mKFC3%3DwR9n%2BhK9njhh7drLV2EMrzKahv%3Ds4dTw%40mail.gmail.com.
Mark Brown
maintainerRe: [PATCH v5 02/15] ASoC: sun4i-i2s: Add set_tdm_slot functionality
In reply to [email protected]Quoted 4 lines · On Wed, Aug 14, 2019 at 08:08:41AM +0200…
From: Marcus Cooper <[email protected]>
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.
sound/soc/sunxi/sun4i-i2s.c static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, · line 457
Quoted 4 lines
sound/soc/sunxi/sun4i-i2s.c +2
-1return 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.
sound/soc/sunxi/sun4i-i2s.c static int sun4i_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, · line 712
Quoted 13 lines
sound/soc/sunxi/sun4i-i2s.c +13
-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 ; +}
No validation of the parameters here?
Mark Brown
maintainerRe: [PATCH v5 03/15] ASoC: sun4i-i2s: Correct divider calculations
In reply to [email protected]sound/soc/sunxi/sun4i-i2s.c static int sun4i_i2s_set_clk_rate(struct snd_soc_dai *dai, · line 348
Quoted 10 lines · On Wed, Aug 14, 2019 at 08:08:42AM +0200…
sound/soc/sunxi/sun4i-i2s.c +10
-0+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?
Re: [PATCH v5 11/15] ASoC: sun4i-i2s: Add support for H6 I2S
In reply to Jernej ŠkrabecQuoted 7 lines · Jernej Škrabec
Hi!
Dne sreda, 14. avgust 2019 ob 08:08:50 CEST je [email protected] napisal(a):
Quoted 2 lines
From: Jernej Skrabec <[email protected]>
Quoted 7 lines
Quoted 4 lines
H6 I2S is very similar to that in H3, except it supports up to 16 channels.
Signed-off-by Jernej Skrabec <[email protected]>Your Signed-off-by is missing here and on all other patches made originally by me.
ACK
Quoted 242 lines
Best regards,
JernejQuoted 31 lines
--- sound/soc/sunxi/sun4i-i2s.c | 148 ++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+)sound/soc/sunxi/sun4i-i2s.c +15
-0@@ -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 ;/** sound/soc/sunxi/sun4i-i2s.c static void sun8i_i2s_set_rxchanoffset(const struct · line 462
Quoted 8 lines
sound/soc/sunxi/sun4i-i2s.c +4
-0@@ -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),
sound/soc/sunxi/sun4i-i2s.c static void sun8i_i2s_set_rxchanoffset(const struct · line 463
Quoted 1 line
sound/soc/sunxi/sun4i-i2s.c +1
-0+
SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET_MASK,sound/soc/sunxi/sun4i-i2s.c static void sun8i_i2s_set_rxchanoffset(const struct · line 464
Quoted 1 line
sound/soc/sunxi/sun4i-i2s.c +1
-0+
SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET(i2s->offset));Quoted 17 lines
sound/soc/sunxi/sun4i-i2s.c +12
-0+ } + +} + +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)
{sound/soc/sunxi/sun4i-i2s.c static void sun8i_i2s_set_rxchanen(const struct · line 502
Quoted 10 lines
sound/soc/sunxi/sun4i-i2s.c +6
-0@@ -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),
sound/soc/sunxi/sun4i-i2s.c static void sun8i_i2s_set_rxchanen(const struct · line 504
Quoted 2 lines
sound/soc/sunxi/sun4i-i2s.c +2
-0+ SUN50I_H6_I2S_TX_CHAN_EN_MASK, +
SUN50I_H6_I2S_TX_CHAN_EN(channel));Quoted 15 lines
sound/soc/sunxi/sun4i-i2s.c +10
-0+ } +} + +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, intoutput, int channel)
{sound/soc/sunxi/sun4i-i2s.c static void sun8i_i2s_set_rxchansel(const struct · line 557
Quoted 9 lines
sound/soc/sunxi/sun4i-i2s.c +5
-0@@ -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),
sound/soc/sunxi/sun4i-i2s.c static void sun8i_i2s_set_rxchansel(const struct · line 559
Quoted 2 lines
sound/soc/sunxi/sun4i-i2s.c +2
-0+ SUN50I_H6_I2S_TX_CHAN_SEL_MASK, +
SUN50I_H6_I2S_TX_CHAN_SEL(channel));Quoted 15 lines
sound/soc/sunxi/sun4i-i2s.c +10
-0+ } +} + +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, intoutput, int channel)
{sound/soc/sunxi/sun4i-i2s.c static void sun8i_i2s_set_rxchanmap(const struct · line 602
Quoted 10 lines
sound/soc/sunxi/sun4i-i2s.c +5
-0@@ -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);
Quoted 55 lines
sound/soc/sunxi/sun4i-i2s.c +36
-0+ } +} + +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_quirksQuoted 1 line
sun50i_a64_codec_i2s_quirks= { .set_rxchanmap =
sun4i_i2s_set_rxchanmap,sound/soc/sunxi/sun4i-i2s.c static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = {
Quoted 12 lines
};
sound/soc/sunxi/sun4i-i2s.c +10
-0+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),
sound/soc/sunxi/sun4i-i2s.c
Quoted 2 lines
sound/soc/sunxi/sun4i-i2s.c +2
-0+ .field_fmt_sr = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 6), + .field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG,7, 7),
sound/soc/sunxi/sun4i-i2s.c
Quoted 2 lines
sound/soc/sunxi/sun4i-i2s.c +2
-0+ .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 19, 19), + .field_fmt_mode = REG_FIELD(SUN4I_I2S_CTRL_REG, 4,5),
Quoted 1 line
+ .field_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG,
4, 5),
Quoted 27 lines
sound/soc/sunxi/sun4i-i2s.c +17
-0+ .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);
Signature · 3 lines
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-/JYPxA39Uh5TLH3MbocFF+G/[email protected] To view this discussion on the web, visit https://groups.google.com/d/msgid/linux-sunxi/CAEKpxBnLfpUdnksxVVrJt7TESsLq%3DfnkezBnRZsC1kHRrDrSOQ%40mail.gmail.com.
Re: [PATCH v5 12/15] ASoC: sun4i-i2s: Add multi-lane functionality
In reply to Maxime RipardQuoted 8 lines · Maxime Ripard
Quoted 6 lines · On Wed, Aug 14, 2019 at 08:08:51AM +0200…
From: Marcus Cooper <[email protected]>
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.
Quoted 6 lines
Quoted 4 lines
To enable this functionality, an optional property has been added to the bindings.
Signed-off-by Marcus Cooper <[email protected]>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.
Quoted 2 lines
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
Quoted 6 lines
Maxime
--
Maxime Ripard, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
Re: [PATCH v5 04/15] ASoC: sun4i-i2s: Support more formats on newer SoCs
In reply to Maxime RipardQuoted 126 lines · Maxime Ripard
Quoted 124 lines · On Wed, Aug 14, 2019 at 08:08:43AM +0200…
From: Marcus Cooper <[email protected]>
There is a need to support more formats on the newer SoCs(H3 and later). Extend the formats supported to include
DSP_AandDSP_Bmodes.Signed-off-by Marcus Cooper <[email protected]>--- sound/soc/sunxi/sun4i-i2s.c | 87 +++++++++++++++++++++++++++---------- 1 file changed, 63 insertions(+), 24 deletions(-)sound/soc/sunxi/sun4i-i2s.c +34
-24@@ -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 ; }
Quoted 13 lines
sound/soc/sunxi/sun4i-i2s.c static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) · line 549
Quoted 8 lines
sound/soc/sunxi/sun4i-i2s.c +7
-0+/* +* 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).
Quoted 3 lines
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
Quoted 7 lines
Maxime
--
Maxime Ripard, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
Re: [PATCH v5 01/15] ASoC: sun4i-i2s: Add regmap field to sign extend sample
In reply to Maxime RipardQuoted 85 lines · Maxime Ripard
Hi,
Quoted 81 lines · On Wed, Aug 14, 2019 at 08:08:40AM +0200…
From: Marcus Cooper <[email protected]>
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 <[email protected]>--- sound/soc/sunxi/sun4i-i2s.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)sound/soc/sunxi/sun4i-i2s.c +9
-0@@ -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),
Quoted 8 lines
sound/soc/sunxi/sun4i-i2s.c static const struct sun4i_i2s_quirks sun4i_a10_i2s_quirks = { · line 929
Quoted 6 lines
sound/soc/sunxi/sun4i-i2s.c +1
-0.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
Quoted 7 lines
Maxime
--
Maxime Ripard, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
Jernej Škrabec
maintainerRe: [linux-sunxi] Re: [PATCH v5 15/15] ASoC: sun4i-i2s: Adjust regmap settings
In reply to Maxime RipardDne sreda, 14. avgust 2019 ob 09:20:07 CEST je Maxime Ripard napisal(a):
Quoted 10 lines · On Wed, Aug 14, 2019 at 08:08:54AM +0200, [email protected] wrote:
Quoted 6 lines
From: Marcus Cooper <[email protected]>
Bypass the regmap cache when flushing the i2s FIFOs and modify the tables to reflect this.
Signed-off-by Marcus Cooper <[email protected]>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
Quoted 9 lines
Having some context here would be great.
Maxime
--
Maxime Ripard, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
Re: [PATCH v5 15/15] ASoC: sun4i-i2s: Adjust regmap settings
In reply to [email protected]Quoted 41 lines · On Wed, Aug 14, 2019 at 2:09 PM <[email protected]> wrote:
From: Marcus Cooper <[email protected]>
Bypass the regmap cache when flushing the i2s FIFOs and modify the tables to reflect this.
Signed-off-by Marcus Cooper <[email protected]>--- sound/soc/sunxi/sun4i-i2s.c | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-)sound/soc/sunxi/sun4i-i2s.c +4
-0@@ -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 = {
sound/soc/sunxi/sun4i-i2s.c static const struct snd_soc_component_driver sun4i_i2s_component = { · line 1060
Quoted 10 lines
sound/soc/sunxi/sun4i-i2s.c +1
-7static 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
Quoted 58 lines
sound/soc/sunxi/sun4i-i2s.c +5
-15}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
Re: [PATCH v5 02/15] ASoC: sun4i-i2s: Add set_tdm_slot functionality
In reply to Mark BrownQuoted 9 lines · Mark Brown
Quoted 4 lines · On Wed, Aug 14, 2019 at 08:08:41AM +0200…
From: Marcus Cooper <[email protected]>
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
Quoted 8 lines
sound/soc/sunxi/sun4i-i2s.c static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, · line 457
Quoted 5 lines
return sun4i_i2s_set_clk_rate(dai,
params_rate(params),sound/soc/sunxi/sun4i-i2s.c +2
-1- 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
Quoted 16 lines
Quoted 4 lines
sound/soc/sunxi/sun4i-i2s.c +3
-0+static int sun4i_i2s_set_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, + unsigned int rx_mask,sound/soc/sunxi/sun4i-i2s.c static int sun4i_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, · line 712
Quoted 10 lines
sound/soc/sunxi/sun4i-i2s.c +10
-0+ 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
Re: [PATCH v5 02/15] ASoC: sun4i-i2s: Add set_tdm_slot functionality
In reply to Maxime RipardQuoted 43 lines · Maxime Ripard
Hi,
Quoted 39 lines · On Wed, Aug 14, 2019 at 08:08:41AM +0200…
From: Marcus Cooper <[email protected]>
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 <[email protected]>--- sound/soc/sunxi/sun4i-i2s.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-)sound/soc/sunxi/sun4i-i2s.c +4
-1@@ -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);sound/soc/sunxi/sun4i-i2s.c +0
-0@@ -450,7 +453,8 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,regmap_field_write (i2s->field_fmt_sr, sr);
Quoted 6 lines
sound/soc/sunxi/sun4i-i2s.c static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, · line 457
Quoted 4 lines
sound/soc/sunxi/sun4i-i2s.c +2
-1return 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
Quoted 11 lines
On the H3 (and all related ones), the
CHAN_CFG_TX_SLOT_NUMand_RX_SLOT_NUMfields in theCHAN_CFGregister need to be set to the number of slots as well.Maxime
--
Maxime Ripard, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
Re: [linux-sunxi] [PATCH v5 09/15] clk: sunxi-ng: h6: Allow I2S to change parent rate
In reply to [email protected]Quoted 9 lines · On Wed, Aug 14, 2019 at 2:09 PM <[email protected]…
From: Jernej Skrabec <[email protected]>
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_PARENTflag to I2S clocks.Signed-off-by Jernej Skrabec <[email protected]>
This lacks your SoB. Please reply and I can add it when applying.
ChenYu
Re: [linux-sunxi] [PATCH v5 09/15] clk: sunxi-ng: h6: Allow I2S to change parent rate
Thanks....I've added to my next patch series but if you could add it when applying that would be great.
BR,
CK
Quoted 15 lines · Chen-Yu Tsai
Quoted 9 lines · On Wed, Aug 14, 2019 at 2:09 PM <[email protected]…
From: Jernej Skrabec <[email protected]>
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_PARENTflag to I2S clocks.Signed-off-by Jernej Skrabec <[email protected]>This lacks your SoB. Please reply and I can add it when applying.
ChenYu
Re: [linux-sunxi] [PATCH v5 09/15] clk: sunxi-ng: h6: Allow I2S to change parent rate
Quoted 3 lines · Code Kipper
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
Quoted 24 lines
BR,
CKQuoted 8 lines · Chen-Yu Tsai
Quoted 6 lines · On Wed, Aug 14, 2019 at 2:09 PM <[email protected]…
From: Jernej Skrabec <[email protected]>
I2S doesn't work if parent rate couldn't be change. Difference between wanted and actual rate is too big.
Quoted 7 lines
Quoted 3 lines
Fix this by adding
CLK_SET_RATE_PARENTflag to I2S clocks.Signed-off-by Jernej Skrabec <[email protected]>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-/JYPxA39Uh5TLH3MbocFF+G/[email protected] To view this discussion on the web, visit https://groups.google.com/d/msgid/linux-sunxi/CAEKpxBnxf%3Diejk887A7qFkzt3BXVxiRS1PeA45aZYR9DsBAU4Q%40mail.gmail.com.
Re: [PATCH v5 09/15] clk: sunxi-ng: h6: Allow I2S to change parent rate
In reply to [email protected]Quoted 9 lines · On Wed, 14 Aug 2019 at 08:09, <[email protected]…
From: Jernej Skrabec <[email protected]>
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_PARENTflag to I2S clocks.Signed-off-by Jernej Skrabec <[email protected]>
Quoted 47 lines
--- drivers/clk/sunxi-ng/ccu-sun50i-h6.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)drivers/clk/sunxi-ng/ccu-sun50i-h6.c +4
-5@@ -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
Re: [linux-sunxi] Re: [PATCH v5 09/15] clk: sunxi-ng: h6: Allow I2S to change parent rate
Quoted 5 lines · Code Kipper
Quoted 3 lines · On Wed, 14 Aug 2019 at 08:09, <[email protected]…
From: Jernej Skrabec <[email protected]>
Quoted 8 lines
Quoted 6 lines
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_PARENTflag to I2S clocks.Signed-off-by Jernej Skrabec <[email protected]>Signed-off-by Marcus Cooper <[email protected]>
Applied for 5.4