summarylogtreecommitdiffstats
path: root/general-meson-aiu-Fix-HDMI-codec-control-selection.patch
blob: e391cc30848af7f9665e63dfe94c6159caad13e8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
From 712edc341073c350a11658186609eafd292dbe8a Mon Sep 17 00:00:00 2001
From: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Date: Sun, 3 Oct 2021 05:35:48 +0000
Subject: [PATCH 27/90] FROMLIST(v1): ASoC: meson: aiu: Fix HDMI codec control
 selection

The HDMI controllers on Amlogic Meson SoCs which use the AIU
audio-controller have two different audio format inputs:
- I2S which is also the only configuration supported on GXBB, GXL and
  GXM SoCs since there's no SPDIF support in the DesignWare HDMI
  controller driver (at the time of writing this)
- SPDIF can be used optionally, including pass-through formats

Switching between these requires us to set different registers:
AIU_HDMI_CLK_DATA_CTRL[1:0] "HDMI_DATA_CLK_SEL":
- 0x0 disables the HDMI output clock
- 0x1 selects the PCM clock
- 0x2 selects the AIU clock
- 0x3 is reserved

AIU_HDMI_CLK_DATA_CTRL[5:4] "HDMI_DATA_SEL":
- 0x0 outputs constant zero, disables HDMI data
- 0x1 selects PCM data
- 0x2 selects AIU I2S data
- 0x3 is reserved

AIU_CLK_CTRL_MORE[6] "HDMITX_SEL_AOCLKX2":
- 0x0 selects cts_i958 as AIU clk to hdmi_tx_audio_master_clk
- 0x1 selects cts_aoclkx2_int as AIU clk to hdmi_tx_audio_master_clk

The Meson8/8b/8m2 vendor driver uses the following settings:
SPDIF output to the HDMI controller:
- 0x2 (AIU clock) in AIU_HDMI_CLK_DATA_CTRL[1:0]
- 0x0 (no HDMI data) in AIU_HDMI_CLK_DATA_CTRL[5:4]
- 0x0 (using cts_i958 as AIU clk) in AIU_CLK_CTRL_MORE[6]
I2S output to the HDMI controller:
- 0x2 (AIU clock) in AIU_HDMI_CLK_DATA_CTRL[1:0]
- 0x2 (I2S data) in AIU_HDMI_CLK_DATA_CTRL[5:4]
- 0x0 (using cts_aoclkx2_int as AIU clk) in AIU_CLK_CTRL_MORE[6]

The GXBB/GXL/GXM vendor driver uses the following settings:
SPDIF output to the HDMI controller:
- not setting AIU_HDMI_CLK_DATA_CTRL at all
- 0x0 (using cts_i958 as AIU clk) in AIU_CLK_CTRL_MORE[6]
I2S output to the HDMI controller:
- 0x2 (AIU clock) in AIU_HDMI_CLK_DATA_CTRL[1:0]
- 0x2 (I2S data) in AIU_HDMI_CLK_DATA_CTRL[5:4]
- 0x0 (using cts_aoclkx2_int as AIU clk) in AIU_CLK_CTRL_MORE[6]

Set the three registers at the same time following what the vendor
driver does on Meson8/8b/8m2 SoCs. This makes the SPDIF output to the
HDMI controller work. The entries and order of the entries in the enum
is not changed on purpose to not break old configurations.

Fixes: b82b734c0e9a7 ("ASoC: meson: aiu: add hdmi codec control support")
Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
---
 sound/soc/meson/aiu-codec-ctrl.c  | 108 ++++++++++++++++++++++--------
 sound/soc/meson/aiu-encoder-i2s.c |   6 --
 2 files changed, 80 insertions(+), 34 deletions(-)

diff --git a/sound/soc/meson/aiu-codec-ctrl.c b/sound/soc/meson/aiu-codec-ctrl.c
index c3ea733fce91..2b8575491aeb 100644
--- a/sound/soc/meson/aiu-codec-ctrl.c
+++ b/sound/soc/meson/aiu-codec-ctrl.c
@@ -12,14 +12,60 @@
 #include "aiu.h"
 #include "meson-codec-glue.h"
 
-#define CTRL_CLK_SEL		GENMASK(1, 0)
-#define CTRL_DATA_SEL_SHIFT	4
-#define CTRL_DATA_SEL		(0x3 << CTRL_DATA_SEL_SHIFT)
-
-static const char * const aiu_codec_ctrl_mux_texts[] = {
-	"DISABLED", "PCM", "I2S",
+#define AIU_HDMI_CLK_DATA_CTRL_CLK_SEL			GENMASK(1, 0)
+#define AIU_HDMI_CLK_DATA_CTRL_CLK_SEL_DISABLE		0x0
+#define AIU_HDMI_CLK_DATA_CTRL_CLK_SEL_PCM		0x1
+#define AIU_HDMI_CLK_DATA_CTRL_CLK_SEL_AIU		0x2
+#define AIU_HDMI_CLK_DATA_CTRL_DATA_SEL			GENMASK(5, 4)
+#define AIU_HDMI_CLK_DATA_CTRL_DATA_SEL_OUTPUT_ZERO	0x0
+#define AIU_HDMI_CLK_DATA_CTRL_DATA_SEL_PCM_DATA	0x1
+#define AIU_HDMI_CLK_DATA_CTRL_DATA_SEL_I2S_DATA	0x2
+
+#define AIU_CLK_CTRL_MORE_AMCLK				BIT(6)
+
+#define AIU_HDMI_CTRL_MUX_DISABLED			0
+#define AIU_HDMI_CTRL_MUX_PCM				1
+#define AIU_HDMI_CTRL_MUX_I2S				2
+
+static const char * const aiu_codec_hdmi_ctrl_mux_texts[] = {
+	[AIU_HDMI_CTRL_MUX_DISABLED] =  "DISABLED",
+	[AIU_HDMI_CTRL_MUX_PCM] = "PCM",
+	[AIU_HDMI_CTRL_MUX_I2S] = "I2S",
 };
 
+static int aiu_codec_ctrl_mux_get_enum(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component =
+		snd_soc_dapm_kcontrol_component(kcontrol);
+	unsigned int ctrl, more, mux = AIU_HDMI_CTRL_MUX_DISABLED;
+
+	ctrl = snd_soc_component_read(component, AIU_HDMI_CLK_DATA_CTRL);
+	if (FIELD_GET(AIU_HDMI_CLK_DATA_CTRL_CLK_SEL, ctrl) !=
+	    AIU_HDMI_CLK_DATA_CTRL_CLK_SEL_AIU) {
+		goto out;
+	}
+
+	more = snd_soc_component_read(component, AIU_CLK_CTRL_MORE);
+	if (FIELD_GET(AIU_HDMI_CLK_DATA_CTRL_DATA_SEL, ctrl) ==
+	    AIU_HDMI_CLK_DATA_CTRL_DATA_SEL_I2S_DATA &&
+	    !!(more & AIU_CLK_CTRL_MORE_AMCLK)) {
+		mux = AIU_HDMI_CTRL_MUX_I2S;
+		goto out;
+	}
+
+	if (FIELD_GET(AIU_HDMI_CLK_DATA_CTRL_DATA_SEL, ctrl) ==
+	    AIU_HDMI_CLK_DATA_CTRL_DATA_SEL_OUTPUT_ZERO &&
+	    !(more & AIU_CLK_CTRL_MORE_AMCLK)) {
+		mux = AIU_HDMI_CTRL_MUX_PCM;
+		goto out;
+	}
+
+out:
+	ucontrol->value.enumerated.item[0] = mux;
+	return 0;
+}
+
 static int aiu_codec_ctrl_mux_put_enum(struct snd_kcontrol *kcontrol,
 				       struct snd_ctl_elem_value *ucontrol)
 {
@@ -28,45 +74,51 @@ static int aiu_codec_ctrl_mux_put_enum(struct snd_kcontrol *kcontrol,
 	struct snd_soc_dapm_context *dapm =
 		snd_soc_dapm_kcontrol_dapm(kcontrol);
 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-	unsigned int mux, changed;
+	unsigned int mux, ctrl, more;
 
 	mux = snd_soc_enum_item_to_val(e, ucontrol->value.enumerated.item[0]);
-	changed = snd_soc_component_test_bits(component, e->reg,
-					      CTRL_DATA_SEL,
-					      FIELD_PREP(CTRL_DATA_SEL, mux));
 
-	if (!changed)
-		return 0;
+	if (mux == AIU_HDMI_CTRL_MUX_I2S) {
+		ctrl = FIELD_PREP(AIU_HDMI_CLK_DATA_CTRL_DATA_SEL,
+				  AIU_HDMI_CLK_DATA_CTRL_DATA_SEL_I2S_DATA);
+		more = AIU_CLK_CTRL_MORE_AMCLK;
+	} else {
+		ctrl = FIELD_PREP(AIU_HDMI_CLK_DATA_CTRL_DATA_SEL,
+				  AIU_HDMI_CLK_DATA_CTRL_DATA_SEL_OUTPUT_ZERO);
+		more = 0;
+	}
+
+	if (mux == AIU_HDMI_CTRL_MUX_DISABLED) {
+		ctrl |= FIELD_PREP(AIU_HDMI_CLK_DATA_CTRL_CLK_SEL,
+				   AIU_HDMI_CLK_DATA_CTRL_CLK_SEL_DISABLE);
+	} else {
+		ctrl |= FIELD_PREP(AIU_HDMI_CLK_DATA_CTRL_CLK_SEL,
+				   AIU_HDMI_CLK_DATA_CTRL_CLK_SEL_AIU);
+	}
 
 	/* Force disconnect of the mux while updating */
 	snd_soc_dapm_mux_update_power(dapm, kcontrol, 0, NULL, NULL);
 
-	/* Reset the source first */
-	snd_soc_component_update_bits(component, e->reg,
-				      CTRL_CLK_SEL |
-				      CTRL_DATA_SEL,
-				      FIELD_PREP(CTRL_CLK_SEL, 0) |
-				      FIELD_PREP(CTRL_DATA_SEL, 0));
+	snd_soc_component_update_bits(component, AIU_HDMI_CLK_DATA_CTRL,
+				      AIU_HDMI_CLK_DATA_CTRL_CLK_SEL |
+				      AIU_HDMI_CLK_DATA_CTRL_DATA_SEL,
+				      ctrl);
 
-	/* Set the appropriate source */
-	snd_soc_component_update_bits(component, e->reg,
-				      CTRL_CLK_SEL |
-				      CTRL_DATA_SEL,
-				      FIELD_PREP(CTRL_CLK_SEL, mux) |
-				      FIELD_PREP(CTRL_DATA_SEL, mux));
+	snd_soc_component_update_bits(component, AIU_CLK_CTRL_MORE,
+				      AIU_CLK_CTRL_MORE_AMCLK,
+				      more);
 
 	snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL);
 
	return 1;
 }
 
-static SOC_ENUM_SINGLE_DECL(aiu_hdmi_ctrl_mux_enum, AIU_HDMI_CLK_DATA_CTRL,
-			    CTRL_DATA_SEL_SHIFT,
-			    aiu_codec_ctrl_mux_texts);
+static SOC_ENUM_SINGLE_VIRT_DECL(aiu_hdmi_ctrl_mux_enum,
+				 aiu_codec_hdmi_ctrl_mux_texts);
 
 static const struct snd_kcontrol_new aiu_hdmi_ctrl_mux =
 	SOC_DAPM_ENUM_EXT("HDMI Source", aiu_hdmi_ctrl_mux_enum,
-			  snd_soc_dapm_get_enum_double,
+			  aiu_codec_ctrl_mux_get_enum,
 			  aiu_codec_ctrl_mux_put_enum);
 
 static const struct snd_soc_dapm_widget aiu_hdmi_ctrl_widgets[] = {
diff --git a/sound/soc/meson/aiu-encoder-i2s.c b/sound/soc/meson/aiu-encoder-i2s.c
index 67729de41a73..88637deb2d7a 100644
--- a/sound/soc/meson/aiu-encoder-i2s.c
+++ b/sound/soc/meson/aiu-encoder-i2s.c
@@ -23,7 +23,6 @@
 #define AIU_CLK_CTRL_AOCLK_INVERT	BIT(6)
 #define AIU_CLK_CTRL_LRCLK_INVERT	BIT(7)
 #define AIU_CLK_CTRL_LRCLK_SKEW		GENMASK(9, 8)
-#define AIU_CLK_CTRL_MORE_HDMI_AMCLK	BIT(6)
 #define AIU_CLK_CTRL_MORE_I2S_DIV	GENMASK(5, 0)
 #define AIU_CODEC_DAC_LRCLK_CTRL_DIV	GENMASK(11, 0)
 
@@ -176,11 +175,6 @@ static int aiu_encoder_i2s_set_clocks(struct snd_soc_component *component,
 	if (ret)
 		return ret;
 
-	/* Make sure amclk is used for HDMI i2s as well */
-	snd_soc_component_update_bits(component, AIU_CLK_CTRL_MORE,
-				      AIU_CLK_CTRL_MORE_HDMI_AMCLK,
-				      AIU_CLK_CTRL_MORE_HDMI_AMCLK);
-
 	return 0;
 }
 
-- 
2.35.1