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