diff options
Diffstat (limited to 'sound')
-rw-r--r-- | sound/pci/hda/patch_hdmi.c | 110 |
1 files changed, 73 insertions, 37 deletions
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index cb997ca0fdf..1f4ae1aeca4 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c | |||
@@ -84,13 +84,25 @@ struct hdmi_audio_infoframe { | |||
84 | u8 ver; /* 0x01 */ | 84 | u8 ver; /* 0x01 */ |
85 | u8 len; /* 0x0a */ | 85 | u8 len; /* 0x0a */ |
86 | 86 | ||
87 | u8 checksum; /* PB0 */ | 87 | u8 checksum; |
88 | |||
88 | u8 CC02_CT47; /* CC in bits 0:2, CT in 4:7 */ | 89 | u8 CC02_CT47; /* CC in bits 0:2, CT in 4:7 */ |
89 | u8 SS01_SF24; | 90 | u8 SS01_SF24; |
90 | u8 CXT04; | 91 | u8 CXT04; |
91 | u8 CA; | 92 | u8 CA; |
92 | u8 LFEPBL01_LSV36_DM_INH7; | 93 | u8 LFEPBL01_LSV36_DM_INH7; |
93 | u8 reserved[5]; /* PB6 - PB10 */ | 94 | }; |
95 | |||
96 | struct dp_audio_infoframe { | ||
97 | u8 type; /* 0x84 */ | ||
98 | u8 len; /* 0x1b */ | ||
99 | u8 ver; /* 0x11 << 2 */ | ||
100 | |||
101 | u8 CC02_CT47; /* match with HDMI infoframe from this on */ | ||
102 | u8 SS01_SF24; | ||
103 | u8 CXT04; | ||
104 | u8 CA; | ||
105 | u8 LFEPBL01_LSV36_DM_INH7; | ||
94 | }; | 106 | }; |
95 | 107 | ||
96 | /* | 108 | /* |
@@ -194,7 +206,7 @@ static int hdmi_channel_mapping[0x32][8] = { | |||
194 | * This is an ordered list! | 206 | * This is an ordered list! |
195 | * | 207 | * |
196 | * The preceding ones have better chances to be selected by | 208 | * The preceding ones have better chances to be selected by |
197 | * hdmi_setup_channel_allocation(). | 209 | * hdmi_channel_allocation(). |
198 | */ | 210 | */ |
199 | static struct cea_channel_speaker_allocation channel_allocations[] = { | 211 | static struct cea_channel_speaker_allocation channel_allocations[] = { |
200 | /* channel: 7 6 5 4 3 2 1 0 */ | 212 | /* channel: 7 6 5 4 3 2 1 0 */ |
@@ -371,14 +383,14 @@ static void init_channel_allocations(void) | |||
371 | * | 383 | * |
372 | * TODO: it could select the wrong CA from multiple candidates. | 384 | * TODO: it could select the wrong CA from multiple candidates. |
373 | */ | 385 | */ |
374 | static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid, | 386 | static int hdmi_channel_allocation(struct hda_codec *codec, hda_nid_t nid, |
375 | struct hdmi_audio_infoframe *ai) | 387 | int channels) |
376 | { | 388 | { |
377 | struct hdmi_spec *spec = codec->spec; | 389 | struct hdmi_spec *spec = codec->spec; |
378 | struct hdmi_eld *eld; | 390 | struct hdmi_eld *eld; |
379 | int i; | 391 | int i; |
392 | int ca = 0; | ||
380 | int spk_mask = 0; | 393 | int spk_mask = 0; |
381 | int channels = 1 + (ai->CC02_CT47 & 0x7); | ||
382 | char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; | 394 | char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; |
383 | 395 | ||
384 | /* | 396 | /* |
@@ -416,16 +428,16 @@ static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid, | |||
416 | if (channels == channel_allocations[i].channels && | 428 | if (channels == channel_allocations[i].channels && |
417 | (spk_mask & channel_allocations[i].spk_mask) == | 429 | (spk_mask & channel_allocations[i].spk_mask) == |
418 | channel_allocations[i].spk_mask) { | 430 | channel_allocations[i].spk_mask) { |
419 | ai->CA = channel_allocations[i].ca_index; | 431 | ca = channel_allocations[i].ca_index; |
420 | break; | 432 | break; |
421 | } | 433 | } |
422 | } | 434 | } |
423 | 435 | ||
424 | snd_print_channel_allocation(eld->spk_alloc, buf, sizeof(buf)); | 436 | snd_print_channel_allocation(eld->spk_alloc, buf, sizeof(buf)); |
425 | snd_printdd("HDMI: select CA 0x%x for %d-channel allocation: %s\n", | 437 | snd_printdd("HDMI: select CA 0x%x for %d-channel allocation: %s\n", |
426 | ai->CA, channels, buf); | 438 | ca, channels, buf); |
427 | 439 | ||
428 | return ai->CA; | 440 | return ca; |
429 | } | 441 | } |
430 | 442 | ||
431 | static void hdmi_debug_channel_mapping(struct hda_codec *codec, | 443 | static void hdmi_debug_channel_mapping(struct hda_codec *codec, |
@@ -447,10 +459,9 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec, | |||
447 | 459 | ||
448 | static void hdmi_setup_channel_mapping(struct hda_codec *codec, | 460 | static void hdmi_setup_channel_mapping(struct hda_codec *codec, |
449 | hda_nid_t pin_nid, | 461 | hda_nid_t pin_nid, |
450 | struct hdmi_audio_infoframe *ai) | 462 | int ca) |
451 | { | 463 | { |
452 | int i; | 464 | int i; |
453 | int ca = ai->CA; | ||
454 | int err; | 465 | int err; |
455 | 466 | ||
456 | if (hdmi_channel_mapping[ca][1] == 0) { | 467 | if (hdmi_channel_mapping[ca][1] == 0) { |
@@ -547,41 +558,37 @@ static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid) | |||
547 | #endif | 558 | #endif |
548 | } | 559 | } |
549 | 560 | ||
550 | static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *ai) | 561 | static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *hdmi_ai) |
551 | { | 562 | { |
552 | u8 *bytes = (u8 *)ai; | 563 | u8 *bytes = (u8 *)hdmi_ai; |
553 | u8 sum = 0; | 564 | u8 sum = 0; |
554 | int i; | 565 | int i; |
555 | 566 | ||
556 | ai->checksum = 0; | 567 | hdmi_ai->checksum = 0; |
557 | 568 | ||
558 | for (i = 0; i < sizeof(*ai); i++) | 569 | for (i = 0; i < sizeof(*hdmi_ai); i++) |
559 | sum += bytes[i]; | 570 | sum += bytes[i]; |
560 | 571 | ||
561 | ai->checksum = -sum; | 572 | hdmi_ai->checksum = -sum; |
562 | } | 573 | } |
563 | 574 | ||
564 | static void hdmi_fill_audio_infoframe(struct hda_codec *codec, | 575 | static void hdmi_fill_audio_infoframe(struct hda_codec *codec, |
565 | hda_nid_t pin_nid, | 576 | hda_nid_t pin_nid, |
566 | struct hdmi_audio_infoframe *ai) | 577 | u8 *dip, int size) |
567 | { | 578 | { |
568 | u8 *bytes = (u8 *)ai; | ||
569 | int i; | 579 | int i; |
570 | 580 | ||
571 | hdmi_debug_dip_size(codec, pin_nid); | 581 | hdmi_debug_dip_size(codec, pin_nid); |
572 | hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */ | 582 | hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */ |
573 | 583 | ||
574 | hdmi_checksum_audio_infoframe(ai); | ||
575 | |||
576 | hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); | 584 | hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); |
577 | for (i = 0; i < sizeof(*ai); i++) | 585 | for (i = 0; i < size; i++) |
578 | hdmi_write_dip_byte(codec, pin_nid, bytes[i]); | 586 | hdmi_write_dip_byte(codec, pin_nid, dip[i]); |
579 | } | 587 | } |
580 | 588 | ||
581 | static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid, | 589 | static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid, |
582 | struct hdmi_audio_infoframe *ai) | 590 | u8 *dip, int size) |
583 | { | 591 | { |
584 | u8 *bytes = (u8 *)ai; | ||
585 | u8 val; | 592 | u8 val; |
586 | int i; | 593 | int i; |
587 | 594 | ||
@@ -590,10 +597,10 @@ static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid, | |||
590 | return false; | 597 | return false; |
591 | 598 | ||
592 | hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); | 599 | hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); |
593 | for (i = 0; i < sizeof(*ai); i++) { | 600 | for (i = 0; i < size; i++) { |
594 | val = snd_hda_codec_read(codec, pin_nid, 0, | 601 | val = snd_hda_codec_read(codec, pin_nid, 0, |
595 | AC_VERB_GET_HDMI_DIP_DATA, 0); | 602 | AC_VERB_GET_HDMI_DIP_DATA, 0); |
596 | if (val != bytes[i]) | 603 | if (val != dip[i]) |
597 | return false; | 604 | return false; |
598 | } | 605 | } |
599 | 606 | ||
@@ -605,15 +612,13 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid, | |||
605 | { | 612 | { |
606 | struct hdmi_spec *spec = codec->spec; | 613 | struct hdmi_spec *spec = codec->spec; |
607 | hda_nid_t pin_nid; | 614 | hda_nid_t pin_nid; |
615 | int channels = substream->runtime->channels; | ||
616 | int ca; | ||
608 | int i; | 617 | int i; |
609 | struct hdmi_audio_infoframe ai = { | 618 | u8 ai[max(sizeof(struct hdmi_audio_infoframe), |
610 | .type = 0x84, | 619 | sizeof(struct dp_audio_infoframe))]; |
611 | .ver = 0x01, | ||
612 | .len = 0x0a, | ||
613 | .CC02_CT47 = substream->runtime->channels - 1, | ||
614 | }; | ||
615 | 620 | ||
616 | hdmi_setup_channel_allocation(codec, nid, &ai); | 621 | ca = hdmi_channel_allocation(codec, nid, channels); |
617 | 622 | ||
618 | for (i = 0; i < spec->num_pins; i++) { | 623 | for (i = 0; i < spec->num_pins; i++) { |
619 | if (spec->pin_cvt[i] != nid) | 624 | if (spec->pin_cvt[i] != nid) |
@@ -622,14 +627,45 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid, | |||
622 | continue; | 627 | continue; |
623 | 628 | ||
624 | pin_nid = spec->pin[i]; | 629 | pin_nid = spec->pin[i]; |
625 | if (!hdmi_infoframe_uptodate(codec, pin_nid, &ai)) { | 630 | |
631 | memset(ai, 0, sizeof(ai)); | ||
632 | if (spec->sink_eld[i].conn_type == 0) { /* HDMI */ | ||
633 | struct hdmi_audio_infoframe *hdmi_ai; | ||
634 | |||
635 | hdmi_ai = (struct hdmi_audio_infoframe *)ai; | ||
636 | hdmi_ai->type = 0x84; | ||
637 | hdmi_ai->ver = 0x01; | ||
638 | hdmi_ai->len = 0x0a; | ||
639 | hdmi_ai->CC02_CT47 = channels - 1; | ||
640 | hdmi_checksum_audio_infoframe(hdmi_ai); | ||
641 | } else if (spec->sink_eld[i].conn_type == 1) { /* DisplayPort */ | ||
642 | struct dp_audio_infoframe *dp_ai; | ||
643 | |||
644 | dp_ai = (struct dp_audio_infoframe *)ai; | ||
645 | dp_ai->type = 0x84; | ||
646 | dp_ai->len = 0x1b; | ||
647 | dp_ai->ver = 0x11 << 2; | ||
648 | dp_ai->CC02_CT47 = channels - 1; | ||
649 | } else { | ||
650 | snd_printd("HDMI: unknown connection type at pin %d\n", | ||
651 | pin_nid); | ||
652 | continue; | ||
653 | } | ||
654 | |||
655 | /* | ||
656 | * sizeof(ai) is used instead of sizeof(*hdmi_ai) or | ||
657 | * sizeof(*dp_ai) to avoid partial match/update problems when | ||
658 | * the user switches between HDMI/DP monitors. | ||
659 | */ | ||
660 | if (!hdmi_infoframe_uptodate(codec, pin_nid, ai, sizeof(ai))) { | ||
626 | snd_printdd("hdmi_setup_audio_infoframe: " | 661 | snd_printdd("hdmi_setup_audio_infoframe: " |
627 | "cvt=%d pin=%d channels=%d\n", | 662 | "cvt=%d pin=%d channels=%d\n", |
628 | nid, pin_nid, | 663 | nid, pin_nid, |
629 | substream->runtime->channels); | 664 | channels); |
630 | hdmi_setup_channel_mapping(codec, pin_nid, &ai); | 665 | hdmi_setup_channel_mapping(codec, pin_nid, ca); |
631 | hdmi_stop_infoframe_trans(codec, pin_nid); | 666 | hdmi_stop_infoframe_trans(codec, pin_nid); |
632 | hdmi_fill_audio_infoframe(codec, pin_nid, &ai); | 667 | hdmi_fill_audio_infoframe(codec, pin_nid, |
668 | ai, sizeof(ai)); | ||
633 | hdmi_start_infoframe_trans(codec, pin_nid); | 669 | hdmi_start_infoframe_trans(codec, pin_nid); |
634 | } | 670 | } |
635 | } | 671 | } |