diff options
author | Wu Fengguang <wfg@linux.intel.com> | 2008-11-17 22:47:52 -0500 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2008-11-18 01:39:03 -0500 |
commit | 7f4a9f43427793bfe4d42e71f42e2b551bcfe354 (patch) | |
tree | 32fac4e6b2c84e79231345ed5dc6f7c76c37a6cb /sound/pci/hda/hda_eld.c | |
parent | 33deeca3bb6a945677d70876ea9d044fc5797eb3 (diff) |
ALSA: create hda_eld.c for ELD routines and proc interface
ELD handling routines can be shared by all HDMI codecs,
and they are large enough to make a standalone source file.
Signed-off-by: Wu Fengguang <wfg@linux.intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/hda/hda_eld.c')
-rw-r--r-- | sound/pci/hda/hda_eld.c | 454 |
1 files changed, 454 insertions, 0 deletions
diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c new file mode 100644 index 000000000000..a69a7e87d26a --- /dev/null +++ b/sound/pci/hda/hda_eld.c | |||
@@ -0,0 +1,454 @@ | |||
1 | /* | ||
2 | * Generic routines and proc interface for ELD(EDID Like Data) information | ||
3 | * | ||
4 | * Copyright(c) 2008 Intel Corporation. | ||
5 | * | ||
6 | * Authors: | ||
7 | * Wu Fengguang <wfg@linux.intel.com> | ||
8 | * | ||
9 | * This driver is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or | ||
12 | * (at your option) any later version. | ||
13 | * | ||
14 | * This driver is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
22 | */ | ||
23 | |||
24 | #include <linux/init.h> | ||
25 | #include <sound/core.h> | ||
26 | #include <asm/unaligned.h> | ||
27 | #include "hda_codec.h" | ||
28 | #include "hda_local.h" | ||
29 | |||
30 | enum eld_versions { | ||
31 | ELD_VER_CEA_861D = 2, | ||
32 | ELD_VER_PARTIAL = 31, | ||
33 | }; | ||
34 | |||
35 | static char *eld_versoin_names[32] = { | ||
36 | "reserved", | ||
37 | "reserved", | ||
38 | "CEA-861D or below", | ||
39 | [3 ... 30] = "reserved", | ||
40 | [31] = "partial" | ||
41 | }; | ||
42 | |||
43 | enum cea_edid_versions { | ||
44 | CEA_EDID_VER_NONE = 0, | ||
45 | CEA_EDID_VER_CEA861 = 1, | ||
46 | CEA_EDID_VER_CEA861A = 2, | ||
47 | CEA_EDID_VER_CEA861BCD = 3, | ||
48 | CEA_EDID_VER_RESERVED = 4, | ||
49 | }; | ||
50 | |||
51 | static char *cea_edid_version_names[8] = { | ||
52 | "no CEA EDID Timing Extension block present", | ||
53 | "CEA-861", | ||
54 | "CEA-861-A", | ||
55 | "CEA-861-B, C or D", | ||
56 | [4 ... 7] = "reserved" | ||
57 | }; | ||
58 | |||
59 | static char *cea_speaker_allocation_names[] = { | ||
60 | /* 0 */ "FL/FR", | ||
61 | /* 1 */ "LFE", | ||
62 | /* 2 */ "FC", | ||
63 | /* 3 */ "RL/RR", | ||
64 | /* 4 */ "RC", | ||
65 | /* 5 */ "FLC/FRC", | ||
66 | /* 6 */ "RLC/RRC", | ||
67 | /* 7 */ "FLW/FRW", | ||
68 | /* 8 */ "FLH/FRH", | ||
69 | /* 9 */ "TC", | ||
70 | /* 10 */ "FCH", | ||
71 | }; | ||
72 | |||
73 | static char *eld_connection_type_names[4] = { | ||
74 | "HDMI", | ||
75 | "Display Port", | ||
76 | "2-reserved", | ||
77 | "3-reserved" | ||
78 | }; | ||
79 | |||
80 | enum cea_audio_coding_types { | ||
81 | AUDIO_CODING_TYPE_REF_STREAM_HEADER = 0, | ||
82 | AUDIO_CODING_TYPE_LPCM = 1, | ||
83 | AUDIO_CODING_TYPE_AC3 = 2, | ||
84 | AUDIO_CODING_TYPE_MPEG1 = 3, | ||
85 | AUDIO_CODING_TYPE_MP3 = 4, | ||
86 | AUDIO_CODING_TYPE_MPEG2 = 5, | ||
87 | AUDIO_CODING_TYPE_AACLC = 6, | ||
88 | AUDIO_CODING_TYPE_DTS = 7, | ||
89 | AUDIO_CODING_TYPE_ATRAC = 8, | ||
90 | AUDIO_CODING_TYPE_SACD = 9, | ||
91 | AUDIO_CODING_TYPE_EAC3 = 10, | ||
92 | AUDIO_CODING_TYPE_DTS_HD = 11, | ||
93 | AUDIO_CODING_TYPE_MLP = 12, | ||
94 | AUDIO_CODING_TYPE_DST = 13, | ||
95 | AUDIO_CODING_TYPE_WMAPRO = 14, | ||
96 | AUDIO_CODING_TYPE_REF_CXT = 15, | ||
97 | /* also include valid xtypes below */ | ||
98 | AUDIO_CODING_TYPE_HE_AAC = 15, | ||
99 | AUDIO_CODING_TYPE_HE_AAC2 = 16, | ||
100 | AUDIO_CODING_TYPE_MPEG_SURROUND = 17, | ||
101 | }; | ||
102 | |||
103 | enum cea_audio_coding_xtypes { | ||
104 | AUDIO_CODING_XTYPE_HE_REF_CT = 0, | ||
105 | AUDIO_CODING_XTYPE_HE_AAC = 1, | ||
106 | AUDIO_CODING_XTYPE_HE_AAC2 = 2, | ||
107 | AUDIO_CODING_XTYPE_MPEG_SURROUND = 3, | ||
108 | AUDIO_CODING_XTYPE_FIRST_RESERVED = 4, | ||
109 | }; | ||
110 | |||
111 | static char *cea_audio_coding_type_names[] = { | ||
112 | /* 0 */ "undefined", | ||
113 | /* 1 */ "LPCM", | ||
114 | /* 2 */ "AC-3", | ||
115 | /* 3 */ "MPEG1", | ||
116 | /* 4 */ "MP3", | ||
117 | /* 5 */ "MPEG2", | ||
118 | /* 6 */ "AAC-LC", | ||
119 | /* 7 */ "DTS", | ||
120 | /* 8 */ "ATRAC", | ||
121 | /* 9 */ "DSD (1-bit audio)", | ||
122 | /* 10 */ "E-AC-3/DD+ (Dolby Digital Plus)", | ||
123 | /* 11 */ "DTS-HD", | ||
124 | /* 12 */ "MLP (Dolby TrueHD)", | ||
125 | /* 13 */ "DST", | ||
126 | /* 14 */ "WMAPro", | ||
127 | /* 15 */ "HE-AAC", | ||
128 | /* 16 */ "HE-AACv2", | ||
129 | /* 17 */ "MPEG Surround", | ||
130 | }; | ||
131 | |||
132 | /* | ||
133 | * The following two lists are shared between | ||
134 | * - HDMI audio InfoFrame (source to sink) | ||
135 | * - CEA E-EDID extension (sink to source) | ||
136 | */ | ||
137 | |||
138 | /* | ||
139 | * SS1:SS0 index => sample size | ||
140 | */ | ||
141 | static int cea_sample_sizes[4] = { | ||
142 | 0, /* 0: Refer to Stream Header */ | ||
143 | AC_SUPPCM_BITS_16, /* 1: 16 bits */ | ||
144 | AC_SUPPCM_BITS_20, /* 2: 20 bits */ | ||
145 | AC_SUPPCM_BITS_24, /* 3: 24 bits */ | ||
146 | }; | ||
147 | |||
148 | /* | ||
149 | * SF2:SF1:SF0 index => sampling frequency | ||
150 | */ | ||
151 | static int cea_sampling_frequencies[8] = { | ||
152 | 0, /* 0: Refer to Stream Header */ | ||
153 | SNDRV_PCM_RATE_32000, /* 1: 32000Hz */ | ||
154 | SNDRV_PCM_RATE_44100, /* 2: 44100Hz */ | ||
155 | SNDRV_PCM_RATE_48000, /* 3: 48000Hz */ | ||
156 | SNDRV_PCM_RATE_88200, /* 4: 88200Hz */ | ||
157 | SNDRV_PCM_RATE_96000, /* 5: 96000Hz */ | ||
158 | SNDRV_PCM_RATE_176400, /* 6: 176400Hz */ | ||
159 | SNDRV_PCM_RATE_192000, /* 7: 192000Hz */ | ||
160 | }; | ||
161 | |||
162 | static unsigned char hdmi_get_eld_byte(struct hda_codec *codec, hda_nid_t nid, | ||
163 | int byte_index) | ||
164 | { | ||
165 | unsigned int val; | ||
166 | |||
167 | val = snd_hda_codec_read(codec, nid, 0, | ||
168 | AC_VERB_GET_HDMI_ELDD, byte_index); | ||
169 | |||
170 | #ifdef BE_PARANOID | ||
171 | printk(KERN_INFO "ELD data byte %d: 0x%x\n", byte_index, val); | ||
172 | #endif | ||
173 | |||
174 | if ((val & AC_ELDD_ELD_VALID) == 0) { | ||
175 | snd_printd(KERN_INFO "Invalid ELD data byte %d\n", | ||
176 | byte_index); | ||
177 | val = 0; | ||
178 | } | ||
179 | |||
180 | return val & AC_ELDD_ELD_DATA; | ||
181 | } | ||
182 | |||
183 | #define GRAB_BITS(buf, byte, lowbit, bits) \ | ||
184 | ({ \ | ||
185 | BUILD_BUG_ON(lowbit > 7); \ | ||
186 | BUILD_BUG_ON(bits > 8); \ | ||
187 | BUILD_BUG_ON(bits <= 0); \ | ||
188 | \ | ||
189 | (buf[byte] >> (lowbit)) & ((1 << (bits)) - 1); \ | ||
190 | }) | ||
191 | |||
192 | static void hdmi_update_short_audio_desc(struct cea_sad *a, | ||
193 | const unsigned char *buf) | ||
194 | { | ||
195 | int i; | ||
196 | int val; | ||
197 | |||
198 | val = GRAB_BITS(buf, 1, 0, 7); | ||
199 | a->rates = 0; | ||
200 | for (i = 0; i < 7; i++) | ||
201 | if (val & (1 << i)) | ||
202 | a->rates |= cea_sampling_frequencies[i + 1]; | ||
203 | |||
204 | a->channels = GRAB_BITS(buf, 0, 0, 3); | ||
205 | a->channels++; | ||
206 | |||
207 | a->format = GRAB_BITS(buf, 0, 3, 4); | ||
208 | switch (a->format) { | ||
209 | case AUDIO_CODING_TYPE_REF_STREAM_HEADER: | ||
210 | snd_printd(KERN_INFO | ||
211 | "audio coding type 0 not expected in ELD\n"); | ||
212 | break; | ||
213 | |||
214 | case AUDIO_CODING_TYPE_LPCM: | ||
215 | val = GRAB_BITS(buf, 2, 0, 3); | ||
216 | a->sample_bits = 0; | ||
217 | for (i = 0; i < 3; i++) | ||
218 | if (val & (1 << i)) | ||
219 | a->sample_bits |= cea_sample_sizes[i + 1]; | ||
220 | break; | ||
221 | |||
222 | case AUDIO_CODING_TYPE_AC3: | ||
223 | case AUDIO_CODING_TYPE_MPEG1: | ||
224 | case AUDIO_CODING_TYPE_MP3: | ||
225 | case AUDIO_CODING_TYPE_MPEG2: | ||
226 | case AUDIO_CODING_TYPE_AACLC: | ||
227 | case AUDIO_CODING_TYPE_DTS: | ||
228 | case AUDIO_CODING_TYPE_ATRAC: | ||
229 | a->max_bitrate = GRAB_BITS(buf, 2, 0, 8); | ||
230 | a->max_bitrate *= 8000; | ||
231 | break; | ||
232 | |||
233 | case AUDIO_CODING_TYPE_SACD: | ||
234 | break; | ||
235 | |||
236 | case AUDIO_CODING_TYPE_EAC3: | ||
237 | break; | ||
238 | |||
239 | case AUDIO_CODING_TYPE_DTS_HD: | ||
240 | break; | ||
241 | |||
242 | case AUDIO_CODING_TYPE_MLP: | ||
243 | break; | ||
244 | |||
245 | case AUDIO_CODING_TYPE_DST: | ||
246 | break; | ||
247 | |||
248 | case AUDIO_CODING_TYPE_WMAPRO: | ||
249 | a->profile = GRAB_BITS(buf, 2, 0, 3); | ||
250 | break; | ||
251 | |||
252 | case AUDIO_CODING_TYPE_REF_CXT: | ||
253 | a->format = GRAB_BITS(buf, 2, 3, 5); | ||
254 | if (a->format == AUDIO_CODING_XTYPE_HE_REF_CT || | ||
255 | a->format >= AUDIO_CODING_XTYPE_FIRST_RESERVED) { | ||
256 | snd_printd(KERN_INFO | ||
257 | "audio coding xtype %d not expected in ELD\n", | ||
258 | a->format); | ||
259 | a->format = 0; | ||
260 | } else | ||
261 | a->format += AUDIO_CODING_TYPE_HE_AAC - | ||
262 | AUDIO_CODING_XTYPE_HE_AAC; | ||
263 | break; | ||
264 | } | ||
265 | } | ||
266 | |||
267 | /* | ||
268 | * Be careful, ELD buf could be totally rubbish! | ||
269 | */ | ||
270 | static int hdmi_update_sink_eld(struct sink_eld *e, | ||
271 | const unsigned char *buf, int size) | ||
272 | { | ||
273 | int mnl; | ||
274 | int i; | ||
275 | |||
276 | e->eld_ver = GRAB_BITS(buf, 0, 3, 5); | ||
277 | if (e->eld_ver != ELD_VER_CEA_861D && | ||
278 | e->eld_ver != ELD_VER_PARTIAL) { | ||
279 | snd_printd(KERN_INFO "Unknown ELD version %d\n", e->eld_ver); | ||
280 | goto out_fail; | ||
281 | } | ||
282 | |||
283 | e->eld_size = size; | ||
284 | e->baseline_len = GRAB_BITS(buf, 2, 0, 8); | ||
285 | mnl = GRAB_BITS(buf, 4, 0, 5); | ||
286 | e->cea_edid_ver = GRAB_BITS(buf, 4, 5, 3); | ||
287 | |||
288 | e->support_hdcp = GRAB_BITS(buf, 5, 0, 1); | ||
289 | e->support_ai = GRAB_BITS(buf, 5, 1, 1); | ||
290 | e->conn_type = GRAB_BITS(buf, 5, 2, 2); | ||
291 | e->sad_count = GRAB_BITS(buf, 5, 4, 4); | ||
292 | |||
293 | e->aud_synch_delay = GRAB_BITS(buf, 6, 0, 8) * 2; | ||
294 | e->spk_alloc = GRAB_BITS(buf, 7, 0, 7); | ||
295 | |||
296 | e->port_id = get_unaligned_le64(buf + 8); | ||
297 | |||
298 | /* not specified, but the spec's tendency is little endian */ | ||
299 | e->manufacture_id = get_unaligned_le16(buf + 16); | ||
300 | e->product_id = get_unaligned_le16(buf + 18); | ||
301 | |||
302 | if (mnl > ELD_MAX_MNL) { | ||
303 | snd_printd(KERN_INFO "MNL is reserved value %d\n", mnl); | ||
304 | goto out_fail; | ||
305 | } else if (ELD_FIXED_BYTES + mnl > size) { | ||
306 | snd_printd(KERN_INFO "out of range MNL %d\n", mnl); | ||
307 | goto out_fail; | ||
308 | } else | ||
309 | strlcpy(e->monitor_name, buf + ELD_FIXED_BYTES, mnl); | ||
310 | |||
311 | for (i = 0; i < e->sad_count; i++) { | ||
312 | if (ELD_FIXED_BYTES + mnl + 3 * (i + 1) > size) { | ||
313 | snd_printd(KERN_INFO "out of range SAD %d\n", i); | ||
314 | goto out_fail; | ||
315 | } | ||
316 | hdmi_update_short_audio_desc(e->sad + i, | ||
317 | buf + ELD_FIXED_BYTES + mnl + 3 * i); | ||
318 | } | ||
319 | |||
320 | return 0; | ||
321 | |||
322 | out_fail: | ||
323 | e->eld_ver = 0; | ||
324 | return -EINVAL; | ||
325 | } | ||
326 | |||
327 | static int hdmi_present_sense(struct hda_codec *codec, hda_nid_t nid) | ||
328 | { | ||
329 | return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0); | ||
330 | } | ||
331 | |||
332 | static int hdmi_eld_valid(struct hda_codec *codec, hda_nid_t nid) | ||
333 | { | ||
334 | int eldv; | ||
335 | int present; | ||
336 | |||
337 | present = hdmi_present_sense(codec, nid); | ||
338 | eldv = (present & AC_PINSENSE_ELDV); | ||
339 | present = (present & AC_PINSENSE_PRESENCE); | ||
340 | |||
341 | #ifdef CONFIG_SND_DEBUG_VERBOSE | ||
342 | printk(KERN_INFO "pinp = %d, eldv = %d\n", !!present, !!eldv); | ||
343 | #endif | ||
344 | |||
345 | return eldv && present; | ||
346 | } | ||
347 | |||
348 | int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid) | ||
349 | { | ||
350 | return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE, | ||
351 | AC_DIPSIZE_ELD_BUF); | ||
352 | } | ||
353 | |||
354 | int snd_hdmi_get_eld(struct sink_eld *eld, | ||
355 | struct hda_codec *codec, hda_nid_t nid) | ||
356 | { | ||
357 | int i; | ||
358 | int ret; | ||
359 | int size; | ||
360 | unsigned char *buf; | ||
361 | |||
362 | if (!hdmi_eld_valid(codec, nid)) | ||
363 | return -ENOENT; | ||
364 | |||
365 | size = snd_hdmi_get_eld_size(codec, nid); | ||
366 | if (size == 0) { | ||
367 | /* wfg: workaround for ASUS P5E-VM HDMI board */ | ||
368 | snd_printd(KERN_INFO "ELD buf size is 0, force 128\n"); | ||
369 | size = 128; | ||
370 | } | ||
371 | if (size < ELD_FIXED_BYTES || size > PAGE_SIZE) { | ||
372 | snd_printd(KERN_INFO "Invalid ELD buf size %d\n", size); | ||
373 | return -ERANGE; | ||
374 | } | ||
375 | |||
376 | buf = kmalloc(size, GFP_KERNEL); | ||
377 | if (!buf) | ||
378 | return -ENOMEM; | ||
379 | |||
380 | for (i = 0; i < size; i++) | ||
381 | buf[i] = hdmi_get_eld_byte(codec, nid, i); | ||
382 | |||
383 | ret = hdmi_update_sink_eld(eld, buf, size); | ||
384 | |||
385 | kfree(buf); | ||
386 | return ret; | ||
387 | } | ||
388 | |||
389 | static void hdmi_show_short_audio_desc(struct cea_sad *a) | ||
390 | { | ||
391 | char buf[SND_PRINT_RATES_ADVISED_BUFSIZE]; | ||
392 | |||
393 | printk(KERN_INFO "coding type: %s\n", | ||
394 | cea_audio_coding_type_names[a->format]); | ||
395 | printk(KERN_INFO "channels: %d\n", a->channels); | ||
396 | |||
397 | snd_print_pcm_rates(a->rates, buf, sizeof(buf)); | ||
398 | printk(KERN_INFO "sampling frequencies: %s\n", buf); | ||
399 | |||
400 | if (a->format == AUDIO_CODING_TYPE_LPCM) | ||
401 | printk(KERN_INFO "sample bits: 0x%x\n", a->sample_bits); | ||
402 | |||
403 | if (a->max_bitrate) | ||
404 | printk(KERN_INFO "max bitrate: %d\n", a->max_bitrate); | ||
405 | |||
406 | if (a->profile) | ||
407 | printk(KERN_INFO "profile: %d\n", a->profile); | ||
408 | } | ||
409 | |||
410 | #define HDMI_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80 | ||
411 | static void hdmi_print_channel_allocation(int spk_alloc, char *buf, int buflen) | ||
412 | { | ||
413 | int i, j; | ||
414 | |||
415 | for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) { | ||
416 | if (spk_alloc & (1 << i)) | ||
417 | j += snprintf(buf + j, buflen - j, "%s ", | ||
418 | cea_speaker_allocation_names[i]); | ||
419 | } | ||
420 | if (j) | ||
421 | j--; /* skip last space */ | ||
422 | buf[j] = '\0'; /* necessary when j == 0 */ | ||
423 | } | ||
424 | |||
425 | void snd_hdmi_show_eld(struct sink_eld *e) | ||
426 | { | ||
427 | int i; | ||
428 | char buf[HDMI_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; | ||
429 | |||
430 | printk(KERN_INFO "ELD buffer size is %d\n", e->eld_size); | ||
431 | printk(KERN_INFO "ELD baseline len is %d*4\n", e->baseline_len); | ||
432 | printk(KERN_INFO "vendor block len is %d\n", | ||
433 | e->eld_size - e->baseline_len * 4 - 4); | ||
434 | printk(KERN_INFO "ELD version is %s\n", | ||
435 | eld_versoin_names[e->eld_ver]); | ||
436 | printk(KERN_INFO "CEA EDID version is %s\n", | ||
437 | cea_edid_version_names[e->cea_edid_ver]); | ||
438 | printk(KERN_INFO "manufacture id is 0x%x\n", e->manufacture_id); | ||
439 | printk(KERN_INFO "product id is 0x%x\n", e->product_id); | ||
440 | printk(KERN_INFO "port id is 0x%llx\n", (long long)e->port_id); | ||
441 | printk(KERN_INFO "HDCP support is %d\n", e->support_hdcp); | ||
442 | printk(KERN_INFO "AI support is %d\n", e->support_ai); | ||
443 | printk(KERN_INFO "SAD count is %d\n", e->sad_count); | ||
444 | printk(KERN_INFO "audio sync delay is %x\n", e->aud_synch_delay); | ||
445 | printk(KERN_INFO "connection type is %s\n", | ||
446 | eld_connection_type_names[e->conn_type]); | ||
447 | printk(KERN_INFO "monitor name is %s\n", e->monitor_name); | ||
448 | |||
449 | hdmi_print_channel_allocation(e->spk_alloc, buf, sizeof(buf)); | ||
450 | printk(KERN_INFO "speaker allocations: (0x%x)%s\n", e->spk_alloc, buf); | ||
451 | |||
452 | for (i = 0; i < e->sad_count; i++) | ||
453 | hdmi_show_short_audio_desc(e->sad + i); | ||
454 | } | ||