ALSA: hda - Add dock-mic detection support to Realtek auto-parser

In addition to the normal mic jack, the mic (or line-in) jack on the
docking-station is checked also as a candidate for auto-selection.

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 24bc8a6..cee89b1 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -360,6 +360,7 @@
 	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;
 
 	/* channel model */
@@ -1057,6 +1058,7 @@
 	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;
 
 	if (hp_nid) {
 		err = snd_hda_input_jack_add(codec, hp_nid,
@@ -1073,6 +1075,13 @@
 			return err;
 		snd_hda_input_jack_report(codec, mic_nid);
 	}
+	if (dock_nid) {
+		err = snd_hda_input_jack_add(codec, dock_nid,
+					     SND_JACK_MICROPHONE, NULL);
+		if (err < 0)
+			return err;
+		snd_hda_input_jack_report(codec, dock_nid);
+	}
 #endif /* CONFIG_SND_HDA_INPUT_JACK */
 	return 0;
 }
@@ -1217,7 +1226,7 @@
 static void alc_mic_automute(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
-	struct alc_mic_route *dead, *alive;
+	struct alc_mic_route *dead1, *dead2, *alive;
 	unsigned int present, type;
 	hda_nid_t cap_nid;
 
@@ -1235,13 +1244,24 @@
 
 	cap_nid = spec->capsrc_nids ? spec->capsrc_nids[0] : spec->adc_nids[0];
 
+	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;
-		dead = &spec->int_mic;
-	} else {
-		alive = &spec->int_mic;
-		dead = &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));
@@ -1250,9 +1270,14 @@
 		snd_hda_codec_amp_stereo(codec, cap_nid, HDA_INPUT,
 					 alive->mux_idx,
 					 HDA_AMP_MUTE, 0);
-		snd_hda_codec_amp_stereo(codec, cap_nid, HDA_INPUT,
-					 dead->mux_idx,
-					 HDA_AMP_MUTE, HDA_AMP_MUTE);
+		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,
@@ -1598,15 +1623,10 @@
 {
 	struct alc_spec *spec = codec->spec;
 	struct auto_pin_cfg *cfg = &spec->autocfg;
-	hda_nid_t fixed, ext;
+	hda_nid_t fixed, ext, dock;
 	int i;
 
-	/* there must be only two mic inputs exclusively */
-	for (i = 0; i < cfg->num_inputs; i++)
-		if (cfg->inputs[i].type >= AUTO_PIN_LINE_IN)
-			return;
-
-	fixed = ext = 0;
+	fixed = ext = dock = 0;
 	for (i = 0; i < cfg->num_inputs; i++) {
 		hda_nid_t nid = cfg->inputs[i].pin;
 		unsigned int defcfg;
@@ -1615,26 +1635,45 @@
 		case INPUT_PIN_ATTR_INT:
 			if (fixed)
 				return; /* already occupied */
+			if (cfg->inputs[i].type != AUTO_PIN_MIC)
+				return; /* invalid type */
 			fixed = nid;
 			break;
 		case INPUT_PIN_ATTR_UNUSED:
 			return; /* invalid entry */
+		case INPUT_PIN_ATTR_DOCK:
+			if (dock)
+				return; /* already occupied */
+			if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
+				return; /* invalid type */
+			dock = nid;
+			break;
 		default:
 			if (ext)
 				return; /* already occupied */
+			if (cfg->inputs[i].type != AUTO_PIN_MIC)
+				return; /* invalid type */
 			ext = nid;
 			break;
 		}
 	}
+	if (!ext && dock) {
+		ext = dock;
+		dock = 0;
+	}
 	if (!ext || !fixed)
 		return;
 	if (!is_jack_detectable(codec, ext))
 		return; /* no unsol support */
-	snd_printdd("realtek: Enable auto-mic switch on NID 0x%x/0x%x\n",
-		    ext, fixed);
+	if (dock && !is_jack_detectable(codec, dock))
+		return; /* no unsol support */
+	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,
@@ -5716,6 +5755,12 @@
 			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: "
@@ -5743,6 +5788,8 @@
 	struct alc_spec *spec = codec->spec;
 	int i;
 
+	if (!pin)
+		return 0;
 	for (i = 0; i < spec->num_adc_nids; i++) {
 		hda_nid_t cap = spec->capsrc_nids ?
 			spec->capsrc_nids[i] : spec->adc_nids[i];
@@ -5783,6 +5830,7 @@
 {
 	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);
 }