aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci
diff options
context:
space:
mode:
Diffstat (limited to 'sound/pci')
-rw-r--r--sound/pci/hda/hda_eld.c5
-rw-r--r--sound/pci/hda/hda_local.h6
-rw-r--r--sound/pci/hda/patch_intelhdmi.c191
3 files changed, 155 insertions, 47 deletions
diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c
index 9446a5abea13..20fa6aee29c0 100644
--- a/sound/pci/hda/hda_eld.c
+++ b/sound/pci/hda/hda_eld.c
@@ -560,13 +560,14 @@ static void hdmi_write_eld_info(struct snd_info_entry *entry,
560} 560}
561 561
562 562
563int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld) 563int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld,
564 int index)
564{ 565{
565 char name[32]; 566 char name[32];
566 struct snd_info_entry *entry; 567 struct snd_info_entry *entry;
567 int err; 568 int err;
568 569
569 snprintf(name, sizeof(name), "eld#%d", codec->addr); 570 snprintf(name, sizeof(name), "eld#%d.%d", codec->addr, index);
570 err = snd_card_proc_new(codec->bus->card, name, &entry); 571 err = snd_card_proc_new(codec->bus->card, name, &entry);
571 if (err < 0) 572 if (err < 0)
572 return err; 573 return err;
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 5f1dcc59002b..461e0c15c77a 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -541,11 +541,13 @@ int snd_hdmi_get_eld(struct hdmi_eld *, struct hda_codec *, hda_nid_t);
541void snd_hdmi_show_eld(struct hdmi_eld *eld); 541void snd_hdmi_show_eld(struct hdmi_eld *eld);
542 542
543#ifdef CONFIG_PROC_FS 543#ifdef CONFIG_PROC_FS
544int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld); 544int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld,
545 int index);
545void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld); 546void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld);
546#else 547#else
547static inline int snd_hda_eld_proc_new(struct hda_codec *codec, 548static inline int snd_hda_eld_proc_new(struct hda_codec *codec,
548 struct hdmi_eld *eld) 549 struct hdmi_eld *eld,
550 int index)
549{ 551{
550 return 0; 552 return 0;
551} 553}
diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c
index 6be5ca44a83b..08ea88deba6f 100644
--- a/sound/pci/hda/patch_intelhdmi.c
+++ b/sound/pci/hda/patch_intelhdmi.c
@@ -33,14 +33,43 @@
33#include "hda_codec.h" 33#include "hda_codec.h"
34#include "hda_local.h" 34#include "hda_local.h"
35 35
36static hda_nid_t cvt_nid; /* audio converter */ 36/*
37static hda_nid_t pin_nid; /* HDMI output pin */ 37 * The HDMI/DisplayPort configuration can be highly dynamic. A graphics device
38 * could support two independent pipes, each of them can be connected to one or
39 * more ports (DVI, HDMI or DisplayPort).
40 *
41 * The HDA correspondence of pipes/ports are converter/pin nodes.
42 */
43#define INTEL_HDMI_CVTS 2
44#define INTEL_HDMI_PINS 3
38 45
39#define INTEL_HDMI_EVENT_TAG 0x08 46static char *intel_hdmi_pcm_names[INTEL_HDMI_CVTS] = {
47 "INTEL HDMI 0",
48 "INTEL HDMI 1",
49};
40 50
41struct intel_hdmi_spec { 51struct intel_hdmi_spec {
42 struct hda_pcm pcm_rec; 52 int num_cvts;
43 struct hdmi_eld sink_eld; 53 int num_pins;
54 hda_nid_t cvt[INTEL_HDMI_CVTS+1]; /* audio sources */
55 hda_nid_t pin[INTEL_HDMI_PINS+1]; /* audio sinks */
56
57 /*
58 * source connection for each pin
59 */
60 hda_nid_t pin_cvt[INTEL_HDMI_PINS+1];
61
62 /*
63 * HDMI sink attached to each pin
64 */
65 bool sink_present[INTEL_HDMI_PINS];
66 bool sink_eldv[INTEL_HDMI_PINS];
67 struct hdmi_eld sink_eld[INTEL_HDMI_PINS];
68
69 /*
70 * export one pcm per pipe
71 */
72 struct hda_pcm pcm_rec[INTEL_HDMI_CVTS];
44}; 73};
45 74
46struct hdmi_audio_infoframe { 75struct hdmi_audio_infoframe {
@@ -183,6 +212,19 @@ static struct cea_channel_speaker_allocation channel_allocations[] = {
183{ .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } }, 212{ .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } },
184}; 213};
185 214
215
216static int hda_node_index(hda_nid_t *nids, hda_nid_t nid)
217{
218 int i;
219
220 for (i = 0; nids[i]; i++)
221 if (nids[i] == nid)
222 return i;
223
224 snd_printk(KERN_WARNING "HDMI: nid %d not registered\n", nid);
225 return -EINVAL;
226}
227
186/* 228/*
187 * HDMI routines 229 * HDMI routines
188 */ 230 */
@@ -283,12 +325,12 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec, hda_nid_t nid)
283#endif 325#endif
284} 326}
285 327
286static void hdmi_parse_eld(struct hda_codec *codec) 328static void hdmi_parse_eld(struct hda_codec *codec, int index)
287{ 329{
288 struct intel_hdmi_spec *spec = codec->spec; 330 struct intel_hdmi_spec *spec = codec->spec;
289 struct hdmi_eld *eld = &spec->sink_eld; 331 struct hdmi_eld *eld = &spec->sink_eld[index];
290 332
291 if (!snd_hdmi_get_eld(eld, codec, pin_nid)) 333 if (!snd_hdmi_get_eld(eld, codec, spec->pin[index]))
292 snd_hdmi_show_eld(eld); 334 snd_hdmi_show_eld(eld);
293} 335}
294 336
@@ -395,7 +437,7 @@ static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
395 struct hdmi_audio_infoframe *ai) 437 struct hdmi_audio_infoframe *ai)
396{ 438{
397 struct intel_hdmi_spec *spec = codec->spec; 439 struct intel_hdmi_spec *spec = codec->spec;
398 struct hdmi_eld *eld = &spec->sink_eld; 440 struct hdmi_eld *eld;
399 int i; 441 int i;
400 int spk_mask = 0; 442 int spk_mask = 0;
401 int channels = 1 + (ai->CC02_CT47 & 0x7); 443 int channels = 1 + (ai->CC02_CT47 & 0x7);
@@ -407,6 +449,11 @@ static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
407 if (channels <= 2) 449 if (channels <= 2)
408 return 0; 450 return 0;
409 451
452 i = hda_node_index(spec->pin_cvt, nid);
453 if (i < 0)
454 return 0;
455 eld = &spec->sink_eld[i];
456
410 /* 457 /*
411 * HDMI sink's ELD info cannot always be retrieved for now, e.g. 458 * HDMI sink's ELD info cannot always be retrieved for now, e.g.
412 * in console or for audio devices. Assume the highest speakers 459 * in console or for audio devices. Assume the highest speakers
@@ -469,6 +516,9 @@ static void hdmi_setup_channel_mapping(struct hda_codec *codec, hda_nid_t nid,
469static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid, 516static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid,
470 struct snd_pcm_substream *substream) 517 struct snd_pcm_substream *substream)
471{ 518{
519 struct intel_hdmi_spec *spec = codec->spec;
520 hda_nid_t pin_nid;
521 int i;
472 struct hdmi_audio_infoframe ai = { 522 struct hdmi_audio_infoframe ai = {
473 .type = 0x84, 523 .type = 0x84,
474 .ver = 0x01, 524 .ver = 0x01,
@@ -479,8 +529,16 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid,
479 hdmi_setup_channel_allocation(codec, nid, &ai); 529 hdmi_setup_channel_allocation(codec, nid, &ai);
480 hdmi_setup_channel_mapping(codec, nid, &ai); 530 hdmi_setup_channel_mapping(codec, nid, &ai);
481 531
482 hdmi_fill_audio_infoframe(codec, pin_nid, &ai); 532 for (i = 0; i < spec->num_pins; i++) {
483 hdmi_start_infoframe_trans(codec, pin_nid); 533 if (spec->pin_cvt[i] != nid)
534 continue;
535 if (spec->sink_present[i] != true)
536 continue;
537
538 pin_nid = spec->pin[i];
539 hdmi_fill_audio_infoframe(codec, pin_nid, &ai);
540 hdmi_start_infoframe_trans(codec, pin_nid);
541 }
484} 542}
485 543
486 544
@@ -490,27 +548,39 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid,
490 548
491static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) 549static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
492{ 550{
551 struct intel_hdmi_spec *spec = codec->spec;
552 int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
493 int pind = !!(res & AC_UNSOL_RES_PD); 553 int pind = !!(res & AC_UNSOL_RES_PD);
494 int eldv = !!(res & AC_UNSOL_RES_ELDV); 554 int eldv = !!(res & AC_UNSOL_RES_ELDV);
555 int index;
495 556
496 printk(KERN_INFO 557 printk(KERN_INFO
497 "HDMI hot plug event: Presence_Detect=%d ELD_Valid=%d\n", 558 "HDMI hot plug event: Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
498 pind, eldv); 559 tag, pind, eldv);
560
561 index = hda_node_index(spec->pin, tag);
562 if (index < 0)
563 return;
564
565 spec->sink_present[index] = pind;
566 spec->sink_eldv[index] = eldv;
499 567
500 if (pind && eldv) { 568 if (pind && eldv) {
501 hdmi_parse_eld(codec); 569 hdmi_parse_eld(codec, index);
502 /* TODO: do real things about ELD */ 570 /* TODO: do real things about ELD */
503 } 571 }
504} 572}
505 573
506static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res) 574static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
507{ 575{
576 int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
508 int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT; 577 int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
509 int cp_state = !!(res & AC_UNSOL_RES_CP_STATE); 578 int cp_state = !!(res & AC_UNSOL_RES_CP_STATE);
510 int cp_ready = !!(res & AC_UNSOL_RES_CP_READY); 579 int cp_ready = !!(res & AC_UNSOL_RES_CP_READY);
511 580
512 printk(KERN_INFO 581 printk(KERN_INFO
513 "HDMI content protection event: SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n", 582 "HDMI CP event: PIN=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n",
583 tag,
514 subtag, 584 subtag,
515 cp_state, 585 cp_state,
516 cp_ready); 586 cp_ready);
@@ -525,10 +595,11 @@ static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
525 595
526static void intel_hdmi_unsol_event(struct hda_codec *codec, unsigned int res) 596static void intel_hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
527{ 597{
598 struct intel_hdmi_spec *spec = codec->spec;
528 int tag = res >> AC_UNSOL_RES_TAG_SHIFT; 599 int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
529 int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT; 600 int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
530 601
531 if (tag != INTEL_HDMI_EVENT_TAG) { 602 if (hda_node_index(spec->pin, tag) < 0) {
532 snd_printd(KERN_INFO "Unexpected HDMI event tag 0x%x\n", tag); 603 snd_printd(KERN_INFO "Unexpected HDMI event tag 0x%x\n", tag);
533 return; 604 return;
534 } 605 }
@@ -549,10 +620,10 @@ static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
549 unsigned int format, 620 unsigned int format,
550 struct snd_pcm_substream *substream) 621 struct snd_pcm_substream *substream)
551{ 622{
552 hdmi_set_channel_count(codec, cvt_nid, 623 hdmi_set_channel_count(codec, hinfo->nid,
553 substream->runtime->channels); 624 substream->runtime->channels);
554 625
555 hdmi_setup_audio_infoframe(codec, cvt_nid, substream); 626 hdmi_setup_audio_infoframe(codec, hinfo->nid, substream);
556 627
557 snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); 628 snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format);
558 return 0; 629 return 0;
@@ -563,8 +634,14 @@ static int intel_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
563 struct snd_pcm_substream *substream) 634 struct snd_pcm_substream *substream)
564{ 635{
565 struct intel_hdmi_spec *spec = codec->spec; 636 struct intel_hdmi_spec *spec = codec->spec;
637 int i;
638
639 for (i = 0; i < spec->num_pins; i++) {
640 if (spec->pin_cvt[i] != hinfo->nid)
641 continue;
566 642
567 hdmi_stop_infoframe_trans(codec, pin_nid); 643 hdmi_stop_infoframe_trans(codec, spec->pin[i]);
644 }
568 645
569 snd_hda_codec_cleanup_stream(codec, hinfo->nid); 646 snd_hda_codec_cleanup_stream(codec, hinfo->nid);
570 return 0; 647 return 0;
@@ -583,17 +660,19 @@ static struct hda_pcm_stream intel_hdmi_pcm_playback = {
583static int intel_hdmi_build_pcms(struct hda_codec *codec) 660static int intel_hdmi_build_pcms(struct hda_codec *codec)
584{ 661{
585 struct intel_hdmi_spec *spec = codec->spec; 662 struct intel_hdmi_spec *spec = codec->spec;
586 struct hda_pcm *info = &spec->pcm_rec; 663 struct hda_pcm *info = spec->pcm_rec;
664 int i;
587 665
588 codec->num_pcms = 1; 666 codec->num_pcms = spec->num_cvts;
589 codec->pcm_info = info; 667 codec->pcm_info = info;
590 668
591 /* NID to query formats and rates and setup streams */ 669 for (i = 0; i < codec->num_pcms; i++, info++) {
592 intel_hdmi_pcm_playback.nid = cvt_nid; 670 info->name = intel_hdmi_pcm_names[i];
593 671 info->pcm_type = HDA_PCM_TYPE_HDMI;
594 info->name = "INTEL HDMI"; 672 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
595 info->pcm_type = HDA_PCM_TYPE_HDMI; 673 intel_hdmi_pcm_playback;
596 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = intel_hdmi_pcm_playback; 674 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->cvt[i];
675 }
597 676
598 return 0; 677 return 0;
599} 678}
@@ -602,29 +681,39 @@ static int intel_hdmi_build_controls(struct hda_codec *codec)
602{ 681{
603 struct intel_hdmi_spec *spec = codec->spec; 682 struct intel_hdmi_spec *spec = codec->spec;
604 int err; 683 int err;
684 int i;
605 685
606 err = snd_hda_create_spdif_out_ctls(codec, cvt_nid); 686 for (i = 0; i < codec->num_pcms; i++) {
607 if (err < 0) 687 err = snd_hda_create_spdif_out_ctls(codec, spec->cvt[i]);
608 return err; 688 if (err < 0)
689 return err;
690 }
609 691
610 return 0; 692 return 0;
611} 693}
612 694
613static int intel_hdmi_init(struct hda_codec *codec) 695static int intel_hdmi_init(struct hda_codec *codec)
614{ 696{
615 hdmi_enable_output(codec, pin_nid); 697 struct intel_hdmi_spec *spec = codec->spec;
698 int i;
616 699
617 snd_hda_codec_write(codec, pin_nid, 0, 700 for (i = 0; spec->pin[i]; i++) {
618 AC_VERB_SET_UNSOLICITED_ENABLE, 701 hdmi_enable_output(codec, spec->pin[i]);
619 AC_USRSP_EN | INTEL_HDMI_EVENT_TAG); 702 snd_hda_codec_write(codec, spec->pin[i], 0,
703 AC_VERB_SET_UNSOLICITED_ENABLE,
704 AC_USRSP_EN | spec->pin[i]);
705 }
620 return 0; 706 return 0;
621} 707}
622 708
623static void intel_hdmi_free(struct hda_codec *codec) 709static void intel_hdmi_free(struct hda_codec *codec)
624{ 710{
625 struct intel_hdmi_spec *spec = codec->spec; 711 struct intel_hdmi_spec *spec = codec->spec;
712 int i;
713
714 for (i = 0; i < spec->num_pins; i++)
715 snd_hda_eld_proc_free(codec, &spec->sink_eld[i]);
626 716
627 snd_hda_eld_proc_free(codec, &spec->sink_eld);
628 kfree(spec); 717 kfree(spec);
629} 718}
630 719
@@ -636,18 +725,38 @@ static struct hda_codec_ops intel_hdmi_patch_ops = {
636 .unsol_event = intel_hdmi_unsol_event, 725 .unsol_event = intel_hdmi_unsol_event,
637}; 726};
638 727
639static int do_patch_intel_hdmi(struct hda_codec *codec) 728static struct intel_hdmi_spec static_specs[] = {
729 {
730 .num_cvts = 1,
731 .num_pins = 1,
732 .cvt = { 0x2 },
733 .pin = { 0x3 },
734 .pin_cvt = { 0x2 },
735 },
736 {
737 .num_cvts = 2,
738 .num_pins = 3,
739 .cvt = { 0x2, 0x3 },
740 .pin = { 0x4, 0x5, 0x6 },
741 .pin_cvt = { 0x2, 0x2, 0x2 },
742 },
743};
744
745static int do_patch_intel_hdmi(struct hda_codec *codec, int spec_id)
640{ 746{
641 struct intel_hdmi_spec *spec; 747 struct intel_hdmi_spec *spec;
748 int i;
642 749
643 spec = kzalloc(sizeof(*spec), GFP_KERNEL); 750 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
644 if (spec == NULL) 751 if (spec == NULL)
645 return -ENOMEM; 752 return -ENOMEM;
646 753
754 *spec = static_specs[spec_id];
647 codec->spec = spec; 755 codec->spec = spec;
648 codec->patch_ops = intel_hdmi_patch_ops; 756 codec->patch_ops = intel_hdmi_patch_ops;
649 757
650 snd_hda_eld_proc_new(codec, &spec->sink_eld); 758 for (i = 0; i < spec->num_pins; i++)
759 snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i);
651 760
652 init_channel_allocations(); 761 init_channel_allocations();
653 762
@@ -656,16 +765,12 @@ static int do_patch_intel_hdmi(struct hda_codec *codec)
656 765
657static int patch_intel_hdmi(struct hda_codec *codec) 766static int patch_intel_hdmi(struct hda_codec *codec)
658{ 767{
659 cvt_nid = 0x02; 768 return do_patch_intel_hdmi(codec, 0);
660 pin_nid = 0x03;
661 return do_patch_intel_hdmi(codec);
662} 769}
663 770
664static int patch_intel_hdmi_ibexpeak(struct hda_codec *codec) 771static int patch_intel_hdmi_ibexpeak(struct hda_codec *codec)
665{ 772{
666 cvt_nid = 0x02; 773 return do_patch_intel_hdmi(codec, 1);
667 pin_nid = 0x04;
668 return do_patch_intel_hdmi(codec);
669} 774}
670 775
671static struct hda_codec_preset snd_hda_preset_intelhdmi[] = { 776static struct hda_codec_preset snd_hda_preset_intelhdmi[] = {