ALSA: hda - Add dock-mic detection support to Realtek auto-parser
Takashi Iwai [Tue, 17 May 2011 10:05:02 +0000 (12:05 +0200)]
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>

sound/pci/hda/patch_realtek.c

index 24bc8a6..cee89b1 100644 (file)
@@ -360,6 +360,7 @@ struct alc_spec {
        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 @@ static int alc_init_jacks(struct hda_codec *codec)
        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 @@ static int alc_init_jacks(struct hda_codec *codec)
                        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_dual_mic_adc_auto_switch(struct hda_codec *codec)
 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 @@ static void alc_mic_automute(struct hda_codec *codec)
 
        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 @@ static void alc_mic_automute(struct hda_codec *codec)
                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 @@ static void alc_init_auto_mic(struct hda_codec *codec)
 {
        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 @@ static void alc_init_auto_mic(struct hda_codec *codec)
                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 @@ static void fixup_automic_adc(struct hda_codec *codec)
                        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 @@ static int init_capsrc_for_pin(struct hda_codec *codec, hda_nid_t pin)
        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 @@ 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);
 }