Commit | Line | Data |
---|---|---|
457c8996 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
739ffee9 SP |
2 | /* |
3 | * HDMI Channel map support helpers | |
4 | */ | |
5 | ||
2f6e8a85 SP |
6 | #include <linux/module.h> |
7 | #include <sound/control.h> | |
8 | #include <sound/tlv.h> | |
739ffee9 SP |
9 | #include <sound/hda_chmap.h> |
10 | ||
2f6e8a85 SP |
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 | */ | |
bf82326f | 62 | static const int eld_speaker_allocation_bits[] = { |
2f6e8a85 SP |
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 | ||
739ffee9 SP |
182 | static int hdmi_pin_set_slot_channel(struct hdac_device *codec, |
183 | hda_nid_t pin_nid, int asp_slot, int channel) | |
184 | { | |
185 | return snd_hdac_codec_write(codec, pin_nid, 0, | |
186 | AC_VERB_SET_HDMI_CHAN_SLOT, | |
187 | (channel << 4) | asp_slot); | |
188 | } | |
189 | ||
190 | static int hdmi_pin_get_slot_channel(struct hdac_device *codec, | |
191 | hda_nid_t pin_nid, int asp_slot) | |
192 | { | |
193 | return (snd_hdac_codec_read(codec, pin_nid, 0, | |
194 | AC_VERB_GET_HDMI_CHAN_SLOT, | |
195 | asp_slot) & 0xf0) >> 4; | |
196 | } | |
197 | ||
198 | static int hdmi_get_channel_count(struct hdac_device *codec, hda_nid_t cvt_nid) | |
199 | { | |
200 | return 1 + snd_hdac_codec_read(codec, cvt_nid, 0, | |
201 | AC_VERB_GET_CVT_CHAN_COUNT, 0); | |
202 | } | |
203 | ||
204 | static void hdmi_set_channel_count(struct hdac_device *codec, | |
205 | hda_nid_t cvt_nid, int chs) | |
206 | { | |
207 | if (chs != hdmi_get_channel_count(codec, cvt_nid)) | |
208 | snd_hdac_codec_write(codec, cvt_nid, 0, | |
209 | AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); | |
210 | } | |
211 | ||
2f6e8a85 SP |
212 | /* |
213 | * Channel mapping routines | |
214 | */ | |
215 | ||
216 | /* | |
217 | * Compute derived values in channel_allocations[]. | |
218 | */ | |
219 | static void init_channel_allocations(void) | |
220 | { | |
221 | int i, j; | |
222 | struct hdac_cea_channel_speaker_allocation *p; | |
223 | ||
224 | for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { | |
225 | p = channel_allocations + i; | |
226 | p->channels = 0; | |
227 | p->spk_mask = 0; | |
228 | for (j = 0; j < ARRAY_SIZE(p->speakers); j++) | |
229 | if (p->speakers[j]) { | |
230 | p->channels++; | |
231 | p->spk_mask |= p->speakers[j]; | |
232 | } | |
233 | } | |
234 | } | |
235 | ||
236 | static int get_channel_allocation_order(int ca) | |
237 | { | |
238 | int i; | |
239 | ||
240 | for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { | |
241 | if (channel_allocations[i].ca_index == ca) | |
242 | break; | |
243 | } | |
244 | return i; | |
245 | } | |
246 | ||
bb63f726 | 247 | void snd_hdac_print_channel_allocation(int spk_alloc, char *buf, int buflen) |
2f6e8a85 SP |
248 | { |
249 | int i, j; | |
250 | ||
251 | for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) { | |
252 | if (spk_alloc & (1 << i)) | |
44eeb081 | 253 | j += scnprintf(buf + j, buflen - j, " %s", |
2f6e8a85 SP |
254 | cea_speaker_allocation_names[i]); |
255 | } | |
256 | buf[j] = '\0'; /* necessary when j == 0 */ | |
257 | } | |
bb63f726 | 258 | EXPORT_SYMBOL_GPL(snd_hdac_print_channel_allocation); |
2f6e8a85 SP |
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 | ||
bb63f726 | 316 | snd_hdac_print_channel_allocation(spk_alloc, buf, sizeof(buf)); |
2f6e8a85 SP |
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 | 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 | } | |
338 | ||
339 | 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 | struct hdac_cea_channel_speaker_allocation *ch_alloc; | |
345 | int i; | |
346 | int err; | |
347 | int order; | |
348 | int non_pcm_mapping[8]; | |
349 | ||
350 | order = get_channel_allocation_order(ca); | |
351 | ch_alloc = &channel_allocations[order]; | |
352 | ||
353 | if (hdmi_channel_mapping[ca][1] == 0) { | |
354 | int hdmi_slot = 0; | |
355 | /* fill actual channel mappings in ALSA channel (i) order */ | |
356 | for (i = 0; i < ch_alloc->channels; i++) { | |
960a581e LY |
357 | while (!WARN_ON(hdmi_slot >= 8) && |
358 | !ch_alloc->speakers[7 - hdmi_slot]) | |
2f6e8a85 SP |
359 | hdmi_slot++; /* skip zero slots */ |
360 | ||
361 | hdmi_channel_mapping[ca][i] = (i << 4) | hdmi_slot++; | |
362 | } | |
363 | /* fill the rest of the slots with ALSA channel 0xf */ | |
364 | for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) | |
365 | if (!ch_alloc->speakers[7 - hdmi_slot]) | |
366 | hdmi_channel_mapping[ca][i++] = (0xf << 4) | hdmi_slot; | |
367 | } | |
368 | ||
369 | if (non_pcm) { | |
370 | for (i = 0; i < ch_alloc->channels; i++) | |
371 | non_pcm_mapping[i] = (i << 4) | i; | |
372 | for (; i < 8; i++) | |
373 | non_pcm_mapping[i] = (0xf << 4) | i; | |
374 | } | |
375 | ||
376 | for (i = 0; i < 8; i++) { | |
377 | int slotsetup = non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i]; | |
378 | int hdmi_slot = slotsetup & 0x0f; | |
379 | int channel = (slotsetup & 0xf0) >> 4; | |
380 | ||
381 | err = chmap->ops.pin_set_slot_channel(chmap->hdac, | |
382 | pin_nid, hdmi_slot, channel); | |
383 | if (err) { | |
384 | dev_dbg(&chmap->hdac->dev, "HDMI: channel mapping failed\n"); | |
385 | break; | |
386 | } | |
387 | } | |
388 | } | |
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 */ | |
bb63f726 | 417 | int snd_hdac_chmap_to_spk_mask(unsigned char c) |
2f6e8a85 SP |
418 | { |
419 | struct channel_map_table *t = map_tables; | |
420 | ||
421 | for (; t->map; t++) { | |
422 | if (t->map == c) | |
423 | return t->spk_mask; | |
424 | } | |
425 | return 0; | |
426 | } | |
bb63f726 | 427 | EXPORT_SYMBOL_GPL(snd_hdac_chmap_to_spk_mask); |
2f6e8a85 SP |
428 | |
429 | /* from ALSA API channel position to CEA slot */ | |
430 | static int to_cea_slot(int ordered_ca, unsigned char pos) | |
431 | { | |
bb63f726 | 432 | int mask = snd_hdac_chmap_to_spk_mask(pos); |
2f6e8a85 SP |
433 | int i; |
434 | ||
960a581e LY |
435 | /* Add sanity check to pass klockwork check. |
436 | * This should never happen. | |
437 | */ | |
438 | if (ordered_ca >= ARRAY_SIZE(channel_allocations)) | |
439 | return -1; | |
440 | ||
2f6e8a85 SP |
441 | if (mask) { |
442 | for (i = 0; i < 8; i++) { | |
443 | 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 */ | |
bb63f726 | 452 | int snd_hdac_spk_to_chmap(int spk) |
2f6e8a85 SP |
453 | { |
454 | struct channel_map_table *t = map_tables; | |
455 | ||
456 | for (; t->map; t++) { | |
457 | if (t->spk_mask == spk) | |
458 | return t->map; | |
459 | } | |
460 | return 0; | |
461 | } | |
bb63f726 | 462 | EXPORT_SYMBOL_GPL(snd_hdac_spk_to_chmap); |
2f6e8a85 SP |
463 | |
464 | /* from CEA slot to ALSA API channel position */ | |
465 | static int from_cea_slot(int ordered_ca, unsigned char slot) | |
466 | { | |
960a581e LY |
467 | int mask; |
468 | ||
469 | /* Add sanity check to pass klockwork check. | |
470 | * This should never happen. | |
471 | */ | |
472 | if (slot >= 8) | |
473 | return 0; | |
474 | ||
475 | mask = channel_allocations[ordered_ca].speakers[7 - slot]; | |
2f6e8a85 | 476 | |
bb63f726 | 477 | return snd_hdac_spk_to_chmap(mask); |
2f6e8a85 SP |
478 | } |
479 | ||
480 | /* get the CA index corresponding to the given ALSA API channel map */ | |
481 | static int hdmi_manual_channel_allocation(int chs, unsigned char *map) | |
482 | { | |
483 | int i, spks = 0, spk_mask = 0; | |
484 | ||
485 | for (i = 0; i < chs; i++) { | |
bb63f726 | 486 | int mask = snd_hdac_chmap_to_spk_mask(map[i]); |
2f6e8a85 SP |
487 | |
488 | if (mask) { | |
489 | spk_mask |= mask; | |
490 | spks++; | |
491 | } | |
492 | } | |
493 | ||
494 | for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { | |
495 | if ((chs == channel_allocations[i].channels || | |
496 | spks == channel_allocations[i].channels) && | |
497 | (spk_mask & channel_allocations[i].spk_mask) == | |
498 | channel_allocations[i].spk_mask) | |
499 | 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 | 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 | int ordered_ca = get_channel_allocation_order(ca); | |
511 | int alsa_pos, hdmi_slot; | |
512 | int assignments[8] = {[0 ... 7] = 0xf}; | |
513 | ||
514 | for (alsa_pos = 0; alsa_pos < chs; alsa_pos++) { | |
515 | ||
516 | hdmi_slot = to_cea_slot(ordered_ca, map[alsa_pos]); | |
517 | ||
518 | if (hdmi_slot < 0) | |
519 | continue; /* unassigned channel */ | |
520 | ||
521 | assignments[hdmi_slot] = alsa_pos; | |
522 | } | |
523 | ||
524 | for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) { | |
525 | int err; | |
526 | ||
527 | err = chmap->ops.pin_set_slot_channel(chmap->hdac, | |
528 | pin_nid, hdmi_slot, assignments[hdmi_slot]); | |
529 | if (err) | |
530 | return -EINVAL; | |
531 | } | |
532 | return 0; | |
533 | } | |
534 | ||
535 | /* store ALSA API channel map from the current default map */ | |
536 | static void hdmi_setup_fake_chmap(unsigned char *map, int ca) | |
537 | { | |
538 | int i; | |
539 | int ordered_ca = get_channel_allocation_order(ca); | |
540 | ||
541 | for (i = 0; i < 8; i++) { | |
960a581e LY |
542 | if (ordered_ca < ARRAY_SIZE(channel_allocations) && |
543 | i < channel_allocations[ordered_ca].channels) | |
2f6e8a85 SP |
544 | map[i] = from_cea_slot(ordered_ca, hdmi_channel_mapping[ca][i] & 0x0f); |
545 | else | |
546 | map[i] = 0; | |
547 | } | |
548 | } | |
549 | ||
bb63f726 | 550 | void snd_hdac_setup_channel_mapping(struct hdac_chmap *chmap, |
2f6e8a85 SP |
551 | hda_nid_t pin_nid, bool non_pcm, int ca, |
552 | int channels, unsigned char *map, | |
553 | bool chmap_set) | |
554 | { | |
555 | if (!non_pcm && chmap_set) { | |
556 | hdmi_manual_setup_channel_mapping(chmap, pin_nid, | |
557 | channels, map, ca); | |
558 | } else { | |
559 | hdmi_std_setup_channel_mapping(chmap, pin_nid, non_pcm, ca); | |
560 | hdmi_setup_fake_chmap(map, ca); | |
561 | } | |
562 | ||
563 | hdmi_debug_channel_mapping(chmap, pin_nid); | |
564 | } | |
bb63f726 | 565 | EXPORT_SYMBOL_GPL(snd_hdac_setup_channel_mapping); |
2f6e8a85 | 566 | |
bb63f726 | 567 | int snd_hdac_get_active_channels(int ca) |
2f6e8a85 SP |
568 | { |
569 | int ordered_ca = get_channel_allocation_order(ca); | |
570 | ||
960a581e LY |
571 | /* Add sanity check to pass klockwork check. |
572 | * This should never happen. | |
573 | */ | |
574 | if (ordered_ca >= ARRAY_SIZE(channel_allocations)) | |
575 | ordered_ca = 0; | |
576 | ||
2f6e8a85 SP |
577 | return channel_allocations[ordered_ca].channels; |
578 | } | |
bb63f726 | 579 | EXPORT_SYMBOL_GPL(snd_hdac_get_active_channels); |
2f6e8a85 | 580 | |
bb63f726 | 581 | struct hdac_cea_channel_speaker_allocation *snd_hdac_get_ch_alloc_from_ca(int ca) |
2f6e8a85 SP |
582 | { |
583 | return &channel_allocations[get_channel_allocation_order(ca)]; | |
584 | } | |
bb63f726 | 585 | EXPORT_SYMBOL_GPL(snd_hdac_get_ch_alloc_from_ca); |
2f6e8a85 | 586 | |
bb63f726 | 587 | int snd_hdac_channel_allocation(struct hdac_device *hdac, int spk_alloc, |
2f6e8a85 SP |
588 | int channels, bool chmap_set, bool non_pcm, unsigned char *map) |
589 | { | |
590 | int ca; | |
591 | ||
592 | if (!non_pcm && chmap_set) | |
593 | ca = hdmi_manual_channel_allocation(channels, map); | |
594 | else | |
595 | ca = hdmi_channel_allocation_spk_alloc_blk(hdac, | |
596 | spk_alloc, channels); | |
597 | ||
598 | if (ca < 0) | |
599 | ca = 0; | |
600 | ||
601 | return ca; | |
602 | } | |
bb63f726 | 603 | EXPORT_SYMBOL_GPL(snd_hdac_channel_allocation); |
2f6e8a85 SP |
604 | |
605 | /* | |
606 | * ALSA API channel-map control callbacks | |
607 | */ | |
608 | static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol, | |
609 | struct snd_ctl_elem_info *uinfo) | |
610 | { | |
611 | struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); | |
612 | struct hdac_chmap *chmap = info->private_data; | |
613 | ||
614 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | |
615 | uinfo->count = chmap->channels_max; | |
616 | uinfo->value.integer.min = 0; | |
617 | uinfo->value.integer.max = SNDRV_CHMAP_LAST; | |
618 | return 0; | |
619 | } | |
620 | ||
621 | 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 | if (cap->channels != channels) | |
626 | return -1; | |
627 | ||
628 | /* all channels are remappable freely */ | |
629 | return SNDRV_CTL_TLVT_CHMAP_VAR; | |
630 | } | |
631 | ||
632 | 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 | int count = 0; | |
637 | int c; | |
638 | ||
639 | for (c = 7; c >= 0; c--) { | |
640 | int spk = cap->speakers[c]; | |
641 | ||
642 | if (!spk) | |
643 | continue; | |
644 | ||
bb63f726 | 645 | chmap[count++] = snd_hdac_spk_to_chmap(spk); |
2f6e8a85 SP |
646 | } |
647 | ||
648 | WARN_ON(count != channels); | |
649 | } | |
650 | ||
44fde3b8 SP |
651 | static int spk_mask_from_spk_alloc(int spk_alloc) |
652 | { | |
653 | int i; | |
654 | int spk_mask = eld_speaker_allocation_bits[0]; | |
655 | ||
656 | for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) { | |
657 | if (spk_alloc & (1 << i)) | |
658 | spk_mask |= eld_speaker_allocation_bits[i]; | |
659 | } | |
660 | ||
661 | return spk_mask; | |
662 | } | |
663 | ||
2f6e8a85 SP |
664 | static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, |
665 | unsigned int size, unsigned int __user *tlv) | |
666 | { | |
667 | struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); | |
668 | struct hdac_chmap *chmap = info->private_data; | |
44fde3b8 | 669 | int pcm_idx = kcontrol->private_value; |
2f6e8a85 SP |
670 | unsigned int __user *dst; |
671 | int chs, count = 0; | |
44fde3b8 SP |
672 | unsigned long max_chs; |
673 | int type; | |
674 | int spk_alloc, spk_mask; | |
2f6e8a85 SP |
675 | |
676 | if (size < 8) | |
677 | return -ENOMEM; | |
678 | if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv)) | |
679 | return -EFAULT; | |
680 | size -= 8; | |
681 | dst = tlv + 2; | |
44fde3b8 SP |
682 | |
683 | spk_alloc = chmap->ops.get_spk_alloc(chmap->hdac, pcm_idx); | |
684 | spk_mask = spk_mask_from_spk_alloc(spk_alloc); | |
685 | ||
686 | max_chs = hweight_long(spk_mask); | |
687 | ||
688 | for (chs = 2; chs <= max_chs; chs++) { | |
2f6e8a85 SP |
689 | int i; |
690 | struct hdac_cea_channel_speaker_allocation *cap; | |
691 | ||
692 | cap = channel_allocations; | |
693 | for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) { | |
694 | int chs_bytes = chs * 4; | |
2f6e8a85 SP |
695 | unsigned int tlv_chmap[8]; |
696 | ||
44fde3b8 SP |
697 | if (cap->channels != chs) |
698 | continue; | |
699 | ||
700 | if (!(cap->spk_mask == (spk_mask & cap->spk_mask))) | |
2f6e8a85 | 701 | continue; |
44fde3b8 SP |
702 | |
703 | type = chmap->ops.chmap_cea_alloc_validate_get_type( | |
704 | chmap, cap, chs); | |
705 | if (type < 0) | |
706 | return -ENODEV; | |
2f6e8a85 SP |
707 | if (size < 8) |
708 | return -ENOMEM; | |
44fde3b8 | 709 | |
2f6e8a85 SP |
710 | if (put_user(type, dst) || |
711 | put_user(chs_bytes, dst + 1)) | |
712 | return -EFAULT; | |
44fde3b8 | 713 | |
2f6e8a85 SP |
714 | dst += 2; |
715 | size -= 8; | |
716 | count += 8; | |
44fde3b8 | 717 | |
2f6e8a85 SP |
718 | if (size < chs_bytes) |
719 | return -ENOMEM; | |
44fde3b8 | 720 | |
2f6e8a85 SP |
721 | size -= chs_bytes; |
722 | count += chs_bytes; | |
723 | chmap->ops.cea_alloc_to_tlv_chmap(chmap, cap, | |
724 | tlv_chmap, chs); | |
44fde3b8 | 725 | |
2f6e8a85 SP |
726 | if (copy_to_user(dst, tlv_chmap, chs_bytes)) |
727 | return -EFAULT; | |
728 | dst += chs; | |
729 | } | |
730 | } | |
44fde3b8 | 731 | |
2f6e8a85 SP |
732 | if (put_user(count, tlv + 1)) |
733 | return -EFAULT; | |
44fde3b8 | 734 | |
2f6e8a85 SP |
735 | return 0; |
736 | } | |
737 | ||
738 | static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol, | |
739 | struct snd_ctl_elem_value *ucontrol) | |
740 | { | |
741 | struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); | |
742 | struct hdac_chmap *chmap = info->private_data; | |
743 | int pcm_idx = kcontrol->private_value; | |
744 | unsigned char pcm_chmap[8]; | |
745 | int i; | |
746 | ||
747 | memset(pcm_chmap, 0, sizeof(pcm_chmap)); | |
748 | chmap->ops.get_chmap(chmap->hdac, pcm_idx, pcm_chmap); | |
749 | ||
c2432466 | 750 | for (i = 0; i < ARRAY_SIZE(pcm_chmap); i++) |
2f6e8a85 SP |
751 | ucontrol->value.integer.value[i] = pcm_chmap[i]; |
752 | ||
753 | return 0; | |
754 | } | |
755 | ||
756 | static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, | |
757 | struct snd_ctl_elem_value *ucontrol) | |
758 | { | |
759 | struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); | |
760 | struct hdac_chmap *hchmap = info->private_data; | |
761 | int pcm_idx = kcontrol->private_value; | |
762 | unsigned int ctl_idx; | |
763 | struct snd_pcm_substream *substream; | |
764 | unsigned char chmap[8], per_pin_chmap[8]; | |
765 | 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 | if (!hchmap->ops.is_pcm_attached(hchmap->hdac, pcm_idx)) | |
771 | return 0; | |
772 | ||
773 | ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); | |
774 | substream = snd_pcm_chmap_substream(info, ctl_idx); | |
775 | if (!substream || !substream->runtime) | |
776 | return 0; /* just for avoiding error from alsactl restore */ | |
38d8be5d | 777 | switch (substream->runtime->state) { |
2f6e8a85 SP |
778 | case SNDRV_PCM_STATE_OPEN: |
779 | case SNDRV_PCM_STATE_SETUP: | |
780 | break; | |
781 | case SNDRV_PCM_STATE_PREPARED: | |
782 | prepared = 1; | |
783 | break; | |
784 | default: | |
785 | return -EBUSY; | |
786 | } | |
787 | memset(chmap, 0, sizeof(chmap)); | |
788 | for (i = 0; i < ARRAY_SIZE(chmap); i++) | |
789 | chmap[i] = ucontrol->value.integer.value[i]; | |
790 | ||
791 | hchmap->ops.get_chmap(hchmap->hdac, pcm_idx, per_pin_chmap); | |
792 | if (!memcmp(chmap, per_pin_chmap, sizeof(chmap))) | |
793 | return 0; | |
794 | ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap); | |
795 | if (ca < 0) | |
796 | return -EINVAL; | |
797 | if (hchmap->ops.chmap_validate) { | |
798 | err = hchmap->ops.chmap_validate(hchmap, ca, | |
799 | ARRAY_SIZE(chmap), chmap); | |
800 | if (err) | |
801 | return err; | |
802 | } | |
803 | ||
804 | hchmap->ops.set_chmap(hchmap->hdac, pcm_idx, chmap, prepared); | |
805 | ||
806 | return 0; | |
807 | } | |
808 | ||
739ffee9 | 809 | static const struct hdac_chmap_ops chmap_ops = { |
2f6e8a85 SP |
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, | |
739ffee9 SP |
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 | void snd_hdac_register_chmap_ops(struct hdac_device *hdac, | |
818 | struct hdac_chmap *chmap) | |
819 | { | |
820 | chmap->ops = chmap_ops; | |
821 | chmap->hdac = hdac; | |
2f6e8a85 | 822 | init_channel_allocations(); |
739ffee9 SP |
823 | } |
824 | EXPORT_SYMBOL_GPL(snd_hdac_register_chmap_ops); | |
2f6e8a85 SP |
825 | |
826 | int snd_hdac_add_chmap_ctls(struct snd_pcm *pcm, int pcm_idx, | |
827 | struct hdac_chmap *hchmap) | |
828 | { | |
829 | struct snd_pcm_chmap *chmap; | |
830 | struct snd_kcontrol *kctl; | |
831 | int err, i; | |
832 | ||
833 | err = snd_pcm_add_chmap_ctls(pcm, | |
834 | SNDRV_PCM_STREAM_PLAYBACK, | |
835 | NULL, 0, pcm_idx, &chmap); | |
836 | if (err < 0) | |
837 | return err; | |
838 | /* override handlers */ | |
839 | chmap->private_data = hchmap; | |
840 | kctl = chmap->kctl; | |
841 | for (i = 0; i < kctl->count; i++) | |
842 | kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE; | |
843 | kctl->info = hdmi_chmap_ctl_info; | |
844 | kctl->get = hdmi_chmap_ctl_get; | |
845 | kctl->put = hdmi_chmap_ctl_put; | |
846 | kctl->tlv.c = hdmi_chmap_ctl_tlv; | |
847 | ||
848 | return 0; | |
849 | } | |
850 | EXPORT_SYMBOL_GPL(snd_hdac_add_chmap_ctls); |