ALSA: hda - More flexible dynamic-ADC switching for Realtek codecs

This patch changes the auto-parser and the auto-mic handling codes to
allow more flexible dynamic ADC-switching with Realtek codecs.

In the new code, the following strategy is taken:

- When a cap-src can't handle all input-sources, either skip it, or
  switch to the ADC-switching mode.  In ADC-switching mode, like the
  former dual-ADC mode for ALC275, it changes ADC on the fly according
  to the current input source.
- When auto-mic is possible, always assign imux.  If the mic pins are
  set statically via a quirk, rebuild imux according to the pins.
  In the auto-mic mode, the driver always changes the imux (although
  the imux isn't exposed as a mixer element).

Signed-off-by: Takashi Iwai <tiwai@suse.de>
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 53188c4..42026f4 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -276,14 +276,6 @@
 	ALC_INIT_GPIO3,
 };
 
-struct alc_mic_route {
-	hda_nid_t pin;
-	unsigned char mux_idx;
-	unsigned char amix_idx;
-};
-
-#define MUX_IDX_UNDEF	((unsigned char)-1)
-
 struct alc_customize_define {
 	unsigned int  sku_cfg;
 	unsigned char port_connectivity;
@@ -351,7 +343,6 @@
 	hda_nid_t mixer_nid;		/* analog-mixer NID */
 
 	/* capture setup for dynamic dual-adc switch */
-	unsigned int cur_adc_idx;
 	hda_nid_t cur_adc;
 	unsigned int cur_adc_stream_tag;
 	unsigned int cur_adc_format;
@@ -360,9 +351,9 @@
 	unsigned int num_mux_defs;
 	const struct hda_input_mux *input_mux;
 	unsigned int cur_mux[3];
-	struct alc_mic_route ext_mic;
-	struct alc_mic_route dock_mic;
-	struct alc_mic_route int_mic;
+	hda_nid_t ext_mic_pin;
+	hda_nid_t dock_mic_pin;
+	hda_nid_t int_mic_pin;
 
 	/* channel model */
 	const struct hda_channel_mode *channel_mode;
@@ -382,6 +373,9 @@
 	hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
 	hda_nid_t private_adc_nids[AUTO_CFG_MAX_OUTS];
 	hda_nid_t private_capsrc_nids[AUTO_CFG_MAX_OUTS];
+	hda_nid_t imux_pins[HDA_MAX_NUM_INPUTS];
+	unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS];
+	int int_mic_idx, ext_mic_idx, dock_mic_idx; /* for auto-mic */
 
 	/* hooks */
 	void (*init_hook)(struct hda_codec *codec);
@@ -396,6 +390,7 @@
 	unsigned int line_jack_present:1;
 	unsigned int master_mute:1;
 	unsigned int auto_mic:1;
+	unsigned int auto_mic_valid_imux:1;	/* valid imux for auto-mic */
 	unsigned int automute:1;	/* HP automute enabled */
 	unsigned int detect_line:1;	/* Line-out detection enabled */
 	unsigned int automute_lines:1;	/* automute line-out as well */
@@ -403,7 +398,7 @@
 
 	/* other flags */
 	unsigned int no_analog :1; /* digital I/O only */
-	unsigned int dual_adc_switch:1; /* switch ADCs (for ALC275) */
+	unsigned int dyn_adc_switch:1; /* switch ADCs (for ALC275) */
 	unsigned int single_input_src:1;
 	unsigned int vol_in_capsrc:1; /* use capsrc volume (ADC has no vol) */
 
@@ -495,47 +490,81 @@
 	return 0;
 }
 
-static int alc_mux_enum_put(struct snd_kcontrol *kcontrol,
-			    struct snd_ctl_elem_value *ucontrol)
+static bool alc_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
 {
-	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct alc_spec *spec = codec->spec;
+	hda_nid_t new_adc = spec->adc_nids[spec->dyn_adc_idx[cur]];
+
+	if (spec->cur_adc && spec->cur_adc != new_adc) {
+		/* stream is running, let's swap the current ADC */
+		__snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
+		spec->cur_adc = new_adc;
+		snd_hda_codec_setup_stream(codec, new_adc,
+					   spec->cur_adc_stream_tag, 0,
+					   spec->cur_adc_format);
+		return true;
+	}
+	return false;
+}
+
+/* select the given imux item; either unmute exclusively or select the route */
+static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx,
+			  unsigned int idx, bool force)
+{
 	struct alc_spec *spec = codec->spec;
 	const struct hda_input_mux *imux;
-	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 	unsigned int mux_idx;
-	hda_nid_t nid = spec->capsrc_nids ?
-		spec->capsrc_nids[adc_idx] : spec->adc_nids[adc_idx];
-	unsigned int type;
+	int i, type;
+	hda_nid_t nid;
 
 	mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx;
 	imux = &spec->input_mux[mux_idx];
 	if (!imux->num_items && mux_idx > 0)
 		imux = &spec->input_mux[0];
 
+	if (idx >= imux->num_items)
+		idx = imux->num_items - 1;
+	if (spec->cur_mux[adc_idx] == idx && !force)
+		return 0;
+	spec->cur_mux[adc_idx] = idx;
+
+	if (spec->dyn_adc_switch) {
+		alc_dyn_adc_pcm_resetup(codec, idx);
+		adc_idx = spec->dyn_adc_idx[idx];
+	}
+
+	nid = spec->capsrc_nids ?
+		spec->capsrc_nids[adc_idx] : spec->adc_nids[adc_idx];
+
+	/* no selection? */
+	if (snd_hda_get_conn_list(codec, nid, NULL) <= 1)
+		return 1;
+
 	type = get_wcaps_type(get_wcaps(codec, nid));
 	if (type == AC_WID_AUD_MIX) {
 		/* Matrix-mixer style (e.g. ALC882) */
-		unsigned int *cur_val = &spec->cur_mux[adc_idx];
-		unsigned int i, idx;
-
-		idx = ucontrol->value.enumerated.item[0];
-		if (idx >= imux->num_items)
-			idx = imux->num_items - 1;
-		if (*cur_val == idx)
-			return 0;
 		for (i = 0; i < imux->num_items; i++) {
 			unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE;
 			snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT,
 						 imux->items[i].index,
 						 HDA_AMP_MUTE, v);
 		}
-		*cur_val = idx;
-		return 1;
 	} else {
 		/* MUX style (e.g. ALC880) */
-		return snd_hda_input_mux_put(codec, imux, ucontrol, nid,
-					     &spec->cur_mux[adc_idx]);
+		snd_hda_codec_write_cache(codec, nid, 0,
+					  AC_VERB_SET_CONNECT_SEL,
+					  imux->items[idx].index);
 	}
+	return 1;
+}
+
+static int alc_mux_enum_put(struct snd_kcontrol *kcontrol,
+			    struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+	return alc_mux_select(codec, adc_idx,
+			      ucontrol->value.enumerated.item[0], false);
 }
 
 /*
@@ -1059,8 +1088,8 @@
 	struct alc_spec *spec = codec->spec;
 	int err;
 	unsigned int hp_nid = spec->autocfg.hp_pins[0];
-	unsigned int mic_nid = spec->ext_mic.pin;
-	unsigned int dock_nid = spec->dock_mic.pin;
+	unsigned int mic_nid = spec->ext_mic_pin;
+	unsigned int dock_nid = spec->dock_mic_pin;
 
 	if (hp_nid) {
 		err = snd_hda_input_jack_add(codec, hp_nid,
@@ -1199,93 +1228,29 @@
 #define get_connection_index(codec, mux, nid) \
 	snd_hda_get_conn_index(codec, mux, nid, 0)
 
-/* switch the current ADC according to the jack state */
-static void alc_dual_mic_adc_auto_switch(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	unsigned int present;
-	hda_nid_t new_adc;
-
-	present = snd_hda_jack_detect(codec, spec->ext_mic.pin);
-	if (present)
-		spec->cur_adc_idx = 1;
-	else
-		spec->cur_adc_idx = 0;
-	new_adc = spec->adc_nids[spec->cur_adc_idx];
-	if (spec->cur_adc && spec->cur_adc != new_adc) {
-		/* stream is running, let's swap the current ADC */
-		__snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
-		spec->cur_adc = new_adc;
-		snd_hda_codec_setup_stream(codec, new_adc,
-					   spec->cur_adc_stream_tag, 0,
-					   spec->cur_adc_format);
-	}
-}
-
 static void alc_mic_automute(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
-	struct alc_mic_route *dead1, *dead2, *alive;
-	unsigned int present, type;
-	hda_nid_t cap_nid;
+	hda_nid_t *pins = spec->imux_pins;
 
-	if (!spec->auto_mic)
-		return;
-	if (!spec->int_mic.pin || !spec->ext_mic.pin)
+	if (!spec->auto_mic || !spec->auto_mic_valid_imux)
 		return;
 	if (snd_BUG_ON(!spec->adc_nids))
 		return;
-
-	if (spec->dual_adc_switch) {
-		alc_dual_mic_adc_auto_switch(codec);
+	if (snd_BUG_ON(spec->int_mic_idx < 0 || spec->ext_mic_idx < 0))
 		return;
-	}
 
-	cap_nid = spec->capsrc_nids ? spec->capsrc_nids[0] : spec->adc_nids[0];
+	if (snd_hda_jack_detect(codec, pins[spec->ext_mic_idx]))
+		alc_mux_select(codec, 0, spec->ext_mic_idx, false);
+	else if (spec->dock_mic_idx >= 0 &&
+		   snd_hda_jack_detect(codec, pins[spec->dock_mic_idx]))
+		alc_mux_select(codec, 0, spec->dock_mic_idx, false);
+	else
+		alc_mux_select(codec, 0, spec->int_mic_idx, false);
 
-	alive = &spec->int_mic;
-	dead1 = &spec->ext_mic;
-	dead2 = &spec->dock_mic;
-
-	present = snd_hda_jack_detect(codec, spec->ext_mic.pin);
-	if (present) {
-		alive = &spec->ext_mic;
-		dead1 = &spec->int_mic;
-		dead2 = &spec->dock_mic;
-	}
-	if (!present && spec->dock_mic.pin > 0) {
-		present = snd_hda_jack_detect(codec, spec->dock_mic.pin);
-		if (present) {
-			alive = &spec->dock_mic;
-			dead1 = &spec->int_mic;
-			dead2 = &spec->ext_mic;
-		}
-		snd_hda_input_jack_report(codec, spec->dock_mic.pin);
-	}
-
-	type = get_wcaps_type(get_wcaps(codec, cap_nid));
-	if (type == AC_WID_AUD_MIX) {
-		/* Matrix-mixer style (e.g. ALC882) */
-		snd_hda_codec_amp_stereo(codec, cap_nid, HDA_INPUT,
-					 alive->mux_idx,
-					 HDA_AMP_MUTE, 0);
-		if (dead1->pin > 0)
-			snd_hda_codec_amp_stereo(codec, cap_nid, HDA_INPUT,
-						 dead1->mux_idx,
-						 HDA_AMP_MUTE, HDA_AMP_MUTE);
-		if (dead2->pin > 0)
-			snd_hda_codec_amp_stereo(codec, cap_nid, HDA_INPUT,
-						 dead2->mux_idx,
-						 HDA_AMP_MUTE, HDA_AMP_MUTE);
-	} else {
-		/* MUX style (e.g. ALC880) */
-		snd_hda_codec_write_cache(codec, cap_nid, 0,
-					  AC_VERB_SET_CONNECT_SEL,
-					  alive->mux_idx);
-	}
-	snd_hda_input_jack_report(codec, spec->ext_mic.pin);
-
-	/* FIXME: analog mixer */
+	snd_hda_input_jack_report(codec, pins[spec->ext_mic_idx]);
+	if (spec->dock_mic_idx >= 0)
+		snd_hda_input_jack_report(codec, pins[spec->dock_mic_idx]);
 }
 
 /* unsolicited event for HP jack sensing */
@@ -1602,6 +1567,87 @@
 	}
 }
 
+static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
+{
+	int i;
+	for (i = 0; i < nums; i++)
+		if (list[i] == nid)
+			return i;
+	return -1;
+}
+
+static bool alc_check_dyn_adc_switch(struct hda_codec *codec);
+
+/* rebuild imux for matching with the given auto-mic pins (if not yet) */
+static bool alc_rebuild_imux_for_auto_mic(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	struct hda_input_mux *imux;
+	static char * const texts[3] = {
+		"Mic", "Internal Mic", "Dock Mic"
+	};
+	int i;
+
+	if (!spec->auto_mic)
+		return false;
+	imux = &spec->private_imux[0];
+	if (spec->input_mux == imux)
+		return true;
+	spec->imux_pins[0] = spec->ext_mic_pin;
+	spec->imux_pins[1] = spec->int_mic_pin;
+	spec->imux_pins[2] = spec->dock_mic_pin;
+	for (i = 0; i < 3; i++) {
+		strcpy(imux->items[i].label, texts[i]);
+		if (spec->imux_pins[i])
+			imux->num_items = i + 1;
+	}
+	spec->num_mux_defs = 1;
+	spec->input_mux = imux;
+	return true;
+}
+
+/* check whether all auto-mic pins are valid; setup indices if OK */
+static bool alc_auto_mic_check_imux(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	const struct hda_input_mux *imux;
+
+	if (!spec->auto_mic)
+		return false;
+	if (spec->auto_mic_valid_imux)
+		return true; /* already checked */
+
+	/* fill up imux indices */
+	if (!alc_check_dyn_adc_switch(codec)) {
+		spec->auto_mic = 0;
+		return false;
+	}
+
+	imux = spec->input_mux;
+	spec->ext_mic_idx = find_idx_in_nid_list(spec->ext_mic_pin,
+					spec->imux_pins, imux->num_items);
+	spec->int_mic_idx = find_idx_in_nid_list(spec->int_mic_pin,
+					spec->imux_pins, imux->num_items);
+	spec->dock_mic_idx = find_idx_in_nid_list(spec->dock_mic_pin,
+					spec->imux_pins, imux->num_items);
+	if (spec->ext_mic_idx < 0 || spec->int_mic_idx < 0) {
+		spec->auto_mic = 0;
+		return false; /* no corresponding imux */
+	}
+
+	snd_hda_codec_write_cache(codec, spec->ext_mic_pin, 0,
+				  AC_VERB_SET_UNSOLICITED_ENABLE,
+				  AC_USRSP_EN | ALC880_MIC_EVENT);
+	if (spec->dock_mic_pin)
+		snd_hda_codec_write_cache(codec, spec->dock_mic_pin, 0,
+				  AC_VERB_SET_UNSOLICITED_ENABLE,
+				  AC_USRSP_EN | ALC880_MIC_EVENT);
+
+	spec->auto_mic_valid_imux = 1;
+	spec->auto_mic = 1;
+	return true;
+}
+
 static void alc_init_auto_mic(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
@@ -1609,6 +1655,8 @@
 	hda_nid_t fixed, ext, dock;
 	int i;
 
+	spec->ext_mic_idx = spec->int_mic_idx = spec->dock_mic_idx = -1;
+
 	fixed = ext = dock = 0;
 	for (i = 0; i < cfg->num_inputs; i++) {
 		hda_nid_t nid = cfg->inputs[i].pin;
@@ -1650,18 +1698,18 @@
 		return; /* no unsol support */
 	if (dock && !is_jack_detectable(codec, dock))
 		return; /* no unsol support */
+
+	/* check imux indices */
+	spec->ext_mic_pin = ext;
+	spec->int_mic_pin = fixed;
+	spec->dock_mic_pin = dock;
+
+	spec->auto_mic = 1;
+	if (!alc_auto_mic_check_imux(codec))
+		return;
+
 	snd_printdd("realtek: Enable auto-mic switch on NID 0x%x/0x%x/0x%x\n",
 		    ext, fixed, dock);
-	spec->ext_mic.pin = ext;
-	spec->dock_mic.pin = dock;
-	spec->int_mic.pin = fixed;
-	spec->ext_mic.mux_idx = MUX_IDX_UNDEF; /* set later */
-	spec->dock_mic.mux_idx = MUX_IDX_UNDEF; /* set later */
-	spec->int_mic.mux_idx = MUX_IDX_UNDEF; /* set later */
-	spec->auto_mic = 1;
-	snd_hda_codec_write_cache(codec, spec->ext_mic.pin, 0,
-				  AC_VERB_SET_UNSOLICITED_ENABLE,
-				  AC_USRSP_EN | ALC880_MIC_EVENT);
 	spec->unsol_event = alc_sku_unsol_event;
 }
 
@@ -1737,11 +1785,7 @@
 
 static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
 {
-	int i;
-	for (i = 0; i < nums; i++)
-		if (list[i] == nid)
-			return true;
-	return false;
+	return find_idx_in_nid_list(nid, list, nums) >= 0;
 }
 
 /* check subsystem ID and set up device-specific initialization;
@@ -1871,7 +1915,11 @@
 			   "Enable default setup for auto mode as fallback\n");
 		spec->init_amp = ALC_INIT_DEFAULT;
 	}
+}
 
+/* check the availabilities of auto-mute and auto-mic switches */
+static void alc_auto_check_switches(struct hda_codec *codec)
+{
 	alc_init_auto_hp(codec);
 	alc_init_auto_mic(codec);
 }
@@ -2722,10 +2770,10 @@
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct alc_spec *spec = codec->spec;
-	int i, err;
+	int i, err = 0;
 
 	mutex_lock(&codec->control_mutex);
-	if (check_adc_switch && spec->dual_adc_switch) {
+	if (check_adc_switch && spec->dyn_adc_switch) {
 		for (i = 0; i < spec->num_adc_nids; i++) {
 			kcontrol->private_value =
 				HDA_COMPOSE_AMP_VAL(spec->adc_nids[i],
@@ -2742,8 +2790,8 @@
 						    3, 0, HDA_OUTPUT);
 		else
 			kcontrol->private_value =
-				val = HDA_COMPOSE_AMP_VAL(spec->adc_nids[i],
-							  3, 0, HDA_INPUT);
+				HDA_COMPOSE_AMP_VAL(spec->adc_nids[i],
+						    3, 0, HDA_INPUT);
 		err = func(kcontrol, ucontrol);
 	}
  error:
@@ -4299,21 +4347,21 @@
 }
 
 /* analog capture with dynamic dual-adc changes */
-static int dualmic_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+static int dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
 				       struct hda_codec *codec,
 				       unsigned int stream_tag,
 				       unsigned int format,
 				       struct snd_pcm_substream *substream)
 {
 	struct alc_spec *spec = codec->spec;
-	spec->cur_adc = spec->adc_nids[spec->cur_adc_idx];
+	spec->cur_adc = spec->adc_nids[spec->dyn_adc_idx[spec->cur_mux[0]]];
 	spec->cur_adc_stream_tag = stream_tag;
 	spec->cur_adc_format = format;
 	snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
 	return 0;
 }
 
-static int dualmic_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+static int dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
 				       struct hda_codec *codec,
 				       struct snd_pcm_substream *substream)
 {
@@ -4323,14 +4371,14 @@
 	return 0;
 }
 
-static const struct hda_pcm_stream dualmic_pcm_analog_capture = {
+static const struct hda_pcm_stream dyn_adc_pcm_analog_capture = {
 	.substreams = 1,
 	.channels_min = 2,
 	.channels_max = 2,
 	.nid = 0, /* fill later */
 	.ops = {
-		.prepare = dualmic_capture_pcm_prepare,
-		.cleanup = dualmic_capture_pcm_cleanup
+		.prepare = dyn_adc_capture_pcm_prepare,
+		.cleanup = dyn_adc_capture_pcm_cleanup
 	},
 };
 
@@ -4426,8 +4474,12 @@
 	}
 	if (spec->adc_nids) {
 		p = spec->stream_analog_capture;
-		if (!p)
-			p = &alc_pcm_analog_capture;
+		if (!p) {
+			if (spec->dyn_adc_switch)
+				p = &dyn_adc_pcm_analog_capture;
+			else
+				p = &alc_pcm_analog_capture;
+		}
 		info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p;
 		info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
 	}
@@ -5452,8 +5504,7 @@
 			break;
 	}
 	spec->adc_nids = spec->private_adc_nids;
-	if (indep_capsrc)
-		spec->capsrc_nids = spec->private_capsrc_nids;
+	spec->capsrc_nids = spec->private_capsrc_nids;
 	spec->num_adc_nids = nums;
 	return nums;
 }
@@ -5504,11 +5555,16 @@
 				spec->capsrc_nids[c] : spec->adc_nids[c];
 			idx = get_connection_index(codec, cap, pin);
 			if (idx >= 0) {
+				spec->imux_pins[imux->num_items] = pin;
 				snd_hda_add_imux_item(imux, label, idx, NULL);
 				break;
 			}
 		}
 	}
+
+	spec->num_mux_defs = 1;
+	spec->input_mux = imux;
+
 	return 0;
 }
 
@@ -5613,13 +5669,10 @@
 	if (spec->kctls.list)
 		add_mixer(spec, spec->kctls.list);
 
-	spec->num_mux_defs = 1;
-	spec->input_mux = &spec->private_imux[0];
-
-	if (!spec->dual_adc_switch)
-		alc_remove_invalid_adc_nids(codec);
+	alc_remove_invalid_adc_nids(codec);
 
 	alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
+	alc_auto_check_switches(codec);
 
 	return 1;
 }
@@ -5637,45 +5690,6 @@
 		alc_inithook(codec);
 }
 
-/* check the ADC/MUX contains all input pins; some ADC/MUX contains only
- * one of two digital mic pins, e.g. on ALC272
- */
-static void fixup_automic_adc(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	int i;
-
-	for (i = 0; i < spec->num_adc_nids; i++) {
-		hda_nid_t cap = spec->capsrc_nids ?
-			spec->capsrc_nids[i] : spec->adc_nids[i];
-		int iidx, eidx;
-
-		iidx = get_connection_index(codec, cap, spec->int_mic.pin);
-		if (iidx < 0)
-			continue;
-		eidx = get_connection_index(codec, cap, spec->ext_mic.pin);
-		if (eidx < 0)
-			continue;
-		spec->int_mic.mux_idx = iidx;
-		spec->ext_mic.mux_idx = eidx;
-		if (spec->capsrc_nids)
-			spec->capsrc_nids += i;
-		spec->adc_nids += i;
-		spec->num_adc_nids = 1;
-		/* optional dock-mic */
-		eidx = get_connection_index(codec, cap, spec->dock_mic.pin);
-		if (eidx < 0)
-			spec->dock_mic.pin = 0;
-		else
-			spec->dock_mic.mux_idx = eidx;
-		return;
-	}
-	snd_printd(KERN_INFO "hda_codec: %s: "
-		   "No ADC/MUX containing both 0x%x and 0x%x pins\n",
-		   codec->chip_name, spec->int_mic.pin, spec->ext_mic.pin);
-	spec->auto_mic = 0; /* disable auto-mic to be sure */
-}
-
 /* select or unmute the given capsrc route */
 static void select_or_unmute_capsrc(struct hda_codec *codec, hda_nid_t cap,
 				    int idx)
@@ -5683,7 +5697,7 @@
 	if (get_wcaps_type(get_wcaps(codec, cap)) == AC_WID_AUD_MIX) {
 		snd_hda_codec_amp_stereo(codec, cap, HDA_INPUT, idx,
 					 HDA_AMP_MUTE, 0);
-	} else {
+	} else if (snd_hda_get_conn_list(codec, cap, NULL) > 1) {
 		snd_hda_codec_write_cache(codec, cap, 0,
 					  AC_VERB_SET_CONNECT_SEL, idx);
 	}
@@ -5711,44 +5725,14 @@
 	return -1; /* not found */
 }
 
-/* choose the ADC/MUX containing the input pin and initialize the setup */
-static void fixup_single_adc(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	struct auto_pin_cfg *cfg = &spec->autocfg;
-	int i;
-
-	/* search for the input pin; there must be only one */
-	if (cfg->num_inputs != 1)
-		return;
-	i = init_capsrc_for_pin(codec, cfg->inputs[0].pin);
-	if (i >= 0) {
-		/* use only this ADC */
-		if (spec->capsrc_nids)
-			spec->capsrc_nids += i;
-		spec->adc_nids += i;
-		spec->num_adc_nids = 1;
-		spec->single_input_src = 1;
-	}
-}
-
-/* initialize dual adcs */
-static void fixup_dual_adc_switch(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	init_capsrc_for_pin(codec, spec->ext_mic.pin);
-	init_capsrc_for_pin(codec, spec->dock_mic.pin);
-	init_capsrc_for_pin(codec, spec->int_mic.pin);
-}
-
 /* initialize some special cases for input sources */
 static void alc_init_special_input_src(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
-	if (spec->dual_adc_switch)
-		fixup_dual_adc_switch(codec);
-	else if (spec->single_input_src)
-		init_capsrc_for_pin(codec, spec->autocfg.inputs[0].pin);
+	int i;
+
+	for (i = 0; i < spec->autocfg.num_inputs; i++)
+		init_capsrc_for_pin(codec, spec->autocfg.inputs[i].pin);
 }
 
 static void set_capture_mixer(struct hda_codec *codec)
@@ -5777,16 +5761,14 @@
 	if (spec->num_adc_nids > 0) {
 		int mux = 0;
 		int num_adcs = 0;
-		if (spec->dual_adc_switch)
+
+		if (spec->input_mux && spec->input_mux->num_items > 1)
+			mux = 1;
+		if (spec->auto_mic) {
 			num_adcs = 1;
-		else if (spec->auto_mic)
-			fixup_automic_adc(codec);
-		else if (spec->input_mux) {
-			if (spec->input_mux->num_items > 1)
-				mux = 1;
-			else if (spec->input_mux->num_items == 1)
-				fixup_single_adc(codec);
-		}
+			mux = 0;
+		} else if (spec->dyn_adc_switch)
+			num_adcs = 1;
 		if (!num_adcs) {
 			if (spec->num_adc_nids > 3)
 				spec->num_adc_nids = 3;
@@ -5798,35 +5780,92 @@
 	}
 }
 
+/* check whether dynamic ADC-switching is available */
+static bool alc_check_dyn_adc_switch(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	struct hda_input_mux *imux = &spec->private_imux[0];
+	int i, n, idx;
+	hda_nid_t cap, pin;
+
+	if (imux != spec->input_mux) /* no dynamic imux? */
+		return false;
+
+	for (n = 0; n < spec->num_adc_nids; n++) {
+		cap = spec->private_capsrc_nids[n];
+		for (i = 0; i < imux->num_items; i++) {
+			pin = spec->imux_pins[i];
+			if (!pin)
+				return false;
+			if (get_connection_index(codec, cap, pin) < 0)
+				break;
+		}
+		if (i >= imux->num_items)
+			return false; /* no ADC-switch is needed */
+	}
+
+	for (i = 0; i < imux->num_items; i++) {
+		pin = spec->imux_pins[i];
+		for (n = 0; n < spec->num_adc_nids; n++) {
+			cap = spec->private_capsrc_nids[n];
+			idx = get_connection_index(codec, cap, pin);
+			if (idx >= 0) {
+				imux->items[i].index = idx;
+				spec->dyn_adc_idx[i] = n;
+				break;
+			}
+		}
+	}
+
+	snd_printdd("realtek: enabling ADC switching\n");
+	spec->dyn_adc_switch = 1;
+	return true;
+}
+
 /* filter out invalid adc_nids (and capsrc_nids) that don't give all
  * active input pins
  */
 static void alc_remove_invalid_adc_nids(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
-	struct auto_pin_cfg *cfg = &spec->autocfg;
+	const struct hda_input_mux *imux;
 	hda_nid_t adc_nids[ARRAY_SIZE(spec->private_adc_nids)];
 	hda_nid_t capsrc_nids[ARRAY_SIZE(spec->private_adc_nids)];
 	int i, n, nums;
 
+	imux = spec->input_mux;
+	if (!imux)
+		return;
+	if (spec->dyn_adc_switch)
+		return;
+
 	nums = 0;
 	for (n = 0; n < spec->num_adc_nids; n++) {
 		hda_nid_t cap = spec->private_capsrc_nids[n];
-		for (i = 0; i < cfg->num_inputs; i++) {
-			hda_nid_t pin = cfg->inputs[i].pin;
-			if (get_connection_index(codec, cap, pin) < 0)
+		int num_conns = snd_hda_get_conn_list(codec, cap, NULL);
+		for (i = 0; i < imux->num_items; i++) {
+			hda_nid_t pin = spec->imux_pins[i];
+			if (pin) {
+				if (get_connection_index(codec, cap, pin) < 0)
+					break;
+			} else if (num_conns <= imux->items[i].index)
 				break;
 		}
-		if (i >= cfg->num_inputs) {
+		if (i >= imux->num_items) {
 			adc_nids[nums] = spec->private_adc_nids[n];
 			capsrc_nids[nums++] = cap;
 		}
 	}
 	if (!nums) {
-		printk(KERN_WARNING "hda_codec: %s: no valid ADC found;"
-		       " using fallback 0x%x\n",
-		       codec->chip_name, spec->private_adc_nids[0]);
-		spec->num_adc_nids = 1;
+		/* check whether ADC-switch is possible */
+		if (!alc_check_dyn_adc_switch(codec)) {
+			printk(KERN_WARNING "hda_codec: %s: no valid ADC found;"
+			       " using fallback 0x%x\n",
+			       codec->chip_name, spec->private_adc_nids[0]);
+			spec->num_adc_nids = 1;
+			spec->auto_mic = 0;
+			return;
+		}
 	} else if (nums != spec->num_adc_nids) {
 		memcpy(spec->private_adc_nids, adc_nids,
 		       nums * sizeof(hda_nid_t));
@@ -5834,6 +5873,11 @@
 		       nums * sizeof(hda_nid_t));
 		spec->num_adc_nids = nums;
 	}
+
+	if (spec->auto_mic)
+		alc_auto_mic_check_imux(codec); /* check auto-mic setups */
+	else if (spec->input_mux->num_items == 1)
+		spec->num_adc_nids = 1; /* reduce to a single ADC */
 }
 
 #ifdef CONFIG_SND_HDA_INPUT_BEEP
@@ -5915,6 +5959,7 @@
 
 	if (!spec->adc_nids && spec->input_mux) {
 		alc_auto_fill_adc_caps(codec);
+		alc_rebuild_imux_for_auto_mic(codec);
 		alc_remove_invalid_adc_nids(codec);
 	}
 	set_capture_mixer(codec);
@@ -7160,13 +7205,10 @@
 	if (spec->kctls.list)
 		add_mixer(spec, spec->kctls.list);
 
-	spec->num_mux_defs = 1;
-	spec->input_mux = &spec->private_imux[0];
-
-	if (!spec->dual_adc_switch)
-		alc_remove_invalid_adc_nids(codec);
+	alc_remove_invalid_adc_nids(codec);
 
 	alc_ssid_check(codec, 0x10, 0x15, 0x0f, 0);
+	alc_auto_check_switches(codec);
 
 	return 1;
 }
@@ -7450,6 +7492,7 @@
 
 	if (!spec->adc_nids && spec->input_mux) {
 		alc_auto_fill_adc_caps(codec);
+		alc_rebuild_imux_for_auto_mic(codec);
 		alc_remove_invalid_adc_nids(codec);
 	}
 	set_capture_mixer(codec);
@@ -9704,10 +9747,8 @@
 	spec->autocfg.speaker_pins[0] = 0x14;
 	spec->autocfg.speaker_pins[1] = 0x15;
 	spec->autocfg.speaker_pins[2] = 0x16;
-	spec->ext_mic.pin = 0x18;
-	spec->int_mic.pin = 0x19;
-	spec->ext_mic.mux_idx = 0;
-	spec->int_mic.mux_idx = 1;
+	spec->ext_mic_pin = 0x18;
+	spec->int_mic_pin = 0x19;
 	spec->auto_mic = 1;
 	spec->automute = 1;
 	spec->automute_mode = ALC_AUTOMUTE_AMP;
@@ -10780,67 +10821,41 @@
 /*
  * BIOS auto configuration
  */
+static void alc_auto_init_adc(struct hda_codec *codec, int adc_idx)
+{
+	struct alc_spec *spec = codec->spec;
+	hda_nid_t nid;
+
+	nid = spec->adc_nids[adc_idx];
+	/* mute ADC */
+	if (query_amp_caps(codec, nid, HDA_INPUT) & AC_AMPCAP_MUTE) {
+		snd_hda_codec_write(codec, nid, 0,
+				    AC_VERB_SET_AMP_GAIN_MUTE,
+				    AMP_IN_MUTE(0));
+		return;
+	}
+	if (!spec->capsrc_nids)
+		return;
+	nid = spec->capsrc_nids[adc_idx];
+	if (query_amp_caps(codec, nid, HDA_OUTPUT) & AC_AMPCAP_MUTE)
+		snd_hda_codec_write(codec, nid, 0,
+				    AC_VERB_SET_AMP_GAIN_MUTE,
+				    AMP_OUT_MUTE);
+}
+
 static void alc_auto_init_input_src(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
-	int c;
+	int c, nums;
 
-	if (spec->dual_adc_switch)
-		return;
-
-	for (c = 0; c < spec->num_adc_nids; c++) {
-		hda_nid_t nid;
-		unsigned int mux_idx;
-		const struct hda_input_mux *imux;
-		int conns, mute, idx, item;
-		unsigned int wid_type;
-
-		nid = spec->capsrc_nids ?
-			spec->capsrc_nids[c] : spec->adc_nids[c];
-		/* mute ADC */
-		if (query_amp_caps(codec, spec->adc_nids[c], HDA_INPUT) &
-		    AC_AMPCAP_MUTE)
-			snd_hda_codec_write(codec, spec->adc_nids[c], 0,
-				    AC_VERB_SET_AMP_GAIN_MUTE,
-				    AMP_IN_MUTE(0));
-		else if (query_amp_caps(codec, nid, HDA_OUTPUT) &
-			 AC_AMPCAP_MUTE)
-			snd_hda_codec_write(codec, nid, 0,
-				    AC_VERB_SET_AMP_GAIN_MUTE,
-				    AMP_OUT_MUTE);
-
-		conns = snd_hda_get_conn_list(codec, nid, NULL);
-		if (conns <= 0)
-			continue;
-		mux_idx = c >= spec->num_mux_defs ? 0 : c;
-		imux = &spec->input_mux[mux_idx];
-		if (!imux->num_items && mux_idx > 0)
-			imux = &spec->input_mux[0];
-		wid_type = get_wcaps_type(get_wcaps(codec, nid));
-		for (idx = 0; idx < conns; idx++) {
-			/* if the current connection is the selected one,
-			 * unmute it as default - otherwise mute it
-			 */
-			mute = AMP_IN_MUTE(idx);
-			for (item = 0; item < imux->num_items; item++) {
-				if (imux->items[item].index == idx) {
-					if (spec->cur_mux[c] == item)
-						mute = AMP_IN_UNMUTE(idx);
-					break;
-				}
-			}
-			/* initialize the mute status if mute-amp is present */
-			if (query_amp_caps(codec, nid, HDA_INPUT) & AC_AMPCAP_MUTE)
-				snd_hda_codec_write(codec, nid, 0,
-						    AC_VERB_SET_AMP_GAIN_MUTE,
-						    mute);
-			if (wid_type == AC_WID_AUD_SEL &&
-			    mute != AMP_IN_MUTE(idx))
-				snd_hda_codec_write(codec, nid, 0,
-						    AC_VERB_SET_CONNECT_SEL,
-						    idx);
-		}
-	}
+	for (c = 0; c < spec->num_adc_nids; c++)
+		alc_auto_init_adc(codec, c);
+	if (spec->dyn_adc_switch)
+		nums = 1;
+	else
+		nums = spec->num_adc_nids;
+	for (c = 0; c < nums; c++)
+		alc_mux_select(codec, 0, spec->cur_mux[c], true);
 }
 
 /* add mic boosts if needed */
@@ -10920,18 +10935,15 @@
 	if (spec->kctls.list)
 		add_mixer(spec, spec->kctls.list);
 
-	spec->num_mux_defs = 1;
-	spec->input_mux = &spec->private_imux[0];
-
-	if (!spec->dual_adc_switch)
-		alc_remove_invalid_adc_nids(codec);
-
-	alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
-
 	err = alc_auto_add_mic_boost(codec);
 	if (err < 0)
 		return err;
 
+	alc_remove_invalid_adc_nids(codec);
+
+	alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
+	alc_auto_check_switches(codec);
+
 	return 1; /* config found */
 }
 
@@ -11019,6 +11031,7 @@
 
 	if (!spec->adc_nids && spec->input_mux) {
 		alc_auto_fill_adc_caps(codec);
+		alc_rebuild_imux_for_auto_mic(codec);
 		alc_remove_invalid_adc_nids(codec);
 	}
 
@@ -11515,10 +11528,8 @@
 
 	spec->autocfg.hp_pins[0] = 0x15;
 	spec->autocfg.speaker_pins[0] = 0x14;
-	spec->ext_mic.pin = 0x18;
-	spec->ext_mic.mux_idx = 0;
-	spec->int_mic.pin = 0x12;
-	spec->int_mic.mux_idx = 9;
+	spec->ext_mic_pin = 0x18;
+	spec->int_mic_pin = 0x12;
 	spec->auto_mic = 1;
 	spec->automute = 1;
 	spec->automute_mode = ALC_AUTOMUTE_PIN;
@@ -12243,17 +12254,14 @@
 	if (spec->kctls.list)
 		add_mixer(spec, spec->kctls.list);
 
-	spec->num_mux_defs = 1;
-	spec->input_mux = &spec->private_imux[0];
-
-	if (!spec->dual_adc_switch)
-		alc_remove_invalid_adc_nids(codec);
-
 	err = alc_auto_add_mic_boost(codec);
 	if (err < 0)
 		return err;
 
+	alc_remove_invalid_adc_nids(codec);
+
 	alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
+	alc_auto_check_switches(codec);
 
 	return 1;
 }
@@ -12661,6 +12669,7 @@
 
 	if (!spec->adc_nids && spec->input_mux) {
 		alc_auto_fill_adc_caps(codec);
+		alc_rebuild_imux_for_auto_mic(codec);
 		alc_remove_invalid_adc_nids(codec);
 	}
 	if (!spec->cap_mixer && !spec->no_analog)
@@ -12863,10 +12872,8 @@
 	spec->autocfg.speaker_pins[0] = 0x14;
 	spec->automute = 1;
 	spec->automute_mode = ALC_AUTOMUTE_AMP;
-	spec->ext_mic.pin = 0x18;
-	spec->ext_mic.mux_idx = 0;
-	spec->int_mic.pin = 0x12;
-	spec->int_mic.mux_idx = 6;
+	spec->ext_mic_pin = 0x18;
+	spec->int_mic_pin = 0x12;
 	spec->auto_mic = 1;
 }
 
@@ -12896,10 +12903,8 @@
 
 	spec->autocfg.hp_pins[0] = 0x15;
 	spec->autocfg.speaker_pins[0] = 0x14;
-	spec->ext_mic.pin = 0x18;
-	spec->ext_mic.mux_idx = 0;
-	spec->int_mic.pin = 0x19;
-	spec->int_mic.mux_idx = 1;
+	spec->ext_mic_pin = 0x18;
+	spec->int_mic_pin = 0x19;
 	spec->auto_mic = 1;
 	spec->automute = 1;
 	spec->automute_mode = ALC_AUTOMUTE_PIN;
@@ -12928,10 +12933,8 @@
 	struct alc_spec *spec = codec->spec;
 	spec->autocfg.hp_pins[0] = 0x15;
 	spec->autocfg.speaker_pins[0] = 0x14;
-	spec->ext_mic.pin = 0x18;
-	spec->ext_mic.mux_idx = 0;
-	spec->int_mic.pin = 0x19;
-	spec->int_mic.mux_idx = 1;
+	spec->ext_mic_pin = 0x18;
+	spec->int_mic_pin = 0x19;
 	spec->auto_mic = 1;
 	spec->automute = 1;
 	spec->automute_mode = ALC_AUTOMUTE_PIN;
@@ -13358,17 +13361,14 @@
 		add_verb(spec, alc268_beep_init_verbs);
 	}
 
-	spec->num_mux_defs = 1;
-	spec->input_mux = &spec->private_imux[0];
-
-	if (!spec->dual_adc_switch)
-		alc_remove_invalid_adc_nids(codec);
-
 	err = alc_auto_add_mic_boost(codec);
 	if (err < 0)
 		return err;
 
+	alc_remove_invalid_adc_nids(codec);
+
 	alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
+	alc_auto_check_switches(codec);
 
 	return 1;
 }
@@ -13668,6 +13668,7 @@
 
 	if (!spec->no_analog && !spec->adc_nids && spec->input_mux) {
 		alc_auto_fill_adc_caps(codec);
+		alc_rebuild_imux_for_auto_mic(codec);
 		alc_remove_invalid_adc_nids(codec);
 	}
 
@@ -13924,10 +13925,8 @@
 	spec->automute_mixer_nid[0] = 0x0c;
 	spec->automute = 1;
 	spec->automute_mode = ALC_AUTOMUTE_MIXER;
-	spec->ext_mic.pin = 0x18;
-	spec->ext_mic.mux_idx = 0;
-	spec->int_mic.pin = 0x19;
-	spec->int_mic.mux_idx = 1;
+	spec->ext_mic_pin = 0x18;
+	spec->int_mic_pin = 0x19;
 	spec->auto_mic = 1;
 }
 
@@ -14019,10 +14018,8 @@
 	spec->automute_mixer_nid[0] = 0x0c;
 	spec->automute = 1;
 	spec->automute_mode = ALC_AUTOMUTE_MIXER;
-	spec->ext_mic.pin = 0x18;
-	spec->ext_mic.mux_idx = 0;
-	spec->int_mic.pin = 0x19;
-	spec->int_mic.mux_idx = 1;
+	spec->ext_mic_pin = 0x18;
+	spec->int_mic_pin = 0x19;
 	spec->auto_mic = 1;
 }
 
@@ -14034,10 +14031,8 @@
 	spec->automute_mixer_nid[0] = 0x0c;
 	spec->automute = 1;
 	spec->automute_mode = ALC_AUTOMUTE_MIXER;
-	spec->ext_mic.pin = 0x18;
-	spec->ext_mic.mux_idx = 0;
-	spec->int_mic.pin = 0x12;
-	spec->int_mic.mux_idx = 5;
+	spec->ext_mic_pin = 0x18;
+	spec->int_mic_pin = 0x12;
 	spec->auto_mic = 1;
 }
 
@@ -14049,10 +14044,8 @@
 	spec->automute_mixer_nid[0] = 0x0c;
 	spec->automute = 1;
 	spec->automute_mode = ALC_AUTOMUTE_MIXER;
-	spec->ext_mic.pin = 0x18;
-	spec->ext_mic.mux_idx = 0;
-	spec->int_mic.pin = 0x19;
-	spec->int_mic.mux_idx = 1;
+	spec->ext_mic_pin = 0x18;
+	spec->int_mic_pin = 0x19;
 	spec->auto_mic = 1;
 }
 
@@ -14064,10 +14057,8 @@
 	spec->automute_mixer_nid[0] = 0x0c;
 	spec->automute = 1;
 	spec->automute_mode = ALC_AUTOMUTE_MIXER;
-	spec->ext_mic.pin = 0x18;
-	spec->ext_mic.mux_idx = 0;
-	spec->int_mic.pin = 0x12;
-	spec->int_mic.mux_idx = 6;
+	spec->ext_mic_pin = 0x18;
+	spec->int_mic_pin = 0x12;
 	spec->auto_mic = 1;
 }
 
@@ -14217,36 +14208,6 @@
 }
 #endif /* CONFIG_SND_HDA_POWER_SAVE */
 
-static int alc275_setup_dual_adc(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-
-	if (codec->vendor_id != 0x10ec0275 || !spec->auto_mic)
-		return 0;
-	if ((spec->ext_mic.pin >= 0x18 && spec->int_mic.pin <= 0x13) ||
-	    (spec->ext_mic.pin <= 0x12 && spec->int_mic.pin >= 0x18)) {
-		if (spec->ext_mic.pin <= 0x12) {
-			spec->private_adc_nids[0] = 0x08;
-			spec->private_adc_nids[1] = 0x11;
-			spec->private_capsrc_nids[0] = 0x23;
-			spec->private_capsrc_nids[1] = 0x22;
-		} else {
-			spec->private_adc_nids[0] = 0x11;
-			spec->private_adc_nids[1] = 0x08;
-			spec->private_capsrc_nids[0] = 0x22;
-			spec->private_capsrc_nids[1] = 0x23;
-		}
-		spec->adc_nids = spec->private_adc_nids;
-		spec->capsrc_nids = spec->private_capsrc_nids;
-		spec->num_adc_nids = 2;
-		spec->dual_adc_switch = 1;
-		snd_printdd("realtek: enabling dual ADC switchg (%02x:%02x)\n",
-			    spec->adc_nids[0], spec->adc_nids[1]);
-		return 1;
-	}
-	return 0;
-}
-
 /* different alc269-variants */
 enum {
 	ALC269_TYPE_ALC269VA,
@@ -14282,17 +14243,13 @@
 	if (spec->kctls.list)
 		add_mixer(spec, spec->kctls.list);
 
+	alc_remove_invalid_adc_nids(codec);
+
 	if (spec->codec_variant != ALC269_TYPE_ALC269VA)
 		alc_ssid_check(codec, 0, 0x1b, 0x14, 0x21);
 	else
 		alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
-
-	spec->num_mux_defs = 1;
-	spec->input_mux = &spec->private_imux[0];
-
-	alc275_setup_dual_adc(codec);
-	if (!spec->dual_adc_switch)
-		alc_remove_invalid_adc_nids(codec);
+	alc_auto_check_switches(codec);
 
 	err = alc_auto_add_mic_boost(codec);
 	if (err < 0)
@@ -14819,6 +14776,7 @@
 
 	if (!spec->adc_nids) { /* wasn't filled automatically? use default */
 		alc_auto_fill_adc_caps(codec);
+		alc_rebuild_imux_for_auto_mic(codec);
 		alc_remove_invalid_adc_nids(codec);
 	}
 
@@ -15616,13 +15574,10 @@
 	if (spec->kctls.list)
 		add_mixer(spec, spec->kctls.list);
 
-	spec->num_mux_defs = 1;
-	spec->input_mux = &spec->private_imux[0];
-
-	if (!spec->dual_adc_switch)
-		alc_remove_invalid_adc_nids(codec);
+	alc_remove_invalid_adc_nids(codec);
 
 	alc_ssid_check(codec, 0x0e, 0x0f, 0x0b, 0);
+	alc_auto_check_switches(codec);
 
 	set_capture_mixer(codec);
 
@@ -15871,6 +15826,12 @@
 	if (board_config != ALC861_AUTO)
 		setup_preset(codec, &alc861_presets[board_config]);
 
+	if (!spec->adc_nids) {
+		alc_auto_fill_adc_caps(codec);
+		alc_rebuild_imux_for_auto_mic(codec);
+		alc_remove_invalid_adc_nids(codec);
+	}
+
 	if (!spec->cap_mixer)
 		set_capture_mixer(codec);
 	set_beep_amp(spec, 0x23, 0, HDA_OUTPUT);
@@ -16664,18 +16625,15 @@
 	if (spec->kctls.list)
 		add_mixer(spec, spec->kctls.list);
 
-	spec->num_mux_defs = 1;
-	spec->input_mux = &spec->private_imux[0];
+	alc_remove_invalid_adc_nids(codec);
 
-	if (!spec->dual_adc_switch)
-		alc_remove_invalid_adc_nids(codec);
+	alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
+	alc_auto_check_switches(codec);
 
 	err = alc_auto_add_mic_boost(codec);
 	if (err < 0)
 		return err;
 
-	alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
-
 	return 1;
 }
 
@@ -16772,6 +16730,7 @@
 
 	if (!spec->adc_nids) {
 		alc_auto_fill_adc_caps(codec);
+		alc_rebuild_imux_for_auto_mic(codec);
 		alc_remove_invalid_adc_nids(codec);
 	}
 
@@ -17539,10 +17498,8 @@
 	struct alc_spec *spec = codec->spec;
 
 	alc262_hippo1_setup(codec);
-	spec->ext_mic.pin = 0x18;
-	spec->ext_mic.mux_idx = 0;
-	spec->int_mic.pin = 0x19;
-	spec->int_mic.mux_idx = 1;
+	spec->ext_mic_pin = 0x18;
+	spec->int_mic_pin = 0x19;
 	spec->auto_mic = 1;
 }
 
@@ -17564,10 +17521,8 @@
 	spec->automute_mixer_nid[0] = 0x0c;
 	spec->automute = 1;
 	spec->automute_mode = ALC_AUTOMUTE_MIXER;
-	spec->ext_mic.pin = 0x18;
-	spec->ext_mic.mux_idx = 0;
-	spec->int_mic.pin = 0x12;
-	spec->int_mic.mux_idx = 9;
+	spec->ext_mic_pin = 0x18;
+	spec->int_mic_pin = 0x12;
 	spec->auto_mic = 1;
 }
 
@@ -17580,10 +17535,8 @@
 	spec->automute_mixer_nid[0] = 0x0c;
 	spec->automute = 1;
 	spec->automute_mode = ALC_AUTOMUTE_MIXER;
-	spec->ext_mic.pin = 0x18;
-	spec->ext_mic.mux_idx = 0;
-	spec->int_mic.pin = 0x19;
-	spec->int_mic.mux_idx = 1;
+	spec->ext_mic_pin = 0x18;
+	spec->int_mic_pin = 0x19;
 	spec->auto_mic = 1;
 }
 
@@ -17595,10 +17548,8 @@
 	spec->autocfg.speaker_pins[0] = 0x14;
 	spec->automute = 1;
 	spec->automute_mode = ALC_AUTOMUTE_PIN;
-	spec->ext_mic.pin = 0x18;
-	spec->ext_mic.mux_idx = 0;
-	spec->int_mic.pin = 0x19;
-	spec->int_mic.mux_idx = 1;
+	spec->ext_mic_pin = 0x18;
+	spec->int_mic_pin = 0x19;
 	spec->auto_mic = 1;
 }
 
@@ -17611,10 +17562,8 @@
 	spec->autocfg.speaker_pins[0] = 0x14;
 	spec->automute = 1;
 	spec->automute_mode = ALC_AUTOMUTE_PIN;
-	spec->ext_mic.pin = 0x18;
-	spec->ext_mic.mux_idx = 0;
-	spec->int_mic.pin = 0x19;
-	spec->int_mic.mux_idx = 1;
+	spec->ext_mic_pin = 0x18;
+	spec->int_mic_pin = 0x19;
 	spec->auto_mic = 1;
 }
 
@@ -17629,10 +17578,8 @@
 	spec->automute_mixer_nid[1] = 0x0e;
 	spec->automute = 1;
 	spec->automute_mode = ALC_AUTOMUTE_MIXER;
-	spec->ext_mic.pin = 0x18;
-	spec->ext_mic.mux_idx = 0;
-	spec->int_mic.pin = 0x19;
-	spec->int_mic.mux_idx = 1;
+	spec->ext_mic_pin = 0x18;
+	spec->int_mic_pin = 0x19;
 	spec->auto_mic = 1;
 }
 
@@ -17647,10 +17594,8 @@
 	spec->automute_mixer_nid[1] = 0x0e;
 	spec->automute = 1;
 	spec->automute_mode = ALC_AUTOMUTE_MIXER;
-	spec->ext_mic.pin = 0x18;
-	spec->ext_mic.mux_idx = 0;
-	spec->int_mic.pin = 0x19;
-	spec->int_mic.mux_idx = 1;
+	spec->ext_mic_pin = 0x18;
+	spec->int_mic_pin = 0x19;
 	spec->auto_mic = 1;
 }
 
@@ -17664,10 +17609,8 @@
 	spec->automute_mixer_nid[0] = 0x0c;
 	spec->automute = 1;
 	spec->automute_mode = ALC_AUTOMUTE_MIXER;
-	spec->ext_mic.pin = 0x18;
-	spec->ext_mic.mux_idx = 0;
-	spec->int_mic.pin = 0x19;
-	spec->int_mic.mux_idx = 1;
+	spec->ext_mic_pin = 0x18;
+	spec->int_mic_pin = 0x19;
 	spec->auto_mic = 1;
 }
 
@@ -17681,10 +17624,8 @@
 	spec->autocfg.speaker_pins[0] = 0x17;
 	spec->automute = 1;
 	spec->automute_mode = ALC_AUTOMUTE_PIN;
-	spec->ext_mic.pin = 0x18;
-	spec->ext_mic.mux_idx = 0;
-	spec->int_mic.pin = 0x19;
-	spec->int_mic.mux_idx = 1;
+	spec->ext_mic_pin = 0x18;
+	spec->int_mic_pin = 0x19;
 	spec->auto_mic = 1;
 }
 
@@ -17698,10 +17639,8 @@
 	spec->autocfg.speaker_pins[0] = 0x17;
 	spec->automute = 1;
 	spec->automute_mode = ALC_AUTOMUTE_PIN;
-	spec->ext_mic.pin = 0x18;
-	spec->ext_mic.mux_idx = 0;
-	spec->int_mic.pin = 0x12;
-	spec->int_mic.mux_idx = 9;
+	spec->ext_mic_pin = 0x18;
+	spec->int_mic_pin = 0x12;
 	spec->auto_mic = 1;
 }
 
@@ -17715,10 +17654,8 @@
 	spec->automute_mode = ALC_AUTOMUTE_AMP;
 	spec->detect_line = 1;
 	spec->automute_lines = 1;
-	spec->ext_mic.pin = 0x18;
-	spec->ext_mic.mux_idx = 0;
-	spec->int_mic.pin = 0x12;
-	spec->int_mic.mux_idx = 9;
+	spec->ext_mic_pin = 0x18;
+	spec->int_mic_pin = 0x12;
 	spec->auto_mic = 1;
 }
 
@@ -18779,21 +18716,18 @@
 	if (spec->kctls.list)
 		add_mixer(spec, spec->kctls.list);
 
-	spec->num_mux_defs = 1;
-	spec->input_mux = &spec->private_imux[0];
-
-	if (!spec->dual_adc_switch)
-		alc_remove_invalid_adc_nids(codec);
-
-	err = alc_auto_add_mic_boost(codec);
-	if (err < 0)
-		return err;
+	alc_remove_invalid_adc_nids(codec);
 
 	if (codec->vendor_id == 0x10ec0272 || codec->vendor_id == 0x10ec0663 ||
 	    codec->vendor_id == 0x10ec0665 || codec->vendor_id == 0x10ec0670)
 	    alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0x21);
 	else
 	    alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
+	alc_auto_check_switches(codec);
+
+	err = alc_auto_add_mic_boost(codec);
+	if (err < 0)
+		return err;
 
 	return 1;
 }
@@ -18949,6 +18883,7 @@
 
 	if (!spec->adc_nids) {
 		alc_auto_fill_adc_caps(codec);
+		alc_rebuild_imux_for_auto_mic(codec);
 		alc_remove_invalid_adc_nids(codec);
 	}
 
@@ -19036,36 +18971,32 @@
 /*
  * Analog capture ADC cgange
  */
+static hda_nid_t alc680_get_cur_adc(struct hda_codec *codec)
+{
+	static hda_nid_t pins[] = {0x18, 0x19};
+	static hda_nid_t adcs[] = {0x08, 0x09};
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(pins); i++) {
+		if (!is_jack_detectable(codec, pins[i]))
+			continue;
+		if (snd_hda_jack_detect(codec, pins[i]))
+			return adcs[i];
+	}
+	return 0x07;
+}
+
 static void alc680_rec_autoswitch(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
-	struct auto_pin_cfg *cfg = &spec->autocfg;
-	int pin_found = 0;
-	int type_found = AUTO_PIN_LAST;
-	hda_nid_t nid;
-	int i;
-
-	for (i = 0; i < cfg->num_inputs; i++) {
-		nid = cfg->inputs[i].pin;
-		if (!is_jack_detectable(codec, nid))
-			continue;
-		if (snd_hda_jack_detect(codec, nid)) {
-			if (cfg->inputs[i].type < type_found) {
-				type_found = cfg->inputs[i].type;
-				pin_found = nid;
-			}
-		}
-	}
-
-	nid = 0x07;
-	if (pin_found)
-		snd_hda_get_connections(codec, pin_found, &nid, 1);
-
-	if (nid != spec->cur_adc)
+	hda_nid_t nid = alc680_get_cur_adc(codec);
+	if (spec->cur_adc && nid != spec->cur_adc) {
 		__snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
-	spec->cur_adc = nid;
-	snd_hda_codec_setup_stream(codec, nid, spec->cur_adc_stream_tag, 0,
-				   spec->cur_adc_format);
+		spec->cur_adc = nid;
+		snd_hda_codec_setup_stream(codec, nid,
+					   spec->cur_adc_stream_tag, 0,
+					   spec->cur_adc_format);
+	}
 }
 
 static int alc680_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
@@ -19075,12 +19006,12 @@
 				      struct snd_pcm_substream *substream)
 {
 	struct alc_spec *spec = codec->spec;
+	hda_nid_t nid = alc680_get_cur_adc(codec);
 
-	spec->cur_adc = 0x07;
+	spec->cur_adc = nid;
 	spec->cur_adc_stream_tag = stream_tag;
 	spec->cur_adc_format = format;
-
-	alc680_rec_autoswitch(codec);
+	snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
 	return 0;
 }
 
@@ -19088,9 +19019,9 @@
 				      struct hda_codec *codec,
 				      struct snd_pcm_substream *substream)
 {
-	snd_hda_codec_cleanup_stream(codec, 0x07);
-	snd_hda_codec_cleanup_stream(codec, 0x08);
-	snd_hda_codec_cleanup_stream(codec, 0x09);
+	struct alc_spec *spec = codec->spec;
+	snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
+	spec->cur_adc = 0;
 	return 0;
 }
 
@@ -19332,6 +19263,10 @@
 	if (err < 0)
 		return err;
 
+	err = alc_auto_create_input_ctls(codec);
+	if (err < 0)
+		return err;
+
 	spec->multiout.max_channels = 2;
 
  dig_only:
@@ -19340,6 +19275,10 @@
 	if (spec->kctls.list)
 		add_mixer(spec, spec->kctls.list);
 
+	alc_remove_invalid_adc_nids(codec);
+
+	alc_auto_check_switches(codec);
+
 	err = alc_auto_add_mic_boost(codec);
 	if (err < 0)
 		return err;
@@ -19354,6 +19293,7 @@
 	alc680_auto_init_multi_out(codec);
 	alc680_auto_init_hp_out(codec);
 	alc_auto_init_analog_input(codec);
+	alc_auto_init_input_src(codec);
 	alc_auto_init_digital(codec);
 	if (spec->unsol_event)
 		alc_inithook(codec);
@@ -19427,11 +19367,14 @@
 		}
 	}
 
-	if (board_config != ALC680_AUTO)
+	if (board_config != ALC680_AUTO) {
 		setup_preset(codec, &alc680_presets[board_config]);
+		spec->stream_analog_capture = &alc680_pcm_analog_auto_capture;
+	}
 
 	if (!spec->adc_nids) {
 		alc_auto_fill_adc_caps(codec);
+		alc_rebuild_imux_for_auto_mic(codec);
 		alc_remove_invalid_adc_nids(codec);
 	}