From 5f3bfa9f618d08ecc9c681e20bfab210ad0d2a06 Mon Sep 17 00:00:00 2001
From: Takashi Iwai <tiwai@suse.de>
Date: Fri, 1 Feb 2013 14:04:12 +0100
Subject: [PATCH 2/2] ALSA: hda - Yet another fix for broken HSW HDMI pin connections
Git-commit: c88d4e84e639df9a9640ecff71de2501a84d1f48
Patch-mainline: v3.9-rc1
Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
References: bnc#801713

A Haswell test machine showed that the invalid connection list, but
this time it has only a single pin on the codec, thus the former fixup
code doesn't work as it assumes the three pins blindly.

This patch splits the former fixup code to two parts:
- Enable eDP 1.2 for Haswell codec
- Fix the connection list of pins on Haswell codec;
  the converter list is recorded dynamically in hdmi_add_cvt(), and
  applied in hdmi_add_pin()

Signed-off-by: Takashi Iwai <tiwai@suse.de>

---
 sound/pci/hda/patch_hdmi.c |   35 +++++++++++++++++++++++++----------
 1 file changed, 25 insertions(+), 10 deletions(-)

--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -79,6 +79,7 @@ struct hdmi_spec_per_pin {
 struct hdmi_spec {
 	int num_cvts;
 	struct hdmi_spec_per_cvt cvts[MAX_HDMI_CVTS];
+	hda_nid_t cvt_nids[MAX_HDMI_CVTS];
 
 	int num_pins;
 	struct hdmi_spec_per_pin pins[MAX_HDMI_PINS];
@@ -1051,6 +1052,9 @@ static void hdmi_repoll_eld(struct work_
 	hdmi_present_sense(per_pin, per_pin->repoll_count);
 }
 
+static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
+					     hda_nid_t nid);
+
 static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
 {
 	struct hdmi_spec *spec = codec->spec;
@@ -1071,6 +1075,9 @@ static int hdmi_add_pin(struct hda_codec
 	if (snd_BUG_ON(spec->num_pins >= MAX_HDMI_PINS))
 		return -E2BIG;
 
+	if (codec->vendor_id == 0x80862807)
+		intel_haswell_fixup_connect_list(codec, pin_nid);
+
 	pin_idx = spec->num_pins;
 	per_pin = &spec->pins[pin_idx];
 
@@ -1115,6 +1122,7 @@ static int hdmi_add_cvt(struct hda_codec
 	if (err < 0)
 		return err;
 
+	spec->cvt_nids[spec->num_cvts] = cvt_nid;
 	spec->num_cvts++;
 
 	return 0;
@@ -1389,10 +1397,9 @@ static const struct hda_codec_ops generi
 	.unsol_event		= hdmi_unsol_event,
 };
 
-static void intel_haswell_fixup_connect_list(struct hda_codec *codec)
+static void intel_haswell_fixup_enable_dp12(struct hda_codec *codec)
 {
 	unsigned int vendor_param;
-	hda_nid_t list[3] = {0x2, 0x3, 0x4};
 
 	vendor_param = snd_hda_codec_read(codec, 0x08, 0, 0xf81, 0);
 	if (vendor_param == -1 || vendor_param & 0x02)
@@ -1400,16 +1407,24 @@ static void intel_haswell_fixup_connect_
 
 	/* enable DP1.2 mode */
 	vendor_param |= 0x02;
-	snd_hda_codec_read(codec, 0x08, 0, 0x781, vendor_param);
+	snd_hda_codec_write_cache(codec, 0x08, 0, 0x781, vendor_param);
+}
 
-	vendor_param = snd_hda_codec_read(codec, 0x08, 0, 0xf81, 0);
-	if (vendor_param == -1 || !(vendor_param & 0x02))
+static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
+					     hda_nid_t nid)
+{
+	struct hdmi_spec *spec = codec->spec;
+	hda_nid_t conns[4];
+	int nconns;
+
+	nconns = snd_hda_get_connections(codec, nid, conns, ARRAY_SIZE(conns));
+	if (nconns == spec->num_cvts &&
+	    !memcmp(conns, spec->cvt_nids, spec->num_cvts * sizeof(hda_nid_t)))
 		return;
 
-	/* override 3 pins connection list */
-	snd_hda_override_conn_list(codec, 0x05, 3, list);
-	snd_hda_override_conn_list(codec, 0x06, 3, list);
-	snd_hda_override_conn_list(codec, 0x07, 3, list);
+	/* override pins connection list */
+	snd_printdd("hdmi: haswell: override pin connection 0x%x\n", nid);
+	snd_hda_override_conn_list(codec, nid, spec->num_cvts, spec->cvt_nids);
 }
 
 #define INTEL_VENDOR_NID 0x08
@@ -1452,7 +1467,7 @@ static int patch_generic_hdmi(struct hda
 		intel_haswell_enable_all_pins(codec);
 
 	if (codec->vendor_id == 0x80862807)
-		intel_haswell_fixup_connect_list(codec);
+		intel_haswell_fixup_enable_dp12(codec);
 
 	if (hdmi_parse_codec(codec) < 0) {
 		codec->spec = NULL;
