2019-08-14 06:10:13

by Code Kipper

[permalink] [raw]
Subject: [PATCH v5 00/15] 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(-)

--
2.22.0


2019-08-14 06:10:13

by Code Kipper

[permalink] [raw]
Subject: [PATCH v5 01/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.

Signed-off-by: Marcus Cooper <[email protected]>
---
sound/soc/sunxi/sun4i-i2s.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)

diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
index 793457394efe..8201334a059b 100644
--- a/sound/soc/sunxi/sun4i-i2s.c
+++ b/sound/soc/sunxi/sun4i-i2s.c
@@ -135,6 +135,7 @@ struct sun4i_i2s;
* @field_fmt_bclk: regmap field to set clk polarity.
* @field_fmt_lrclk: regmap field to set frame polarity.
* @field_fmt_mode: regmap field to set the operational mode.
+ * @field_fmt_sext: regmap field to set the sign extension.
* @field_txchanmap: location of the tx channel mapping register.
* @field_rxchanmap: location of the rx channel mapping register.
* @field_txchansel: location of the tx channel select bit fields.
@@ -159,6 +160,7 @@ struct sun4i_i2s_quirks {
struct reg_field field_fmt_bclk;
struct reg_field field_fmt_lrclk;
struct reg_field field_fmt_mode;
+ struct reg_field field_fmt_sext;
struct reg_field field_txchanmap;
struct reg_field field_rxchanmap;
struct reg_field field_txchansel;
@@ -186,6 +188,7 @@ struct sun4i_i2s {
struct regmap_field *field_fmt_bclk;
struct regmap_field *field_fmt_lrclk;
struct regmap_field *field_fmt_mode;
+ struct regmap_field *field_fmt_sext;
struct regmap_field *field_txchanmap;
struct regmap_field *field_rxchanmap;
struct regmap_field *field_txchansel;
@@ -345,6 +348,9 @@ static int sun4i_i2s_set_clk_rate(struct snd_soc_dai *dai,
SUN8I_I2S_FMT0_LRCK_PERIOD_MASK,
SUN8I_I2S_FMT0_LRCK_PERIOD(32));

+ /* Set sign extension to pad out LSB with 0 */
+ regmap_field_write(i2s->field_fmt_sext, 0);
+
return 0;
}

@@ -917,6 +923,7 @@ static const struct sun4i_i2s_quirks sun4i_a10_i2s_quirks = {
.field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
.has_slave_select_bit = true,
.field_fmt_mode = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1),
+ .field_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG, 8, 8),
.field_txchanmap = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31),
.field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31),
.field_txchansel = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2),
@@ -936,6 +943,7 @@ static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = {
.field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
.has_slave_select_bit = true,
.field_fmt_mode = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1),
+ .field_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG, 8, 8),
.field_txchanmap = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31),
.field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31),
.field_txchansel = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2),
@@ -979,6 +987,7 @@ static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = {
.field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
.field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 19, 19),
.field_fmt_mode = REG_FIELD(SUN4I_I2S_CTRL_REG, 4, 5),
+ .field_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG, 4, 5),
.field_txchanmap = REG_FIELD(SUN8I_I2S_TX_CHAN_MAP_REG, 0, 31),
.field_rxchanmap = REG_FIELD(SUN8I_I2S_RX_CHAN_MAP_REG, 0, 31),
.field_txchansel = REG_FIELD(SUN8I_I2S_TX_CHAN_SEL_REG, 0, 2),
@@ -998,6 +1007,7 @@ static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = {
.field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6),
.field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
.field_fmt_mode = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1),
+ .field_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG, 8, 8),
.field_txchanmap = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31),
.field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31),
.field_txchansel = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2),
@@ -1045,6 +1055,12 @@ static int sun4i_i2s_init_regmap_fields(struct device *dev,
if (IS_ERR(i2s->field_fmt_mode))
return PTR_ERR(i2s->field_fmt_mode);

+ i2s->field_fmt_sext =
+ devm_regmap_field_alloc(dev, i2s->regmap,
+ i2s->variant->field_fmt_sext);
+ if (IS_ERR(i2s->field_fmt_sext))
+ return PTR_ERR(i2s->field_fmt_sext);
+
i2s->field_txchanmap =
devm_regmap_field_alloc(dev, i2s->regmap,
i2s->variant->field_txchanmap);
--
2.22.0

2019-08-14 06:10:21

by Code Kipper

[permalink] [raw]
Subject: [PATCH v5 08/15] ASoC: sun4i-i2s: Add functions for channel mapping

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.

Signed-off-by: Marcus Cooper <[email protected]>
---
sound/soc/sunxi/sun4i-i2s.c | 71 +++++++++++++++++++++----------------
1 file changed, 40 insertions(+), 31 deletions(-)

diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
index 0b98adde0717..6de3cb41aaf6 100644
--- a/sound/soc/sunxi/sun4i-i2s.c
+++ b/sound/soc/sunxi/sun4i-i2s.c
@@ -141,8 +141,6 @@ struct sun4i_i2s;
* @field_fmt_lrclk: regmap field to set frame polarity.
* @field_fmt_mode: regmap field to set the operational mode.
* @field_fmt_sext: regmap field to set the sign extension.
- * @field_txchanmap: location of the tx channel mapping register.
- * @field_rxchanmap: location of the rx channel mapping register.
*/
struct sun4i_i2s_quirks {
bool has_reset;
@@ -162,8 +160,6 @@ struct sun4i_i2s_quirks {
struct reg_field field_fmt_lrclk;
struct reg_field field_fmt_mode;
struct reg_field field_fmt_sext;
- struct reg_field field_txchanmap;
- struct reg_field field_rxchanmap;

s8 (*get_sr)(const struct sun4i_i2s *, int);
s8 (*get_wss)(const struct sun4i_i2s *, int);
@@ -174,6 +170,8 @@ struct sun4i_i2s_quirks {
void (*set_rxchanen)(const struct sun4i_i2s *, int);
void (*set_txchansel)(const struct sun4i_i2s *, int, int);
void (*set_rxchansel)(const struct sun4i_i2s *, int);
+ void (*set_txchanmap)(const struct sun4i_i2s *, int, int);
+ void (*set_rxchanmap)(const struct sun4i_i2s *, int);
};

struct sun4i_i2s {
@@ -195,8 +193,6 @@ struct sun4i_i2s {
struct regmap_field *field_fmt_lrclk;
struct regmap_field *field_fmt_mode;
struct regmap_field *field_fmt_sext;
- struct regmap_field *field_txchanmap;
- struct regmap_field *field_rxchanmap;

const struct sun4i_i2s_quirks *variant;

@@ -499,6 +495,31 @@ static void sun8i_i2s_set_rxchansel(const struct sun4i_i2s *i2s, int channel)
SUN8I_I2S_TX_CHAN_SEL(channel));
}

+static void sun4i_i2s_set_txchanmap(const struct sun4i_i2s *i2s, int output,
+ int channel)
+{
+ regmap_write(i2s->regmap, SUN4I_I2S_TX_CHAN_MAP_REG, channel);
+}
+
+static void sun8i_i2s_set_txchanmap(const struct sun4i_i2s *i2s, int output,
+ int channel)
+{
+ if (output >= 0 && output < 4) {
+ regmap_write(i2s->regmap,
+ SUN8I_I2S_TX_CHAN_MAP_REG + (output * 4), channel);
+ }
+}
+
+static void sun4i_i2s_set_rxchanmap(const struct sun4i_i2s *i2s, int channel)
+{
+ regmap_write(i2s->regmap, SUN4I_I2S_RX_CHAN_MAP_REG, channel);
+}
+
+static void sun8i_i2s_set_rxchanmap(const struct sun4i_i2s *i2s, int channel)
+{
+ regmap_write(i2s->regmap, SUN8I_I2S_RX_CHAN_MAP_REG, channel);
+}
+
static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -524,8 +545,8 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
}

/* Map the channels for playback and capture */
- regmap_field_write(i2s->field_txchanmap, 0x76543210);
- regmap_field_write(i2s->field_rxchanmap, 0x00003210);
+ i2s->variant->set_txchanmap(i2s, 0, 0x76543210);
+ i2s->variant->set_rxchanmap(i2s, 0x00003210);

/* Configure the channels */
i2s->variant->set_txchansel(i2s, 0, channels);
@@ -1077,13 +1098,13 @@ static const struct sun4i_i2s_quirks sun4i_a10_i2s_quirks = {
.has_slave_select_bit = true,
.field_fmt_mode = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1),
.field_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG, 8, 8),
- .field_txchanmap = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31),
- .field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31),
.get_sr = sun4i_i2s_get_sr,
.get_wss = sun4i_i2s_get_wss,
.set_format = sun4i_i2s_set_format,
.set_txchansel = sun4i_i2s_set_txchansel,
.set_rxchansel = sun4i_i2s_set_rxchansel,
+ .set_txchanmap = sun4i_i2s_set_txchanmap,
+ .set_rxchanmap = sun4i_i2s_set_rxchanmap,
};

static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = {
@@ -1098,13 +1119,13 @@ static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = {
.has_slave_select_bit = true,
.field_fmt_mode = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1),
.field_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG, 8, 8),
- .field_txchanmap = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31),
- .field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31),
.get_sr = sun4i_i2s_get_sr,
.get_wss = sun4i_i2s_get_wss,
.set_format = sun4i_i2s_set_format,
.set_txchansel = sun4i_i2s_set_txchansel,
.set_rxchansel = sun4i_i2s_set_rxchansel,
+ .set_txchanmap = sun4i_i2s_set_txchanmap,
+ .set_rxchanmap = sun4i_i2s_set_rxchanmap,
};

static const struct sun4i_i2s_quirks sun8i_a83t_i2s_quirks = {
@@ -1118,13 +1139,13 @@ static const struct sun4i_i2s_quirks sun8i_a83t_i2s_quirks = {
.field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
.has_slave_select_bit = true,
.field_fmt_mode = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1),
- .field_txchanmap = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31),
- .field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31),
.get_sr = sun8i_i2s_get_sr_wss,
.get_wss = sun8i_i2s_get_sr_wss,
.set_format = sun4i_i2s_set_format,
.set_txchansel = sun4i_i2s_set_txchansel,
.set_rxchansel = sun4i_i2s_set_rxchansel,
+ .set_txchanmap = sun4i_i2s_set_txchanmap,
+ .set_rxchanmap = sun4i_i2s_set_rxchanmap,
};

static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = {
@@ -1142,8 +1163,6 @@ static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = {
.field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 19, 19),
.field_fmt_mode = REG_FIELD(SUN4I_I2S_CTRL_REG, 4, 5),
.field_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG, 4, 5),
- .field_txchanmap = REG_FIELD(SUN8I_I2S_TX_CHAN_MAP_REG, 0, 31),
- .field_rxchanmap = REG_FIELD(SUN8I_I2S_RX_CHAN_MAP_REG, 0, 31),
.get_sr = sun8i_i2s_get_sr_wss,
.get_wss = sun8i_i2s_get_sr_wss,
.set_format = sun8i_i2s_set_format,
@@ -1153,6 +1172,8 @@ static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = {
.set_rxchanen = sun8i_i2s_set_rxchanen,
.set_txchansel = sun8i_i2s_set_txchansel,
.set_rxchansel = sun8i_i2s_set_rxchansel,
+ .set_txchanmap = sun8i_i2s_set_txchanmap,
+ .set_rxchanmap = sun8i_i2s_set_rxchanmap,
};

static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = {
@@ -1167,13 +1188,13 @@ static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = {
.field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
.field_fmt_mode = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1),
.field_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG, 8, 8),
- .field_txchanmap = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31),
- .field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31),
.get_sr = sun4i_i2s_get_sr,
.get_wss = sun4i_i2s_get_wss,
.set_format = sun4i_i2s_set_format,
.set_txchansel = sun4i_i2s_set_txchansel,
.set_rxchansel = sun4i_i2s_set_rxchansel,
+ .set_txchanmap = sun4i_i2s_set_txchanmap,
+ .set_rxchanmap = sun4i_i2s_set_rxchanmap,
};

static int sun4i_i2s_init_regmap_fields(struct device *dev,
@@ -1218,19 +1239,7 @@ static int sun4i_i2s_init_regmap_fields(struct device *dev,
i2s->field_fmt_sext =
devm_regmap_field_alloc(dev, i2s->regmap,
i2s->variant->field_fmt_sext);
- if (IS_ERR(i2s->field_fmt_sext))
- return PTR_ERR(i2s->field_fmt_sext);
-
- i2s->field_txchanmap =
- devm_regmap_field_alloc(dev, i2s->regmap,
- i2s->variant->field_txchanmap);
- if (IS_ERR(i2s->field_txchanmap))
- return PTR_ERR(i2s->field_txchanmap);
-
- i2s->field_rxchanmap =
- devm_regmap_field_alloc(dev, i2s->regmap,
- i2s->variant->field_rxchanmap);
- return PTR_ERR_OR_ZERO(i2s->field_rxchanmap);
+ return PTR_ERR_OR_ZERO(i2s->field_fmt_sext);
}

static int sun4i_i2s_probe(struct platform_device *pdev)
--
2.22.0

2019-08-14 06:10:27

by Code Kipper

[permalink] [raw]
Subject: [PATCH v5 14/15] ASoc: sun4i-i2s: Add 20, 24 and 32 bit support

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(-)

diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
index a71969167053..d3c8789f70bb 100644
--- a/sound/soc/sunxi/sun4i-i2s.c
+++ b/sound/soc/sunxi/sun4i-i2s.c
@@ -690,6 +690,11 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
case 16:
width = DMA_SLAVE_BUSWIDTH_2_BYTES;
break;
+ case 20:
+ case 24:
+ case 32:
+ width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ break;
default:
dev_err(dai->dev, "Unsupported physical sample width: %d\n",
params_physical_width(params));
@@ -1015,6 +1020,13 @@ static int sun4i_i2s_dai_probe(struct snd_soc_dai *dai)
return 0;
}

+#define SUN4I_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+#define SUN8I_FORMATS (SUN4I_FORMATS | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
static struct snd_soc_dai_driver sun4i_i2s_dai = {
.probe = sun4i_i2s_dai_probe,
.capture = {
@@ -1022,14 +1034,14 @@ static struct snd_soc_dai_driver sun4i_i2s_dai = {
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .formats = SUN4I_FORMATS,
},
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .formats = SUN4I_FORMATS,
},
.ops = &sun4i_i2s_dai_ops,
.symmetric_rates = 1,
@@ -1505,6 +1517,11 @@ static int sun4i_i2s_probe(struct platform_device *pdev)
goto err_pm_disable;
}

+ if (i2s->variant->has_fmt_set_lrck_period) {
+ soc_dai->playback.formats = SUN8I_FORMATS;
+ soc_dai->capture.formats = SUN8I_FORMATS;
+ }
+
if (!of_property_read_u32(pdev->dev.of_node,
"allwinner,playback-channels", &val)) {
if (val >= 2 && val <= 8)
--
2.22.0

2019-08-14 06:10:32

by Code Kipper

[permalink] [raw]
Subject: [PATCH v5 15/15] ASoC: sun4i-i2s: Adjust regmap settings

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(-)

diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
index d3c8789f70bb..ecfc1ed79379 100644
--- a/sound/soc/sunxi/sun4i-i2s.c
+++ b/sound/soc/sunxi/sun4i-i2s.c
@@ -876,9 +876,11 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
static void sun4i_i2s_start_capture(struct sun4i_i2s *i2s)
{
/* Flush RX FIFO */
+ regcache_cache_bypass(i2s->regmap, true);
regmap_update_bits(i2s->regmap, SUN4I_I2S_FIFO_CTRL_REG,
SUN4I_I2S_FIFO_CTRL_FLUSH_RX,
SUN4I_I2S_FIFO_CTRL_FLUSH_RX);
+ regcache_cache_bypass(i2s->regmap, false);

/* Clear RX counter */
regmap_write(i2s->regmap, SUN4I_I2S_RX_CNT_REG, 0);
@@ -897,9 +899,11 @@ static void sun4i_i2s_start_capture(struct sun4i_i2s *i2s)
static void sun4i_i2s_start_playback(struct sun4i_i2s *i2s)
{
/* Flush TX FIFO */
+ regcache_cache_bypass(i2s->regmap, true);
regmap_update_bits(i2s->regmap, SUN4I_I2S_FIFO_CTRL_REG,
SUN4I_I2S_FIFO_CTRL_FLUSH_TX,
SUN4I_I2S_FIFO_CTRL_FLUSH_TX);
+ regcache_cache_bypass(i2s->regmap, false);

/* Clear TX counter */
regmap_write(i2s->regmap, SUN4I_I2S_TX_CNT_REG, 0);
@@ -1053,13 +1057,7 @@ static const struct snd_soc_component_driver sun4i_i2s_component = {

static bool sun4i_i2s_rd_reg(struct device *dev, unsigned int reg)
{
- switch (reg) {
- case SUN4I_I2S_FIFO_TX_REG:
- return false;
-
- default:
- return true;
- }
+ return true;
}

static bool sun4i_i2s_wr_reg(struct device *dev, unsigned int reg)
@@ -1078,6 +1076,8 @@ static bool sun4i_i2s_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case SUN4I_I2S_FIFO_RX_REG:
+ case SUN4I_I2S_FIFO_TX_REG:
+ case SUN4I_I2S_FIFO_STA_REG:
case SUN4I_I2S_INT_STA_REG:
case SUN4I_I2S_RX_CNT_REG:
case SUN4I_I2S_TX_CNT_REG:
@@ -1088,23 +1088,12 @@ static bool sun4i_i2s_volatile_reg(struct device *dev, unsigned int reg)
}
}

-static bool sun8i_i2s_rd_reg(struct device *dev, unsigned int reg)
-{
- switch (reg) {
- case SUN8I_I2S_FIFO_TX_REG:
- return false;
-
- default:
- return true;
- }
-}
-
static bool sun8i_i2s_volatile_reg(struct device *dev, unsigned int reg)
{
if (reg == SUN8I_I2S_INT_STA_REG)
return true;
if (reg == SUN8I_I2S_FIFO_TX_REG)
- return false;
+ return true;

return sun4i_i2s_volatile_reg(dev, reg);
}
@@ -1175,7 +1164,7 @@ static const struct regmap_config sun8i_i2s_regmap_config = {
.reg_defaults = sun8i_i2s_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(sun8i_i2s_reg_defaults),
.writeable_reg = sun4i_i2s_wr_reg,
- .readable_reg = sun8i_i2s_rd_reg,
+ .readable_reg = sun4i_i2s_rd_reg,
.volatile_reg = sun8i_i2s_volatile_reg,
};

@@ -1188,7 +1177,7 @@ static const struct regmap_config sun50i_i2s_regmap_config = {
.reg_defaults = sun50i_i2s_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(sun50i_i2s_reg_defaults),
.writeable_reg = sun4i_i2s_wr_reg,
- .readable_reg = sun8i_i2s_rd_reg,
+ .readable_reg = sun4i_i2s_rd_reg,
.volatile_reg = sun8i_i2s_volatile_reg,
};

--
2.22.0

2019-08-14 06:10:41

by Code Kipper

[permalink] [raw]
Subject: [PATCH v5 12/15] ASoC: sun4i-i2s: Add multi-lane functionality

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(-)

diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
index a8d98696fe7c..a020c3b372a8 100644
--- a/sound/soc/sunxi/sun4i-i2s.c
+++ b/sound/soc/sunxi/sun4i-i2s.c
@@ -23,7 +23,7 @@

#define SUN4I_I2S_CTRL_REG 0x00
#define SUN4I_I2S_CTRL_SDO_EN_MASK GENMASK(11, 8)
-#define SUN4I_I2S_CTRL_SDO_EN(sdo) BIT(8 + (sdo))
+#define SUN4I_I2S_CTRL_SDO_EN(lines) (((1 << lines) - 1) << 8)
#define SUN4I_I2S_CTRL_MODE_MASK BIT(5)
#define SUN4I_I2S_CTRL_MODE_SLAVE (1 << 5)
#define SUN4I_I2S_CTRL_MODE_MASTER (0 << 5)
@@ -614,6 +614,7 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
int sr, wss, channels;
u32 width;
+ int lines;

channels = params_channels(params);
if (channels != 2) {
@@ -622,6 +623,13 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}

+ lines = (channels + 1) / 2;
+
+ /* Enable the required output lines */
+ regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
+ SUN4I_I2S_CTRL_SDO_EN_MASK,
+ SUN4I_I2S_CTRL_SDO_EN(lines));
+
if (i2s->variant->has_chcfg) {
regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG,
SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM_MASK,
@@ -1389,9 +1397,10 @@ static int sun4i_i2s_init_regmap_fields(struct device *dev,
static int sun4i_i2s_probe(struct platform_device *pdev)
{
struct sun4i_i2s *i2s;
+ struct snd_soc_dai_driver *soc_dai;
struct resource *res;
void __iomem *regs;
- int irq, ret;
+ int irq, ret, val;

i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
if (!i2s)
@@ -1456,6 +1465,19 @@ static int sun4i_i2s_probe(struct platform_device *pdev)
i2s->capture_dma_data.addr = res->start + SUN4I_I2S_FIFO_RX_REG;
i2s->capture_dma_data.maxburst = 8;

+ soc_dai = devm_kmemdup(&pdev->dev, &sun4i_i2s_dai,
+ sizeof(*soc_dai), GFP_KERNEL);
+ if (!soc_dai) {
+ ret = -ENOMEM;
+ goto err_pm_disable;
+ }
+
+ if (!of_property_read_u32(pdev->dev.of_node,
+ "allwinner,playback-channels", &val)) {
+ if (val >= 2 && val <= 8)
+ soc_dai->playback.channels_max = val;
+ }
+
pm_runtime_enable(&pdev->dev);
if (!pm_runtime_enabled(&pdev->dev)) {
ret = sun4i_i2s_runtime_resume(&pdev->dev);
@@ -1465,7 +1487,7 @@ static int sun4i_i2s_probe(struct platform_device *pdev)

ret = devm_snd_soc_register_component(&pdev->dev,
&sun4i_i2s_component,
- &sun4i_i2s_dai, 1);
+ soc_dai, 1);
if (ret) {
dev_err(&pdev->dev, "Could not register DAI\n");
goto err_suspend;
--
2.22.0

2019-08-14 06:10:47

by Code Kipper

[permalink] [raw]
Subject: [PATCH v5 13/15] ASoC: sun4i-i2s: Add multichannel functionality

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.

Signed-off-by: Marcus Cooper <[email protected]>
---
sound/soc/sunxi/sun4i-i2s.c | 93 +++++++++++++++++++++++++------------
1 file changed, 63 insertions(+), 30 deletions(-)

diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
index a020c3b372a8..a71969167053 100644
--- a/sound/soc/sunxi/sun4i-i2s.c
+++ b/sound/soc/sunxi/sun4i-i2s.c
@@ -617,41 +617,74 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
int lines;

channels = params_channels(params);
- if (channels != 2) {
- dev_err(dai->dev, "Unsupported number of channels: %d\n",
- channels);
- return -EINVAL;
- }
-
- lines = (channels + 1) / 2;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if ((channels > dai->driver->playback.channels_max) ||
+ (channels < dai->driver->playback.channels_min)) {
+ dev_err(dai->dev, "Unsupported number of channels: %d\n",
+ channels);
+ return -EINVAL;
+ }

- /* Enable the required output lines */
- regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
- SUN4I_I2S_CTRL_SDO_EN_MASK,
- SUN4I_I2S_CTRL_SDO_EN(lines));
-
- if (i2s->variant->has_chcfg) {
- regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG,
- SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM_MASK,
- SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM(channels));
- regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG,
- SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM_MASK,
- SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM(channels));
- }
+ lines = (channels + 1) / 2;

- /* Map the channels for playback and capture */
- i2s->variant->set_txchanmap(i2s, 0, 0x76543210);
- i2s->variant->set_rxchanmap(i2s, 0x00003210);
+ /* Enable the required output lines */
+ regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
+ SUN4I_I2S_CTRL_SDO_EN_MASK,
+ SUN4I_I2S_CTRL_SDO_EN(lines));
+
+ i2s->variant->set_txchanmap(i2s, 0, 0x10);
+ i2s->variant->set_txchansel(i2s, 0, channels > 1 ? 2:1);
+
+ if (i2s->variant->set_txchanen)
+ i2s->variant->set_txchanen(i2s, 0, 2);
+
+ if (i2s->variant->has_chcfg) {
+ regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG,
+ SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM_MASK,
+ SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM(channels));
+
+ if (channels > 2) {
+ i2s->variant->set_txchanmap(i2s, 1, 0x32);
+ i2s->variant->set_txchanoffset(i2s, 1);
+ i2s->variant->set_txchansel(i2s, 1,
+ channels > 3 ? 2:1);
+ i2s->variant->set_txchanen(i2s, 1, 2);
+ }
+ if (channels > 4) {
+ i2s->variant->set_txchanmap(i2s, 2, 0x54);
+ i2s->variant->set_txchanoffset(i2s, 2);
+ i2s->variant->set_txchansel(i2s, 2,
+ channels > 5 ? 2:1);
+ i2s->variant->set_txchanen(i2s, 2, 2);
+ }
+ if (channels > 6) {
+ i2s->variant->set_txchanmap(i2s, 3, 0x76);
+ i2s->variant->set_txchanoffset(i2s, 3);
+ i2s->variant->set_txchansel(i2s, 3,
+ channels > 6 ? 2:1);
+ i2s->variant->set_txchanen(i2s, 3, 2);
+ }
+ }
+ } else {
+ if ((channels > dai->driver->capture.channels_max) ||
+ (channels < dai->driver->capture.channels_min)) {
+ dev_err(dai->dev, "Unsupported number of channels: %d\n",
+ channels);
+ return -EINVAL;
+ }

- /* Configure the channels */
- i2s->variant->set_txchansel(i2s, 0, channels);
- i2s->variant->set_rxchansel(i2s, channels);
+ /* Map the channels for capture */
+ i2s->variant->set_rxchanmap(i2s, 0x10);
+ i2s->variant->set_rxchansel(i2s, channels);

- if (i2s->variant->set_txchanen)
- i2s->variant->set_txchanen(i2s, 0, channels);
+ if (i2s->variant->set_rxchanen)
+ i2s->variant->set_rxchanen(i2s, channels);

- if (i2s->variant->set_rxchanen)
- i2s->variant->set_rxchanen(i2s, channels);
+ if (i2s->variant->has_chcfg)
+ regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG,
+ SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM_MASK,
+ SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM(channels));
+ }

switch (params_physical_width(params)) {
case 16:
--
2.22.0

2019-08-14 06:10:49

by Code Kipper

[permalink] [raw]
Subject: [PATCH v5 11/15] ASoC: sun4i-i2s: Add support for H6 I2S

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]>
---
sound/soc/sunxi/sun4i-i2s.c | 148 ++++++++++++++++++++++++++++++++++++
1 file changed, 148 insertions(+)

diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
index 6de3cb41aaf6..a8d98696fe7c 100644
--- a/sound/soc/sunxi/sun4i-i2s.c
+++ b/sound/soc/sunxi/sun4i-i2s.c
@@ -121,6 +121,21 @@
#define SUN8I_I2S_RX_CHAN_SEL_REG 0x54
#define SUN8I_I2S_RX_CHAN_MAP_REG 0x58

+/* Defines required for sun50i-h6 support */
+#define SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET_MASK GENMASK(21, 20)
+#define SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET(offset) ((offset) << 20)
+#define SUN50I_H6_I2S_TX_CHAN_SEL_MASK GENMASK(19, 16)
+#define SUN50I_H6_I2S_TX_CHAN_SEL(chan) ((chan - 1) << 16)
+#define SUN50I_H6_I2S_TX_CHAN_EN_MASK GENMASK(15, 0)
+#define SUN50I_H6_I2S_TX_CHAN_EN(num_chan) (((1 << num_chan) - 1))
+
+#define SUN50I_H6_I2S_TX_CHAN_MAP0_REG 0x44
+#define SUN50I_H6_I2S_TX_CHAN_MAP1_REG 0x48
+
+#define SUN50I_H6_I2S_RX_CHAN_SEL_REG 0x64
+#define SUN50I_H6_I2S_RX_CHAN_MAP0_REG 0x68
+#define SUN50I_H6_I2S_RX_CHAN_MAP1_REG 0x6C
+
struct sun4i_i2s;

/**
@@ -440,6 +455,25 @@ static void sun8i_i2s_set_rxchanoffset(const struct sun4i_i2s *i2s)
SUN8I_I2S_TX_CHAN_OFFSET(i2s->offset));
}

+static void sun50i_h6_i2s_set_txchanoffset(const struct sun4i_i2s *i2s, int output)
+{
+ if (output >= 0 && output < 4) {
+ regmap_update_bits(i2s->regmap,
+ SUN8I_I2S_TX_CHAN_SEL_REG + (output * 4),
+ SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET_MASK,
+ SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET(i2s->offset));
+ }
+
+}
+
+static void sun50i_h6_i2s_set_rxchanoffset(const struct sun4i_i2s *i2s)
+{
+ regmap_update_bits(i2s->regmap,
+ SUN50I_H6_I2S_RX_CHAN_SEL_REG,
+ SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET_MASK,
+ SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET(i2s->offset));
+}
+
static void sun8i_i2s_set_txchanen(const struct sun4i_i2s *i2s, int output,
int channel)
{
@@ -459,6 +493,26 @@ static void sun8i_i2s_set_rxchanen(const struct sun4i_i2s *i2s, int channel)
SUN8I_I2S_TX_CHAN_EN(channel));
}

+
+static void sun50i_h6_i2s_set_txchanen(const struct sun4i_i2s *i2s, int output,
+ int channel)
+{
+ if (output >= 0 && output < 4) {
+ regmap_update_bits(i2s->regmap,
+ SUN8I_I2S_TX_CHAN_SEL_REG + (output * 4),
+ SUN50I_H6_I2S_TX_CHAN_EN_MASK,
+ SUN50I_H6_I2S_TX_CHAN_EN(channel));
+ }
+}
+
+static void sun50i_h6_i2s_set_rxchanen(const struct sun4i_i2s *i2s, int channel)
+{
+ regmap_update_bits(i2s->regmap,
+ SUN50I_H6_I2S_RX_CHAN_SEL_REG,
+ SUN50I_H6_I2S_TX_CHAN_EN_MASK,
+ SUN50I_H6_I2S_TX_CHAN_EN(channel));
+}
+
static void sun4i_i2s_set_txchansel(const struct sun4i_i2s *i2s, int output,
int channel)
{
@@ -495,6 +549,25 @@ static void sun8i_i2s_set_rxchansel(const struct sun4i_i2s *i2s, int channel)
SUN8I_I2S_TX_CHAN_SEL(channel));
}

+static void sun50i_h6_i2s_set_txchansel(const struct sun4i_i2s *i2s, int output,
+ int channel)
+{
+ if (output >= 0 && output < 4) {
+ regmap_update_bits(i2s->regmap,
+ SUN8I_I2S_TX_CHAN_SEL_REG + (output * 4),
+ SUN50I_H6_I2S_TX_CHAN_SEL_MASK,
+ SUN50I_H6_I2S_TX_CHAN_SEL(channel));
+ }
+}
+
+static void sun50i_h6_i2s_set_rxchansel(const struct sun4i_i2s *i2s, int channel)
+{
+ regmap_update_bits(i2s->regmap,
+ SUN50I_H6_I2S_RX_CHAN_SEL_REG,
+ SUN50I_H6_I2S_TX_CHAN_SEL_MASK,
+ SUN50I_H6_I2S_TX_CHAN_SEL(channel));
+}
+
static void sun4i_i2s_set_txchanmap(const struct sun4i_i2s *i2s, int output,
int channel)
{
@@ -520,6 +593,20 @@ static void sun8i_i2s_set_rxchanmap(const struct sun4i_i2s *i2s, int channel)
regmap_write(i2s->regmap, SUN8I_I2S_RX_CHAN_MAP_REG, channel);
}

+static void sun50i_h6_i2s_set_txchanmap(const struct sun4i_i2s *i2s, int output,
+ int channel)
+{
+ if (output >= 0 && output < 4) {
+ regmap_write(i2s->regmap,
+ SUN50I_H6_I2S_TX_CHAN_MAP1_REG + (output * 8), channel);
+ }
+}
+
+static void sun50i_h6_i2s_set_rxchanmap(const struct sun4i_i2s *i2s, int channel)
+{
+ regmap_write(i2s->regmap, SUN50I_H6_I2S_RX_CHAN_MAP1_REG, channel);
+}
+
static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -996,6 +1083,22 @@ static const struct reg_default sun8i_i2s_reg_defaults[] = {
{ SUN8I_I2S_RX_CHAN_MAP_REG, 0x00000000 },
};

+static const struct reg_default sun50i_i2s_reg_defaults[] = {
+ { SUN4I_I2S_CTRL_REG, 0x00060000 },
+ { SUN4I_I2S_FMT0_REG, 0x00000033 },
+ { SUN4I_I2S_FMT1_REG, 0x00000030 },
+ { SUN4I_I2S_FIFO_CTRL_REG, 0x000400f0 },
+ { SUN4I_I2S_DMA_INT_CTRL_REG, 0x00000000 },
+ { SUN4I_I2S_CLK_DIV_REG, 0x00000000 },
+ { SUN8I_I2S_CHAN_CFG_REG, 0x00000000 },
+ { SUN8I_I2S_TX_CHAN_SEL_REG, 0x00000000 },
+ { SUN50I_H6_I2S_TX_CHAN_MAP0_REG, 0x00000000 },
+ { SUN50I_H6_I2S_TX_CHAN_MAP1_REG, 0x00000000 },
+ { SUN50I_H6_I2S_RX_CHAN_SEL_REG, 0x00000000 },
+ { SUN50I_H6_I2S_RX_CHAN_MAP0_REG, 0x00000000 },
+ { SUN50I_H6_I2S_RX_CHAN_MAP1_REG, 0x00000000 },
+};
+
static const struct regmap_config sun4i_i2s_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
@@ -1023,6 +1126,19 @@ static const struct regmap_config sun8i_i2s_regmap_config = {
.volatile_reg = sun8i_i2s_volatile_reg,
};

+static const struct regmap_config sun50i_i2s_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = SUN50I_H6_I2S_RX_CHAN_MAP1_REG,
+ .cache_type = REGCACHE_FLAT,
+ .reg_defaults = sun50i_i2s_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(sun50i_i2s_reg_defaults),
+ .writeable_reg = sun4i_i2s_wr_reg,
+ .readable_reg = sun8i_i2s_rd_reg,
+ .volatile_reg = sun8i_i2s_volatile_reg,
+};
+
static int sun4i_i2s_runtime_resume(struct device *dev)
{
struct sun4i_i2s *i2s = dev_get_drvdata(dev);
@@ -1197,6 +1313,34 @@ static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = {
.set_rxchanmap = sun4i_i2s_set_rxchanmap,
};

+static const struct sun4i_i2s_quirks sun50i_h6_i2s_quirks = {
+ .has_reset = true,
+ .reg_offset_txdata = SUN8I_I2S_FIFO_TX_REG,
+ .sun4i_i2s_regmap = &sun50i_i2s_regmap_config,
+ .has_fmt_set_lrck_period = true,
+ .has_chcfg = true,
+ .has_chsel_tx_chen = true,
+ .has_chsel_offset = true,
+ .field_clkdiv_mclk_en = REG_FIELD(SUN4I_I2S_CLK_DIV_REG, 8, 8),
+ .field_fmt_wss = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 2),
+ .field_fmt_sr = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 6),
+ .field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
+ .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 19, 19),
+ .field_fmt_mode = REG_FIELD(SUN4I_I2S_CTRL_REG, 4, 5),
+ .field_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG, 4, 5),
+ .get_sr = sun8i_i2s_get_sr_wss,
+ .get_wss = sun8i_i2s_get_sr_wss,
+ .set_format = sun8i_i2s_set_format,
+ .set_txchanoffset = sun50i_h6_i2s_set_txchanoffset,
+ .set_rxchanoffset = sun50i_h6_i2s_set_rxchanoffset,
+ .set_txchanen = sun50i_h6_i2s_set_txchanen,
+ .set_rxchanen = sun50i_h6_i2s_set_rxchanen,
+ .set_txchansel = sun50i_h6_i2s_set_txchansel,
+ .set_rxchansel = sun50i_h6_i2s_set_rxchansel,
+ .set_txchanmap = sun50i_h6_i2s_set_txchanmap,
+ .set_rxchanmap = sun50i_h6_i2s_set_rxchanmap,
+};
+
static int sun4i_i2s_init_regmap_fields(struct device *dev,
struct sun4i_i2s *i2s)
{
@@ -1389,6 +1533,10 @@ static const struct of_device_id sun4i_i2s_match[] = {
.compatible = "allwinner,sun50i-a64-codec-i2s",
.data = &sun50i_a64_codec_i2s_quirks,
},
+ {
+ .compatible = "allwinner,sun50i-h6-i2s",
+ .data = &sun50i_h6_i2s_quirks,
+ },
{}
};
MODULE_DEVICE_TABLE(of, sun4i_i2s_match);
--
2.22.0

2019-08-14 06:11:00

by Code Kipper

[permalink] [raw]
Subject: [PATCH v5 06/15] ASoC: sun4i-i2s: Add functions for RX and TX channel enables

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.

Signed-off-by: Marcus Cooper <[email protected]>
---
sound/soc/sunxi/sun4i-i2s.c | 32 ++++++++++++++++++++++++++++----
1 file changed, 28 insertions(+), 4 deletions(-)

diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
index 4a748747ccd7..ad2ff83deeb7 100644
--- a/sound/soc/sunxi/sun4i-i2s.c
+++ b/sound/soc/sunxi/sun4i-i2s.c
@@ -172,6 +172,8 @@ struct sun4i_i2s_quirks {
int (*set_format)(struct sun4i_i2s *, unsigned int);
void (*set_txchanoffset)(const struct sun4i_i2s *, int);
void (*set_rxchanoffset)(const struct sun4i_i2s *);
+ void (*set_txchanen)(const struct sun4i_i2s *, int, int);
+ void (*set_rxchanen)(const struct sun4i_i2s *, int);
};

struct sun4i_i2s {
@@ -444,6 +446,25 @@ static void sun8i_i2s_set_rxchanoffset(const struct sun4i_i2s *i2s)
SUN8I_I2S_TX_CHAN_OFFSET(i2s->offset));
}

+static void sun8i_i2s_set_txchanen(const struct sun4i_i2s *i2s, int output,
+ int channel)
+{
+ if (output >= 0 && output < 4) {
+ regmap_update_bits(i2s->regmap,
+ SUN8I_I2S_TX_CHAN_SEL_REG + (output * 4),
+ SUN8I_I2S_TX_CHAN_EN_MASK,
+ SUN8I_I2S_TX_CHAN_EN(channel));
+ }
+}
+
+static void sun8i_i2s_set_rxchanen(const struct sun4i_i2s *i2s, int channel)
+{
+ regmap_update_bits(i2s->regmap,
+ SUN8I_I2S_RX_CHAN_SEL_REG,
+ SUN8I_I2S_TX_CHAN_EN_MASK,
+ SUN8I_I2S_TX_CHAN_EN(channel));
+}
+
static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -479,10 +500,11 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
regmap_field_write(i2s->field_rxchansel,
SUN4I_I2S_CHAN_SEL(params_channels(params)));

- if (i2s->variant->has_chsel_tx_chen)
- regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG,
- SUN8I_I2S_TX_CHAN_EN_MASK,
- SUN8I_I2S_TX_CHAN_EN(channels));
+ if (i2s->variant->set_txchanen)
+ i2s->variant->set_txchanen(i2s, 0, channels);
+
+ if (i2s->variant->set_rxchanen)
+ i2s->variant->set_rxchanen(i2s, channels);

switch (params_physical_width(params)) {
case 16:
@@ -1098,6 +1120,8 @@ static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = {
.set_format = sun8i_i2s_set_format,
.set_txchanoffset = sun8i_i2s_set_txchanoffset,
.set_rxchanoffset = sun8i_i2s_set_rxchanoffset,
+ .set_txchanen = sun8i_i2s_set_txchanen,
+ .set_rxchanen = sun8i_i2s_set_rxchanen,
};

static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = {
--
2.22.0

2019-08-14 06:11:08

by Code Kipper

[permalink] [raw]
Subject: [PATCH v5 07/15] ASoC: sun4i-i2s: Add functions for RX and TX channel selects

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.

Signed-off-by: Marcus Cooper <[email protected]>
---
sound/soc/sunxi/sun4i-i2s.c | 87 ++++++++++++++++++++++---------------
1 file changed, 53 insertions(+), 34 deletions(-)

diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
index ad2ff83deeb7..0b98adde0717 100644
--- a/sound/soc/sunxi/sun4i-i2s.c
+++ b/sound/soc/sunxi/sun4i-i2s.c
@@ -115,6 +115,8 @@
#define SUN8I_I2S_TX_CHAN_OFFSET(offset) ((offset) << 12)
#define SUN8I_I2S_TX_CHAN_EN_MASK GENMASK(11, 4)
#define SUN8I_I2S_TX_CHAN_EN(num_chan) (((1 << num_chan) - 1) << 4)
+#define SUN8I_I2S_TX_CHAN_SEL_MASK GENMASK(2, 0)
+#define SUN8I_I2S_TX_CHAN_SEL(chan) (chan - 1)

#define SUN8I_I2S_RX_CHAN_SEL_REG 0x54
#define SUN8I_I2S_RX_CHAN_MAP_REG 0x58
@@ -141,8 +143,6 @@ struct sun4i_i2s;
* @field_fmt_sext: regmap field to set the sign extension.
* @field_txchanmap: location of the tx channel mapping register.
* @field_rxchanmap: location of the rx channel mapping register.
- * @field_txchansel: location of the tx channel select bit fields.
- * @field_rxchansel: location of the rx channel select bit fields.
*/
struct sun4i_i2s_quirks {
bool has_reset;
@@ -164,8 +164,6 @@ struct sun4i_i2s_quirks {
struct reg_field field_fmt_sext;
struct reg_field field_txchanmap;
struct reg_field field_rxchanmap;
- struct reg_field field_txchansel;
- struct reg_field field_rxchansel;

s8 (*get_sr)(const struct sun4i_i2s *, int);
s8 (*get_wss)(const struct sun4i_i2s *, int);
@@ -174,6 +172,8 @@ struct sun4i_i2s_quirks {
void (*set_rxchanoffset)(const struct sun4i_i2s *);
void (*set_txchanen)(const struct sun4i_i2s *, int, int);
void (*set_rxchanen)(const struct sun4i_i2s *, int);
+ void (*set_txchansel)(const struct sun4i_i2s *, int, int);
+ void (*set_rxchansel)(const struct sun4i_i2s *, int);
};

struct sun4i_i2s {
@@ -197,8 +197,6 @@ struct sun4i_i2s {
struct regmap_field *field_fmt_sext;
struct regmap_field *field_txchanmap;
struct regmap_field *field_rxchanmap;
- struct regmap_field *field_txchansel;
- struct regmap_field *field_rxchansel;

const struct sun4i_i2s_quirks *variant;

@@ -465,6 +463,42 @@ static void sun8i_i2s_set_rxchanen(const struct sun4i_i2s *i2s, int channel)
SUN8I_I2S_TX_CHAN_EN(channel));
}

+static void sun4i_i2s_set_txchansel(const struct sun4i_i2s *i2s, int output,
+ int channel)
+{
+ /* Configure the channels */
+ regmap_write(i2s->regmap,
+ SUN4I_I2S_TX_CHAN_SEL_REG,
+ SUN4I_I2S_CHAN_SEL(channel));
+}
+
+static void sun8i_i2s_set_txchansel(const struct sun4i_i2s *i2s, int output,
+ int channel)
+{
+ if (output >= 0 && output < 4) {
+ regmap_update_bits(i2s->regmap,
+ SUN8I_I2S_TX_CHAN_SEL_REG + (output * 4),
+ SUN8I_I2S_TX_CHAN_SEL_MASK,
+ SUN8I_I2S_TX_CHAN_SEL(channel));
+ }
+}
+
+static void sun4i_i2s_set_rxchansel(const struct sun4i_i2s *i2s, int channel)
+{
+ /* Configure the channels */
+ regmap_write(i2s->regmap,
+ SUN4I_I2S_RX_CHAN_SEL_REG,
+ SUN4I_I2S_CHAN_SEL(channel));
+}
+
+static void sun8i_i2s_set_rxchansel(const struct sun4i_i2s *i2s, int channel)
+{
+ regmap_update_bits(i2s->regmap,
+ SUN8I_I2S_RX_CHAN_SEL_REG,
+ SUN8I_I2S_TX_CHAN_SEL_MASK,
+ SUN8I_I2S_TX_CHAN_SEL(channel));
+}
+
static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -494,11 +528,8 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
regmap_field_write(i2s->field_rxchanmap, 0x00003210);

/* Configure the channels */
- regmap_field_write(i2s->field_txchansel,
- SUN4I_I2S_CHAN_SEL(params_channels(params)));
-
- regmap_field_write(i2s->field_rxchansel,
- SUN4I_I2S_CHAN_SEL(params_channels(params)));
+ i2s->variant->set_txchansel(i2s, 0, channels);
+ i2s->variant->set_rxchansel(i2s, channels);

if (i2s->variant->set_txchanen)
i2s->variant->set_txchanen(i2s, 0, channels);
@@ -1048,11 +1079,11 @@ static const struct sun4i_i2s_quirks sun4i_a10_i2s_quirks = {
.field_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG, 8, 8),
.field_txchanmap = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31),
.field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31),
- .field_txchansel = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2),
- .field_rxchansel = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2),
.get_sr = sun4i_i2s_get_sr,
.get_wss = sun4i_i2s_get_wss,
.set_format = sun4i_i2s_set_format,
+ .set_txchansel = sun4i_i2s_set_txchansel,
+ .set_rxchansel = sun4i_i2s_set_rxchansel,
};

static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = {
@@ -1069,11 +1100,11 @@ static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = {
.field_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG, 8, 8),
.field_txchanmap = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31),
.field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31),
- .field_txchansel = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2),
- .field_rxchansel = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2),
.get_sr = sun4i_i2s_get_sr,
.get_wss = sun4i_i2s_get_wss,
.set_format = sun4i_i2s_set_format,
+ .set_txchansel = sun4i_i2s_set_txchansel,
+ .set_rxchansel = sun4i_i2s_set_rxchansel,
};

static const struct sun4i_i2s_quirks sun8i_a83t_i2s_quirks = {
@@ -1089,11 +1120,11 @@ static const struct sun4i_i2s_quirks sun8i_a83t_i2s_quirks = {
.field_fmt_mode = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1),
.field_txchanmap = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31),
.field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31),
- .field_txchansel = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2),
- .field_rxchansel = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2),
.get_sr = sun8i_i2s_get_sr_wss,
.get_wss = sun8i_i2s_get_sr_wss,
.set_format = sun4i_i2s_set_format,
+ .set_txchansel = sun4i_i2s_set_txchansel,
+ .set_rxchansel = sun4i_i2s_set_rxchansel,
};

static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = {
@@ -1113,8 +1144,6 @@ static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = {
.field_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG, 4, 5),
.field_txchanmap = REG_FIELD(SUN8I_I2S_TX_CHAN_MAP_REG, 0, 31),
.field_rxchanmap = REG_FIELD(SUN8I_I2S_RX_CHAN_MAP_REG, 0, 31),
- .field_txchansel = REG_FIELD(SUN8I_I2S_TX_CHAN_SEL_REG, 0, 2),
- .field_rxchansel = REG_FIELD(SUN8I_I2S_RX_CHAN_SEL_REG, 0, 2),
.get_sr = sun8i_i2s_get_sr_wss,
.get_wss = sun8i_i2s_get_sr_wss,
.set_format = sun8i_i2s_set_format,
@@ -1122,6 +1151,8 @@ static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = {
.set_rxchanoffset = sun8i_i2s_set_rxchanoffset,
.set_txchanen = sun8i_i2s_set_txchanen,
.set_rxchanen = sun8i_i2s_set_rxchanen,
+ .set_txchansel = sun8i_i2s_set_txchansel,
+ .set_rxchansel = sun8i_i2s_set_rxchansel,
};

static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = {
@@ -1138,11 +1169,11 @@ static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = {
.field_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG, 8, 8),
.field_txchanmap = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31),
.field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31),
- .field_txchansel = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2),
- .field_rxchansel = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2),
.get_sr = sun4i_i2s_get_sr,
.get_wss = sun4i_i2s_get_wss,
.set_format = sun4i_i2s_set_format,
+ .set_txchansel = sun4i_i2s_set_txchansel,
+ .set_rxchansel = sun4i_i2s_set_rxchansel,
};

static int sun4i_i2s_init_regmap_fields(struct device *dev,
@@ -1199,19 +1230,7 @@ static int sun4i_i2s_init_regmap_fields(struct device *dev,
i2s->field_rxchanmap =
devm_regmap_field_alloc(dev, i2s->regmap,
i2s->variant->field_rxchanmap);
- if (IS_ERR(i2s->field_rxchanmap))
- return PTR_ERR(i2s->field_rxchanmap);
-
- i2s->field_txchansel =
- devm_regmap_field_alloc(dev, i2s->regmap,
- i2s->variant->field_txchansel);
- if (IS_ERR(i2s->field_txchansel))
- return PTR_ERR(i2s->field_txchansel);
-
- i2s->field_rxchansel =
- devm_regmap_field_alloc(dev, i2s->regmap,
- i2s->variant->field_rxchansel);
- return PTR_ERR_OR_ZERO(i2s->field_rxchansel);
+ return PTR_ERR_OR_ZERO(i2s->field_rxchanmap);
}

static int sun4i_i2s_probe(struct platform_device *pdev)
--
2.22.0

2019-08-14 06:11:13

by Code Kipper

[permalink] [raw]
Subject: [PATCH v5 09/15] clk: sunxi-ng: h6: Allow I2S to change parent rate

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.

Signed-off-by: Jernej Skrabec <[email protected]>
---
drivers/clk/sunxi-ng/ccu-sun50i-h6.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-h6.c b/drivers/clk/sunxi-ng/ccu-sun50i-h6.c
index aebef4af9861..d89353a3cdec 100644
--- a/drivers/clk/sunxi-ng/ccu-sun50i-h6.c
+++ b/drivers/clk/sunxi-ng/ccu-sun50i-h6.c
@@ -505,7 +505,7 @@ static struct ccu_div i2s3_clk = {
.hw.init = CLK_HW_INIT_PARENTS("i2s3",
audio_parents,
&ccu_div_ops,
- 0),
+ CLK_SET_RATE_PARENT),
},
};

@@ -518,7 +518,7 @@ static struct ccu_div i2s0_clk = {
.hw.init = CLK_HW_INIT_PARENTS("i2s0",
audio_parents,
&ccu_div_ops,
- 0),
+ CLK_SET_RATE_PARENT),
},
};

@@ -531,7 +531,7 @@ static struct ccu_div i2s1_clk = {
.hw.init = CLK_HW_INIT_PARENTS("i2s1",
audio_parents,
&ccu_div_ops,
- 0),
+ CLK_SET_RATE_PARENT),
},
};

@@ -544,7 +544,7 @@ static struct ccu_div i2s2_clk = {
.hw.init = CLK_HW_INIT_PARENTS("i2s2",
audio_parents,
&ccu_div_ops,
- 0),
+ CLK_SET_RATE_PARENT),
},
};

--
2.22.0

2019-08-14 06:11:40

by Code Kipper

[permalink] [raw]
Subject: [PATCH v5 04/15] ASoC: sun4i-i2s: Support more formats on newer SoCs

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.

Signed-off-by: Marcus Cooper <[email protected]>
---
sound/soc/sunxi/sun4i-i2s.c | 87 +++++++++++++++++++++++++++----------
1 file changed, 63 insertions(+), 24 deletions(-)

diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
index 34f31439ae7b..3553c17318b0 100644
--- a/sound/soc/sunxi/sun4i-i2s.c
+++ b/sound/soc/sunxi/sun4i-i2s.c
@@ -27,6 +27,8 @@
#define SUN4I_I2S_CTRL_MODE_MASK BIT(5)
#define SUN4I_I2S_CTRL_MODE_SLAVE (1 << 5)
#define SUN4I_I2S_CTRL_MODE_MASTER (0 << 5)
+#define SUN4I_I2S_CTRL_PCM BIT(4)
+#define SUN4I_I2S_CTRL_LOOP BIT(3)
#define SUN4I_I2S_CTRL_TX_EN BIT(2)
#define SUN4I_I2S_CTRL_RX_EN BIT(1)
#define SUN4I_I2S_CTRL_GL_EN BIT(0)
@@ -91,6 +93,9 @@
/* Defines required for sun8i-h3 support */
#define SUN8I_I2S_CTRL_BCLK_OUT BIT(18)
#define SUN8I_I2S_CTRL_LRCK_OUT BIT(17)
+#define SUN8I_I2S_CTRL_MODE_RIGHT_J (2 << 0)
+#define SUN8I_I2S_CTRL_MODE_I2S_LEFT_J (1 << 0)
+#define SUN8I_I2S_CTRL_MODE_PCM (0 << 0)

#define SUN8I_I2S_FMT0_LRCK_PERIOD_MASK GENMASK(17, 8)
#define SUN8I_I2S_FMT0_LRCK_PERIOD(period) ((period - 1) << 8)
@@ -164,6 +169,7 @@ struct sun4i_i2s_quirks {

s8 (*get_sr)(const struct sun4i_i2s *, int);
s8 (*get_wss)(const struct sun4i_i2s *, int);
+ int (*set_format)(struct sun4i_i2s *, unsigned int);
};

struct sun4i_i2s {
@@ -194,6 +200,7 @@ struct sun4i_i2s {

unsigned int tdm_slots;
unsigned int slot_width;
+ unsigned int offset;
};

struct sun4i_i2s_clk_div {
@@ -484,19 +491,14 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
i2s->slot_width : params_width(params));
}

-static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+static int sun4i_i2s_set_format(struct sun4i_i2s *i2s, unsigned int fmt)
{
- struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
u32 val;
- u32 offset = 0;
- u32 bclk_polarity = SUN4I_I2S_FMT0_POLARITY_NORMAL;
- u32 lrclk_polarity = SUN4I_I2S_FMT0_POLARITY_NORMAL;

/* DAI Mode */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
val = SUN4I_I2S_FMT0_FMT_I2S;
- offset = 1;
break;
case SND_SOC_DAIFMT_LEFT_J:
val = SUN4I_I2S_FMT0_FMT_LEFT_J;
@@ -505,32 +507,64 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
val = SUN4I_I2S_FMT0_FMT_RIGHT_J;
break;
default:
- dev_err(dai->dev, "Unsupported format: %d\n",
- fmt & SND_SOC_DAIFMT_FORMAT_MASK);
return -EINVAL;
}

- if (i2s->variant->has_chsel_offset) {
- /*
- * offset being set indicates that we're connected to an i2s
- * device, however offset is only used on the sun8i block and
- * i2s shares the same setting with the LJ format. Increment
- * val so that the bit to value to write is correct.
- */
- if (offset > 0)
- val++;
- /* blck offset determines whether i2s or LJ */
- regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG,
- SUN8I_I2S_TX_CHAN_OFFSET_MASK,
- SUN8I_I2S_TX_CHAN_OFFSET(offset));
+ regmap_field_write(i2s->field_fmt_mode, val);
+
+ return 0;
+}
+
+static int sun8i_i2s_set_format(struct sun4i_i2s *i2s, unsigned int fmt)
+{
+ u32 val;

- regmap_update_bits(i2s->regmap, SUN8I_I2S_RX_CHAN_SEL_REG,
- SUN8I_I2S_TX_CHAN_OFFSET_MASK,
- SUN8I_I2S_TX_CHAN_OFFSET(offset));
+ /* DAI Mode */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ i2s->offset = 1;
+ case SND_SOC_DAIFMT_LEFT_J:
+ val = SUN8I_I2S_CTRL_MODE_I2S_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ val = SUN8I_I2S_CTRL_MODE_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ i2s->offset = 1;
+ case SND_SOC_DAIFMT_DSP_B:
+ val = SUN8I_I2S_CTRL_MODE_PCM;
+ break;
+
+ default:
+ return -EINVAL;
}

+ /*
+ * bclk offset determines whether i2s or LJ if in i2s mode and
+ * DSP_A or DSP_B if in PCM mode.
+ */
+ i2s->variant->set_txchanoffset(i2s, 0);
+ i2s->variant->set_rxchanoffset(i2s);
+
regmap_field_write(i2s->field_fmt_mode, val);

+ return 0;
+}
+
+static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ u32 val;
+ u32 bclk_polarity = SUN4I_I2S_FMT0_POLARITY_NORMAL;
+ u32 lrclk_polarity = SUN4I_I2S_FMT0_POLARITY_NORMAL;
+
+ /* Set DAI Mode */
+ if (i2s->variant->set_format(i2s, fmt) != 0) {
+ dev_err(dai->dev, "Unsupported format: %d\n",
+ fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ return -EINVAL;
+ }
+
/* DAI clock polarity */
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_IB_IF:
@@ -976,6 +1010,7 @@ static const struct sun4i_i2s_quirks sun4i_a10_i2s_quirks = {
.field_rxchansel = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2),
.get_sr = sun4i_i2s_get_sr,
.get_wss = sun4i_i2s_get_wss,
+ .set_format = sun4i_i2s_set_format,
};

static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = {
@@ -996,6 +1031,7 @@ static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = {
.field_rxchansel = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2),
.get_sr = sun4i_i2s_get_sr,
.get_wss = sun4i_i2s_get_wss,
+ .set_format = sun4i_i2s_set_format,
};

static const struct sun4i_i2s_quirks sun8i_a83t_i2s_quirks = {
@@ -1015,6 +1051,7 @@ static const struct sun4i_i2s_quirks sun8i_a83t_i2s_quirks = {
.field_rxchansel = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2),
.get_sr = sun8i_i2s_get_sr_wss,
.get_wss = sun8i_i2s_get_sr_wss,
+ .set_format = sun4i_i2s_set_format,
};

static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = {
@@ -1038,6 +1075,7 @@ static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = {
.field_rxchansel = REG_FIELD(SUN8I_I2S_RX_CHAN_SEL_REG, 0, 2),
.get_sr = sun8i_i2s_get_sr_wss,
.get_wss = sun8i_i2s_get_sr_wss,
+ .set_format = sun8i_i2s_set_format,
};

static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = {
@@ -1058,6 +1096,7 @@ static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = {
.field_rxchansel = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2),
.get_sr = sun4i_i2s_get_sr,
.get_wss = sun4i_i2s_get_wss,
+ .set_format = sun4i_i2s_set_format,
};

static int sun4i_i2s_init_regmap_fields(struct device *dev,
--
2.22.0

2019-08-14 06:11:42

by Code Kipper

[permalink] [raw]
Subject: [PATCH v5 10/15] dt-bindings: ASoC: sun4i-i2s: Add H6 compatible

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.

Signed-off-by: Jernej Skrabec <[email protected]>
---
.../devicetree/bindings/sound/allwinner,sun4i-a10-i2s.yaml | 2 ++
1 file changed, 2 insertions(+)

diff --git a/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-i2s.yaml b/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-i2s.yaml
index eb3992138eec..6928d0a1dcc8 100644
--- a/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-i2s.yaml
+++ b/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-i2s.yaml
@@ -24,6 +24,7 @@ properties:
- items:
- const: allwinner,sun50i-a64-i2s
- const: allwinner,sun8i-h3-i2s
+ - const: allwinner,sun50i-h6-i2s

reg:
maxItems: 1
@@ -59,6 +60,7 @@ allOf:
- allwinner,sun8i-a83t-i2s
- allwinner,sun8i-h3-i2s
- allwinner,sun50i-a64-codec-i2s
+ - allwinner,sun50i-h6-i2s

then:
required:
--
2.22.0

2019-08-14 06:11:43

by Code Kipper

[permalink] [raw]
Subject: [PATCH v5 02/15] ASoC: sun4i-i2s: Add set_tdm_slot functionality

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(-)

diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
index 8201334a059b..7c37b6291df0 100644
--- a/sound/soc/sunxi/sun4i-i2s.c
+++ b/sound/soc/sunxi/sun4i-i2s.c
@@ -195,6 +195,9 @@ struct sun4i_i2s {
struct regmap_field *field_rxchansel;

const struct sun4i_i2s_quirks *variant;
+
+ unsigned int tdm_slots;
+ unsigned int slot_width;
};

struct sun4i_i2s_clk_div {
@@ -346,7 +349,7 @@ static int sun4i_i2s_set_clk_rate(struct snd_soc_dai *dai,
if (i2s->variant->has_fmt_set_lrck_period)
regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT0_REG,
SUN8I_I2S_FMT0_LRCK_PERIOD_MASK,
- SUN8I_I2S_FMT0_LRCK_PERIOD(32));
+ SUN8I_I2S_FMT0_LRCK_PERIOD(word_size));

/* Set sign extension to pad out LSB with 0 */
regmap_field_write(i2s->field_fmt_sext, 0);
@@ -450,7 +453,8 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
regmap_field_write(i2s->field_fmt_sr, sr);

return sun4i_i2s_set_clk_rate(dai, params_rate(params),
- params_width(params));
+ i2s->tdm_slots ?
+ i2s->slot_width : params_width(params));
}

static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
@@ -693,10 +697,25 @@ static int sun4i_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id,
return 0;
}

+static int sun4i_i2s_set_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask,
+ unsigned int rx_mask,
+ int slots, int width)
+{
+ struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+ i2s->tdm_slots = slots;
+
+ i2s->slot_width = width;
+
+ return 0;
+}
+
static const struct snd_soc_dai_ops sun4i_i2s_dai_ops = {
.hw_params = sun4i_i2s_hw_params,
.set_fmt = sun4i_i2s_set_fmt,
.set_sysclk = sun4i_i2s_set_sysclk,
+ .set_tdm_slot = sun4i_i2s_set_dai_tdm_slot,
.trigger = sun4i_i2s_trigger,
};

--
2.22.0

2019-08-14 06:11:44

by Code Kipper

[permalink] [raw]
Subject: [PATCH v5 03/15] ASoC: sun4i-i2s: Correct divider calculations

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(-)

diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
index 7c37b6291df0..34f31439ae7b 100644
--- a/sound/soc/sunxi/sun4i-i2s.c
+++ b/sound/soc/sunxi/sun4i-i2s.c
@@ -127,8 +127,6 @@ struct sun4i_i2s;
* @has_chsel_offset: SoC uses offset for selecting dai operational mode.
* @reg_offset_txdata: offset of the tx fifo.
* @sun4i_i2s_regmap: regmap config to use.
- * @mclk_offset: Value by which mclkdiv needs to be adjusted.
- * @bclk_offset: Value by which bclkdiv needs to be adjusted.
* @field_clkdiv_mclk_en: regmap field to enable mclk output.
* @field_fmt_wss: regmap field to set word select size.
* @field_fmt_sr: regmap field to set sample resolution.
@@ -150,8 +148,6 @@ struct sun4i_i2s_quirks {
bool has_chsel_offset;
unsigned int reg_offset_txdata; /* TX FIFO */
const struct regmap_config *sun4i_i2s_regmap;
- unsigned int mclk_offset;
- unsigned int bclk_offset;

/* Register fields for i2s */
struct reg_field field_clkdiv_mclk_en;
@@ -212,7 +208,25 @@ static const struct sun4i_i2s_clk_div sun4i_i2s_bclk_div[] = {
{ .div = 8, .val = 3 },
{ .div = 12, .val = 4 },
{ .div = 16, .val = 5 },
- /* TODO - extend divide ratio supported by newer SoCs */
+};
+
+static const struct sun4i_i2s_clk_div sun8i_i2s_clk_div[] = {
+ { .div = 0, .val = 0 },
+ { .div = 1, .val = 1 },
+ { .div = 2, .val = 2 },
+ { .div = 4, .val = 3 },
+ { .div = 6, .val = 4 },
+ { .div = 8, .val = 5 },
+ { .div = 12, .val = 6 },
+ { .div = 16, .val = 7 },
+ { .div = 24, .val = 8 },
+ { .div = 32, .val = 9 },
+ { .div = 48, .val = 10 },
+ { .div = 64, .val = 11 },
+ { .div = 96, .val = 12 },
+ { .div = 128, .val = 13 },
+ { .div = 176, .val = 14 },
+ { .div = 192, .val = 15 },
};

static const struct sun4i_i2s_clk_div sun4i_i2s_mclk_div[] = {
@@ -224,21 +238,21 @@ static const struct sun4i_i2s_clk_div sun4i_i2s_mclk_div[] = {
{ .div = 12, .val = 5 },
{ .div = 16, .val = 6 },
{ .div = 24, .val = 7 },
- /* TODO - extend divide ratio supported by newer SoCs */
};

static int sun4i_i2s_get_bclk_div(struct sun4i_i2s *i2s,
unsigned int oversample_rate,
- unsigned int word_size)
+ unsigned int word_size,
+ const struct sun4i_i2s_clk_div *bdiv,
+ unsigned int size)
{
int div = oversample_rate / word_size / 2;
int i;

- for (i = 0; i < ARRAY_SIZE(sun4i_i2s_bclk_div); i++) {
- const struct sun4i_i2s_clk_div *bdiv = &sun4i_i2s_bclk_div[i];
-
+ for (i = 0; i < size; i++) {
if (bdiv->div == div)
return bdiv->val;
+ bdiv++;
}

return -EINVAL;
@@ -247,16 +261,17 @@ static int sun4i_i2s_get_bclk_div(struct sun4i_i2s *i2s,
static int sun4i_i2s_get_mclk_div(struct sun4i_i2s *i2s,
unsigned int oversample_rate,
unsigned int module_rate,
- unsigned int sampling_rate)
+ unsigned int sampling_rate,
+ const struct sun4i_i2s_clk_div *mdiv,
+ unsigned int size)
{
int div = module_rate / sampling_rate / oversample_rate;
int i;

- for (i = 0; i < ARRAY_SIZE(sun4i_i2s_mclk_div); i++) {
- const struct sun4i_i2s_clk_div *mdiv = &sun4i_i2s_mclk_div[i];
-
+ for (i = 0; i < size; i++) {
if (mdiv->div == div)
return mdiv->val;
+ mdiv++;
}

return -EINVAL;
@@ -321,24 +336,36 @@ static int sun4i_i2s_set_clk_rate(struct snd_soc_dai *dai,
return -EINVAL;
}

- bclk_div = sun4i_i2s_get_bclk_div(i2s, oversample_rate,
- word_size);
+ if (i2s->variant->has_fmt_set_lrck_period)
+ bclk_div = sun4i_i2s_get_bclk_div(i2s, clk_rate / rate,
+ word_size,
+ sun8i_i2s_clk_div,
+ ARRAY_SIZE(sun8i_i2s_clk_div));
+ else
+ bclk_div = sun4i_i2s_get_bclk_div(i2s, oversample_rate,
+ word_size,
+ sun4i_i2s_bclk_div,
+ ARRAY_SIZE(sun4i_i2s_bclk_div));
if (bclk_div < 0) {
dev_err(dai->dev, "Unsupported BCLK divider: %d\n", bclk_div);
return -EINVAL;
}

- mclk_div = sun4i_i2s_get_mclk_div(i2s, oversample_rate,
- clk_rate, rate);
+ if (i2s->variant->has_fmt_set_lrck_period)
+ mclk_div = sun4i_i2s_get_mclk_div(i2s, oversample_rate,
+ clk_rate, rate,
+ sun8i_i2s_clk_div,
+ ARRAY_SIZE(sun8i_i2s_clk_div));
+ else
+ mclk_div = sun4i_i2s_get_mclk_div(i2s, oversample_rate,
+ clk_rate, rate,
+ sun4i_i2s_mclk_div,
+ ARRAY_SIZE(sun4i_i2s_mclk_div));
if (mclk_div < 0) {
dev_err(dai->dev, "Unsupported MCLK divider: %d\n", mclk_div);
return -EINVAL;
}

- /* Adjust the clock division values if needed */
- bclk_div += i2s->variant->bclk_offset;
- mclk_div += i2s->variant->mclk_offset;
-
regmap_write(i2s->regmap, SUN4I_I2S_CLK_DIV_REG,
SUN4I_I2S_CLK_DIV_BCLK(bclk_div) |
SUN4I_I2S_CLK_DIV_MCLK(mclk_div));
@@ -994,8 +1021,6 @@ static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = {
.has_reset = true,
.reg_offset_txdata = SUN8I_I2S_FIFO_TX_REG,
.sun4i_i2s_regmap = &sun8i_i2s_regmap_config,
- .mclk_offset = 1,
- .bclk_offset = 2,
.has_fmt_set_lrck_period = true,
.has_chcfg = true,
.has_chsel_tx_chen = true,
--
2.22.0

2019-08-14 06:12:22

by Code Kipper

[permalink] [raw]
Subject: [PATCH v5 05/15] ASoC: sun4i-i2s: Add functions for RX and TX channel offsets

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.

Signed-off-by: Marcus Cooper <[email protected]>
---
sound/soc/sunxi/sun4i-i2s.c | 24 +++++++++++++++++++++++-
1 file changed, 23 insertions(+), 1 deletion(-)

diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
index 3553c17318b0..4a748747ccd7 100644
--- a/sound/soc/sunxi/sun4i-i2s.c
+++ b/sound/soc/sunxi/sun4i-i2s.c
@@ -112,7 +112,7 @@
#define SUN8I_I2S_TX_CHAN_MAP_REG 0x44
#define SUN8I_I2S_TX_CHAN_SEL_REG 0x34
#define SUN8I_I2S_TX_CHAN_OFFSET_MASK GENMASK(13, 12)
-#define SUN8I_I2S_TX_CHAN_OFFSET(offset) (offset << 12)
+#define SUN8I_I2S_TX_CHAN_OFFSET(offset) ((offset) << 12)
#define SUN8I_I2S_TX_CHAN_EN_MASK GENMASK(11, 4)
#define SUN8I_I2S_TX_CHAN_EN(num_chan) (((1 << num_chan) - 1) << 4)

@@ -170,6 +170,8 @@ struct sun4i_i2s_quirks {
s8 (*get_sr)(const struct sun4i_i2s *, int);
s8 (*get_wss)(const struct sun4i_i2s *, int);
int (*set_format)(struct sun4i_i2s *, unsigned int);
+ void (*set_txchanoffset)(const struct sun4i_i2s *, int);
+ void (*set_rxchanoffset)(const struct sun4i_i2s *);
};

struct sun4i_i2s {
@@ -424,6 +426,24 @@ static s8 sun8i_i2s_get_sr_wss(const struct sun4i_i2s *i2s, int width)
return (width - 8) / 4 + 1;
}

+static void sun8i_i2s_set_txchanoffset(const struct sun4i_i2s *i2s, int output)
+{
+ if (output >= 0 && output < 4) {
+ regmap_update_bits(i2s->regmap,
+ SUN8I_I2S_TX_CHAN_SEL_REG + (output * 4),
+ SUN8I_I2S_TX_CHAN_OFFSET_MASK,
+ SUN8I_I2S_TX_CHAN_OFFSET(i2s->offset));
+ }
+}
+
+static void sun8i_i2s_set_rxchanoffset(const struct sun4i_i2s *i2s)
+{
+ regmap_update_bits(i2s->regmap,
+ SUN8I_I2S_RX_CHAN_SEL_REG,
+ SUN8I_I2S_TX_CHAN_OFFSET_MASK,
+ SUN8I_I2S_TX_CHAN_OFFSET(i2s->offset));
+}
+
static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -1076,6 +1096,8 @@ static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = {
.get_sr = sun8i_i2s_get_sr_wss,
.get_wss = sun8i_i2s_get_sr_wss,
.set_format = sun8i_i2s_set_format,
+ .set_txchanoffset = sun8i_i2s_set_txchanoffset,
+ .set_rxchanoffset = sun8i_i2s_set_rxchanoffset,
};

static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = {
--
2.22.0

2019-08-14 07:59:29

by Jernej Skrabec

[permalink] [raw]
Subject: Re: [PATCH v5 11/15] ASoC: sun4i-i2s: Add support for H6 I2S

Hi!

Dne sreda, 14. avgust 2019 ob 08:08:50 CEST je [email protected]
napisal(a):
> 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

> ---
> sound/soc/sunxi/sun4i-i2s.c | 148 ++++++++++++++++++++++++++++++++++++
> 1 file changed, 148 insertions(+)
>
> diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
> index 6de3cb41aaf6..a8d98696fe7c 100644
> --- a/sound/soc/sunxi/sun4i-i2s.c
> +++ b/sound/soc/sunxi/sun4i-i2s.c
> @@ -121,6 +121,21 @@
> #define SUN8I_I2S_RX_CHAN_SEL_REG 0x54
> #define SUN8I_I2S_RX_CHAN_MAP_REG 0x58
>
> +/* Defines required for sun50i-h6 support */
> +#define SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET_MASK GENMASK(21, 20)
> +#define SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET(offset) ((offset) << 20)
> +#define SUN50I_H6_I2S_TX_CHAN_SEL_MASK GENMASK(19, 16)
> +#define SUN50I_H6_I2S_TX_CHAN_SEL(chan) ((chan - 1) << 16)
> +#define SUN50I_H6_I2S_TX_CHAN_EN_MASK GENMASK(15, 0)
> +#define SUN50I_H6_I2S_TX_CHAN_EN(num_chan) (((1 << num_chan) - 1))
> +
> +#define SUN50I_H6_I2S_TX_CHAN_MAP0_REG 0x44
> +#define SUN50I_H6_I2S_TX_CHAN_MAP1_REG 0x48
> +
> +#define SUN50I_H6_I2S_RX_CHAN_SEL_REG 0x64
> +#define SUN50I_H6_I2S_RX_CHAN_MAP0_REG 0x68
> +#define SUN50I_H6_I2S_RX_CHAN_MAP1_REG 0x6C
> +
> struct sun4i_i2s;
>
> /**
> @@ -440,6 +455,25 @@ static void sun8i_i2s_set_rxchanoffset(const struct
> sun4i_i2s *i2s) SUN8I_I2S_TX_CHAN_OFFSET(i2s->offset));
> }
>
> +static void sun50i_h6_i2s_set_txchanoffset(const struct sun4i_i2s *i2s, int
> output) +{
> + if (output >= 0 && output < 4) {
> + regmap_update_bits(i2s->regmap,
> + SUN8I_I2S_TX_CHAN_SEL_REG +
(output * 4),
> +
SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET_MASK,
> +
SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET(i2s->offset));
> + }
> +
> +}
> +
> +static void sun50i_h6_i2s_set_rxchanoffset(const struct sun4i_i2s *i2s)
> +{
> + regmap_update_bits(i2s->regmap,
> + SUN50I_H6_I2S_RX_CHAN_SEL_REG,
> + SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET_MASK,
> + SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET(i2s-
>offset));
> +}
> +
> static void sun8i_i2s_set_txchanen(const struct sun4i_i2s *i2s, int output,
> int channel)
> {
> @@ -459,6 +493,26 @@ static void sun8i_i2s_set_rxchanen(const struct
> sun4i_i2s *i2s, int channel) SUN8I_I2S_TX_CHAN_EN(channel));
> }
>
> +
> +static void sun50i_h6_i2s_set_txchanen(const struct sun4i_i2s *i2s, int
> output, + int channel)
> +{
> + if (output >= 0 && output < 4) {
> + regmap_update_bits(i2s->regmap,
> + SUN8I_I2S_TX_CHAN_SEL_REG +
(output * 4),
> + SUN50I_H6_I2S_TX_CHAN_EN_MASK,
> +
SUN50I_H6_I2S_TX_CHAN_EN(channel));
> + }
> +}
> +
> +static void sun50i_h6_i2s_set_rxchanen(const struct sun4i_i2s *i2s, int
> channel) +{
> + regmap_update_bits(i2s->regmap,
> + SUN50I_H6_I2S_RX_CHAN_SEL_REG,
> + SUN50I_H6_I2S_TX_CHAN_EN_MASK,
> + SUN50I_H6_I2S_TX_CHAN_EN(channel));
> +}
> +
> static void sun4i_i2s_set_txchansel(const struct sun4i_i2s *i2s, int
> output, int channel)
> {
> @@ -495,6 +549,25 @@ static void sun8i_i2s_set_rxchansel(const struct
> sun4i_i2s *i2s, int channel) SUN8I_I2S_TX_CHAN_SEL(channel));
> }
>
> +static void sun50i_h6_i2s_set_txchansel(const struct sun4i_i2s *i2s, int
> output, + int channel)
> +{
> + if (output >= 0 && output < 4) {
> + regmap_update_bits(i2s->regmap,
> + SUN8I_I2S_TX_CHAN_SEL_REG +
(output * 4),
> + SUN50I_H6_I2S_TX_CHAN_SEL_MASK,
> +
SUN50I_H6_I2S_TX_CHAN_SEL(channel));
> + }
> +}
> +
> +static void sun50i_h6_i2s_set_rxchansel(const struct sun4i_i2s *i2s, int
> channel) +{
> + regmap_update_bits(i2s->regmap,
> + SUN50I_H6_I2S_RX_CHAN_SEL_REG,
> + SUN50I_H6_I2S_TX_CHAN_SEL_MASK,
> + SUN50I_H6_I2S_TX_CHAN_SEL(channel));
> +}
> +
> static void sun4i_i2s_set_txchanmap(const struct sun4i_i2s *i2s, int
> output, int channel)
> {
> @@ -520,6 +593,20 @@ static void sun8i_i2s_set_rxchanmap(const struct
> sun4i_i2s *i2s, int channel) regmap_write(i2s->regmap,
> SUN8I_I2S_RX_CHAN_MAP_REG, channel);
> }
>
> +static void sun50i_h6_i2s_set_txchanmap(const struct sun4i_i2s *i2s, int
> output, + int channel)
> +{
> + if (output >= 0 && output < 4) {
> + regmap_write(i2s->regmap,
> + SUN50I_H6_I2S_TX_CHAN_MAP1_REG + (output
* 8), channel);
> + }
> +}
> +
> +static void sun50i_h6_i2s_set_rxchanmap(const struct sun4i_i2s *i2s, int
> channel) +{
> + regmap_write(i2s->regmap, SUN50I_H6_I2S_RX_CHAN_MAP1_REG, channel);
> +}
> +
> static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
> struct snd_pcm_hw_params *params,
> struct snd_soc_dai *dai)
> @@ -996,6 +1083,22 @@ static const struct reg_default
> sun8i_i2s_reg_defaults[] = { { SUN8I_I2S_RX_CHAN_MAP_REG, 0x00000000 },
> };
>
> +static const struct reg_default sun50i_i2s_reg_defaults[] = {
> + { SUN4I_I2S_CTRL_REG, 0x00060000 },
> + { SUN4I_I2S_FMT0_REG, 0x00000033 },
> + { SUN4I_I2S_FMT1_REG, 0x00000030 },
> + { SUN4I_I2S_FIFO_CTRL_REG, 0x000400f0 },
> + { SUN4I_I2S_DMA_INT_CTRL_REG, 0x00000000 },
> + { SUN4I_I2S_CLK_DIV_REG, 0x00000000 },
> + { SUN8I_I2S_CHAN_CFG_REG, 0x00000000 },
> + { SUN8I_I2S_TX_CHAN_SEL_REG, 0x00000000 },
> + { SUN50I_H6_I2S_TX_CHAN_MAP0_REG, 0x00000000 },
> + { SUN50I_H6_I2S_TX_CHAN_MAP1_REG, 0x00000000 },
> + { SUN50I_H6_I2S_RX_CHAN_SEL_REG, 0x00000000 },
> + { SUN50I_H6_I2S_RX_CHAN_MAP0_REG, 0x00000000 },
> + { SUN50I_H6_I2S_RX_CHAN_MAP1_REG, 0x00000000 },
> +};
> +
> static const struct regmap_config sun4i_i2s_regmap_config = {
> .reg_bits = 32,
> .reg_stride = 4,
> @@ -1023,6 +1126,19 @@ static const struct regmap_config
> sun8i_i2s_regmap_config = { .volatile_reg = sun8i_i2s_volatile_reg,
> };
>
> +static const struct regmap_config sun50i_i2s_regmap_config = {
> + .reg_bits = 32,
> + .reg_stride = 4,
> + .val_bits = 32,
> + .max_register = SUN50I_H6_I2S_RX_CHAN_MAP1_REG,
> + .cache_type = REGCACHE_FLAT,
> + .reg_defaults = sun50i_i2s_reg_defaults,
> + .num_reg_defaults = ARRAY_SIZE(sun50i_i2s_reg_defaults),
> + .writeable_reg = sun4i_i2s_wr_reg,
> + .readable_reg = sun8i_i2s_rd_reg,
> + .volatile_reg = sun8i_i2s_volatile_reg,
> +};
> +
> static int sun4i_i2s_runtime_resume(struct device *dev)
> {
> struct sun4i_i2s *i2s = dev_get_drvdata(dev);
> @@ -1197,6 +1313,34 @@ static const struct sun4i_i2s_quirks
> sun50i_a64_codec_i2s_quirks = { .set_rxchanmap =
sun4i_i2s_set_rxchanmap,
> };
>
> +static const struct sun4i_i2s_quirks sun50i_h6_i2s_quirks = {
> + .has_reset = true,
> + .reg_offset_txdata = SUN8I_I2S_FIFO_TX_REG,
> + .sun4i_i2s_regmap = &sun50i_i2s_regmap_config,
> + .has_fmt_set_lrck_period = true,
> + .has_chcfg = true,
> + .has_chsel_tx_chen = true,
> + .has_chsel_offset = true,
> + .field_clkdiv_mclk_en = REG_FIELD(SUN4I_I2S_CLK_DIV_REG, 8, 8),
> + .field_fmt_wss = REG_FIELD(SUN4I_I2S_FMT0_REG,
0, 2),
> + .field_fmt_sr = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 6),
> + .field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG,
7, 7),
> + .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 19, 19),
> + .field_fmt_mode = REG_FIELD(SUN4I_I2S_CTRL_REG, 4,
5),
> + .field_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG,
4, 5),
> + .get_sr = sun8i_i2s_get_sr_wss,
> + .get_wss = sun8i_i2s_get_sr_wss,
> + .set_format = sun8i_i2s_set_format,
> + .set_txchanoffset = sun50i_h6_i2s_set_txchanoffset,
> + .set_rxchanoffset = sun50i_h6_i2s_set_rxchanoffset,
> + .set_txchanen = sun50i_h6_i2s_set_txchanen,
> + .set_rxchanen = sun50i_h6_i2s_set_rxchanen,
> + .set_txchansel = sun50i_h6_i2s_set_txchansel,
> + .set_rxchansel = sun50i_h6_i2s_set_rxchansel,
> + .set_txchanmap = sun50i_h6_i2s_set_txchanmap,
> + .set_rxchanmap = sun50i_h6_i2s_set_rxchanmap,
> +};
> +
> static int sun4i_i2s_init_regmap_fields(struct device *dev,
> struct sun4i_i2s *i2s)
> {
> @@ -1389,6 +1533,10 @@ static const struct of_device_id sun4i_i2s_match[] =
> { .compatible = "allwinner,sun50i-a64-codec-i2s",
> .data = &sun50i_a64_codec_i2s_quirks,
> },
> + {
> + .compatible = "allwinner,sun50i-h6-i2s",
> + .data = &sun50i_h6_i2s_quirks,
> + },
> {}
> };
> MODULE_DEVICE_TABLE(of, sun4i_i2s_match);




2019-08-14 08:28:30

by Jernej Škrabec

[permalink] [raw]
Subject: Re: [linux-sunxi] [PATCH v5 12/15] ASoC: sun4i-i2s: Add multi-lane functionality

Hi!

Dne sreda, 14. avgust 2019 ob 08:08:51 CEST je [email protected]
napisal(a):
> 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(-)
>
> diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
> index a8d98696fe7c..a020c3b372a8 100644
> --- a/sound/soc/sunxi/sun4i-i2s.c
> +++ b/sound/soc/sunxi/sun4i-i2s.c
> @@ -23,7 +23,7 @@
>
> #define SUN4I_I2S_CTRL_REG 0x00
> #define SUN4I_I2S_CTRL_SDO_EN_MASK GENMASK(11, 8)
> -#define SUN4I_I2S_CTRL_SDO_EN(sdo) BIT(8 +
(sdo))
> +#define SUN4I_I2S_CTRL_SDO_EN(lines) (((1 << lines) - 1)
<< 8)
> #define SUN4I_I2S_CTRL_MODE_MASK BIT(5)
> #define SUN4I_I2S_CTRL_MODE_SLAVE (1 << 5)
> #define SUN4I_I2S_CTRL_MODE_MASTER (0 << 5)
> @@ -614,6 +614,7 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream
> *substream, struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
> int sr, wss, channels;
> u32 width;
> + int lines;
>
> channels = params_channels(params);
> if (channels != 2) {
> @@ -622,6 +623,13 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream
> *substream, return -EINVAL;
> }
>
> + lines = (channels + 1) / 2;
> +
> + /* Enable the required output lines */
> + regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
> + SUN4I_I2S_CTRL_SDO_EN_MASK,
> + SUN4I_I2S_CTRL_SDO_EN(lines));

As Maxime said before, this doesn't work for TDM. Maybe we can skip this for
now, until we agree on method how to describe channel allocation?

> +
> if (i2s->variant->has_chcfg) {
> regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG,
>
SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM_MASK,
> @@ -1389,9 +1397,10 @@ static int sun4i_i2s_init_regmap_fields(struct device
> *dev, static int sun4i_i2s_probe(struct platform_device *pdev)
> {
> struct sun4i_i2s *i2s;
> + struct snd_soc_dai_driver *soc_dai;
> struct resource *res;
> void __iomem *regs;
> - int irq, ret;
> + int irq, ret, val;
>
> i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
> if (!i2s)
> @@ -1456,6 +1465,19 @@ static int sun4i_i2s_probe(struct platform_device
> *pdev) i2s->capture_dma_data.addr = res->start + SUN4I_I2S_FIFO_RX_REG;
> i2s->capture_dma_data.maxburst = 8;
>
> + soc_dai = devm_kmemdup(&pdev->dev, &sun4i_i2s_dai,
> + sizeof(*soc_dai), GFP_KERNEL);
> + if (!soc_dai) {
> + ret = -ENOMEM;
> + goto err_pm_disable;
> + }
> +
> + if (!of_property_read_u32(pdev->dev.of_node,
> + "allwinner,playback-channels",
&val)) {
> + if (val >= 2 && val <= 8)
> + soc_dai->playback.channels_max = val;
> + }
> +

Rather than inventing new DT properties, I would rather have multiple
snd_soc_dai_driver structures, depending on capabilities of that particular
I2S block. That way we avoid some boilerplate code as can be seen here and
it's IMO more transparent.

In this case, I would make another snd_soc_dai_driver struct for H3, which has
channel_max property set to 8 and from patch 14, additional supported formats.

Best regards,
Jernej

> pm_runtime_enable(&pdev->dev);
> if (!pm_runtime_enabled(&pdev->dev)) {
> ret = sun4i_i2s_runtime_resume(&pdev->dev);
> @@ -1465,7 +1487,7 @@ static int sun4i_i2s_probe(struct platform_device
> *pdev)
>
> ret = devm_snd_soc_register_component(&pdev->dev,
>
&sun4i_i2s_component,
> - &sun4i_i2s_dai,
1);
> + soc_dai, 1);
> if (ret) {
> dev_err(&pdev->dev, "Could not register DAI\n");
> goto err_suspend;




2019-08-14 08:30:04

by Jernej Škrabec

[permalink] [raw]
Subject: Re: [linux-sunxi] [PATCH v5 14/15] ASoc: sun4i-i2s: Add 20, 24 and 32 bit support

Hi!

Dne sreda, 14. avgust 2019 ob 08:08:53 CEST je [email protected]
napisal(a):
> 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(-)
>
> diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
> index a71969167053..d3c8789f70bb 100644
> --- a/sound/soc/sunxi/sun4i-i2s.c
> +++ b/sound/soc/sunxi/sun4i-i2s.c
> @@ -690,6 +690,11 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream
> *substream, case 16:
> width = DMA_SLAVE_BUSWIDTH_2_BYTES;
> break;
> + case 20:
> + case 24:
> + case 32:

params_physical_width() returns 32 also for 20 and 24-bit formats, so drop 20
and 24.

Best regards,
Jernej

> + width = DMA_SLAVE_BUSWIDTH_4_BYTES;
> + 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)




2019-08-14 08:40:44

by Jernej Škrabec

[permalink] [raw]
Subject: Re: [linux-sunxi] [PATCH v5 15/15] ASoC: sun4i-i2s: Adjust regmap settings

Hi!

Dne sreda, 14. avgust 2019 ob 08:08:54 CEST je [email protected]
napisal(a):
> 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(-)
>
> diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
> index d3c8789f70bb..ecfc1ed79379 100644
> --- a/sound/soc/sunxi/sun4i-i2s.c
> +++ b/sound/soc/sunxi/sun4i-i2s.c
> @@ -876,9 +876,11 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai,
> unsigned int fmt) static void sun4i_i2s_start_capture(struct sun4i_i2s
> *i2s)
> {
> /* Flush RX FIFO */
> + regcache_cache_bypass(i2s->regmap, true);
> regmap_update_bits(i2s->regmap, SUN4I_I2S_FIFO_CTRL_REG,
> SUN4I_I2S_FIFO_CTRL_FLUSH_RX,
> SUN4I_I2S_FIFO_CTRL_FLUSH_RX);
> + regcache_cache_bypass(i2s->regmap, false);

Did you try with regmap_write_bits() instead? This function will
unconditionally write bits so it's nicer solution, because you don't have to
use regcache_cache_bypass().

>
> /* Clear RX counter */
> regmap_write(i2s->regmap, SUN4I_I2S_RX_CNT_REG, 0);
> @@ -897,9 +899,11 @@ static void sun4i_i2s_start_capture(struct sun4i_i2s
> *i2s) static void sun4i_i2s_start_playback(struct sun4i_i2s *i2s)
> {
> /* Flush TX FIFO */
> + regcache_cache_bypass(i2s->regmap, true);
> regmap_update_bits(i2s->regmap, SUN4I_I2S_FIFO_CTRL_REG,
> SUN4I_I2S_FIFO_CTRL_FLUSH_TX,
> SUN4I_I2S_FIFO_CTRL_FLUSH_TX);
> + regcache_cache_bypass(i2s->regmap, false);

Ditto.

>
> /* Clear TX counter */
> regmap_write(i2s->regmap, SUN4I_I2S_TX_CNT_REG, 0);
> @@ -1053,13 +1057,7 @@ static const struct snd_soc_component_driver
> sun4i_i2s_component = {
>
> static bool sun4i_i2s_rd_reg(struct device *dev, unsigned int reg)
> {
> - switch (reg) {
> - case SUN4I_I2S_FIFO_TX_REG:
> - return false;
> -
> - default:
> - return true;
> - }
> + return true;

Why did you change this? Manual mentions that SUN4I_I2S_FIFO_TX_REG is write-
only register. Even if it can be read, then it's better to remove whole
function, which will automatically mean that all registers can be read.


> }
>
> static bool sun4i_i2s_wr_reg(struct device *dev, unsigned int reg)
> @@ -1078,6 +1076,8 @@ static bool sun4i_i2s_volatile_reg(struct device *dev,
> unsigned int reg) {
> switch (reg) {
> case SUN4I_I2S_FIFO_RX_REG:
> + case SUN4I_I2S_FIFO_TX_REG:
> + case SUN4I_I2S_FIFO_STA_REG:
> case SUN4I_I2S_INT_STA_REG:
> case SUN4I_I2S_RX_CNT_REG:
> case SUN4I_I2S_TX_CNT_REG:

SUN4I_I2S_FIFO_CTRL_REG should be put here, because it has two bits which
returns to 0 immediately after they are set to 1.

Best regards,
Jernej

> @@ -1088,23 +1088,12 @@ static bool sun4i_i2s_volatile_reg(struct device
> *dev, unsigned int reg) }
> }
>
> -static bool sun8i_i2s_rd_reg(struct device *dev, unsigned int reg)
> -{
> - switch (reg) {
> - case SUN8I_I2S_FIFO_TX_REG:
> - return false;
> -
> - default:
> - return true;
> - }
> -}
> -
> static bool sun8i_i2s_volatile_reg(struct device *dev, unsigned int reg)
> {
> if (reg == SUN8I_I2S_INT_STA_REG)
> return true;
> if (reg == SUN8I_I2S_FIFO_TX_REG)
> - return false;
> + return true;
>
> return sun4i_i2s_volatile_reg(dev, reg);
> }
> @@ -1175,7 +1164,7 @@ static const struct regmap_config
> sun8i_i2s_regmap_config = { .reg_defaults = sun8i_i2s_reg_defaults,
> .num_reg_defaults = ARRAY_SIZE(sun8i_i2s_reg_defaults),
> .writeable_reg = sun4i_i2s_wr_reg,
> - .readable_reg = sun8i_i2s_rd_reg,
> + .readable_reg = sun4i_i2s_rd_reg,
> .volatile_reg = sun8i_i2s_volatile_reg,
> };
>
> @@ -1188,7 +1177,7 @@ static const struct regmap_config
> sun50i_i2s_regmap_config = { .reg_defaults = sun50i_i2s_reg_defaults,
> .num_reg_defaults = ARRAY_SIZE(sun50i_i2s_reg_defaults),
> .writeable_reg = sun4i_i2s_wr_reg,
> - .readable_reg = sun8i_i2s_rd_reg,
> + .readable_reg = sun4i_i2s_rd_reg,
> .volatile_reg = sun8i_i2s_volatile_reg,
> };




2019-08-14 09:04:05

by Code Kipper

[permalink] [raw]
Subject: Re: [linux-sunxi] [PATCH v5 15/15] ASoC: sun4i-i2s: Adjust regmap settings

On Wed, 14 Aug 2019 at 10:38, Jernej Škrabec <[email protected]> wrote:
>
> Hi!
>
> Dne sreda, 14. avgust 2019 ob 08:08:54 CEST je [email protected]
> napisal(a):
> > 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(-)
> >
> > diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
> > index d3c8789f70bb..ecfc1ed79379 100644
> > --- a/sound/soc/sunxi/sun4i-i2s.c
> > +++ b/sound/soc/sunxi/sun4i-i2s.c
> > @@ -876,9 +876,11 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai,
> > unsigned int fmt) static void sun4i_i2s_start_capture(struct sun4i_i2s
> > *i2s)
> > {
> > /* Flush RX FIFO */
> > + regcache_cache_bypass(i2s->regmap, true);
> > regmap_update_bits(i2s->regmap, SUN4I_I2S_FIFO_CTRL_REG,
> > SUN4I_I2S_FIFO_CTRL_FLUSH_RX,
> > SUN4I_I2S_FIFO_CTRL_FLUSH_RX);
> > + regcache_cache_bypass(i2s->regmap, false);
>
> Did you try with regmap_write_bits() instead? This function will
> unconditionally write bits so it's nicer solution, because you don't have to
> use regcache_cache_bypass().

I didn't....with all the rework I've avoided messing with this change.
Now that the dust
has settled, I can go back to look at this.
Thanks,
CK
>
> >
> > /* Clear RX counter */
> > regmap_write(i2s->regmap, SUN4I_I2S_RX_CNT_REG, 0);
> > @@ -897,9 +899,11 @@ static void sun4i_i2s_start_capture(struct sun4i_i2s
> > *i2s) static void sun4i_i2s_start_playback(struct sun4i_i2s *i2s)
> > {
> > /* Flush TX FIFO */
> > + regcache_cache_bypass(i2s->regmap, true);
> > regmap_update_bits(i2s->regmap, SUN4I_I2S_FIFO_CTRL_REG,
> > SUN4I_I2S_FIFO_CTRL_FLUSH_TX,
> > SUN4I_I2S_FIFO_CTRL_FLUSH_TX);
> > + regcache_cache_bypass(i2s->regmap, false);
>
> Ditto.
>
> >
> > /* Clear TX counter */
> > regmap_write(i2s->regmap, SUN4I_I2S_TX_CNT_REG, 0);
> > @@ -1053,13 +1057,7 @@ static const struct snd_soc_component_driver
> > sun4i_i2s_component = {
> >
> > static bool sun4i_i2s_rd_reg(struct device *dev, unsigned int reg)
> > {
> > - switch (reg) {
> > - case SUN4I_I2S_FIFO_TX_REG:
> > - return false;
> > -
> > - default:
> > - return true;
> > - }
> > + return true;
>
> Why did you change this? Manual mentions that SUN4I_I2S_FIFO_TX_REG is write-
> only register. Even if it can be read, then it's better to remove whole
> function, which will automatically mean that all registers can be read.
>
>
> > }
> >
> > static bool sun4i_i2s_wr_reg(struct device *dev, unsigned int reg)
> > @@ -1078,6 +1076,8 @@ static bool sun4i_i2s_volatile_reg(struct device *dev,
> > unsigned int reg) {
> > switch (reg) {
> > case SUN4I_I2S_FIFO_RX_REG:
> > + case SUN4I_I2S_FIFO_TX_REG:
> > + case SUN4I_I2S_FIFO_STA_REG:
> > case SUN4I_I2S_INT_STA_REG:
> > case SUN4I_I2S_RX_CNT_REG:
> > case SUN4I_I2S_TX_CNT_REG:
>
> SUN4I_I2S_FIFO_CTRL_REG should be put here, because it has two bits which
> returns to 0 immediately after they are set to 1.
>
> Best regards,
> Jernej
>
> > @@ -1088,23 +1088,12 @@ static bool sun4i_i2s_volatile_reg(struct device
> > *dev, unsigned int reg) }
> > }
> >
> > -static bool sun8i_i2s_rd_reg(struct device *dev, unsigned int reg)
> > -{
> > - switch (reg) {
> > - case SUN8I_I2S_FIFO_TX_REG:
> > - return false;
> > -
> > - default:
> > - return true;
> > - }
> > -}
> > -
> > static bool sun8i_i2s_volatile_reg(struct device *dev, unsigned int reg)
> > {
> > if (reg == SUN8I_I2S_INT_STA_REG)
> > return true;
> > if (reg == SUN8I_I2S_FIFO_TX_REG)
> > - return false;
> > + return true;
> >
> > return sun4i_i2s_volatile_reg(dev, reg);
> > }
> > @@ -1175,7 +1164,7 @@ static const struct regmap_config
> > sun8i_i2s_regmap_config = { .reg_defaults = sun8i_i2s_reg_defaults,
> > .num_reg_defaults = ARRAY_SIZE(sun8i_i2s_reg_defaults),
> > .writeable_reg = sun4i_i2s_wr_reg,
> > - .readable_reg = sun8i_i2s_rd_reg,
> > + .readable_reg = sun4i_i2s_rd_reg,
> > .volatile_reg = sun8i_i2s_volatile_reg,
> > };
> >
> > @@ -1188,7 +1177,7 @@ static const struct regmap_config
> > sun50i_i2s_regmap_config = { .reg_defaults = sun50i_i2s_reg_defaults,
> > .num_reg_defaults = ARRAY_SIZE(sun50i_i2s_reg_defaults),
> > .writeable_reg = sun4i_i2s_wr_reg,
> > - .readable_reg = sun8i_i2s_rd_reg,
> > + .readable_reg = sun4i_i2s_rd_reg,
> > .volatile_reg = sun8i_i2s_volatile_reg,
> > };
>
>
>
>

2019-08-14 09:05:20

by Code Kipper

[permalink] [raw]
Subject: Re: [linux-sunxi] [PATCH v5 14/15] ASoc: sun4i-i2s: Add 20, 24 and 32 bit support

On Wed, 14 Aug 2019 at 10:28, Jernej Škrabec <[email protected]> wrote:
>
> Hi!
>
> Dne sreda, 14. avgust 2019 ob 08:08:53 CEST je [email protected]
> napisal(a):
> > 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(-)
> >
> > diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
> > index a71969167053..d3c8789f70bb 100644
> > --- a/sound/soc/sunxi/sun4i-i2s.c
> > +++ b/sound/soc/sunxi/sun4i-i2s.c
> > @@ -690,6 +690,11 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream
> > *substream, case 16:
> > width = DMA_SLAVE_BUSWIDTH_2_BYTES;
> > break;
> > + case 20:
> > + case 24:
> > + case 32:
>
> params_physical_width() returns 32 also for 20 and 24-bit formats, so drop 20
> and 24.
ACK
>
> Best regards,
> Jernej
>
> > + width = DMA_SLAVE_BUSWIDTH_4_BYTES;
> > + break;
> > default:
> > dev_err(dai->dev, "Unsupported physical sample width:
> %d\n",
> > params_physical_width(params));
> > @@ -1015,6 +1020,13 @@ static int sun4i_i2s_dai_probe(struct snd_soc_dai
> > *dai) return 0;
> > }
> >
> > +#define SUN4I_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
> > + SNDRV_PCM_FMTBIT_S20_LE | \
> > + SNDRV_PCM_FMTBIT_S24_LE)
> > +
> > +#define SUN8I_FORMATS (SUN4I_FORMATS | \
> > + SNDRV_PCM_FMTBIT_S32_LE)
> > +
> > static struct snd_soc_dai_driver sun4i_i2s_dai = {
> > .probe = sun4i_i2s_dai_probe,
> > .capture = {
> > @@ -1022,14 +1034,14 @@ static struct snd_soc_dai_driver sun4i_i2s_dai = {
> > .channels_min = 2,
> > .channels_max = 2,
> > .rates = SNDRV_PCM_RATE_8000_192000,
> > - .formats = SNDRV_PCM_FMTBIT_S16_LE,
> > + .formats = SUN4I_FORMATS,
> > },
> > .playback = {
> > .stream_name = "Playback",
> > .channels_min = 2,
> > .channels_max = 2,
> > .rates = SNDRV_PCM_RATE_8000_192000,
> > - .formats = SNDRV_PCM_FMTBIT_S16_LE,
> > + .formats = SUN4I_FORMATS,
> > },
> > .ops = &sun4i_i2s_dai_ops,
> > .symmetric_rates = 1,
> > @@ -1505,6 +1517,11 @@ static int sun4i_i2s_probe(struct platform_device
> > *pdev) goto err_pm_disable;
> > }
> >
> > + if (i2s->variant->has_fmt_set_lrck_period) {
> > + soc_dai->playback.formats = SUN8I_FORMATS;
> > + soc_dai->capture.formats = SUN8I_FORMATS;
> > + }
> > +
> > if (!of_property_read_u32(pdev->dev.of_node,
> > "allwinner,playback-channels",
> &val)) {
> > if (val >= 2 && val <= 8)
>
>
>
>

2019-08-14 09:31:43

by Mark Brown

[permalink] [raw]
Subject: Re: [PATCH v5 02/15] ASoC: sun4i-i2s: Add set_tdm_slot functionality

On Wed, Aug 14, 2019 at 08:08:41AM +0200, [email protected] wrote:
> 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.

> return sun4i_i2s_set_clk_rate(dai, params_rate(params),
> - params_width(params));
> + i2s->tdm_slots ?
> + i2s->slot_width : params_width(params));

Please write normal conditional statements unless there's a strong
reason to do otherwise, it makes things more legible.

> +static int sun4i_i2s_set_dai_tdm_slot(struct snd_soc_dai *dai,
> + unsigned int tx_mask,
> + unsigned int rx_mask,
> + int slots, int width)
> +{
> + struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
> +
> + i2s->tdm_slots = slots;
> +
> + i2s->slot_width = width;
> +
> + return 0;
> +}

No validation of the parameters here?


Attachments:
(No filename) (1.06 kB)
signature.asc (499.00 B)
Download all attachments

2019-08-14 09:32:56

by Mark Brown

[permalink] [raw]
Subject: Re: [PATCH v5 03/15] ASoC: sun4i-i2s: Correct divider calculations

On Wed, Aug 14, 2019 at 08:08:42AM +0200, [email protected] wrote:

> + if (i2s->variant->has_fmt_set_lrck_period)
> + bclk_div = sun4i_i2s_get_bclk_div(i2s, clk_rate / rate,
> + word_size,
> + sun8i_i2s_clk_div,
> + ARRAY_SIZE(sun8i_i2s_clk_div));
> + else
> + bclk_div = sun4i_i2s_get_bclk_div(i2s, oversample_rate,
> + word_size,
> + sun4i_i2s_bclk_div,
> + ARRAY_SIZE(sun4i_i2s_bclk_div));

Are we sure there'll never be any new variants which would make a switch
statement for the variant work better?


Attachments:
(No filename) (569.00 B)
signature.asc (499.00 B)
Download all attachments

2019-08-14 11:09:30

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH v5 02/15] ASoC: sun4i-i2s: Add set_tdm_slot functionality

Hi,

On Wed, Aug 14, 2019 at 08:08:41AM +0200, [email protected] wrote:
> 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(-)
>
> diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
> index 8201334a059b..7c37b6291df0 100644
> --- a/sound/soc/sunxi/sun4i-i2s.c
> +++ b/sound/soc/sunxi/sun4i-i2s.c
> @@ -195,6 +195,9 @@ struct sun4i_i2s {
> struct regmap_field *field_rxchansel;
>
> const struct sun4i_i2s_quirks *variant;
> +
> + unsigned int tdm_slots;
> + unsigned int slot_width;
> };
>
> struct sun4i_i2s_clk_div {
> @@ -346,7 +349,7 @@ static int sun4i_i2s_set_clk_rate(struct snd_soc_dai *dai,
> if (i2s->variant->has_fmt_set_lrck_period)
> regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT0_REG,
> SUN8I_I2S_FMT0_LRCK_PERIOD_MASK,
> - SUN8I_I2S_FMT0_LRCK_PERIOD(32));
> + SUN8I_I2S_FMT0_LRCK_PERIOD(word_size));
>
>
> /* Set sign extension to pad out LSB with 0 */
> regmap_field_write(i2s->field_fmt_sext, 0);
> @@ -450,7 +453,8 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
> regmap_field_write(i2s->field_fmt_sr, sr);
>
> return sun4i_i2s_set_clk_rate(dai, params_rate(params),
> - params_width(params));
> + i2s->tdm_slots ?
> + i2s->slot_width : params_width(params));

This is slightly more complicated than that.

On the H3 (and all related ones), the CHAN_CFG_TX_SLOT_NUM and
_RX_SLOT_NUM fields in the CHAN_CFG register need to be set to the
number of slots as well.

Maxime

--
Maxime Ripard, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com


Attachments:
(No filename) (2.00 kB)
signature.asc (235.00 B)
Download all attachments

2019-08-14 11:09:30

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH v5 01/15] ASoC: sun4i-i2s: Add regmap field to sign extend sample

Hi,

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(+)
>
> diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
> index 793457394efe..8201334a059b 100644
> --- a/sound/soc/sunxi/sun4i-i2s.c
> +++ b/sound/soc/sunxi/sun4i-i2s.c
> @@ -135,6 +135,7 @@ struct sun4i_i2s;
> * @field_fmt_bclk: regmap field to set clk polarity.
> * @field_fmt_lrclk: regmap field to set frame polarity.
> * @field_fmt_mode: regmap field to set the operational mode.
> + * @field_fmt_sext: regmap field to set the sign extension.
> * @field_txchanmap: location of the tx channel mapping register.
> * @field_rxchanmap: location of the rx channel mapping register.
> * @field_txchansel: location of the tx channel select bit fields.
> @@ -159,6 +160,7 @@ struct sun4i_i2s_quirks {
> struct reg_field field_fmt_bclk;
> struct reg_field field_fmt_lrclk;
> struct reg_field field_fmt_mode;
> + struct reg_field field_fmt_sext;
> struct reg_field field_txchanmap;
> struct reg_field field_rxchanmap;
> struct reg_field field_txchansel;
> @@ -186,6 +188,7 @@ struct sun4i_i2s {
> struct regmap_field *field_fmt_bclk;
> struct regmap_field *field_fmt_lrclk;
> struct regmap_field *field_fmt_mode;
> + struct regmap_field *field_fmt_sext;
> struct regmap_field *field_txchanmap;
> struct regmap_field *field_rxchanmap;
> struct regmap_field *field_txchansel;
> @@ -345,6 +348,9 @@ static int sun4i_i2s_set_clk_rate(struct snd_soc_dai *dai,
> SUN8I_I2S_FMT0_LRCK_PERIOD_MASK,
> SUN8I_I2S_FMT0_LRCK_PERIOD(32));
>
> + /* Set sign extension to pad out LSB with 0 */
> + regmap_field_write(i2s->field_fmt_sext, 0);
> +
> return 0;
> }
>
> @@ -917,6 +923,7 @@ static const struct sun4i_i2s_quirks sun4i_a10_i2s_quirks = {
> .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
> .has_slave_select_bit = true,
> .field_fmt_mode = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1),
> + .field_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG, 8, 8),
> .field_txchanmap = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31),
> .field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31),
> .field_txchansel = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2),
> @@ -936,6 +943,7 @@ static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = {
> .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
> .has_slave_select_bit = true,
> .field_fmt_mode = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1),
> + .field_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG, 8, 8),
> .field_txchanmap = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31),
> .field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31),
> .field_txchansel = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2),
> @@ -979,6 +987,7 @@ static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = {
> .field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
> .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 19, 19),
> .field_fmt_mode = REG_FIELD(SUN4I_I2S_CTRL_REG, 4, 5),
> + .field_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG, 4, 5),
> .field_txchanmap = REG_FIELD(SUN8I_I2S_TX_CHAN_MAP_REG, 0, 31),
> .field_rxchanmap = REG_FIELD(SUN8I_I2S_RX_CHAN_MAP_REG, 0, 31),
> .field_txchansel = REG_FIELD(SUN8I_I2S_TX_CHAN_SEL_REG, 0, 2),
> @@ -998,6 +1007,7 @@ static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = {
> .field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6),
> .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
> .field_fmt_mode = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1),
> + .field_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG, 8, 8),
> .field_txchanmap = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31),
> .field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31),
> .field_txchansel = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2),

You're missing the A83t here

Maxime

--
Maxime Ripard, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com


Attachments:
(No filename) (4.56 kB)
signature.asc (235.00 B)
Download all attachments

2019-08-14 11:09:39

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH v5 15/15] ASoC: sun4i-i2s: Adjust regmap settings

On Wed, Aug 14, 2019 at 08:08:54AM +0200, [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]>

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


Attachments:
(No filename) (525.00 B)
signature.asc (235.00 B)
Download all attachments

2019-08-14 11:09:46

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH v5 04/15] ASoC: sun4i-i2s: Support more formats on newer SoCs

On Wed, Aug 14, 2019 at 08:08:43AM +0200, [email protected] wrote:
> 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.
>
> Signed-off-by: Marcus Cooper <[email protected]>
> ---
> sound/soc/sunxi/sun4i-i2s.c | 87 +++++++++++++++++++++++++++----------
> 1 file changed, 63 insertions(+), 24 deletions(-)
>
> diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
> index 34f31439ae7b..3553c17318b0 100644
> --- a/sound/soc/sunxi/sun4i-i2s.c
> +++ b/sound/soc/sunxi/sun4i-i2s.c
> @@ -27,6 +27,8 @@
> #define SUN4I_I2S_CTRL_MODE_MASK BIT(5)
> #define SUN4I_I2S_CTRL_MODE_SLAVE (1 << 5)
> #define SUN4I_I2S_CTRL_MODE_MASTER (0 << 5)
> +#define SUN4I_I2S_CTRL_PCM BIT(4)
> +#define SUN4I_I2S_CTRL_LOOP BIT(3)
> #define SUN4I_I2S_CTRL_TX_EN BIT(2)
> #define SUN4I_I2S_CTRL_RX_EN BIT(1)
> #define SUN4I_I2S_CTRL_GL_EN BIT(0)
> @@ -91,6 +93,9 @@
> /* Defines required for sun8i-h3 support */
> #define SUN8I_I2S_CTRL_BCLK_OUT BIT(18)
> #define SUN8I_I2S_CTRL_LRCK_OUT BIT(17)
> +#define SUN8I_I2S_CTRL_MODE_RIGHT_J (2 << 0)
> +#define SUN8I_I2S_CTRL_MODE_I2S_LEFT_J (1 << 0)
> +#define SUN8I_I2S_CTRL_MODE_PCM (0 << 0)
>
> #define SUN8I_I2S_FMT0_LRCK_PERIOD_MASK GENMASK(17, 8)
> #define SUN8I_I2S_FMT0_LRCK_PERIOD(period) ((period - 1) << 8)
> @@ -164,6 +169,7 @@ struct sun4i_i2s_quirks {
>
> s8 (*get_sr)(const struct sun4i_i2s *, int);
> s8 (*get_wss)(const struct sun4i_i2s *, int);
> + int (*set_format)(struct sun4i_i2s *, unsigned int);
> };
>
> struct sun4i_i2s {
> @@ -194,6 +200,7 @@ struct sun4i_i2s {
>
> unsigned int tdm_slots;
> unsigned int slot_width;
> + unsigned int offset;
> };
>
> struct sun4i_i2s_clk_div {
> @@ -484,19 +491,14 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
> i2s->slot_width : params_width(params));
> }
>
> -static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
> +static int sun4i_i2s_set_format(struct sun4i_i2s *i2s, unsigned int fmt)
> {
> - struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
> u32 val;
> - u32 offset = 0;
> - u32 bclk_polarity = SUN4I_I2S_FMT0_POLARITY_NORMAL;
> - u32 lrclk_polarity = SUN4I_I2S_FMT0_POLARITY_NORMAL;
>
> /* DAI Mode */
> switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
> case SND_SOC_DAIFMT_I2S:
> val = SUN4I_I2S_FMT0_FMT_I2S;
> - offset = 1;
> break;
> case SND_SOC_DAIFMT_LEFT_J:
> val = SUN4I_I2S_FMT0_FMT_LEFT_J;
> @@ -505,32 +507,64 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
> val = SUN4I_I2S_FMT0_FMT_RIGHT_J;
> break;
> default:
> - dev_err(dai->dev, "Unsupported format: %d\n",
> - fmt & SND_SOC_DAIFMT_FORMAT_MASK);
> return -EINVAL;
> }
>
> - if (i2s->variant->has_chsel_offset) {
> - /*
> - * offset being set indicates that we're connected to an i2s
> - * device, however offset is only used on the sun8i block and
> - * i2s shares the same setting with the LJ format. Increment
> - * val so that the bit to value to write is correct.
> - */
> - if (offset > 0)
> - val++;
> - /* blck offset determines whether i2s or LJ */
> - regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG,
> - SUN8I_I2S_TX_CHAN_OFFSET_MASK,
> - SUN8I_I2S_TX_CHAN_OFFSET(offset));
> + regmap_field_write(i2s->field_fmt_mode, val);
> +
> + return 0;
> +}
> +
> +static int sun8i_i2s_set_format(struct sun4i_i2s *i2s, unsigned int fmt)
> +{
> + u32 val;
>
> - regmap_update_bits(i2s->regmap, SUN8I_I2S_RX_CHAN_SEL_REG,
> - SUN8I_I2S_TX_CHAN_OFFSET_MASK,
> - SUN8I_I2S_TX_CHAN_OFFSET(offset));
> + /* DAI Mode */
> + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
> + case SND_SOC_DAIFMT_I2S:
> + i2s->offset = 1;
> + case SND_SOC_DAIFMT_LEFT_J:
> + val = SUN8I_I2S_CTRL_MODE_I2S_LEFT_J;
> + break;
> + case SND_SOC_DAIFMT_RIGHT_J:
> + val = SUN8I_I2S_CTRL_MODE_RIGHT_J;
> + break;
> + case SND_SOC_DAIFMT_DSP_A:
> + i2s->offset = 1;
> + case SND_SOC_DAIFMT_DSP_B:
> + val = SUN8I_I2S_CTRL_MODE_PCM;
> + break;
> +
> + default:
> + return -EINVAL;
> }
>
> + /*
> + * bclk offset determines whether i2s or LJ if in i2s mode and
> + * DSP_A or DSP_B if in PCM mode.
> + */
> + i2s->variant->set_txchanoffset(i2s, 0);
> + i2s->variant->set_rxchanoffset(i2s);
> +
> regmap_field_write(i2s->field_fmt_mode, val);

It's a bit more complicated in the sun8i case. The LRCK period also
needs to be changed when in PCM / DSP_* mode since it changes from a
number of periods for one channel to a number of periods for all the
channels.

I have patches that still need a bit of rework and take care of all of
that, I'll try to post them by the end of the week

Maxime

--
Maxime Ripard, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com


Attachments:
(No filename) (4.92 kB)
signature.asc (235.00 B)
Download all attachments

2019-08-14 11:09:48

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH v5 12/15] ASoC: sun4i-i2s: Add multi-lane functionality

On Wed, Aug 14, 2019 at 08:08:51AM +0200, [email protected] wrote:
> 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


Attachments:
(No filename) (672.00 B)
signature.asc (235.00 B)
Download all attachments

2019-08-14 11:10:43

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH v5 03/15] ASoC: sun4i-i2s: Correct divider calculations

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])

On Wed, Aug 14, 2019 at 08:08:42AM +0200, [email protected] wrote:
> 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(-)
>
> diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
> index 7c37b6291df0..34f31439ae7b 100644
> --- a/sound/soc/sunxi/sun4i-i2s.c
> +++ b/sound/soc/sunxi/sun4i-i2s.c
> @@ -127,8 +127,6 @@ struct sun4i_i2s;
> * @has_chsel_offset: SoC uses offset for selecting dai operational mode.
> * @reg_offset_txdata: offset of the tx fifo.
> * @sun4i_i2s_regmap: regmap config to use.
> - * @mclk_offset: Value by which mclkdiv needs to be adjusted.
> - * @bclk_offset: Value by which bclkdiv needs to be adjusted.
> * @field_clkdiv_mclk_en: regmap field to enable mclk output.
> * @field_fmt_wss: regmap field to set word select size.
> * @field_fmt_sr: regmap field to set sample resolution.
> @@ -150,8 +148,6 @@ struct sun4i_i2s_quirks {
> bool has_chsel_offset;
> unsigned int reg_offset_txdata; /* TX FIFO */
> const struct regmap_config *sun4i_i2s_regmap;
> - unsigned int mclk_offset;
> - unsigned int bclk_offset;
>
> /* Register fields for i2s */
> struct reg_field field_clkdiv_mclk_en;
> @@ -212,7 +208,25 @@ static const struct sun4i_i2s_clk_div sun4i_i2s_bclk_div[] = {
> { .div = 8, .val = 3 },
> { .div = 12, .val = 4 },
> { .div = 16, .val = 5 },
> - /* TODO - extend divide ratio supported by newer SoCs */
> +};
> +
> +static const struct sun4i_i2s_clk_div sun8i_i2s_clk_div[] = {
> + { .div = 0, .val = 0 },

Having a divider of 0 seems like a bad idea.

> + { .div = 1, .val = 1 },
> + { .div = 2, .val = 2 },
> + { .div = 4, .val = 3 },
> + { .div = 6, .val = 4 },
> + { .div = 8, .val = 5 },
> + { .div = 12, .val = 6 },
> + { .div = 16, .val = 7 },
> + { .div = 24, .val = 8 },
> + { .div = 32, .val = 9 },
> + { .div = 48, .val = 10 },
> + { .div = 64, .val = 11 },
> + { .div = 96, .val = 12 },
> + { .div = 128, .val = 13 },
> + { .div = 176, .val = 14 },
> + { .div = 192, .val = 15 },
> };
>
> static const struct sun4i_i2s_clk_div sun4i_i2s_mclk_div[] = {
> @@ -224,21 +238,21 @@ static const struct sun4i_i2s_clk_div sun4i_i2s_mclk_div[] = {
> { .div = 12, .val = 5 },
> { .div = 16, .val = 6 },
> { .div = 24, .val = 7 },
> - /* TODO - extend divide ratio supported by newer SoCs */
> };
>
> static int sun4i_i2s_get_bclk_div(struct sun4i_i2s *i2s,
> unsigned int oversample_rate,
> - unsigned int word_size)
> + unsigned int word_size,
> + const struct sun4i_i2s_clk_div *bdiv,
> + unsigned int size)

Wouldn't it be simpler to just have the divider list in the variant
structure? It would avoid having to refactor all the functions, and
it's not like it's really going to change from one call to another
anyway.

Maxime

--
Maxime Ripard, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com


Attachments:
(No filename) (3.34 kB)
signature.asc (235.00 B)
Download all attachments

2019-08-14 11:10:57

by Code Kipper

[permalink] [raw]
Subject: Re: [PATCH v5 11/15] ASoC: sun4i-i2s: Add support for H6 I2S

On Wed, 14 Aug 2019 at 09:57, Jernej Škrabec <[email protected]> wrote:
>
> Hi!
>
> Dne sreda, 14. avgust 2019 ob 08:08:50 CEST je [email protected]
> napisal(a):
> > 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.
ACK
>
> Best regards,
> Jernej
>
> > ---
> > sound/soc/sunxi/sun4i-i2s.c | 148 ++++++++++++++++++++++++++++++++++++
> > 1 file changed, 148 insertions(+)
> >
> > diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
> > index 6de3cb41aaf6..a8d98696fe7c 100644
> > --- a/sound/soc/sunxi/sun4i-i2s.c
> > +++ b/sound/soc/sunxi/sun4i-i2s.c
> > @@ -121,6 +121,21 @@
> > #define SUN8I_I2S_RX_CHAN_SEL_REG 0x54
> > #define SUN8I_I2S_RX_CHAN_MAP_REG 0x58
> >
> > +/* Defines required for sun50i-h6 support */
> > +#define SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET_MASK GENMASK(21, 20)
> > +#define SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET(offset) ((offset) << 20)
> > +#define SUN50I_H6_I2S_TX_CHAN_SEL_MASK GENMASK(19, 16)
> > +#define SUN50I_H6_I2S_TX_CHAN_SEL(chan) ((chan - 1) << 16)
> > +#define SUN50I_H6_I2S_TX_CHAN_EN_MASK GENMASK(15, 0)
> > +#define SUN50I_H6_I2S_TX_CHAN_EN(num_chan) (((1 << num_chan) - 1))
> > +
> > +#define SUN50I_H6_I2S_TX_CHAN_MAP0_REG 0x44
> > +#define SUN50I_H6_I2S_TX_CHAN_MAP1_REG 0x48
> > +
> > +#define SUN50I_H6_I2S_RX_CHAN_SEL_REG 0x64
> > +#define SUN50I_H6_I2S_RX_CHAN_MAP0_REG 0x68
> > +#define SUN50I_H6_I2S_RX_CHAN_MAP1_REG 0x6C
> > +
> > struct sun4i_i2s;
> >
> > /**
> > @@ -440,6 +455,25 @@ static void sun8i_i2s_set_rxchanoffset(const struct
> > sun4i_i2s *i2s) SUN8I_I2S_TX_CHAN_OFFSET(i2s->offset));
> > }
> >
> > +static void sun50i_h6_i2s_set_txchanoffset(const struct sun4i_i2s *i2s, int
> > output) +{
> > + if (output >= 0 && output < 4) {
> > + regmap_update_bits(i2s->regmap,
> > + SUN8I_I2S_TX_CHAN_SEL_REG +
> (output * 4),
> > +
> SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET_MASK,
> > +
> SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET(i2s->offset));
> > + }
> > +
> > +}
> > +
> > +static void sun50i_h6_i2s_set_rxchanoffset(const struct sun4i_i2s *i2s)
> > +{
> > + regmap_update_bits(i2s->regmap,
> > + SUN50I_H6_I2S_RX_CHAN_SEL_REG,
> > + SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET_MASK,
> > + SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET(i2s-
> >offset));
> > +}
> > +
> > static void sun8i_i2s_set_txchanen(const struct sun4i_i2s *i2s, int output,
> > int channel)
> > {
> > @@ -459,6 +493,26 @@ static void sun8i_i2s_set_rxchanen(const struct
> > sun4i_i2s *i2s, int channel) SUN8I_I2S_TX_CHAN_EN(channel));
> > }
> >
> > +
> > +static void sun50i_h6_i2s_set_txchanen(const struct sun4i_i2s *i2s, int
> > output, + int channel)
> > +{
> > + if (output >= 0 && output < 4) {
> > + regmap_update_bits(i2s->regmap,
> > + SUN8I_I2S_TX_CHAN_SEL_REG +
> (output * 4),
> > + SUN50I_H6_I2S_TX_CHAN_EN_MASK,
> > +
> SUN50I_H6_I2S_TX_CHAN_EN(channel));
> > + }
> > +}
> > +
> > +static void sun50i_h6_i2s_set_rxchanen(const struct sun4i_i2s *i2s, int
> > channel) +{
> > + regmap_update_bits(i2s->regmap,
> > + SUN50I_H6_I2S_RX_CHAN_SEL_REG,
> > + SUN50I_H6_I2S_TX_CHAN_EN_MASK,
> > + SUN50I_H6_I2S_TX_CHAN_EN(channel));
> > +}
> > +
> > static void sun4i_i2s_set_txchansel(const struct sun4i_i2s *i2s, int
> > output, int channel)
> > {
> > @@ -495,6 +549,25 @@ static void sun8i_i2s_set_rxchansel(const struct
> > sun4i_i2s *i2s, int channel) SUN8I_I2S_TX_CHAN_SEL(channel));
> > }
> >
> > +static void sun50i_h6_i2s_set_txchansel(const struct sun4i_i2s *i2s, int
> > output, + int channel)
> > +{
> > + if (output >= 0 && output < 4) {
> > + regmap_update_bits(i2s->regmap,
> > + SUN8I_I2S_TX_CHAN_SEL_REG +
> (output * 4),
> > + SUN50I_H6_I2S_TX_CHAN_SEL_MASK,
> > +
> SUN50I_H6_I2S_TX_CHAN_SEL(channel));
> > + }
> > +}
> > +
> > +static void sun50i_h6_i2s_set_rxchansel(const struct sun4i_i2s *i2s, int
> > channel) +{
> > + regmap_update_bits(i2s->regmap,
> > + SUN50I_H6_I2S_RX_CHAN_SEL_REG,
> > + SUN50I_H6_I2S_TX_CHAN_SEL_MASK,
> > + SUN50I_H6_I2S_TX_CHAN_SEL(channel));
> > +}
> > +
> > static void sun4i_i2s_set_txchanmap(const struct sun4i_i2s *i2s, int
> > output, int channel)
> > {
> > @@ -520,6 +593,20 @@ static void sun8i_i2s_set_rxchanmap(const struct
> > sun4i_i2s *i2s, int channel) regmap_write(i2s->regmap,
> > SUN8I_I2S_RX_CHAN_MAP_REG, channel);
> > }
> >
> > +static void sun50i_h6_i2s_set_txchanmap(const struct sun4i_i2s *i2s, int
> > output, + int channel)
> > +{
> > + if (output >= 0 && output < 4) {
> > + regmap_write(i2s->regmap,
> > + SUN50I_H6_I2S_TX_CHAN_MAP1_REG + (output
> * 8), channel);
> > + }
> > +}
> > +
> > +static void sun50i_h6_i2s_set_rxchanmap(const struct sun4i_i2s *i2s, int
> > channel) +{
> > + regmap_write(i2s->regmap, SUN50I_H6_I2S_RX_CHAN_MAP1_REG, channel);
> > +}
> > +
> > static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
> > struct snd_pcm_hw_params *params,
> > struct snd_soc_dai *dai)
> > @@ -996,6 +1083,22 @@ static const struct reg_default
> > sun8i_i2s_reg_defaults[] = { { SUN8I_I2S_RX_CHAN_MAP_REG, 0x00000000 },
> > };
> >
> > +static const struct reg_default sun50i_i2s_reg_defaults[] = {
> > + { SUN4I_I2S_CTRL_REG, 0x00060000 },
> > + { SUN4I_I2S_FMT0_REG, 0x00000033 },
> > + { SUN4I_I2S_FMT1_REG, 0x00000030 },
> > + { SUN4I_I2S_FIFO_CTRL_REG, 0x000400f0 },
> > + { SUN4I_I2S_DMA_INT_CTRL_REG, 0x00000000 },
> > + { SUN4I_I2S_CLK_DIV_REG, 0x00000000 },
> > + { SUN8I_I2S_CHAN_CFG_REG, 0x00000000 },
> > + { SUN8I_I2S_TX_CHAN_SEL_REG, 0x00000000 },
> > + { SUN50I_H6_I2S_TX_CHAN_MAP0_REG, 0x00000000 },
> > + { SUN50I_H6_I2S_TX_CHAN_MAP1_REG, 0x00000000 },
> > + { SUN50I_H6_I2S_RX_CHAN_SEL_REG, 0x00000000 },
> > + { SUN50I_H6_I2S_RX_CHAN_MAP0_REG, 0x00000000 },
> > + { SUN50I_H6_I2S_RX_CHAN_MAP1_REG, 0x00000000 },
> > +};
> > +
> > static const struct regmap_config sun4i_i2s_regmap_config = {
> > .reg_bits = 32,
> > .reg_stride = 4,
> > @@ -1023,6 +1126,19 @@ static const struct regmap_config
> > sun8i_i2s_regmap_config = { .volatile_reg = sun8i_i2s_volatile_reg,
> > };
> >
> > +static const struct regmap_config sun50i_i2s_regmap_config = {
> > + .reg_bits = 32,
> > + .reg_stride = 4,
> > + .val_bits = 32,
> > + .max_register = SUN50I_H6_I2S_RX_CHAN_MAP1_REG,
> > + .cache_type = REGCACHE_FLAT,
> > + .reg_defaults = sun50i_i2s_reg_defaults,
> > + .num_reg_defaults = ARRAY_SIZE(sun50i_i2s_reg_defaults),
> > + .writeable_reg = sun4i_i2s_wr_reg,
> > + .readable_reg = sun8i_i2s_rd_reg,
> > + .volatile_reg = sun8i_i2s_volatile_reg,
> > +};
> > +
> > static int sun4i_i2s_runtime_resume(struct device *dev)
> > {
> > struct sun4i_i2s *i2s = dev_get_drvdata(dev);
> > @@ -1197,6 +1313,34 @@ static const struct sun4i_i2s_quirks
> > sun50i_a64_codec_i2s_quirks = { .set_rxchanmap =
> sun4i_i2s_set_rxchanmap,
> > };
> >
> > +static const struct sun4i_i2s_quirks sun50i_h6_i2s_quirks = {
> > + .has_reset = true,
> > + .reg_offset_txdata = SUN8I_I2S_FIFO_TX_REG,
> > + .sun4i_i2s_regmap = &sun50i_i2s_regmap_config,
> > + .has_fmt_set_lrck_period = true,
> > + .has_chcfg = true,
> > + .has_chsel_tx_chen = true,
> > + .has_chsel_offset = true,
> > + .field_clkdiv_mclk_en = REG_FIELD(SUN4I_I2S_CLK_DIV_REG, 8, 8),
> > + .field_fmt_wss = REG_FIELD(SUN4I_I2S_FMT0_REG,
> 0, 2),
> > + .field_fmt_sr = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 6),
> > + .field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG,
> 7, 7),
> > + .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 19, 19),
> > + .field_fmt_mode = REG_FIELD(SUN4I_I2S_CTRL_REG, 4,
> 5),
> > + .field_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG,
> 4, 5),
> > + .get_sr = sun8i_i2s_get_sr_wss,
> > + .get_wss = sun8i_i2s_get_sr_wss,
> > + .set_format = sun8i_i2s_set_format,
> > + .set_txchanoffset = sun50i_h6_i2s_set_txchanoffset,
> > + .set_rxchanoffset = sun50i_h6_i2s_set_rxchanoffset,
> > + .set_txchanen = sun50i_h6_i2s_set_txchanen,
> > + .set_rxchanen = sun50i_h6_i2s_set_rxchanen,
> > + .set_txchansel = sun50i_h6_i2s_set_txchansel,
> > + .set_rxchansel = sun50i_h6_i2s_set_rxchansel,
> > + .set_txchanmap = sun50i_h6_i2s_set_txchanmap,
> > + .set_rxchanmap = sun50i_h6_i2s_set_rxchanmap,
> > +};
> > +
> > static int sun4i_i2s_init_regmap_fields(struct device *dev,
> > struct sun4i_i2s *i2s)
> > {
> > @@ -1389,6 +1533,10 @@ static const struct of_device_id sun4i_i2s_match[] =
> > { .compatible = "allwinner,sun50i-a64-codec-i2s",
> > .data = &sun50i_a64_codec_i2s_quirks,
> > },
> > + {
> > + .compatible = "allwinner,sun50i-h6-i2s",
> > + .data = &sun50i_h6_i2s_quirks,
> > + },
> > {}
> > };
> > MODULE_DEVICE_TABLE(of, sun4i_i2s_match);
>
>
>
>

2019-08-14 11:19:28

by Code Kipper

[permalink] [raw]
Subject: Re: [PATCH v5 12/15] ASoC: sun4i-i2s: Add multi-lane functionality

On Wed, 14 Aug 2019 at 13:08, Maxime Ripard <[email protected]> wrote:
>
> On Wed, Aug 14, 2019 at 08:08:51AM +0200, [email protected] wrote:
> > 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?
Stereo HDMI can be introduced on the H3 and later if we get the first
three patches
merged. Post those patches is the work to get multi-channel working.
>
> Either way, that property should be documented.
I can do this...but I'm thinking we should bang our heads together to
find a solution
that we all agree on...especially if we're considering multi-channel
tdm support.
Thanks,
CK
>
> Maxime
> --
> Maxime Ripard, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com

2019-08-14 11:25:44

by Code Kipper

[permalink] [raw]
Subject: Re: [PATCH v5 04/15] ASoC: sun4i-i2s: Support more formats on newer SoCs

On Wed, 14 Aug 2019 at 13:08, Maxime Ripard <[email protected]> wrote:
>
> On Wed, Aug 14, 2019 at 08:08:43AM +0200, [email protected] wrote:
> > 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.
> >
> > Signed-off-by: Marcus Cooper <[email protected]>
> > ---
> > sound/soc/sunxi/sun4i-i2s.c | 87 +++++++++++++++++++++++++++----------
> > 1 file changed, 63 insertions(+), 24 deletions(-)
> >
> > diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
> > index 34f31439ae7b..3553c17318b0 100644
> > --- a/sound/soc/sunxi/sun4i-i2s.c
> > +++ b/sound/soc/sunxi/sun4i-i2s.c
> > @@ -27,6 +27,8 @@
> > #define SUN4I_I2S_CTRL_MODE_MASK BIT(5)
> > #define SUN4I_I2S_CTRL_MODE_SLAVE (1 << 5)
> > #define SUN4I_I2S_CTRL_MODE_MASTER (0 << 5)
> > +#define SUN4I_I2S_CTRL_PCM BIT(4)
> > +#define SUN4I_I2S_CTRL_LOOP BIT(3)
> > #define SUN4I_I2S_CTRL_TX_EN BIT(2)
> > #define SUN4I_I2S_CTRL_RX_EN BIT(1)
> > #define SUN4I_I2S_CTRL_GL_EN BIT(0)
> > @@ -91,6 +93,9 @@
> > /* Defines required for sun8i-h3 support */
> > #define SUN8I_I2S_CTRL_BCLK_OUT BIT(18)
> > #define SUN8I_I2S_CTRL_LRCK_OUT BIT(17)
> > +#define SUN8I_I2S_CTRL_MODE_RIGHT_J (2 << 0)
> > +#define SUN8I_I2S_CTRL_MODE_I2S_LEFT_J (1 << 0)
> > +#define SUN8I_I2S_CTRL_MODE_PCM (0 << 0)
> >
> > #define SUN8I_I2S_FMT0_LRCK_PERIOD_MASK GENMASK(17, 8)
> > #define SUN8I_I2S_FMT0_LRCK_PERIOD(period) ((period - 1) << 8)
> > @@ -164,6 +169,7 @@ struct sun4i_i2s_quirks {
> >
> > s8 (*get_sr)(const struct sun4i_i2s *, int);
> > s8 (*get_wss)(const struct sun4i_i2s *, int);
> > + int (*set_format)(struct sun4i_i2s *, unsigned int);
> > };
> >
> > struct sun4i_i2s {
> > @@ -194,6 +200,7 @@ struct sun4i_i2s {
> >
> > unsigned int tdm_slots;
> > unsigned int slot_width;
> > + unsigned int offset;
> > };
> >
> > struct sun4i_i2s_clk_div {
> > @@ -484,19 +491,14 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
> > i2s->slot_width : params_width(params));
> > }
> >
> > -static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
> > +static int sun4i_i2s_set_format(struct sun4i_i2s *i2s, unsigned int fmt)
> > {
> > - struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
> > u32 val;
> > - u32 offset = 0;
> > - u32 bclk_polarity = SUN4I_I2S_FMT0_POLARITY_NORMAL;
> > - u32 lrclk_polarity = SUN4I_I2S_FMT0_POLARITY_NORMAL;
> >
> > /* DAI Mode */
> > switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
> > case SND_SOC_DAIFMT_I2S:
> > val = SUN4I_I2S_FMT0_FMT_I2S;
> > - offset = 1;
> > break;
> > case SND_SOC_DAIFMT_LEFT_J:
> > val = SUN4I_I2S_FMT0_FMT_LEFT_J;
> > @@ -505,32 +507,64 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
> > val = SUN4I_I2S_FMT0_FMT_RIGHT_J;
> > break;
> > default:
> > - dev_err(dai->dev, "Unsupported format: %d\n",
> > - fmt & SND_SOC_DAIFMT_FORMAT_MASK);
> > return -EINVAL;
> > }
> >
> > - if (i2s->variant->has_chsel_offset) {
> > - /*
> > - * offset being set indicates that we're connected to an i2s
> > - * device, however offset is only used on the sun8i block and
> > - * i2s shares the same setting with the LJ format. Increment
> > - * val so that the bit to value to write is correct.
> > - */
> > - if (offset > 0)
> > - val++;
> > - /* blck offset determines whether i2s or LJ */
> > - regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG,
> > - SUN8I_I2S_TX_CHAN_OFFSET_MASK,
> > - SUN8I_I2S_TX_CHAN_OFFSET(offset));
> > + regmap_field_write(i2s->field_fmt_mode, val);
> > +
> > + return 0;
> > +}
> > +
> > +static int sun8i_i2s_set_format(struct sun4i_i2s *i2s, unsigned int fmt)
> > +{
> > + u32 val;
> >
> > - regmap_update_bits(i2s->regmap, SUN8I_I2S_RX_CHAN_SEL_REG,
> > - SUN8I_I2S_TX_CHAN_OFFSET_MASK,
> > - SUN8I_I2S_TX_CHAN_OFFSET(offset));
> > + /* DAI Mode */
> > + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
> > + case SND_SOC_DAIFMT_I2S:
> > + i2s->offset = 1;
> > + case SND_SOC_DAIFMT_LEFT_J:
> > + val = SUN8I_I2S_CTRL_MODE_I2S_LEFT_J;
> > + break;
> > + case SND_SOC_DAIFMT_RIGHT_J:
> > + val = SUN8I_I2S_CTRL_MODE_RIGHT_J;
> > + break;
> > + case SND_SOC_DAIFMT_DSP_A:
> > + i2s->offset = 1;
> > + case SND_SOC_DAIFMT_DSP_B:
> > + val = SUN8I_I2S_CTRL_MODE_PCM;
> > + break;
> > +
> > + default:
> > + return -EINVAL;
> > }
> >
> > + /*
> > + * bclk offset determines whether i2s or LJ if in i2s mode and
> > + * DSP_A or DSP_B if in PCM mode.
> > + */
> > + i2s->variant->set_txchanoffset(i2s, 0);
> > + i2s->variant->set_rxchanoffset(i2s);
> > +
> > regmap_field_write(i2s->field_fmt_mode, val);
>
> It's a bit more complicated in the sun8i case. The LRCK period also
> needs to be changed when in PCM / DSP_* mode since it changes from a
> number of periods for one channel to a number of periods for all the
> channels.
Yeah I was thinking that but I don't have any hardware to test this with (been
helping out someone trying to connect to a modem).
>
> I have patches that still need a bit of rework and take care of all of
> that, I'll try to post them by the end of the week

This patch could be dropped for now or at least we just keep the
offset parts in.
BR,
CK

>
> Maxime
>
> --
> Maxime Ripard, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com

2019-08-14 11:25:51

by Code Kipper

[permalink] [raw]
Subject: Re: [PATCH v5 01/15] ASoC: sun4i-i2s: Add regmap field to sign extend sample

On Wed, 14 Aug 2019 at 13:08, Maxime Ripard <[email protected]> wrote:
>
> Hi,
>
> 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(+)
> >
> > diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
> > index 793457394efe..8201334a059b 100644
> > --- a/sound/soc/sunxi/sun4i-i2s.c
> > +++ b/sound/soc/sunxi/sun4i-i2s.c
> > @@ -135,6 +135,7 @@ struct sun4i_i2s;
> > * @field_fmt_bclk: regmap field to set clk polarity.
> > * @field_fmt_lrclk: regmap field to set frame polarity.
> > * @field_fmt_mode: regmap field to set the operational mode.
> > + * @field_fmt_sext: regmap field to set the sign extension.
> > * @field_txchanmap: location of the tx channel mapping register.
> > * @field_rxchanmap: location of the rx channel mapping register.
> > * @field_txchansel: location of the tx channel select bit fields.
> > @@ -159,6 +160,7 @@ struct sun4i_i2s_quirks {
> > struct reg_field field_fmt_bclk;
> > struct reg_field field_fmt_lrclk;
> > struct reg_field field_fmt_mode;
> > + struct reg_field field_fmt_sext;
> > struct reg_field field_txchanmap;
> > struct reg_field field_rxchanmap;
> > struct reg_field field_txchansel;
> > @@ -186,6 +188,7 @@ struct sun4i_i2s {
> > struct regmap_field *field_fmt_bclk;
> > struct regmap_field *field_fmt_lrclk;
> > struct regmap_field *field_fmt_mode;
> > + struct regmap_field *field_fmt_sext;
> > struct regmap_field *field_txchanmap;
> > struct regmap_field *field_rxchanmap;
> > struct regmap_field *field_txchansel;
> > @@ -345,6 +348,9 @@ static int sun4i_i2s_set_clk_rate(struct snd_soc_dai *dai,
> > SUN8I_I2S_FMT0_LRCK_PERIOD_MASK,
> > SUN8I_I2S_FMT0_LRCK_PERIOD(32));
> >
> > + /* Set sign extension to pad out LSB with 0 */
> > + regmap_field_write(i2s->field_fmt_sext, 0);
> > +
> > return 0;
> > }
> >
> > @@ -917,6 +923,7 @@ static const struct sun4i_i2s_quirks sun4i_a10_i2s_quirks = {
> > .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
> > .has_slave_select_bit = true,
> > .field_fmt_mode = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1),
> > + .field_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG, 8, 8),
> > .field_txchanmap = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31),
> > .field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31),
> > .field_txchansel = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2),
> > @@ -936,6 +943,7 @@ static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = {
> > .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
> > .has_slave_select_bit = true,
> > .field_fmt_mode = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1),
> > + .field_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG, 8, 8),
> > .field_txchanmap = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31),
> > .field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31),
> > .field_txchansel = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2),
> > @@ -979,6 +987,7 @@ static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = {
> > .field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
> > .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 19, 19),
> > .field_fmt_mode = REG_FIELD(SUN4I_I2S_CTRL_REG, 4, 5),
> > + .field_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG, 4, 5),
> > .field_txchanmap = REG_FIELD(SUN8I_I2S_TX_CHAN_MAP_REG, 0, 31),
> > .field_rxchanmap = REG_FIELD(SUN8I_I2S_RX_CHAN_MAP_REG, 0, 31),
> > .field_txchansel = REG_FIELD(SUN8I_I2S_TX_CHAN_SEL_REG, 0, 2),
> > @@ -998,6 +1007,7 @@ static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = {
> > .field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6),
> > .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
> > .field_fmt_mode = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1),
> > + .field_fmt_sext = REG_FIELD(SUN4I_I2S_FMT1_REG, 8, 8),
> > .field_txchanmap = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31),
> > .field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31),
> > .field_txchansel = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2),
>
> You're missing the A83t here

ARRGGGHHHHH...ACK...thanks,
CK
>
> Maxime
>
> --
> Maxime Ripard, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com

2019-08-14 11:32:13

by Jernej Škrabec

[permalink] [raw]
Subject: Re: [linux-sunxi] Re: [PATCH v5 15/15] ASoC: sun4i-i2s: Adjust regmap settings

Dne sreda, 14. avgust 2019 ob 09:20:07 CEST je Maxime Ripard napisal(a):
> On Wed, Aug 14, 2019 at 08:08:54AM +0200, [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]>
>
> This patch looks like it's fixing something while the commit log
> doesn't mention what is being fixed.

Main issue addressed here is that SUN4I_I2S_FIFO_CTRL_REG has two self-clear
registers (SUN4I_I2S_FIFO_CTRL_FLUSH_RX and SUN4I_I2S_FIFO_CTRL_FLUSH_TX) and
thus it should be marked as volatile.

Best regards,
Jernej

>
> Having some context here would be great.
>
> Maxime
>
> --
> Maxime Ripard, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com




2019-08-14 11:34:39

by Chen-Yu Tsai

[permalink] [raw]
Subject: Re: [PATCH v5 15/15] ASoC: sun4i-i2s: Adjust regmap settings

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(-)
>
> diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
> index d3c8789f70bb..ecfc1ed79379 100644
> --- a/sound/soc/sunxi/sun4i-i2s.c
> +++ b/sound/soc/sunxi/sun4i-i2s.c
> @@ -876,9 +876,11 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
> static void sun4i_i2s_start_capture(struct sun4i_i2s *i2s)
> {
> /* Flush RX FIFO */
> + regcache_cache_bypass(i2s->regmap, true);
> regmap_update_bits(i2s->regmap, SUN4I_I2S_FIFO_CTRL_REG,
> SUN4I_I2S_FIFO_CTRL_FLUSH_RX,
> SUN4I_I2S_FIFO_CTRL_FLUSH_RX);
> + regcache_cache_bypass(i2s->regmap, false);
>
> /* Clear RX counter */
> regmap_write(i2s->regmap, SUN4I_I2S_RX_CNT_REG, 0);
> @@ -897,9 +899,11 @@ static void sun4i_i2s_start_capture(struct sun4i_i2s *i2s)
> static void sun4i_i2s_start_playback(struct sun4i_i2s *i2s)
> {
> /* Flush TX FIFO */
> + regcache_cache_bypass(i2s->regmap, true);
> regmap_update_bits(i2s->regmap, SUN4I_I2S_FIFO_CTRL_REG,
> SUN4I_I2S_FIFO_CTRL_FLUSH_TX,
> SUN4I_I2S_FIFO_CTRL_FLUSH_TX);
> + regcache_cache_bypass(i2s->regmap, false);
>
> /* Clear TX counter */
> regmap_write(i2s->regmap, SUN4I_I2S_TX_CNT_REG, 0);
> @@ -1053,13 +1057,7 @@ static const struct snd_soc_component_driver sun4i_i2s_component = {
>
> static bool sun4i_i2s_rd_reg(struct device *dev, unsigned int reg)
> {
> - switch (reg) {
> - case SUN4I_I2S_FIFO_TX_REG:
> - return false;
> -
> - default:
> - return true;
> - }
> + return true;

The commit log needs to explain why this is relevant. And I'm not sure why one
would read back the TX FIFO. Also, if it's always true, just drop the callback.

ChenYu

> }
>
> static bool sun4i_i2s_wr_reg(struct device *dev, unsigned int reg)
> @@ -1078,6 +1076,8 @@ static bool sun4i_i2s_volatile_reg(struct device *dev, unsigned int reg)
> {
> switch (reg) {
> case SUN4I_I2S_FIFO_RX_REG:
> + case SUN4I_I2S_FIFO_TX_REG:
> + case SUN4I_I2S_FIFO_STA_REG:
> case SUN4I_I2S_INT_STA_REG:
> case SUN4I_I2S_RX_CNT_REG:
> case SUN4I_I2S_TX_CNT_REG:
> @@ -1088,23 +1088,12 @@ static bool sun4i_i2s_volatile_reg(struct device *dev, unsigned int reg)
> }
> }
>
> -static bool sun8i_i2s_rd_reg(struct device *dev, unsigned int reg)
> -{
> - switch (reg) {
> - case SUN8I_I2S_FIFO_TX_REG:
> - return false;
> -
> - default:
> - return true;
> - }
> -}
> -
> static bool sun8i_i2s_volatile_reg(struct device *dev, unsigned int reg)
> {
> if (reg == SUN8I_I2S_INT_STA_REG)
> return true;
> if (reg == SUN8I_I2S_FIFO_TX_REG)
> - return false;
> + return true;
>
> return sun4i_i2s_volatile_reg(dev, reg);
> }
> @@ -1175,7 +1164,7 @@ static const struct regmap_config sun8i_i2s_regmap_config = {
> .reg_defaults = sun8i_i2s_reg_defaults,
> .num_reg_defaults = ARRAY_SIZE(sun8i_i2s_reg_defaults),
> .writeable_reg = sun4i_i2s_wr_reg,
> - .readable_reg = sun8i_i2s_rd_reg,
> + .readable_reg = sun4i_i2s_rd_reg,
> .volatile_reg = sun8i_i2s_volatile_reg,
> };
>
> @@ -1188,7 +1177,7 @@ static const struct regmap_config sun50i_i2s_regmap_config = {
> .reg_defaults = sun50i_i2s_reg_defaults,
> .num_reg_defaults = ARRAY_SIZE(sun50i_i2s_reg_defaults),
> .writeable_reg = sun4i_i2s_wr_reg,
> - .readable_reg = sun8i_i2s_rd_reg,
> + .readable_reg = sun4i_i2s_rd_reg,
> .volatile_reg = sun8i_i2s_volatile_reg,
> };
>
> --
> 2.22.0
>

2019-08-16 06:23:53

by Code Kipper

[permalink] [raw]
Subject: Re: [PATCH v5 02/15] ASoC: sun4i-i2s: Add set_tdm_slot functionality

On Wed, 14 Aug 2019 at 11:30, Mark Brown <[email protected]> wrote:
>
> On Wed, Aug 14, 2019 at 08:08:41AM +0200, [email protected] wrote:
> > 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
>
> > return sun4i_i2s_set_clk_rate(dai, params_rate(params),
> > - params_width(params));
> > + i2s->tdm_slots ?
> > + i2s->slot_width : params_width(params));
>
> Please write normal conditional statements unless there's a strong
> reason to do otherwise, it makes things more legible.
ACK
>
> > +static int sun4i_i2s_set_dai_tdm_slot(struct snd_soc_dai *dai,
> > + unsigned int tx_mask,
> > + unsigned int rx_mask,
> > + int slots, int width)
> > +{
> > + struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
> > +
> > + i2s->tdm_slots = slots;
> > +
> > + i2s->slot_width = width;
> > +
> > + return 0;
> > +}
>
> No validation of the parameters here?
ACK
Thanks,
CK

2019-08-16 06:29:58

by Code Kipper

[permalink] [raw]
Subject: Re: [PATCH v5 02/15] ASoC: sun4i-i2s: Add set_tdm_slot functionality

On Wed, 14 Aug 2019 at 13:08, Maxime Ripard <[email protected]> wrote:
>
> Hi,
>
> On Wed, Aug 14, 2019 at 08:08:41AM +0200, [email protected] wrote:
> > 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(-)
> >
> > diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
> > index 8201334a059b..7c37b6291df0 100644
> > --- a/sound/soc/sunxi/sun4i-i2s.c
> > +++ b/sound/soc/sunxi/sun4i-i2s.c
> > @@ -195,6 +195,9 @@ struct sun4i_i2s {
> > struct regmap_field *field_rxchansel;
> >
> > const struct sun4i_i2s_quirks *variant;
> > +
> > + unsigned int tdm_slots;
> > + unsigned int slot_width;
> > };
> >
> > struct sun4i_i2s_clk_div {
> > @@ -346,7 +349,7 @@ static int sun4i_i2s_set_clk_rate(struct snd_soc_dai *dai,
> > if (i2s->variant->has_fmt_set_lrck_period)
> > regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT0_REG,
> > SUN8I_I2S_FMT0_LRCK_PERIOD_MASK,
> > - SUN8I_I2S_FMT0_LRCK_PERIOD(32));
> > + SUN8I_I2S_FMT0_LRCK_PERIOD(word_size));
> >
> >
> > /* Set sign extension to pad out LSB with 0 */
> > regmap_field_write(i2s->field_fmt_sext, 0);
> > @@ -450,7 +453,8 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
> > regmap_field_write(i2s->field_fmt_sr, sr);
> >
> > return sun4i_i2s_set_clk_rate(dai, params_rate(params),
> > - params_width(params));
> > + i2s->tdm_slots ?
> > + i2s->slot_width : params_width(params));
>
> This is slightly more complicated than that.

At this point we're only supporting 2 channels with fixed slot
settings. I've added a comment to state
that we're using the tdm_slot at the moment as an indicator to
override the slot width. Do you think
that is enough for now?.

Thanks,
CK
>
> On the H3 (and all related ones), the CHAN_CFG_TX_SLOT_NUM and
> _RX_SLOT_NUM fields in the CHAN_CFG register need to be set to the
> number of slots as well.
>
> Maxime
>
> --
> Maxime Ripard, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com

2019-08-21 04:11:09

by Chen-Yu Tsai

[permalink] [raw]
Subject: Re: [linux-sunxi] [PATCH v5 09/15] clk: sunxi-ng: h6: Allow I2S to change parent rate

On Wed, Aug 14, 2019 at 2:09 PM <[email protected]> wrote:
>
> 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.
>
> Signed-off-by: Jernej Skrabec <[email protected]>

This lacks your SoB. Please reply and I can add it when applying.

ChenYu

2019-08-21 06:00:42

by Code Kipper

[permalink] [raw]
Subject: 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

On Wed, 21 Aug 2019 at 06:07, Chen-Yu Tsai <[email protected]> wrote:
>
> On Wed, Aug 14, 2019 at 2:09 PM <[email protected]> wrote:
> >
> > 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.
> >
> > Signed-off-by: Jernej Skrabec <[email protected]>
>
> This lacks your SoB. Please reply and I can add it when applying.
>
> ChenYu

2019-08-21 07:01:44

by Chen-Yu Tsai

[permalink] [raw]
Subject: Re: [linux-sunxi] [PATCH v5 09/15] clk: sunxi-ng: h6: Allow I2S to change parent rate

On Wed, Aug 21, 2019 at 1:52 PM Code Kipper <[email protected]> wrote:
>
> Thanks....I've added to my next patch series but if you could add it
> when applying that would be great.

Please reply with an explicit SoB to put it on the record.

ChenYu

> BR,
> CK
>
> On Wed, 21 Aug 2019 at 06:07, Chen-Yu Tsai <[email protected]> wrote:
> >
> > On Wed, Aug 14, 2019 at 2:09 PM <[email protected]> wrote:
> > >
> > > 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.
> > >
> > > 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 [email protected].
> To view this discussion on the web, visit https://groups.google.com/d/msgid/linux-sunxi/CAEKpxBnxf%3Diejk887A7qFkzt3BXVxiRS1PeA45aZYR9DsBAU4Q%40mail.gmail.com.

2019-08-21 09:21:56

by Code Kipper

[permalink] [raw]
Subject: Re: [PATCH v5 09/15] clk: sunxi-ng: h6: Allow I2S to change parent rate

On Wed, 14 Aug 2019 at 08:09, <[email protected]> wrote:
>
> 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.
>
> Signed-off-by: Jernej Skrabec <[email protected]>

Signed-off-by: Marcus Cooper <[email protected]>

> ---
> drivers/clk/sunxi-ng/ccu-sun50i-h6.c | 8 ++++----
> 1 file changed, 4 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-h6.c b/drivers/clk/sunxi-ng/ccu-sun50i-h6.c
> index aebef4af9861..d89353a3cdec 100644
> --- a/drivers/clk/sunxi-ng/ccu-sun50i-h6.c
> +++ b/drivers/clk/sunxi-ng/ccu-sun50i-h6.c
> @@ -505,7 +505,7 @@ static struct ccu_div i2s3_clk = {
> .hw.init = CLK_HW_INIT_PARENTS("i2s3",
> audio_parents,
> &ccu_div_ops,
> - 0),
> + CLK_SET_RATE_PARENT),
> },
> };
>
> @@ -518,7 +518,7 @@ static struct ccu_div i2s0_clk = {
> .hw.init = CLK_HW_INIT_PARENTS("i2s0",
> audio_parents,
> &ccu_div_ops,
> - 0),
> + CLK_SET_RATE_PARENT),
> },
> };
>
> @@ -531,7 +531,7 @@ static struct ccu_div i2s1_clk = {
> .hw.init = CLK_HW_INIT_PARENTS("i2s1",
> audio_parents,
> &ccu_div_ops,
> - 0),
> + CLK_SET_RATE_PARENT),
> },
> };
>
> @@ -544,7 +544,7 @@ static struct ccu_div i2s2_clk = {
> .hw.init = CLK_HW_INIT_PARENTS("i2s2",
> audio_parents,
> &ccu_div_ops,
> - 0),
> + CLK_SET_RATE_PARENT),
> },
> };
>
> --
> 2.22.0
>

2019-08-21 10:29:23

by Chen-Yu Tsai

[permalink] [raw]
Subject: Re: [linux-sunxi] Re: [PATCH v5 09/15] clk: sunxi-ng: h6: Allow I2S to change parent rate

On Wed, Aug 21, 2019 at 5:19 PM Code Kipper <[email protected]> wrote:
>
> On Wed, 14 Aug 2019 at 08:09, <[email protected]> wrote:
> >
> > 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.
> >
> > Signed-off-by: Jernej Skrabec <[email protected]>
>
> Signed-off-by: Marcus Cooper <[email protected]>

Applied for 5.4