diff options
author | Stephen Warren <swarren@nvidia.com> | 2011-05-24 19:11:17 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2011-05-25 01:31:32 -0400 |
commit | 5d44f927a5467d64c4f1e0d579bcb3f543c275e0 (patch) | |
tree | 87a270a36fee088f59bee2fd2d231e8f7dc656ca /sound | |
parent | 4e60b4f8300c1e15753e8c82c699d52603646200 (diff) |
ALSA: HDA: Unify HDMI hotplug handling.
This change unifies the initial handling of a pin's state with the code to
update a pin's state after a hotplug (unsolicited response) event. The
initial probing, and all updates, are now routed through hdmi_present_sense.
The stored PD and ELDV status is now always derived from GetPinSense verb
execution, and not from the data in the unsolicited response. This means:
a) The WAR for NVIDIA codec's UR.PD values ("old_pin_detect") can be
removed, since this only affected the no-longer-used unsolicited
response payload.
b) In turn, this means that most NVIDIA codecs can simply use
patch_generic_hdmi instead of having a custom variant just to set
old_pin_detect.
c) When PD && ELDV becomes true, no extra verbs are executed, because the
GetPinSense that was previously executed by snd_hdmi_get_eld (really,
hdmi_eld_valid) has simply moved into hdmi_present_sense.
d) When PD && ELDV becomes false, there is a single extra GetPinSense verb
executed for codecs where old_pin_detect wasn't set, i.e. some NVIDIA,
and all ATI/AMD and Intel codecs. I doubt this will be a performance
issue.
The new unified code in hdmi_present_sense also ensures that eld->eld_valid
is not set unless eld->monitor_present is also set. This protects against
potential invalid combinations of PD and ELDV received from HW, and
transitively from a graphics driver.
Also, print the derived PD/ELDV bits from hdmi_present_sense so the kernel
log always displays the actual state stored, which will differ from the
values in the unsolicited response for NVIDIA HW where old_pin_detect was
previously set.
Finally, a couple of small tweaks originally by Takashi:
* Clear the ELD content to zero before reading it, so that if it's not
read (i.e. when !(PD && ELDV)) it's in a known state.
* Don't show ELD fields in /proc ELD files when the ELD isn't valid.
The only possibility I can see for regression here is a codec where the
GetPinSense verb returns incorrect data. However, we're already exposed
to that, since that data is used (a) from hdmi_add_pin to set up the
initial pin state, and (b) within snd_hda_input_jack_report to query
a pin's presence value. As such, I don't believe any HW has bugs here.
Includes-changes-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Stephen Warren <swarren@nvidia.com>
Acked-by: Wu Fengguang <fengguang.wu@intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/pci/hda/hda_eld.c | 21 | ||||
-rw-r--r-- | sound/pci/hda/patch_hdmi.c | 118 |
2 files changed, 52 insertions, 87 deletions
diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index 74b0560289c0..b05f7be9dc1b 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c | |||
@@ -312,23 +312,6 @@ out_fail: | |||
312 | return -EINVAL; | 312 | return -EINVAL; |
313 | } | 313 | } |
314 | 314 | ||
315 | static int hdmi_eld_valid(struct hda_codec *codec, hda_nid_t nid) | ||
316 | { | ||
317 | int eldv; | ||
318 | int present; | ||
319 | |||
320 | present = snd_hda_pin_sense(codec, nid); | ||
321 | eldv = (present & AC_PINSENSE_ELDV); | ||
322 | present = (present & AC_PINSENSE_PRESENCE); | ||
323 | |||
324 | #ifdef CONFIG_SND_DEBUG_VERBOSE | ||
325 | printk(KERN_INFO "HDMI: sink_present = %d, eld_valid = %d\n", | ||
326 | !!present, !!eldv); | ||
327 | #endif | ||
328 | |||
329 | return eldv && present; | ||
330 | } | ||
331 | |||
332 | int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid) | 315 | int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid) |
333 | { | 316 | { |
334 | return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE, | 317 | return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE, |
@@ -343,7 +326,7 @@ int snd_hdmi_get_eld(struct hdmi_eld *eld, | |||
343 | int size; | 326 | int size; |
344 | unsigned char *buf; | 327 | unsigned char *buf; |
345 | 328 | ||
346 | if (!hdmi_eld_valid(codec, nid)) | 329 | if (!eld->eld_valid) |
347 | return -ENOENT; | 330 | return -ENOENT; |
348 | 331 | ||
349 | size = snd_hdmi_get_eld_size(codec, nid); | 332 | size = snd_hdmi_get_eld_size(codec, nid); |
@@ -477,6 +460,8 @@ static void hdmi_print_eld_info(struct snd_info_entry *entry, | |||
477 | 460 | ||
478 | snd_iprintf(buffer, "monitor_present\t\t%d\n", e->monitor_present); | 461 | snd_iprintf(buffer, "monitor_present\t\t%d\n", e->monitor_present); |
479 | snd_iprintf(buffer, "eld_valid\t\t%d\n", e->eld_valid); | 462 | snd_iprintf(buffer, "eld_valid\t\t%d\n", e->eld_valid); |
463 | if (!e->eld_valid) | ||
464 | return; | ||
480 | snd_iprintf(buffer, "monitor_name\t\t%s\n", e->monitor_name); | 465 | snd_iprintf(buffer, "monitor_name\t\t%s\n", e->monitor_name); |
481 | snd_iprintf(buffer, "connection_type\t\t%s\n", | 466 | snd_iprintf(buffer, "connection_type\t\t%s\n", |
482 | eld_connection_type_names[e->conn_type]); | 467 | eld_connection_type_names[e->conn_type]); |
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 322901873222..d786ecc56801 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c | |||
@@ -78,10 +78,6 @@ struct hdmi_spec { | |||
78 | */ | 78 | */ |
79 | struct hda_multi_out multiout; | 79 | struct hda_multi_out multiout; |
80 | const struct hda_pcm_stream *pcm_playback; | 80 | const struct hda_pcm_stream *pcm_playback; |
81 | |||
82 | /* misc flags */ | ||
83 | /* PD bit indicates only the update, not the current state */ | ||
84 | unsigned int old_pin_detect:1; | ||
85 | }; | 81 | }; |
86 | 82 | ||
87 | 83 | ||
@@ -300,13 +296,6 @@ static int hda_node_index(hda_nid_t *nids, hda_nid_t nid) | |||
300 | return -EINVAL; | 296 | return -EINVAL; |
301 | } | 297 | } |
302 | 298 | ||
303 | static void hdmi_get_show_eld(struct hda_codec *codec, hda_nid_t pin_nid, | ||
304 | struct hdmi_eld *eld) | ||
305 | { | ||
306 | if (!snd_hdmi_get_eld(eld, codec, pin_nid)) | ||
307 | snd_hdmi_show_eld(eld); | ||
308 | } | ||
309 | |||
310 | #ifdef BE_PARANOID | 299 | #ifdef BE_PARANOID |
311 | static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t pin_nid, | 300 | static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t pin_nid, |
312 | int *packet_index, int *byte_index) | 301 | int *packet_index, int *byte_index) |
@@ -694,35 +683,20 @@ static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid, | |||
694 | static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) | 683 | static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) |
695 | { | 684 | { |
696 | struct hdmi_spec *spec = codec->spec; | 685 | struct hdmi_spec *spec = codec->spec; |
697 | int tag = res >> AC_UNSOL_RES_TAG_SHIFT; | 686 | int pin_nid = res >> AC_UNSOL_RES_TAG_SHIFT; |
698 | int pind = !!(res & AC_UNSOL_RES_PD); | 687 | int pd = !!(res & AC_UNSOL_RES_PD); |
699 | int eldv = !!(res & AC_UNSOL_RES_ELDV); | 688 | int eldv = !!(res & AC_UNSOL_RES_ELDV); |
700 | int index; | 689 | int index; |
701 | 690 | ||
702 | printk(KERN_INFO | 691 | printk(KERN_INFO |
703 | "HDMI hot plug event: Pin=%d Presence_Detect=%d ELD_Valid=%d\n", | 692 | "HDMI hot plug event: Pin=%d Presence_Detect=%d ELD_Valid=%d\n", |
704 | tag, pind, eldv); | 693 | pin_nid, pd, eldv); |
705 | 694 | ||
706 | index = hda_node_index(spec->pin, tag); | 695 | index = hda_node_index(spec->pin, pin_nid); |
707 | if (index < 0) | 696 | if (index < 0) |
708 | return; | 697 | return; |
709 | 698 | ||
710 | if (spec->old_pin_detect) { | 699 | hdmi_present_sense(codec, pin_nid, &spec->sink_eld[index]); |
711 | if (pind) | ||
712 | hdmi_present_sense(codec, tag, &spec->sink_eld[index]); | ||
713 | pind = spec->sink_eld[index].monitor_present; | ||
714 | } | ||
715 | |||
716 | spec->sink_eld[index].monitor_present = pind; | ||
717 | spec->sink_eld[index].eld_valid = eldv; | ||
718 | |||
719 | if (pind && eldv) { | ||
720 | hdmi_get_show_eld(codec, spec->pin[index], | ||
721 | &spec->sink_eld[index]); | ||
722 | /* TODO: do real things about ELD */ | ||
723 | } | ||
724 | |||
725 | snd_hda_input_jack_report(codec, tag); | ||
726 | } | 700 | } |
727 | 701 | ||
728 | static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res) | 702 | static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res) |
@@ -903,13 +877,33 @@ static int hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid) | |||
903 | static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid, | 877 | static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid, |
904 | struct hdmi_eld *eld) | 878 | struct hdmi_eld *eld) |
905 | { | 879 | { |
880 | /* | ||
881 | * Always execute a GetPinSense verb here, even when called from | ||
882 | * hdmi_intrinsic_event; for some NVIDIA HW, the unsolicited | ||
883 | * response's PD bit is not the real PD value, but indicates that | ||
884 | * the real PD value changed. An older version of the HD-audio | ||
885 | * specification worked this way. Hence, we just ignore the data in | ||
886 | * the unsolicited response to avoid custom WARs. | ||
887 | */ | ||
906 | int present = snd_hda_pin_sense(codec, pin_nid); | 888 | int present = snd_hda_pin_sense(codec, pin_nid); |
907 | 889 | ||
890 | memset(eld, 0, sizeof(*eld)); | ||
891 | |||
908 | eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE); | 892 | eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE); |
909 | eld->eld_valid = !!(present & AC_PINSENSE_ELDV); | 893 | if (eld->monitor_present) |
894 | eld->eld_valid = !!(present & AC_PINSENSE_ELDV); | ||
895 | else | ||
896 | eld->eld_valid = 0; | ||
910 | 897 | ||
911 | if (present & AC_PINSENSE_ELDV) | 898 | printk(KERN_INFO |
912 | hdmi_get_show_eld(codec, pin_nid, eld); | 899 | "HDMI status: Pin=%d Presence_Detect=%d ELD_Valid=%d\n", |
900 | pin_nid, eld->monitor_present, eld->eld_valid); | ||
901 | |||
902 | if (eld->eld_valid) | ||
903 | if (!snd_hdmi_get_eld(eld, codec, pin_nid)) | ||
904 | snd_hdmi_show_eld(eld); | ||
905 | |||
906 | snd_hda_input_jack_report(codec, pin_nid); | ||
913 | } | 907 | } |
914 | 908 | ||
915 | static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid) | 909 | static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid) |
@@ -927,7 +921,6 @@ static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid) | |||
927 | SND_JACK_VIDEOOUT, NULL); | 921 | SND_JACK_VIDEOOUT, NULL); |
928 | if (err < 0) | 922 | if (err < 0) |
929 | return err; | 923 | return err; |
930 | snd_hda_input_jack_report(codec, pin_nid); | ||
931 | 924 | ||
932 | hdmi_present_sense(codec, pin_nid, &spec->sink_eld[spec->num_pins]); | 925 | hdmi_present_sense(codec, pin_nid, &spec->sink_eld[spec->num_pins]); |
933 | 926 | ||
@@ -1490,18 +1483,6 @@ static const struct hda_codec_ops nvhdmi_patch_ops_2ch = { | |||
1490 | .free = generic_hdmi_free, | 1483 | .free = generic_hdmi_free, |
1491 | }; | 1484 | }; |
1492 | 1485 | ||
1493 | static int patch_nvhdmi_8ch_89(struct hda_codec *codec) | ||
1494 | { | ||
1495 | struct hdmi_spec *spec; | ||
1496 | int err = patch_generic_hdmi(codec); | ||
1497 | |||
1498 | if (err < 0) | ||
1499 | return err; | ||
1500 | spec = codec->spec; | ||
1501 | spec->old_pin_detect = 1; | ||
1502 | return 0; | ||
1503 | } | ||
1504 | |||
1505 | static int patch_nvhdmi_2ch(struct hda_codec *codec) | 1486 | static int patch_nvhdmi_2ch(struct hda_codec *codec) |
1506 | { | 1487 | { |
1507 | struct hdmi_spec *spec; | 1488 | struct hdmi_spec *spec; |
@@ -1515,7 +1496,6 @@ static int patch_nvhdmi_2ch(struct hda_codec *codec) | |||
1515 | spec->multiout.num_dacs = 0; /* no analog */ | 1496 | spec->multiout.num_dacs = 0; /* no analog */ |
1516 | spec->multiout.max_channels = 2; | 1497 | spec->multiout.max_channels = 2; |
1517 | spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x; | 1498 | spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x; |
1518 | spec->old_pin_detect = 1; | ||
1519 | spec->num_cvts = 1; | 1499 | spec->num_cvts = 1; |
1520 | spec->cvt[0] = nvhdmi_master_con_nid_7x; | 1500 | spec->cvt[0] = nvhdmi_master_con_nid_7x; |
1521 | spec->pcm_playback = &nvhdmi_pcm_playback_2ch; | 1501 | spec->pcm_playback = &nvhdmi_pcm_playback_2ch; |
@@ -1658,28 +1638,28 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = { | |||
1658 | { .id = 0x10de0005, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x }, | 1638 | { .id = 0x10de0005, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x }, |
1659 | { .id = 0x10de0006, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x }, | 1639 | { .id = 0x10de0006, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x }, |
1660 | { .id = 0x10de0007, .name = "MCP79/7A HDMI", .patch = patch_nvhdmi_8ch_7x }, | 1640 | { .id = 0x10de0007, .name = "MCP79/7A HDMI", .patch = patch_nvhdmi_8ch_7x }, |
1661 | { .id = 0x10de000a, .name = "GPU 0a HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, | 1641 | { .id = 0x10de000a, .name = "GPU 0a HDMI/DP", .patch = patch_generic_hdmi }, |
1662 | { .id = 0x10de000b, .name = "GPU 0b HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, | 1642 | { .id = 0x10de000b, .name = "GPU 0b HDMI/DP", .patch = patch_generic_hdmi }, |
1663 | { .id = 0x10de000c, .name = "MCP89 HDMI", .patch = patch_nvhdmi_8ch_89 }, | 1643 | { .id = 0x10de000c, .name = "MCP89 HDMI", .patch = patch_generic_hdmi }, |
1664 | { .id = 0x10de000d, .name = "GPU 0d HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, | 1644 | { .id = 0x10de000d, .name = "GPU 0d HDMI/DP", .patch = patch_generic_hdmi }, |
1665 | { .id = 0x10de0010, .name = "GPU 10 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, | 1645 | { .id = 0x10de0010, .name = "GPU 10 HDMI/DP", .patch = patch_generic_hdmi }, |
1666 | { .id = 0x10de0011, .name = "GPU 11 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, | 1646 | { .id = 0x10de0011, .name = "GPU 11 HDMI/DP", .patch = patch_generic_hdmi }, |
1667 | { .id = 0x10de0012, .name = "GPU 12 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, | 1647 | { .id = 0x10de0012, .name = "GPU 12 HDMI/DP", .patch = patch_generic_hdmi }, |
1668 | { .id = 0x10de0013, .name = "GPU 13 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, | 1648 | { .id = 0x10de0013, .name = "GPU 13 HDMI/DP", .patch = patch_generic_hdmi }, |
1669 | { .id = 0x10de0014, .name = "GPU 14 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, | 1649 | { .id = 0x10de0014, .name = "GPU 14 HDMI/DP", .patch = patch_generic_hdmi }, |
1670 | { .id = 0x10de0015, .name = "GPU 15 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, | 1650 | { .id = 0x10de0015, .name = "GPU 15 HDMI/DP", .patch = patch_generic_hdmi }, |
1671 | { .id = 0x10de0016, .name = "GPU 16 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, | 1651 | { .id = 0x10de0016, .name = "GPU 16 HDMI/DP", .patch = patch_generic_hdmi }, |
1672 | /* 17 is known to be absent */ | 1652 | /* 17 is known to be absent */ |
1673 | { .id = 0x10de0018, .name = "GPU 18 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, | 1653 | { .id = 0x10de0018, .name = "GPU 18 HDMI/DP", .patch = patch_generic_hdmi }, |
1674 | { .id = 0x10de0019, .name = "GPU 19 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, | 1654 | { .id = 0x10de0019, .name = "GPU 19 HDMI/DP", .patch = patch_generic_hdmi }, |
1675 | { .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, | 1655 | { .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_generic_hdmi }, |
1676 | { .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, | 1656 | { .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_generic_hdmi }, |
1677 | { .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, | 1657 | { .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_generic_hdmi }, |
1678 | { .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, | 1658 | { .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_generic_hdmi }, |
1679 | { .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, | 1659 | { .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_generic_hdmi }, |
1680 | { .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, | 1660 | { .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_generic_hdmi }, |
1681 | { .id = 0x10de0043, .name = "GPU 43 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, | 1661 | { .id = 0x10de0043, .name = "GPU 43 HDMI/DP", .patch = patch_generic_hdmi }, |
1682 | { .id = 0x10de0044, .name = "GPU 44 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, | 1662 | { .id = 0x10de0044, .name = "GPU 44 HDMI/DP", .patch = patch_generic_hdmi }, |
1683 | { .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch }, | 1663 | { .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch }, |
1684 | { .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch }, | 1664 | { .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch }, |
1685 | { .id = 0x80860054, .name = "IbexPeak HDMI", .patch = patch_generic_hdmi }, | 1665 | { .id = 0x80860054, .name = "IbexPeak HDMI", .patch = patch_generic_hdmi }, |