From c2a6b21954471f74c299711204d12be3f5ec73e4 Mon Sep 17 00:00:00 2001
From: Daniel Chen <crimsun@Box.ncat.edu>
Date: Thu, 31 May 2007 22:19:59 -0400
Subject: [PATCH] [PATCH] sound/pci/hda/: (hda_*)
Bug: 83015
Bug: 88570
Bug: 89889
Bug: 118169

Status: Merged in alsa-kernel hg

This changeset is the last of four necessary portions to fix the
above bugs.  It fixes line-out (speaker vs. headphones) parsing and
surround channels enumeration.

Signed-off-by: Daniel T Chen <crimsun@ubuntu.com>
---
 sound/pci/hda/hda_codec.c   |  115 +++++++++++++++++++++++++++----------------
 sound/pci/hda/hda_codec.h   |    2 +-
 sound/pci/hda/hda_generic.c |    2 +-
 sound/pci/hda/hda_local.h   |    7 +++
 4 files changed, 81 insertions(+), 45 deletions(-)

diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index a3c4259..8d744b2 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -582,7 +582,7 @@ int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
 	}
 
 	codec->preset = find_codec_preset(codec);
-	if (! *bus->card->mixername)
+	if (codec->afg || !*bus->card->mixername)
 		snd_hda_get_codec_name(codec, bus->card->mixername,
 				       sizeof(bus->card->mixername));
 
@@ -2007,6 +2007,32 @@ static int is_in_nid_list(hda_nid_t nid, hda_nid_t *list)
 	return 0;
 }
 
+
+/*
+ * Sort an associated group of pins according to their sequence numbers.
+ */
+static void sort_pins_by_sequence(hda_nid_t * pins, short * sequences,
+				  int num_pins)
+{
+	int i, j;
+	short seq;
+	hda_nid_t nid;
+	
+	for (i = 0; i < num_pins; i++) {
+		for (j = i + 1; j < num_pins; j++) {
+			if (sequences[i] > sequences[j]) {
+				seq = sequences[i];
+				sequences[i] = sequences[j];
+				sequences[j] = seq;
+				nid = pins[i];
+				pins[i] = pins[j];
+				pins[j] = nid;
+			}
+		}
+	}
+}
+
+
 /*
  * Parse all pin widgets and store the useful pin nids to cfg
  *
@@ -2028,13 +2054,16 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *c
 				 hda_nid_t *ignore_nids)
 {
 	hda_nid_t nid, nid_start;
-	int i, j, nodes;
-	short seq, assoc_line_out, sequences[ARRAY_SIZE(cfg->line_out_pins)];
+	int nodes;
+	short seq, assoc_line_out, assoc_speaker;
+	short sequences_line_out[ARRAY_SIZE(cfg->line_out_pins)];
+	short sequences_speaker[ARRAY_SIZE(cfg->speaker_pins)];
 
 	memset(cfg, 0, sizeof(*cfg));
 
-	memset(sequences, 0, sizeof(sequences));
-	assoc_line_out = 0;
+	memset(sequences_line_out, 0, sizeof(sequences_line_out));
+	memset(sequences_speaker, 0, sizeof(sequences_speaker));
+	assoc_line_out = assoc_speaker = 0;
 
 	nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid_start);
 	for (nid = nid_start; nid < nodes + nid_start; nid++) {
@@ -2067,13 +2096,22 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *c
 			if (cfg->line_outs >= ARRAY_SIZE(cfg->line_out_pins))
 				continue;
 			cfg->line_out_pins[cfg->line_outs] = nid;
-			sequences[cfg->line_outs] = seq;
+			sequences_line_out[cfg->line_outs] = seq;
 			cfg->line_outs++;
 			break;
 		case AC_JACK_SPEAKER:
+			seq = get_defcfg_sequence(def_conf);
+			assoc = get_defcfg_association(def_conf);
+			if (! assoc)
+				continue;
+			if (! assoc_speaker)
+				assoc_speaker = assoc;
+			else if (assoc_speaker != assoc)
+				continue;
 			if (cfg->speaker_outs >= ARRAY_SIZE(cfg->speaker_pins))
 				continue;
 			cfg->speaker_pins[cfg->speaker_outs] = nid;
+			sequences_speaker[cfg->speaker_outs] = seq;
 			cfg->speaker_outs++;
 			break;
 		case AC_JACK_HP_OUT:
@@ -2119,34 +2157,45 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *c
 	}
 
 	/* sort by sequence */
-	for (i = 0; i < cfg->line_outs; i++)
-		for (j = i + 1; j < cfg->line_outs; j++)
-			if (sequences[i] > sequences[j]) {
-				seq = sequences[i];
-				sequences[i] = sequences[j];
-				sequences[j] = seq;
-				nid = cfg->line_out_pins[i];
-				cfg->line_out_pins[i] = cfg->line_out_pins[j];
-				cfg->line_out_pins[j] = nid;
-			}
+	sort_pins_by_sequence(cfg->line_out_pins, sequences_line_out,
+			      cfg->line_outs);
+	sort_pins_by_sequence(cfg->speaker_pins, sequences_speaker,
+			      cfg->speaker_outs);
+	
+	/*
+	 * FIX-UP: if no line-outs are detected, try to use speaker or HP pin
+	 * as a primary output
+	 */
+	if (!cfg->line_outs) {
+		if (cfg->speaker_outs) {
+			cfg->line_outs = cfg->speaker_outs;
+			memcpy(cfg->line_out_pins, cfg->speaker_pins,
+			       sizeof(cfg->speaker_pins));
+			cfg->speaker_outs = 0;
+			memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins));
+			cfg->line_out_type = AUTO_PIN_SPEAKER_OUT;
+		} else if (cfg->hp_outs) {
+			cfg->line_outs = cfg->hp_outs;
+			memcpy(cfg->line_out_pins, cfg->hp_pins,
+			       sizeof(cfg->hp_pins));
+			cfg->hp_outs = 0;
+			memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
+			cfg->line_out_type = AUTO_PIN_HP_OUT;
+		}
+	}
 
 	/* Reorder the surround channels
 	 * ALSA sequence is front/surr/clfe/side
 	 * HDA sequence is:
 	 *    4-ch: front/surr  =>  OK as it is
 	 *    6-ch: front/clfe/surr
-	 *    8-ch: front/clfe/side/surr
+	 *    8-ch: front/clfe/rear/side|fc
 	 */
 	switch (cfg->line_outs) {
 	case 3:
-		nid = cfg->line_out_pins[1];
-		cfg->line_out_pins[1] = cfg->line_out_pins[2];
-		cfg->line_out_pins[2] = nid;
-		break;
 	case 4:
 		nid = cfg->line_out_pins[1];
-		cfg->line_out_pins[1] = cfg->line_out_pins[3];
-		cfg->line_out_pins[3] = cfg->line_out_pins[2];
+		cfg->line_out_pins[1] = cfg->line_out_pins[2];
 		cfg->line_out_pins[2] = nid;
 		break;
 	}
@@ -2175,26 +2224,6 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *c
 		   cfg->input_pins[AUTO_PIN_CD],
 		   cfg->input_pins[AUTO_PIN_AUX]);
 
-	/*
-	 * FIX-UP: if no line-outs are detected, try to use speaker or HP pin
-	 * as a primary output
-	 */
-	if (! cfg->line_outs) {
-		if (cfg->speaker_outs) {
-			cfg->line_outs = cfg->speaker_outs;
-			memcpy(cfg->line_out_pins, cfg->speaker_pins,
-			       sizeof(cfg->speaker_pins));
-			cfg->speaker_outs = 0;
-			memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins));
-		} else if (cfg->hp_outs) {
-			cfg->line_outs = cfg->hp_outs;
-			memcpy(cfg->line_out_pins, cfg->hp_pins,
-			       sizeof(cfg->hp_pins));
-			cfg->hp_outs = 0;
-			memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
-		}
-	}
-
 	return 0;
 }
 
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index c12bc4e..56c26e7 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -233,7 +233,7 @@ enum {
  */
 
 /* Amp gain/mute */
-#define AC_AMP_MUTE			(1<<8)
+#define AC_AMP_MUTE			(1<<7)
 #define AC_AMP_GAIN			(0x7f)
 #define AC_AMP_GET_INDEX		(0xf<<0)
 
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 1589d2f..b7e7d1d 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -134,7 +134,7 @@ static int add_new_node(struct hda_codec *codec, struct hda_gspec *spec, hda_nid
 			return -ENOMEM;
 		}
 	}
-	memcpy(node->conn_list, conn_list, nconns);
+	memcpy(node->conn_list, conn_list, nconns * sizeof(hda_nid_t));
 	node->nconns = nconns;
 	node->wid_caps = get_wcaps(codec, nid);
 	node->type = (node->wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 9ca1baf..eb7595c 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -223,6 +223,12 @@ enum {
 	AUTO_PIN_LAST
 };
 
+enum {
+	AUTO_PIN_LINE_OUT,
+	AUTO_PIN_SPEAKER_OUT,
+	AUTO_PIN_HP_OUT
+};
+
 extern const char *auto_pin_cfg_labels[AUTO_PIN_LAST];
 
 struct auto_pin_cfg {
@@ -231,6 +237,7 @@ struct auto_pin_cfg {
 	int speaker_outs;
 	hda_nid_t speaker_pins[5];
 	int hp_outs;
+	int line_out_type;	/* AUTO_PIN_XXX_OUT */
 	hda_nid_t hp_pins[5];
 	hda_nid_t input_pins[AUTO_PIN_LAST];
 	hda_nid_t dig_out_pin;
-- 
1.5.2

