diff options
Diffstat (limited to 'sound/pci/hda/hda_eld.c')
-rw-r--r-- | sound/pci/hda/hda_eld.c | 590 |
1 files changed, 590 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..fcad5ec31773 --- /dev/null +++ b/sound/pci/hda/hda_eld.c | |||
@@ -0,0 +1,590 @@ | |||
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 | enum cea_edid_versions { | ||
36 | CEA_EDID_VER_NONE = 0, | ||
37 | CEA_EDID_VER_CEA861 = 1, | ||
38 | CEA_EDID_VER_CEA861A = 2, | ||
39 | CEA_EDID_VER_CEA861BCD = 3, | ||
40 | CEA_EDID_VER_RESERVED = 4, | ||
41 | }; | ||
42 | |||
43 | static char *cea_speaker_allocation_names[] = { | ||
44 | /* 0 */ "FL/FR", | ||
45 | /* 1 */ "LFE", | ||
46 | /* 2 */ "FC", | ||
47 | /* 3 */ "RL/RR", | ||
48 | /* 4 */ "RC", | ||
49 | /* 5 */ "FLC/FRC", | ||
50 | /* 6 */ "RLC/RRC", | ||
51 | /* 7 */ "FLW/FRW", | ||
52 | /* 8 */ "FLH/FRH", | ||
53 | /* 9 */ "TC", | ||
54 | /* 10 */ "FCH", | ||
55 | }; | ||
56 | |||
57 | static char *eld_connection_type_names[4] = { | ||
58 | "HDMI", | ||
59 | "DisplayPort", | ||
60 | "2-reserved", | ||
61 | "3-reserved" | ||
62 | }; | ||
63 | |||
64 | enum cea_audio_coding_types { | ||
65 | AUDIO_CODING_TYPE_REF_STREAM_HEADER = 0, | ||
66 | AUDIO_CODING_TYPE_LPCM = 1, | ||
67 | AUDIO_CODING_TYPE_AC3 = 2, | ||
68 | AUDIO_CODING_TYPE_MPEG1 = 3, | ||
69 | AUDIO_CODING_TYPE_MP3 = 4, | ||
70 | AUDIO_CODING_TYPE_MPEG2 = 5, | ||
71 | AUDIO_CODING_TYPE_AACLC = 6, | ||
72 | AUDIO_CODING_TYPE_DTS = 7, | ||
73 | AUDIO_CODING_TYPE_ATRAC = 8, | ||
74 | AUDIO_CODING_TYPE_SACD = 9, | ||
75 | AUDIO_CODING_TYPE_EAC3 = 10, | ||
76 | AUDIO_CODING_TYPE_DTS_HD = 11, | ||
77 | AUDIO_CODING_TYPE_MLP = 12, | ||
78 | AUDIO_CODING_TYPE_DST = 13, | ||
79 | AUDIO_CODING_TYPE_WMAPRO = 14, | ||
80 | AUDIO_CODING_TYPE_REF_CXT = 15, | ||
81 | /* also include valid xtypes below */ | ||
82 | AUDIO_CODING_TYPE_HE_AAC = 15, | ||
83 | AUDIO_CODING_TYPE_HE_AAC2 = 16, | ||
84 | AUDIO_CODING_TYPE_MPEG_SURROUND = 17, | ||
85 | }; | ||
86 | |||
87 | enum cea_audio_coding_xtypes { | ||
88 | AUDIO_CODING_XTYPE_HE_REF_CT = 0, | ||
89 | AUDIO_CODING_XTYPE_HE_AAC = 1, | ||
90 | AUDIO_CODING_XTYPE_HE_AAC2 = 2, | ||
91 | AUDIO_CODING_XTYPE_MPEG_SURROUND = 3, | ||
92 | AUDIO_CODING_XTYPE_FIRST_RESERVED = 4, | ||
93 | }; | ||
94 | |||
95 | static char *cea_audio_coding_type_names[] = { | ||
96 | /* 0 */ "undefined", | ||
97 | /* 1 */ "LPCM", | ||
98 | /* 2 */ "AC-3", | ||
99 | /* 3 */ "MPEG1", | ||
100 | /* 4 */ "MP3", | ||
101 | /* 5 */ "MPEG2", | ||
102 | /* 6 */ "AAC-LC", | ||
103 | /* 7 */ "DTS", | ||
104 | /* 8 */ "ATRAC", | ||
105 | /* 9 */ "DSD (One Bit Audio)", | ||
106 | /* 10 */ "E-AC-3/DD+ (Dolby Digital Plus)", | ||
107 | /* 11 */ "DTS-HD", | ||
108 | /* 12 */ "MLP (Dolby TrueHD)", | ||
109 | /* 13 */ "DST", | ||
110 | /* 14 */ "WMAPro", | ||
111 | /* 15 */ "HE-AAC", | ||
112 | /* 16 */ "HE-AACv2", | ||
113 | /* 17 */ "MPEG Surround", | ||
114 | }; | ||
115 | |||
116 | /* | ||
117 | * The following two lists are shared between | ||
118 | * - HDMI audio InfoFrame (source to sink) | ||
119 | * - CEA E-EDID Extension (sink to source) | ||
120 | */ | ||
121 | |||
122 | /* | ||
123 | * SS1:SS0 index => sample size | ||
124 | */ | ||
125 | static int cea_sample_sizes[4] = { | ||
126 | 0, /* 0: Refer to Stream Header */ | ||
127 | AC_SUPPCM_BITS_16, /* 1: 16 bits */ | ||
128 | AC_SUPPCM_BITS_20, /* 2: 20 bits */ | ||
129 | AC_SUPPCM_BITS_24, /* 3: 24 bits */ | ||
130 | }; | ||
131 | |||
132 | /* | ||
133 | * SF2:SF1:SF0 index => sampling frequency | ||
134 | */ | ||
135 | static int cea_sampling_frequencies[8] = { | ||
136 | 0, /* 0: Refer to Stream Header */ | ||
137 | SNDRV_PCM_RATE_32000, /* 1: 32000Hz */ | ||
138 | SNDRV_PCM_RATE_44100, /* 2: 44100Hz */ | ||
139 | SNDRV_PCM_RATE_48000, /* 3: 48000Hz */ | ||
140 | SNDRV_PCM_RATE_88200, /* 4: 88200Hz */ | ||
141 | SNDRV_PCM_RATE_96000, /* 5: 96000Hz */ | ||
142 | SNDRV_PCM_RATE_176400, /* 6: 176400Hz */ | ||
143 | SNDRV_PCM_RATE_192000, /* 7: 192000Hz */ | ||
144 | }; | ||
145 | |||
146 | static unsigned char hdmi_get_eld_byte(struct hda_codec *codec, hda_nid_t nid, | ||
147 | int byte_index) | ||
148 | { | ||
149 | unsigned int val; | ||
150 | |||
151 | val = snd_hda_codec_read(codec, nid, 0, | ||
152 | AC_VERB_GET_HDMI_ELDD, byte_index); | ||
153 | |||
154 | #ifdef BE_PARANOID | ||
155 | printk(KERN_INFO "HDMI: ELD data byte %d: 0x%x\n", byte_index, val); | ||
156 | #endif | ||
157 | |||
158 | if ((val & AC_ELDD_ELD_VALID) == 0) { | ||
159 | snd_printd(KERN_INFO "HDMI: invalid ELD data byte %d\n", | ||
160 | byte_index); | ||
161 | val = 0; | ||
162 | } | ||
163 | |||
164 | return val & AC_ELDD_ELD_DATA; | ||
165 | } | ||
166 | |||
167 | #define GRAB_BITS(buf, byte, lowbit, bits) \ | ||
168 | ({ \ | ||
169 | BUILD_BUG_ON(lowbit > 7); \ | ||
170 | BUILD_BUG_ON(bits > 8); \ | ||
171 | BUILD_BUG_ON(bits <= 0); \ | ||
172 | \ | ||
173 | (buf[byte] >> (lowbit)) & ((1 << (bits)) - 1); \ | ||
174 | }) | ||
175 | |||
176 | static void hdmi_update_short_audio_desc(struct cea_sad *a, | ||
177 | const unsigned char *buf) | ||
178 | { | ||
179 | int i; | ||
180 | int val; | ||
181 | |||
182 | val = GRAB_BITS(buf, 1, 0, 7); | ||
183 | a->rates = 0; | ||
184 | for (i = 0; i < 7; i++) | ||
185 | if (val & (1 << i)) | ||
186 | a->rates |= cea_sampling_frequencies[i + 1]; | ||
187 | |||
188 | a->channels = GRAB_BITS(buf, 0, 0, 3); | ||
189 | a->channels++; | ||
190 | |||
191 | a->format = GRAB_BITS(buf, 0, 3, 4); | ||
192 | switch (a->format) { | ||
193 | case AUDIO_CODING_TYPE_REF_STREAM_HEADER: | ||
194 | snd_printd(KERN_INFO | ||
195 | "HDMI: audio coding type 0 not expected\n"); | ||
196 | break; | ||
197 | |||
198 | case AUDIO_CODING_TYPE_LPCM: | ||
199 | val = GRAB_BITS(buf, 2, 0, 3); | ||
200 | a->sample_bits = 0; | ||
201 | for (i = 0; i < 3; i++) | ||
202 | if (val & (1 << i)) | ||
203 | a->sample_bits |= cea_sample_sizes[i + 1]; | ||
204 | break; | ||
205 | |||
206 | case AUDIO_CODING_TYPE_AC3: | ||
207 | case AUDIO_CODING_TYPE_MPEG1: | ||
208 | case AUDIO_CODING_TYPE_MP3: | ||
209 | case AUDIO_CODING_TYPE_MPEG2: | ||
210 | case AUDIO_CODING_TYPE_AACLC: | ||
211 | case AUDIO_CODING_TYPE_DTS: | ||
212 | case AUDIO_CODING_TYPE_ATRAC: | ||
213 | a->max_bitrate = GRAB_BITS(buf, 2, 0, 8); | ||
214 | a->max_bitrate *= 8000; | ||
215 | break; | ||
216 | |||
217 | case AUDIO_CODING_TYPE_SACD: | ||
218 | break; | ||
219 | |||
220 | case AUDIO_CODING_TYPE_EAC3: | ||
221 | break; | ||
222 | |||
223 | case AUDIO_CODING_TYPE_DTS_HD: | ||
224 | break; | ||
225 | |||
226 | case AUDIO_CODING_TYPE_MLP: | ||
227 | break; | ||
228 | |||
229 | case AUDIO_CODING_TYPE_DST: | ||
230 | break; | ||
231 | |||
232 | case AUDIO_CODING_TYPE_WMAPRO: | ||
233 | a->profile = GRAB_BITS(buf, 2, 0, 3); | ||
234 | break; | ||
235 | |||
236 | case AUDIO_CODING_TYPE_REF_CXT: | ||
237 | a->format = GRAB_BITS(buf, 2, 3, 5); | ||
238 | if (a->format == AUDIO_CODING_XTYPE_HE_REF_CT || | ||
239 | a->format >= AUDIO_CODING_XTYPE_FIRST_RESERVED) { | ||
240 | snd_printd(KERN_INFO | ||
241 | "HDMI: audio coding xtype %d not expected\n", | ||
242 | a->format); | ||
243 | a->format = 0; | ||
244 | } else | ||
245 | a->format += AUDIO_CODING_TYPE_HE_AAC - | ||
246 | AUDIO_CODING_XTYPE_HE_AAC; | ||
247 | break; | ||
248 | } | ||
249 | } | ||
250 | |||
251 | /* | ||
252 | * Be careful, ELD buf could be totally rubbish! | ||
253 | */ | ||
254 | static int hdmi_update_eld(struct hdmi_eld *e, | ||
255 | const unsigned char *buf, int size) | ||
256 | { | ||
257 | int mnl; | ||
258 | int i; | ||
259 | |||
260 | e->eld_ver = GRAB_BITS(buf, 0, 3, 5); | ||
261 | if (e->eld_ver != ELD_VER_CEA_861D && | ||
262 | e->eld_ver != ELD_VER_PARTIAL) { | ||
263 | snd_printd(KERN_INFO "HDMI: Unknown ELD version %d\n", | ||
264 | e->eld_ver); | ||
265 | goto out_fail; | ||
266 | } | ||
267 | |||
268 | e->eld_size = size; | ||
269 | e->baseline_len = GRAB_BITS(buf, 2, 0, 8); | ||
270 | mnl = GRAB_BITS(buf, 4, 0, 5); | ||
271 | e->cea_edid_ver = GRAB_BITS(buf, 4, 5, 3); | ||
272 | |||
273 | e->support_hdcp = GRAB_BITS(buf, 5, 0, 1); | ||
274 | e->support_ai = GRAB_BITS(buf, 5, 1, 1); | ||
275 | e->conn_type = GRAB_BITS(buf, 5, 2, 2); | ||
276 | e->sad_count = GRAB_BITS(buf, 5, 4, 4); | ||
277 | |||
278 | e->aud_synch_delay = GRAB_BITS(buf, 6, 0, 8) * 2; | ||
279 | e->spk_alloc = GRAB_BITS(buf, 7, 0, 7); | ||
280 | |||
281 | e->port_id = get_unaligned_le64(buf + 8); | ||
282 | |||
283 | /* not specified, but the spec's tendency is little endian */ | ||
284 | e->manufacture_id = get_unaligned_le16(buf + 16); | ||
285 | e->product_id = get_unaligned_le16(buf + 18); | ||
286 | |||
287 | if (mnl > ELD_MAX_MNL) { | ||
288 | snd_printd(KERN_INFO "HDMI: MNL is reserved value %d\n", mnl); | ||
289 | goto out_fail; | ||
290 | } else if (ELD_FIXED_BYTES + mnl > size) { | ||
291 | snd_printd(KERN_INFO "HDMI: out of range MNL %d\n", mnl); | ||
292 | goto out_fail; | ||
293 | } else | ||
294 | strlcpy(e->monitor_name, buf + ELD_FIXED_BYTES, mnl); | ||
295 | |||
296 | for (i = 0; i < e->sad_count; i++) { | ||
297 | if (ELD_FIXED_BYTES + mnl + 3 * (i + 1) > size) { | ||
298 | snd_printd(KERN_INFO "HDMI: out of range SAD %d\n", i); | ||
299 | goto out_fail; | ||
300 | } | ||
301 | hdmi_update_short_audio_desc(e->sad + i, | ||
302 | buf + ELD_FIXED_BYTES + mnl + 3 * i); | ||
303 | } | ||
304 | |||
305 | return 0; | ||
306 | |||
307 | out_fail: | ||
308 | e->eld_ver = 0; | ||
309 | return -EINVAL; | ||
310 | } | ||
311 | |||
312 | static int hdmi_present_sense(struct hda_codec *codec, hda_nid_t nid) | ||
313 | { | ||
314 | return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0); | ||
315 | } | ||
316 | |||
317 | static int hdmi_eld_valid(struct hda_codec *codec, hda_nid_t nid) | ||
318 | { | ||
319 | int eldv; | ||
320 | int present; | ||
321 | |||
322 | present = hdmi_present_sense(codec, nid); | ||
323 | eldv = (present & AC_PINSENSE_ELDV); | ||
324 | present = (present & AC_PINSENSE_PRESENCE); | ||
325 | |||
326 | #ifdef CONFIG_SND_DEBUG_VERBOSE | ||
327 | printk(KERN_INFO "HDMI: sink_present = %d, eld_valid = %d\n", | ||
328 | !!present, !!eldv); | ||
329 | #endif | ||
330 | |||
331 | return eldv && present; | ||
332 | } | ||
333 | |||
334 | int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid) | ||
335 | { | ||
336 | return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE, | ||
337 | AC_DIPSIZE_ELD_BUF); | ||
338 | } | ||
339 | |||
340 | int snd_hdmi_get_eld(struct hdmi_eld *eld, | ||
341 | struct hda_codec *codec, hda_nid_t nid) | ||
342 | { | ||
343 | int i; | ||
344 | int ret; | ||
345 | int size; | ||
346 | unsigned char *buf; | ||
347 | |||
348 | if (!hdmi_eld_valid(codec, nid)) | ||
349 | return -ENOENT; | ||
350 | |||
351 | size = snd_hdmi_get_eld_size(codec, nid); | ||
352 | if (size == 0) { | ||
353 | /* wfg: workaround for ASUS P5E-VM HDMI board */ | ||
354 | snd_printd(KERN_INFO "HDMI: ELD buf size is 0, force 128\n"); | ||
355 | size = 128; | ||
356 | } | ||
357 | if (size < ELD_FIXED_BYTES || size > PAGE_SIZE) { | ||
358 | snd_printd(KERN_INFO "HDMI: invalid ELD buf size %d\n", size); | ||
359 | return -ERANGE; | ||
360 | } | ||
361 | |||
362 | buf = kmalloc(size, GFP_KERNEL); | ||
363 | if (!buf) | ||
364 | return -ENOMEM; | ||
365 | |||
366 | for (i = 0; i < size; i++) | ||
367 | buf[i] = hdmi_get_eld_byte(codec, nid, i); | ||
368 | |||
369 | ret = hdmi_update_eld(eld, buf, size); | ||
370 | |||
371 | kfree(buf); | ||
372 | return ret; | ||
373 | } | ||
374 | |||
375 | static void hdmi_show_short_audio_desc(struct cea_sad *a) | ||
376 | { | ||
377 | char buf[SND_PRINT_RATES_ADVISED_BUFSIZE]; | ||
378 | char buf2[8 + SND_PRINT_BITS_ADVISED_BUFSIZE] = ", bits ="; | ||
379 | |||
380 | if (!a->format) | ||
381 | return; | ||
382 | |||
383 | snd_print_pcm_rates(a->rates, buf, sizeof(buf)); | ||
384 | |||
385 | if (a->format == AUDIO_CODING_TYPE_LPCM) | ||
386 | snd_print_pcm_bits(a->sample_bits, buf2 + 8, sizeof(buf2 - 8)); | ||
387 | else if (a->max_bitrate) | ||
388 | snprintf(buf2, sizeof(buf2), | ||
389 | ", max bitrate = %d", a->max_bitrate); | ||
390 | else | ||
391 | buf2[0] = '\0'; | ||
392 | |||
393 | printk(KERN_INFO "HDMI: supports coding type %s:" | ||
394 | " channels = %d, rates =%s%s\n", | ||
395 | cea_audio_coding_type_names[a->format], | ||
396 | a->channels, | ||
397 | buf, | ||
398 | buf2); | ||
399 | } | ||
400 | |||
401 | void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen) | ||
402 | { | ||
403 | int i, j; | ||
404 | |||
405 | for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) { | ||
406 | if (spk_alloc & (1 << i)) | ||
407 | j += snprintf(buf + j, buflen - j, " %s", | ||
408 | cea_speaker_allocation_names[i]); | ||
409 | } | ||
410 | buf[j] = '\0'; /* necessary when j == 0 */ | ||
411 | } | ||
412 | |||
413 | void snd_hdmi_show_eld(struct hdmi_eld *e) | ||
414 | { | ||
415 | int i; | ||
416 | |||
417 | printk(KERN_INFO "HDMI: detected monitor %s at connection type %s\n", | ||
418 | e->monitor_name, | ||
419 | eld_connection_type_names[e->conn_type]); | ||
420 | |||
421 | if (e->spk_alloc) { | ||
422 | char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; | ||
423 | snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf)); | ||
424 | printk(KERN_INFO "HDMI: available speakers:%s\n", buf); | ||
425 | } | ||
426 | |||
427 | for (i = 0; i < e->sad_count; i++) | ||
428 | hdmi_show_short_audio_desc(e->sad + i); | ||
429 | } | ||
430 | |||
431 | #ifdef CONFIG_PROC_FS | ||
432 | |||
433 | static void hdmi_print_sad_info(int i, struct cea_sad *a, | ||
434 | struct snd_info_buffer *buffer) | ||
435 | { | ||
436 | char buf[SND_PRINT_RATES_ADVISED_BUFSIZE]; | ||
437 | |||
438 | snd_iprintf(buffer, "sad%d_coding_type\t[0x%x] %s\n", | ||
439 | i, a->format, cea_audio_coding_type_names[a->format]); | ||
440 | snd_iprintf(buffer, "sad%d_channels\t\t%d\n", i, a->channels); | ||
441 | |||
442 | snd_print_pcm_rates(a->rates, buf, sizeof(buf)); | ||
443 | snd_iprintf(buffer, "sad%d_rates\t\t[0x%x]%s\n", i, a->rates, buf); | ||
444 | |||
445 | if (a->format == AUDIO_CODING_TYPE_LPCM) { | ||
446 | snd_print_pcm_bits(a->sample_bits, buf, sizeof(buf)); | ||
447 | snd_iprintf(buffer, "sad%d_bits\t\t[0x%x]%s\n", | ||
448 | i, a->sample_bits, buf); | ||
449 | } | ||
450 | |||
451 | if (a->max_bitrate) | ||
452 | snd_iprintf(buffer, "sad%d_max_bitrate\t%d\n", | ||
453 | i, a->max_bitrate); | ||
454 | |||
455 | if (a->profile) | ||
456 | snd_iprintf(buffer, "sad%d_profile\t\t%d\n", i, a->profile); | ||
457 | } | ||
458 | |||
459 | static void hdmi_print_eld_info(struct snd_info_entry *entry, | ||
460 | struct snd_info_buffer *buffer) | ||
461 | { | ||
462 | struct hdmi_eld *e = entry->private_data; | ||
463 | char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; | ||
464 | int i; | ||
465 | static char *eld_versoin_names[32] = { | ||
466 | "reserved", | ||
467 | "reserved", | ||
468 | "CEA-861D or below", | ||
469 | [3 ... 30] = "reserved", | ||
470 | [31] = "partial" | ||
471 | }; | ||
472 | static char *cea_edid_version_names[8] = { | ||
473 | "no CEA EDID Timing Extension block present", | ||
474 | "CEA-861", | ||
475 | "CEA-861-A", | ||
476 | "CEA-861-B, C or D", | ||
477 | [4 ... 7] = "reserved" | ||
478 | }; | ||
479 | |||
480 | snd_iprintf(buffer, "monitor_name\t\t%s\n", e->monitor_name); | ||
481 | snd_iprintf(buffer, "connection_type\t\t%s\n", | ||
482 | eld_connection_type_names[e->conn_type]); | ||
483 | snd_iprintf(buffer, "eld_version\t\t[0x%x] %s\n", e->eld_ver, | ||
484 | eld_versoin_names[e->eld_ver]); | ||
485 | snd_iprintf(buffer, "edid_version\t\t[0x%x] %s\n", e->cea_edid_ver, | ||
486 | cea_edid_version_names[e->cea_edid_ver]); | ||
487 | snd_iprintf(buffer, "manufacture_id\t\t0x%x\n", e->manufacture_id); | ||
488 | snd_iprintf(buffer, "product_id\t\t0x%x\n", e->product_id); | ||
489 | snd_iprintf(buffer, "port_id\t\t\t0x%llx\n", (long long)e->port_id); | ||
490 | snd_iprintf(buffer, "support_hdcp\t\t%d\n", e->support_hdcp); | ||
491 | snd_iprintf(buffer, "support_ai\t\t%d\n", e->support_ai); | ||
492 | snd_iprintf(buffer, "audio_sync_delay\t%d\n", e->aud_synch_delay); | ||
493 | |||
494 | snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf)); | ||
495 | snd_iprintf(buffer, "speakers\t\t[0x%x]%s\n", e->spk_alloc, buf); | ||
496 | |||
497 | snd_iprintf(buffer, "sad_count\t\t%d\n", e->sad_count); | ||
498 | |||
499 | for (i = 0; i < e->sad_count; i++) | ||
500 | hdmi_print_sad_info(i, e->sad + i, buffer); | ||
501 | } | ||
502 | |||
503 | static void hdmi_write_eld_info(struct snd_info_entry *entry, | ||
504 | struct snd_info_buffer *buffer) | ||
505 | { | ||
506 | struct hdmi_eld *e = entry->private_data; | ||
507 | char line[64]; | ||
508 | char name[64]; | ||
509 | char *sname; | ||
510 | long long val; | ||
511 | int n; | ||
512 | |||
513 | while (!snd_info_get_line(buffer, line, sizeof(line))) { | ||
514 | if (sscanf(line, "%s %llx", name, &val) != 2) | ||
515 | continue; | ||
516 | /* | ||
517 | * We don't allow modification to these fields: | ||
518 | * monitor_name manufacture_id product_id | ||
519 | * eld_version edid_version | ||
520 | */ | ||
521 | if (!strcmp(name, "connection_type")) | ||
522 | e->conn_type = val; | ||
523 | else if (!strcmp(name, "port_id")) | ||
524 | e->port_id = val; | ||
525 | else if (!strcmp(name, "support_hdcp")) | ||
526 | e->support_hdcp = val; | ||
527 | else if (!strcmp(name, "support_ai")) | ||
528 | e->support_ai = val; | ||
529 | else if (!strcmp(name, "audio_sync_delay")) | ||
530 | e->aud_synch_delay = val; | ||
531 | else if (!strcmp(name, "speakers")) | ||
532 | e->spk_alloc = val; | ||
533 | else if (!strcmp(name, "sad_count")) | ||
534 | e->sad_count = val; | ||
535 | else if (!strncmp(name, "sad", 3)) { | ||
536 | sname = name + 4; | ||
537 | n = name[3] - '0'; | ||
538 | if (name[4] >= '0' && name[4] <= '9') { | ||
539 | sname++; | ||
540 | n = 10 * n + name[4] - '0'; | ||
541 | } | ||
542 | if (n < 0 || n > 31) /* double the CEA limit */ | ||
543 | continue; | ||
544 | if (!strcmp(sname, "_coding_type")) | ||
545 | e->sad[n].format = val; | ||
546 | else if (!strcmp(sname, "_channels")) | ||
547 | e->sad[n].channels = val; | ||
548 | else if (!strcmp(sname, "_rates")) | ||
549 | e->sad[n].rates = val; | ||
550 | else if (!strcmp(sname, "_bits")) | ||
551 | e->sad[n].sample_bits = val; | ||
552 | else if (!strcmp(sname, "_max_bitrate")) | ||
553 | e->sad[n].max_bitrate = val; | ||
554 | else if (!strcmp(sname, "_profile")) | ||
555 | e->sad[n].profile = val; | ||
556 | if (n >= e->sad_count) | ||
557 | e->sad_count = n + 1; | ||
558 | } | ||
559 | } | ||
560 | } | ||
561 | |||
562 | |||
563 | int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld) | ||
564 | { | ||
565 | char name[32]; | ||
566 | struct snd_info_entry *entry; | ||
567 | int err; | ||
568 | |||
569 | snprintf(name, sizeof(name), "eld#%d", codec->addr); | ||
570 | err = snd_card_proc_new(codec->bus->card, name, &entry); | ||
571 | if (err < 0) | ||
572 | return err; | ||
573 | |||
574 | snd_info_set_text_ops(entry, eld, hdmi_print_eld_info); | ||
575 | entry->c.text.write = hdmi_write_eld_info; | ||
576 | entry->mode |= S_IWUSR; | ||
577 | eld->proc_entry = entry; | ||
578 | |||
579 | return 0; | ||
580 | } | ||
581 | |||
582 | void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld) | ||
583 | { | ||
584 | if (!codec->bus->shutdown && eld->proc_entry) { | ||
585 | snd_device_free(codec->bus->card, eld->proc_entry); | ||
586 | eld->proc_entry = NULL; | ||
587 | } | ||
588 | } | ||
589 | |||
590 | #endif /* CONFIG_PROC_FS */ | ||