firmware-utils: bump to git HEAD
[openwrt/openwrt.git] / target / linux / bcm27xx / patches-5.4 / 950-0920-vc4-Report-channel-mapping-back-to-userspace.patch
1 From 058328afcdd3d5c9531cfac8b980d0c0db75856f Mon Sep 17 00:00:00 2001
2 From: popcornmix <popcornmix@gmail.com>
3 Date: Mon, 20 Apr 2020 18:00:38 +0100
4 Subject: [PATCH] vc4: Report channel mapping back to userspace
5
6 This follows logic in hdmi-codec.c to use speaker layout
7 from ELD to choose a suitable speaker mapping based on
8 number of channels requested and signal that in audio
9 infoframe and report this back to userspace.
10
11 This allows apps like speaker-test and kodi to get the
12 output to the right speakers.
13
14 Signed-off-by: Dom Cobley <popcornmix@gmail.com>
15 ---
16 drivers/gpu/drm/vc4/vc4_hdmi.c | 415 +++++++++++++++++++++++++++++++++
17 drivers/gpu/drm/vc4/vc4_hdmi.h | 3 +
18 2 files changed, 418 insertions(+)
19
20 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c
21 +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
22 @@ -48,6 +48,7 @@
23 #include <sound/pcm_drm_eld.h>
24 #include <sound/pcm_params.h>
25 #include <sound/soc.h>
26 +#include <sound/tlv.h>
27 #include "media/cec.h"
28 #include "vc4_drv.h"
29 #include "vc4_hdmi.h"
30 @@ -82,6 +83,311 @@
31 #define CEC_CLOCK_FREQ 40000
32 #define VC4_HSM_CLOCK 163682864
33
34 +#define HDMI_CODEC_CHMAP_IDX_UNKNOWN -1
35 +
36 +/*
37 + * CEA speaker placement for HDMI 1.4:
38 + *
39 + * FL FLC FC FRC FR FRW
40 + *
41 + * LFE
42 + *
43 + * RL RLC RC RRC RR
44 + *
45 + * Speaker placement has to be extended to support HDMI 2.0
46 + */
47 +enum hdmi_codec_cea_spk_placement {
48 + FL = BIT(0), /* Front Left */
49 + FC = BIT(1), /* Front Center */
50 + FR = BIT(2), /* Front Right */
51 + FLC = BIT(3), /* Front Left Center */
52 + FRC = BIT(4), /* Front Right Center */
53 + RL = BIT(5), /* Rear Left */
54 + RC = BIT(6), /* Rear Center */
55 + RR = BIT(7), /* Rear Right */
56 + RLC = BIT(8), /* Rear Left Center */
57 + RRC = BIT(9), /* Rear Right Center */
58 + LFE = BIT(10), /* Low Frequency Effect */
59 +};
60 +
61 +/*
62 + * cea Speaker allocation structure
63 + */
64 +struct hdmi_codec_cea_spk_alloc {
65 + const int ca_id;
66 + unsigned int n_ch;
67 + unsigned long mask;
68 +};
69 +
70 +/* Channel maps stereo HDMI */
71 +static const struct snd_pcm_chmap_elem hdmi_codec_stereo_chmaps[] = {
72 + { .channels = 2,
73 + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
74 + { }
75 +};
76 +
77 +/* Channel maps for multi-channel playbacks, up to 8 n_ch */
78 +static const struct snd_pcm_chmap_elem hdmi_codec_8ch_chmaps[] = {
79 + { .channels = 2, /* CA_ID 0x00 */
80 + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
81 + { .channels = 4, /* CA_ID 0x01 */
82 + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
83 + SNDRV_CHMAP_NA } },
84 + { .channels = 4, /* CA_ID 0x02 */
85 + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
86 + SNDRV_CHMAP_FC } },
87 + { .channels = 4, /* CA_ID 0x03 */
88 + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
89 + SNDRV_CHMAP_FC } },
90 + { .channels = 6, /* CA_ID 0x04 */
91 + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
92 + SNDRV_CHMAP_NA, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
93 + { .channels = 6, /* CA_ID 0x05 */
94 + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
95 + SNDRV_CHMAP_NA, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
96 + { .channels = 6, /* CA_ID 0x06 */
97 + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
98 + SNDRV_CHMAP_FC, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
99 + { .channels = 6, /* CA_ID 0x07 */
100 + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
101 + SNDRV_CHMAP_FC, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
102 + { .channels = 6, /* CA_ID 0x08 */
103 + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
104 + SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
105 + { .channels = 6, /* CA_ID 0x09 */
106 + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
107 + SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
108 + { .channels = 6, /* CA_ID 0x0A */
109 + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
110 + SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
111 + { .channels = 6, /* CA_ID 0x0B */
112 + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
113 + SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
114 + { .channels = 8, /* CA_ID 0x0C */
115 + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
116 + SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
117 + SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
118 + { .channels = 8, /* CA_ID 0x0D */
119 + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
120 + SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
121 + SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
122 + { .channels = 8, /* CA_ID 0x0E */
123 + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
124 + SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
125 + SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
126 + { .channels = 8, /* CA_ID 0x0F */
127 + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
128 + SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
129 + SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
130 + { .channels = 8, /* CA_ID 0x10 */
131 + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
132 + SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
133 + SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } },
134 + { .channels = 8, /* CA_ID 0x11 */
135 + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
136 + SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
137 + SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } },
138 + { .channels = 8, /* CA_ID 0x12 */
139 + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
140 + SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
141 + SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } },
142 + { .channels = 8, /* CA_ID 0x13 */
143 + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
144 + SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
145 + SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } },
146 + { .channels = 8, /* CA_ID 0x14 */
147 + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
148 + SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
149 + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
150 + { .channels = 8, /* CA_ID 0x15 */
151 + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
152 + SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
153 + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
154 + { .channels = 8, /* CA_ID 0x16 */
155 + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
156 + SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
157 + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
158 + { .channels = 8, /* CA_ID 0x17 */
159 + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
160 + SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
161 + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
162 + { .channels = 8, /* CA_ID 0x18 */
163 + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
164 + SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
165 + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
166 + { .channels = 8, /* CA_ID 0x19 */
167 + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
168 + SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
169 + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
170 + { .channels = 8, /* CA_ID 0x1A */
171 + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
172 + SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
173 + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
174 + { .channels = 8, /* CA_ID 0x1B */
175 + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
176 + SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
177 + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
178 + { .channels = 8, /* CA_ID 0x1C */
179 + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
180 + SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
181 + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
182 + { .channels = 8, /* CA_ID 0x1D */
183 + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
184 + SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
185 + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
186 + { .channels = 8, /* CA_ID 0x1E */
187 + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
188 + SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
189 + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
190 + { .channels = 8, /* CA_ID 0x1F */
191 + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
192 + SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
193 + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
194 + { }
195 +};
196 +
197 +/*
198 + * hdmi_codec_channel_alloc: speaker configuration available for CEA
199 + *
200 + * This is an ordered list that must match with hdmi_codec_8ch_chmaps struct
201 + * The preceding ones have better chances to be selected by
202 + * hdmi_codec_get_ch_alloc_table_idx().
203 + */
204 +static const struct hdmi_codec_cea_spk_alloc hdmi_codec_channel_alloc[] = {
205 + { .ca_id = 0x00, .n_ch = 2,
206 + .mask = FL | FR},
207 + /* 2.1 */
208 + { .ca_id = 0x01, .n_ch = 4,
209 + .mask = FL | FR | LFE},
210 + /* Dolby Surround */
211 + { .ca_id = 0x02, .n_ch = 4,
212 + .mask = FL | FR | FC },
213 + /* surround51 */
214 + { .ca_id = 0x0b, .n_ch = 6,
215 + .mask = FL | FR | LFE | FC | RL | RR},
216 + /* surround40 */
217 + { .ca_id = 0x08, .n_ch = 6,
218 + .mask = FL | FR | RL | RR },
219 + /* surround41 */
220 + { .ca_id = 0x09, .n_ch = 6,
221 + .mask = FL | FR | LFE | RL | RR },
222 + /* surround50 */
223 + { .ca_id = 0x0a, .n_ch = 6,
224 + .mask = FL | FR | FC | RL | RR },
225 + /* 6.1 */
226 + { .ca_id = 0x0f, .n_ch = 8,
227 + .mask = FL | FR | LFE | FC | RL | RR | RC },
228 + /* surround71 */
229 + { .ca_id = 0x13, .n_ch = 8,
230 + .mask = FL | FR | LFE | FC | RL | RR | RLC | RRC },
231 + /* others */
232 + { .ca_id = 0x03, .n_ch = 8,
233 + .mask = FL | FR | LFE | FC },
234 + { .ca_id = 0x04, .n_ch = 8,
235 + .mask = FL | FR | RC},
236 + { .ca_id = 0x05, .n_ch = 8,
237 + .mask = FL | FR | LFE | RC },
238 + { .ca_id = 0x06, .n_ch = 8,
239 + .mask = FL | FR | FC | RC },
240 + { .ca_id = 0x07, .n_ch = 8,
241 + .mask = FL | FR | LFE | FC | RC },
242 + { .ca_id = 0x0c, .n_ch = 8,
243 + .mask = FL | FR | RC | RL | RR },
244 + { .ca_id = 0x0d, .n_ch = 8,
245 + .mask = FL | FR | LFE | RL | RR | RC },
246 + { .ca_id = 0x0e, .n_ch = 8,
247 + .mask = FL | FR | FC | RL | RR | RC },
248 + { .ca_id = 0x10, .n_ch = 8,
249 + .mask = FL | FR | RL | RR | RLC | RRC },
250 + { .ca_id = 0x11, .n_ch = 8,
251 + .mask = FL | FR | LFE | RL | RR | RLC | RRC },
252 + { .ca_id = 0x12, .n_ch = 8,
253 + .mask = FL | FR | FC | RL | RR | RLC | RRC },
254 + { .ca_id = 0x14, .n_ch = 8,
255 + .mask = FL | FR | FLC | FRC },
256 + { .ca_id = 0x15, .n_ch = 8,
257 + .mask = FL | FR | LFE | FLC | FRC },
258 + { .ca_id = 0x16, .n_ch = 8,
259 + .mask = FL | FR | FC | FLC | FRC },
260 + { .ca_id = 0x17, .n_ch = 8,
261 + .mask = FL | FR | LFE | FC | FLC | FRC },
262 + { .ca_id = 0x18, .n_ch = 8,
263 + .mask = FL | FR | RC | FLC | FRC },
264 + { .ca_id = 0x19, .n_ch = 8,
265 + .mask = FL | FR | LFE | RC | FLC | FRC },
266 + { .ca_id = 0x1a, .n_ch = 8,
267 + .mask = FL | FR | RC | FC | FLC | FRC },
268 + { .ca_id = 0x1b, .n_ch = 8,
269 + .mask = FL | FR | LFE | RC | FC | FLC | FRC },
270 + { .ca_id = 0x1c, .n_ch = 8,
271 + .mask = FL | FR | RL | RR | FLC | FRC },
272 + { .ca_id = 0x1d, .n_ch = 8,
273 + .mask = FL | FR | LFE | RL | RR | FLC | FRC },
274 + { .ca_id = 0x1e, .n_ch = 8,
275 + .mask = FL | FR | FC | RL | RR | FLC | FRC },
276 + { .ca_id = 0x1f, .n_ch = 8,
277 + .mask = FL | FR | LFE | FC | RL | RR | FLC | FRC },
278 +};
279 +
280 +static unsigned long hdmi_codec_spk_mask_from_alloc(int spk_alloc)
281 +{
282 + int i;
283 + static const unsigned long hdmi_codec_eld_spk_alloc_bits[] = {
284 + [0] = FL | FR, [1] = LFE, [2] = FC, [3] = RL | RR,
285 + [4] = RC, [5] = FLC | FRC, [6] = RLC | RRC,
286 + };
287 + unsigned long spk_mask = 0;
288 +
289 + for (i = 0; i < ARRAY_SIZE(hdmi_codec_eld_spk_alloc_bits); i++) {
290 + if (spk_alloc & (1 << i))
291 + spk_mask |= hdmi_codec_eld_spk_alloc_bits[i];
292 + }
293 +
294 + return spk_mask;
295 +}
296 +
297 +static int hdmi_codec_get_ch_alloc_table_idx(struct vc4_hdmi *vc4_hdmi,
298 + unsigned char channels)
299 +{
300 + struct drm_connector *connector = &vc4_hdmi->connector;
301 + int i;
302 + u8 spk_alloc;
303 + unsigned long spk_mask;
304 + const struct hdmi_codec_cea_spk_alloc *cap = hdmi_codec_channel_alloc;
305 +
306 + spk_alloc = drm_eld_get_spk_alloc(connector->eld);
307 + spk_mask = hdmi_codec_spk_mask_from_alloc(spk_alloc);
308 +
309 + for (i = 0; i < ARRAY_SIZE(hdmi_codec_channel_alloc); i++, cap++) {
310 + /* If spk_alloc == 0, HDMI is unplugged return stereo config*/
311 + if (!spk_alloc && cap->ca_id == 0)
312 + return i;
313 + if (cap->n_ch != channels)
314 + continue;
315 + if (!(cap->mask == (spk_mask & cap->mask)))
316 + continue;
317 + return i;
318 + }
319 +
320 + return -EINVAL;
321 +}
322 +
323 +static void hdmi_codec_eld_chmap(struct vc4_hdmi *vc4_hdmi)
324 +{
325 + struct drm_connector *connector = &vc4_hdmi->connector;
326 + u8 spk_alloc;
327 + unsigned long spk_mask;
328 +
329 + spk_alloc = drm_eld_get_spk_alloc(connector->eld);
330 + spk_mask = hdmi_codec_spk_mask_from_alloc(spk_alloc);
331 +
332 + /* Detect if only stereo supported, else return 8 channels mappings */
333 + if ((spk_mask & ~(FL | FR)))
334 + vc4_hdmi->audio.chmap = hdmi_codec_8ch_chmaps;
335 + else
336 + vc4_hdmi->audio.chmap = hdmi_codec_stereo_chmaps;
337 +}
338 +
339 static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused)
340 {
341 struct drm_info_node *node = (struct drm_info_node *)m->private;
342 @@ -350,6 +656,9 @@ static void vc4_hdmi_set_audio_infoframe
343 frame.audio.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM;
344 frame.audio.channels = vc4_hdmi->audio.channels;
345
346 + /* Select a channel allocation that matches with ELD and pcm channels */
347 + frame.audio.channel_allocation = vc4_hdmi->audio.chmap_idx;
348 +
349 vc4_hdmi_write_infoframe(encoder, &frame);
350 }
351
352 @@ -881,6 +1190,10 @@ static int vc4_hdmi_audio_startup(struct
353 if (ret)
354 return ret;
355
356 + /* Select chmap supported */
357 + vc4_hdmi->audio.max_channels = 8;
358 + hdmi_codec_eld_chmap(vc4_hdmi);
359 +
360 return 0;
361 }
362
363 @@ -967,6 +1280,7 @@ static int vc4_hdmi_audio_prepare(struct
364 u32 channel_map;
365 u32 mai_audio_format;
366 u32 mai_sample_rate;
367 + int idx;
368
369 if (substream != vc4_hdmi->audio.substream)
370 return -EINVAL;
371 @@ -1027,6 +1341,14 @@ static int vc4_hdmi_audio_prepare(struct
372 HDMI_WRITE(HDMI_AUDIO_PACKET_CONFIG, audio_packet_config);
373 vc4_hdmi_set_n_cts(vc4_hdmi);
374
375 + idx = hdmi_codec_get_ch_alloc_table_idx(vc4_hdmi, vc4_hdmi->audio.channels);
376 + if (idx < 0) {
377 + DRM_ERROR("Not able to map channels to speakers (%d)\n", idx);
378 + vc4_hdmi->audio.chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
379 + } else {
380 + vc4_hdmi->audio.chmap_idx = hdmi_codec_channel_alloc[idx].ca_id;
381 + }
382 +
383 return 0;
384 }
385
386 @@ -1145,6 +1467,89 @@ static int vc4_spdif_mask_get(struct snd
387 return 0;
388 }
389
390 +/*
391 + * ALSA API channel-map control callbacks
392 + */
393 +static int vc4_chmap_ctl_info(struct snd_kcontrol *kcontrol,
394 + struct snd_ctl_elem_info *uinfo)
395 +{
396 + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
397 + struct vc4_hdmi *vc4_hdmi = snd_component_to_hdmi(component);
398 +
399 + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
400 + uinfo->count = vc4_hdmi->audio.max_channels;
401 + uinfo->value.integer.min = 0;
402 + uinfo->value.integer.max = SNDRV_CHMAP_LAST;
403 +
404 + return 0;
405 +}
406 +
407 +static int vc4_chmap_ctl_get(struct snd_kcontrol *kcontrol,
408 + struct snd_ctl_elem_value *ucontrol)
409 +{
410 + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
411 + struct vc4_hdmi *vc4_hdmi = snd_component_to_hdmi(component);
412 + unsigned const char *map;
413 + unsigned int i;
414 +
415 + if (!vc4_hdmi->audio.chmap)
416 + return -EINVAL;
417 +
418 + map = vc4_hdmi->audio.chmap[vc4_hdmi->audio.chmap_idx].map;
419 +
420 + for (i = 0; i < vc4_hdmi->audio.max_channels; i++) {
421 + if (vc4_hdmi->audio.chmap_idx == HDMI_CODEC_CHMAP_IDX_UNKNOWN)
422 + ucontrol->value.integer.value[i] = 0;
423 + else
424 + ucontrol->value.integer.value[i] = map[i];
425 + }
426 + return 0;
427 +}
428 +
429 +static int vc4_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
430 + unsigned int size, unsigned int __user *tlv)
431 +{
432 + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
433 + struct vc4_hdmi *vc4_hdmi = snd_component_to_hdmi(component);
434 + const struct snd_pcm_chmap_elem *map;
435 + unsigned int __user *dst;
436 + int c, count = 0;
437 +
438 + if (!vc4_hdmi->audio.chmap)
439 + return -EINVAL;
440 + if (size < 8)
441 + return -ENOMEM;
442 + if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv))
443 + return -EFAULT;
444 + size -= 8;
445 + dst = tlv + 2;
446 + for (map = vc4_hdmi->audio.chmap; map->channels; map++) {
447 + int chs_bytes = map->channels * 4;
448 + //if (!valid_chmap_channels(info, map->channels))
449 + // continue;
450 + if (size < 8)
451 + return -ENOMEM;
452 + if (put_user(SNDRV_CTL_TLVT_CHMAP_FIXED, dst) ||
453 + put_user(chs_bytes, dst + 1))
454 + return -EFAULT;
455 + dst += 2;
456 + size -= 8;
457 + count += 8;
458 + if (size < chs_bytes)
459 + return -ENOMEM;
460 + size -= chs_bytes;
461 + count += chs_bytes;
462 + for (c = 0; c < map->channels; c++) {
463 + if (put_user(map->map[c], dst))
464 + return -EFAULT;
465 + dst++;
466 + }
467 + }
468 + if (put_user(count, tlv + 1))
469 + return -EFAULT;
470 + return 0;
471 +}
472 +
473 static const struct snd_kcontrol_new vc4_hdmi_audio_controls[] = {
474 {
475 .access = SNDRV_CTL_ELEM_ACCESS_READ |
476 @@ -1167,6 +1572,16 @@ static const struct snd_kcontrol_new vc4
477 .info = vc4_spdif_info,
478 .get = vc4_spdif_mask_get,
479 },
480 + {
481 + .access = SNDRV_CTL_ELEM_ACCESS_READ |
482 + SNDRV_CTL_ELEM_ACCESS_TLV_READ |
483 + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,
484 + .iface = SNDRV_CTL_ELEM_IFACE_PCM,
485 + .name = "Playback Channel Map",
486 + .info = vc4_chmap_ctl_info,
487 + .get = vc4_chmap_ctl_get,
488 + .tlv.c = vc4_chmap_ctl_tlv,
489 + },
490 };
491
492 static const struct snd_soc_dapm_widget vc4_hdmi_audio_widgets[] = {
493 --- a/drivers/gpu/drm/vc4/vc4_hdmi.h
494 +++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
495 @@ -117,6 +117,9 @@ struct vc4_hdmi_audio {
496 bool streaming;
497
498 unsigned char iec_status[4];
499 + const struct snd_pcm_chmap_elem *chmap;
500 + unsigned int chmap_idx;
501 + unsigned int max_channels;
502 };
503
504 /* General HDMI hardware state. */