]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - sound/pci/hda/patch_realtek.c
ALSA: hda - Support multiple headphone auto-mute
[net-next-2.6.git] / sound / pci / hda / patch_realtek.c
index a4dd04524e4391ce7d47f76196b6eeca5e6e9f85..ee59df7a41f8086abc737e6112526e6a2dcf91a4 100644 (file)
@@ -990,25 +990,46 @@ static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid,
        alc_fix_pll(codec);
 }
 
-static void alc_automute_pin(struct hda_codec *codec)
+static void alc_automute_speaker(struct hda_codec *codec, int pinctl)
 {
        struct alc_spec *spec = codec->spec;
-       unsigned int nid = spec->autocfg.hp_pins[0];
+       unsigned int mute;
+       hda_nid_t nid;
        int i;
 
-       if (!nid)
-               return;
-       spec->jack_present = snd_hda_jack_detect(codec, nid);
+       spec->jack_present = 0;
+       for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) {
+               nid = spec->autocfg.hp_pins[i];
+               if (!nid)
+                       break;
+               if (snd_hda_jack_detect(codec, nid)) {
+                       spec->jack_present = 1;
+                       break;
+               }
+       }
+
+       mute = spec->jack_present ? HDA_AMP_MUTE : 0;
+       /* Toggle internal speakers muting */
        for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) {
                nid = spec->autocfg.speaker_pins[i];
                if (!nid)
                        break;
-               snd_hda_codec_write(codec, nid, 0,
+               if (pinctl) {
+                       snd_hda_codec_write(codec, nid, 0,
                                    AC_VERB_SET_PIN_WIDGET_CONTROL,
                                    spec->jack_present ? 0 : PIN_OUT);
+               } else {
+                       snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
+                                        HDA_AMP_MUTE, mute);
+               }
        }
 }
 
+static void alc_automute_pin(struct hda_codec *codec)
+{
+       alc_automute_speaker(codec, 1);
+}
+
 static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
                                hda_nid_t nid)
 {
@@ -1236,24 +1257,35 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type)
 static void alc_init_auto_hp(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       int i;
 
-       if (!spec->autocfg.hp_pins[0])
-               return;
+       if (!cfg->hp_pins[0]) {
+               if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+                       return;
+       }
 
-       if (!spec->autocfg.speaker_pins[0]) {
-               if (spec->autocfg.line_out_pins[0] &&
-                   spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
-                       spec->autocfg.speaker_pins[0] =
-                               spec->autocfg.line_out_pins[0];
-               else
+       if (!cfg->speaker_pins[0]) {
+               if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
                        return;
+               memcpy(cfg->speaker_pins, cfg->line_out_pins,
+                      sizeof(cfg->speaker_pins));
+               cfg->speaker_outs = cfg->line_outs;
        }
 
-       snd_printdd("realtek: Enable HP auto-muting on NID 0x%x\n",
-                   spec->autocfg.hp_pins[0]);
-       snd_hda_codec_write_cache(codec, spec->autocfg.hp_pins[0], 0,
+       if (!cfg->hp_pins[0]) {
+               memcpy(cfg->hp_pins, cfg->line_out_pins,
+                      sizeof(cfg->hp_pins));
+               cfg->hp_outs = cfg->line_outs;
+       }
+
+       for (i = 0; i < cfg->hp_outs; i++) {
+               snd_printdd("realtek: Enable HP auto-muting on NID 0x%x\n",
+                           cfg->hp_pins[i]);
+               snd_hda_codec_write_cache(codec, cfg->hp_pins[i], 0,
                                  AC_VERB_SET_UNSOLICITED_ENABLE,
                                  AC_USRSP_EN | ALC880_HP_EVENT);
+       }
        spec->unsol_event = alc_sku_unsol_event;
 }
 
@@ -1265,16 +1297,14 @@ static void alc_init_auto_mic(struct hda_codec *codec)
        int i;
 
        /* there must be only two mic inputs exclusively */
-       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)
                        return;
 
        fixed = ext = 0;
-       for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++) {
-               hda_nid_t nid = cfg->input_pins[i];
+       for (i = 0; i < cfg->num_inputs; i++) {
+               hda_nid_t nid = cfg->inputs[i].pin;
                unsigned int defcfg;
-               if (!nid)
-                       return;
                defcfg = snd_hda_codec_get_pincfg(codec, nid);
                switch (get_defcfg_connect(defcfg)) {
                case AC_JACK_PORT_FIXED:
@@ -1713,31 +1743,7 @@ static struct hda_verb alc888_fujitsu_xa3530_verbs[] = {
 
 static void alc_automute_amp(struct hda_codec *codec)
 {
-       struct alc_spec *spec = codec->spec;
-       unsigned int mute;
-       hda_nid_t nid;
-       int i;
-
-       spec->jack_present = 0;
-       for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) {
-               nid = spec->autocfg.hp_pins[i];
-               if (!nid)
-                       break;
-               if (snd_hda_jack_detect(codec, nid)) {
-                       spec->jack_present = 1;
-                       break;
-               }
-       }
-
-       mute = spec->jack_present ? HDA_AMP_MUTE : 0;
-       /* Toggle internal speakers muting */
-       for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) {
-               nid = spec->autocfg.speaker_pins[i];
-               if (!nid)
-                       break;
-               snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
-                                        HDA_AMP_MUTE, mute);
-       }
+       alc_automute_speaker(codec, 0);
 }
 
 static void alc_automute_amp_unsol_event(struct hda_codec *codec,
@@ -4719,7 +4725,7 @@ static struct snd_kcontrol_new alc880_control_templates[] = {
 
 /* add dynamic controls */
 static int add_control(struct alc_spec *spec, int type, const char *name,
-                      unsigned long val)
+                      int cidx, unsigned long val)
 {
        struct snd_kcontrol_new *knew;
 
@@ -4731,6 +4737,7 @@ static int add_control(struct alc_spec *spec, int type, const char *name,
        knew->name = kstrdup(name, GFP_KERNEL);
        if (!knew->name)
                return -ENOMEM;
+       knew->index = cidx;
        if (get_amp_nid_(val))
                knew->subdevice = HDA_SUBDEV_AMP_FLAG;
        knew->private_value = val;
@@ -4739,17 +4746,21 @@ static int add_control(struct alc_spec *spec, int type, const char *name,
 
 static int add_control_with_pfx(struct alc_spec *spec, int type,
                                const char *pfx, const char *dir,
-                               const char *sfx, unsigned long val)
+                               const char *sfx, int cidx, unsigned long val)
 {
        char name[32];
        snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx);
-       return add_control(spec, type, name, val);
+       return add_control(spec, type, name, cidx, val);
 }
 
-#define add_pb_vol_ctrl(spec, type, pfx, val) \
-       add_control_with_pfx(spec, type, pfx, "Playback", "Volume", val)
-#define add_pb_sw_ctrl(spec, type, pfx, val) \
-       add_control_with_pfx(spec, type, pfx, "Playback", "Switch", val)
+#define add_pb_vol_ctrl(spec, type, pfx, val)                  \
+       add_control_with_pfx(spec, type, pfx, "Playback", "Volume", 0, val)
+#define add_pb_sw_ctrl(spec, type, pfx, val)                   \
+       add_control_with_pfx(spec, type, pfx, "Playback", "Switch", 0, val)
+#define __add_pb_vol_ctrl(spec, type, pfx, cidx, val)                  \
+       add_control_with_pfx(spec, type, pfx, "Playback", "Volume", cidx, val)
+#define __add_pb_sw_ctrl(spec, type, pfx, cidx, val)                   \
+       add_control_with_pfx(spec, type, pfx, "Playback", "Switch", cidx, val)
 
 #define alc880_is_fixed_pin(nid)       ((nid) >= 0x14 && (nid) <= 0x17)
 #define alc880_fixed_pin_idx(nid)      ((nid) - 0x14)
@@ -4902,16 +4913,16 @@ static int alc880_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin,
 
 /* create input playback/capture controls for the given pin */
 static int new_analog_input(struct alc_spec *spec, hda_nid_t pin,
-                           const char *ctlname,
+                           const char *ctlname, int ctlidx,
                            int idx, hda_nid_t mix_nid)
 {
        int err;
 
-       err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname,
+       err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, ctlidx,
                          HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
        if (err < 0)
                return err;
-       err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname,
+       err = __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, ctlidx,
                          HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
        if (err < 0)
                return err;
@@ -4932,21 +4943,26 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec,
 {
        struct alc_spec *spec = codec->spec;
        struct hda_input_mux *imux = &spec->private_imux[0];
-       int i, err, idx;
+       int i, err, idx, type, type_idx = 0;
 
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
+       for (i = 0; i < cfg->num_inputs; i++) {
                hda_nid_t pin;
 
-               pin = cfg->input_pins[i];
+               pin = cfg->inputs[i].pin;
                if (!alc_is_input_pin(codec, pin))
                        continue;
 
+               type = cfg->inputs[i].type;
+               if (i > 0 && type == cfg->inputs[i - 1].type)
+                       type_idx++;
+               else
+                       type_idx = 0;
                if (mixer) {
                        idx = get_connection_index(codec, mixer, pin);
                        if (idx >= 0) {
                                err = new_analog_input(spec, pin,
-                                                      auto_pin_cfg_labels[i],
-                                                      idx, mixer);
+                                                      auto_pin_cfg_labels[type],
+                                                      type_idx, idx, mixer);
                                if (err < 0)
                                        return err;
                        }
@@ -4959,7 +4975,7 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec,
                        idx = get_connection_index(codec, cap2, pin);
                if (idx >= 0) {
                        imux->items[imux->num_items].label =
-                               auto_pin_cfg_labels[i];
+                               snd_hda_get_input_pin_label(cfg, i);
                        imux->items[imux->num_items].index = idx;
                        imux->num_items++;
                }
@@ -5034,10 +5050,11 @@ static void alc880_auto_init_extra_out(struct hda_codec *codec)
 static void alc880_auto_init_analog_input(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
        int i;
 
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               hda_nid_t nid = spec->autocfg.input_pins[i];
+       for (i = 0; i < cfg->num_inputs; i++) {
+               hda_nid_t nid = cfg->inputs[i].pin;
                if (alc_is_input_pin(codec, nid)) {
                        alc_set_input_pin(codec, nid, i);
                        if (nid != ALC880_PIN_CD_NID &&
@@ -5204,19 +5221,13 @@ static int init_capsrc_for_pin(struct hda_codec *codec, hda_nid_t pin)
 static void fixup_single_adc(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       hda_nid_t pin = 0;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
        int i;
 
        /* search for the input pin; there must be only one */
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               if (spec->autocfg.input_pins[i]) {
-                       pin = spec->autocfg.input_pins[i];
-                       break;
-               }
-       }
-       if (!pin)
+       if (cfg->num_inputs != 1)
                return;
-       i = init_capsrc_for_pin(codec, pin);
+       i = init_capsrc_for_pin(codec, cfg->inputs[0].pin);
        if (i >= 0) {
                /* use only this ADC */
                if (spec->capsrc_nids)
@@ -5269,6 +5280,7 @@ static void fillup_priv_adc_nids(struct hda_codec *codec, hda_nid_t *nids,
                                 int num_nids)
 {
        struct alc_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
        int n;
        hda_nid_t fallback_adc = 0, fallback_cap = 0;
 
@@ -5294,10 +5306,8 @@ static void fillup_priv_adc_nids(struct hda_codec *codec, hda_nid_t *nids,
                        fallback_adc = adc;
                        fallback_cap = cap;
                }
-               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;
                        for (j = 0; j < nconns; j++) {
                                if (conn[j] == nid)
                                        break;
@@ -5305,7 +5315,7 @@ static void fillup_priv_adc_nids(struct hda_codec *codec, hda_nid_t *nids,
                        if (j >= nconns)
                                break;
                }
-               if (i >= AUTO_PIN_LAST) {
+               if (i >= cfg->num_inputs) {
                        int num_adcs = spec->num_adc_nids;
                        spec->private_adc_nids[num_adcs] = adc;
                        spec->private_capsrc_nids[num_adcs] = cap;
@@ -5334,6 +5344,7 @@ static void fillup_priv_adc_nids(struct hda_codec *codec, hda_nid_t *nids,
 
 static struct snd_pci_quirk beep_white_list[] = {
        SND_PCI_QUIRK(0x1043, 0x829f, "ASUS", 1),
+       SND_PCI_QUIRK(0x1043, 0x83ce, "EeePC", 1),
        SND_PCI_QUIRK(0x8086, 0xd613, "Intel", 1),
        {}
 };
@@ -6672,10 +6683,11 @@ static void alc260_auto_init_multi_out(struct hda_codec *codec)
 static void alc260_auto_init_analog_input(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
        int i;
 
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               hda_nid_t nid = spec->autocfg.input_pins[i];
+       for (i = 0; i < cfg->num_inputs; i++) {
+               hda_nid_t nid = cfg->inputs[i].pin;
                if (nid >= 0x12) {
                        alc_set_input_pin(codec, nid, i);
                        if (nid != ALC260_PIN_CD_NID &&
@@ -6799,14 +6811,12 @@ enum {
        PINFIX_HP_DC5750,
 };
 
-static struct alc_pincfg alc260_hp_dc5750_pinfix[] = {
-       { 0x11, 0x90130110 }, /* speaker */
-       { }
-};
-
 static const struct alc_fixup alc260_fixups[] = {
        [PINFIX_HP_DC5750] = {
-               .pins = alc260_hp_dc5750_pinfix
+               .pins = (const struct alc_pincfg[]) {
+                       { 0x11, 0x90130110 }, /* speaker */
+                       { }
+               }
        },
 };
 
@@ -10452,24 +10462,20 @@ enum {
        PINFIX_PB_M5210,
 };
 
-static struct alc_pincfg alc882_abit_aw9d_pinfix[] = {
-       { 0x15, 0x01080104 }, /* side */
-       { 0x16, 0x01011012 }, /* rear */
-       { 0x17, 0x01016011 }, /* clfe */
-       { }
-};
-
-static const struct hda_verb pb_m5210_verbs[] = {
-       { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 },
-       {}
-};
-
 static const struct alc_fixup alc882_fixups[] = {
        [PINFIX_ABIT_AW9D_MAX] = {
-               .pins = alc882_abit_aw9d_pinfix
+               .pins = (const struct alc_pincfg[]) {
+                       { 0x15, 0x01080104 }, /* side */
+                       { 0x16, 0x01011012 }, /* rear */
+                       { 0x17, 0x01016011 }, /* clfe */
+                       { }
+               }
        },
        [PINFIX_PB_M5210] = {
-               .verbs = pb_m5210_verbs
+               .verbs = (const struct hda_verb[]) {
+                       { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 },
+                       {}
+               }
        },
 };
 
@@ -10544,12 +10550,11 @@ static void alc882_auto_init_hp_out(struct hda_codec *codec)
 static void alc882_auto_init_analog_input(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
        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;
                alc_set_input_pin(codec, nid, i);
                if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
                        snd_hda_codec_write(codec, nid, 0,
@@ -10612,24 +10617,23 @@ static void alc882_auto_init_input_src(struct hda_codec *codec)
 static int alc_auto_add_mic_boost(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       int err;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       int i, err;
        hda_nid_t nid;
 
-       nid = spec->autocfg.input_pins[AUTO_PIN_MIC];
-       if (nid && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) {
-               err = add_control(spec, ALC_CTL_WIDGET_VOL,
-                                 "Mic Boost",
-                                 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
-               if (err < 0)
-                       return err;
-       }
-       nid = spec->autocfg.input_pins[AUTO_PIN_FRONT_MIC];
-       if (nid && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) {
-               err = add_control(spec, ALC_CTL_WIDGET_VOL,
-                                 "Front Mic Boost",
+       for (i = 0; i < cfg->num_inputs; i++) {
+               if (cfg->inputs[i].type > AUTO_PIN_FRONT_MIC)
+                       break;
+               nid = cfg->inputs[i].pin;
+               if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) {
+                       char label[32];
+                       snprintf(label, sizeof(label), "%s Boost",
+                                snd_hda_get_input_pin_label(cfg, i));
+                       err = add_control(spec, ALC_CTL_WIDGET_VOL, label, 0,
                                  HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
-               if (err < 0)
-                       return err;
+                       if (err < 0)
+                               return err;
+               }
        }
        return 0;
 }
@@ -14454,19 +14458,18 @@ enum {
        ALC269_FIXUP_SONY_VAIO,
 };
 
-static const struct hda_verb alc269_sony_vaio_fixup_verbs[] = {
-       {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREFGRD},
-       {}
-};
-
 static const struct alc_fixup alc269_fixups[] = {
        [ALC269_FIXUP_SONY_VAIO] = {
-               .verbs = alc269_sony_vaio_fixup_verbs
+               .verbs = (const struct hda_verb[]) {
+                       {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREFGRD},
+                       {}
+               }
        },
 };
 
 static struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x104d, 0x9071, "Sony VAIO", ALC269_FIXUP_SONY_VAIO),
+       SND_PCI_QUIRK(0x104d, 0x9077, "Sony VAIO", ALC269_FIXUP_SONY_VAIO),
        {}
 };
 
@@ -15584,10 +15587,11 @@ static void alc861_auto_init_hp_out(struct hda_codec *codec)
 static void alc861_auto_init_analog_input(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
        int i;
 
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               hda_nid_t nid = spec->autocfg.input_pins[i];
+       for (i = 0; i < cfg->num_inputs; i++) {
+               hda_nid_t nid = cfg->inputs[i].pin;
                if (nid >= 0x0c && nid <= 0x11)
                        alc_set_input_pin(codec, nid, i);
        }
@@ -15818,15 +15822,13 @@ enum {
        PINFIX_FSC_AMILO_PI1505,
 };
 
-static struct alc_pincfg alc861_fsc_amilo_pi1505_pinfix[] = {
-       { 0x0b, 0x0221101f }, /* HP */
-       { 0x0f, 0x90170310 }, /* speaker */
-       { }
-};
-
 static const struct alc_fixup alc861_fixups[] = {
        [PINFIX_FSC_AMILO_PI1505] = {
-               .pins = alc861_fsc_amilo_pi1505_pinfix
+               .pins = (const struct alc_pincfg[]) {
+                       { 0x0b, 0x0221101f }, /* HP */
+                       { 0x0f, 0x90170310 }, /* speaker */
+                       { }
+               }
        },
 };
 
@@ -16578,10 +16580,11 @@ static void alc861vd_auto_init_hp_out(struct hda_codec *codec)
 static void alc861vd_auto_init_analog_input(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
        int i;
 
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               hda_nid_t nid = spec->autocfg.input_pins[i];
+       for (i = 0; i < cfg->num_inputs; i++) {
+               hda_nid_t nid = cfg->inputs[i].pin;
                if (alc_is_input_pin(codec, nid)) {
                        alc_set_input_pin(codec, nid, i);
                        if (nid != ALC861VD_PIN_CD_NID &&
@@ -16793,16 +16796,14 @@ enum {
 };
 
 /* reset GPIO1 */
-static const struct hda_verb alc660vd_fix_asus_gpio1_verbs[] = {
-       {0x01, AC_VERB_SET_GPIO_MASK, 0x03},
-       {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01},
-       {0x01, AC_VERB_SET_GPIO_DATA, 0x01},
-       { }
-};
-
 static const struct alc_fixup alc861vd_fixups[] = {
        [ALC660VD_FIX_ASUS_GPIO1] = {
-               .verbs = alc660vd_fix_asus_gpio1_verbs,
+               .verbs = (const struct hda_verb[]) {
+                       {0x01, AC_VERB_SET_GPIO_MASK, 0x03},
+                       {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01},
+                       {0x01, AC_VERB_SET_GPIO_DATA, 0x01},
+                       { }
+               }
        },
 };
 
@@ -18816,10 +18817,11 @@ static void alc662_auto_init_hp_out(struct hda_codec *codec)
 static void alc662_auto_init_analog_input(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
        int i;
 
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               hda_nid_t nid = spec->autocfg.input_pins[i];
+       for (i = 0; i < cfg->num_inputs; i++) {
+               hda_nid_t nid = cfg->inputs[i].pin;
                if (alc_is_input_pin(codec, nid)) {
                        alc_set_input_pin(codec, nid, i);
                        if (nid != ALC662_PIN_CD_NID &&
@@ -19048,6 +19050,39 @@ static hda_nid_t alc680_adc_nids[3] = {
 /*
  * Analog capture ADC cgange
  */
+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 (!(snd_hda_query_pin_caps(codec, nid) &
+                     AC_PINCAP_PRES_DETECT))
+                       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)
+               __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);
+}
+
 static int alc680_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
                                      struct hda_codec *codec,
                                      unsigned int stream_tag,
@@ -19055,24 +19090,12 @@ static int alc680_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
                                      struct snd_pcm_substream *substream)
 {
        struct alc_spec *spec = codec->spec;
-       struct auto_pin_cfg *cfg = &spec->autocfg;
-       unsigned int pre_mic, pre_line;
-
-       pre_mic  = snd_hda_jack_detect(codec, cfg->input_pins[AUTO_PIN_MIC]);
-       pre_line = snd_hda_jack_detect(codec, cfg->input_pins[AUTO_PIN_LINE]);
 
+       spec->cur_adc = 0x07;
        spec->cur_adc_stream_tag = stream_tag;
        spec->cur_adc_format = format;
 
-       if (pre_mic || pre_line) {
-               if (pre_mic)
-                       snd_hda_codec_setup_stream(codec, 0x08, stream_tag, 0,
-                                                                       format);
-               else
-                       snd_hda_codec_setup_stream(codec, 0x09, stream_tag, 0,
-                                                                       format);
-       } else
-               snd_hda_codec_setup_stream(codec, 0x07, stream_tag, 0, format);
+       alc680_rec_autoswitch(codec);
        return 0;
 }
 
@@ -19158,6 +19181,7 @@ static struct hda_verb alc680_init_verbs[] = {
 
        {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT   | AC_USRSP_EN},
        {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT  | AC_USRSP_EN},
+       {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT  | AC_USRSP_EN},
 
        { }
 };
@@ -19170,25 +19194,11 @@ static void alc680_base_setup(struct hda_codec *codec)
        spec->autocfg.hp_pins[0] = 0x16;
        spec->autocfg.speaker_pins[0] = 0x14;
        spec->autocfg.speaker_pins[1] = 0x15;
-       spec->autocfg.input_pins[AUTO_PIN_MIC] = 0x18;
-       spec->autocfg.input_pins[AUTO_PIN_LINE] = 0x19;
-}
-
-static void alc680_rec_autoswitch(struct hda_codec *codec)
-{
-       struct alc_spec *spec = codec->spec;
-       struct auto_pin_cfg *cfg = &spec->autocfg;
-       unsigned int present;
-       hda_nid_t new_adc;
-
-       present = snd_hda_jack_detect(codec, cfg->input_pins[AUTO_PIN_MIC]);
-
-       new_adc = present ? 0x8 : 0x7;
-       __snd_hda_codec_cleanup_stream(codec, !present ? 0x8 : 0x7, 1);
-       snd_hda_codec_setup_stream(codec, new_adc,
-                                  spec->cur_adc_stream_tag, 0,
-                                  spec->cur_adc_format);
-
+       spec->autocfg.num_inputs = 2;
+       spec->autocfg.inputs[0].pin = 0x18;
+       spec->autocfg.inputs[0].type = AUTO_PIN_MIC;
+       spec->autocfg.inputs[1].pin = 0x19;
+       spec->autocfg.inputs[1].type = AUTO_PIN_LINE;
 }
 
 static void alc680_unsol_event(struct hda_codec *codec,