Browse Source

Merge branch 'topic/hda' into for-linus

Takashi Iwai 14 năm trước cách đây
mục cha
commit
506ecbca71

+ 5 - 3
Documentation/sound/alsa/HD-Audio.txt

@@ -57,9 +57,11 @@ dead.  However, this detection isn't perfect on some devices.  In such
 a case, you can change the default method via `position_fix` option.
 
 `position_fix=1` means to use LPIB method explicitly.
-`position_fix=2` means to use the position-buffer.  0 is the default
-value, the automatic check and fallback to LPIB as described in the
-above.  If you get a problem of repeated sounds, this option might
+`position_fix=2` means to use the position-buffer.
+`position_fix=3` means to use a combination of both methods, needed
+for some VIA and ATI controllers.  0 is the default value for all other
+controllers, the automatic check and fallback to LPIB as described in
+the above.  If you get a problem of repeated sounds, this option might
 help.
 
 In addition to that, every controller is known to be broken regarding

+ 3 - 1
include/sound/tlv.h

@@ -38,9 +38,11 @@
 #define SNDRV_CTL_TLVT_DB_MINMAX 4	/* dB scale with min/max */
 #define SNDRV_CTL_TLVT_DB_MINMAX_MUTE 5	/* dB scale with min/max with mute */
 
+#define TLV_DB_SCALE_MASK	0xffff
+#define TLV_DB_SCALE_MUTE	0x10000
 #define TLV_DB_SCALE_ITEM(min, step, mute)			\
 	SNDRV_CTL_TLVT_DB_SCALE, 2 * sizeof(unsigned int),	\
-	(min), ((step) & 0xffff) | ((mute) ? 0x10000 : 0)
+	(min), ((step) & TLV_DB_SCALE_MASK) | ((mute) ? TLV_DB_SCALE_MUTE : 0)
 #define DECLARE_TLV_DB_SCALE(name, min, step, mute) \
 	unsigned int name[] = { TLV_DB_SCALE_ITEM(min, step, mute) }
 

+ 6 - 33
sound/pci/hda/Kconfig

@@ -119,47 +119,20 @@ config SND_HDA_CODEC_VIA
 	  snd-hda-codec-via.
 	  This module is automatically loaded at probing.
 
-config SND_HDA_CODEC_ATIHDMI
-	bool "Build ATI HDMI HD-audio codec support"
-	default y
-	help
-	  Say Y here to include ATI HDMI HD-audio codec support in
-	  snd-hda-intel driver, such as ATI RS600 HDMI.
-
-	  When the HD-audio driver is built as a module, the codec
-	  support code is also built as another module,
-	  snd-hda-codec-atihdmi.
-	  This module is automatically loaded at probing.
-
-config SND_HDA_CODEC_NVHDMI
-	bool "Build NVIDIA HDMI HD-audio codec support"
-	default y
-	help
-	  Say Y here to include NVIDIA HDMI HD-audio codec support in
-	  snd-hda-intel driver, such as NVIDIA MCP78 HDMI.
-
-	  When the HD-audio driver is built as a module, the codec
-	  support code is also built as another module,
-	  snd-hda-codec-nvhdmi.
-	  This module is automatically loaded at probing.
-
-config SND_HDA_CODEC_INTELHDMI
-	bool "Build INTEL HDMI HD-audio codec support"
+config SND_HDA_CODEC_HDMI
+	bool "Build HDMI/DisplayPort HD-audio codec support"
 	select SND_DYNAMIC_MINORS
 	default y
 	help
-	  Say Y here to include INTEL HDMI HD-audio codec support in
-	  snd-hda-intel driver, such as Eaglelake integrated HDMI.
+	  Say Y here to include HDMI and DisplayPort HD-audio codec
+	  support in snd-hda-intel driver.  This includes all AMD/ATI,
+	  Intel and Nvidia HDMI/DisplayPort codecs.
 
 	  When the HD-audio driver is built as a module, the codec
 	  support code is also built as another module,
-	  snd-hda-codec-intelhdmi.
+	  snd-hda-codec-hdmi.
 	  This module is automatically loaded at probing.
 
-config SND_HDA_ELD
-	def_bool y
-	depends on SND_HDA_CODEC_INTELHDMI || SND_HDA_CODEC_NVHDMI
-
 config SND_HDA_CODEC_CIRRUS
 	bool "Build Cirrus Logic codec support"
 	depends on SND_HDA_INTEL

+ 3 - 12
sound/pci/hda/Makefile

@@ -3,7 +3,6 @@ snd-hda-intel-objs := hda_intel.o
 snd-hda-codec-y := hda_codec.o
 snd-hda-codec-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o
 snd-hda-codec-$(CONFIG_PROC_FS) += hda_proc.o
-snd-hda-codec-$(CONFIG_SND_HDA_ELD) += hda_eld.o
 snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o
 snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o
 
@@ -12,13 +11,11 @@ snd-hda-codec-cmedia-objs :=	patch_cmedia.o
 snd-hda-codec-analog-objs :=	patch_analog.o
 snd-hda-codec-idt-objs :=	patch_sigmatel.o
 snd-hda-codec-si3054-objs :=	patch_si3054.o
-snd-hda-codec-atihdmi-objs :=	patch_atihdmi.o
 snd-hda-codec-cirrus-objs :=	patch_cirrus.o
 snd-hda-codec-ca0110-objs :=	patch_ca0110.o
 snd-hda-codec-conexant-objs :=	patch_conexant.o
 snd-hda-codec-via-objs :=	patch_via.o
-snd-hda-codec-nvhdmi-objs :=	patch_nvhdmi.o
-snd-hda-codec-intelhdmi-objs :=	patch_intelhdmi.o
+snd-hda-codec-hdmi-objs :=	patch_hdmi.o hda_eld.o
 
 # common driver
 obj-$(CONFIG_SND_HDA_INTEL) := snd-hda-codec.o
@@ -39,9 +36,6 @@ endif
 ifdef CONFIG_SND_HDA_CODEC_SI3054
 obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-si3054.o
 endif
-ifdef CONFIG_SND_HDA_CODEC_ATIHDMI
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-atihdmi.o
-endif
 ifdef CONFIG_SND_HDA_CODEC_CIRRUS
 obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-cirrus.o
 endif
@@ -54,11 +48,8 @@ endif
 ifdef CONFIG_SND_HDA_CODEC_VIA
 obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-via.o
 endif
-ifdef CONFIG_SND_HDA_CODEC_NVHDMI
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-nvhdmi.o
-endif
-ifdef CONFIG_SND_HDA_CODEC_INTELHDMI
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-intelhdmi.o
+ifdef CONFIG_SND_HDA_CODEC_HDMI
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-hdmi.o
 endif
 
 # this must be the last entry after codec drivers;

+ 216 - 55
sound/pci/hda/hda_codec.c

@@ -1216,6 +1216,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
 	struct hda_codec *c;
 	struct hda_cvt_setup *p;
 	unsigned int oldval, newval;
+	int type;
 	int i;
 
 	if (!nid)
@@ -1254,10 +1255,12 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
 	p->dirty = 0;
 
 	/* make other inactive cvts with the same stream-tag dirty */
+	type = get_wcaps_type(get_wcaps(codec, nid));
 	list_for_each_entry(c, &codec->bus->codec_list, list) {
 		for (i = 0; i < c->cvt_setups.used; i++) {
 			p = snd_array_elem(&c->cvt_setups, i);
-			if (!p->active && p->stream_tag == stream_tag)
+			if (!p->active && p->stream_tag == stream_tag &&
+			    get_wcaps_type(get_wcaps(codec, p->nid)) == type)
 				p->dirty = 1;
 		}
 	}
@@ -1281,6 +1284,9 @@ void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid,
 	if (!nid)
 		return;
 
+	if (codec->no_sticky_stream)
+		do_now = 1;
+
 	snd_printdd("hda_codec_cleanup_stream: NID=0x%x\n", nid);
 	p = get_hda_cvt_setup(codec, nid);
 	if (p) {
@@ -1831,6 +1837,7 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
 	hda_nid_t nid = get_amp_nid(kcontrol);
 	int dir = get_amp_direction(kcontrol);
 	unsigned int ofs = get_amp_offset(kcontrol);
+	bool min_mute = get_amp_min_mute(kcontrol);
 	u32 caps, val1, val2;
 
 	if (size < 4 * sizeof(unsigned int))
@@ -1841,6 +1848,8 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
 	val1 = -((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT);
 	val1 += ofs;
 	val1 = ((int)val1) * ((int)val2);
+	if (min_mute)
+		val2 |= TLV_DB_SCALE_MUTE;
 	if (put_user(SNDRV_CTL_TLVT_DB_SCALE, _tlv))
 		return -EFAULT;
 	if (put_user(2 * sizeof(unsigned int), _tlv + 1))
@@ -2228,10 +2237,7 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
 		change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx,
 						   HDA_AMP_MUTE,
 						   *valp ? 0 : HDA_AMP_MUTE);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-	if (codec->patch_ops.check_power_status)
-		codec->patch_ops.check_power_status(codec, nid);
-#endif
+	hda_call_check_power_status(codec, nid);
 	snd_hda_power_down(codec);
 	return change;
 }
@@ -4372,6 +4378,34 @@ static void sort_pins_by_sequence(hda_nid_t *pins, short *sequences,
 }
 
 
+/* add the found input-pin to the cfg->inputs[] table */
+static void add_auto_cfg_input_pin(struct auto_pin_cfg *cfg, hda_nid_t nid,
+				   int type)
+{
+	if (cfg->num_inputs < AUTO_CFG_MAX_INS) {
+		cfg->inputs[cfg->num_inputs].pin = nid;
+		cfg->inputs[cfg->num_inputs].type = type;
+		cfg->num_inputs++;
+	}
+}
+
+/* sort inputs in the order of AUTO_PIN_* type */
+static void sort_autocfg_input_pins(struct auto_pin_cfg *cfg)
+{
+	int i, j;
+
+	for (i = 0; i < cfg->num_inputs; i++) {
+		for (j = i + 1; j < cfg->num_inputs; j++) {
+			if (cfg->inputs[i].type > cfg->inputs[j].type) {
+				struct auto_pin_cfg_item tmp;
+				tmp = cfg->inputs[i];
+				cfg->inputs[i] = cfg->inputs[j];
+				cfg->inputs[j] = tmp;
+			}
+		}
+	}
+}
+
 /*
  * Parse all pin widgets and store the useful pin nids to cfg
  *
@@ -4385,7 +4419,7 @@ static void sort_pins_by_sequence(hda_nid_t *pins, short *sequences,
  * output, i.e. to line_out_pins[0].  So, line_outs is always positive
  * if any analog output exists.
  *
- * The analog input pins are assigned to input_pins array.
+ * The analog input pins are assigned to inputs array.
  * The digital input/output pins are assigned to dig_in_pin and dig_out_pin,
  * respectively.
  */
@@ -4398,6 +4432,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
 	short sequences_line_out[ARRAY_SIZE(cfg->line_out_pins)];
 	short sequences_speaker[ARRAY_SIZE(cfg->speaker_pins)];
 	short sequences_hp[ARRAY_SIZE(cfg->hp_pins)];
+	int i;
 
 	memset(cfg, 0, sizeof(*cfg));
 
@@ -4468,33 +4503,17 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
 			sequences_hp[cfg->hp_outs] = (assoc << 4) | seq;
 			cfg->hp_outs++;
 			break;
-		case AC_JACK_MIC_IN: {
-			int preferred, alt;
-			if (loc == AC_JACK_LOC_FRONT ||
-			    (loc & 0x30) == AC_JACK_LOC_INTERNAL) {
-				preferred = AUTO_PIN_FRONT_MIC;
-				alt = AUTO_PIN_MIC;
-			} else {
-				preferred = AUTO_PIN_MIC;
-				alt = AUTO_PIN_FRONT_MIC;
-			}
-			if (!cfg->input_pins[preferred])
-				cfg->input_pins[preferred] = nid;
-			else if (!cfg->input_pins[alt])
-				cfg->input_pins[alt] = nid;
+		case AC_JACK_MIC_IN:
+			add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_MIC);
 			break;
-		}
 		case AC_JACK_LINE_IN:
-			if (loc == AC_JACK_LOC_FRONT)
-				cfg->input_pins[AUTO_PIN_FRONT_LINE] = nid;
-			else
-				cfg->input_pins[AUTO_PIN_LINE] = nid;
+			add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_LINE_IN);
 			break;
 		case AC_JACK_CD:
-			cfg->input_pins[AUTO_PIN_CD] = nid;
+			add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_CD);
 			break;
 		case AC_JACK_AUX:
-			cfg->input_pins[AUTO_PIN_AUX] = nid;
+			add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_AUX);
 			break;
 		case AC_JACK_SPDIF_OUT:
 		case AC_JACK_DIG_OTHER_OUT:
@@ -4539,6 +4558,8 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
 			memmove(sequences_hp + i, sequences_hp + i + 1,
 				sizeof(sequences_hp[0]) * (cfg->hp_outs - i));
 		}
+		memset(cfg->hp_pins + cfg->hp_outs, 0,
+		       sizeof(hda_nid_t) * (AUTO_CFG_MAX_OUTS - cfg->hp_outs));
 	}
 
 	/* sort by sequence */
@@ -4549,21 +4570,6 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
 	sort_pins_by_sequence(cfg->hp_pins, sequences_hp,
 			      cfg->hp_outs);
 
-	/* if we have only one mic, make it AUTO_PIN_MIC */
-	if (!cfg->input_pins[AUTO_PIN_MIC] &&
-	    cfg->input_pins[AUTO_PIN_FRONT_MIC]) {
-		cfg->input_pins[AUTO_PIN_MIC] =
-			cfg->input_pins[AUTO_PIN_FRONT_MIC];
-		cfg->input_pins[AUTO_PIN_FRONT_MIC] = 0;
-	}
-	/* ditto for line-in */
-	if (!cfg->input_pins[AUTO_PIN_LINE] &&
-	    cfg->input_pins[AUTO_PIN_FRONT_LINE]) {
-		cfg->input_pins[AUTO_PIN_LINE] =
-			cfg->input_pins[AUTO_PIN_FRONT_LINE];
-		cfg->input_pins[AUTO_PIN_FRONT_LINE] = 0;
-	}
-
 	/*
 	 * FIX-UP: if no line-outs are detected, try to use speaker or HP pin
 	 * as a primary output
@@ -4602,6 +4608,8 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
 		break;
 	}
 
+	sort_autocfg_input_pins(cfg);
+
 	/*
 	 * debug prints of the parsed results
 	 */
@@ -4621,14 +4629,13 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
 	if (cfg->dig_outs)
 		snd_printd("   dig-out=0x%x/0x%x\n",
 			   cfg->dig_out_pins[0], cfg->dig_out_pins[1]);
-	snd_printd("   inputs: mic=0x%x, fmic=0x%x, line=0x%x, fline=0x%x,"
-		   " cd=0x%x, aux=0x%x\n",
-		   cfg->input_pins[AUTO_PIN_MIC],
-		   cfg->input_pins[AUTO_PIN_FRONT_MIC],
-		   cfg->input_pins[AUTO_PIN_LINE],
-		   cfg->input_pins[AUTO_PIN_FRONT_LINE],
-		   cfg->input_pins[AUTO_PIN_CD],
-		   cfg->input_pins[AUTO_PIN_AUX]);
+	snd_printd("   inputs:");
+	for (i = 0; i < cfg->num_inputs; i++) {
+		snd_printdd(" %s=0x%x",
+			    hda_get_autocfg_input_label(codec, cfg, i),
+			    cfg->inputs[i].pin);
+	}
+	snd_printd("\n");
 	if (cfg->dig_in_pin)
 		snd_printd("   dig-in=0x%x\n", cfg->dig_in_pin);
 
@@ -4636,11 +4643,165 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
 }
 EXPORT_SYMBOL_HDA(snd_hda_parse_pin_def_config);
 
-/* labels for input pins */
-const char *auto_pin_cfg_labels[AUTO_PIN_LAST] = {
-	"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux"
-};
-EXPORT_SYMBOL_HDA(auto_pin_cfg_labels);
+int snd_hda_get_input_pin_attr(unsigned int def_conf)
+{
+	unsigned int loc = get_defcfg_location(def_conf);
+	unsigned int conn = get_defcfg_connect(def_conf);
+	if (conn == AC_JACK_PORT_NONE)
+		return INPUT_PIN_ATTR_UNUSED;
+	/* Windows may claim the internal mic to be BOTH, too */
+	if (conn == AC_JACK_PORT_FIXED || conn == AC_JACK_PORT_BOTH)
+		return INPUT_PIN_ATTR_INT;
+	if ((loc & 0x30) == AC_JACK_LOC_INTERNAL)
+		return INPUT_PIN_ATTR_INT;
+	if ((loc & 0x30) == AC_JACK_LOC_SEPARATE)
+		return INPUT_PIN_ATTR_DOCK;
+	if (loc == AC_JACK_LOC_REAR)
+		return INPUT_PIN_ATTR_REAR;
+	if (loc == AC_JACK_LOC_FRONT)
+		return INPUT_PIN_ATTR_FRONT;
+	return INPUT_PIN_ATTR_NORMAL;
+}
+EXPORT_SYMBOL_HDA(snd_hda_get_input_pin_attr);
+
+/**
+ * hda_get_input_pin_label - Give a label for the given input pin
+ *
+ * When check_location is true, the function checks the pin location
+ * for mic and line-in pins, and set an appropriate prefix like "Front",
+ * "Rear", "Internal".
+ */
+
+const char *hda_get_input_pin_label(struct hda_codec *codec, hda_nid_t pin,
+					int check_location)
+{
+	unsigned int def_conf;
+	static const char *mic_names[] = {
+		"Internal Mic", "Dock Mic", "Mic", "Front Mic", "Rear Mic",
+	};
+	int attr;
+
+	def_conf = snd_hda_codec_get_pincfg(codec, pin);
+
+	switch (get_defcfg_device(def_conf)) {
+	case AC_JACK_MIC_IN:
+		if (!check_location)
+			return "Mic";
+		attr = snd_hda_get_input_pin_attr(def_conf);
+		if (!attr)
+			return "None";
+		return mic_names[attr - 1];
+	case AC_JACK_LINE_IN:
+		if (!check_location)
+			return "Line";
+		attr = snd_hda_get_input_pin_attr(def_conf);
+		if (!attr)
+			return "None";
+		if (attr == INPUT_PIN_ATTR_DOCK)
+			return "Dock Line";
+		return "Line";
+	case AC_JACK_AUX:
+		return "Aux";
+	case AC_JACK_CD:
+		return "CD";
+	case AC_JACK_SPDIF_IN:
+		return "SPDIF In";
+	case AC_JACK_DIG_OTHER_IN:
+		return "Digital In";
+	default:
+		return "Misc";
+	}
+}
+EXPORT_SYMBOL_HDA(hda_get_input_pin_label);
+
+/* Check whether the location prefix needs to be added to the label.
+ * If all mic-jacks are in the same location (e.g. rear panel), we don't
+ * have to put "Front" prefix to each label.  In such a case, returns false.
+ */
+static int check_mic_location_need(struct hda_codec *codec,
+				   const struct auto_pin_cfg *cfg,
+				   int input)
+{
+	unsigned int defc;
+	int i, attr, attr2;
+
+	defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[input].pin);
+	attr = snd_hda_get_input_pin_attr(defc);
+	/* for internal or docking mics, we need locations */
+	if (attr <= INPUT_PIN_ATTR_NORMAL)
+		return 1;
+
+	attr = 0;
+	for (i = 0; i < cfg->num_inputs; i++) {
+		defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[i].pin);
+		attr2 = snd_hda_get_input_pin_attr(defc);
+		if (attr2 >= INPUT_PIN_ATTR_NORMAL) {
+			if (attr && attr != attr2)
+				return 1; /* different locations found */
+			attr = attr2;
+		}
+	}
+	return 0;
+}
+
+/**
+ * hda_get_autocfg_input_label - Get a label for the given input
+ *
+ * Get a label for the given input pin defined by the autocfg item.
+ * Unlike hda_get_input_pin_label(), this function checks all inputs
+ * defined in autocfg and avoids the redundant mic/line prefix as much as
+ * possible.
+ */
+const char *hda_get_autocfg_input_label(struct hda_codec *codec,
+					const struct auto_pin_cfg *cfg,
+					int input)
+{
+	int type = cfg->inputs[input].type;
+	int has_multiple_pins = 0;
+
+	if ((input > 0 && cfg->inputs[input - 1].type == type) ||
+	    (input < cfg->num_inputs - 1 && cfg->inputs[input + 1].type == type))
+		has_multiple_pins = 1;
+	if (has_multiple_pins && type == AUTO_PIN_MIC)
+		has_multiple_pins &= check_mic_location_need(codec, cfg, input);
+	return hda_get_input_pin_label(codec, cfg->inputs[input].pin,
+				       has_multiple_pins);
+}
+EXPORT_SYMBOL_HDA(hda_get_autocfg_input_label);
+
+/**
+ * snd_hda_add_imux_item - Add an item to input_mux
+ *
+ * When the same label is used already in the existing items, the number
+ * suffix is appended to the label.  This label index number is stored
+ * to type_idx when non-NULL pointer is given.
+ */
+int snd_hda_add_imux_item(struct hda_input_mux *imux, const char *label,
+			  int index, int *type_idx)
+{
+	int i, label_idx = 0;
+	if (imux->num_items >= HDA_MAX_NUM_INPUTS) {
+		snd_printd(KERN_ERR "hda_codec: Too many imux items!\n");
+		return -EINVAL;
+	}
+	for (i = 0; i < imux->num_items; i++) {
+		if (!strncmp(label, imux->items[i].label, strlen(label)))
+			label_idx++;
+	}
+	if (type_idx)
+		*type_idx = label_idx;
+	if (label_idx > 0)
+		snprintf(imux->items[imux->num_items].label,
+			 sizeof(imux->items[imux->num_items].label),
+			 "%s %d", label, label_idx);
+	else
+		strlcpy(imux->items[imux->num_items].label, label,
+			sizeof(imux->items[imux->num_items].label));
+	imux->items[imux->num_items].index = index;
+	imux->num_items++;
+	return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_add_imux_item);
 
 
 #ifdef CONFIG_PM

+ 13 - 0
sound/pci/hda/hda_codec.h

@@ -850,6 +850,7 @@ struct hda_codec {
 	unsigned int pin_amp_workaround:1; /* pin out-amp takes index
 					    * (e.g. Conexant codecs)
 					    */
+	unsigned int no_sticky_stream:1; /* no sticky-PCM stream assignment */
 	unsigned int pins_shutup:1;	/* pins are shut up */
 	unsigned int no_trigger_sense:1; /* don't trigger at pin-sensing */
 #ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -989,6 +990,18 @@ int snd_hda_suspend(struct hda_bus *bus);
 int snd_hda_resume(struct hda_bus *bus);
 #endif
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static inline
+int hda_call_check_power_status(struct hda_codec *codec, hda_nid_t nid)
+{
+	if (codec->patch_ops.check_power_status)
+		return codec->patch_ops.check_power_status(codec, nid);
+	return 0;
+}
+#else	
+#define hda_call_check_power_status(codec, nid)		0
+#endif
+
 /*
  * get widget information
  */

+ 0 - 7
sound/pci/hda/hda_eld.c

@@ -332,7 +332,6 @@ int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid)
 	return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE,
 						 AC_DIPSIZE_ELD_BUF);
 }
-EXPORT_SYMBOL_HDA(snd_hdmi_get_eld_size);
 
 int snd_hdmi_get_eld(struct hdmi_eld *eld,
 		     struct hda_codec *codec, hda_nid_t nid)
@@ -368,7 +367,6 @@ int snd_hdmi_get_eld(struct hdmi_eld *eld,
 	kfree(buf);
 	return ret;
 }
-EXPORT_SYMBOL_HDA(snd_hdmi_get_eld);
 
 static void hdmi_show_short_audio_desc(struct cea_sad *a)
 {
@@ -407,7 +405,6 @@ void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen)
 	}
 	buf[j] = '\0';	/* necessary when j == 0 */
 }
-EXPORT_SYMBOL_HDA(snd_print_channel_allocation);
 
 void snd_hdmi_show_eld(struct hdmi_eld *e)
 {
@@ -426,7 +423,6 @@ void snd_hdmi_show_eld(struct hdmi_eld *e)
 	for (i = 0; i < e->sad_count; i++)
 		hdmi_show_short_audio_desc(e->sad + i);
 }
-EXPORT_SYMBOL_HDA(snd_hdmi_show_eld);
 
 #ifdef CONFIG_PROC_FS
 
@@ -585,7 +581,6 @@ int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld,
 
 	return 0;
 }
-EXPORT_SYMBOL_HDA(snd_hda_eld_proc_new);
 
 void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld)
 {
@@ -594,7 +589,6 @@ void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld)
 		eld->proc_entry = NULL;
 	}
 }
-EXPORT_SYMBOL_HDA(snd_hda_eld_proc_free);
 
 #endif /* CONFIG_PROC_FS */
 
@@ -645,4 +639,3 @@ void hdmi_eld_update_pcm_info(struct hdmi_eld *eld, struct hda_pcm_stream *pcm,
 	pcm->channels_max = min(pcm->channels_max, codec_pars->channels_max);
 	pcm->maxbps = min(pcm->maxbps, codec_pars->maxbps);
 }
-EXPORT_SYMBOL_HDA(hdmi_eld_update_pcm_info);

+ 5 - 36
sound/pci/hda/hda_generic.c

@@ -61,7 +61,6 @@ struct hda_gspec {
 	struct hda_gnode *cap_vol_node;	/* Node for capture volume */
 	unsigned int cur_cap_src;	/* current capture source */
 	struct hda_input_mux input_mux;
-	char cap_labels[HDA_MAX_NUM_INPUTS][16];
 
 	unsigned int def_amp_in_caps;
 	unsigned int def_amp_out_caps;
@@ -506,11 +505,10 @@ static const char *get_input_type(struct hda_gnode *node, unsigned int *pinctl)
  * returns 0 if not found, 1 if found, or a negative error code.
  */
 static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec,
-			       struct hda_gnode *node)
+			       struct hda_gnode *node, int idx)
 {
 	int i, err;
 	unsigned int pinctl;
-	char *label;
 	const char *type;
 
 	if (node->checked)
@@ -523,7 +521,7 @@ static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec,
 			child = hda_get_node(spec, node->conn_list[i]);
 			if (! child)
 				continue;
-			err = parse_adc_sub_nodes(codec, spec, child);
+			err = parse_adc_sub_nodes(codec, spec, child, idx);
 			if (err < 0)
 				return err;
 			if (err > 0) {
@@ -564,9 +562,7 @@ static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec,
 			return 0;
 		type = "Input";
 	}
-	label = spec->cap_labels[spec->input_mux.num_items];
-	strcpy(label, type);
-	spec->input_mux.items[spec->input_mux.num_items].label = label;
+	snd_hda_add_imux_item(&spec->input_mux, type, idx, NULL);
 
 	/* unmute the PIN external input */
 	unmute_input(codec, node, 0); /* index = 0? */
@@ -577,29 +573,6 @@ static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec,
 	return 1; /* found */
 }
 
-/* add a capture source element */
-static void add_cap_src(struct hda_gspec *spec, int idx)
-{
-	struct hda_input_mux_item *csrc;
-	char *buf;
-	int num, ocap;
-
-	num = spec->input_mux.num_items;
-	csrc = &spec->input_mux.items[num];
-	buf = spec->cap_labels[num];
-	for (ocap = 0; ocap < num; ocap++) {
-		if (! strcmp(buf, spec->cap_labels[ocap])) {
-			/* same label already exists,
-			 * put the index number to be unique
-			 */
-			sprintf(buf, "%s %d", spec->cap_labels[ocap], num);
-			break;
-		}
-	}
-	csrc->index = idx;
-	spec->input_mux.num_items++;
-}
-
 /*
  * parse input
  */
@@ -624,22 +597,18 @@ static int parse_input_path(struct hda_codec *codec, struct hda_gnode *adc_node)
 	for (i = 0; i < adc_node->nconns; i++) {
 		node = hda_get_node(spec, adc_node->conn_list[i]);
 		if (node && node->type == AC_WID_PIN) {
-			err = parse_adc_sub_nodes(codec, spec, node);
+			err = parse_adc_sub_nodes(codec, spec, node, i);
 			if (err < 0)
 				return err;
-			else if (err > 0)
-				add_cap_src(spec, i);
 		}
 	}
 	/* ... then check the rests, more complicated connections */
 	for (i = 0; i < adc_node->nconns; i++) {
 		node = hda_get_node(spec, adc_node->conn_list[i]);
 		if (node && node->type != AC_WID_PIN) {
-			err = parse_adc_sub_nodes(codec, spec, node);
+			err = parse_adc_sub_nodes(codec, spec, node, i);
 			if (err < 0)
 				return err;
-			else if (err > 0)
-				add_cap_src(spec, i);
 		}
 	}
 

+ 53 - 48
sound/pci/hda/hda_intel.c

@@ -78,8 +78,8 @@ MODULE_PARM_DESC(enable, "Enable Intel HD audio interface.");
 module_param_array(model, charp, NULL, 0444);
 MODULE_PARM_DESC(model, "Use the given board model.");
 module_param_array(position_fix, int, NULL, 0444);
-MODULE_PARM_DESC(position_fix, "Fix DMA pointer "
-		 "(0 = auto, 1 = none, 2 = POSBUF).");
+MODULE_PARM_DESC(position_fix, "DMA pointer read method."
+		 "(0 = auto, 1 = LPIB, 2 = POSBUF, 3 = VIACOMBO).");
 module_param_array(bdl_pos_adj, int, NULL, 0644);
 MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset.");
 module_param_array(probe_mask, int, NULL, 0444);
@@ -305,6 +305,7 @@ enum {
 	POS_FIX_AUTO,
 	POS_FIX_LPIB,
 	POS_FIX_POSBUF,
+	POS_FIX_VIACOMBO,
 };
 
 /* Defines for ATI HD Audio support in SB450 south bridge */
@@ -433,7 +434,6 @@ struct azx {
 	unsigned int polling_mode :1;
 	unsigned int msi :1;
 	unsigned int irq_pending_warned :1;
-	unsigned int via_dmapos_patch :1; /* enable DMA-position fix for VIA */
 	unsigned int probing :1; /* codec probing phase */
 
 	/* for debugging */
@@ -458,6 +458,7 @@ enum {
 	AZX_DRIVER_ULI,
 	AZX_DRIVER_NVIDIA,
 	AZX_DRIVER_TERA,
+	AZX_DRIVER_CTX,
 	AZX_DRIVER_GENERIC,
 	AZX_NUM_DRIVERS, /* keep this as last entry */
 };
@@ -473,6 +474,7 @@ static char *driver_short_names[] __devinitdata = {
 	[AZX_DRIVER_ULI] = "HDA ULI M5461",
 	[AZX_DRIVER_NVIDIA] = "HDA NVidia",
 	[AZX_DRIVER_TERA] = "HDA Teradici", 
+	[AZX_DRIVER_CTX] = "HDA Creative", 
 	[AZX_DRIVER_GENERIC] = "HD-Audio Generic",
 };
 
@@ -563,7 +565,10 @@ static void azx_init_cmd_io(struct azx *chip)
 	/* reset the rirb hw write pointer */
 	azx_writew(chip, RIRBWP, ICH6_RIRBWP_RST);
 	/* set N=1, get RIRB response interrupt for new entry */
-	azx_writew(chip, RINTCNT, 1);
+	if (chip->driver_type == AZX_DRIVER_CTX)
+		azx_writew(chip, RINTCNT, 0xc0);
+	else
+		azx_writew(chip, RINTCNT, 1);
 	/* enable rirb dma and response irq */
 	azx_writeb(chip, RIRBCTL, ICH6_RBCTL_DMA_EN | ICH6_RBCTL_IRQ_EN);
 	spin_unlock_irq(&chip->reg_lock);
@@ -1136,8 +1141,11 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
 	/* clear rirb int */
 	status = azx_readb(chip, RIRBSTS);
 	if (status & RIRB_INT_MASK) {
-		if (status & RIRB_INT_RESPONSE)
+		if (status & RIRB_INT_RESPONSE) {
+			if (chip->driver_type == AZX_DRIVER_CTX)
+				udelay(80);
 			azx_update_rirb(chip);
+		}
 		azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
 	}
 
@@ -1309,11 +1317,8 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)
 	azx_sd_writel(azx_dev, SD_BDLPU, upper_32_bits(azx_dev->bdl.addr));
 
 	/* enable the position buffer */
-	if (chip->position_fix[0] == POS_FIX_POSBUF ||
-	    chip->position_fix[0] == POS_FIX_AUTO ||
-	    chip->position_fix[1] == POS_FIX_POSBUF ||
-	    chip->position_fix[1] == POS_FIX_AUTO ||
-	    chip->via_dmapos_patch) {
+	if (chip->position_fix[0] != POS_FIX_LPIB ||
+	    chip->position_fix[1] != POS_FIX_LPIB) {
 		if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE))
 			azx_writel(chip, DPLBASE,
 				(u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE);
@@ -1647,7 +1652,7 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
 	struct azx_dev *azx_dev = get_azx_dev(substream);
 	struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
 	struct snd_pcm_runtime *runtime = substream->runtime;
-	unsigned int bufsize, period_bytes, format_val;
+	unsigned int bufsize, period_bytes, format_val, stream_tag;
 	int err;
 
 	azx_stream_reset(chip, azx_dev);
@@ -1689,7 +1694,12 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
 	else
 		azx_dev->fifo_size = 0;
 
-	return snd_hda_codec_prepare(apcm->codec, hinfo, azx_dev->stream_tag,
+	stream_tag = azx_dev->stream_tag;
+	/* CA-IBG chips need the playback stream starting from 1 */
+	if (chip->driver_type == AZX_DRIVER_CTX &&
+	    stream_tag > chip->capture_streams)
+		stream_tag -= chip->capture_streams;
+	return snd_hda_codec_prepare(apcm->codec, hinfo, stream_tag,
 				     azx_dev->format_val, substream);
 }
 
@@ -1852,20 +1862,21 @@ static unsigned int azx_get_position(struct azx *chip,
 				     struct azx_dev *azx_dev)
 {
 	unsigned int pos;
+	int stream = azx_dev->substream->stream;
 
-	if (chip->via_dmapos_patch)
+	switch (chip->position_fix[stream]) {
+	case POS_FIX_LPIB:
+		/* read LPIB */
+		pos = azx_sd_readl(azx_dev, SD_LPIB);
+		break;
+	case POS_FIX_VIACOMBO:
 		pos = azx_via_get_position(chip, azx_dev);
-	else {
-		int stream = azx_dev->substream->stream;
-		if (chip->position_fix[stream] == POS_FIX_POSBUF ||
-		    chip->position_fix[stream] == POS_FIX_AUTO) {
-			/* use the position buffer */
-			pos = le32_to_cpu(*azx_dev->posbuf);
-		} else {
-			/* read LPIB */
-			pos = azx_sd_readl(azx_dev, SD_LPIB);
-		}
+		break;
+	default:
+		/* use the position buffer */
+		pos = le32_to_cpu(*azx_dev->posbuf);
 	}
+
 	if (pos >= azx_dev->bufsize)
 		pos = 0;
 	return pos;
@@ -2313,19 +2324,10 @@ static int __devinit check_position_fix(struct azx *chip, int fix)
 	switch (fix) {
 	case POS_FIX_LPIB:
 	case POS_FIX_POSBUF:
+	case POS_FIX_VIACOMBO:
 		return fix;
 	}
 
-	/* Check VIA/ATI HD Audio Controller exist */
-	switch (chip->driver_type) {
-	case AZX_DRIVER_VIA:
-	case AZX_DRIVER_ATI:
-		chip->via_dmapos_patch = 1;
-		/* Use link position directly, avoid any transfer problem. */
-		return POS_FIX_LPIB;
-	}
-	chip->via_dmapos_patch = 0;
-
 	q = snd_pci_quirk_lookup(chip->pci, position_fix_list);
 	if (q) {
 		printk(KERN_INFO
@@ -2334,6 +2336,15 @@ static int __devinit check_position_fix(struct azx *chip, int fix)
 		       q->value, q->subvendor, q->subdevice);
 		return q->value;
 	}
+
+	/* Check VIA/ATI HD Audio Controller exist */
+	switch (chip->driver_type) {
+	case AZX_DRIVER_VIA:
+	case AZX_DRIVER_ATI:
+		/* Use link position directly, avoid any transfer problem. */
+		return POS_FIX_VIACOMBO;
+	}
+
 	return POS_FIX_AUTO;
 }
 
@@ -2735,25 +2746,17 @@ static void __devexit azx_remove(struct pci_dev *pci)
 
 /* PCI IDs */
 static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
-	/* ICH 6..10 */
-	{ PCI_DEVICE(0x8086, 0x2668), .driver_data = AZX_DRIVER_ICH },
-	{ PCI_DEVICE(0x8086, 0x27d8), .driver_data = AZX_DRIVER_ICH },
-	{ PCI_DEVICE(0x8086, 0x269a), .driver_data = AZX_DRIVER_ICH },
-	{ PCI_DEVICE(0x8086, 0x284b), .driver_data = AZX_DRIVER_ICH },
-	{ PCI_DEVICE(0x8086, 0x2911), .driver_data = AZX_DRIVER_ICH },
-	{ PCI_DEVICE(0x8086, 0x293e), .driver_data = AZX_DRIVER_ICH },
-	{ PCI_DEVICE(0x8086, 0x293f), .driver_data = AZX_DRIVER_ICH },
-	{ PCI_DEVICE(0x8086, 0x3a3e), .driver_data = AZX_DRIVER_ICH },
-	{ PCI_DEVICE(0x8086, 0x3a6e), .driver_data = AZX_DRIVER_ICH },
-	/* PCH */
-	{ PCI_DEVICE(0x8086, 0x3b56), .driver_data = AZX_DRIVER_ICH },
-	{ PCI_DEVICE(0x8086, 0x3b57), .driver_data = AZX_DRIVER_ICH },
 	/* CPT */
 	{ PCI_DEVICE(0x8086, 0x1c20), .driver_data = AZX_DRIVER_PCH },
 	/* PBG */
 	{ PCI_DEVICE(0x8086, 0x1d20), .driver_data = AZX_DRIVER_PCH },
 	/* SCH */
 	{ PCI_DEVICE(0x8086, 0x811b), .driver_data = AZX_DRIVER_SCH },
+	/* Generic Intel */
+	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_ANY_ID),
+	  .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
+	  .class_mask = 0xffffff,
+	  .driver_data = AZX_DRIVER_ICH },
 	/* ATI SB 450/600 */
 	{ PCI_DEVICE(0x1002, 0x437b), .driver_data = AZX_DRIVER_ATI },
 	{ PCI_DEVICE(0x1002, 0x4383), .driver_data = AZX_DRIVER_ATI },
@@ -2794,11 +2797,13 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
 	{ PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_ANY_ID),
 	  .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
 	  .class_mask = 0xffffff,
-	  .driver_data = AZX_DRIVER_GENERIC },
+	  .driver_data = AZX_DRIVER_CTX },
 #else
 	/* this entry seems still valid -- i.e. without emu20kx chip */
-	{ PCI_DEVICE(0x1102, 0x0009), .driver_data = AZX_DRIVER_GENERIC },
+	{ PCI_DEVICE(0x1102, 0x0009), .driver_data = AZX_DRIVER_CTX },
 #endif
+	/* Vortex86MX */
+	{ PCI_DEVICE(0x17f3, 0x3010), .driver_data = AZX_DRIVER_GENERIC },
 	/* AMD/ATI Generic, PCI class code and Vendor ID for HD Audio */
 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_ANY_ID),
 	  .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,

+ 40 - 11
sound/pci/hda/hda_local.h

@@ -38,10 +38,11 @@
  */
 #define HDA_COMPOSE_AMP_VAL_OFS(nid,chs,idx,dir,ofs)		\
 	((nid) | ((chs)<<16) | ((dir)<<18) | ((idx)<<19) | ((ofs)<<23))
+#define HDA_AMP_VAL_MIN_MUTE (1<<29)
 #define HDA_COMPOSE_AMP_VAL(nid,chs,idx,dir) \
 	HDA_COMPOSE_AMP_VAL_OFS(nid, chs, idx, dir, 0)
 /* mono volume with index (index=0,1,...) (channel=1,2) */
-#define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
+#define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, dir, flags) \
 	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx,  \
 	  .subdevice = HDA_SUBDEV_AMP_FLAG, \
 	  .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
@@ -51,16 +52,20 @@
 	  .get = snd_hda_mixer_amp_volume_get, \
 	  .put = snd_hda_mixer_amp_volume_put, \
 	  .tlv = { .c = snd_hda_mixer_amp_tlv },		\
-	  .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) }
+	  .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, dir) | flags }
 /* stereo volume with index */
 #define HDA_CODEC_VOLUME_IDX(xname, xcidx, nid, xindex, direction) \
-	HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, 3, xindex, direction)
+	HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, 3, xindex, direction, 0)
 /* mono volume */
 #define HDA_CODEC_VOLUME_MONO(xname, nid, channel, xindex, direction) \
-	HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, channel, xindex, direction)
+	HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, channel, xindex, direction, 0)
 /* stereo volume */
 #define HDA_CODEC_VOLUME(xname, nid, xindex, direction) \
 	HDA_CODEC_VOLUME_MONO(xname, nid, 3, xindex, direction)
+/* stereo volume with min=mute */
+#define HDA_CODEC_VOLUME_MIN_MUTE(xname, nid, xindex, direction) \
+	HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, 3, xindex, direction, \
+				  HDA_AMP_VAL_MIN_MUTE)
 /* mono mute switch with index (index=0,1,...) (channel=1,2) */
 #define HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
 	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
@@ -215,7 +220,7 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid);
  */
 #define HDA_MAX_NUM_INPUTS	16
 struct hda_input_mux_item {
-	const char *label;
+	char label[32];
 	unsigned int index;
 };
 struct hda_input_mux {
@@ -366,9 +371,7 @@ struct hda_bus_unsolicited {
 
 enum {
 	AUTO_PIN_MIC,
-	AUTO_PIN_FRONT_MIC,
-	AUTO_PIN_LINE,
-	AUTO_PIN_FRONT_LINE,
+	AUTO_PIN_LINE_IN,
 	AUTO_PIN_CD,
 	AUTO_PIN_AUX,
 	AUTO_PIN_LAST
@@ -380,9 +383,33 @@ enum {
 	AUTO_PIN_HP_OUT
 };
 
-extern const char *auto_pin_cfg_labels[AUTO_PIN_LAST];
-
 #define AUTO_CFG_MAX_OUTS	5
+#define AUTO_CFG_MAX_INS	8
+
+struct auto_pin_cfg_item {
+	hda_nid_t pin;
+	int type;
+};
+
+struct auto_pin_cfg;
+const char *hda_get_input_pin_label(struct hda_codec *codec, hda_nid_t pin,
+				    int check_location);
+const char *hda_get_autocfg_input_label(struct hda_codec *codec,
+					const struct auto_pin_cfg *cfg,
+					int input);
+int snd_hda_add_imux_item(struct hda_input_mux *imux, const char *label,
+			  int index, int *type_index_ret);
+
+enum {
+	INPUT_PIN_ATTR_UNUSED,	/* pin not connected */
+	INPUT_PIN_ATTR_INT,	/* internal mic/line-in */
+	INPUT_PIN_ATTR_DOCK,	/* docking mic/line-in */
+	INPUT_PIN_ATTR_NORMAL,	/* mic/line-in jack */
+	INPUT_PIN_ATTR_FRONT,	/* mic/line-in jack in front */
+	INPUT_PIN_ATTR_REAR,	/* mic/line-in jack in rear */
+};
+
+int snd_hda_get_input_pin_attr(unsigned int def_conf);
 
 struct auto_pin_cfg {
 	int line_outs;
@@ -393,7 +420,8 @@ struct auto_pin_cfg {
 	int hp_outs;
 	int line_out_type;	/* AUTO_PIN_XXX_OUT */
 	hda_nid_t hp_pins[AUTO_CFG_MAX_OUTS];
-	hda_nid_t input_pins[AUTO_PIN_LAST];
+	int num_inputs;
+	struct auto_pin_cfg_item inputs[AUTO_CFG_MAX_INS];
 	int dig_outs;
 	hda_nid_t dig_out_pins[2];
 	hda_nid_t dig_in_pin;
@@ -558,6 +586,7 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
 #define get_amp_direction(kc)	(((kc)->private_value >> 18) & 0x1)
 #define get_amp_index(kc)	(((kc)->private_value >> 19) & 0xf)
 #define get_amp_offset(kc)	(((kc)->private_value >> 23) & 0x3f)
+#define get_amp_min_mute(kc)	(((kc)->private_value >> 29) & 0x1)
 
 /*
  * CEA Short Audio Descriptor data

+ 28 - 20
sound/pci/hda/patch_analog.c

@@ -1276,6 +1276,7 @@ static int patch_ad1986a(struct hda_codec *codec)
 	spec->multiout.no_share_stream = 1;
 
 	codec->no_trigger_sense = 1;
+	codec->no_sticky_stream = 1;
 
 	return 0;
 }
@@ -1463,6 +1464,7 @@ static int patch_ad1983(struct hda_codec *codec)
 	codec->patch_ops = ad198x_patch_ops;
 
 	codec->no_trigger_sense = 1;
+	codec->no_sticky_stream = 1;
 
 	return 0;
 }
@@ -1917,6 +1919,7 @@ static int patch_ad1981(struct hda_codec *codec)
 	}
 
 	codec->no_trigger_sense = 1;
+	codec->no_sticky_stream = 1;
 
 	return 0;
 }
@@ -2880,7 +2883,7 @@ static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
 
 /* create input playback/capture controls for the given pin */
 static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
-			    const char *ctlname, int boost)
+			    const char *ctlname, int ctlidx, int boost)
 {
 	char name[32];
 	int err, idx;
@@ -2909,25 +2912,27 @@ static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
 }
 
 /* create playback/capture controls for input pins */
-static int ad1988_auto_create_analog_input_ctls(struct ad198x_spec *spec,
+static int ad1988_auto_create_analog_input_ctls(struct hda_codec *codec,
 						const struct auto_pin_cfg *cfg)
 {
+	struct ad198x_spec *spec = codec->spec;
 	struct hda_input_mux *imux = &spec->private_imux;
-	int i, err;
-
-	for (i = 0; i < AUTO_PIN_LAST; i++) {
-		err = new_analog_input(spec, cfg->input_pins[i],
-				       auto_pin_cfg_labels[i],
-				       i <= AUTO_PIN_FRONT_MIC);
+	int i, err, type, type_idx;
+
+	for (i = 0; i < cfg->num_inputs; i++) {
+		const char *label;
+		type = cfg->inputs[i].type;
+		label = hda_get_autocfg_input_label(codec, cfg, i);
+		snd_hda_add_imux_item(imux, label,
+				      ad1988_pin_to_adc_idx(cfg->inputs[i].pin),
+				      &type_idx);
+		err = new_analog_input(spec, cfg->inputs[i].pin,
+				       label, type_idx,
+				       type == AUTO_PIN_MIC);
 		if (err < 0)
 			return err;
-		imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
-		imux->items[imux->num_items].index = ad1988_pin_to_adc_idx(cfg->input_pins[i]);
-		imux->num_items++;
 	}
-	imux->items[imux->num_items].label = "Mix";
-	imux->items[imux->num_items].index = 9;
-	imux->num_items++;
+	snd_hda_add_imux_item(imux, "Mix", 9, NULL);
 
 	if ((err = add_control(spec, AD_CTL_WIDGET_VOL,
 			       "Analog Mix Playback Volume",
@@ -2994,12 +2999,11 @@ static void ad1988_auto_init_extra_out(struct hda_codec *codec)
 static void ad1988_auto_init_analog_input(struct hda_codec *codec)
 {
 	struct ad198x_spec *spec = codec->spec;
+	const struct auto_pin_cfg *cfg = &spec->autocfg;
 	int i, idx;
 
-	for (i = 0; i < AUTO_PIN_LAST; i++) {
-		hda_nid_t nid = spec->autocfg.input_pins[i];
-		if (! nid)
-			continue;
+	for (i = 0; i < cfg->num_inputs; i++) {
+		hda_nid_t nid = cfg->inputs[i].pin;
 		switch (nid) {
 		case 0x15: /* port-C */
 			snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
@@ -3009,7 +3013,7 @@ static void ad1988_auto_init_analog_input(struct hda_codec *codec)
 			break;
 		}
 		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-				    i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
+				    i == AUTO_PIN_MIC ? PIN_VREF80 : PIN_IN);
 		if (nid != AD1988_PIN_CD_NID)
 			snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
 					    AMP_OUT_MUTE);
@@ -3040,7 +3044,7 @@ static int ad1988_parse_auto_config(struct hda_codec *codec)
 						"Speaker")) < 0 ||
 	    (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
 						"Headphone")) < 0 ||
-	    (err = ad1988_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
+	    (err = ad1988_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0)
 		return err;
 
 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
@@ -3235,6 +3239,7 @@ static int patch_ad1988(struct hda_codec *codec)
 	spec->vmaster_nid = 0x04;
 
 	codec->no_trigger_sense = 1;
+	codec->no_sticky_stream = 1;
 
 	return 0;
 }
@@ -3449,6 +3454,7 @@ static int patch_ad1884(struct hda_codec *codec)
 	codec->patch_ops = ad198x_patch_ops;
 
 	codec->no_trigger_sense = 1;
+	codec->no_sticky_stream = 1;
 
 	return 0;
 }
@@ -4422,6 +4428,7 @@ static int patch_ad1884a(struct hda_codec *codec)
 	}
 
 	codec->no_trigger_sense = 1;
+	codec->no_sticky_stream = 1;
 
 	return 0;
 }
@@ -4761,6 +4768,7 @@ static int patch_ad1882(struct hda_codec *codec)
 	}
 
 	codec->no_trigger_sense = 1;
+	codec->no_sticky_stream = 1;
 
 	return 0;
 }

+ 0 - 224
sound/pci/hda/patch_atihdmi.c

@@ -1,224 +0,0 @@
-/*
- * Universal Interface for Intel High Definition Audio Codec
- *
- * HD audio interface patch for ATI HDMI codecs
- *
- * Copyright (c) 2006 ATI Technologies Inc.
- *
- *
- *  This driver is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This driver is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
- */
-
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <sound/core.h>
-#include "hda_codec.h"
-#include "hda_local.h"
-
-struct atihdmi_spec {
-	struct hda_multi_out multiout;
-
-	struct hda_pcm pcm_rec;
-};
-
-#define CVT_NID		0x02	/* audio converter */
-#define PIN_NID		0x03	/* HDMI output pin */
-
-static struct hda_verb atihdmi_basic_init[] = {
-	/* enable digital output on pin widget */
-	{ 0x03, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
-	{} /* terminator */
-};
-
-/*
- * Controls
- */
-static int atihdmi_build_controls(struct hda_codec *codec)
-{
-	struct atihdmi_spec *spec = codec->spec;
-	int err;
-
-	err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
-	if (err < 0)
-		return err;
-
-	return 0;
-}
-
-static int atihdmi_init(struct hda_codec *codec)
-{
-	snd_hda_sequence_write(codec, atihdmi_basic_init);
-	/* SI codec requires to unmute the pin */
-	if (get_wcaps(codec, PIN_NID) & AC_WCAP_OUT_AMP)
-		snd_hda_codec_write(codec, PIN_NID, 0,
-				    AC_VERB_SET_AMP_GAIN_MUTE,
-				    AMP_OUT_UNMUTE);
-	return 0;
-}
-
-/*
- * Digital out
- */
-static int atihdmi_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
-				     struct hda_codec *codec,
-				     struct snd_pcm_substream *substream)
-{
-	struct atihdmi_spec *spec = codec->spec;
-	return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
-
-static int atihdmi_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
-				      struct hda_codec *codec,
-				      struct snd_pcm_substream *substream)
-{
-	struct atihdmi_spec *spec = codec->spec;
-	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-static int atihdmi_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
-					    struct hda_codec *codec,
-					    unsigned int stream_tag,
-					    unsigned int format,
-					    struct snd_pcm_substream *substream)
-{
-	struct atihdmi_spec *spec = codec->spec;
-	int chans = substream->runtime->channels;
-	int i, err;
-
-	err = snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
-					    format, substream);
-	if (err < 0)
-		return err;
-	snd_hda_codec_write(codec, CVT_NID, 0, AC_VERB_SET_CVT_CHAN_COUNT,
-			    chans - 1);
-	/* FIXME: XXX */
-	for (i = 0; i < chans; i++) {
-		snd_hda_codec_write(codec, CVT_NID, 0,
-				    AC_VERB_SET_HDMI_CHAN_SLOT,
-				    (i << 4) | i);
-	}
-	return 0;
-}
-
-static struct hda_pcm_stream atihdmi_pcm_digital_playback = {
-	.substreams = 1,
-	.channels_min = 2,
-	.channels_max = 2,
-	.nid = CVT_NID, /* NID to query formats and rates and setup streams */
-	.ops = {
-		.open = atihdmi_dig_playback_pcm_open,
-		.close = atihdmi_dig_playback_pcm_close,
-		.prepare = atihdmi_dig_playback_pcm_prepare
-	},
-};
-
-static int atihdmi_build_pcms(struct hda_codec *codec)
-{
-	struct atihdmi_spec *spec = codec->spec;
-	struct hda_pcm *info = &spec->pcm_rec;
-	unsigned int chans;
-
-	codec->num_pcms = 1;
-	codec->pcm_info = info;
-
-	info->name = "ATI HDMI";
-	info->pcm_type = HDA_PCM_TYPE_HDMI;
-	info->stream[SNDRV_PCM_STREAM_PLAYBACK] = atihdmi_pcm_digital_playback;
-
-	/* FIXME: we must check ELD and change the PCM parameters dynamically
-	 */
-	chans = get_wcaps(codec, CVT_NID);
-	chans = get_wcaps_channels(chans);
-	info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans;
-
-	return 0;
-}
-
-static void atihdmi_free(struct hda_codec *codec)
-{
-	kfree(codec->spec);
-}
-
-static struct hda_codec_ops atihdmi_patch_ops = {
-	.build_controls = atihdmi_build_controls,
-	.build_pcms = atihdmi_build_pcms,
-	.init = atihdmi_init,
-	.free = atihdmi_free,
-};
-
-static int patch_atihdmi(struct hda_codec *codec)
-{
-	struct atihdmi_spec *spec;
-
-	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-	if (spec == NULL)
-		return -ENOMEM;
-
-	codec->spec = spec;
-
-	spec->multiout.num_dacs = 0;	  /* no analog */
-	spec->multiout.max_channels = 2;
-	/* NID for copying analog to digital,
-	 * seems to be unused in pure-digital
-	 * case.
-	 */
-	spec->multiout.dig_out_nid = CVT_NID;
-
-	codec->patch_ops = atihdmi_patch_ops;
-
-	return 0;
-}
-
-/*
- * patch entries
- */
-static struct hda_codec_preset snd_hda_preset_atihdmi[] = {
-	{ .id = 0x1002793c, .name = "RS600 HDMI", .patch = patch_atihdmi },
-	{ .id = 0x10027919, .name = "RS600 HDMI", .patch = patch_atihdmi },
-	{ .id = 0x1002791a, .name = "RS690/780 HDMI", .patch = patch_atihdmi },
-	{ .id = 0x1002aa01, .name = "R6xx HDMI", .patch = patch_atihdmi },
-	{ .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_atihdmi },
-	{ .id = 0x17e80047, .name = "Chrontel HDMI",  .patch = patch_atihdmi },
-	{} /* terminator */
-};
-
-MODULE_ALIAS("snd-hda-codec-id:1002793c");
-MODULE_ALIAS("snd-hda-codec-id:10027919");
-MODULE_ALIAS("snd-hda-codec-id:1002791a");
-MODULE_ALIAS("snd-hda-codec-id:1002aa01");
-MODULE_ALIAS("snd-hda-codec-id:10951390");
-MODULE_ALIAS("snd-hda-codec-id:17e80047");
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("ATI HDMI HD-audio codec");
-
-static struct hda_codec_preset_list atihdmi_list = {
-	.preset = snd_hda_preset_atihdmi,
-	.owner = THIS_MODULE,
-};
-
-static int __init patch_atihdmi_init(void)
-{
-	return snd_hda_add_codec_preset(&atihdmi_list);
-}
-
-static void __exit patch_atihdmi_exit(void)
-{
-	snd_hda_delete_codec_preset(&atihdmi_list);
-}
-
-module_init(patch_atihdmi_init)
-module_exit(patch_atihdmi_exit)

+ 5 - 5
sound/pci/hda/patch_ca0110.c

@@ -468,13 +468,13 @@ static void parse_input(struct hda_codec *codec)
 			spec->dig_in = nid;
 			continue;
 		}
-		for (j = 0; j < AUTO_PIN_LAST; j++)
-			if (cfg->input_pins[j] == pin)
+		for (j = 0; j < cfg->num_inputs; j++)
+			if (cfg->inputs[j].pin == pin)
 				break;
-		if (j >= AUTO_PIN_LAST)
+		if (j >= cfg->num_inputs)
 			continue;
 		spec->input_pins[n] = pin;
-		spec->input_labels[n] = auto_pin_cfg_labels[j];
+		spec->input_labels[n] = hda_get_input_pin_label(codec, pin, 1);
 		spec->adcs[n] = nid;
 		n++;
 	}
@@ -489,7 +489,7 @@ static void parse_digital(struct hda_codec *codec)
 	if (cfg->dig_outs &&
 	    snd_hda_get_connections(codec, cfg->dig_out_pins[0],
 				    &spec->dig_out, 1) == 1)
-		spec->multiout.dig_out_nid = cfg->dig_out_pins[0];
+		spec->multiout.dig_out_nid = spec->dig_out;
 }
 
 static int ca0110_parse_auto_config(struct hda_codec *codec)

+ 70 - 24
sound/pci/hda/patch_cirrus.c

@@ -65,6 +65,7 @@ struct cs_spec {
 
 /* available models */
 enum {
+	CS420X_MBP53,
 	CS420X_MBP55,
 	CS420X_IMAC27,
 	CS420X_AUTO,
@@ -329,12 +330,12 @@ static int is_ext_mic(struct hda_codec *codec, unsigned int idx)
 {
 	struct cs_spec *spec = codec->spec;
 	struct auto_pin_cfg *cfg = &spec->autocfg;
-	hda_nid_t pin = cfg->input_pins[idx];
+	hda_nid_t pin = cfg->inputs[idx].pin;
 	unsigned int val = snd_hda_query_pin_caps(codec, pin);
 	if (!(val & AC_PINCAP_PRES_DETECT))
 		return 0;
 	val = snd_hda_codec_get_pincfg(codec, pin);
-	return (get_defcfg_connect(val) == AC_JACK_PORT_COMPLEX);
+	return (snd_hda_get_input_pin_attr(val) != INPUT_PIN_ATTR_INT);
 }
 
 static hda_nid_t get_adc(struct hda_codec *codec, hda_nid_t pin,
@@ -424,10 +425,8 @@ static int parse_input(struct hda_codec *codec)
 	struct auto_pin_cfg *cfg = &spec->autocfg;
 	int i;
 
-	for (i = 0; i < AUTO_PIN_LAST; i++) {
-		hda_nid_t pin = cfg->input_pins[i];
-		if (!pin)
-			continue;
+	for (i = 0; i < cfg->num_inputs; i++) {
+		hda_nid_t pin = cfg->inputs[i].pin;
 		spec->input_idx[spec->num_inputs] = i;
 		spec->capsrc_idx[i] = spec->num_inputs++;
 		spec->cur_input = i;
@@ -438,16 +437,17 @@ static int parse_input(struct hda_codec *codec)
 
 	/* check whether the automatic mic switch is available */
 	if (spec->num_inputs == 2 &&
-	    spec->adc_nid[AUTO_PIN_MIC] && spec->adc_nid[AUTO_PIN_FRONT_MIC]) {
-		if (is_ext_mic(codec, cfg->input_pins[AUTO_PIN_FRONT_MIC])) {
-			if (!is_ext_mic(codec, cfg->input_pins[AUTO_PIN_MIC])) {
+	    cfg->inputs[0].type == AUTO_PIN_MIC &&
+	    cfg->inputs[1].type == AUTO_PIN_MIC) {
+		if (is_ext_mic(codec, cfg->inputs[0].pin)) {
+			if (!is_ext_mic(codec, cfg->inputs[1].pin)) {
 				spec->mic_detect = 1;
-				spec->automic_idx = AUTO_PIN_FRONT_MIC;
+				spec->automic_idx = 0;
 			}
 		} else {
-			if (is_ext_mic(codec, cfg->input_pins[AUTO_PIN_MIC])) {
+			if (is_ext_mic(codec, cfg->inputs[1].pin)) {
 				spec->mic_detect = 1;
-				spec->automic_idx = AUTO_PIN_MIC;
+				spec->automic_idx = 1;
 			}
 		}
 	}
@@ -674,6 +674,7 @@ static int cs_capture_source_info(struct snd_kcontrol *kcontrol,
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct cs_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
 	unsigned int idx;
 
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
@@ -682,7 +683,8 @@ static int cs_capture_source_info(struct snd_kcontrol *kcontrol,
 	if (uinfo->value.enumerated.item >= spec->num_inputs)
 		uinfo->value.enumerated.item = spec->num_inputs - 1;
 	idx = spec->input_idx[uinfo->value.enumerated.item];
-	strcpy(uinfo->value.enumerated.name, auto_pin_cfg_labels[idx]);
+	strcpy(uinfo->value.enumerated.name,
+	       hda_get_input_pin_label(codec, cfg->inputs[idx].pin, 1));
 	return 0;
 }
 
@@ -740,6 +742,27 @@ static struct hda_bind_ctls *make_bind_capture(struct hda_codec *codec,
 	return bind;
 }
 
+/* add a (input-boost) volume control to the given input pin */
+static int add_input_volume_control(struct hda_codec *codec,
+				    struct auto_pin_cfg *cfg,
+				    int item)
+{
+	hda_nid_t pin = cfg->inputs[item].pin;
+	u32 caps;
+	const char *label;
+	struct snd_kcontrol *kctl;
+		
+	if (!(get_wcaps(codec, pin) & AC_WCAP_IN_AMP))
+		return 0;
+	caps = query_amp_caps(codec, pin, HDA_INPUT);
+	caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
+	if (caps <= 1)
+		return 0;
+	label = hda_get_autocfg_input_label(codec, cfg, item);
+	return add_volume(codec, label, 0,
+			  HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT), 1, &kctl);
+}
+
 static int build_input(struct hda_codec *codec)
 {
 	struct cs_spec *spec = codec->spec;
@@ -779,6 +802,12 @@ static int build_input(struct hda_codec *codec)
 			return err;
 	}
 
+	for (i = 0; i < spec->num_inputs; i++) {
+		err = add_input_volume_control(codec, &spec->autocfg, i);
+		if (err < 0)
+			return err;
+	}
+
 	return 0;
 }
 
@@ -838,7 +867,8 @@ static void cs_automute(struct hda_codec *codec)
 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
 				    hp_present ? 0 : PIN_OUT);
 	}
-	if (spec->board_config == CS420X_MBP55 ||
+	if (spec->board_config == CS420X_MBP53 ||
+	    spec->board_config == CS420X_MBP55 ||
 	    spec->board_config == CS420X_IMAC27) {
 		unsigned int gpio = hp_present ? 0x02 : 0x08;
 		snd_hda_codec_write(codec, 0x01, 0,
@@ -853,15 +883,12 @@ static void cs_automic(struct hda_codec *codec)
 	hda_nid_t nid;
 	unsigned int present;
 	
-	nid = cfg->input_pins[spec->automic_idx];
+	nid = cfg->inputs[spec->automic_idx].pin;
 	present = snd_hda_jack_detect(codec, nid);
 	if (present)
 		change_cur_input(codec, spec->automic_idx, 0);
-	else {
-		unsigned int imic = (spec->automic_idx == AUTO_PIN_MIC) ?
-			AUTO_PIN_FRONT_MIC : AUTO_PIN_MIC;
-		change_cur_input(codec, imic, 0);
-	}
+	else
+		change_cur_input(codec, !spec->automic_idx, 0);
 }
 
 /*
@@ -918,14 +945,14 @@ static void init_input(struct hda_codec *codec)
 	unsigned int coef;
 	int i;
 
-	for (i = 0; i < AUTO_PIN_LAST; i++) {
+	for (i = 0; i < cfg->num_inputs; i++) {
 		unsigned int ctl;
-		hda_nid_t pin = cfg->input_pins[i];
-		if (!pin || !spec->adc_nid[i])
+		hda_nid_t pin = cfg->inputs[i].pin;
+		if (!spec->adc_nid[i])
 			continue;
 		/* set appropriate pin control and mute first */
 		ctl = PIN_IN;
-		if (i <= AUTO_PIN_FRONT_MIC) {
+		if (cfg->inputs[i].type == AUTO_PIN_MIC) {
 			unsigned int caps = snd_hda_query_pin_caps(codec, pin);
 			caps >>= AC_PINCAP_VREF_SHIFT;
 			if (caps & AC_PINCAP_VREF_80)
@@ -1130,6 +1157,7 @@ static int cs_parse_auto_config(struct hda_codec *codec)
 }
 
 static const char *cs420x_models[CS420X_MODELS] = {
+	[CS420X_MBP53] = "mbp53",
 	[CS420X_MBP55] = "mbp55",
 	[CS420X_IMAC27] = "imac27",
 	[CS420X_AUTO] = "auto",
@@ -1137,7 +1165,9 @@ static const char *cs420x_models[CS420X_MODELS] = {
 
 
 static struct snd_pci_quirk cs420x_cfg_tbl[] = {
+	SND_PCI_QUIRK(0x10de, 0x0ac0, "MacBookPro 5,3", CS420X_MBP53),
 	SND_PCI_QUIRK(0x10de, 0xcb79, "MacBookPro 5,5", CS420X_MBP55),
+	SND_PCI_QUIRK(0x10de, 0xcb89, "MacBookPro 7,1", CS420X_MBP55),
 	SND_PCI_QUIRK(0x8086, 0x7270, "IMac 27 Inch", CS420X_IMAC27),
 	{} /* terminator */
 };
@@ -1147,6 +1177,20 @@ struct cs_pincfg {
 	u32 val;
 };
 
+static struct cs_pincfg mbp53_pincfgs[] = {
+	{ 0x09, 0x012b4050 },
+	{ 0x0a, 0x90100141 },
+	{ 0x0b, 0x90100140 },
+	{ 0x0c, 0x018b3020 },
+	{ 0x0d, 0x90a00110 },
+	{ 0x0e, 0x400000f0 },
+	{ 0x0f, 0x01cbe030 },
+	{ 0x10, 0x014be060 },
+	{ 0x12, 0x400000f0 },
+	{ 0x15, 0x400000f0 },
+	{} /* terminator */
+};
+
 static struct cs_pincfg mbp55_pincfgs[] = {
 	{ 0x09, 0x012b4030 },
 	{ 0x0a, 0x90100121 },
@@ -1176,6 +1220,7 @@ static struct cs_pincfg imac27_pincfgs[] = {
 };
 
 static struct cs_pincfg *cs_pincfgs[CS420X_MODELS] = {
+	[CS420X_MBP53] = mbp53_pincfgs,
 	[CS420X_MBP55] = mbp55_pincfgs,
 	[CS420X_IMAC27] = imac27_pincfgs,
 };
@@ -1208,6 +1253,7 @@ static int patch_cs420x(struct hda_codec *codec)
 
 	switch (spec->board_config) {
 	case CS420X_IMAC27:
+	case CS420X_MBP53:
 	case CS420X_MBP55:
 		/* GPIO1 = headphones */
 		/* GPIO3 = speakers */

+ 642 - 9
sound/pci/hda/patch_conexant.c

@@ -57,6 +57,12 @@ struct conexant_jack {
 
 };
 
+struct pin_dac_pair {
+	hda_nid_t pin;
+	hda_nid_t dac;
+	int type;
+};
+
 struct conexant_spec {
 
 	struct snd_kcontrol_new *mixers[5];
@@ -77,6 +83,7 @@ struct conexant_spec {
 	unsigned int cur_eapd;
 	unsigned int hp_present;
 	unsigned int auto_mic;
+	int auto_mic_ext;		/* autocfg.inputs[] index for ext mic */
 	unsigned int need_dac_fix;
 
 	/* capture */
@@ -110,9 +117,12 @@ struct conexant_spec {
 	struct auto_pin_cfg autocfg;
 	struct hda_input_mux private_imux;
 	hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
+	struct pin_dac_pair dac_info[8];
+	int dac_info_filled;
 
-	unsigned int dell_automute;
 	unsigned int port_d_mode;
+	unsigned int auto_mute:1;	/* used in auto-parser */
+	unsigned int dell_automute:1;
 	unsigned int dell_vostro:1;
 	unsigned int ideapad:1;
 	unsigned int thinkpad:1;
@@ -3065,7 +3075,7 @@ enum {
 	CXT5066_LAPTOP,		/* Laptops w/ EAPD support */
 	CXT5066_DELL_LAPTOP,	/* Dell Laptop */
 	CXT5066_OLPC_XO_1_5,	/* OLPC XO 1.5 */
-	CXT5066_DELL_VOSTO,	/* Dell Vostro 1015i */
+	CXT5066_DELL_VOSTRO,	/* Dell Vostro 1015i */
 	CXT5066_IDEAPAD,	/* Lenovo IdeaPad U150 */
 	CXT5066_THINKPAD,	/* Lenovo ThinkPad T410s, others? */
 	CXT5066_HP_LAPTOP,      /* HP Laptop */
@@ -3076,25 +3086,26 @@ static const char *cxt5066_models[CXT5066_MODELS] = {
 	[CXT5066_LAPTOP]	= "laptop",
 	[CXT5066_DELL_LAPTOP]	= "dell-laptop",
 	[CXT5066_OLPC_XO_1_5]	= "olpc-xo-1_5",
-	[CXT5066_DELL_VOSTO]    = "dell-vostro",
+	[CXT5066_DELL_VOSTRO]	= "dell-vostro",
 	[CXT5066_IDEAPAD]	= "ideapad",
 	[CXT5066_THINKPAD]	= "thinkpad",
 	[CXT5066_HP_LAPTOP]	= "hp-laptop",
 };
 
 static struct snd_pci_quirk cxt5066_cfg_tbl[] = {
-	SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board",
-		      CXT5066_LAPTOP),
+	SND_PCI_QUIRK_MASK(0x1025, 0xff00, 0x0400, "Acer", CXT5066_IDEAPAD),
+	SND_PCI_QUIRK(0x1028, 0x02d8, "Dell Vostro", CXT5066_DELL_VOSTRO),
 	SND_PCI_QUIRK(0x1028, 0x02f5, "Dell",
 		      CXT5066_DELL_LAPTOP),
-	SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5),
-	SND_PCI_QUIRK(0x1028, 0x02d8, "Dell Vostro", CXT5066_DELL_VOSTO),
-	SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTO),
+	SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTRO),
 	SND_PCI_QUIRK(0x1028, 0x0408, "Dell Inspiron One 19T", CXT5066_IDEAPAD),
 	SND_PCI_QUIRK(0x103c, 0x360b, "HP G60", CXT5066_HP_LAPTOP),
 	SND_PCI_QUIRK(0x1179, 0xff1e, "Toshiba Satellite C650D", CXT5066_IDEAPAD),
 	SND_PCI_QUIRK(0x1179, 0xff50, "Toshiba Satellite P500-PSPGSC-01800T", CXT5066_OLPC_XO_1_5),
 	SND_PCI_QUIRK(0x1179, 0xffe0, "Toshiba Satellite Pro T130-15F", CXT5066_OLPC_XO_1_5),
+	SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board",
+		      CXT5066_LAPTOP),
+	SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5),
 	SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400s", CXT5066_THINKPAD),
 	SND_PCI_QUIRK(0x17aa, 0x21b2, "Thinkpad X100e", CXT5066_IDEAPAD),
 	SND_PCI_QUIRK(0x17aa, 0x21b3, "Thinkpad Edge 13 (197)", CXT5066_IDEAPAD),
@@ -3196,7 +3207,7 @@ static int patch_cxt5066(struct hda_codec *codec)
 		spec->capture_prepare = cxt5066_olpc_capture_prepare;
 		spec->capture_cleanup = cxt5066_olpc_capture_cleanup;
 		break;
-	case CXT5066_DELL_VOSTO:
+	case CXT5066_DELL_VOSTRO:
 		codec->patch_ops.init = cxt5066_init;
 		codec->patch_ops.unsol_event = cxt5066_vostro_event;
 		spec->init_verbs[0] = cxt5066_init_verbs_vostro;
@@ -3253,6 +3264,604 @@ static int patch_cxt5066(struct hda_codec *codec)
 	return 0;
 }
 
+/*
+ * Automatic parser for CX20641 & co
+ */
+
+static hda_nid_t cx_auto_adc_nids[] = { 0x14 };
+
+/* get the connection index of @nid in the widget @mux */
+static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
+				hda_nid_t nid)
+{
+	hda_nid_t conn[HDA_MAX_NUM_INPUTS];
+	int i, nums;
+
+	nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
+	for (i = 0; i < nums; i++)
+		if (conn[i] == nid)
+			return i;
+	return -1;
+}
+
+/* get an unassigned DAC from the given list.
+ * Return the nid if found and reduce the DAC list, or return zero if
+ * not found
+ */
+static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t pin,
+				    hda_nid_t *dacs, int *num_dacs)
+{
+	int i, nums = *num_dacs;
+	hda_nid_t ret = 0;
+
+	for (i = 0; i < nums; i++) {
+		if (get_connection_index(codec, pin, dacs[i]) >= 0) {
+			ret = dacs[i];
+			break;
+		}
+	}
+	if (!ret)
+		return 0;
+	if (--nums > 0)
+		memmove(dacs, dacs + 1, nums * sizeof(hda_nid_t));
+	*num_dacs = nums;
+	return ret;
+}
+
+#define MAX_AUTO_DACS	5
+
+/* fill analog DAC list from the widget tree */
+static int fill_cx_auto_dacs(struct hda_codec *codec, hda_nid_t *dacs)
+{
+	hda_nid_t nid, end_nid;
+	int nums = 0;
+
+	end_nid = codec->start_nid + codec->num_nodes;
+	for (nid = codec->start_nid; nid < end_nid; nid++) {
+		unsigned int wcaps = get_wcaps(codec, nid);
+		unsigned int type = get_wcaps_type(wcaps);
+		if (type == AC_WID_AUD_OUT && !(wcaps & AC_WCAP_DIGITAL)) {
+			dacs[nums++] = nid;
+			if (nums >= MAX_AUTO_DACS)
+				break;
+		}
+	}
+	return nums;
+}
+
+/* fill pin_dac_pair list from the pin and dac list */
+static int fill_dacs_for_pins(struct hda_codec *codec, hda_nid_t *pins,
+			      int num_pins, hda_nid_t *dacs, int *rest,
+			      struct pin_dac_pair *filled, int type)
+{
+	int i, nums;
+
+	nums = 0;
+	for (i = 0; i < num_pins; i++) {
+		filled[nums].pin = pins[i];
+		filled[nums].type = type;
+		filled[nums].dac = get_unassigned_dac(codec, pins[i], dacs, rest);
+		nums++;
+	}
+	return nums;
+}
+
+/* parse analog output paths */
+static void cx_auto_parse_output(struct hda_codec *codec)
+{
+	struct conexant_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	hda_nid_t dacs[MAX_AUTO_DACS];
+	int i, j, nums, rest;
+
+	rest = fill_cx_auto_dacs(codec, dacs);
+	/* parse all analog output pins */
+	nums = fill_dacs_for_pins(codec, cfg->line_out_pins, cfg->line_outs,
+				  dacs, &rest, spec->dac_info,
+				  AUTO_PIN_LINE_OUT);
+	nums += fill_dacs_for_pins(codec, cfg->hp_pins, cfg->hp_outs,
+				  dacs, &rest, spec->dac_info + nums,
+				  AUTO_PIN_HP_OUT);
+	nums += fill_dacs_for_pins(codec, cfg->speaker_pins, cfg->speaker_outs,
+				  dacs, &rest, spec->dac_info + nums,
+				  AUTO_PIN_SPEAKER_OUT);
+	spec->dac_info_filled = nums;
+	/* fill multiout struct */
+	for (i = 0; i < nums; i++) {
+		hda_nid_t dac = spec->dac_info[i].dac;
+		if (!dac)
+			continue;
+		switch (spec->dac_info[i].type) {
+		case AUTO_PIN_LINE_OUT:
+			spec->private_dac_nids[spec->multiout.num_dacs] = dac;
+			spec->multiout.num_dacs++;
+			break;
+		case AUTO_PIN_HP_OUT:
+		case AUTO_PIN_SPEAKER_OUT:
+			if (!spec->multiout.hp_nid) {
+				spec->multiout.hp_nid = dac;
+				break;
+			}
+			for (j = 0; j < ARRAY_SIZE(spec->multiout.extra_out_nid); j++)
+				if (!spec->multiout.extra_out_nid[j]) {
+					spec->multiout.extra_out_nid[j] = dac;
+					break;
+				}
+			break;
+		}
+	}
+	spec->multiout.dac_nids = spec->private_dac_nids;
+	spec->multiout.max_channels = nums * 2;
+
+	if (cfg->hp_outs > 0)
+		spec->auto_mute = 1;
+	spec->vmaster_nid = spec->private_dac_nids[0];
+}
+
+/* auto-mute/unmute speaker and line outs according to headphone jack */
+static void cx_auto_hp_automute(struct hda_codec *codec)
+{
+	struct conexant_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	int i, present;
+
+	if (!spec->auto_mute)
+		return;
+	present = 0;
+	for (i = 0; i < cfg->hp_outs; i++) {
+		if (snd_hda_jack_detect(codec, cfg->hp_pins[i])) {
+			present = 1;
+			break;
+		}
+	}
+	for (i = 0; i < cfg->line_outs; i++) {
+		snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
+				    AC_VERB_SET_PIN_WIDGET_CONTROL,
+				    present ? 0 : PIN_OUT);
+	}
+	for (i = 0; i < cfg->speaker_outs; i++) {
+		snd_hda_codec_write(codec, cfg->speaker_pins[i], 0,
+				    AC_VERB_SET_PIN_WIDGET_CONTROL,
+				    present ? 0 : PIN_OUT);
+	}
+}
+
+/* automatic switch internal and external mic */
+static void cx_auto_automic(struct hda_codec *codec)
+{
+	struct conexant_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	struct hda_input_mux *imux = &spec->private_imux;
+	int ext_idx = spec->auto_mic_ext;
+
+	if (!spec->auto_mic)
+		return;
+	if (snd_hda_jack_detect(codec, cfg->inputs[ext_idx].pin)) {
+		snd_hda_codec_write(codec, spec->adc_nids[0], 0,
+				    AC_VERB_SET_CONNECT_SEL,
+				    imux->items[ext_idx].index);
+	} else {
+		snd_hda_codec_write(codec, spec->adc_nids[0], 0,
+				    AC_VERB_SET_CONNECT_SEL,
+				    imux->items[!ext_idx].index);
+	}
+}
+
+static void cx_auto_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+	int nid = (res & AC_UNSOL_RES_SUBTAG) >> 20;
+	switch (res >> 26) {
+	case CONEXANT_HP_EVENT:
+		cx_auto_hp_automute(codec);
+		conexant_report_jack(codec, nid);
+		break;
+	case CONEXANT_MIC_EVENT:
+		cx_auto_automic(codec);
+		conexant_report_jack(codec, nid);
+		break;
+	}
+}
+
+/* return true if it's an internal-mic pin */
+static int is_int_mic(struct hda_codec *codec, hda_nid_t pin)
+{
+	unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin);
+	return get_defcfg_device(def_conf) == AC_JACK_MIC_IN &&
+		snd_hda_get_input_pin_attr(def_conf) == INPUT_PIN_ATTR_INT;
+}
+
+/* return true if it's an external-mic pin */
+static int is_ext_mic(struct hda_codec *codec, hda_nid_t pin)
+{
+	unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin);
+	return get_defcfg_device(def_conf) == AC_JACK_MIC_IN &&
+		snd_hda_get_input_pin_attr(def_conf) >= INPUT_PIN_ATTR_NORMAL &&
+		(snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_PRES_DETECT);
+}
+
+/* check whether the pin config is suitable for auto-mic switching;
+ * auto-mic is enabled only when one int-mic and one-ext mic exist
+ */
+static void cx_auto_check_auto_mic(struct hda_codec *codec)
+{
+	struct conexant_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+
+	if (is_ext_mic(codec, cfg->inputs[0].pin) &&
+	    is_int_mic(codec, cfg->inputs[1].pin)) {
+		spec->auto_mic = 1;
+		spec->auto_mic_ext = 1;
+		return;
+	}
+	if (is_int_mic(codec, cfg->inputs[1].pin) &&
+	    is_ext_mic(codec, cfg->inputs[0].pin)) {
+		spec->auto_mic = 1;
+		spec->auto_mic_ext = 0;
+		return;
+	}
+}
+
+static void cx_auto_parse_input(struct hda_codec *codec)
+{
+	struct conexant_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	struct hda_input_mux *imux;
+	int i;
+
+	imux = &spec->private_imux;
+	for (i = 0; i < cfg->num_inputs; i++) {
+		int idx = get_connection_index(codec, spec->adc_nids[0],
+					       cfg->inputs[i].pin);
+		if (idx >= 0) {
+			const char *label;
+			label = hda_get_autocfg_input_label(codec, cfg, i);
+			snd_hda_add_imux_item(imux, label, idx, NULL);
+		}
+	}
+	if (imux->num_items == 2 && cfg->num_inputs == 2)
+		cx_auto_check_auto_mic(codec);
+	if (imux->num_items > 1 && !spec->auto_mic)
+		spec->input_mux = imux;
+}
+
+/* get digital-input audio widget corresponding to the given pin */
+static hda_nid_t cx_auto_get_dig_in(struct hda_codec *codec, hda_nid_t pin)
+{
+	hda_nid_t nid, end_nid;
+
+	end_nid = codec->start_nid + codec->num_nodes;
+	for (nid = codec->start_nid; nid < end_nid; nid++) {
+		unsigned int wcaps = get_wcaps(codec, nid);
+		unsigned int type = get_wcaps_type(wcaps);
+		if (type == AC_WID_AUD_IN && (wcaps & AC_WCAP_DIGITAL)) {
+			if (get_connection_index(codec, nid, pin) >= 0)
+				return nid;
+		}
+	}
+	return 0;
+}
+
+static void cx_auto_parse_digital(struct hda_codec *codec)
+{
+	struct conexant_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	hda_nid_t nid;
+
+	if (cfg->dig_outs &&
+	    snd_hda_get_connections(codec, cfg->dig_out_pins[0], &nid, 1) == 1)
+		spec->multiout.dig_out_nid = nid;
+	if (cfg->dig_in_pin)
+		spec->dig_in_nid = cx_auto_get_dig_in(codec, cfg->dig_in_pin);
+}
+
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+static void cx_auto_parse_beep(struct hda_codec *codec)
+{
+	struct conexant_spec *spec = codec->spec;
+	hda_nid_t nid, end_nid;
+
+	end_nid = codec->start_nid + codec->num_nodes;
+	for (nid = codec->start_nid; nid < end_nid; nid++)
+		if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) {
+			set_beep_amp(spec, nid, 0, HDA_OUTPUT);
+			break;
+		}
+}
+#else
+#define cx_auto_parse_beep(codec)
+#endif
+
+static int cx_auto_parse_auto_config(struct hda_codec *codec)
+{
+	struct conexant_spec *spec = codec->spec;
+	int err;
+
+	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
+	if (err < 0)
+		return err;
+
+	cx_auto_parse_output(codec);
+	cx_auto_parse_input(codec);
+	cx_auto_parse_digital(codec);
+	cx_auto_parse_beep(codec);
+	return 0;
+}
+
+static void cx_auto_turn_on_eapd(struct hda_codec *codec, int num_pins,
+				 hda_nid_t *pins)
+{
+	int i;
+	for (i = 0; i < num_pins; i++) {
+		if (snd_hda_query_pin_caps(codec, pins[i]) & AC_PINCAP_EAPD)
+			snd_hda_codec_write(codec, pins[i], 0,
+					    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
+	}
+}
+
+static void select_connection(struct hda_codec *codec, hda_nid_t pin,
+			      hda_nid_t src)
+{
+	int idx = get_connection_index(codec, pin, src);
+	if (idx >= 0)
+		snd_hda_codec_write(codec, pin, 0,
+				    AC_VERB_SET_CONNECT_SEL, idx);
+}
+
+static void cx_auto_init_output(struct hda_codec *codec)
+{
+	struct conexant_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	hda_nid_t nid;
+	int i;
+
+	for (i = 0; i < spec->multiout.num_dacs; i++)
+		snd_hda_codec_write(codec, spec->multiout.dac_nids[i], 0,
+				    AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+
+	for (i = 0; i < cfg->hp_outs; i++)
+		snd_hda_codec_write(codec, cfg->hp_pins[i], 0,
+				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
+	if (spec->auto_mute) {
+		for (i = 0; i < cfg->hp_outs; i++) {
+			snd_hda_codec_write(codec, cfg->hp_pins[i], 0,
+				    AC_VERB_SET_UNSOLICITED_ENABLE,
+				    AC_USRSP_EN | CONEXANT_HP_EVENT);
+		}
+		cx_auto_hp_automute(codec);
+	} else {
+		for (i = 0; i < cfg->line_outs; i++)
+			snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
+				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+		for (i = 0; i < cfg->speaker_outs; i++)
+			snd_hda_codec_write(codec, cfg->speaker_pins[i], 0,
+				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+	}
+
+	for (i = 0; i < spec->dac_info_filled; i++) {
+		nid = spec->dac_info[i].dac;
+		if (!nid)
+			nid = spec->multiout.dac_nids[0];
+		select_connection(codec, spec->dac_info[i].pin, nid);
+	}
+
+	/* turn on EAPD */
+	cx_auto_turn_on_eapd(codec, cfg->line_outs, cfg->line_out_pins);
+	cx_auto_turn_on_eapd(codec, cfg->hp_outs, cfg->hp_pins);
+	cx_auto_turn_on_eapd(codec, cfg->speaker_outs, cfg->speaker_pins);
+}
+
+static void cx_auto_init_input(struct hda_codec *codec)
+{
+	struct conexant_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	int i;
+
+	for (i = 0; i < spec->num_adc_nids; i++)
+		snd_hda_codec_write(codec, spec->adc_nids[i], 0,
+				    AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0));
+
+	for (i = 0; i < cfg->num_inputs; i++) {
+		unsigned int type;
+		if (cfg->inputs[i].type == AUTO_PIN_MIC)
+			type = PIN_VREF80;
+		else
+			type = PIN_IN;
+		snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
+				    AC_VERB_SET_PIN_WIDGET_CONTROL, type);
+	}
+
+	if (spec->auto_mic) {
+		int ext_idx = spec->auto_mic_ext;
+		snd_hda_codec_write(codec, cfg->inputs[ext_idx].pin, 0,
+				    AC_VERB_SET_UNSOLICITED_ENABLE,
+				    AC_USRSP_EN | CONEXANT_MIC_EVENT);
+		cx_auto_automic(codec);
+	} else {
+		for (i = 0; i < spec->num_adc_nids; i++) {
+			snd_hda_codec_write(codec, spec->adc_nids[i], 0,
+					    AC_VERB_SET_CONNECT_SEL,
+					    spec->private_imux.items[0].index);
+		}
+	}
+}
+
+static void cx_auto_init_digital(struct hda_codec *codec)
+{
+	struct conexant_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+
+	if (spec->multiout.dig_out_nid)
+		snd_hda_codec_write(codec, cfg->dig_out_pins[0], 0,
+				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+	if (spec->dig_in_nid)
+		snd_hda_codec_write(codec, cfg->dig_in_pin, 0,
+				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
+}
+
+static int cx_auto_init(struct hda_codec *codec)
+{
+	/*snd_hda_sequence_write(codec, cx_auto_init_verbs);*/
+	cx_auto_init_output(codec);
+	cx_auto_init_input(codec);
+	cx_auto_init_digital(codec);
+	return 0;
+}
+
+static int cx_auto_add_volume(struct hda_codec *codec, const char *basename,
+			      const char *dir, int cidx,
+			      hda_nid_t nid, int hda_dir)
+{
+	static char name[32];
+	static struct snd_kcontrol_new knew[] = {
+		HDA_CODEC_VOLUME(name, 0, 0, 0),
+		HDA_CODEC_MUTE(name, 0, 0, 0),
+	};
+	static char *sfx[2] = { "Volume", "Switch" };
+	int i, err;
+
+	for (i = 0; i < 2; i++) {
+		struct snd_kcontrol *kctl;
+		knew[i].private_value = HDA_COMPOSE_AMP_VAL(nid, 3, 0, hda_dir);
+		knew[i].subdevice = HDA_SUBDEV_AMP_FLAG;
+		knew[i].index = cidx;
+		snprintf(name, sizeof(name), "%s%s %s", basename, dir, sfx[i]);
+		kctl = snd_ctl_new1(&knew[i], codec);
+		if (!kctl)
+			return -ENOMEM;
+		err = snd_hda_ctl_add(codec, nid, kctl);
+		if (err < 0)
+			return err;
+		if (!(query_amp_caps(codec, nid, hda_dir) & AC_AMPCAP_MUTE))
+			break;
+	}
+	return 0;
+}
+
+#define cx_auto_add_pb_volume(codec, nid, str, idx)			\
+	cx_auto_add_volume(codec, str, " Playback", idx, nid, HDA_OUTPUT)
+
+static int cx_auto_build_output_controls(struct hda_codec *codec)
+{
+	struct conexant_spec *spec = codec->spec;
+	int i, err;
+	int num_line = 0, num_hp = 0, num_spk = 0;
+	static const char *texts[3] = { "Front", "Surround", "CLFE" };
+
+	if (spec->dac_info_filled == 1)
+		return cx_auto_add_pb_volume(codec, spec->dac_info[0].dac,
+					     "Master", 0);
+	for (i = 0; i < spec->dac_info_filled; i++) {
+		const char *label;
+		int idx, type;
+		if (!spec->dac_info[i].dac)
+			continue;
+		type = spec->dac_info[i].type;
+		if (type == AUTO_PIN_LINE_OUT)
+			type = spec->autocfg.line_out_type;
+		switch (type) {
+		case AUTO_PIN_LINE_OUT:
+		default:
+			label = texts[num_line++];
+			idx = 0;
+			break;
+		case AUTO_PIN_HP_OUT:
+			label = "Headphone";
+			idx = num_hp++;
+			break;
+		case AUTO_PIN_SPEAKER_OUT:
+			label = "Speaker";
+			idx = num_spk++;
+			break;
+		}
+		err = cx_auto_add_pb_volume(codec, spec->dac_info[i].dac,
+					    label, idx);
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+static int cx_auto_build_input_controls(struct hda_codec *codec)
+{
+	struct conexant_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	static const char *prev_label;
+	int i, err, cidx;
+
+	err = cx_auto_add_volume(codec, "Capture", "", 0, spec->adc_nids[0],
+				 HDA_INPUT);
+	if (err < 0)
+		return err;
+	prev_label = NULL;
+	cidx = 0;
+	for (i = 0; i < cfg->num_inputs; i++) {
+		hda_nid_t nid = cfg->inputs[i].pin;
+		const char *label;
+		if (!(get_wcaps(codec, nid) & AC_WCAP_IN_AMP))
+			continue;
+		label = hda_get_autocfg_input_label(codec, cfg, i);
+		if (label == prev_label)
+			cidx++;
+		else
+			cidx = 0;
+		prev_label = label;
+		err = cx_auto_add_volume(codec, label, " Capture", cidx,
+					 nid, HDA_INPUT);
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+static int cx_auto_build_controls(struct hda_codec *codec)
+{
+	int err;
+
+	err = cx_auto_build_output_controls(codec);
+	if (err < 0)
+		return err;
+	err = cx_auto_build_input_controls(codec);
+	if (err < 0)
+		return err;
+	return conexant_build_controls(codec);
+}
+
+static struct hda_codec_ops cx_auto_patch_ops = {
+	.build_controls = cx_auto_build_controls,
+	.build_pcms = conexant_build_pcms,
+	.init = cx_auto_init,
+	.free = conexant_free,
+	.unsol_event = cx_auto_unsol_event,
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+	.suspend = conexant_suspend,
+#endif
+	.reboot_notify = snd_hda_shutup_pins,
+};
+
+static int patch_conexant_auto(struct hda_codec *codec)
+{
+	struct conexant_spec *spec;
+	int err;
+
+	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (!spec)
+		return -ENOMEM;
+	codec->spec = spec;
+	spec->adc_nids = cx_auto_adc_nids;
+	spec->num_adc_nids = ARRAY_SIZE(cx_auto_adc_nids);
+	spec->capsrc_nids = spec->adc_nids;
+	err = cx_auto_parse_auto_config(codec);
+	if (err < 0) {
+		kfree(codec->spec);
+		codec->spec = NULL;
+		return err;
+	}
+	codec->patch_ops = cx_auto_patch_ops;
+	if (spec->beep_amp)
+		snd_hda_attach_beep_device(codec, spec->beep_amp);
+	return 0;
+}
+
 /*
  */
 
@@ -3271,6 +3880,22 @@ static struct hda_codec_preset snd_hda_preset_conexant[] = {
 	  .patch = patch_cxt5066 },
 	{ .id = 0x14f15069, .name = "CX20585",
 	  .patch = patch_cxt5066 },
+	{ .id = 0x14f15097, .name = "CX20631",
+	  .patch = patch_conexant_auto },
+	{ .id = 0x14f15098, .name = "CX20632",
+	  .patch = patch_conexant_auto },
+	{ .id = 0x14f150a1, .name = "CX20641",
+	  .patch = patch_conexant_auto },
+	{ .id = 0x14f150a2, .name = "CX20642",
+	  .patch = patch_conexant_auto },
+	{ .id = 0x14f150ab, .name = "CX20651",
+	  .patch = patch_conexant_auto },
+	{ .id = 0x14f150ac, .name = "CX20652",
+	  .patch = patch_conexant_auto },
+	{ .id = 0x14f150b8, .name = "CX20664",
+	  .patch = patch_conexant_auto },
+	{ .id = 0x14f150b9, .name = "CX20665",
+	  .patch = patch_conexant_auto },
 	{} /* terminator */
 };
 
@@ -3281,6 +3906,14 @@ MODULE_ALIAS("snd-hda-codec-id:14f15066");
 MODULE_ALIAS("snd-hda-codec-id:14f15067");
 MODULE_ALIAS("snd-hda-codec-id:14f15068");
 MODULE_ALIAS("snd-hda-codec-id:14f15069");
+MODULE_ALIAS("snd-hda-codec-id:14f15097");
+MODULE_ALIAS("snd-hda-codec-id:14f15098");
+MODULE_ALIAS("snd-hda-codec-id:14f150a1");
+MODULE_ALIAS("snd-hda-codec-id:14f150a2");
+MODULE_ALIAS("snd-hda-codec-id:14f150ab");
+MODULE_ALIAS("snd-hda-codec-id:14f150ac");
+MODULE_ALIAS("snd-hda-codec-id:14f150b8");
+MODULE_ALIAS("snd-hda-codec-id:14f150b9");
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Conexant HD-audio codec");

+ 756 - 41
sound/pci/hda/patch_hdmi.c

@@ -3,6 +3,9 @@
  *  patch_hdmi.c - routines for HDMI/DisplayPort codecs
  *
  *  Copyright(c) 2008-2010 Intel Corporation. All rights reserved.
+ *  Copyright (c) 2006 ATI Technologies Inc.
+ *  Copyright (c) 2008 NVIDIA Corp.  All rights reserved.
+ *  Copyright (c) 2008 Wei Ni <wni@nvidia.com>
  *
  *  Authors:
  *			Wu Fengguang <wfg@linux.intel.com>
@@ -25,6 +28,22 @@
  *  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  */
 
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+/*
+ * The HDMI/DisplayPort configuration can be highly dynamic. A graphics device
+ * could support two independent pipes, each of them can be connected to one or
+ * more ports (DVI, HDMI or DisplayPort).
+ *
+ * The HDA correspondence of pipes/ports are converter/pin nodes.
+ */
+#define MAX_HDMI_CVTS	3
+#define MAX_HDMI_PINS	3
 
 struct hdmi_spec {
 	int num_cvts;
@@ -49,10 +68,10 @@ struct hdmi_spec {
 	struct hda_pcm_stream codec_pcm_pars[MAX_HDMI_CVTS];
 
 	/*
-	 * nvhdmi specific
+	 * ati/nvhdmi specific
 	 */
 	struct hda_multi_out multiout;
-	unsigned int codec_type;
+	struct hda_pcm_stream *pcm_playback;
 
 	/* misc flags */
 	/* PD bit indicates only the update, not the current state */
@@ -65,13 +84,25 @@ struct hdmi_audio_infoframe {
 	u8 ver;  /* 0x01 */
 	u8 len;  /* 0x0a */
 
-	u8 checksum;	/* PB0 */
+	u8 checksum;
+
 	u8 CC02_CT47;	/* CC in bits 0:2, CT in 4:7 */
 	u8 SS01_SF24;
 	u8 CXT04;
 	u8 CA;
 	u8 LFEPBL01_LSV36_DM_INH7;
-	u8 reserved[5];	/* PB6 - PB10 */
+};
+
+struct dp_audio_infoframe {
+	u8 type; /* 0x84 */
+	u8 len;  /* 0x1b */
+	u8 ver;  /* 0x11 << 2 */
+
+	u8 CC02_CT47;	/* match with HDMI infoframe from this on */
+	u8 SS01_SF24;
+	u8 CXT04;
+	u8 CA;
+	u8 LFEPBL01_LSV36_DM_INH7;
 };
 
 /*
@@ -162,7 +193,7 @@ static int hdmi_channel_mapping[0x32][8] = {
 	/* 4ch */
 	[0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 },
 	/* surround41 */
-	[0x09] = { 0x00, 0x11, 0x24, 0x34, 0x43, 0xf2, 0xf6, 0xf7 },
+	[0x09] = { 0x00, 0x11, 0x24, 0x35, 0x42, 0xf3, 0xf6, 0xf7 },
 	/* surround50 */
 	[0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 },
 	/* surround51 */
@@ -175,7 +206,7 @@ static int hdmi_channel_mapping[0x32][8] = {
  * This is an ordered list!
  *
  * The preceding ones have better chances to be selected by
- * hdmi_setup_channel_allocation().
+ * hdmi_channel_allocation().
  */
 static struct cea_channel_speaker_allocation channel_allocations[] = {
 /*			  channel:   7     6    5    4    3     2    1    0  */
@@ -352,14 +383,14 @@ static void init_channel_allocations(void)
  *
  * TODO: it could select the wrong CA from multiple candidates.
 */
-static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
-					 struct hdmi_audio_infoframe *ai)
+static int hdmi_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
+				   int channels)
 {
 	struct hdmi_spec *spec = codec->spec;
 	struct hdmi_eld *eld;
 	int i;
+	int ca = 0;
 	int spk_mask = 0;
-	int channels = 1 + (ai->CC02_CT47 & 0x7);
 	char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
 
 	/*
@@ -397,16 +428,16 @@ static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
 		if (channels == channel_allocations[i].channels &&
 		    (spk_mask & channel_allocations[i].spk_mask) ==
 				channel_allocations[i].spk_mask) {
-			ai->CA = channel_allocations[i].ca_index;
+			ca = channel_allocations[i].ca_index;
 			break;
 		}
 	}
 
 	snd_print_channel_allocation(eld->spk_alloc, buf, sizeof(buf));
 	snd_printdd("HDMI: select CA 0x%x for %d-channel allocation: %s\n",
-		    ai->CA, channels, buf);
+		    ca, channels, buf);
 
-	return ai->CA;
+	return ca;
 }
 
 static void hdmi_debug_channel_mapping(struct hda_codec *codec,
@@ -428,10 +459,9 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec,
 
 static void hdmi_setup_channel_mapping(struct hda_codec *codec,
 				       hda_nid_t pin_nid,
-				       struct hdmi_audio_infoframe *ai)
+				       int ca)
 {
 	int i;
-	int ca = ai->CA;
 	int err;
 
 	if (hdmi_channel_mapping[ca][1] == 0) {
@@ -528,41 +558,37 @@ static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid)
 #endif
 }
 
-static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *ai)
+static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *hdmi_ai)
 {
-	u8 *bytes = (u8 *)ai;
+	u8 *bytes = (u8 *)hdmi_ai;
 	u8 sum = 0;
 	int i;
 
-	ai->checksum = 0;
+	hdmi_ai->checksum = 0;
 
-	for (i = 0; i < sizeof(*ai); i++)
+	for (i = 0; i < sizeof(*hdmi_ai); i++)
 		sum += bytes[i];
 
-	ai->checksum = -sum;
+	hdmi_ai->checksum = -sum;
 }
 
 static void hdmi_fill_audio_infoframe(struct hda_codec *codec,
 				      hda_nid_t pin_nid,
-				      struct hdmi_audio_infoframe *ai)
+				      u8 *dip, int size)
 {
-	u8 *bytes = (u8 *)ai;
 	int i;
 
 	hdmi_debug_dip_size(codec, pin_nid);
 	hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */
 
-	hdmi_checksum_audio_infoframe(ai);
-
 	hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
-	for (i = 0; i < sizeof(*ai); i++)
-		hdmi_write_dip_byte(codec, pin_nid, bytes[i]);
+	for (i = 0; i < size; i++)
+		hdmi_write_dip_byte(codec, pin_nid, dip[i]);
 }
 
 static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
-				    struct hdmi_audio_infoframe *ai)
+				    u8 *dip, int size)
 {
-	u8 *bytes = (u8 *)ai;
 	u8 val;
 	int i;
 
@@ -571,10 +597,10 @@ static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
 		return false;
 
 	hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
-	for (i = 0; i < sizeof(*ai); i++) {
+	for (i = 0; i < size; i++) {
 		val = snd_hda_codec_read(codec, pin_nid, 0,
 					 AC_VERB_GET_HDMI_DIP_DATA, 0);
-		if (val != bytes[i])
+		if (val != dip[i])
 			return false;
 	}
 
@@ -586,15 +612,13 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid,
 {
 	struct hdmi_spec *spec = codec->spec;
 	hda_nid_t pin_nid;
+	int channels = substream->runtime->channels;
+	int ca;
 	int i;
-	struct hdmi_audio_infoframe ai = {
-		.type		= 0x84,
-		.ver		= 0x01,
-		.len		= 0x0a,
-		.CC02_CT47	= substream->runtime->channels - 1,
-	};
+	u8 ai[max(sizeof(struct hdmi_audio_infoframe),
+		  sizeof(struct dp_audio_infoframe))];
 
-	hdmi_setup_channel_allocation(codec, nid, &ai);
+	ca = hdmi_channel_allocation(codec, nid, channels);
 
 	for (i = 0; i < spec->num_pins; i++) {
 		if (spec->pin_cvt[i] != nid)
@@ -603,14 +627,45 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid,
 			continue;
 
 		pin_nid = spec->pin[i];
-		if (!hdmi_infoframe_uptodate(codec, pin_nid, &ai)) {
+
+		memset(ai, 0, sizeof(ai));
+		if (spec->sink_eld[i].conn_type == 0) { /* HDMI */
+			struct hdmi_audio_infoframe *hdmi_ai;
+
+			hdmi_ai = (struct hdmi_audio_infoframe *)ai;
+			hdmi_ai->type		= 0x84;
+			hdmi_ai->ver		= 0x01;
+			hdmi_ai->len		= 0x0a;
+			hdmi_ai->CC02_CT47	= channels - 1;
+			hdmi_checksum_audio_infoframe(hdmi_ai);
+		} else if (spec->sink_eld[i].conn_type == 1) { /* DisplayPort */
+			struct dp_audio_infoframe *dp_ai;
+
+			dp_ai = (struct dp_audio_infoframe *)ai;
+			dp_ai->type		= 0x84;
+			dp_ai->len		= 0x1b;
+			dp_ai->ver		= 0x11 << 2;
+			dp_ai->CC02_CT47	= channels - 1;
+		} else {
+			snd_printd("HDMI: unknown connection type at pin %d\n",
+				   pin_nid);
+			continue;
+		}
+
+		/*
+		 * sizeof(ai) is used instead of sizeof(*hdmi_ai) or
+		 * sizeof(*dp_ai) to avoid partial match/update problems when
+		 * the user switches between HDMI/DP monitors.
+		 */
+		if (!hdmi_infoframe_uptodate(codec, pin_nid, ai, sizeof(ai))) {
 			snd_printdd("hdmi_setup_audio_infoframe: "
 				    "cvt=%d pin=%d channels=%d\n",
 				    nid, pin_nid,
-				    substream->runtime->channels);
-			hdmi_setup_channel_mapping(codec, pin_nid, &ai);
+				    channels);
+			hdmi_setup_channel_mapping(codec, pin_nid, ca);
 			hdmi_stop_infoframe_trans(codec, pin_nid);
-			hdmi_fill_audio_infoframe(codec, pin_nid, &ai);
+			hdmi_fill_audio_infoframe(codec, pin_nid,
+						  ai, sizeof(ai));
 			hdmi_start_infoframe_trans(codec, pin_nid);
 		}
 	}
@@ -791,7 +846,6 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
 /*
  * HDA/HDMI auto parsing
  */
-
 static int hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid)
 {
 	struct hdmi_spec *spec = codec->spec;
@@ -922,3 +976,664 @@ static int hdmi_parse_codec(struct hda_codec *codec)
 	return 0;
 }
 
+/*
+ */
+static char *generic_hdmi_pcm_names[MAX_HDMI_CVTS] = {
+	"HDMI 0",
+	"HDMI 1",
+	"HDMI 2",
+};
+
+/*
+ * HDMI callbacks
+ */
+
+static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+					   struct hda_codec *codec,
+					   unsigned int stream_tag,
+					   unsigned int format,
+					   struct snd_pcm_substream *substream)
+{
+	hdmi_set_channel_count(codec, hinfo->nid,
+			       substream->runtime->channels);
+
+	hdmi_setup_audio_infoframe(codec, hinfo->nid, substream);
+
+	return hdmi_setup_stream(codec, hinfo->nid, stream_tag, format);
+}
+
+static struct hda_pcm_stream generic_hdmi_pcm_playback = {
+	.substreams = 1,
+	.channels_min = 2,
+	.ops = {
+		.open = hdmi_pcm_open,
+		.prepare = generic_hdmi_playback_pcm_prepare,
+	},
+};
+
+static int generic_hdmi_build_pcms(struct hda_codec *codec)
+{
+	struct hdmi_spec *spec = codec->spec;
+	struct hda_pcm *info = spec->pcm_rec;
+	int i;
+
+	codec->num_pcms = spec->num_cvts;
+	codec->pcm_info = info;
+
+	for (i = 0; i < codec->num_pcms; i++, info++) {
+		unsigned int chans;
+		struct hda_pcm_stream *pstr;
+
+		chans = get_wcaps(codec, spec->cvt[i]);
+		chans = get_wcaps_channels(chans);
+
+		info->name = generic_hdmi_pcm_names[i];
+		info->pcm_type = HDA_PCM_TYPE_HDMI;
+		pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
+		if (spec->pcm_playback)
+			*pstr = *spec->pcm_playback;
+		else
+			*pstr = generic_hdmi_pcm_playback;
+		pstr->nid = spec->cvt[i];
+		if (pstr->channels_max <= 2 && chans && chans <= 16)
+			pstr->channels_max = chans;
+	}
+
+	return 0;
+}
+
+static int generic_hdmi_build_controls(struct hda_codec *codec)
+{
+	struct hdmi_spec *spec = codec->spec;
+	int err;
+	int i;
+
+	for (i = 0; i < codec->num_pcms; i++) {
+		err = snd_hda_create_spdif_out_ctls(codec, spec->cvt[i]);
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+static int generic_hdmi_init(struct hda_codec *codec)
+{
+	struct hdmi_spec *spec = codec->spec;
+	int i;
+
+	for (i = 0; spec->pin[i]; i++) {
+		hdmi_enable_output(codec, spec->pin[i]);
+		snd_hda_codec_write(codec, spec->pin[i], 0,
+				    AC_VERB_SET_UNSOLICITED_ENABLE,
+				    AC_USRSP_EN | spec->pin[i]);
+	}
+	return 0;
+}
+
+static void generic_hdmi_free(struct hda_codec *codec)
+{
+	struct hdmi_spec *spec = codec->spec;
+	int i;
+
+	for (i = 0; i < spec->num_pins; i++)
+		snd_hda_eld_proc_free(codec, &spec->sink_eld[i]);
+
+	kfree(spec);
+}
+
+static struct hda_codec_ops generic_hdmi_patch_ops = {
+	.init			= generic_hdmi_init,
+	.free			= generic_hdmi_free,
+	.build_pcms		= generic_hdmi_build_pcms,
+	.build_controls		= generic_hdmi_build_controls,
+	.unsol_event		= hdmi_unsol_event,
+};
+
+static int patch_generic_hdmi(struct hda_codec *codec)
+{
+	struct hdmi_spec *spec;
+	int i;
+
+	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	codec->spec = spec;
+	if (hdmi_parse_codec(codec) < 0) {
+		codec->spec = NULL;
+		kfree(spec);
+		return -EINVAL;
+	}
+	codec->patch_ops = generic_hdmi_patch_ops;
+
+	for (i = 0; i < spec->num_pins; i++)
+		snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i);
+
+	init_channel_allocations();
+
+	return 0;
+}
+
+/*
+ * Nvidia specific implementations
+ */
+
+#define Nv_VERB_SET_Channel_Allocation          0xF79
+#define Nv_VERB_SET_Info_Frame_Checksum         0xF7A
+#define Nv_VERB_SET_Audio_Protection_On         0xF98
+#define Nv_VERB_SET_Audio_Protection_Off        0xF99
+
+#define nvhdmi_master_con_nid_7x	0x04
+#define nvhdmi_master_pin_nid_7x	0x05
+
+static hda_nid_t nvhdmi_con_nids_7x[4] = {
+	/*front, rear, clfe, rear_surr */
+	0x6, 0x8, 0xa, 0xc,
+};
+
+static struct hda_verb nvhdmi_basic_init_7x[] = {
+	/* set audio protect on */
+	{ 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
+	/* enable digital output on pin widget */
+	{ 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+	{ 0x7, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+	{ 0x9, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+	{ 0xb, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+	{ 0xd, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+	{} /* terminator */
+};
+
+#ifdef LIMITED_RATE_FMT_SUPPORT
+/* support only the safe format and rate */
+#define SUPPORTED_RATES		SNDRV_PCM_RATE_48000
+#define SUPPORTED_MAXBPS	16
+#define SUPPORTED_FORMATS	SNDRV_PCM_FMTBIT_S16_LE
+#else
+/* support all rates and formats */
+#define SUPPORTED_RATES \
+	(SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
+	SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\
+	 SNDRV_PCM_RATE_192000)
+#define SUPPORTED_MAXBPS	24
+#define SUPPORTED_FORMATS \
+	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
+#endif
+
+static int nvhdmi_7x_init(struct hda_codec *codec)
+{
+	snd_hda_sequence_write(codec, nvhdmi_basic_init_7x);
+	return 0;
+}
+
+static int simple_playback_pcm_open(struct hda_pcm_stream *hinfo,
+				    struct hda_codec *codec,
+				    struct snd_pcm_substream *substream)
+{
+	struct hdmi_spec *spec = codec->spec;
+	return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int simple_playback_pcm_close(struct hda_pcm_stream *hinfo,
+				     struct hda_codec *codec,
+				     struct snd_pcm_substream *substream)
+{
+	struct hdmi_spec *spec = codec->spec;
+	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+static int simple_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+				       struct hda_codec *codec,
+				       unsigned int stream_tag,
+				       unsigned int format,
+				       struct snd_pcm_substream *substream)
+{
+	struct hdmi_spec *spec = codec->spec;
+	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
+					     stream_tag, format, substream);
+}
+
+static int nvhdmi_8ch_7x_pcm_close(struct hda_pcm_stream *hinfo,
+				   struct hda_codec *codec,
+				   struct snd_pcm_substream *substream)
+{
+	struct hdmi_spec *spec = codec->spec;
+	int i;
+
+	snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x,
+			0, AC_VERB_SET_CHANNEL_STREAMID, 0);
+	for (i = 0; i < 4; i++) {
+		/* set the stream id */
+		snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
+				AC_VERB_SET_CHANNEL_STREAMID, 0);
+		/* set the stream format */
+		snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
+				AC_VERB_SET_STREAM_FORMAT, 0);
+	}
+
+	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo,
+				     struct hda_codec *codec,
+				     unsigned int stream_tag,
+				     unsigned int format,
+				     struct snd_pcm_substream *substream)
+{
+	int chs;
+	unsigned int dataDCC1, dataDCC2, chan, chanmask, channel_id;
+	int i;
+
+	mutex_lock(&codec->spdif_mutex);
+
+	chs = substream->runtime->channels;
+	chan = chs ? (chs - 1) : 1;
+
+	switch (chs) {
+	default:
+	case 0:
+	case 2:
+		chanmask = 0x00;
+		break;
+	case 4:
+		chanmask = 0x08;
+		break;
+	case 6:
+		chanmask = 0x0b;
+		break;
+	case 8:
+		chanmask = 0x13;
+		break;
+	}
+	dataDCC1 = AC_DIG1_ENABLE | AC_DIG1_COPYRIGHT;
+	dataDCC2 = 0x2;
+
+	/* set the Audio InforFrame Channel Allocation */
+	snd_hda_codec_write(codec, 0x1, 0,
+			Nv_VERB_SET_Channel_Allocation, chanmask);
+
+	/* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
+	if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
+		snd_hda_codec_write(codec,
+				nvhdmi_master_con_nid_7x,
+				0,
+				AC_VERB_SET_DIGI_CONVERT_1,
+				codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
+
+	/* set the stream id */
+	snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
+			AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | 0x0);
+
+	/* set the stream format */
+	snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
+			AC_VERB_SET_STREAM_FORMAT, format);
+
+	/* turn on again (if needed) */
+	/* enable and set the channel status audio/data flag */
+	if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) {
+		snd_hda_codec_write(codec,
+				nvhdmi_master_con_nid_7x,
+				0,
+				AC_VERB_SET_DIGI_CONVERT_1,
+				codec->spdif_ctls & 0xff);
+		snd_hda_codec_write(codec,
+				nvhdmi_master_con_nid_7x,
+				0,
+				AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
+	}
+
+	for (i = 0; i < 4; i++) {
+		if (chs == 2)
+			channel_id = 0;
+		else
+			channel_id = i * 2;
+
+		/* turn off SPDIF once;
+		 *otherwise the IEC958 bits won't be updated
+		 */
+		if (codec->spdif_status_reset &&
+		(codec->spdif_ctls & AC_DIG1_ENABLE))
+			snd_hda_codec_write(codec,
+				nvhdmi_con_nids_7x[i],
+				0,
+				AC_VERB_SET_DIGI_CONVERT_1,
+				codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
+		/* set the stream id */
+		snd_hda_codec_write(codec,
+				nvhdmi_con_nids_7x[i],
+				0,
+				AC_VERB_SET_CHANNEL_STREAMID,
+				(stream_tag << 4) | channel_id);
+		/* set the stream format */
+		snd_hda_codec_write(codec,
+				nvhdmi_con_nids_7x[i],
+				0,
+				AC_VERB_SET_STREAM_FORMAT,
+				format);
+		/* turn on again (if needed) */
+		/* enable and set the channel status audio/data flag */
+		if (codec->spdif_status_reset &&
+		(codec->spdif_ctls & AC_DIG1_ENABLE)) {
+			snd_hda_codec_write(codec,
+					nvhdmi_con_nids_7x[i],
+					0,
+					AC_VERB_SET_DIGI_CONVERT_1,
+					codec->spdif_ctls & 0xff);
+			snd_hda_codec_write(codec,
+					nvhdmi_con_nids_7x[i],
+					0,
+					AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
+		}
+	}
+
+	/* set the Audio Info Frame Checksum */
+	snd_hda_codec_write(codec, 0x1, 0,
+			Nv_VERB_SET_Info_Frame_Checksum,
+			(0x71 - chan - chanmask));
+
+	mutex_unlock(&codec->spdif_mutex);
+	return 0;
+}
+
+static struct hda_pcm_stream nvhdmi_pcm_playback_8ch_7x = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 8,
+	.nid = nvhdmi_master_con_nid_7x,
+	.rates = SUPPORTED_RATES,
+	.maxbps = SUPPORTED_MAXBPS,
+	.formats = SUPPORTED_FORMATS,
+	.ops = {
+		.open = simple_playback_pcm_open,
+		.close = nvhdmi_8ch_7x_pcm_close,
+		.prepare = nvhdmi_8ch_7x_pcm_prepare
+	},
+};
+
+static struct hda_pcm_stream nvhdmi_pcm_playback_2ch = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 2,
+	.nid = nvhdmi_master_con_nid_7x,
+	.rates = SUPPORTED_RATES,
+	.maxbps = SUPPORTED_MAXBPS,
+	.formats = SUPPORTED_FORMATS,
+	.ops = {
+		.open = simple_playback_pcm_open,
+		.close = simple_playback_pcm_close,
+		.prepare = simple_playback_pcm_prepare
+	},
+};
+
+static struct hda_codec_ops nvhdmi_patch_ops_8ch_7x = {
+	.build_controls = generic_hdmi_build_controls,
+	.build_pcms = generic_hdmi_build_pcms,
+	.init = nvhdmi_7x_init,
+	.free = generic_hdmi_free,
+};
+
+static struct hda_codec_ops nvhdmi_patch_ops_2ch = {
+	.build_controls = generic_hdmi_build_controls,
+	.build_pcms = generic_hdmi_build_pcms,
+	.init = nvhdmi_7x_init,
+	.free = generic_hdmi_free,
+};
+
+static int patch_nvhdmi_8ch_89(struct hda_codec *codec)
+{
+	struct hdmi_spec *spec;
+	int err = patch_generic_hdmi(codec);
+
+	if (err < 0)
+		return err;
+	spec = codec->spec;
+	spec->old_pin_detect = 1;
+	return 0;
+}
+
+static int patch_nvhdmi_2ch(struct hda_codec *codec)
+{
+	struct hdmi_spec *spec;
+
+	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	codec->spec = spec;
+
+	spec->multiout.num_dacs = 0;  /* no analog */
+	spec->multiout.max_channels = 2;
+	spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x;
+	spec->old_pin_detect = 1;
+	spec->num_cvts = 1;
+	spec->cvt[0] = nvhdmi_master_con_nid_7x;
+	spec->pcm_playback = &nvhdmi_pcm_playback_2ch;
+
+	codec->patch_ops = nvhdmi_patch_ops_2ch;
+
+	return 0;
+}
+
+static int patch_nvhdmi_8ch_7x(struct hda_codec *codec)
+{
+	struct hdmi_spec *spec;
+	int err = patch_nvhdmi_2ch(codec);
+
+	if (err < 0)
+		return err;
+	spec = codec->spec;
+	spec->multiout.max_channels = 8;
+	spec->pcm_playback = &nvhdmi_pcm_playback_8ch_7x;
+	codec->patch_ops = nvhdmi_patch_ops_8ch_7x;
+	return 0;
+}
+
+/*
+ * ATI-specific implementations
+ *
+ * FIXME: we may omit the whole this and use the generic code once after
+ * it's confirmed to work.
+ */
+
+#define ATIHDMI_CVT_NID		0x02	/* audio converter */
+#define ATIHDMI_PIN_NID		0x03	/* HDMI output pin */
+
+static int atihdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+					struct hda_codec *codec,
+					unsigned int stream_tag,
+					unsigned int format,
+					struct snd_pcm_substream *substream)
+{
+	struct hdmi_spec *spec = codec->spec;
+	int chans = substream->runtime->channels;
+	int i, err;
+
+	err = simple_playback_pcm_prepare(hinfo, codec, stream_tag, format,
+					  substream);
+	if (err < 0)
+		return err;
+	snd_hda_codec_write(codec, spec->cvt[0], 0, AC_VERB_SET_CVT_CHAN_COUNT,
+			    chans - 1);
+	/* FIXME: XXX */
+	for (i = 0; i < chans; i++) {
+		snd_hda_codec_write(codec, spec->cvt[0], 0,
+				    AC_VERB_SET_HDMI_CHAN_SLOT,
+				    (i << 4) | i);
+	}
+	return 0;
+}
+
+static struct hda_pcm_stream atihdmi_pcm_digital_playback = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 2,
+	.nid = ATIHDMI_CVT_NID,
+	.ops = {
+		.open = simple_playback_pcm_open,
+		.close = simple_playback_pcm_close,
+		.prepare = atihdmi_playback_pcm_prepare
+	},
+};
+
+static struct hda_verb atihdmi_basic_init[] = {
+	/* enable digital output on pin widget */
+	{ 0x03, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{} /* terminator */
+};
+
+static int atihdmi_init(struct hda_codec *codec)
+{
+	struct hdmi_spec *spec = codec->spec;
+
+	snd_hda_sequence_write(codec, atihdmi_basic_init);
+	/* SI codec requires to unmute the pin */
+	if (get_wcaps(codec, spec->pin[0]) & AC_WCAP_OUT_AMP)
+		snd_hda_codec_write(codec, spec->pin[0], 0,
+				    AC_VERB_SET_AMP_GAIN_MUTE,
+				    AMP_OUT_UNMUTE);
+	return 0;
+}
+
+static struct hda_codec_ops atihdmi_patch_ops = {
+	.build_controls = generic_hdmi_build_controls,
+	.build_pcms = generic_hdmi_build_pcms,
+	.init = atihdmi_init,
+	.free = generic_hdmi_free,
+};
+
+
+static int patch_atihdmi(struct hda_codec *codec)
+{
+	struct hdmi_spec *spec;
+
+	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	codec->spec = spec;
+
+	spec->multiout.num_dacs = 0;	  /* no analog */
+	spec->multiout.max_channels = 2;
+	spec->multiout.dig_out_nid = ATIHDMI_CVT_NID;
+	spec->num_cvts = 1;
+	spec->cvt[0] = ATIHDMI_CVT_NID;
+	spec->pin[0] = ATIHDMI_PIN_NID;
+	spec->pcm_playback = &atihdmi_pcm_digital_playback;
+
+	codec->patch_ops = atihdmi_patch_ops;
+
+	return 0;
+}
+
+
+/*
+ * patch entries
+ */
+static struct hda_codec_preset snd_hda_preset_hdmi[] = {
+{ .id = 0x1002793c, .name = "RS600 HDMI",	.patch = patch_atihdmi },
+{ .id = 0x10027919, .name = "RS600 HDMI",	.patch = patch_atihdmi },
+{ .id = 0x1002791a, .name = "RS690/780 HDMI",	.patch = patch_atihdmi },
+{ .id = 0x1002aa01, .name = "R6xx HDMI",	.patch = patch_atihdmi },
+{ .id = 0x10951390, .name = "SiI1390 HDMI",	.patch = patch_generic_hdmi },
+{ .id = 0x10951392, .name = "SiI1392 HDMI",	.patch = patch_generic_hdmi },
+{ .id = 0x17e80047, .name = "Chrontel HDMI",	.patch = patch_generic_hdmi },
+{ .id = 0x10de0002, .name = "MCP77/78 HDMI",	.patch = patch_nvhdmi_8ch_7x },
+{ .id = 0x10de0003, .name = "MCP77/78 HDMI",	.patch = patch_nvhdmi_8ch_7x },
+{ .id = 0x10de0005, .name = "MCP77/78 HDMI",	.patch = patch_nvhdmi_8ch_7x },
+{ .id = 0x10de0006, .name = "MCP77/78 HDMI",	.patch = patch_nvhdmi_8ch_7x },
+{ .id = 0x10de0007, .name = "MCP79/7A HDMI",	.patch = patch_nvhdmi_8ch_7x },
+{ .id = 0x10de000a, .name = "GPU 0a HDMI/DP",	.patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de000b, .name = "GPU 0b HDMI/DP",	.patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de000c, .name = "MCP89 HDMI",	.patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de000d, .name = "GPU 0d HDMI/DP",	.patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0010, .name = "GPU 10 HDMI/DP",	.patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0011, .name = "GPU 11 HDMI/DP",	.patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0012, .name = "GPU 12 HDMI/DP",	.patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0013, .name = "GPU 13 HDMI/DP",	.patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0014, .name = "GPU 14 HDMI/DP",	.patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0018, .name = "GPU 18 HDMI/DP",	.patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0019, .name = "GPU 19 HDMI/DP",	.patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de001a, .name = "GPU 1a HDMI/DP",	.patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de001b, .name = "GPU 1b HDMI/DP",	.patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de001c, .name = "GPU 1c HDMI/DP",	.patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0040, .name = "GPU 40 HDMI/DP",	.patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0041, .name = "GPU 41 HDMI/DP",	.patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0042, .name = "GPU 42 HDMI/DP",	.patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0043, .name = "GPU 43 HDMI/DP",	.patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0044, .name = "GPU 44 HDMI/DP",	.patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0067, .name = "MCP67 HDMI",	.patch = patch_nvhdmi_2ch },
+{ .id = 0x10de8001, .name = "MCP73 HDMI",	.patch = patch_nvhdmi_2ch },
+{ .id = 0x80860054, .name = "IbexPeak HDMI",	.patch = patch_generic_hdmi },
+{ .id = 0x80862801, .name = "Bearlake HDMI",	.patch = patch_generic_hdmi },
+{ .id = 0x80862802, .name = "Cantiga HDMI",	.patch = patch_generic_hdmi },
+{ .id = 0x80862803, .name = "Eaglelake HDMI",	.patch = patch_generic_hdmi },
+{ .id = 0x80862804, .name = "IbexPeak HDMI",	.patch = patch_generic_hdmi },
+{ .id = 0x80862805, .name = "CougarPoint HDMI",	.patch = patch_generic_hdmi },
+{ .id = 0x808629fb, .name = "Crestline HDMI",	.patch = patch_generic_hdmi },
+{} /* terminator */
+};
+
+MODULE_ALIAS("snd-hda-codec-id:1002793c");
+MODULE_ALIAS("snd-hda-codec-id:10027919");
+MODULE_ALIAS("snd-hda-codec-id:1002791a");
+MODULE_ALIAS("snd-hda-codec-id:1002aa01");
+MODULE_ALIAS("snd-hda-codec-id:10951390");
+MODULE_ALIAS("snd-hda-codec-id:10951392");
+MODULE_ALIAS("snd-hda-codec-id:10de0002");
+MODULE_ALIAS("snd-hda-codec-id:10de0003");
+MODULE_ALIAS("snd-hda-codec-id:10de0005");
+MODULE_ALIAS("snd-hda-codec-id:10de0006");
+MODULE_ALIAS("snd-hda-codec-id:10de0007");
+MODULE_ALIAS("snd-hda-codec-id:10de000a");
+MODULE_ALIAS("snd-hda-codec-id:10de000b");
+MODULE_ALIAS("snd-hda-codec-id:10de000c");
+MODULE_ALIAS("snd-hda-codec-id:10de000d");
+MODULE_ALIAS("snd-hda-codec-id:10de0010");
+MODULE_ALIAS("snd-hda-codec-id:10de0011");
+MODULE_ALIAS("snd-hda-codec-id:10de0012");
+MODULE_ALIAS("snd-hda-codec-id:10de0013");
+MODULE_ALIAS("snd-hda-codec-id:10de0014");
+MODULE_ALIAS("snd-hda-codec-id:10de0018");
+MODULE_ALIAS("snd-hda-codec-id:10de0019");
+MODULE_ALIAS("snd-hda-codec-id:10de001a");
+MODULE_ALIAS("snd-hda-codec-id:10de001b");
+MODULE_ALIAS("snd-hda-codec-id:10de001c");
+MODULE_ALIAS("snd-hda-codec-id:10de0040");
+MODULE_ALIAS("snd-hda-codec-id:10de0041");
+MODULE_ALIAS("snd-hda-codec-id:10de0042");
+MODULE_ALIAS("snd-hda-codec-id:10de0043");
+MODULE_ALIAS("snd-hda-codec-id:10de0044");
+MODULE_ALIAS("snd-hda-codec-id:10de0067");
+MODULE_ALIAS("snd-hda-codec-id:10de8001");
+MODULE_ALIAS("snd-hda-codec-id:17e80047");
+MODULE_ALIAS("snd-hda-codec-id:80860054");
+MODULE_ALIAS("snd-hda-codec-id:80862801");
+MODULE_ALIAS("snd-hda-codec-id:80862802");
+MODULE_ALIAS("snd-hda-codec-id:80862803");
+MODULE_ALIAS("snd-hda-codec-id:80862804");
+MODULE_ALIAS("snd-hda-codec-id:80862805");
+MODULE_ALIAS("snd-hda-codec-id:808629fb");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("HDMI HD-audio codec");
+MODULE_ALIAS("snd-hda-codec-intelhdmi");
+MODULE_ALIAS("snd-hda-codec-nvhdmi");
+MODULE_ALIAS("snd-hda-codec-atihdmi");
+
+static struct hda_codec_preset_list intel_list = {
+	.preset = snd_hda_preset_hdmi,
+	.owner = THIS_MODULE,
+};
+
+static int __init patch_hdmi_init(void)
+{
+	return snd_hda_add_codec_preset(&intel_list);
+}
+
+static void __exit patch_hdmi_exit(void)
+{
+	snd_hda_delete_codec_preset(&intel_list);
+}
+
+module_init(patch_hdmi_init)
+module_exit(patch_hdmi_exit)

+ 0 - 220
sound/pci/hda/patch_intelhdmi.c

@@ -1,220 +0,0 @@
-/*
- *
- *  patch_intelhdmi.c - Patch for Intel HDMI codecs
- *
- *  Copyright(c) 2008 Intel Corporation. All rights reserved.
- *
- *  Authors:
- *  			Jiang Zhe <zhe.jiang@intel.com>
- *  			Wu Fengguang <wfg@linux.intel.com>
- *
- *  Maintained by:
- *  			Wu Fengguang <wfg@linux.intel.com>
- *
- *  This program is free software; you can redistribute it and/or modify it
- *  under the terms of the GNU General Public License as published by the Free
- *  Software Foundation; either version 2 of the License, or (at your option)
- *  any later version.
- *
- *  This program is distributed in the hope that it will be useful, but
- *  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- *  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- *  for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software Foundation,
- *  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
- */
-
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <sound/core.h>
-#include "hda_codec.h"
-#include "hda_local.h"
-
-/*
- * The HDMI/DisplayPort configuration can be highly dynamic. A graphics device
- * could support two independent pipes, each of them can be connected to one or
- * more ports (DVI, HDMI or DisplayPort).
- *
- * The HDA correspondence of pipes/ports are converter/pin nodes.
- */
-#define MAX_HDMI_CVTS	3
-#define MAX_HDMI_PINS	3
-
-#include "patch_hdmi.c"
-
-static char *intel_hdmi_pcm_names[MAX_HDMI_CVTS] = {
-	"INTEL HDMI 0",
-	"INTEL HDMI 1",
-	"INTEL HDMI 2",
-};
-
-/*
- * HDMI callbacks
- */
-
-static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
-					   struct hda_codec *codec,
-					   unsigned int stream_tag,
-					   unsigned int format,
-					   struct snd_pcm_substream *substream)
-{
-	hdmi_set_channel_count(codec, hinfo->nid,
-			       substream->runtime->channels);
-
-	hdmi_setup_audio_infoframe(codec, hinfo->nid, substream);
-
-	return hdmi_setup_stream(codec, hinfo->nid, stream_tag, format);
-}
-
-static struct hda_pcm_stream intel_hdmi_pcm_playback = {
-	.substreams = 1,
-	.channels_min = 2,
-	.ops = {
-		.open = hdmi_pcm_open,
-		.prepare = intel_hdmi_playback_pcm_prepare,
-	},
-};
-
-static int intel_hdmi_build_pcms(struct hda_codec *codec)
-{
-	struct hdmi_spec *spec = codec->spec;
-	struct hda_pcm *info = spec->pcm_rec;
-	int i;
-
-	codec->num_pcms = spec->num_cvts;
-	codec->pcm_info = info;
-
-	for (i = 0; i < codec->num_pcms; i++, info++) {
-		unsigned int chans;
-
-		chans = get_wcaps(codec, spec->cvt[i]);
-		chans = get_wcaps_channels(chans);
-
-		info->name = intel_hdmi_pcm_names[i];
-		info->pcm_type = HDA_PCM_TYPE_HDMI;
-		info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
-							intel_hdmi_pcm_playback;
-		info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->cvt[i];
-		info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans;
-	}
-
-	return 0;
-}
-
-static int intel_hdmi_build_controls(struct hda_codec *codec)
-{
-	struct hdmi_spec *spec = codec->spec;
-	int err;
-	int i;
-
-	for (i = 0; i < codec->num_pcms; i++) {
-		err = snd_hda_create_spdif_out_ctls(codec, spec->cvt[i]);
-		if (err < 0)
-			return err;
-	}
-
-	return 0;
-}
-
-static int intel_hdmi_init(struct hda_codec *codec)
-{
-	struct hdmi_spec *spec = codec->spec;
-	int i;
-
-	for (i = 0; spec->pin[i]; i++) {
-		hdmi_enable_output(codec, spec->pin[i]);
-		snd_hda_codec_write(codec, spec->pin[i], 0,
-				    AC_VERB_SET_UNSOLICITED_ENABLE,
-				    AC_USRSP_EN | spec->pin[i]);
-	}
-	return 0;
-}
-
-static void intel_hdmi_free(struct hda_codec *codec)
-{
-	struct hdmi_spec *spec = codec->spec;
-	int i;
-
-	for (i = 0; i < spec->num_pins; i++)
-		snd_hda_eld_proc_free(codec, &spec->sink_eld[i]);
-
-	kfree(spec);
-}
-
-static struct hda_codec_ops intel_hdmi_patch_ops = {
-	.init			= intel_hdmi_init,
-	.free			= intel_hdmi_free,
-	.build_pcms		= intel_hdmi_build_pcms,
-	.build_controls 	= intel_hdmi_build_controls,
-	.unsol_event		= hdmi_unsol_event,
-};
-
-static int patch_intel_hdmi(struct hda_codec *codec)
-{
-	struct hdmi_spec *spec;
-	int i;
-
-	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-	if (spec == NULL)
-		return -ENOMEM;
-
-	codec->spec = spec;
-	if (hdmi_parse_codec(codec) < 0) {
-		codec->spec = NULL;
-		kfree(spec);
-		return -EINVAL;
-	}
-	codec->patch_ops = intel_hdmi_patch_ops;
-
-	for (i = 0; i < spec->num_pins; i++)
-		snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i);
-
-	init_channel_allocations();
-
-	return 0;
-}
-
-static struct hda_codec_preset snd_hda_preset_intelhdmi[] = {
-{ .id = 0x808629fb, .name = "Crestline HDMI",	.patch = patch_intel_hdmi },
-{ .id = 0x80862801, .name = "Bearlake HDMI",	.patch = patch_intel_hdmi },
-{ .id = 0x80862802, .name = "Cantiga HDMI",	.patch = patch_intel_hdmi },
-{ .id = 0x80862803, .name = "Eaglelake HDMI",	.patch = patch_intel_hdmi },
-{ .id = 0x80862804, .name = "IbexPeak HDMI",	.patch = patch_intel_hdmi },
-{ .id = 0x80860054, .name = "IbexPeak HDMI",	.patch = patch_intel_hdmi },
-{ .id = 0x80862805, .name = "CougarPoint HDMI",	.patch = patch_intel_hdmi },
-{ .id = 0x10951392, .name = "SiI1392 HDMI",	.patch = patch_intel_hdmi },
-{} /* terminator */
-};
-
-MODULE_ALIAS("snd-hda-codec-id:808629fb");
-MODULE_ALIAS("snd-hda-codec-id:80862801");
-MODULE_ALIAS("snd-hda-codec-id:80862802");
-MODULE_ALIAS("snd-hda-codec-id:80862803");
-MODULE_ALIAS("snd-hda-codec-id:80862804");
-MODULE_ALIAS("snd-hda-codec-id:80862805");
-MODULE_ALIAS("snd-hda-codec-id:80860054");
-MODULE_ALIAS("snd-hda-codec-id:10951392");
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Intel HDMI HD-audio codec");
-
-static struct hda_codec_preset_list intel_list = {
-	.preset = snd_hda_preset_intelhdmi,
-	.owner = THIS_MODULE,
-};
-
-static int __init patch_intelhdmi_init(void)
-{
-	return snd_hda_add_codec_preset(&intel_list);
-}
-
-static void __exit patch_intelhdmi_exit(void)
-{
-	snd_hda_delete_codec_preset(&intel_list);
-}
-
-module_init(patch_intelhdmi_init)
-module_exit(patch_intelhdmi_exit)

+ 0 - 608
sound/pci/hda/patch_nvhdmi.c

@@ -1,608 +0,0 @@
-/*
- * Universal Interface for Intel High Definition Audio Codec
- *
- * HD audio interface patch for NVIDIA HDMI codecs
- *
- * Copyright (c) 2008 NVIDIA Corp.  All rights reserved.
- * Copyright (c) 2008 Wei Ni <wni@nvidia.com>
- *
- *
- *  This driver is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This driver is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
- */
-
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <sound/core.h>
-#include "hda_codec.h"
-#include "hda_local.h"
-
-#define MAX_HDMI_CVTS	1
-#define MAX_HDMI_PINS	1
-
-#include "patch_hdmi.c"
-
-static char *nvhdmi_pcm_names[MAX_HDMI_CVTS] = {
-	"NVIDIA HDMI",
-};
-
-/* define below to restrict the supported rates and formats */
-/* #define LIMITED_RATE_FMT_SUPPORT */
-
-enum HDACodec {
-	HDA_CODEC_NVIDIA_MCP7X,
-	HDA_CODEC_NVIDIA_MCP89,
-	HDA_CODEC_NVIDIA_GT21X,
-	HDA_CODEC_INVALID
-};
-
-#define Nv_VERB_SET_Channel_Allocation          0xF79
-#define Nv_VERB_SET_Info_Frame_Checksum         0xF7A
-#define Nv_VERB_SET_Audio_Protection_On         0xF98
-#define Nv_VERB_SET_Audio_Protection_Off        0xF99
-
-#define nvhdmi_master_con_nid_7x	0x04
-#define nvhdmi_master_pin_nid_7x	0x05
-
-#define nvhdmi_master_con_nid_89	0x04
-#define nvhdmi_master_pin_nid_89	0x05
-
-static hda_nid_t nvhdmi_con_nids_7x[4] = {
-	/*front, rear, clfe, rear_surr */
-	0x6, 0x8, 0xa, 0xc,
-};
-
-static struct hda_verb nvhdmi_basic_init_7x[] = {
-	/* set audio protect on */
-	{ 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
-	/* enable digital output on pin widget */
-	{ 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
-	{ 0x7, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
-	{ 0x9, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
-	{ 0xb, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
-	{ 0xd, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
-	{} /* terminator */
-};
-
-#ifdef LIMITED_RATE_FMT_SUPPORT
-/* support only the safe format and rate */
-#define SUPPORTED_RATES		SNDRV_PCM_RATE_48000
-#define SUPPORTED_MAXBPS	16
-#define SUPPORTED_FORMATS	SNDRV_PCM_FMTBIT_S16_LE
-#else
-/* support all rates and formats */
-#define SUPPORTED_RATES \
-	(SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
-	SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\
-	 SNDRV_PCM_RATE_192000)
-#define SUPPORTED_MAXBPS	24
-#define SUPPORTED_FORMATS \
-	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
-#endif
-
-/*
- * Controls
- */
-static int nvhdmi_build_controls(struct hda_codec *codec)
-{
-	struct hdmi_spec *spec = codec->spec;
-	int err;
-	int i;
-
-	if ((spec->codec_type == HDA_CODEC_NVIDIA_MCP89)
-	|| (spec->codec_type == HDA_CODEC_NVIDIA_GT21X)) {
-		for (i = 0; i < codec->num_pcms; i++) {
-			err = snd_hda_create_spdif_out_ctls(codec,
-							    spec->cvt[i]);
-			if (err < 0)
-				return err;
-		}
-	} else {
-		err = snd_hda_create_spdif_out_ctls(codec,
-						    spec->multiout.dig_out_nid);
-		if (err < 0)
-			return err;
-	}
-
-	return 0;
-}
-
-static int nvhdmi_init(struct hda_codec *codec)
-{
-	struct hdmi_spec *spec = codec->spec;
-	int i;
-	if ((spec->codec_type == HDA_CODEC_NVIDIA_MCP89)
-	|| (spec->codec_type == HDA_CODEC_NVIDIA_GT21X)) {
-		for (i = 0; spec->pin[i]; i++) {
-			hdmi_enable_output(codec, spec->pin[i]);
-			snd_hda_codec_write(codec, spec->pin[i], 0,
-					    AC_VERB_SET_UNSOLICITED_ENABLE,
-					    AC_USRSP_EN | spec->pin[i]);
-		}
-	} else {
-		snd_hda_sequence_write(codec, nvhdmi_basic_init_7x);
-	}
-	return 0;
-}
-
-static void nvhdmi_free(struct hda_codec *codec)
-{
-	struct hdmi_spec *spec = codec->spec;
-	int i;
-
-	if ((spec->codec_type == HDA_CODEC_NVIDIA_MCP89)
-	|| (spec->codec_type == HDA_CODEC_NVIDIA_GT21X)) {
-		for (i = 0; i < spec->num_pins; i++)
-			snd_hda_eld_proc_free(codec, &spec->sink_eld[i]);
-	}
-
-	kfree(spec);
-}
-
-/*
- * Digital out
- */
-static int nvhdmi_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
-					struct hda_codec *codec,
-					struct snd_pcm_substream *substream)
-{
-	struct hdmi_spec *spec = codec->spec;
-	return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
-
-static int nvhdmi_dig_playback_pcm_close_8ch_7x(struct hda_pcm_stream *hinfo,
-					struct hda_codec *codec,
-					struct snd_pcm_substream *substream)
-{
-	struct hdmi_spec *spec = codec->spec;
-	int i;
-
-	snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x,
-			0, AC_VERB_SET_CHANNEL_STREAMID, 0);
-	for (i = 0; i < 4; i++) {
-		/* set the stream id */
-		snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
-				AC_VERB_SET_CHANNEL_STREAMID, 0);
-		/* set the stream format */
-		snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
-				AC_VERB_SET_STREAM_FORMAT, 0);
-	}
-
-	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-static int nvhdmi_dig_playback_pcm_close_2ch(struct hda_pcm_stream *hinfo,
-					struct hda_codec *codec,
-					struct snd_pcm_substream *substream)
-{
-	struct hdmi_spec *spec = codec->spec;
-	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-static int nvhdmi_dig_playback_pcm_prepare_8ch_89(struct hda_pcm_stream *hinfo,
-					struct hda_codec *codec,
-					unsigned int stream_tag,
-					unsigned int format,
-					struct snd_pcm_substream *substream)
-{
-	hdmi_set_channel_count(codec, hinfo->nid,
-			       substream->runtime->channels);
-
-	hdmi_setup_audio_infoframe(codec, hinfo->nid, substream);
-
-	return hdmi_setup_stream(codec, hinfo->nid, stream_tag, format);
-}
-
-static int nvhdmi_dig_playback_pcm_prepare_8ch(struct hda_pcm_stream *hinfo,
-					struct hda_codec *codec,
-					unsigned int stream_tag,
-					unsigned int format,
-					struct snd_pcm_substream *substream)
-{
-	int chs;
-	unsigned int dataDCC1, dataDCC2, chan, chanmask, channel_id;
-	int i;
-
-	mutex_lock(&codec->spdif_mutex);
-
-	chs = substream->runtime->channels;
-	chan = chs ? (chs - 1) : 1;
-
-	switch (chs) {
-	default:
-	case 0:
-	case 2:
-		chanmask = 0x00;
-		break;
-	case 4:
-		chanmask = 0x08;
-		break;
-	case 6:
-		chanmask = 0x0b;
-		break;
-	case 8:
-		chanmask = 0x13;
-		break;
-	}
-	dataDCC1 = AC_DIG1_ENABLE | AC_DIG1_COPYRIGHT;
-	dataDCC2 = 0x2;
-
-	/* set the Audio InforFrame Channel Allocation */
-	snd_hda_codec_write(codec, 0x1, 0,
-			Nv_VERB_SET_Channel_Allocation, chanmask);
-
-	/* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
-	if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
-		snd_hda_codec_write(codec,
-				nvhdmi_master_con_nid_7x,
-				0,
-				AC_VERB_SET_DIGI_CONVERT_1,
-				codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
-
-	/* set the stream id */
-	snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
-			AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | 0x0);
-
-	/* set the stream format */
-	snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
-			AC_VERB_SET_STREAM_FORMAT, format);
-
-	/* turn on again (if needed) */
-	/* enable and set the channel status audio/data flag */
-	if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) {
-		snd_hda_codec_write(codec,
-				nvhdmi_master_con_nid_7x,
-				0,
-				AC_VERB_SET_DIGI_CONVERT_1,
-				codec->spdif_ctls & 0xff);
-		snd_hda_codec_write(codec,
-				nvhdmi_master_con_nid_7x,
-				0,
-				AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
-	}
-
-	for (i = 0; i < 4; i++) {
-		if (chs == 2)
-			channel_id = 0;
-		else
-			channel_id = i * 2;
-
-		/* turn off SPDIF once;
-		 *otherwise the IEC958 bits won't be updated
-		 */
-		if (codec->spdif_status_reset &&
-		(codec->spdif_ctls & AC_DIG1_ENABLE))
-			snd_hda_codec_write(codec,
-				nvhdmi_con_nids_7x[i],
-				0,
-				AC_VERB_SET_DIGI_CONVERT_1,
-				codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
-		/* set the stream id */
-		snd_hda_codec_write(codec,
-				nvhdmi_con_nids_7x[i],
-				0,
-				AC_VERB_SET_CHANNEL_STREAMID,
-				(stream_tag << 4) | channel_id);
-		/* set the stream format */
-		snd_hda_codec_write(codec,
-				nvhdmi_con_nids_7x[i],
-				0,
-				AC_VERB_SET_STREAM_FORMAT,
-				format);
-		/* turn on again (if needed) */
-		/* enable and set the channel status audio/data flag */
-		if (codec->spdif_status_reset &&
-		(codec->spdif_ctls & AC_DIG1_ENABLE)) {
-			snd_hda_codec_write(codec,
-					nvhdmi_con_nids_7x[i],
-					0,
-					AC_VERB_SET_DIGI_CONVERT_1,
-					codec->spdif_ctls & 0xff);
-			snd_hda_codec_write(codec,
-					nvhdmi_con_nids_7x[i],
-					0,
-					AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
-		}
-	}
-
-	/* set the Audio Info Frame Checksum */
-	snd_hda_codec_write(codec, 0x1, 0,
-			Nv_VERB_SET_Info_Frame_Checksum,
-			(0x71 - chan - chanmask));
-
-	mutex_unlock(&codec->spdif_mutex);
-	return 0;
-}
-
-static int nvhdmi_dig_playback_pcm_prepare_2ch(struct hda_pcm_stream *hinfo,
-					struct hda_codec *codec,
-					unsigned int stream_tag,
-					unsigned int format,
-					struct snd_pcm_substream *substream)
-{
-	struct hdmi_spec *spec = codec->spec;
-	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
-					format, substream);
-}
-
-static struct hda_pcm_stream nvhdmi_pcm_digital_playback_8ch_89 = {
-	.substreams = 1,
-	.channels_min = 2,
-	.ops = {
-		.open = hdmi_pcm_open,
-		.prepare = nvhdmi_dig_playback_pcm_prepare_8ch_89,
-	},
-};
-
-static struct hda_pcm_stream nvhdmi_pcm_digital_playback_8ch_7x = {
-	.substreams = 1,
-	.channels_min = 2,
-	.channels_max = 8,
-	.nid = nvhdmi_master_con_nid_7x,
-	.rates = SUPPORTED_RATES,
-	.maxbps = SUPPORTED_MAXBPS,
-	.formats = SUPPORTED_FORMATS,
-	.ops = {
-		.open = nvhdmi_dig_playback_pcm_open,
-		.close = nvhdmi_dig_playback_pcm_close_8ch_7x,
-		.prepare = nvhdmi_dig_playback_pcm_prepare_8ch
-	},
-};
-
-static struct hda_pcm_stream nvhdmi_pcm_digital_playback_2ch = {
-	.substreams = 1,
-	.channels_min = 2,
-	.channels_max = 2,
-	.nid = nvhdmi_master_con_nid_7x,
-	.rates = SUPPORTED_RATES,
-	.maxbps = SUPPORTED_MAXBPS,
-	.formats = SUPPORTED_FORMATS,
-	.ops = {
-		.open = nvhdmi_dig_playback_pcm_open,
-		.close = nvhdmi_dig_playback_pcm_close_2ch,
-		.prepare = nvhdmi_dig_playback_pcm_prepare_2ch
-	},
-};
-
-static int nvhdmi_build_pcms_8ch_89(struct hda_codec *codec)
-{
-	struct hdmi_spec *spec = codec->spec;
-	struct hda_pcm *info = spec->pcm_rec;
-	int i;
-
-	codec->num_pcms = spec->num_cvts;
-	codec->pcm_info = info;
-
-	for (i = 0; i < codec->num_pcms; i++, info++) {
-		unsigned int chans;
-
-		chans = get_wcaps(codec, spec->cvt[i]);
-		chans = get_wcaps_channels(chans);
-
-		info->name = nvhdmi_pcm_names[i];
-		info->pcm_type = HDA_PCM_TYPE_HDMI;
-		info->stream[SNDRV_PCM_STREAM_PLAYBACK]
-					= nvhdmi_pcm_digital_playback_8ch_89;
-		info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->cvt[i];
-		info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans;
-	}
-
-	return 0;
-}
-
-static int nvhdmi_build_pcms_8ch_7x(struct hda_codec *codec)
-{
-	struct hdmi_spec *spec = codec->spec;
-	struct hda_pcm *info = spec->pcm_rec;
-
-	codec->num_pcms = 1;
-	codec->pcm_info = info;
-
-	info->name = "NVIDIA HDMI";
-	info->pcm_type = HDA_PCM_TYPE_HDMI;
-	info->stream[SNDRV_PCM_STREAM_PLAYBACK]
-					= nvhdmi_pcm_digital_playback_8ch_7x;
-
-	return 0;
-}
-
-static int nvhdmi_build_pcms_2ch(struct hda_codec *codec)
-{
-	struct hdmi_spec *spec = codec->spec;
-	struct hda_pcm *info = spec->pcm_rec;
-
-	codec->num_pcms = 1;
-	codec->pcm_info = info;
-
-	info->name = "NVIDIA HDMI";
-	info->pcm_type = HDA_PCM_TYPE_HDMI;
-	info->stream[SNDRV_PCM_STREAM_PLAYBACK]
-					= nvhdmi_pcm_digital_playback_2ch;
-
-	return 0;
-}
-
-static struct hda_codec_ops nvhdmi_patch_ops_8ch_89 = {
-	.build_controls = nvhdmi_build_controls,
-	.build_pcms = nvhdmi_build_pcms_8ch_89,
-	.init = nvhdmi_init,
-	.free = nvhdmi_free,
-	.unsol_event = hdmi_unsol_event,
-};
-
-static struct hda_codec_ops nvhdmi_patch_ops_8ch_7x = {
-	.build_controls = nvhdmi_build_controls,
-	.build_pcms = nvhdmi_build_pcms_8ch_7x,
-	.init = nvhdmi_init,
-	.free = nvhdmi_free,
-};
-
-static struct hda_codec_ops nvhdmi_patch_ops_2ch = {
-	.build_controls = nvhdmi_build_controls,
-	.build_pcms = nvhdmi_build_pcms_2ch,
-	.init = nvhdmi_init,
-	.free = nvhdmi_free,
-};
-
-static int patch_nvhdmi_8ch_89(struct hda_codec *codec)
-{
-	struct hdmi_spec *spec;
-	int i;
-
-	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-	if (spec == NULL)
-		return -ENOMEM;
-
-	codec->spec = spec;
-	spec->codec_type = HDA_CODEC_NVIDIA_MCP89;
-	spec->old_pin_detect = 1;
-
-	if (hdmi_parse_codec(codec) < 0) {
-		codec->spec = NULL;
-		kfree(spec);
-		return -EINVAL;
-	}
-	codec->patch_ops = nvhdmi_patch_ops_8ch_89;
-
-	for (i = 0; i < spec->num_pins; i++)
-		snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i);
-
-	init_channel_allocations();
-
-	return 0;
-}
-
-static int patch_nvhdmi_8ch_7x(struct hda_codec *codec)
-{
-	struct hdmi_spec *spec;
-
-	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-	if (spec == NULL)
-		return -ENOMEM;
-
-	codec->spec = spec;
-
-	spec->multiout.num_dacs = 0;  /* no analog */
-	spec->multiout.max_channels = 8;
-	spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x;
-	spec->codec_type = HDA_CODEC_NVIDIA_MCP7X;
-	spec->old_pin_detect = 1;
-
-	codec->patch_ops = nvhdmi_patch_ops_8ch_7x;
-
-	return 0;
-}
-
-static int patch_nvhdmi_2ch(struct hda_codec *codec)
-{
-	struct hdmi_spec *spec;
-
-	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-	if (spec == NULL)
-		return -ENOMEM;
-
-	codec->spec = spec;
-
-	spec->multiout.num_dacs = 0;  /* no analog */
-	spec->multiout.max_channels = 2;
-	spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x;
-	spec->codec_type = HDA_CODEC_NVIDIA_MCP7X;
-	spec->old_pin_detect = 1;
-
-	codec->patch_ops = nvhdmi_patch_ops_2ch;
-
-	return 0;
-}
-
-/*
- * patch entries
- */
-static struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
-	{ .id = 0x10de0002, .name = "MCP77/78 HDMI",  .patch = patch_nvhdmi_8ch_7x },
-	{ .id = 0x10de0003, .name = "MCP77/78 HDMI",  .patch = patch_nvhdmi_8ch_7x },
-	{ .id = 0x10de0005, .name = "MCP77/78 HDMI",  .patch = patch_nvhdmi_8ch_7x },
-	{ .id = 0x10de0006, .name = "MCP77/78 HDMI",  .patch = patch_nvhdmi_8ch_7x },
-	{ .id = 0x10de0007, .name = "MCP79/7A HDMI",  .patch = patch_nvhdmi_8ch_7x },
-	{ .id = 0x10de000a, .name = "GPU 0a HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-	{ .id = 0x10de000b, .name = "GPU 0b HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-	{ .id = 0x10de000c, .name = "MCP89 HDMI",     .patch = patch_nvhdmi_8ch_89 },
-	{ .id = 0x10de000d, .name = "GPU 0d HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-	{ .id = 0x10de0010, .name = "GPU 10 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-	{ .id = 0x10de0011, .name = "GPU 11 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-	{ .id = 0x10de0012, .name = "GPU 12 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-	{ .id = 0x10de0013, .name = "GPU 13 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-	{ .id = 0x10de0014, .name = "GPU 14 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-	{ .id = 0x10de0018, .name = "GPU 18 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-	{ .id = 0x10de0019, .name = "GPU 19 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-	{ .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-	{ .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-	{ .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-	{ .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-	{ .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-	{ .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-	{ .id = 0x10de0043, .name = "GPU 43 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-	{ .id = 0x10de0044, .name = "GPU 44 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-	{ .id = 0x10de0067, .name = "MCP67 HDMI",     .patch = patch_nvhdmi_2ch },
-	{ .id = 0x10de8001, .name = "MCP73 HDMI",     .patch = patch_nvhdmi_2ch },
-	{} /* terminator */
-};
-
-MODULE_ALIAS("snd-hda-codec-id:10de0002");
-MODULE_ALIAS("snd-hda-codec-id:10de0003");
-MODULE_ALIAS("snd-hda-codec-id:10de0005");
-MODULE_ALIAS("snd-hda-codec-id:10de0006");
-MODULE_ALIAS("snd-hda-codec-id:10de0007");
-MODULE_ALIAS("snd-hda-codec-id:10de000a");
-MODULE_ALIAS("snd-hda-codec-id:10de000b");
-MODULE_ALIAS("snd-hda-codec-id:10de000c");
-MODULE_ALIAS("snd-hda-codec-id:10de000d");
-MODULE_ALIAS("snd-hda-codec-id:10de0010");
-MODULE_ALIAS("snd-hda-codec-id:10de0011");
-MODULE_ALIAS("snd-hda-codec-id:10de0012");
-MODULE_ALIAS("snd-hda-codec-id:10de0013");
-MODULE_ALIAS("snd-hda-codec-id:10de0014");
-MODULE_ALIAS("snd-hda-codec-id:10de0018");
-MODULE_ALIAS("snd-hda-codec-id:10de0019");
-MODULE_ALIAS("snd-hda-codec-id:10de001a");
-MODULE_ALIAS("snd-hda-codec-id:10de001b");
-MODULE_ALIAS("snd-hda-codec-id:10de001c");
-MODULE_ALIAS("snd-hda-codec-id:10de0040");
-MODULE_ALIAS("snd-hda-codec-id:10de0041");
-MODULE_ALIAS("snd-hda-codec-id:10de0042");
-MODULE_ALIAS("snd-hda-codec-id:10de0043");
-MODULE_ALIAS("snd-hda-codec-id:10de0044");
-MODULE_ALIAS("snd-hda-codec-id:10de0067");
-MODULE_ALIAS("snd-hda-codec-id:10de8001");
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("NVIDIA HDMI HD-audio codec");
-
-static struct hda_codec_preset_list nvhdmi_list = {
-	.preset = snd_hda_preset_nvhdmi,
-	.owner = THIS_MODULE,
-};
-
-static int __init patch_nvhdmi_init(void)
-{
-	return snd_hda_add_codec_preset(&nvhdmi_list);
-}
-
-static void __exit patch_nvhdmi_exit(void)
-{
-	snd_hda_delete_codec_preset(&nvhdmi_list);
-}
-
-module_init(patch_nvhdmi_init)
-module_exit(patch_nvhdmi_exit)

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 392 - 177
sound/pci/hda/patch_realtek.c


+ 192 - 187
sound/pci/hda/patch_sigmatel.c

@@ -32,6 +32,7 @@
 #include <sound/core.h>
 #include <sound/asoundef.h>
 #include <sound/jack.h>
+#include <sound/tlv.h>
 #include "hda_codec.h"
 #include "hda_local.h"
 #include "hda_beep.h"
@@ -263,6 +264,7 @@ struct sigmatel_spec {
 
 	struct sigmatel_mic_route ext_mic;
 	struct sigmatel_mic_route int_mic;
+	struct sigmatel_mic_route dock_mic;
 
 	const char **spdif_labels;
 
@@ -382,6 +384,11 @@ static unsigned int stac92hd83xxx_pwr_mapping[4] = {
 	0x03, 0x0c, 0x20, 0x40,
 };
 
+#define STAC92HD83XXX_NUM_DMICS	 2
+static hda_nid_t stac92hd83xxx_dmic_nids[STAC92HD83XXX_NUM_DMICS + 1] = {
+	0x11, 0x20, 0
+};
+
 #define STAC92HD83XXX_NUM_CAPS	2
 static unsigned long stac92hd83xxx_capvols[] = {
 	HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_OUTPUT),
@@ -986,7 +993,7 @@ static struct hda_verb stac9205_core_init[] = {
 	}
 
 static struct snd_kcontrol_new stac9200_mixer[] = {
-	HDA_CODEC_VOLUME("Master Playback Volume", 0xb, 0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MIN_MUTE("Master Playback Volume", 0xb, 0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Master Playback Switch", 0xb, 0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x0a, 0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x0a, 0, HDA_OUTPUT),
@@ -1014,7 +1021,7 @@ static struct snd_kcontrol_new stac92hd71bxx_loopback[] = {
 };
 
 static struct snd_kcontrol_new stac925x_mixer[] = {
-	HDA_CODEC_VOLUME("Master Playback Volume", 0x0e, 0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MIN_MUTE("Master Playback Volume", 0xe, 0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Master Playback Switch", 0x0e, 0, HDA_OUTPUT),
 	{ } /* end */
 };
@@ -1105,9 +1112,7 @@ static int stac92xx_build_controls(struct hda_codec *codec)
 		struct hda_input_mux *smux = &spec->private_smux;
 		/* check for mute support on SPDIF out */
 		if (wcaps & AC_WCAP_OUT_AMP) {
-			smux->items[smux->num_items].label = "Off";
-			smux->items[smux->num_items].index = 0;
-			smux->num_items++;
+			snd_hda_add_imux_item(smux, "Off", 0, NULL);
 			spec->spdif_mute = 1;
 		}
 		stac_smux_mixer.count = spec->num_smuxes;
@@ -1140,6 +1145,8 @@ static int stac92xx_build_controls(struct hda_codec *codec)
 					HDA_OUTPUT, vmaster_tlv);
 		/* correct volume offset */
 		vmaster_tlv[2] += vmaster_tlv[3] * spec->volume_offset;
+		/* minimum value is actually mute */
+		vmaster_tlv[3] |= TLV_DB_SCALE_MUTE;
 		err = snd_hda_add_vmaster(codec, "Master Playback Volume",
 					  vmaster_tlv, slave_vols);
 		if (err < 0)
@@ -1180,14 +1187,11 @@ static int stac92xx_build_controls(struct hda_codec *codec)
 		if (err < 0)
 			return err;
 	}
-	for (i = 0; i < AUTO_PIN_LAST; i++) {
-		nid = cfg->input_pins[i];
-		if (nid) {
-			err = stac92xx_add_jack(codec, nid,
-						SND_JACK_MICROPHONE);
-			if (err < 0)
-				return err;
-		}
+	for (i = 0; i < cfg->num_inputs; i++) {
+		nid = cfg->inputs[i].pin;
+		err = stac92xx_add_jack(codec, nid, SND_JACK_MICROPHONE);
+		if (err < 0)
+			return err;
 	}
 
 	return 0;	
@@ -2779,7 +2783,7 @@ static inline int stac92xx_add_jack_mode_control(struct hda_codec *codec,
 	struct sigmatel_spec *spec = codec->spec;
 	char name[22];
 
-	if (!((get_defcfg_connect(def_conf)) & AC_JACK_PORT_FIXED)) {
+	if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) {
 		if (stac92xx_get_default_vref(codec, nid) == AC_PINCTL_VREF_GRD
 			&& nid == spec->line_switch)
 			control = STAC_CTL_WIDGET_IO_SWITCH;
@@ -2791,7 +2795,7 @@ static inline int stac92xx_add_jack_mode_control(struct hda_codec *codec,
 	}
 
 	if (control) {
-		strcpy(name, auto_pin_cfg_labels[idx]);
+		strcpy(name, hda_get_input_pin_label(codec, nid, 1));
 		return stac92xx_add_control(codec->spec, control,
 					strcat(name, " Jack Mode"), nid);
 	}
@@ -2823,41 +2827,49 @@ static hda_nid_t check_line_out_switch(struct hda_codec *codec)
 	struct auto_pin_cfg *cfg = &spec->autocfg;
 	hda_nid_t nid;
 	unsigned int pincap;
+	int i;
 
 	if (cfg->line_out_type != AUTO_PIN_LINE_OUT)
 		return 0;
-	nid = cfg->input_pins[AUTO_PIN_LINE];
-	pincap = snd_hda_query_pin_caps(codec, nid);
-	if (pincap & AC_PINCAP_OUT)
-		return nid;
+	for (i = 0; i < cfg->num_inputs; i++) {
+		if (cfg->inputs[i].type == AUTO_PIN_LINE_IN) {
+			nid = cfg->inputs[i].pin;
+			pincap = snd_hda_query_pin_caps(codec, nid);
+			if (pincap & AC_PINCAP_OUT)
+				return nid;
+		}
+	}
 	return 0;
 }
 
+static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t nid);
+
 /* check whether the mic-input can be used as line-out */
-static hda_nid_t check_mic_out_switch(struct hda_codec *codec)
+static hda_nid_t check_mic_out_switch(struct hda_codec *codec, hda_nid_t *dac)
 {
 	struct sigmatel_spec *spec = codec->spec;
 	struct auto_pin_cfg *cfg = &spec->autocfg;
 	unsigned int def_conf, pincap;
-	unsigned int mic_pin;
+	int i;
 
+	*dac = 0;
 	if (cfg->line_out_type != AUTO_PIN_LINE_OUT)
 		return 0;
-	mic_pin = AUTO_PIN_MIC;
-	for (;;) {
-		hda_nid_t nid = cfg->input_pins[mic_pin];
+	for (i = 0; i < cfg->num_inputs; i++) {
+		hda_nid_t nid = cfg->inputs[i].pin;
+		if (cfg->inputs[i].type != AUTO_PIN_MIC)
+			continue;
 		def_conf = snd_hda_codec_get_pincfg(codec, nid);
 		/* some laptops have an internal analog microphone
 		 * which can't be used as a output */
-		if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED) {
+		if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) {
 			pincap = snd_hda_query_pin_caps(codec, nid);
-			if (pincap & AC_PINCAP_OUT)
-				return nid;
+			if (pincap & AC_PINCAP_OUT) {
+				*dac = get_unassigned_dac(codec, nid);
+				if (*dac)
+					return nid;
+			}
 		}
-		if (mic_pin == AUTO_PIN_MIC)
-			mic_pin = AUTO_PIN_FRONT_MIC;
-		else
-			break;
 	}
 	return 0;
 }
@@ -3004,17 +3016,14 @@ static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec)
 		}
 	}
 	/* add mic as output */
-	nid = check_mic_out_switch(codec);
-	if (nid) {
-		dac = get_unassigned_dac(codec, nid);
-		if (dac) {
-			snd_printdd("STAC: Add mic-in 0x%x as output %d\n",
-				    nid, cfg->line_outs);
-			cfg->line_out_pins[cfg->line_outs] = nid;
-			cfg->line_outs++;
-			spec->mic_switch = nid;
-			add_spec_dacs(spec, dac);
-		}
+	nid = check_mic_out_switch(codec, &dac);
+	if (nid && dac) {
+		snd_printdd("STAC: Add mic-in 0x%x as output %d\n",
+			    nid, cfg->line_outs);
+		cfg->line_out_pins[cfg->line_outs] = nid;
+		cfg->line_outs++;
+		spec->mic_switch = nid;
+		add_spec_dacs(spec, dac);
 	}
 
 	snd_printd("stac92xx: dac_nids=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
@@ -3204,13 +3213,13 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
 			return err;
 	}
 
-	for (idx = AUTO_PIN_MIC; idx <= AUTO_PIN_FRONT_LINE; idx++) {
-		nid = cfg->input_pins[idx];
-		if (nid) {
-			err = stac92xx_add_jack_mode_control(codec, nid, idx);
-			if (err < 0)
-				return err;
-		}
+	for (idx = 0; idx < cfg->num_inputs; idx++) {
+		if (cfg->inputs[idx].type > AUTO_PIN_LINE_IN)
+			break;
+		nid = cfg->inputs[idx].pin;
+		err = stac92xx_add_jack_mode_control(codec, nid, idx);
+		if (err < 0)
+			return err;
 	}
 
 	return 0;
@@ -3256,12 +3265,9 @@ static int stac92xx_auto_create_mono_output_ctls(struct hda_codec *codec)
 	if (num_cons <= 0 || num_cons > ARRAY_SIZE(stac92xx_mono_labels))
 		return -EINVAL;
 
-	for (i = 0; i < num_cons; i++) {
-		mono_mux->items[mono_mux->num_items].label =
-					stac92xx_mono_labels[i];
-		mono_mux->items[mono_mux->num_items].index = i;
-		mono_mux->num_items++;
-	}
+	for (i = 0; i < num_cons; i++)
+		snd_hda_add_imux_item(mono_mux, stac92xx_mono_labels[i], i,
+				      NULL);
 
 	return stac92xx_add_control(spec, STAC_CTL_WIDGET_MONO_MUX,
 				"Mono Mux", spec->mono_nid);
@@ -3386,11 +3392,8 @@ static int stac92xx_auto_create_spdif_mux_ctls(struct hda_codec *codec)
 	if (!labels)
 		labels = stac92xx_spdif_labels;
 
-	for (i = 0; i < num_cons; i++) {
-		spdif_mux->items[spdif_mux->num_items].label = labels[i];
-		spdif_mux->items[spdif_mux->num_items].index = i;
-		spdif_mux->num_items++;
-	}
+	for (i = 0; i < num_cons; i++)
+		snd_hda_add_imux_item(spdif_mux, labels[i], i, NULL);
 
 	return 0;
 }
@@ -3417,7 +3420,7 @@ static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
 /* create a volume assigned to the given pin (only if supported) */
 /* return 1 if the volume control is created */
 static int create_elem_capture_vol(struct hda_codec *codec, hda_nid_t nid,
-				   const char *label, int direction)
+				   const char *label, int idx, int direction)
 {
 	unsigned int caps, nums;
 	char name[32];
@@ -3434,8 +3437,8 @@ static int create_elem_capture_vol(struct hda_codec *codec, hda_nid_t nid,
 	if (!nums)
 		return 0;
 	snprintf(name, sizeof(name), "%s Capture Volume", label);
-	err = stac92xx_add_control(codec->spec, STAC_CTL_WIDGET_VOL, name,
-				    HDA_COMPOSE_AMP_VAL(nid, 3, 0, direction));
+	err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_VOL, idx, name,
+				       HDA_COMPOSE_AMP_VAL(nid, 3, 0, direction));
 	if (err < 0)
 		return err;
 	return 1;
@@ -3448,27 +3451,14 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
 	struct sigmatel_spec *spec = codec->spec;
 	struct hda_input_mux *imux = &spec->private_imux;
 	struct hda_input_mux *dimux = &spec->private_dimux;
-	int err, i, active_mics;
+	int err, i;
 	unsigned int def_conf;
 
-	dimux->items[dimux->num_items].label = stac92xx_dmic_labels[0];
-	dimux->items[dimux->num_items].index = 0;
-	dimux->num_items++;
-
-	active_mics = 0;
-	for (i = 0; i < spec->num_dmics; i++) {
-		/* check the validity: sometimes it's a dead vendor-spec node */
-		if (get_wcaps_type(get_wcaps(codec, spec->dmic_nids[i]))
-		    != AC_WID_PIN)
-			continue;
-		def_conf = snd_hda_codec_get_pincfg(codec, spec->dmic_nids[i]);
-		if (get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)
-			active_mics++;
-	}
+	snd_hda_add_imux_item(dimux, stac92xx_dmic_labels[0], 0, NULL);
 
 	for (i = 0; i < spec->num_dmics; i++) {
 		hda_nid_t nid;
-		int index;
+		int index, type_idx;
 		const char *label;
 
 		nid = spec->dmic_nids[i];
@@ -3482,28 +3472,23 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
 		if (index < 0)
 			continue;
 
-		if (active_mics == 1)
-			label = "Digital Mic";
-		else
-			label = stac92xx_dmic_labels[dimux->num_items];
+		label = hda_get_input_pin_label(codec, nid, 1);
+		snd_hda_add_imux_item(dimux, label, index, &type_idx);
 
-		err = create_elem_capture_vol(codec, nid, label, HDA_INPUT);
+		err = create_elem_capture_vol(codec, nid, label, type_idx,
+					      HDA_INPUT);
 		if (err < 0)
 			return err;
 		if (!err) {
 			err = create_elem_capture_vol(codec, nid, label,
-						      HDA_OUTPUT);
+						      type_idx, HDA_OUTPUT);
 			if (err < 0)
 				return err;
 		}
 
-		dimux->items[dimux->num_items].label = label;
-		dimux->items[dimux->num_items].index = index;
-		dimux->num_items++;
 		if (snd_hda_get_bool_hint(codec, "separate_dmux") != 1) {
-			imux->items[imux->num_items].label = label;
-			imux->items[imux->num_items].index = index;
-			imux->num_items++;
+			snd_hda_add_imux_item(imux, label, index, NULL);
+			spec->num_analog_muxes++;
 		}
 	}
 
@@ -3511,20 +3496,27 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
 }
 
 static int check_mic_pin(struct hda_codec *codec, hda_nid_t nid,
-			 hda_nid_t *fixed, hda_nid_t *ext)
+			 hda_nid_t *fixed, hda_nid_t *ext, hda_nid_t *dock)
 {
 	unsigned int cfg;
 
 	if (!nid)
 		return 0;
 	cfg = snd_hda_codec_get_pincfg(codec, nid);
-	switch (get_defcfg_connect(cfg)) {
-	case AC_JACK_PORT_FIXED:
+	switch (snd_hda_get_input_pin_attr(cfg)) {
+	case INPUT_PIN_ATTR_INT:
 		if (*fixed)
 			return 1; /* already occupied */
 		*fixed = nid;
 		break;
-	case AC_JACK_PORT_COMPLEX:
+	case INPUT_PIN_ATTR_UNUSED:
+		break;
+	case INPUT_PIN_ATTR_DOCK:
+		if (*dock)
+			return 1; /* already occupied */
+		*dock = nid;
+		break;
+	default:
 		if (*ext)
 			return 1; /* already occupied */
 		*ext = nid;
@@ -3542,10 +3534,13 @@ static int set_mic_route(struct hda_codec *codec,
 	int i;
 
 	mic->pin = pin;
-	for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++)
-		if (pin == cfg->input_pins[i])
+	if (pin == 0)
+		return 0;
+	for (i = 0; i < cfg->num_inputs; i++) {
+		if (pin == cfg->inputs[i].pin)
 			break;
-	if (i <= AUTO_PIN_FRONT_MIC) {
+	}
+	if (i < cfg->num_inputs && cfg->inputs[i].type == AUTO_PIN_MIC) {
 		/* analog pin */
 		i = get_connection_index(codec, spec->mux_nids[0], pin);
 		if (i < 0)
@@ -3576,26 +3571,29 @@ static int stac_check_auto_mic(struct hda_codec *codec)
 {
 	struct sigmatel_spec *spec = codec->spec;
 	struct auto_pin_cfg *cfg = &spec->autocfg;
-	hda_nid_t fixed, ext;
+	hda_nid_t fixed, ext, dock;
 	int i;
 
-	for (i = AUTO_PIN_LINE; i < AUTO_PIN_LAST; i++) {
-		if (cfg->input_pins[i])
+	for (i = 0; i < cfg->num_inputs; i++) {
+		if (cfg->inputs[i].type >= AUTO_PIN_LINE_IN)
 			return 0; /* must be exclusively mics */
 	}
-	fixed = ext = 0;
-	for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++)
-		if (check_mic_pin(codec, cfg->input_pins[i], &fixed, &ext))
+	fixed = ext = dock = 0;
+	for (i = 0; i < cfg->num_inputs; i++)
+		if (check_mic_pin(codec, cfg->inputs[i].pin,
+		    &fixed, &ext, &dock))
 			return 0;
 	for (i = 0; i < spec->num_dmics; i++)
-		if (check_mic_pin(codec, spec->dmic_nids[i], &fixed, &ext))
+		if (check_mic_pin(codec, spec->dmic_nids[i],
+		    &fixed, &ext, &dock))
 			return 0;
-	if (!fixed || !ext)
-		return 0;
+	if (!fixed && !ext && !dock)
+		return 0; /* no input to switch */
 	if (!(get_wcaps(codec, ext) & AC_WCAP_UNSOL_CAP))
 		return 0; /* no unsol support */
 	if (set_mic_route(codec, &spec->ext_mic, ext) ||
-	    set_mic_route(codec, &spec->int_mic, fixed))
+	    set_mic_route(codec, &spec->int_mic, fixed) ||
+	    set_mic_route(codec, &spec->dock_mic, dock))
 		return 0; /* something is wrong */
 	return 1;
 }
@@ -3606,13 +3604,12 @@ static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const
 	struct sigmatel_spec *spec = codec->spec;
 	struct hda_input_mux *imux = &spec->private_imux;
 	int i, j;
+	const char *label;
 
-	for (i = 0; i < AUTO_PIN_LAST; i++) {
-		hda_nid_t nid = cfg->input_pins[i];
-		int index, err;
+	for (i = 0; i < cfg->num_inputs; i++) {
+		hda_nid_t nid = cfg->inputs[i].pin;
+		int index, err, type_idx;
 
-		if (!nid)
-			continue;
 		index = -1;
 		for (j = 0; j < spec->num_muxes; j++) {
 			index = get_connection_index(codec, spec->mux_nids[j],
@@ -3623,15 +3620,14 @@ static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const
 		if (index < 0)
 			continue;
 
+		label = hda_get_autocfg_input_label(codec, cfg, i);
+		snd_hda_add_imux_item(imux, label, index, &type_idx);
+
 		err = create_elem_capture_vol(codec, nid,
-					      auto_pin_cfg_labels[i],
+					      label, type_idx,
 					      HDA_INPUT);
 		if (err < 0)
 			return err;
-
-		imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
-		imux->items[imux->num_items].index = index;
-		imux->num_items++;
 	}
 	spec->num_analog_muxes = imux->num_items;
 
@@ -4305,38 +4301,38 @@ static int stac92xx_init(struct hda_codec *codec)
 					  AC_VERB_SET_CONNECT_SEL, 0);
 		if (enable_pin_detect(codec, spec->ext_mic.pin, STAC_MIC_EVENT))
 			stac_issue_unsol_event(codec, spec->ext_mic.pin);
-	}
-	for (i = 0; i < AUTO_PIN_LAST; i++) {
-		hda_nid_t nid = cfg->input_pins[i];
-		if (nid) {
-			unsigned int pinctl, conf;
-			if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC) {
-				/* for mic pins, force to initialize */
-				pinctl = stac92xx_get_default_vref(codec, nid);
+		if (enable_pin_detect(codec, spec->dock_mic.pin,
+		    STAC_MIC_EVENT))
+			stac_issue_unsol_event(codec, spec->dock_mic.pin);
+	}
+	for (i = 0; i < cfg->num_inputs; i++) {
+		hda_nid_t nid = cfg->inputs[i].pin;
+		int type = cfg->inputs[i].type;
+		unsigned int pinctl, conf;
+		if (type == AUTO_PIN_MIC) {
+			/* for mic pins, force to initialize */
+			pinctl = stac92xx_get_default_vref(codec, nid);
+			pinctl |= AC_PINCTL_IN_EN;
+			stac92xx_auto_set_pinctl(codec, nid, pinctl);
+		} else {
+			pinctl = snd_hda_codec_read(codec, nid, 0,
+					AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+			/* if PINCTL already set then skip */
+			/* Also, if both INPUT and OUTPUT are set,
+			 * it must be a BIOS bug; need to override, too
+			 */
+			if (!(pinctl & AC_PINCTL_IN_EN) ||
+			    (pinctl & AC_PINCTL_OUT_EN)) {
+				pinctl &= ~AC_PINCTL_OUT_EN;
 				pinctl |= AC_PINCTL_IN_EN;
 				stac92xx_auto_set_pinctl(codec, nid, pinctl);
-			} else {
-				pinctl = snd_hda_codec_read(codec, nid, 0,
-					AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
-				/* if PINCTL already set then skip */
-				/* Also, if both INPUT and OUTPUT are set,
-				 * it must be a BIOS bug; need to override, too
-				 */
-				if (!(pinctl & AC_PINCTL_IN_EN) ||
-				    (pinctl & AC_PINCTL_OUT_EN)) {
-					pinctl &= ~AC_PINCTL_OUT_EN;
-					pinctl |= AC_PINCTL_IN_EN;
-					stac92xx_auto_set_pinctl(codec, nid,
-								 pinctl);
-				}
-			}
-			conf = snd_hda_codec_get_pincfg(codec, nid);
-			if (get_defcfg_connect(conf) != AC_JACK_PORT_FIXED) {
-				if (enable_pin_detect(codec, nid,
-						      STAC_INSERT_EVENT))
-					stac_issue_unsol_event(codec, nid);
 			}
 		}
+		conf = snd_hda_codec_get_pincfg(codec, nid);
+		if (get_defcfg_connect(conf) != AC_JACK_PORT_FIXED) {
+			if (enable_pin_detect(codec, nid, STAC_INSERT_EVENT))
+				stac_issue_unsol_event(codec, nid);
+		}
 	}
 	for (i = 0; i < spec->num_dmics; i++)
 		stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i],
@@ -4383,11 +4379,9 @@ static int stac92xx_init(struct hda_codec *codec)
 			stac_issue_unsol_event(codec, nid);
 	}
 
-#ifdef CONFIG_SND_HDA_POWER_SAVE
 	/* sync mute LED */
-	if (spec->gpio_led && codec->patch_ops.check_power_status)
-		codec->patch_ops.check_power_status(codec, 0x01);
-#endif	
+	if (spec->gpio_led)
+		hda_call_check_power_status(codec, 0x01);
 	if (spec->dac_list)
 		stac92xx_power_down(codec);
 	return 0;
@@ -4688,6 +4682,36 @@ static void stac92xx_report_jack(struct hda_codec *codec, hda_nid_t nid)
 	}
 }
 
+/* get the pin connection (fixed, none, etc) */
+static unsigned int stac_get_defcfg_connect(struct hda_codec *codec, int idx)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	unsigned int cfg;
+
+	cfg = snd_hda_codec_get_pincfg(codec, spec->pin_nids[idx]);
+	return get_defcfg_connect(cfg);
+}
+
+static int stac92xx_connected_ports(struct hda_codec *codec,
+					 hda_nid_t *nids, int num_nids)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	int idx, num;
+	unsigned int def_conf;
+
+	for (num = 0; num < num_nids; num++) {
+		for (idx = 0; idx < spec->num_pins; idx++)
+			if (spec->pin_nids[idx] == nids[num])
+				break;
+		if (idx >= spec->num_pins)
+			break;
+		def_conf = stac_get_defcfg_connect(codec, idx);
+		if (def_conf == AC_JACK_PORT_NONE)
+			break;
+	}
+	return num;
+}
+
 static void stac92xx_mic_detect(struct hda_codec *codec)
 {
 	struct sigmatel_spec *spec = codec->spec;
@@ -4695,6 +4719,8 @@ static void stac92xx_mic_detect(struct hda_codec *codec)
 
 	if (get_pin_presence(codec, spec->ext_mic.pin))
 		mic = &spec->ext_mic;
+	else if (get_pin_presence(codec, spec->dock_mic.pin))
+		mic = &spec->dock_mic;
 	else
 		mic = &spec->int_mic;
 	if (mic->dmux_idx >= 0)
@@ -4937,11 +4963,9 @@ static int stac92xx_resume(struct hda_codec *codec)
 			stac_issue_unsol_event(codec,
 					       spec->autocfg.line_out_pins[0]);
 	}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
 	/* sync mute LED */
-	if (spec->gpio_led && codec->patch_ops.check_power_status)
-		codec->patch_ops.check_power_status(codec, 0x01);
-#endif	
+	if (spec->gpio_led)
+		hda_call_check_power_status(codec, 0x01);
 	return 0;
 }
 
@@ -5313,11 +5337,16 @@ static int patch_stac92hd83xxx(struct hda_codec *codec)
 	if (spec == NULL)
 		return -ENOMEM;
 
+	/* reset pin power-down; Windows may leave these bits after reboot */
+	snd_hda_codec_write_cache(codec, codec->afg, 0, 0x7EC, 0);
+	snd_hda_codec_write_cache(codec, codec->afg, 0, 0x7ED, 0);
 	codec->no_trigger_sense = 1;
 	codec->spec = spec;
 	spec->linear_tone_beep = 1;
 	codec->slave_dig_outs = stac92hd83xxx_slave_dig_outs;
 	spec->digbeep_nid = 0x21;
+	spec->dmic_nids = stac92hd83xxx_dmic_nids;
+	spec->dmux_nids = stac92hd83xxx_mux_nids;
 	spec->mux_nids = stac92hd83xxx_mux_nids;
 	spec->num_muxes = ARRAY_SIZE(stac92hd83xxx_mux_nids);
 	spec->adc_nids = stac92hd83xxx_adc_nids;
@@ -5363,9 +5392,13 @@ again:
 	case 0x111d76d4:
 	case 0x111d7605:
 	case 0x111d76d5:
+	case 0x111d76e7:
 		if (spec->board_config == STAC_92HD83XXX_PWR_REF)
 			break;
 		spec->num_pwrs = 0;
+		spec->num_dmics = stac92xx_connected_ports(codec,
+				stac92hd83xxx_dmic_nids,
+				STAC92HD83XXX_NUM_DMICS);
 		break;
 	}
 
@@ -5424,36 +5457,6 @@ again:
 	return 0;
 }
 
-/* get the pin connection (fixed, none, etc) */
-static unsigned int stac_get_defcfg_connect(struct hda_codec *codec, int idx)
-{
-	struct sigmatel_spec *spec = codec->spec;
-	unsigned int cfg;
-
-	cfg = snd_hda_codec_get_pincfg(codec, spec->pin_nids[idx]);
-	return get_defcfg_connect(cfg);
-}
-
-static int stac92hd71bxx_connected_ports(struct hda_codec *codec,
-					 hda_nid_t *nids, int num_nids)
-{
-	struct sigmatel_spec *spec = codec->spec;
-	int idx, num;
-	unsigned int def_conf;
-
-	for (num = 0; num < num_nids; num++) {
-		for (idx = 0; idx < spec->num_pins; idx++)
-			if (spec->pin_nids[idx] == nids[num])
-				break;
-		if (idx >= spec->num_pins)
-			break;
-		def_conf = stac_get_defcfg_connect(codec, idx);
-		if (def_conf == AC_JACK_PORT_NONE)
-			break;
-	}
-	return num;
-}
-
 static int stac92hd71bxx_connected_smuxes(struct hda_codec *codec,
 					  hda_nid_t dig0pin)
 {
@@ -5592,7 +5595,7 @@ again:
 	case 0x111d76b5:
 		spec->init = stac92hd71bxx_core_init;
 		codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
-		spec->num_dmics = stac92hd71bxx_connected_ports(codec,
+		spec->num_dmics = stac92xx_connected_ports(codec,
 					stac92hd71bxx_dmic_nids,
 					STAC92HD71BXX_NUM_DMICS);
 		break;
@@ -5624,7 +5627,7 @@ again:
 		snd_hda_codec_set_pincfg(codec, 0x0f, 0x40f000f0);
 		snd_hda_codec_set_pincfg(codec, 0x19, 0x40f000f3);
 		stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS - 1] = 0;
-		spec->num_dmics = stac92hd71bxx_connected_ports(codec,
+		spec->num_dmics = stac92xx_connected_ports(codec,
 					stac92hd71bxx_dmic_nids,
 					STAC92HD71BXX_NUM_DMICS - 1);
 		break;
@@ -5638,7 +5641,7 @@ again:
 	default:
 		spec->init = stac92hd71bxx_core_init;
 		codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
-		spec->num_dmics = stac92hd71bxx_connected_ports(codec,
+		spec->num_dmics = stac92xx_connected_ports(codec,
 					stac92hd71bxx_dmic_nids,
 					STAC92HD71BXX_NUM_DMICS);
 		break;
@@ -6320,6 +6323,8 @@ static struct hda_codec_preset snd_hda_preset_sigmatel[] = {
 	{ .id = 0x111d76cc, .name = "92HD89F3", .patch = patch_stac92hd73xx },
 	{ .id = 0x111d76cd, .name = "92HD89F2", .patch = patch_stac92hd73xx },
 	{ .id = 0x111d76ce, .name = "92HD89F1", .patch = patch_stac92hd73xx },
+	{ .id = 0x111d76e0, .name = "92HD91BXX", .patch = patch_stac92hd83xxx},
+	{ .id = 0x111d76e7, .name = "92HD90BXX", .patch = patch_stac92hd83xxx},
 	{} /* terminator */
 };
 

+ 152 - 435
sound/pci/hda/patch_via.c

@@ -444,8 +444,8 @@ static hda_nid_t vt1812_adc_nids[2] = {
 
 
 /* add dynamic controls */
-static int via_add_control(struct via_spec *spec, int type, const char *name,
-			   unsigned long val)
+static int __via_add_control(struct via_spec *spec, int type, const char *name,
+			     int idx, unsigned long val)
 {
 	struct snd_kcontrol_new *knew;
 
@@ -463,6 +463,9 @@ static int via_add_control(struct via_spec *spec, int type, const char *name,
 	return 0;
 }
 
+#define via_add_control(spec, type, name, val) \
+	__via_add_control(spec, type, name, 0, val)
+
 static struct snd_kcontrol_new *via_clone_control(struct via_spec *spec,
 						struct snd_kcontrol_new *tmpl)
 {
@@ -494,18 +497,18 @@ static void via_free_kctls(struct hda_codec *codec)
 
 /* create input playback/capture controls for the given pin */
 static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
-				int idx, int mix_nid)
+				int type_idx, int idx, int mix_nid)
 {
 	char name[32];
 	int err;
 
 	sprintf(name, "%s Playback Volume", ctlname);
-	err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
+	err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
 			      HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
 	if (err < 0)
 		return err;
 	sprintf(name, "%s Playback Switch", ctlname);
-	err = via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name,
+	err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
 			      HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
 	if (err < 0)
 		return err;
@@ -557,17 +560,15 @@ static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin);
 static void via_auto_init_analog_input(struct hda_codec *codec)
 {
 	struct via_spec *spec = codec->spec;
+	const struct auto_pin_cfg *cfg = &spec->autocfg;
 	unsigned int ctl;
 	int i;
 
-	for (i = 0; i < AUTO_PIN_LAST; i++) {
-		hda_nid_t nid = spec->autocfg.input_pins[i];
-		if (!nid)
-			continue;
-
+	for (i = 0; i < cfg->num_inputs; i++) {
+		hda_nid_t nid = cfg->inputs[i].pin;
 		if (spec->smart51_enabled && is_smart51_pins(spec, nid))
 			ctl = PIN_OUT;
-		else if (i <= AUTO_PIN_FRONT_MIC)
+		else if (i == AUTO_PIN_MIC)
 			ctl = PIN_VREF50;
 		else
 			ctl = PIN_IN;
@@ -1322,15 +1323,14 @@ static void mute_aa_path(struct hda_codec *codec, int mute)
 }
 static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin)
 {
-	int res = 0;
-	int index;
-	for (index = AUTO_PIN_MIC; index < AUTO_PIN_FRONT_LINE; index++) {
-		if (pin == spec->autocfg.input_pins[index]) {
-			res = 1;
-			break;
-		}
+	const struct auto_pin_cfg *cfg = &spec->autocfg;
+	int i;
+
+	for (i = 0; i < cfg->num_inputs; i++) {
+		if (pin == cfg->inputs[i].pin)
+			return cfg->inputs[i].type <= AUTO_PIN_LINE_IN;
 	}
-	return res;
+	return 0;
 }
 
 static int via_smart51_info(struct snd_kcontrol *kcontrol,
@@ -1348,25 +1348,21 @@ static int via_smart51_get(struct snd_kcontrol *kcontrol,
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct via_spec *spec = codec->spec;
-	int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
+	const struct auto_pin_cfg *cfg = &spec->autocfg;
 	int on = 1;
 	int i;
 
-	for (i = 0; i < ARRAY_SIZE(index); i++) {
-		hda_nid_t nid = spec->autocfg.input_pins[index[i]];
-		if (nid) {
-			int ctl =
-			    snd_hda_codec_read(codec, nid, 0,
-					       AC_VERB_GET_PIN_WIDGET_CONTROL,
-					       0);
-			if (i == AUTO_PIN_FRONT_MIC
-			    && spec->hp_independent_mode
-			    && spec->codec_type != VT1718S)
-				continue; /* ignore FMic for independent HP */
-			if (ctl & AC_PINCTL_IN_EN
-			    && !(ctl & AC_PINCTL_OUT_EN))
-				on = 0;
-		}
+	for (i = 0; i < cfg->num_inputs; i++) {
+		hda_nid_t nid = cfg->inputs[i].pin;
+		int ctl = snd_hda_codec_read(codec, nid, 0,
+					     AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+		if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
+			continue;
+		if (cfg->inputs[i].type == AUTO_PIN_MIC &&
+		    spec->hp_independent_mode && spec->codec_type != VT1718S)
+			continue; /* ignore FMic for independent HP */
+		if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN))
+			on = 0;
 	}
 	*ucontrol->value.integer.value = on;
 	return 0;
@@ -1377,36 +1373,38 @@ static int via_smart51_put(struct snd_kcontrol *kcontrol,
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct via_spec *spec = codec->spec;
+	const struct auto_pin_cfg *cfg = &spec->autocfg;
 	int out_in = *ucontrol->value.integer.value
 		? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
-	int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
 	int i;
 
-	for (i = 0; i < ARRAY_SIZE(index); i++) {
-		hda_nid_t nid = spec->autocfg.input_pins[index[i]];
-		if (i == AUTO_PIN_FRONT_MIC
-		    && spec->hp_independent_mode
-		    && spec->codec_type != VT1718S)
+	for (i = 0; i < cfg->num_inputs; i++) {
+		hda_nid_t nid = cfg->inputs[i].pin;
+		unsigned int parm;
+
+		if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
+			continue;
+		if (cfg->inputs[i].type == AUTO_PIN_MIC &&
+		    spec->hp_independent_mode && spec->codec_type != VT1718S)
 			continue; /* don't retask FMic for independent HP */
-		if (nid) {
-			unsigned int parm = snd_hda_codec_read(
-				codec, nid, 0,
-				AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
-			parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
-			parm |= out_in;
-			snd_hda_codec_write(codec, nid, 0,
-					    AC_VERB_SET_PIN_WIDGET_CONTROL,
-					    parm);
-			if (out_in == AC_PINCTL_OUT_EN) {
-				mute_aa_path(codec, 1);
-				notify_aa_path_ctls(codec);
-			}
-			if (spec->codec_type == VT1718S)
-				snd_hda_codec_amp_stereo(
+
+		parm = snd_hda_codec_read(codec, nid, 0,
+					  AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+		parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
+		parm |= out_in;
+		snd_hda_codec_write(codec, nid, 0,
+				    AC_VERB_SET_PIN_WIDGET_CONTROL,
+				    parm);
+		if (out_in == AC_PINCTL_OUT_EN) {
+			mute_aa_path(codec, 1);
+			notify_aa_path_ctls(codec);
+		}
+		if (spec->codec_type == VT1718S) {
+			snd_hda_codec_amp_stereo(
 					codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE,
 					HDA_AMP_UNMUTE);
 		}
-		if (i == AUTO_PIN_FRONT_MIC) {
+		if (cfg->inputs[i].type == AUTO_PIN_MIC) {
 			if (spec->codec_type == VT1708S
 			    || spec->codec_type == VT1716S) {
 				/* input = index 1 (AOW3) */
@@ -1442,7 +1440,7 @@ static struct snd_kcontrol_new via_smart51_mixer[2] = {
 static int via_smart51_build(struct via_spec *spec)
 {
 	struct snd_kcontrol_new *knew;
-	int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
+	const struct auto_pin_cfg *cfg = &spec->autocfg;
 	hda_nid_t nid;
 	int i;
 
@@ -1450,13 +1448,14 @@ static int via_smart51_build(struct via_spec *spec)
 	if (knew == NULL)
 		return -ENOMEM;
 
-	for (i = 0; i < ARRAY_SIZE(index); i++) {
-		nid = spec->autocfg.input_pins[index[i]];
-		if (nid) {
+	for (i = 0; i < cfg->num_inputs; i++) {
+		nid = cfg->inputs[i].pin;
+		if (cfg->inputs[i].type <= AUTO_PIN_LINE_IN) {
 			knew = via_clone_control(spec, &via_smart51_mixer[1]);
 			if (knew == NULL)
 				return -ENOMEM;
 			knew->subdevice = nid;
+			break;
 		}
 	}
 
@@ -2375,13 +2374,8 @@ static void create_hp_imux(struct via_spec *spec)
 	static const char *texts[] = { "OFF", "ON", NULL};
 
 	/* for hp mode select */
-	i = 0;
-	while (texts[i] != NULL) {
-		imux->items[imux->num_items].label =  texts[i];
-		imux->items[imux->num_items].index = i;
-		imux->num_items++;
-		i++;
-	}
+	for (i = 0; texts[i]; i++)
+		snd_hda_add_imux_item(imux, texts[i], i, NULL);
 
 	spec->hp_mux = &spec->private_imux[1];
 }
@@ -2413,51 +2407,53 @@ static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
 }
 
 /* create playback/capture controls for input pins */
-static int vt1708_auto_create_analog_input_ctls(struct via_spec *spec,
-						const struct auto_pin_cfg *cfg)
+static int vt_auto_create_analog_input_ctls(struct hda_codec *codec,
+					    const struct auto_pin_cfg *cfg,
+					    hda_nid_t cap_nid,
+					    hda_nid_t pin_idxs[], int num_idxs)
 {
-	static char *labels[] = {
-		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
-	};
+	struct via_spec *spec = codec->spec;
 	struct hda_input_mux *imux = &spec->private_imux[0];
-	int i, err, idx = 0;
+	int i, err, idx, type, type_idx = 0;
 
 	/* for internal loopback recording select */
-	imux->items[imux->num_items].label = "Stereo Mixer";
-	imux->items[imux->num_items].index = idx;
-	imux->num_items++;
-
-	for (i = 0; i < AUTO_PIN_LAST; i++) {
-		if (!cfg->input_pins[i])
-			continue;
-
-		switch (cfg->input_pins[i]) {
-		case 0x1d: /* Mic */
-			idx = 2;
-			break;
-
-		case 0x1e: /* Line In */
-			idx = 3;
-			break;
-
-		case 0x21: /* Front Mic */
-			idx = 4;
-			break;
-
-		case 0x24: /* CD */
-			idx = 1;
+	for (idx = 0; idx < num_idxs; idx++) {
+		if (pin_idxs[idx] == 0xff) {
+			snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL);
 			break;
 		}
-		err = via_new_analog_input(spec, labels[i], idx, 0x17);
+	}
+
+	for (i = 0; i < cfg->num_inputs; i++) {
+		const char *label;
+		type = cfg->inputs[i].type;
+		for (idx = 0; idx < num_idxs; idx++)
+			if (pin_idxs[idx] == cfg->inputs[i].pin)
+				break;
+		if (idx >= num_idxs)
+			continue;
+		if (i > 0 && type == cfg->inputs[i - 1].type)
+			type_idx++;
+		else
+			type_idx = 0;
+		label = hda_get_autocfg_input_label(codec, cfg, i);
+		err = via_new_analog_input(spec, label, type_idx, idx, cap_nid);
 		if (err < 0)
 			return err;
-		imux->items[imux->num_items].label = labels[i];
-		imux->items[imux->num_items].index = idx;
-		imux->num_items++;
+		snd_hda_add_imux_item(imux, label, idx, NULL);
 	}
 	return 0;
 }
 
+/* create playback/capture controls for input pins */
+static int vt1708_auto_create_analog_input_ctls(struct hda_codec *codec,
+						const struct auto_pin_cfg *cfg)
+{
+	static hda_nid_t pin_idxs[] = { 0xff, 0x24, 0x1d, 0x1e, 0x21 };
+	return vt_auto_create_analog_input_ctls(codec, cfg, 0x17, pin_idxs,
+						ARRAY_SIZE(pin_idxs));
+}
+
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 static struct hda_amp_list vt1708_loopbacks[] = {
 	{ 0x17, HDA_INPUT, 1 },
@@ -2554,7 +2550,7 @@ static int vt1708_parse_auto_config(struct hda_codec *codec)
 	err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
 	if (err < 0)
 		return err;
-	err = vt1708_auto_create_analog_input_ctls(spec, &spec->autocfg);
+	err = vt1708_auto_create_analog_input_ctls(codec, &spec->autocfg);
 	if (err < 0)
 		return err;
 	/* add jack detect on/off control */
@@ -3021,49 +3017,12 @@ static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
 }
 
 /* create playback/capture controls for input pins */
-static int vt1709_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1709_auto_create_analog_input_ctls(struct hda_codec *codec,
 						const struct auto_pin_cfg *cfg)
 {
-	static char *labels[] = {
-		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
-	};
-	struct hda_input_mux *imux = &spec->private_imux[0];
-	int i, err, idx = 0;
-
-	/* for internal loopback recording select */
-	imux->items[imux->num_items].label = "Stereo Mixer";
-	imux->items[imux->num_items].index = idx;
-	imux->num_items++;
-
-	for (i = 0; i < AUTO_PIN_LAST; i++) {
-		if (!cfg->input_pins[i])
-			continue;
-
-		switch (cfg->input_pins[i]) {
-		case 0x1d: /* Mic */
-			idx = 2;
-			break;
-
-		case 0x1e: /* Line In */
-			idx = 3;
-			break;
-
-		case 0x21: /* Front Mic */
-			idx = 4;
-			break;
-
-		case 0x23: /* CD */
-			idx = 1;
-			break;
-		}
-		err = via_new_analog_input(spec, labels[i], idx, 0x18);
-		if (err < 0)
-			return err;
-		imux->items[imux->num_items].label = labels[i];
-		imux->items[imux->num_items].index = idx;
-		imux->num_items++;
-	}
-	return 0;
+	static hda_nid_t pin_idxs[] = { 0xff, 0x23, 0x1d, 0x1e, 0x21 };
+	return vt_auto_create_analog_input_ctls(codec, cfg, 0x18, pin_idxs,
+						ARRAY_SIZE(pin_idxs));
 }
 
 static int vt1709_parse_auto_config(struct hda_codec *codec)
@@ -3086,7 +3045,7 @@ static int vt1709_parse_auto_config(struct hda_codec *codec)
 	err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
 	if (err < 0)
 		return err;
-	err = vt1709_auto_create_analog_input_ctls(spec, &spec->autocfg);
+	err = vt1709_auto_create_analog_input_ctls(codec, &spec->autocfg);
 	if (err < 0)
 		return err;
 
@@ -3588,49 +3547,12 @@ static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
 }
 
 /* create playback/capture controls for input pins */
-static int vt1708B_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1708B_auto_create_analog_input_ctls(struct hda_codec *codec,
 						const struct auto_pin_cfg *cfg)
 {
-	static char *labels[] = {
-		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
-	};
-	struct hda_input_mux *imux = &spec->private_imux[0];
-	int i, err, idx = 0;
-
-	/* for internal loopback recording select */
-	imux->items[imux->num_items].label = "Stereo Mixer";
-	imux->items[imux->num_items].index = idx;
-	imux->num_items++;
-
-	for (i = 0; i < AUTO_PIN_LAST; i++) {
-		if (!cfg->input_pins[i])
-			continue;
-
-		switch (cfg->input_pins[i]) {
-		case 0x1a: /* Mic */
-			idx = 2;
-			break;
-
-		case 0x1b: /* Line In */
-			idx = 3;
-			break;
-
-		case 0x1e: /* Front Mic */
-			idx = 4;
-			break;
-
-		case 0x1f: /* CD */
-			idx = 1;
-			break;
-		}
-		err = via_new_analog_input(spec, labels[i], idx, 0x16);
-		if (err < 0)
-			return err;
-		imux->items[imux->num_items].label = labels[i];
-		imux->items[imux->num_items].index = idx;
-		imux->num_items++;
-	}
-	return 0;
+	static hda_nid_t pin_idxs[] = { 0xff, 0x1f, 0x1a, 0x1b, 0x1e };
+	return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
+						ARRAY_SIZE(pin_idxs));
 }
 
 static int vt1708B_parse_auto_config(struct hda_codec *codec)
@@ -3653,7 +3575,7 @@ static int vt1708B_parse_auto_config(struct hda_codec *codec)
 	err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
 	if (err < 0)
 		return err;
-	err = vt1708B_auto_create_analog_input_ctls(spec, &spec->autocfg);
+	err = vt1708B_auto_create_analog_input_ctls(codec, &spec->autocfg);
 	if (err < 0)
 		return err;
 
@@ -4061,49 +3983,12 @@ static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
 }
 
 /* create playback/capture controls for input pins */
-static int vt1708S_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1708S_auto_create_analog_input_ctls(struct hda_codec *codec,
 						const struct auto_pin_cfg *cfg)
 {
-	static char *labels[] = {
-		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
-	};
-	struct hda_input_mux *imux = &spec->private_imux[0];
-	int i, err, idx = 0;
-
-	/* for internal loopback recording select */
-	imux->items[imux->num_items].label = "Stereo Mixer";
-	imux->items[imux->num_items].index = 5;
-	imux->num_items++;
-
-	for (i = 0; i < AUTO_PIN_LAST; i++) {
-		if (!cfg->input_pins[i])
-			continue;
-
-		switch (cfg->input_pins[i]) {
-		case 0x1a: /* Mic */
-			idx = 2;
-			break;
-
-		case 0x1b: /* Line In */
-			idx = 3;
-			break;
-
-		case 0x1e: /* Front Mic */
-			idx = 4;
-			break;
-
-		case 0x1f: /* CD */
-			idx = 1;
-			break;
-		}
-		err = via_new_analog_input(spec, labels[i], idx, 0x16);
-		if (err < 0)
-			return err;
-		imux->items[imux->num_items].label = labels[i];
-		imux->items[imux->num_items].index = idx-1;
-		imux->num_items++;
-	}
-	return 0;
+	static hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff };
+	return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
+						ARRAY_SIZE(pin_idxs));
 }
 
 /* fill out digital output widgets; one for master and one for slave outputs */
@@ -4151,7 +4036,7 @@ static int vt1708S_parse_auto_config(struct hda_codec *codec)
 	err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
 	if (err < 0)
 		return err;
-	err = vt1708S_auto_create_analog_input_ctls(spec, &spec->autocfg);
+	err = vt1708S_auto_create_analog_input_ctls(codec, &spec->autocfg);
 	if (err < 0)
 		return err;
 
@@ -4441,58 +4326,20 @@ static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
 	imux = &spec->private_imux[1];
 
 	/* for hp mode select */
-	i = 0;
-	while (texts[i] != NULL)	{
-		imux->items[imux->num_items].label =  texts[i];
-		imux->items[imux->num_items].index = i;
-		imux->num_items++;
-		i++;
-	}
+	for (i = 0; texts[i]; i++)
+		snd_hda_add_imux_item(imux, texts[i], i, NULL);
 
 	spec->hp_mux = &spec->private_imux[1];
 	return 0;
 }
 
 /* create playback/capture controls for input pins */
-static int vt1702_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1702_auto_create_analog_input_ctls(struct hda_codec *codec,
 						const struct auto_pin_cfg *cfg)
 {
-	static char *labels[] = {
-		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
-	};
-	struct hda_input_mux *imux = &spec->private_imux[0];
-	int i, err, idx = 0;
-
-	/* for internal loopback recording select */
-	imux->items[imux->num_items].label = "Stereo Mixer";
-	imux->items[imux->num_items].index = 3;
-	imux->num_items++;
-
-	for (i = 0; i < AUTO_PIN_LAST; i++) {
-		if (!cfg->input_pins[i])
-			continue;
-
-		switch (cfg->input_pins[i]) {
-		case 0x14: /* Mic */
-			idx = 1;
-			break;
-
-		case 0x15: /* Line In */
-			idx = 2;
-			break;
-
-		case 0x18: /* Front Mic */
-			idx = 3;
-			break;
-		}
-		err = via_new_analog_input(spec, labels[i], idx, 0x1A);
-		if (err < 0)
-			return err;
-		imux->items[imux->num_items].label = labels[i];
-		imux->items[imux->num_items].index = idx-1;
-		imux->num_items++;
-	}
-	return 0;
+	static hda_nid_t pin_idxs[] = { 0x14, 0x15, 0x18, 0xff };
+	return vt_auto_create_analog_input_ctls(codec, cfg, 0x1a, pin_idxs,
+						ARRAY_SIZE(pin_idxs));
 }
 
 static int vt1702_parse_auto_config(struct hda_codec *codec)
@@ -4521,7 +4368,7 @@ static int vt1702_parse_auto_config(struct hda_codec *codec)
 				  (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
 				  (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
 				  (1 << AC_AMPCAP_MUTE_SHIFT));
-	err = vt1702_auto_create_analog_input_ctls(spec, &spec->autocfg);
+	err = vt1702_auto_create_analog_input_ctls(codec, &spec->autocfg);
 	if (err < 0)
 		return err;
 
@@ -4872,49 +4719,12 @@ static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
 }
 
 /* create playback/capture controls for input pins */
-static int vt1718S_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1718S_auto_create_analog_input_ctls(struct hda_codec *codec,
 						const struct auto_pin_cfg *cfg)
 {
-	static char *labels[] = {
-		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
-	};
-	struct hda_input_mux *imux = &spec->private_imux[0];
-	int i, err, idx = 0;
-
-	/* for internal loopback recording select */
-	imux->items[imux->num_items].label = "Stereo Mixer";
-	imux->items[imux->num_items].index = 5;
-	imux->num_items++;
-
-	for (i = 0; i < AUTO_PIN_LAST; i++) {
-		if (!cfg->input_pins[i])
-			continue;
-
-		switch (cfg->input_pins[i]) {
-		case 0x2b: /* Mic */
-			idx = 1;
-			break;
-
-		case 0x2a: /* Line In */
-			idx = 2;
-			break;
-
-		case 0x29: /* Front Mic */
-			idx = 3;
-			break;
-
-		case 0x2c: /* CD */
-			idx = 0;
-			break;
-		}
-		err = via_new_analog_input(spec, labels[i], idx, 0x21);
-		if (err < 0)
-			return err;
-		imux->items[imux->num_items].label = labels[i];
-		imux->items[imux->num_items].index = idx;
-		imux->num_items++;
-	}
-	return 0;
+	static hda_nid_t pin_idxs[] = { 0x2c, 0x2b, 0x2a, 0x29, 0, 0xff };
+	return vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
+						ARRAY_SIZE(pin_idxs));
 }
 
 static int vt1718S_parse_auto_config(struct hda_codec *codec)
@@ -4938,7 +4748,7 @@ static int vt1718S_parse_auto_config(struct hda_codec *codec)
 	err = vt1718S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
 	if (err < 0)
 		return err;
-	err = vt1718S_auto_create_analog_input_ctls(spec, &spec->autocfg);
+	err = vt1718S_auto_create_analog_input_ctls(codec, &spec->autocfg);
 	if (err < 0)
 		return err;
 
@@ -5371,49 +5181,12 @@ static int vt1716S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
 }
 
 /* create playback/capture controls for input pins */
-static int vt1716S_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1716S_auto_create_analog_input_ctls(struct hda_codec *codec,
 						const struct auto_pin_cfg *cfg)
 {
-	static char *labels[] = {
-		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
-	};
-	struct hda_input_mux *imux = &spec->private_imux[0];
-	int i, err, idx = 0;
-
-	/* for internal loopback recording select */
-	imux->items[imux->num_items].label = "Stereo Mixer";
-	imux->items[imux->num_items].index = 5;
-	imux->num_items++;
-
-	for (i = 0; i < AUTO_PIN_LAST; i++) {
-		if (!cfg->input_pins[i])
-			continue;
-
-		switch (cfg->input_pins[i]) {
-		case 0x1a: /* Mic */
-			idx = 2;
-			break;
-
-		case 0x1b: /* Line In */
-			idx = 3;
-			break;
-
-		case 0x1e: /* Front Mic */
-			idx = 4;
-			break;
-
-		case 0x1f: /* CD */
-			idx = 1;
-			break;
-		}
-		err = via_new_analog_input(spec, labels[i], idx, 0x16);
-		if (err < 0)
-			return err;
-		imux->items[imux->num_items].label = labels[i];
-		imux->items[imux->num_items].index = idx-1;
-		imux->num_items++;
-	}
-	return 0;
+	static hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff };
+	return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
+						ARRAY_SIZE(pin_idxs));
 }
 
 static int vt1716S_parse_auto_config(struct hda_codec *codec)
@@ -5436,7 +5209,7 @@ static int vt1716S_parse_auto_config(struct hda_codec *codec)
 	err = vt1716S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
 	if (err < 0)
 		return err;
-	err = vt1716S_auto_create_analog_input_ctls(spec, &spec->autocfg);
+	err = vt1716S_auto_create_analog_input_ctls(codec, &spec->autocfg);
 	if (err < 0)
 		return err;
 
@@ -5717,54 +5490,25 @@ static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
 }
 
 /* create playback/capture controls for input pins */
-static int vt2002P_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt2002P_auto_create_analog_input_ctls(struct hda_codec *codec,
 						const struct auto_pin_cfg *cfg)
 {
-	static char *labels[] = {
-		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
-	};
+	struct via_spec *spec = codec->spec;
 	struct hda_input_mux *imux = &spec->private_imux[0];
-	int i, err, idx = 0;
-
-	for (i = 0; i < AUTO_PIN_LAST; i++) {
-		if (!cfg->input_pins[i])
-			continue;
-
-		switch (cfg->input_pins[i]) {
-		case 0x2b: /* Mic */
-			idx = 0;
-			break;
-
-		case 0x2a: /* Line In */
-			idx = 1;
-			break;
-
-		case 0x29: /* Front Mic */
-			idx = 2;
-			break;
-		}
-		err = via_new_analog_input(spec, labels[i], idx, 0x21);
-		if (err < 0)
-			return err;
-		imux->items[imux->num_items].label = labels[i];
-		imux->items[imux->num_items].index = idx;
-		imux->num_items++;
-	}
+	static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0xff };
+	int err;
 
+	err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
+					       ARRAY_SIZE(pin_idxs));
+	if (err < 0)
+		return err;
 	/* build volume/mute control of loopback */
-	err = via_new_analog_input(spec, "Stereo Mixer", 3, 0x21);
+	err = via_new_analog_input(spec, "Stereo Mixer", 0, 3, 0x21);
 	if (err < 0)
 		return err;
 
-	/* for internal loopback recording select */
-	imux->items[imux->num_items].label = "Stereo Mixer";
-	imux->items[imux->num_items].index = 3;
-	imux->num_items++;
-
 	/* for digital mic select */
-	imux->items[imux->num_items].label = "Digital Mic";
-	imux->items[imux->num_items].index = 4;
-	imux->num_items++;
+	snd_hda_add_imux_item(imux, "Digital Mic", 4, NULL);
 
 	return 0;
 }
@@ -5792,7 +5536,7 @@ static int vt2002P_parse_auto_config(struct hda_codec *codec)
 	err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
 	if (err < 0)
 		return err;
-	err = vt2002P_auto_create_analog_input_ctls(spec, &spec->autocfg);
+	err = vt2002P_auto_create_analog_input_ctls(codec, &spec->autocfg);
 	if (err < 0)
 		return err;
 
@@ -6067,53 +5811,26 @@ static int vt1812_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
 }
 
 /* create playback/capture controls for input pins */
-static int vt1812_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1812_auto_create_analog_input_ctls(struct hda_codec *codec,
 						const struct auto_pin_cfg *cfg)
 {
-	static char *labels[] = {
-		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
-	};
+	struct via_spec *spec = codec->spec;
 	struct hda_input_mux *imux = &spec->private_imux[0];
-	int i, err, idx = 0;
-
-	for (i = 0; i < AUTO_PIN_LAST; i++) {
-		if (!cfg->input_pins[i])
-			continue;
-
-		switch (cfg->input_pins[i]) {
-		case 0x2b: /* Mic */
-			idx = 0;
-			break;
+	static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0, 0, 0xff };
+	int err;
 
-		case 0x2a: /* Line In */
-			idx = 1;
-			break;
+	err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
+					       ARRAY_SIZE(pin_idxs));
+	if (err < 0)
+		return err;
 
-		case 0x29: /* Front Mic */
-			idx = 2;
-			break;
-		}
-		err = via_new_analog_input(spec, labels[i], idx, 0x21);
-		if (err < 0)
-			return err;
-		imux->items[imux->num_items].label = labels[i];
-		imux->items[imux->num_items].index = idx;
-		imux->num_items++;
-	}
 	/* build volume/mute control of loopback */
-	err = via_new_analog_input(spec, "Stereo Mixer", 5, 0x21);
+	err = via_new_analog_input(spec, "Stereo Mixer", 0, 5, 0x21);
 	if (err < 0)
 		return err;
 
-	/* for internal loopback recording select */
-	imux->items[imux->num_items].label = "Stereo Mixer";
-	imux->items[imux->num_items].index = 5;
-	imux->num_items++;
-
 	/* for digital mic select */
-	imux->items[imux->num_items].label = "Digital Mic";
-	imux->items[imux->num_items].index = 6;
-	imux->num_items++;
+	snd_hda_add_imux_item(imux, "Digital Mic", 6, NULL);
 
 	return 0;
 }
@@ -6141,7 +5858,7 @@ static int vt1812_parse_auto_config(struct hda_codec *codec)
 	err = vt1812_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
 	if (err < 0)
 		return err;
-	err = vt1812_auto_create_analog_input_ctls(spec, &spec->autocfg);
+	err = vt1812_auto_create_analog_input_ctls(codec, &spec->autocfg);
 	if (err < 0)
 		return err;
 

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác