Jelajahi Sumber

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

In addition to the normal external mic jack, check also the mic jack
on a docking-station as well, and select the input source appropriately.

The similar functionality was already implemented in patch_sigmatel.c.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
Takashi Iwai 14 tahun lalu
induk
melakukan
43c1b2e920
1 mengubah file dengan 59 tambahan dan 39 penghapusan
  1. 59 39
      sound/pci/hda/patch_conexant.c

+ 59 - 39
sound/pci/hda/patch_conexant.c

@@ -85,6 +85,8 @@ struct conexant_spec {
 	unsigned int line_present;
 	unsigned int auto_mic;
 	int auto_mic_ext;		/* imux_pins[] index for ext mic */
+	int auto_mic_dock;		/* imux_pins[] index for dock mic */
+	int auto_mic_int;		/* imux_pins[] index for int mic */
 	unsigned int need_dac_fix;
 	hda_nid_t slave_dig_outs[2];
 
@@ -3737,18 +3739,27 @@ static const struct snd_kcontrol_new cx_auto_capture_mixers[] = {
 	{}
 };
 
+static bool select_automic(struct hda_codec *codec, int idx, bool detect)
+{
+	struct conexant_spec *spec = codec->spec;
+	if (idx < 0)
+		return false;
+	if (detect && !snd_hda_jack_detect(codec, spec->imux_info[idx].pin))
+		return false;
+	cx_auto_mux_enum_update(codec, &spec->private_imux, idx);
+	return true;
+}
+
 /* automatic switch internal and external mic */
 static void cx_auto_automic(struct hda_codec *codec)
 {
 	struct conexant_spec *spec = codec->spec;
-	int ext_idx = spec->auto_mic_ext;
 
 	if (!spec->auto_mic)
 		return;
-	if (snd_hda_jack_detect(codec, spec->imux_info[ext_idx].pin))
-		cx_auto_mux_enum_update(codec, &spec->private_imux, ext_idx);
-	else
-		cx_auto_mux_enum_update(codec, &spec->private_imux, !ext_idx);
+	if (!select_automic(codec, spec->auto_mic_ext, true))
+		if (!select_automic(codec, spec->auto_mic_dock, true))
+			select_automic(codec, spec->auto_mic_int, false);
 }
 
 static void cx_auto_unsol_event(struct hda_codec *codec, unsigned int res)
@@ -3768,42 +3779,43 @@ static void cx_auto_unsol_event(struct hda_codec *codec, unsigned int res)
 	}
 }
 
-/* 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 &&
-		is_jack_detectable(codec, pin);
-}
-
 /* 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
+ * auto-mic is enabled only when one int-mic and one ext- and/or
+ * one dock-mic exist
  */
 static void cx_auto_check_auto_mic(struct hda_codec *codec)
 {
 	struct conexant_spec *spec = codec->spec;
+	int pset[INPUT_PIN_ATTR_NORMAL + 1];
+	int i;
 
-	if (is_ext_mic(codec, spec->imux_info[0].pin) &&
-	    is_int_mic(codec, spec->imux_info[1].pin)) {
-		spec->auto_mic = 1;
-		spec->auto_mic_ext = 0;
-		return;
-	}
-	if (is_int_mic(codec, spec->imux_info[0].pin) &&
-	    is_ext_mic(codec, spec->imux_info[1].pin)) {
-		spec->auto_mic = 1;
-		spec->auto_mic_ext = 1;
-		return;
+	for (i = 0; i < INPUT_PIN_ATTR_NORMAL; i++)
+		pset[i] = -1;
+	for (i = 0; i < spec->private_imux.num_items; i++) {
+		hda_nid_t pin = spec->imux_info[i].pin;
+		unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin);
+		int attr;
+		if (get_defcfg_device(def_conf) != AC_JACK_MIC_IN)
+			return; /* no-mic input */
+		attr = snd_hda_get_input_pin_attr(def_conf);
+		if (attr == INPUT_PIN_ATTR_UNUSED)
+			continue;
+		if (attr > INPUT_PIN_ATTR_NORMAL)
+			attr = INPUT_PIN_ATTR_NORMAL;
+		if (attr != INPUT_PIN_ATTR_INT &&
+		    !is_jack_detectable(codec, pin))
+			continue;
+		if (pset[attr] >= 0)
+			return; /* already occupied */
+		pset[attr] = i;
 	}
+	if (pset[INPUT_PIN_ATTR_INT] < 0 ||
+	    (pset[INPUT_PIN_ATTR_NORMAL] < 0 && pset[INPUT_PIN_ATTR_DOCK]))
+		return; /* no input to switch*/
+	spec->auto_mic = 1;
+	spec->auto_mic_ext = pset[INPUT_PIN_ATTR_NORMAL];
+	spec->auto_mic_dock = pset[INPUT_PIN_ATTR_DOCK];
+	spec->auto_mic_int = pset[INPUT_PIN_ATTR_INT];
 }
 
 static void cx_auto_parse_input(struct hda_codec *codec)
@@ -3832,7 +3844,7 @@ static void cx_auto_parse_input(struct hda_codec *codec)
 			}
 		}
 	}
-	if (imux->num_items == 2 && cfg->num_inputs == 2)
+	if (imux->num_items >= 2 && cfg->num_inputs == imux->num_items)
 		cx_auto_check_auto_mic(codec);
 	if (imux->num_items > 1 && !spec->auto_mic) {
 		for (i = 1; i < imux->num_items; i++) {
@@ -4022,10 +4034,18 @@ static void cx_auto_init_input(struct hda_codec *codec)
 	}
 
 	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);
+		if (spec->auto_mic_ext >= 0) {
+			snd_hda_codec_write(codec,
+				cfg->inputs[spec->auto_mic_ext].pin, 0,
+				AC_VERB_SET_UNSOLICITED_ENABLE,
+				AC_USRSP_EN | CONEXANT_MIC_EVENT);
+		}
+		if (spec->auto_mic_dock >= 0) {
+			snd_hda_codec_write(codec,
+				cfg->inputs[spec->auto_mic_dock].pin, 0,
+				AC_VERB_SET_UNSOLICITED_ENABLE,
+				AC_USRSP_EN | CONEXANT_MIC_EVENT);
+		}
 		cx_auto_automic(codec);
 	} else {
 		select_input_connection(codec, spec->imux_info[0].adc,