From 185d99f1924ee0047bcd524c58a01c9f8d58d673 Mon Sep 17 00:00:00 2001
From: Takashi Iwai <tiwai@suse.de>
Date: Thu, 16 Feb 2012 18:39:45 +0100
Subject: [PATCH] ALSA: hda/realtek - Try harder to fit the single-connections
Git-commit: 185d99f1924ee0047bcd524c58a01c9f8d58d673
Patch-mainline: 3.4-rc3

So far, the Realtek driver tires to assign the single-connected routes
for all pins only once at the beginning.  However, since some DACs have
been already mapped, the rest pins might have also single conections.

In this patch, the driver does the single-connection assignment in a
loop until all possbile single-connections are checked.  This will
improve the DAC assignment, e.g. for ASUS G72.

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

---
 sound/pci/hda/patch_realtek.c |  124 ++++++++++++++++++++++++++++++------------
 1 file changed, 91 insertions(+), 33 deletions(-)

--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -2929,10 +2929,22 @@ static int alc_auto_select_dac(struct hd
 	return 0;
 }
 
+static bool alc_is_dac_already_used(struct hda_codec *codec, hda_nid_t nid)
+{
+	struct alc_spec *spec = codec->spec;
+	if (found_in_nid_list(nid, spec->multiout.dac_nids,
+			      ARRAY_SIZE(spec->private_dac_nids)) ||
+	    found_in_nid_list(nid, spec->multiout.hp_out_nid,
+			      ARRAY_SIZE(spec->multiout.hp_out_nid)) ||
+	    found_in_nid_list(nid, spec->multiout.extra_out_nid,
+			      ARRAY_SIZE(spec->multiout.extra_out_nid)))
+		return true;
+	return false;
+}
+
 /* look for an empty DAC slot */
 static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin)
 {
-	struct alc_spec *spec = codec->spec;
 	hda_nid_t srcs[5];
 	int i, num;
 
@@ -2942,16 +2954,8 @@ static hda_nid_t alc_auto_look_for_dac(s
 		hda_nid_t nid = alc_auto_mix_to_dac(codec, srcs[i]);
 		if (!nid)
 			continue;
-		if (found_in_nid_list(nid, spec->multiout.dac_nids,
-				      ARRAY_SIZE(spec->private_dac_nids)))
-			continue;
-		if (found_in_nid_list(nid, spec->multiout.hp_out_nid,
-				      ARRAY_SIZE(spec->multiout.hp_out_nid)))
-		    continue;
-		if (found_in_nid_list(nid, spec->multiout.extra_out_nid,
-				      ARRAY_SIZE(spec->multiout.extra_out_nid)))
-		    continue;
-		return nid;
+		if (!alc_is_dac_already_used(codec, nid))
+			return nid;
 	}
 	return 0;
 }
@@ -2977,12 +2981,23 @@ static hda_nid_t get_dac_if_single(struc
 {
 	struct alc_spec *spec = codec->spec;
 	hda_nid_t sel = alc_go_down_to_selector(codec, pin);
-	hda_nid_t srcs[5];
-	int num = snd_hda_get_connections(codec, sel, srcs,
+	hda_nid_t nid, nid_found, srcs[5];
+	int i, num = snd_hda_get_connections(codec, sel, srcs,
 					  ARRAY_SIZE(srcs));
-	if (num == 1 || (num == 2 && srcs[1] == spec->mixer_nid))
+	if (num == 1)
 		return alc_auto_look_for_dac(codec, pin);
-	return 0;
+	nid_found = 0;
+	for (i = 0; i < num; i++) {
+		if (srcs[i] == spec->mixer_nid)
+			continue;
+		nid = alc_auto_mix_to_dac(codec, srcs[i]);
+		if (nid && !alc_is_dac_already_used(codec, nid)) {
+			if (nid_found)
+				return 0;
+			nid_found = nid;
+		}
+	}
+	return nid_found;
 }
 
 /* mark up volume and mute control NIDs: used during badness parsing and
@@ -3079,16 +3094,30 @@ static int alc_auto_fill_extra_dacs(stru
 	int badness = 0;
 	hda_nid_t dac;
 
-	if (num_outs && !dacs[0]) {
-		dac = dacs[0] = alc_auto_look_for_dac(codec, pins[0]);
-		if (!dacs[0]) {
-			dac = spec->private_dac_nids[0];
-			if (!alc_auto_is_dac_reachable(codec, pins[0], dac))
-				return BAD_NO_DAC;
-			badness += BAD_NO_EXTRA_DAC;
+	if (!num_outs)
+		return 0;
+
+	if (!dacs[0])
+		dacs[0] = alc_auto_look_for_dac(codec, pins[0]);
+	if (!dacs[0]) {
+		for (i = 1; i < num_outs; i++) {
+			dac = dacs[i];
+			if (dac && alc_auto_is_dac_reachable(codec, pins[0], dac)) {
+				dacs[0] = dac;
+				dacs[i] = 0;
+				break;
+			}
 		}
-		badness += eval_shared_vol_badness(codec, pins[0], dac);
 	}
+	dac = dacs[0];
+	if (!dac) {
+		dac = spec->private_dac_nids[0];
+		if (!alc_auto_is_dac_reachable(codec, pins[0], dac))
+			return BAD_NO_DAC;
+		badness += BAD_NO_EXTRA_DAC;
+	}
+	if (dac)
+		badness += eval_shared_vol_badness(codec, pins[0], dac);
 
 	for (i = 1; i < num_outs; i++)
 		dacs[i] = get_dac_if_single(codec, pins[i]);
@@ -3116,6 +3145,21 @@ static int alc_auto_fill_extra_dacs(stru
 static int alc_auto_fill_multi_ios(struct hda_codec *codec,
 				   unsigned int location, int offset);
 
+static bool alc_map_singles(struct hda_codec *codec, int outs,
+			    const hda_nid_t *pins, hda_nid_t *dacs)
+{
+	int i;
+	bool found = false;
+	for (i = 0; i < outs; i++) {
+		if (dacs[i])
+			continue;
+		dacs[i] = get_dac_if_single(codec, pins[i]);
+		if (dacs[i])
+			found = true;
+	}
+	return found;
+}
+
 /* fill in the dac_nids table from the parsed pin configuration */
 static int fill_and_eval_dacs(struct hda_codec *codec,
 			      bool fill_hardwired)
@@ -3123,7 +3167,7 @@ static int fill_and_eval_dacs(struct hda
 	struct alc_spec *spec = codec->spec;
 	const struct auto_pin_cfg *cfg = &spec->autocfg;
 	unsigned int location, defcfg;
-	int i, err, badness;
+	int i, j, err, badness;
 
 	/* set num_dacs once to full for alc_auto_look_for_dac() */
 	spec->multiout.num_dacs = cfg->line_outs;
@@ -3137,15 +3181,18 @@ static int fill_and_eval_dacs(struct hda
 
 	/* fill hard-wired DACs first */
 	if (fill_hardwired) {
-		for (i = 0; i < cfg->line_outs; i++)
-			spec->private_dac_nids[i] =
-				get_dac_if_single(codec, cfg->line_out_pins[i]);
-		for (i = 0; i < cfg->hp_outs; i++)
-			spec->multiout.hp_out_nid[i] =
-				get_dac_if_single(codec, cfg->hp_pins[i]);
-		for (i = 0; i < cfg->speaker_outs; i++)
-			spec->multiout.extra_out_nid[i] =
-				get_dac_if_single(codec, cfg->speaker_pins[i]);
+		bool mapped;
+		do {
+			mapped = alc_map_singles(codec, cfg->line_outs,
+						     cfg->line_out_pins,
+						     spec->private_dac_nids);
+			mapped |= alc_map_singles(codec, cfg->hp_outs,
+						  cfg->hp_pins,
+						  spec->multiout.hp_out_nid);
+			mapped |= alc_map_singles(codec, cfg->speaker_outs,
+						  cfg->speaker_pins,
+						  spec->multiout.extra_out_nid);
+		} while (mapped);
 	}
 
 	for (i = 0; i < cfg->line_outs; i++) {
@@ -3155,6 +3202,17 @@ static int fill_and_eval_dacs(struct hda
 			spec->private_dac_nids[i] =
 				alc_auto_look_for_dac(codec, pin);
 		dac = spec->private_dac_nids[i];
+		if (!dac && !i) {
+			for (j = 1; j < cfg->line_outs; j++) {
+				hda_nid_t dac2 = spec->private_dac_nids[j];
+				if (dac2 &&
+				    alc_auto_is_dac_reachable(codec, pin, dac2)) {
+					dac = spec->private_dac_nids[0] = dac2;
+					spec->private_dac_nids[j] = 0;
+					break;
+				}
+			}
+		}
 		if (!dac) {
 			if (!i)
 				badness += BAD_NO_PRIMARY_DAC;
