2024-05-29 14:13:17

by Matteo Martelli

[permalink] [raw]
Subject: [PATCH 0/1] ASoC: sunxi: sun4i-i2s: fix LRCLK polarity in i2s mode

I found an issue on the sunxi i2s controller driver while doing some
tests with a Pine64 A64 host board and an external codec (ES8311).
The A64 i2s controller is compatible with the sun8i-h3-i2s driver.
The LRCLK was being inverted when the bus was operated in i2s mode:
normally should be left channel on low LRCLK and right channel on high
LRCLK, but it was the opposite instead.
I noticed this issue due to the playback being addressed on the wrong
codec channel, then confirmed by analyzing the clock signal with a logic
analyzer.

Note that this fix applies for all the i2s controllers compatible with
sun8i-h3-i2s and sun50i-h6-i2s, but I could test it only for the A64.

The issue had likely been introduced in commit dd657eae8164 ("ASoC:
sun4i-i2s: Fix the LRCK polarity") due to a misinterpreted bit in the H3
or H6 User Manual. I suppose that the i2s mode had not been tested at
that time. Can this be confirmed? Otherwise there is something else
going on and this patch should not be applied as is.

Matteo Martelli (1):
ASoC: sunxi: sun4i-i2s: fix LRCLK polarity in i2s mode

sound/soc/sunxi/sun4i-i2s.c | 143 ++++++++++++++++++------------------
1 file changed, 73 insertions(+), 70 deletions(-)


base-commit: 47d09270d7776e46858a161f94b735640b2fb056
--
2.45.1



2024-05-29 14:13:34

by Matteo Martelli

[permalink] [raw]
Subject: [PATCH 1/1] ASoC: sunxi: sun4i-i2s: fix LRCLK polarity in i2s mode

This fixes the LRCLK polarity for sun8i-h3 and sun50i-h6 in i2s mode
which was wrongly inverted.

The LRCLK was being set in reversed logic compared to the DAI format:
inverted LRCLK for SND_SOC_DAIFMT_IB_NF and SND_SOC_DAIFMT_NB_NF; normal
LRCLK for SND_SOC_DAIFMT_IB_IF and SND_SOC_DAIFMT_NB_IF. Such reversed
logic applies properly for DSP_A, DSP_B, LEFT_J and RIGHT_J modes but
not for I2S mode, for which the LRCLK signal results reversed to what
expected on the bus. The issue is due to a misinterpretation of the
LRCLK polarity bit of the H3 and H6 i2s controllers. Such bit in this
case does not mean "0 => normal" or "1 => inverted" according to the
expected bus operation, but it means "0 => frame starts on low edge" and
"1 => frame starts on high edge" (from the User Manuals).

This commit fixes the LRCLK polarity by setting the LRCLK polarity bit
according to the selected bus mode and renames the LRCLK polarity bit
definition to avoid further confusion.

Fixes: dd657eae8164 ("ASoC: sun4i-i2s: Fix the LRCK polarity")
Fixes: 73adf87b7a58 ("ASoC: sun4i-i2s: Add support for H6 I2S")
Signed-off-by: Matteo Martelli <[email protected]>
---
sound/soc/sunxi/sun4i-i2s.c | 143 ++++++++++++++++++------------------
1 file changed, 73 insertions(+), 70 deletions(-)

diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
index 5f8d979585b6..a200ba41679a 100644
--- a/sound/soc/sunxi/sun4i-i2s.c
+++ b/sound/soc/sunxi/sun4i-i2s.c
@@ -100,8 +100,8 @@
#define SUN8I_I2S_CTRL_MODE_PCM (0 << 4)

#define SUN8I_I2S_FMT0_LRCLK_POLARITY_MASK BIT(19)
-#define SUN8I_I2S_FMT0_LRCLK_POLARITY_INVERTED (1 << 19)
-#define SUN8I_I2S_FMT0_LRCLK_POLARITY_NORMAL (0 << 19)
+#define SUN8I_I2S_FMT0_LRCLK_POLARITY_START_HIGH (1 << 19)
+#define SUN8I_I2S_FMT0_LRCLK_POLARITY_START_LOW (0 << 19)
#define SUN8I_I2S_FMT0_LRCK_PERIOD_MASK GENMASK(17, 8)
#define SUN8I_I2S_FMT0_LRCK_PERIOD(period) ((period - 1) << 8)
#define SUN8I_I2S_FMT0_BCLK_POLARITY_MASK BIT(7)
@@ -729,65 +729,37 @@ static int sun4i_i2s_set_soc_fmt(const struct sun4i_i2s *i2s,
static int sun8i_i2s_set_soc_fmt(const struct sun4i_i2s *i2s,
unsigned int fmt)
{
- u32 mode, val;
+ u32 mode, lrclk_pol, bclk_pol, val;
u8 offset;

- /*
- * DAI clock polarity
- *
- * The setup for LRCK contradicts the datasheet, but under a
- * scope it's clear that the LRCK polarity is reversed
- * compared to the expected polarity on the bus.
- */
- switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
- case SND_SOC_DAIFMT_IB_IF:
- /* Invert both clocks */
- val = SUN8I_I2S_FMT0_BCLK_POLARITY_INVERTED;
- break;
- case SND_SOC_DAIFMT_IB_NF:
- /* Invert bit clock */
- val = SUN8I_I2S_FMT0_BCLK_POLARITY_INVERTED |
- SUN8I_I2S_FMT0_LRCLK_POLARITY_INVERTED;
- break;
- case SND_SOC_DAIFMT_NB_IF:
- /* Invert frame clock */
- val = 0;
- break;
- case SND_SOC_DAIFMT_NB_NF:
- val = SUN8I_I2S_FMT0_LRCLK_POLARITY_INVERTED;
- break;
- default:
- return -EINVAL;
- }
-
- regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT0_REG,
- SUN8I_I2S_FMT0_LRCLK_POLARITY_MASK |
- SUN8I_I2S_FMT0_BCLK_POLARITY_MASK,
- val);
-
/* DAI Mode */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_DSP_A:
+ lrclk_pol = SUN8I_I2S_FMT0_LRCLK_POLARITY_START_HIGH;
mode = SUN8I_I2S_CTRL_MODE_PCM;
offset = 1;
break;

case SND_SOC_DAIFMT_DSP_B:
+ lrclk_pol = SUN8I_I2S_FMT0_LRCLK_POLARITY_START_HIGH;
mode = SUN8I_I2S_CTRL_MODE_PCM;
offset = 0;
break;

case SND_SOC_DAIFMT_I2S:
+ lrclk_pol = SUN8I_I2S_FMT0_LRCLK_POLARITY_START_LOW;
mode = SUN8I_I2S_CTRL_MODE_LEFT;
offset = 1;
break;

case SND_SOC_DAIFMT_LEFT_J:
+ lrclk_pol = SUN8I_I2S_FMT0_LRCLK_POLARITY_START_HIGH;
mode = SUN8I_I2S_CTRL_MODE_LEFT;
offset = 0;
break;

case SND_SOC_DAIFMT_RIGHT_J:
+ lrclk_pol = SUN8I_I2S_FMT0_LRCLK_POLARITY_START_HIGH;
mode = SUN8I_I2S_CTRL_MODE_RIGHT;
offset = 0;
break;
@@ -805,6 +777,35 @@ static int sun8i_i2s_set_soc_fmt(const struct sun4i_i2s *i2s,
SUN8I_I2S_TX_CHAN_OFFSET_MASK,
SUN8I_I2S_TX_CHAN_OFFSET(offset));

+ /* DAI polarity */
+ bclk_pol = SUN8I_I2S_FMT0_BCLK_POLARITY_NORMAL;
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_IF:
+ /* Invert both clocks */
+ lrclk_pol ^= SUN8I_I2S_FMT0_BCLK_POLARITY_MASK;
+ bclk_pol = SUN8I_I2S_FMT0_BCLK_POLARITY_INVERTED;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ /* Invert bit clock */
+ bclk_pol = SUN8I_I2S_FMT0_BCLK_POLARITY_INVERTED;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ /* Invert frame clock */
+ lrclk_pol ^= SUN8I_I2S_FMT0_BCLK_POLARITY_MASK;
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ /* No inversion */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT0_REG,
+ SUN8I_I2S_FMT0_LRCLK_POLARITY_MASK |
+ SUN8I_I2S_FMT0_BCLK_POLARITY_MASK,
+ lrclk_pol | bclk_pol);
+
/* DAI clock master masks */
switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
case SND_SOC_DAIFMT_BP_FP:
@@ -836,65 +837,37 @@ static int sun8i_i2s_set_soc_fmt(const struct sun4i_i2s *i2s,
static int sun50i_h6_i2s_set_soc_fmt(const struct sun4i_i2s *i2s,
unsigned int fmt)
{
- u32 mode, val;
+ u32 mode, lrclk_pol, bclk_pol, val;
u8 offset;

- /*
- * DAI clock polarity
- *
- * The setup for LRCK contradicts the datasheet, but under a
- * scope it's clear that the LRCK polarity is reversed
- * compared to the expected polarity on the bus.
- */
- switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
- case SND_SOC_DAIFMT_IB_IF:
- /* Invert both clocks */
- val = SUN8I_I2S_FMT0_BCLK_POLARITY_INVERTED;
- break;
- case SND_SOC_DAIFMT_IB_NF:
- /* Invert bit clock */
- val = SUN8I_I2S_FMT0_BCLK_POLARITY_INVERTED |
- SUN8I_I2S_FMT0_LRCLK_POLARITY_INVERTED;
- break;
- case SND_SOC_DAIFMT_NB_IF:
- /* Invert frame clock */
- val = 0;
- break;
- case SND_SOC_DAIFMT_NB_NF:
- val = SUN8I_I2S_FMT0_LRCLK_POLARITY_INVERTED;
- break;
- default:
- return -EINVAL;
- }
-
- regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT0_REG,
- SUN8I_I2S_FMT0_LRCLK_POLARITY_MASK |
- SUN8I_I2S_FMT0_BCLK_POLARITY_MASK,
- val);
-
/* DAI Mode */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_DSP_A:
+ lrclk_pol = SUN8I_I2S_FMT0_LRCLK_POLARITY_START_HIGH;
mode = SUN8I_I2S_CTRL_MODE_PCM;
offset = 1;
break;

case SND_SOC_DAIFMT_DSP_B:
+ lrclk_pol = SUN8I_I2S_FMT0_LRCLK_POLARITY_START_HIGH;
mode = SUN8I_I2S_CTRL_MODE_PCM;
offset = 0;
break;

case SND_SOC_DAIFMT_I2S:
+ lrclk_pol = SUN8I_I2S_FMT0_LRCLK_POLARITY_START_LOW;
mode = SUN8I_I2S_CTRL_MODE_LEFT;
offset = 1;
break;

case SND_SOC_DAIFMT_LEFT_J:
+ lrclk_pol = SUN8I_I2S_FMT0_LRCLK_POLARITY_START_HIGH;
mode = SUN8I_I2S_CTRL_MODE_LEFT;
offset = 0;
break;

case SND_SOC_DAIFMT_RIGHT_J:
+ lrclk_pol = SUN8I_I2S_FMT0_LRCLK_POLARITY_START_HIGH;
mode = SUN8I_I2S_CTRL_MODE_RIGHT;
offset = 0;
break;
@@ -912,6 +885,36 @@ static int sun50i_h6_i2s_set_soc_fmt(const struct sun4i_i2s *i2s,
SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET_MASK,
SUN50I_H6_I2S_TX_CHAN_SEL_OFFSET(offset));

+ /* DAI clock polarity */
+ bclk_pol = SUN8I_I2S_FMT0_BCLK_POLARITY_NORMAL;
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_IF:
+ /* Invert both clocks */
+ lrclk_pol ^= SUN8I_I2S_FMT0_BCLK_POLARITY_MASK;
+ bclk_pol = SUN8I_I2S_FMT0_BCLK_POLARITY_INVERTED;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ /* Invert bit clock */
+ bclk_pol = SUN8I_I2S_FMT0_BCLK_POLARITY_INVERTED;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ /* Invert frame clock */
+ lrclk_pol ^= SUN8I_I2S_FMT0_BCLK_POLARITY_MASK;
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ /* No inversion */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT0_REG,
+ SUN8I_I2S_FMT0_LRCLK_POLARITY_MASK |
+ SUN8I_I2S_FMT0_BCLK_POLARITY_MASK,
+ lrclk_pol | bclk_pol);
+
+
/* DAI clock master masks */
switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
case SND_SOC_DAIFMT_BP_FP:
--
2.45.1


2024-05-29 14:14:58

by Mark Brown

[permalink] [raw]
Subject: Re: [PATCH 0/1] ASoC: sunxi: sun4i-i2s: fix LRCLK polarity in i2s mode

On Wed, May 29, 2024 at 04:00:14PM +0200, Matteo Martelli wrote:

> I found an issue on the sunxi i2s controller driver while doing some
> tests with a Pine64 A64 host board and an external codec (ES8311).
> The A64 i2s controller is compatible with the sun8i-h3-i2s driver.
> The LRCLK was being inverted when the bus was operated in i2s mode:
> normally should be left channel on low LRCLK and right channel on high
> LRCLK, but it was the opposite instead.

Please don't send cover letters for single patches, if there is anything
that needs saying put it in the changelog of the patch or after the ---
if it's administrative stuff. This reduces mail volume and ensures that
any important information is recorded in the changelog rather than being
lost.


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

2024-05-29 14:20:04

by Matteo Martelli

[permalink] [raw]
Subject: Re: [PATCH 0/1] ASoC: sunxi: sun4i-i2s: fix LRCLK polarity in i2s mode

Mark Brown wrote:
> On Wed, May 29, 2024 at 04:00:14PM +0200, Matteo Martelli wrote:
>
> > I found an issue on the sunxi i2s controller driver while doing some
> > tests with a Pine64 A64 host board and an external codec (ES8311).
> > The A64 i2s controller is compatible with the sun8i-h3-i2s driver.
> > The LRCLK was being inverted when the bus was operated in i2s mode:
> > normally should be left channel on low LRCLK and right channel on high
> > LRCLK, but it was the opposite instead.
>
> Please don't send cover letters for single patches, if there is anything
> that needs saying put it in the changelog of the patch or after the ---
> if it's administrative stuff. This reduces mail volume and ensures that
> any important information is recorded in the changelog rather than being
> lost.

All right, sorry for that. Should I resend this patch without cover letter?

2024-05-29 14:23:40

by Mark Brown

[permalink] [raw]
Subject: Re: [PATCH 0/1] ASoC: sunxi: sun4i-i2s: fix LRCLK polarity in i2s mode

On Wed, May 29, 2024 at 04:19:53PM +0200, Matteo Martelli wrote:

> All right, sorry for that. Should I resend this patch without cover letter?

No.


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

2024-06-06 16:14:29

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH 1/1] ASoC: sunxi: sun4i-i2s: fix LRCLK polarity in i2s mode

Hi,

On Wed, May 29, 2024 at 04:00:15PM GMT, Matteo Martelli wrote:
> This fixes the LRCLK polarity for sun8i-h3 and sun50i-h6 in i2s mode
> which was wrongly inverted.
>
> The LRCLK was being set in reversed logic compared to the DAI format:
> inverted LRCLK for SND_SOC_DAIFMT_IB_NF and SND_SOC_DAIFMT_NB_NF; normal
> LRCLK for SND_SOC_DAIFMT_IB_IF and SND_SOC_DAIFMT_NB_IF. Such reversed
> logic applies properly for DSP_A, DSP_B, LEFT_J and RIGHT_J modes but
> not for I2S mode, for which the LRCLK signal results reversed to what
> expected on the bus. The issue is due to a misinterpretation of the
> LRCLK polarity bit of the H3 and H6 i2s controllers. Such bit in this
> case does not mean "0 => normal" or "1 => inverted" according to the
> expected bus operation, but it means "0 => frame starts on low edge" and
> "1 => frame starts on high edge" (from the User Manuals).
>
> This commit fixes the LRCLK polarity by setting the LRCLK polarity bit
> according to the selected bus mode and renames the LRCLK polarity bit
> definition to avoid further confusion.
>
> Fixes: dd657eae8164 ("ASoC: sun4i-i2s: Fix the LRCK polarity")
> Fixes: 73adf87b7a58 ("ASoC: sun4i-i2s: Add support for H6 I2S")
> Signed-off-by: Matteo Martelli <[email protected]>
> ---
> sound/soc/sunxi/sun4i-i2s.c | 143 ++++++++++++++++++------------------
> 1 file changed, 73 insertions(+), 70 deletions(-)
>
> diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
> index 5f8d979585b6..a200ba41679a 100644
> --- a/sound/soc/sunxi/sun4i-i2s.c
> +++ b/sound/soc/sunxi/sun4i-i2s.c
> @@ -100,8 +100,8 @@
> #define SUN8I_I2S_CTRL_MODE_PCM (0 << 4)
>
> #define SUN8I_I2S_FMT0_LRCLK_POLARITY_MASK BIT(19)
> -#define SUN8I_I2S_FMT0_LRCLK_POLARITY_INVERTED (1 << 19)
> -#define SUN8I_I2S_FMT0_LRCLK_POLARITY_NORMAL (0 << 19)
> +#define SUN8I_I2S_FMT0_LRCLK_POLARITY_START_HIGH (1 << 19)
> +#define SUN8I_I2S_FMT0_LRCLK_POLARITY_START_LOW (0 << 19)
> #define SUN8I_I2S_FMT0_LRCK_PERIOD_MASK GENMASK(17, 8)
> #define SUN8I_I2S_FMT0_LRCK_PERIOD(period) ((period - 1) << 8)
> #define SUN8I_I2S_FMT0_BCLK_POLARITY_MASK BIT(7)
> @@ -729,65 +729,37 @@ static int sun4i_i2s_set_soc_fmt(const struct sun4i_i2s *i2s,
> static int sun8i_i2s_set_soc_fmt(const struct sun4i_i2s *i2s,
> unsigned int fmt)
> {
> - u32 mode, val;
> + u32 mode, lrclk_pol, bclk_pol, val;
> u8 offset;
>
> - /*
> - * DAI clock polarity
> - *
> - * The setup for LRCK contradicts the datasheet, but under a
> - * scope it's clear that the LRCK polarity is reversed
> - * compared to the expected polarity on the bus.
> - */

I think we should keep that comment somewhere.

Maxime


Attachments:
(No filename) (2.68 kB)
signature.asc (281.00 B)
Download all attachments

2024-06-07 08:22:03

by Matteo Martelli

[permalink] [raw]
Subject: Re: [PATCH 1/1] ASoC: sunxi: sun4i-i2s: fix LRCLK polarity in i2s mode

Maxime Ripard wrote:
> > - /*
> > - * DAI clock polarity
> > - *
> > - * The setup for LRCK contradicts the datasheet, but under a
> > - * scope it's clear that the LRCK polarity is reversed
> > - * compared to the expected polarity on the bus.
> > - */
>
> I think we should keep that comment somewhere.

I think that keeping that comment would be very misleading since the LRCLK
setup would not contradict the datasheet anymore [1][2].

Also, do you recall any details about the mentioned scope test setup? Was i2s
mode tested in that occasion? It would help clarify the situation.

Could anyone verify this patch against H3/H6 SoCs?

[1]: https://linux-sunxi.org/images/4/4b/Allwinner_H3_Datasheet_V1.2.pdf
section 8.6.7.2
[2]: https://linux-sunxi.org/images/4/46/Allwinner_H6_V200_User_Manual_V1.1.pdf
section 7.2.5.2

Thanks,
Matteo Martelli