ALSA: hda/hdmi - allow PIN_OUT to be dynamically enabled

Commit 384a48d71520 "ALSA: hda: HDMI: Support codecs with fewer cvts
than pins" dynamically enabled each pin widget's PIN_OUT only when the
pin was actively in use. This was required on certain NVIDIA CODECs for
correct operation. Specifically, if multiple pin widgets each had their
mux input select the same audio converter widget and each pin widget had
PIN_OUT enabled, then only one of the pin widgets would actually receive
the audio, and often not the one the user wanted!

However, this apparently broke some Intel systems, and commit
6169b673618b "ALSA: hda - Always turn on pins for HDMI/DP" reverted the
dynamic setting of PIN_OUT. This in turn broke the afore-mentioned NVIDIA
CODECs.

This change supports either dynamic or static handling of PIN_OUT,
selected by a flag set up during CODEC initialization. This flag is
enabled for all recent NVIDIA GPUs.

Reported-by: Uosis <uosisl@gmail.com>
Cc: <stable@vger.kernel.org> # v3.13
Signed-off-by: Stephen Warren <swarren@nvidia.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>

authored by Stephen Warren and committed by Takashi Iwai 75fae117 5e87d580

+36 -4
+36 -4
sound/pci/hda/patch_hdmi.c
··· 132 132 133 133 struct hdmi_eld temp_eld; 134 134 struct hdmi_ops ops; 135 + 136 + bool dyn_pin_out; 137 + 135 138 /* 136 139 * Non-generic VIA/NVIDIA specific 137 140 */ ··· 503 500 504 501 static void hdmi_init_pin(struct hda_codec *codec, hda_nid_t pin_nid) 505 502 { 503 + struct hdmi_spec *spec = codec->spec; 504 + int pin_out; 505 + 506 506 /* Unmute */ 507 507 if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP) 508 508 snd_hda_codec_write(codec, pin_nid, 0, 509 509 AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); 510 - /* Enable pin out: some machines with GM965 gets broken output when 511 - * the pin is disabled or changed while using with HDMI 512 - */ 510 + 511 + if (spec->dyn_pin_out) 512 + /* Disable pin out until stream is active */ 513 + pin_out = 0; 514 + else 515 + /* Enable pin out: some machines with GM965 gets broken output 516 + * when the pin is disabled or changed while using with HDMI 517 + */ 518 + pin_out = PIN_OUT; 519 + 513 520 snd_hda_codec_write(codec, pin_nid, 0, 514 - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); 521 + AC_VERB_SET_PIN_WIDGET_CONTROL, pin_out); 515 522 } 516 523 517 524 static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t cvt_nid) ··· 1748 1735 struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); 1749 1736 hda_nid_t pin_nid = per_pin->pin_nid; 1750 1737 bool non_pcm; 1738 + int pinctl; 1751 1739 1752 1740 non_pcm = check_non_pcm_per_cvt(codec, cvt_nid); 1753 1741 mutex_lock(&per_pin->lock); ··· 1757 1743 1758 1744 hdmi_setup_audio_infoframe(codec, per_pin, non_pcm); 1759 1745 mutex_unlock(&per_pin->lock); 1746 + 1747 + if (spec->dyn_pin_out) { 1748 + pinctl = snd_hda_codec_read(codec, pin_nid, 0, 1749 + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); 1750 + snd_hda_codec_write(codec, pin_nid, 0, 1751 + AC_VERB_SET_PIN_WIDGET_CONTROL, 1752 + pinctl | PIN_OUT); 1753 + } 1760 1754 1761 1755 return spec->ops.setup_stream(codec, cvt_nid, pin_nid, stream_tag, format); 1762 1756 } ··· 1785 1763 int cvt_idx, pin_idx; 1786 1764 struct hdmi_spec_per_cvt *per_cvt; 1787 1765 struct hdmi_spec_per_pin *per_pin; 1766 + int pinctl; 1788 1767 1789 1768 if (hinfo->nid) { 1790 1769 cvt_idx = cvt_nid_to_cvt_index(spec, hinfo->nid); ··· 1801 1778 if (snd_BUG_ON(pin_idx < 0)) 1802 1779 return -EINVAL; 1803 1780 per_pin = get_pin(spec, pin_idx); 1781 + 1782 + if (spec->dyn_pin_out) { 1783 + pinctl = snd_hda_codec_read(codec, per_pin->pin_nid, 0, 1784 + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); 1785 + snd_hda_codec_write(codec, per_pin->pin_nid, 0, 1786 + AC_VERB_SET_PIN_WIDGET_CONTROL, 1787 + pinctl & ~PIN_OUT); 1788 + } 1804 1789 1805 1790 snd_hda_spdif_ctls_unassign(codec, pin_idx); 1806 1791 ··· 2871 2840 return err; 2872 2841 2873 2842 spec = codec->spec; 2843 + spec->dyn_pin_out = true; 2874 2844 2875 2845 spec->ops.chmap_cea_alloc_validate_get_type = 2876 2846 nvhdmi_chmap_cea_alloc_validate_get_type;