Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-only
2 : : /*
3 : : * HDMI Channel map support helpers
4 : : */
5 : :
6 : : #include <linux/module.h>
7 : : #include <sound/control.h>
8 : : #include <sound/tlv.h>
9 : : #include <sound/hda_chmap.h>
10 : :
11 : : /*
12 : : * CEA speaker placement:
13 : : *
14 : : * FLH FCH FRH
15 : : * FLW FL FLC FC FRC FR FRW
16 : : *
17 : : * LFE
18 : : * TC
19 : : *
20 : : * RL RLC RC RRC RR
21 : : *
22 : : * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to
23 : : * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC.
24 : : */
25 : : enum cea_speaker_placement {
26 : : FL = (1 << 0), /* Front Left */
27 : : FC = (1 << 1), /* Front Center */
28 : : FR = (1 << 2), /* Front Right */
29 : : FLC = (1 << 3), /* Front Left Center */
30 : : FRC = (1 << 4), /* Front Right Center */
31 : : RL = (1 << 5), /* Rear Left */
32 : : RC = (1 << 6), /* Rear Center */
33 : : RR = (1 << 7), /* Rear Right */
34 : : RLC = (1 << 8), /* Rear Left Center */
35 : : RRC = (1 << 9), /* Rear Right Center */
36 : : LFE = (1 << 10), /* Low Frequency Effect */
37 : : FLW = (1 << 11), /* Front Left Wide */
38 : : FRW = (1 << 12), /* Front Right Wide */
39 : : FLH = (1 << 13), /* Front Left High */
40 : : FCH = (1 << 14), /* Front Center High */
41 : : FRH = (1 << 15), /* Front Right High */
42 : : TC = (1 << 16), /* Top Center */
43 : : };
44 : :
45 : : static const char * const cea_speaker_allocation_names[] = {
46 : : /* 0 */ "FL/FR",
47 : : /* 1 */ "LFE",
48 : : /* 2 */ "FC",
49 : : /* 3 */ "RL/RR",
50 : : /* 4 */ "RC",
51 : : /* 5 */ "FLC/FRC",
52 : : /* 6 */ "RLC/RRC",
53 : : /* 7 */ "FLW/FRW",
54 : : /* 8 */ "FLH/FRH",
55 : : /* 9 */ "TC",
56 : : /* 10 */ "FCH",
57 : : };
58 : :
59 : : /*
60 : : * ELD SA bits in the CEA Speaker Allocation data block
61 : : */
62 : : static const int eld_speaker_allocation_bits[] = {
63 : : [0] = FL | FR,
64 : : [1] = LFE,
65 : : [2] = FC,
66 : : [3] = RL | RR,
67 : : [4] = RC,
68 : : [5] = FLC | FRC,
69 : : [6] = RLC | RRC,
70 : : /* the following are not defined in ELD yet */
71 : : [7] = FLW | FRW,
72 : : [8] = FLH | FRH,
73 : : [9] = TC,
74 : : [10] = FCH,
75 : : };
76 : :
77 : : /*
78 : : * ALSA sequence is:
79 : : *
80 : : * surround40 surround41 surround50 surround51 surround71
81 : : * ch0 front left = = = =
82 : : * ch1 front right = = = =
83 : : * ch2 rear left = = = =
84 : : * ch3 rear right = = = =
85 : : * ch4 LFE center center center
86 : : * ch5 LFE LFE
87 : : * ch6 side left
88 : : * ch7 side right
89 : : *
90 : : * surround71 = {FL, FR, RLC, RRC, FC, LFE, RL, RR}
91 : : */
92 : : static int hdmi_channel_mapping[0x32][8] = {
93 : : /* stereo */
94 : : [0x00] = { 0x00, 0x11, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
95 : : /* 2.1 */
96 : : [0x01] = { 0x00, 0x11, 0x22, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
97 : : /* Dolby Surround */
98 : : [0x02] = { 0x00, 0x11, 0x23, 0xf2, 0xf4, 0xf5, 0xf6, 0xf7 },
99 : : /* surround40 */
100 : : [0x08] = { 0x00, 0x11, 0x24, 0x35, 0xf3, 0xf2, 0xf6, 0xf7 },
101 : : /* 4ch */
102 : : [0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 },
103 : : /* surround41 */
104 : : [0x09] = { 0x00, 0x11, 0x24, 0x35, 0x42, 0xf3, 0xf6, 0xf7 },
105 : : /* surround50 */
106 : : [0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 },
107 : : /* surround51 */
108 : : [0x0b] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0x52, 0xf6, 0xf7 },
109 : : /* 7.1 */
110 : : [0x13] = { 0x00, 0x11, 0x26, 0x37, 0x43, 0x52, 0x64, 0x75 },
111 : : };
112 : :
113 : : /*
114 : : * This is an ordered list!
115 : : *
116 : : * The preceding ones have better chances to be selected by
117 : : * hdmi_channel_allocation().
118 : : */
119 : : static struct hdac_cea_channel_speaker_allocation channel_allocations[] = {
120 : : /* channel: 7 6 5 4 3 2 1 0 */
121 : : { .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } },
122 : : /* 2.1 */
123 : : { .ca_index = 0x01, .speakers = { 0, 0, 0, 0, 0, LFE, FR, FL } },
124 : : /* Dolby Surround */
125 : : { .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, 0, FR, FL } },
126 : : /* surround40 */
127 : : { .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL } },
128 : : /* surround41 */
129 : : { .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL } },
130 : : /* surround50 */
131 : : { .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL } },
132 : : /* surround51 */
133 : : { .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL } },
134 : : /* 6.1 */
135 : : { .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL } },
136 : : /* surround71 */
137 : : { .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL } },
138 : :
139 : : { .ca_index = 0x03, .speakers = { 0, 0, 0, 0, FC, LFE, FR, FL } },
140 : : { .ca_index = 0x04, .speakers = { 0, 0, 0, RC, 0, 0, FR, FL } },
141 : : { .ca_index = 0x05, .speakers = { 0, 0, 0, RC, 0, LFE, FR, FL } },
142 : : { .ca_index = 0x06, .speakers = { 0, 0, 0, RC, FC, 0, FR, FL } },
143 : : { .ca_index = 0x07, .speakers = { 0, 0, 0, RC, FC, LFE, FR, FL } },
144 : : { .ca_index = 0x0c, .speakers = { 0, RC, RR, RL, 0, 0, FR, FL } },
145 : : { .ca_index = 0x0d, .speakers = { 0, RC, RR, RL, 0, LFE, FR, FL } },
146 : : { .ca_index = 0x0e, .speakers = { 0, RC, RR, RL, FC, 0, FR, FL } },
147 : : { .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, 0, 0, FR, FL } },
148 : : { .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, 0, LFE, FR, FL } },
149 : : { .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, 0, FR, FL } },
150 : : { .ca_index = 0x14, .speakers = { FRC, FLC, 0, 0, 0, 0, FR, FL } },
151 : : { .ca_index = 0x15, .speakers = { FRC, FLC, 0, 0, 0, LFE, FR, FL } },
152 : : { .ca_index = 0x16, .speakers = { FRC, FLC, 0, 0, FC, 0, FR, FL } },
153 : : { .ca_index = 0x17, .speakers = { FRC, FLC, 0, 0, FC, LFE, FR, FL } },
154 : : { .ca_index = 0x18, .speakers = { FRC, FLC, 0, RC, 0, 0, FR, FL } },
155 : : { .ca_index = 0x19, .speakers = { FRC, FLC, 0, RC, 0, LFE, FR, FL } },
156 : : { .ca_index = 0x1a, .speakers = { FRC, FLC, 0, RC, FC, 0, FR, FL } },
157 : : { .ca_index = 0x1b, .speakers = { FRC, FLC, 0, RC, FC, LFE, FR, FL } },
158 : : { .ca_index = 0x1c, .speakers = { FRC, FLC, RR, RL, 0, 0, FR, FL } },
159 : : { .ca_index = 0x1d, .speakers = { FRC, FLC, RR, RL, 0, LFE, FR, FL } },
160 : : { .ca_index = 0x1e, .speakers = { FRC, FLC, RR, RL, FC, 0, FR, FL } },
161 : : { .ca_index = 0x1f, .speakers = { FRC, FLC, RR, RL, FC, LFE, FR, FL } },
162 : : { .ca_index = 0x20, .speakers = { 0, FCH, RR, RL, FC, 0, FR, FL } },
163 : : { .ca_index = 0x21, .speakers = { 0, FCH, RR, RL, FC, LFE, FR, FL } },
164 : : { .ca_index = 0x22, .speakers = { TC, 0, RR, RL, FC, 0, FR, FL } },
165 : : { .ca_index = 0x23, .speakers = { TC, 0, RR, RL, FC, LFE, FR, FL } },
166 : : { .ca_index = 0x24, .speakers = { FRH, FLH, RR, RL, 0, 0, FR, FL } },
167 : : { .ca_index = 0x25, .speakers = { FRH, FLH, RR, RL, 0, LFE, FR, FL } },
168 : : { .ca_index = 0x26, .speakers = { FRW, FLW, RR, RL, 0, 0, FR, FL } },
169 : : { .ca_index = 0x27, .speakers = { FRW, FLW, RR, RL, 0, LFE, FR, FL } },
170 : : { .ca_index = 0x28, .speakers = { TC, RC, RR, RL, FC, 0, FR, FL } },
171 : : { .ca_index = 0x29, .speakers = { TC, RC, RR, RL, FC, LFE, FR, FL } },
172 : : { .ca_index = 0x2a, .speakers = { FCH, RC, RR, RL, FC, 0, FR, FL } },
173 : : { .ca_index = 0x2b, .speakers = { FCH, RC, RR, RL, FC, LFE, FR, FL } },
174 : : { .ca_index = 0x2c, .speakers = { TC, FCH, RR, RL, FC, 0, FR, FL } },
175 : : { .ca_index = 0x2d, .speakers = { TC, FCH, RR, RL, FC, LFE, FR, FL } },
176 : : { .ca_index = 0x2e, .speakers = { FRH, FLH, RR, RL, FC, 0, FR, FL } },
177 : : { .ca_index = 0x2f, .speakers = { FRH, FLH, RR, RL, FC, LFE, FR, FL } },
178 : : { .ca_index = 0x30, .speakers = { FRW, FLW, RR, RL, FC, 0, FR, FL } },
179 : : { .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } },
180 : : };
181 : :
182 : 0 : static int hdmi_pin_set_slot_channel(struct hdac_device *codec,
183 : : hda_nid_t pin_nid, int asp_slot, int channel)
184 : : {
185 : 0 : return snd_hdac_codec_write(codec, pin_nid, 0,
186 : : AC_VERB_SET_HDMI_CHAN_SLOT,
187 : 0 : (channel << 4) | asp_slot);
188 : : }
189 : :
190 : 0 : static int hdmi_pin_get_slot_channel(struct hdac_device *codec,
191 : : hda_nid_t pin_nid, int asp_slot)
192 : : {
193 : 0 : return (snd_hdac_codec_read(codec, pin_nid, 0,
194 : : AC_VERB_GET_HDMI_CHAN_SLOT,
195 : 0 : asp_slot) & 0xf0) >> 4;
196 : : }
197 : :
198 : 0 : static int hdmi_get_channel_count(struct hdac_device *codec, hda_nid_t cvt_nid)
199 : : {
200 : 0 : return 1 + snd_hdac_codec_read(codec, cvt_nid, 0,
201 : : AC_VERB_GET_CVT_CHAN_COUNT, 0);
202 : : }
203 : :
204 : 0 : static void hdmi_set_channel_count(struct hdac_device *codec,
205 : : hda_nid_t cvt_nid, int chs)
206 : : {
207 [ # # ]: 0 : if (chs != hdmi_get_channel_count(codec, cvt_nid))
208 : 0 : snd_hdac_codec_write(codec, cvt_nid, 0,
209 : 0 : AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
210 : 0 : }
211 : :
212 : : /*
213 : : * Channel mapping routines
214 : : */
215 : :
216 : : /*
217 : : * Compute derived values in channel_allocations[].
218 : : */
219 : 0 : static void init_channel_allocations(void)
220 : : {
221 : 0 : int i, j;
222 : 0 : struct hdac_cea_channel_speaker_allocation *p;
223 : :
224 [ # # ]: 0 : for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
225 : 0 : p = channel_allocations + i;
226 : 0 : p->channels = 0;
227 : 0 : p->spk_mask = 0;
228 [ # # ]: 0 : for (j = 0; j < ARRAY_SIZE(p->speakers); j++)
229 [ # # ]: 0 : if (p->speakers[j]) {
230 : 0 : p->channels++;
231 : 0 : p->spk_mask |= p->speakers[j];
232 : : }
233 : : }
234 : 0 : }
235 : :
236 : 0 : static int get_channel_allocation_order(int ca)
237 : : {
238 : 0 : int i;
239 : :
240 [ # # # # : 0 : for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
# # # # #
# ]
241 [ # # # # : 0 : if (channel_allocations[i].ca_index == ca)
# # # # #
# ]
242 : : break;
243 : : }
244 : 0 : return i;
245 : : }
246 : :
247 : 0 : void snd_hdac_print_channel_allocation(int spk_alloc, char *buf, int buflen)
248 : : {
249 : 0 : int i, j;
250 : :
251 [ # # ]: 0 : for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) {
252 [ # # ]: 0 : if (spk_alloc & (1 << i))
253 : 0 : j += scnprintf(buf + j, buflen - j, " %s",
254 : : cea_speaker_allocation_names[i]);
255 : : }
256 : 0 : buf[j] = '\0'; /* necessary when j == 0 */
257 : 0 : }
258 : : EXPORT_SYMBOL_GPL(snd_hdac_print_channel_allocation);
259 : :
260 : : /*
261 : : * The transformation takes two steps:
262 : : *
263 : : * eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask
264 : : * spk_mask => (channel_allocations[]) => ai->CA
265 : : *
266 : : * TODO: it could select the wrong CA from multiple candidates.
267 : : */
268 : : static int hdmi_channel_allocation_spk_alloc_blk(struct hdac_device *codec,
269 : : int spk_alloc, int channels)
270 : : {
271 : : int i;
272 : : int ca = 0;
273 : : int spk_mask = 0;
274 : : char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
275 : :
276 : : /*
277 : : * CA defaults to 0 for basic stereo audio
278 : : */
279 : : if (channels <= 2)
280 : : return 0;
281 : :
282 : : /*
283 : : * expand ELD's speaker allocation mask
284 : : *
285 : : * ELD tells the speaker mask in a compact(paired) form,
286 : : * expand ELD's notions to match the ones used by Audio InfoFrame.
287 : : */
288 : : for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) {
289 : : if (spk_alloc & (1 << i))
290 : : spk_mask |= eld_speaker_allocation_bits[i];
291 : : }
292 : :
293 : : /* search for the first working match in the CA table */
294 : : for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
295 : : if (channels == channel_allocations[i].channels &&
296 : : (spk_mask & channel_allocations[i].spk_mask) ==
297 : : channel_allocations[i].spk_mask) {
298 : : ca = channel_allocations[i].ca_index;
299 : : break;
300 : : }
301 : : }
302 : :
303 : : if (!ca) {
304 : : /*
305 : : * if there was no match, select the regular ALSA channel
306 : : * allocation with the matching number of channels
307 : : */
308 : : for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
309 : : if (channels == channel_allocations[i].channels) {
310 : : ca = channel_allocations[i].ca_index;
311 : : break;
312 : : }
313 : : }
314 : : }
315 : :
316 : : snd_hdac_print_channel_allocation(spk_alloc, buf, sizeof(buf));
317 : : dev_dbg(&codec->dev, "HDMI: select CA 0x%x for %d-channel allocation: %s\n",
318 : : ca, channels, buf);
319 : :
320 : : return ca;
321 : : }
322 : :
323 : 0 : static void hdmi_debug_channel_mapping(struct hdac_chmap *chmap,
324 : : hda_nid_t pin_nid)
325 : : {
326 : : #ifdef CONFIG_SND_DEBUG_VERBOSE
327 : : int i;
328 : : int channel;
329 : :
330 : : for (i = 0; i < 8; i++) {
331 : : channel = chmap->ops.pin_get_slot_channel(
332 : : chmap->hdac, pin_nid, i);
333 : : dev_dbg(&chmap->hdac->dev, "HDMI: ASP channel %d => slot %d\n",
334 : : channel, i);
335 : : }
336 : : #endif
337 : 0 : }
338 : :
339 : 0 : static void hdmi_std_setup_channel_mapping(struct hdac_chmap *chmap,
340 : : hda_nid_t pin_nid,
341 : : bool non_pcm,
342 : : int ca)
343 : : {
344 : 0 : struct hdac_cea_channel_speaker_allocation *ch_alloc;
345 : 0 : int i;
346 : 0 : int err;
347 : 0 : int order;
348 : 0 : int non_pcm_mapping[8];
349 : :
350 : 0 : order = get_channel_allocation_order(ca);
351 : 0 : ch_alloc = &channel_allocations[order];
352 : :
353 [ # # ]: 0 : if (hdmi_channel_mapping[ca][1] == 0) {
354 : : int hdmi_slot = 0;
355 : : /* fill actual channel mappings in ALSA channel (i) order */
356 [ # # ]: 0 : for (i = 0; i < ch_alloc->channels; i++) {
357 [ # # # # ]: 0 : while (!WARN_ON(hdmi_slot >= 8) &&
358 [ # # ]: 0 : !ch_alloc->speakers[7 - hdmi_slot])
359 : 0 : hdmi_slot++; /* skip zero slots */
360 : :
361 : 0 : hdmi_channel_mapping[ca][i] = (i << 4) | hdmi_slot++;
362 : : }
363 : : /* fill the rest of the slots with ALSA channel 0xf */
364 [ # # ]: 0 : for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++)
365 [ # # ]: 0 : if (!ch_alloc->speakers[7 - hdmi_slot])
366 : 0 : hdmi_channel_mapping[ca][i++] = (0xf << 4) | hdmi_slot;
367 : : }
368 : :
369 [ # # ]: 0 : if (non_pcm) {
370 [ # # ]: 0 : for (i = 0; i < ch_alloc->channels; i++)
371 : 0 : non_pcm_mapping[i] = (i << 4) | i;
372 [ # # ]: 0 : for (; i < 8; i++)
373 : 0 : non_pcm_mapping[i] = (0xf << 4) | i;
374 : : }
375 : :
376 [ # # ]: 0 : for (i = 0; i < 8; i++) {
377 [ # # ]: 0 : int slotsetup = non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i];
378 : 0 : int hdmi_slot = slotsetup & 0x0f;
379 : 0 : int channel = (slotsetup & 0xf0) >> 4;
380 : :
381 : 0 : err = chmap->ops.pin_set_slot_channel(chmap->hdac,
382 : : pin_nid, hdmi_slot, channel);
383 [ # # ]: 0 : if (err) {
384 : : dev_dbg(&chmap->hdac->dev, "HDMI: channel mapping failed\n");
385 : : break;
386 : : }
387 : : }
388 : 0 : }
389 : :
390 : : struct channel_map_table {
391 : : unsigned char map; /* ALSA API channel map position */
392 : : int spk_mask; /* speaker position bit mask */
393 : : };
394 : :
395 : : static struct channel_map_table map_tables[] = {
396 : : { SNDRV_CHMAP_FL, FL },
397 : : { SNDRV_CHMAP_FR, FR },
398 : : { SNDRV_CHMAP_RL, RL },
399 : : { SNDRV_CHMAP_RR, RR },
400 : : { SNDRV_CHMAP_LFE, LFE },
401 : : { SNDRV_CHMAP_FC, FC },
402 : : { SNDRV_CHMAP_RLC, RLC },
403 : : { SNDRV_CHMAP_RRC, RRC },
404 : : { SNDRV_CHMAP_RC, RC },
405 : : { SNDRV_CHMAP_FLC, FLC },
406 : : { SNDRV_CHMAP_FRC, FRC },
407 : : { SNDRV_CHMAP_TFL, FLH },
408 : : { SNDRV_CHMAP_TFR, FRH },
409 : : { SNDRV_CHMAP_FLW, FLW },
410 : : { SNDRV_CHMAP_FRW, FRW },
411 : : { SNDRV_CHMAP_TC, TC },
412 : : { SNDRV_CHMAP_TFC, FCH },
413 : : {} /* terminator */
414 : : };
415 : :
416 : : /* from ALSA API channel position to speaker bit mask */
417 : 0 : int snd_hdac_chmap_to_spk_mask(unsigned char c)
418 : : {
419 : 0 : struct channel_map_table *t = map_tables;
420 : :
421 [ # # # # : 0 : for (; t->map; t++) {
# # ]
422 [ # # # # : 0 : if (t->map == c)
# # ]
423 : 0 : return t->spk_mask;
424 : : }
425 : : return 0;
426 : : }
427 : : EXPORT_SYMBOL_GPL(snd_hdac_chmap_to_spk_mask);
428 : :
429 : : /* from ALSA API channel position to CEA slot */
430 : 0 : static int to_cea_slot(int ordered_ca, unsigned char pos)
431 : : {
432 : 0 : int mask = snd_hdac_chmap_to_spk_mask(pos);
433 : 0 : int i;
434 : :
435 : : /* Add sanity check to pass klockwork check.
436 : : * This should never happen.
437 : : */
438 [ # # ]: 0 : if (ordered_ca >= ARRAY_SIZE(channel_allocations))
439 : : return -1;
440 : :
441 [ # # ]: 0 : if (mask) {
442 [ # # ]: 0 : for (i = 0; i < 8; i++) {
443 [ # # ]: 0 : if (channel_allocations[ordered_ca].speakers[7 - i] == mask)
444 : : return i;
445 : : }
446 : : }
447 : :
448 : : return -1;
449 : : }
450 : :
451 : : /* from speaker bit mask to ALSA API channel position */
452 : 0 : int snd_hdac_spk_to_chmap(int spk)
453 : : {
454 : 0 : struct channel_map_table *t = map_tables;
455 : :
456 [ # # # # : 0 : for (; t->map; t++) {
# # ]
457 [ # # # # : 0 : if (t->spk_mask == spk)
# # ]
458 : 0 : return t->map;
459 : : }
460 : : return 0;
461 : : }
462 : : EXPORT_SYMBOL_GPL(snd_hdac_spk_to_chmap);
463 : :
464 : : /* from CEA slot to ALSA API channel position */
465 : 0 : static int from_cea_slot(int ordered_ca, unsigned char slot)
466 : : {
467 : 0 : int mask;
468 : :
469 : : /* Add sanity check to pass klockwork check.
470 : : * This should never happen.
471 : : */
472 : 0 : if (slot >= 8)
473 : : return 0;
474 : :
475 : 0 : mask = channel_allocations[ordered_ca].speakers[7 - slot];
476 : :
477 : 0 : return snd_hdac_spk_to_chmap(mask);
478 : : }
479 : :
480 : : /* get the CA index corresponding to the given ALSA API channel map */
481 : 0 : static int hdmi_manual_channel_allocation(int chs, unsigned char *map)
482 : : {
483 : 0 : int i, spks = 0, spk_mask = 0;
484 : :
485 [ # # ]: 0 : for (i = 0; i < chs; i++) {
486 : 0 : int mask = snd_hdac_chmap_to_spk_mask(map[i]);
487 : :
488 [ # # ]: 0 : if (mask) {
489 : 0 : spk_mask |= mask;
490 : 0 : spks++;
491 : : }
492 : : }
493 : :
494 [ # # ]: 0 : for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
495 [ # # # # ]: 0 : if ((chs == channel_allocations[i].channels ||
496 : 0 : spks == channel_allocations[i].channels) &&
497 [ # # ]: 0 : (spk_mask & channel_allocations[i].spk_mask) ==
498 : : channel_allocations[i].spk_mask)
499 : 0 : return channel_allocations[i].ca_index;
500 : : }
501 : : return -1;
502 : : }
503 : :
504 : : /* set up the channel slots for the given ALSA API channel map */
505 : 0 : static int hdmi_manual_setup_channel_mapping(struct hdac_chmap *chmap,
506 : : hda_nid_t pin_nid,
507 : : int chs, unsigned char *map,
508 : : int ca)
509 : : {
510 : 0 : int ordered_ca = get_channel_allocation_order(ca);
511 : 0 : int alsa_pos, hdmi_slot;
512 : 0 : int assignments[8] = {[0 ... 7] = 0xf};
513 : :
514 [ # # ]: 0 : for (alsa_pos = 0; alsa_pos < chs; alsa_pos++) {
515 : :
516 : 0 : hdmi_slot = to_cea_slot(ordered_ca, map[alsa_pos]);
517 : :
518 [ # # ]: 0 : if (hdmi_slot < 0)
519 : 0 : continue; /* unassigned channel */
520 : :
521 : 0 : assignments[hdmi_slot] = alsa_pos;
522 : : }
523 : :
524 [ # # ]: 0 : for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) {
525 : 0 : int err;
526 : :
527 : 0 : err = chmap->ops.pin_set_slot_channel(chmap->hdac,
528 : : pin_nid, hdmi_slot, assignments[hdmi_slot]);
529 [ # # ]: 0 : if (err)
530 : : return -EINVAL;
531 : : }
532 : : return 0;
533 : : }
534 : :
535 : : /* store ALSA API channel map from the current default map */
536 : 0 : static void hdmi_setup_fake_chmap(unsigned char *map, int ca)
537 : : {
538 : 0 : int i;
539 : 0 : int ordered_ca = get_channel_allocation_order(ca);
540 : :
541 [ # # ]: 0 : for (i = 0; i < 8; i++) {
542 [ # # ]: 0 : if (ordered_ca < ARRAY_SIZE(channel_allocations) &&
543 [ # # ]: 0 : i < channel_allocations[ordered_ca].channels)
544 [ # # ]: 0 : map[i] = from_cea_slot(ordered_ca, hdmi_channel_mapping[ca][i] & 0x0f);
545 : : else
546 : 0 : map[i] = 0;
547 : : }
548 : 0 : }
549 : :
550 : 0 : void snd_hdac_setup_channel_mapping(struct hdac_chmap *chmap,
551 : : hda_nid_t pin_nid, bool non_pcm, int ca,
552 : : int channels, unsigned char *map,
553 : : bool chmap_set)
554 : : {
555 [ # # ]: 0 : if (!non_pcm && chmap_set) {
556 : 0 : hdmi_manual_setup_channel_mapping(chmap, pin_nid,
557 : : channels, map, ca);
558 : : } else {
559 : 0 : hdmi_std_setup_channel_mapping(chmap, pin_nid, non_pcm, ca);
560 : 0 : hdmi_setup_fake_chmap(map, ca);
561 : : }
562 : :
563 : 0 : hdmi_debug_channel_mapping(chmap, pin_nid);
564 : 0 : }
565 : : EXPORT_SYMBOL_GPL(snd_hdac_setup_channel_mapping);
566 : :
567 : 0 : int snd_hdac_get_active_channels(int ca)
568 : : {
569 : 0 : int ordered_ca = get_channel_allocation_order(ca);
570 : :
571 : : /* Add sanity check to pass klockwork check.
572 : : * This should never happen.
573 : : */
574 [ # # ]: 0 : if (ordered_ca >= ARRAY_SIZE(channel_allocations))
575 : 0 : ordered_ca = 0;
576 : :
577 : 0 : return channel_allocations[ordered_ca].channels;
578 : : }
579 : : EXPORT_SYMBOL_GPL(snd_hdac_get_active_channels);
580 : :
581 : 0 : struct hdac_cea_channel_speaker_allocation *snd_hdac_get_ch_alloc_from_ca(int ca)
582 : : {
583 : 0 : return &channel_allocations[get_channel_allocation_order(ca)];
584 : : }
585 : : EXPORT_SYMBOL_GPL(snd_hdac_get_ch_alloc_from_ca);
586 : :
587 : 0 : int snd_hdac_channel_allocation(struct hdac_device *hdac, int spk_alloc,
588 : : int channels, bool chmap_set, bool non_pcm, unsigned char *map)
589 : : {
590 : 0 : int ca;
591 : :
592 [ # # ]: 0 : if (!non_pcm && chmap_set)
593 : 0 : ca = hdmi_manual_channel_allocation(channels, map);
594 : : else
595 : 0 : ca = hdmi_channel_allocation_spk_alloc_blk(hdac,
596 : : spk_alloc, channels);
597 : :
598 : 0 : if (ca < 0)
599 : : ca = 0;
600 : :
601 : 0 : return ca;
602 : : }
603 : : EXPORT_SYMBOL_GPL(snd_hdac_channel_allocation);
604 : :
605 : : /*
606 : : * ALSA API channel-map control callbacks
607 : : */
608 : 0 : static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol,
609 : : struct snd_ctl_elem_info *uinfo)
610 : : {
611 : 0 : struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
612 : 0 : struct hdac_chmap *chmap = info->private_data;
613 : :
614 : 0 : uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
615 : 0 : uinfo->count = chmap->channels_max;
616 : 0 : uinfo->value.integer.min = 0;
617 : 0 : uinfo->value.integer.max = SNDRV_CHMAP_LAST;
618 : 0 : return 0;
619 : : }
620 : :
621 : 0 : static int hdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap,
622 : : struct hdac_cea_channel_speaker_allocation *cap, int channels)
623 : : {
624 : : /* If the speaker allocation matches the channel count, it is OK.*/
625 [ # # ]: 0 : if (cap->channels != channels)
626 : 0 : return -1;
627 : :
628 : : /* all channels are remappable freely */
629 : : return SNDRV_CTL_TLVT_CHMAP_VAR;
630 : : }
631 : :
632 : 0 : static void hdmi_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap,
633 : : struct hdac_cea_channel_speaker_allocation *cap,
634 : : unsigned int *chmap, int channels)
635 : : {
636 : 0 : int count = 0;
637 : 0 : int c;
638 : :
639 [ # # ]: 0 : for (c = 7; c >= 0; c--) {
640 : 0 : int spk = cap->speakers[c];
641 : :
642 [ # # ]: 0 : if (!spk)
643 : 0 : continue;
644 : :
645 : 0 : chmap[count++] = snd_hdac_spk_to_chmap(spk);
646 : : }
647 : :
648 [ # # ]: 0 : WARN_ON(count != channels);
649 : 0 : }
650 : :
651 : 0 : static int spk_mask_from_spk_alloc(int spk_alloc)
652 : : {
653 : 0 : int i;
654 : 0 : int spk_mask = eld_speaker_allocation_bits[0];
655 : :
656 [ # # ]: 0 : for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) {
657 [ # # ]: 0 : if (spk_alloc & (1 << i))
658 : 0 : spk_mask |= eld_speaker_allocation_bits[i];
659 : : }
660 : :
661 : 0 : return spk_mask;
662 : : }
663 : :
664 : 0 : static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
665 : : unsigned int size, unsigned int __user *tlv)
666 : : {
667 : 0 : struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
668 : 0 : struct hdac_chmap *chmap = info->private_data;
669 : 0 : int pcm_idx = kcontrol->private_value;
670 : 0 : unsigned int __user *dst;
671 : 0 : int chs, count = 0;
672 : 0 : unsigned long max_chs;
673 : 0 : int type;
674 : 0 : int spk_alloc, spk_mask;
675 : :
676 [ # # ]: 0 : if (size < 8)
677 : : return -ENOMEM;
678 [ # # ]: 0 : if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv))
679 : : return -EFAULT;
680 : 0 : size -= 8;
681 : 0 : dst = tlv + 2;
682 : :
683 : 0 : spk_alloc = chmap->ops.get_spk_alloc(chmap->hdac, pcm_idx);
684 : 0 : spk_mask = spk_mask_from_spk_alloc(spk_alloc);
685 : :
686 [ # # ]: 0 : max_chs = hweight_long(spk_mask);
687 : :
688 [ # # ]: 0 : for (chs = 2; chs <= max_chs; chs++) {
689 : : int i;
690 : : struct hdac_cea_channel_speaker_allocation *cap;
691 : :
692 : : cap = channel_allocations;
693 [ # # ]: 0 : for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) {
694 : 0 : int chs_bytes = chs * 4;
695 : 0 : unsigned int tlv_chmap[8];
696 : :
697 [ # # ]: 0 : if (cap->channels != chs)
698 : 0 : continue;
699 : :
700 [ # # ]: 0 : if (!(cap->spk_mask == (spk_mask & cap->spk_mask)))
701 : 0 : continue;
702 : :
703 : 0 : type = chmap->ops.chmap_cea_alloc_validate_get_type(
704 : : chmap, cap, chs);
705 [ # # ]: 0 : if (type < 0)
706 : 0 : return -ENODEV;
707 [ # # ]: 0 : if (size < 8)
708 : : return -ENOMEM;
709 : :
710 [ # # ]: 0 : if (put_user(type, dst) ||
711 [ # # ]: 0 : put_user(chs_bytes, dst + 1))
712 : : return -EFAULT;
713 : :
714 : 0 : dst += 2;
715 : 0 : size -= 8;
716 : 0 : count += 8;
717 : :
718 [ # # ]: 0 : if (size < chs_bytes)
719 : : return -ENOMEM;
720 : :
721 : 0 : size -= chs_bytes;
722 : 0 : count += chs_bytes;
723 : 0 : chmap->ops.cea_alloc_to_tlv_chmap(chmap, cap,
724 : : tlv_chmap, chs);
725 : :
726 [ # # # # ]: 0 : if (copy_to_user(dst, tlv_chmap, chs_bytes))
727 : : return -EFAULT;
728 : 0 : dst += chs;
729 : : }
730 : : }
731 : :
732 [ # # ]: 0 : if (put_user(count, tlv + 1))
733 : 0 : return -EFAULT;
734 : :
735 : : return 0;
736 : : }
737 : :
738 : 0 : static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol,
739 : : struct snd_ctl_elem_value *ucontrol)
740 : : {
741 : 0 : struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
742 : 0 : struct hdac_chmap *chmap = info->private_data;
743 : 0 : int pcm_idx = kcontrol->private_value;
744 : 0 : unsigned char pcm_chmap[8];
745 : 0 : int i;
746 : :
747 : 0 : memset(pcm_chmap, 0, sizeof(pcm_chmap));
748 : 0 : chmap->ops.get_chmap(chmap->hdac, pcm_idx, pcm_chmap);
749 : :
750 [ # # ]: 0 : for (i = 0; i < ARRAY_SIZE(pcm_chmap); i++)
751 : 0 : ucontrol->value.integer.value[i] = pcm_chmap[i];
752 : :
753 : 0 : return 0;
754 : : }
755 : :
756 : 0 : static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
757 : : struct snd_ctl_elem_value *ucontrol)
758 : : {
759 : 0 : struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
760 : 0 : struct hdac_chmap *hchmap = info->private_data;
761 : 0 : int pcm_idx = kcontrol->private_value;
762 : 0 : unsigned int ctl_idx;
763 : 0 : struct snd_pcm_substream *substream;
764 : 0 : unsigned char chmap[8], per_pin_chmap[8];
765 : 0 : int i, err, ca, prepared = 0;
766 : :
767 : : /* No monitor is connected in dyn_pcm_assign.
768 : : * It's invalid to setup the chmap
769 : : */
770 [ # # ]: 0 : if (!hchmap->ops.is_pcm_attached(hchmap->hdac, pcm_idx))
771 : : return 0;
772 : :
773 : 0 : ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
774 : 0 : substream = snd_pcm_chmap_substream(info, ctl_idx);
775 [ # # # # ]: 0 : if (!substream || !substream->runtime)
776 : : return 0; /* just for avoiding error from alsactl restore */
777 [ # # # ]: 0 : switch (substream->runtime->status->state) {
778 : : case SNDRV_PCM_STATE_OPEN:
779 : : case SNDRV_PCM_STATE_SETUP:
780 : : break;
781 : 0 : case SNDRV_PCM_STATE_PREPARED:
782 : 0 : prepared = 1;
783 : 0 : break;
784 : : default:
785 : : return -EBUSY;
786 : : }
787 : 0 : memset(chmap, 0, sizeof(chmap));
788 [ # # ]: 0 : for (i = 0; i < ARRAY_SIZE(chmap); i++)
789 : 0 : chmap[i] = ucontrol->value.integer.value[i];
790 : :
791 : 0 : hchmap->ops.get_chmap(hchmap->hdac, pcm_idx, per_pin_chmap);
792 [ # # ]: 0 : if (!memcmp(chmap, per_pin_chmap, sizeof(chmap)))
793 : : return 0;
794 : 0 : ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap);
795 [ # # ]: 0 : if (ca < 0)
796 : : return -EINVAL;
797 [ # # ]: 0 : if (hchmap->ops.chmap_validate) {
798 : 0 : err = hchmap->ops.chmap_validate(hchmap, ca,
799 : : ARRAY_SIZE(chmap), chmap);
800 [ # # ]: 0 : if (err)
801 : : return err;
802 : : }
803 : :
804 : 0 : hchmap->ops.set_chmap(hchmap->hdac, pcm_idx, chmap, prepared);
805 : :
806 : 0 : return 0;
807 : : }
808 : :
809 : : static const struct hdac_chmap_ops chmap_ops = {
810 : : .chmap_cea_alloc_validate_get_type = hdmi_chmap_cea_alloc_validate_get_type,
811 : : .cea_alloc_to_tlv_chmap = hdmi_cea_alloc_to_tlv_chmap,
812 : : .pin_get_slot_channel = hdmi_pin_get_slot_channel,
813 : : .pin_set_slot_channel = hdmi_pin_set_slot_channel,
814 : : .set_channel_count = hdmi_set_channel_count,
815 : : };
816 : :
817 : 0 : void snd_hdac_register_chmap_ops(struct hdac_device *hdac,
818 : : struct hdac_chmap *chmap)
819 : : {
820 : 0 : chmap->ops = chmap_ops;
821 : 0 : chmap->hdac = hdac;
822 : 0 : init_channel_allocations();
823 : 0 : }
824 : : EXPORT_SYMBOL_GPL(snd_hdac_register_chmap_ops);
825 : :
826 : 0 : int snd_hdac_add_chmap_ctls(struct snd_pcm *pcm, int pcm_idx,
827 : : struct hdac_chmap *hchmap)
828 : : {
829 : 0 : struct snd_pcm_chmap *chmap;
830 : 0 : struct snd_kcontrol *kctl;
831 : 0 : int err, i;
832 : :
833 : 0 : err = snd_pcm_add_chmap_ctls(pcm,
834 : : SNDRV_PCM_STREAM_PLAYBACK,
835 : : NULL, 0, pcm_idx, &chmap);
836 [ # # ]: 0 : if (err < 0)
837 : : return err;
838 : : /* override handlers */
839 : 0 : chmap->private_data = hchmap;
840 : 0 : kctl = chmap->kctl;
841 [ # # ]: 0 : for (i = 0; i < kctl->count; i++)
842 : 0 : kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
843 : 0 : kctl->info = hdmi_chmap_ctl_info;
844 : 0 : kctl->get = hdmi_chmap_ctl_get;
845 : 0 : kctl->put = hdmi_chmap_ctl_put;
846 : 0 : kctl->tlv.c = hdmi_chmap_ctl_tlv;
847 : :
848 : 0 : return 0;
849 : : }
850 : : EXPORT_SYMBOL_GPL(snd_hdac_add_chmap_ctls);
|