Merge branch 'fix/hda' into topic/hda
[pandora-kernel.git] / sound / pci / hda / patch_realtek.c
index 3091e0c..4695915 100644 (file)
@@ -299,6 +299,18 @@ struct alc_customize_define {
 
 struct alc_fixup;
 
+struct alc_multi_io {
+       hda_nid_t pin;          /* multi-io widget pin NID */
+       hda_nid_t dac;          /* DAC to be connected */
+       unsigned int ctl_in;    /* cached input-pin control value */
+};
+
+enum {
+       ALC_AUTOMUTE_PIN,       /* change the pin control */
+       ALC_AUTOMUTE_AMP,       /* mute/unmute the pin AMP */
+       ALC_AUTOMUTE_MIXER,     /* mute/unmute mixer widget AMP */
+};
+
 struct alc_spec {
        /* codec parameterization */
        struct snd_kcontrol_new *mixers[5];     /* mixer arrays */
@@ -375,17 +387,27 @@ struct alc_spec {
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        void (*power_hook)(struct hda_codec *codec);
 #endif
+       void (*shutup)(struct hda_codec *codec);
 
        /* for pin sensing */
-       unsigned int sense_updated: 1;
        unsigned int jack_present: 1;
-       unsigned int master_sw: 1;
+       unsigned int line_jack_present:1;
+       unsigned int master_mute:1;
        unsigned int auto_mic:1;
+       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 */
+       unsigned int automute_hp_lo:1;  /* both HP and LO available */
 
        /* other flags */
        unsigned int no_analog :1; /* digital I/O only */
        unsigned int dual_adc_switch:1; /* switch ADCs (for ALC275) */
        unsigned int single_input_src:1;
+
+       /* auto-mute control */
+       int automute_mode;
+       hda_nid_t automute_mixer_nid[AUTO_CFG_MAX_OUTS];
+
        int init_amp;
        int codec_variant;      /* flag for other variants */
 
@@ -403,6 +425,10 @@ struct alc_spec {
        int fixup_id;
        const struct alc_fixup *fixup_list;
        const char *fixup_name;
+
+       /* multi-io */
+       int multi_ios;
+       struct alc_multi_io multi_io[4];
 };
 
 /*
@@ -1051,42 +1077,105 @@ static int alc_init_jacks(struct hda_codec *codec)
        return 0;
 }
 
-static void alc_automute_speaker(struct hda_codec *codec, int pinctl)
+static int detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins)
 {
-       struct alc_spec *spec = codec->spec;
-       unsigned int mute;
-       hda_nid_t nid;
-       int i;
+       int i, present = 0;
 
-       spec->jack_present = 0;
-       for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) {
-               nid = spec->autocfg.hp_pins[i];
+       for (i = 0; i < num_pins; i++) {
+               hda_nid_t nid = pins[i];
                if (!nid)
                        break;
                snd_hda_input_jack_report(codec, nid);
-               spec->jack_present |= snd_hda_jack_detect(codec, nid);
+               present |= snd_hda_jack_detect(codec, nid);
        }
+       return present;
+}
+
+static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
+                       bool mute, bool hp_out)
+{
+       struct alc_spec *spec = codec->spec;
+       unsigned int mute_bits = mute ? HDA_AMP_MUTE : 0;
+       unsigned int pin_bits = mute ? 0 : (hp_out ? PIN_HP : PIN_OUT);
+       int i;
 
-       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];
+       for (i = 0; i < num_pins; i++) {
+               hda_nid_t nid = pins[i];
                if (!nid)
                        break;
-               if (pinctl) {
+               switch (spec->automute_mode) {
+               case ALC_AUTOMUTE_PIN:
                        snd_hda_codec_write(codec, nid, 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                   spec->jack_present ? 0 : PIN_OUT);
-               } else {
+                                           AC_VERB_SET_PIN_WIDGET_CONTROL,
+                                           pin_bits);
+                       break;
+               case ALC_AUTOMUTE_AMP:
                        snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
-                                        HDA_AMP_MUTE, mute);
+                                                HDA_AMP_MUTE, mute_bits);
+                       break;
+               case ALC_AUTOMUTE_MIXER:
+                       nid = spec->automute_mixer_nid[i];
+                       if (!nid)
+                               break;
+                       snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 0,
+                                                HDA_AMP_MUTE, mute_bits);
+                       snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 1,
+                                                HDA_AMP_MUTE, mute_bits);
+                       break;
                }
        }
 }
 
-static void alc_automute_pin(struct hda_codec *codec)
+/* Toggle internal speakers muting */
+static void update_speakers(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       int on;
+
+       if (!spec->automute)
+               on = 0;
+       else
+               on = spec->jack_present | spec->line_jack_present;
+       on |= spec->master_mute;
+       do_automute(codec, ARRAY_SIZE(spec->autocfg.speaker_pins),
+                   spec->autocfg.speaker_pins, on, false);
+
+       /* toggle line-out mutes if needed, too */
+       /* if LO is a copy of either HP or Speaker, don't need to handle it */
+       if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0] ||
+           spec->autocfg.line_out_pins[0] == spec->autocfg.speaker_pins[0])
+               return;
+       if (!spec->automute_lines || !spec->automute)
+               on = 0;
+       else
+               on = spec->jack_present;
+       on |= spec->master_mute;
+       do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins),
+                   spec->autocfg.line_out_pins, on, false);
+}
+
+static void alc_hp_automute(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+
+       if (!spec->automute)
+               return;
+       spec->jack_present =
+               detect_jacks(codec, ARRAY_SIZE(spec->autocfg.hp_pins),
+                            spec->autocfg.hp_pins);
+       update_speakers(codec);
+}
+
+static void alc_line_automute(struct hda_codec *codec)
 {
-       alc_automute_speaker(codec, 1);
+       struct alc_spec *spec = codec->spec;
+
+       if (!spec->automute || !spec->detect_line)
+               return;
+       spec->line_jack_present =
+               detect_jacks(codec, ARRAY_SIZE(spec->autocfg.line_out_pins),
+                            spec->autocfg.line_out_pins);
+       update_speakers(codec);
 }
 
 static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
@@ -1184,7 +1273,10 @@ static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res)
                res >>= 26;
        switch (res) {
        case ALC880_HP_EVENT:
-               alc_automute_pin(codec);
+               alc_hp_automute(codec);
+               break;
+       case ALC880_FRONT_EVENT:
+               alc_line_automute(codec);
                break;
        case ALC880_MIC_EVENT:
                alc_mic_automute(codec);
@@ -1194,7 +1286,8 @@ static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res)
 
 static void alc_inithook(struct hda_codec *codec)
 {
-       alc_automute_pin(codec);
+       alc_hp_automute(codec);
+       alc_line_automute(codec);
        alc_mic_automute(codec);
 }
 
@@ -1236,6 +1329,43 @@ static void set_eapd(struct hda_codec *codec, hda_nid_t nid, int on)
                                    on ? 2 : 0);
 }
 
+/* turn on/off EAPD controls of the codec */
+static void alc_auto_setup_eapd(struct hda_codec *codec, bool on)
+{
+       /* We currently only handle front, HP */
+       switch (codec->vendor_id) {
+       case 0x10ec0260:
+               set_eapd(codec, 0x0f, on);
+               set_eapd(codec, 0x10, on);
+               break;
+       case 0x10ec0262:
+       case 0x10ec0267:
+       case 0x10ec0268:
+       case 0x10ec0269:
+       case 0x10ec0270:
+       case 0x10ec0272:
+       case 0x10ec0660:
+       case 0x10ec0662:
+       case 0x10ec0663:
+       case 0x10ec0665:
+       case 0x10ec0862:
+       case 0x10ec0889:
+       case 0x10ec0892:
+               set_eapd(codec, 0x14, on);
+               set_eapd(codec, 0x15, on);
+               break;
+       }
+}
+
+/* generic shutup callback;
+ * just turning off EPAD and a little pause for avoiding pop-noise
+ */
+static void alc_eapd_shutup(struct hda_codec *codec)
+{
+       alc_auto_setup_eapd(codec, false);
+       msleep(200);
+}
+
 static void alc_auto_init_amp(struct hda_codec *codec, int type)
 {
        unsigned int tmp;
@@ -1251,27 +1381,7 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type)
                snd_hda_sequence_write(codec, alc_gpio3_init_verbs);
                break;
        case ALC_INIT_DEFAULT:
-               switch (codec->vendor_id) {
-               case 0x10ec0260:
-                       set_eapd(codec, 0x0f, 1);
-                       set_eapd(codec, 0x10, 1);
-                       break;
-               case 0x10ec0262:
-               case 0x10ec0267:
-               case 0x10ec0268:
-               case 0x10ec0269:
-               case 0x10ec0270:
-               case 0x10ec0272:
-               case 0x10ec0660:
-               case 0x10ec0662:
-               case 0x10ec0663:
-               case 0x10ec0665:
-               case 0x10ec0862:
-               case 0x10ec0889:
-                       set_eapd(codec, 0x14, 1);
-                       set_eapd(codec, 0x15, 1);
-                       break;
-               }
+               alc_auto_setup_eapd(codec, true);
                switch (codec->vendor_id) {
                case 0x10ec0260:
                        snd_hda_codec_write(codec, 0x1a, 0,
@@ -1315,20 +1425,128 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type)
        }
 }
 
+static int alc_automute_mode_info(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_info *uinfo)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct alc_spec *spec = codec->spec;
+       static const char * const texts2[] = {
+               "Disabled", "Enabled"
+       };
+       static const char * const texts3[] = {
+               "Disabled", "Speaker Only", "Line-Out+Speaker"
+       };
+       const char * const *texts;
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       if (spec->automute_hp_lo) {
+               uinfo->value.enumerated.items = 3;
+               texts = texts3;
+       } else {
+               uinfo->value.enumerated.items = 2;
+               texts = texts2;
+       }
+       if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+               uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+       strcpy(uinfo->value.enumerated.name,
+              texts[uinfo->value.enumerated.item]);
+       return 0;
+}
+
+static int alc_automute_mode_get(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct alc_spec *spec = codec->spec;
+       unsigned int val;
+       if (!spec->automute)
+               val = 0;
+       else if (!spec->automute_lines)
+               val = 1;
+       else
+               val = 2;
+       ucontrol->value.enumerated.item[0] = val;
+       return 0;
+}
+
+static int alc_automute_mode_put(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct alc_spec *spec = codec->spec;
+
+       switch (ucontrol->value.enumerated.item[0]) {
+       case 0:
+               if (!spec->automute)
+                       return 0;
+               spec->automute = 0;
+               break;
+       case 1:
+               if (spec->automute && !spec->automute_lines)
+                       return 0;
+               spec->automute = 1;
+               spec->automute_lines = 0;
+               break;
+       case 2:
+               if (!spec->automute_hp_lo)
+                       return -EINVAL;
+               if (spec->automute && spec->automute_lines)
+                       return 0;
+               spec->automute = 1;
+               spec->automute_lines = 1;
+               break;
+       default:
+               return -EINVAL;
+       }
+       update_speakers(codec);
+       return 1;
+}
+
+static struct snd_kcontrol_new alc_automute_mode_enum = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "Auto-Mute Mode",
+       .info = alc_automute_mode_info,
+       .get = alc_automute_mode_get,
+       .put = alc_automute_mode_put,
+};
+
+static struct snd_kcontrol_new *alc_kcontrol_new(struct alc_spec *spec);
+
+static int alc_add_automute_mode_enum(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       struct snd_kcontrol_new *knew;
+
+       knew = alc_kcontrol_new(spec);
+       if (!knew)
+               return -ENOMEM;
+       *knew = alc_automute_mode_enum;
+       knew->name = kstrdup("Auto-Mute Mode", GFP_KERNEL);
+       if (!knew->name)
+               return -ENOMEM;
+       return 0;
+}
+
 static void alc_init_auto_hp(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
        struct auto_pin_cfg *cfg = &spec->autocfg;
+       int present = 0;
        int i;
 
-       if (!cfg->hp_pins[0]) {
-               if (cfg->line_out_type != AUTO_PIN_HP_OUT)
-                       return;
-       }
+       if (cfg->hp_pins[0])
+               present++;
+       if (cfg->line_out_pins[0])
+               present++;
+       if (cfg->speaker_pins[0])
+               present++;
+       if (present < 2) /* need two different output types */
+               return;
+       if (present == 3)
+               spec->automute_hp_lo = 1; /* both HP and LO automute */
 
        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;
@@ -1341,13 +1559,41 @@ static void alc_init_auto_hp(struct hda_codec *codec)
        }
 
        for (i = 0; i < cfg->hp_outs; i++) {
+               hda_nid_t nid = cfg->hp_pins[i];
+               if (!(snd_hda_query_pin_caps(codec, nid) &
+                     AC_PINCAP_PRES_DETECT))
+                       continue;
                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,
+                           nid);
+               snd_hda_codec_write_cache(codec, nid, 0,
                                  AC_VERB_SET_UNSOLICITED_ENABLE,
                                  AC_USRSP_EN | ALC880_HP_EVENT);
+               spec->automute = 1;
+               spec->automute_mode = ALC_AUTOMUTE_PIN;
+       }
+       if (spec->automute && cfg->line_out_pins[0] &&
+           cfg->line_out_pins[0] != cfg->hp_pins[0] &&
+           cfg->line_out_pins[0] != cfg->speaker_pins[0]) {
+               for (i = 0; i < cfg->line_outs; i++) {
+                       hda_nid_t nid = cfg->line_out_pins[i];
+                       if (!(snd_hda_query_pin_caps(codec, nid) &
+                             AC_PINCAP_PRES_DETECT))
+                               continue;
+                       snd_printdd("realtek: Enable Line-Out auto-muting "
+                                   "on NID 0x%x\n", nid);
+                       snd_hda_codec_write_cache(codec, nid, 0,
+                                       AC_VERB_SET_UNSOLICITED_ENABLE,
+                                       AC_USRSP_EN | ALC880_FRONT_EVENT);
+                       spec->detect_line = 1;
+               }
+               spec->automute_lines = 1;
+       }
+
+       if (spec->automute) {
+               /* create a control for automute mode */
+               alc_add_automute_mode_enum(codec);
+               spec->unsol_event = alc_sku_unsol_event;
        }
-       spec->unsol_event = alc_sku_unsol_event;
 }
 
 static void alc_init_auto_mic(struct hda_codec *codec)
@@ -1583,9 +1829,6 @@ do_sku:
                                return 1;
                spec->autocfg.hp_pins[0] = nid;
        }
-
-       alc_init_auto_hp(codec);
-       alc_init_auto_mic(codec);
        return 1;
 }
 
@@ -1598,9 +1841,10 @@ static void alc_ssid_check(struct hda_codec *codec,
                snd_printd("realtek: "
                           "Enable default setup for auto mode as fallback\n");
                spec->init_amp = ALC_INIT_DEFAULT;
-               alc_init_auto_hp(codec);
-               alc_init_auto_mic(codec);
        }
+
+       alc_init_auto_hp(codec);
+       alc_init_auto_mic(codec);
 }
 
 /*
@@ -1943,22 +2187,6 @@ static struct hda_verb alc888_fujitsu_xa3530_verbs[] = {
        {}
 };
 
-static void alc_automute_amp(struct hda_codec *codec)
-{
-       alc_automute_speaker(codec, 0);
-}
-
-static void alc_automute_amp_unsol_event(struct hda_codec *codec,
-                                        unsigned int res)
-{
-       if (codec->vendor_id == 0x10ec0880)
-               res >>= 28;
-       else
-               res >>= 26;
-       if (res == ALC880_HP_EVENT)
-               alc_automute_amp(codec);
-}
-
 static void alc889_automute_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
@@ -1969,12 +2197,14 @@ static void alc889_automute_setup(struct hda_codec *codec)
        spec->autocfg.speaker_pins[2] = 0x17;
        spec->autocfg.speaker_pins[3] = 0x19;
        spec->autocfg.speaker_pins[4] = 0x1a;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 static void alc889_intel_init_hook(struct hda_codec *codec)
 {
        alc889_coef_init(codec);
-       alc_automute_amp(codec);
+       alc_hp_automute(codec);
 }
 
 static void alc888_fujitsu_xa3530_setup(struct hda_codec *codec)
@@ -1985,6 +2215,8 @@ static void alc888_fujitsu_xa3530_setup(struct hda_codec *codec)
        spec->autocfg.hp_pins[1] = 0x1b; /* hp */
        spec->autocfg.speaker_pins[0] = 0x14; /* speaker */
        spec->autocfg.speaker_pins[1] = 0x15; /* bass */
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 /*
@@ -2267,6 +2499,8 @@ static void alc888_acer_aspire_4930g_setup(struct hda_codec *codec)
        spec->autocfg.speaker_pins[0] = 0x14;
        spec->autocfg.speaker_pins[1] = 0x16;
        spec->autocfg.speaker_pins[2] = 0x17;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 static void alc888_acer_aspire_6530g_setup(struct hda_codec *codec)
@@ -2277,6 +2511,8 @@ static void alc888_acer_aspire_6530g_setup(struct hda_codec *codec)
        spec->autocfg.speaker_pins[0] = 0x14;
        spec->autocfg.speaker_pins[1] = 0x16;
        spec->autocfg.speaker_pins[2] = 0x17;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 static void alc888_acer_aspire_7730g_setup(struct hda_codec *codec)
@@ -2287,6 +2523,8 @@ static void alc888_acer_aspire_7730g_setup(struct hda_codec *codec)
        spec->autocfg.speaker_pins[0] = 0x14;
        spec->autocfg.speaker_pins[1] = 0x16;
        spec->autocfg.speaker_pins[2] = 0x17;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 static void alc889_acer_aspire_8930g_setup(struct hda_codec *codec)
@@ -2297,6 +2535,8 @@ static void alc889_acer_aspire_8930g_setup(struct hda_codec *codec)
        spec->autocfg.speaker_pins[0] = 0x14;
        spec->autocfg.speaker_pins[1] = 0x16;
        spec->autocfg.speaker_pins[2] = 0x1b;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 /*
@@ -3372,11 +3612,13 @@ static void alc880_uniwill_setup(struct hda_codec *codec)
        spec->autocfg.hp_pins[0] = 0x14;
        spec->autocfg.speaker_pins[0] = 0x15;
        spec->autocfg.speaker_pins[0] = 0x16;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 static void alc880_uniwill_init_hook(struct hda_codec *codec)
 {
-       alc_automute_amp(codec);
+       alc_hp_automute(codec);
        alc88x_simple_mic_automute(codec);
 }
 
@@ -3391,7 +3633,7 @@ static void alc880_uniwill_unsol_event(struct hda_codec *codec,
                alc88x_simple_mic_automute(codec);
                break;
        default:
-               alc_automute_amp_unsol_event(codec, res);
+               alc_sku_unsol_event(codec, res);
                break;
        }
 }
@@ -3402,6 +3644,8 @@ static void alc880_uniwill_p53_setup(struct hda_codec *codec)
 
        spec->autocfg.hp_pins[0] = 0x14;
        spec->autocfg.speaker_pins[0] = 0x15;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 static void alc880_uniwill_p53_dcvol_automute(struct hda_codec *codec)
@@ -3426,7 +3670,7 @@ static void alc880_uniwill_p53_unsol_event(struct hda_codec *codec,
        if ((res >> 28) == ALC880_DCVOL_EVENT)
                alc880_uniwill_p53_dcvol_automute(codec);
        else
-               alc_automute_amp_unsol_event(codec, res);
+               alc_sku_unsol_event(codec, res);
 }
 
 /*
@@ -3670,6 +3914,8 @@ static void alc880_lg_setup(struct hda_codec *codec)
 
        spec->autocfg.hp_pins[0] = 0x1b;
        spec->autocfg.speaker_pins[0] = 0x17;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 /*
@@ -3754,6 +4000,8 @@ static void alc880_lg_lw_setup(struct hda_codec *codec)
 
        spec->autocfg.hp_pins[0] = 0x1b;
        spec->autocfg.speaker_pins[0] = 0x14;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 static struct snd_kcontrol_new alc880_medion_rim_mixer[] = {
@@ -3801,7 +4049,7 @@ static struct hda_verb alc880_medion_rim_init_verbs[] = {
 static void alc880_medion_rim_automute(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       alc_automute_amp(codec);
+       alc_hp_automute(codec);
        /* toggle EAPD */
        if (spec->jack_present)
                snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 0);
@@ -3825,6 +4073,8 @@ static void alc880_medion_rim_setup(struct hda_codec *codec)
 
        spec->autocfg.hp_pins[0] = 0x14;
        spec->autocfg.speaker_pins[0] = 0x1b;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -4193,6 +4443,10 @@ static int alc_build_pcms(struct hda_codec *codec)
 
 static inline void alc_shutup(struct hda_codec *codec)
 {
+       struct alc_spec *spec = codec->spec;
+
+       if (spec && spec->shutup)
+               spec->shutup(codec);
        snd_hda_shutup_pins(codec);
 }
 
@@ -4226,28 +4480,7 @@ static void alc_free(struct hda_codec *codec)
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 static void alc_power_eapd(struct hda_codec *codec)
 {
-       /* We currently only handle front, HP */
-       switch (codec->vendor_id) {
-       case 0x10ec0260:
-               set_eapd(codec, 0x0f, 0);
-               set_eapd(codec, 0x10, 0);
-               break;
-       case 0x10ec0262:
-       case 0x10ec0267:
-       case 0x10ec0268:
-       case 0x10ec0269:
-       case 0x10ec0270:
-       case 0x10ec0272:
-       case 0x10ec0660:
-       case 0x10ec0662:
-       case 0x10ec0663:
-       case 0x10ec0665:
-       case 0x10ec0862:
-       case 0x10ec0889:
-               set_eapd(codec, 0x14, 0);
-               set_eapd(codec, 0x15, 0);
-               break;
-       }
+       alc_auto_setup_eapd(codec, false);
 }
 
 static int alc_suspend(struct hda_codec *codec, pm_message_t state)
@@ -4263,6 +4496,7 @@ static int alc_suspend(struct hda_codec *codec, pm_message_t state)
 #ifdef SND_HDA_NEEDS_RESUME
 static int alc_resume(struct hda_codec *codec)
 {
+       msleep(150); /* to avoid pop noise */
        codec->patch_ops.init(codec);
        snd_hda_codec_resume_amp(codec);
        snd_hda_codec_resume_cache(codec);
@@ -4794,7 +5028,7 @@ static struct alc_config_preset alc880_presets[] = {
                .input_mux = &alc880_f1734_capture_source,
                .unsol_event = alc880_uniwill_p53_unsol_event,
                .setup = alc880_uniwill_p53_setup,
-               .init_hook = alc_automute_amp,
+               .init_hook = alc_hp_automute,
        },
        [ALC880_ASUS] = {
                .mixers = { alc880_asus_mixer },
@@ -4885,7 +5119,7 @@ static struct alc_config_preset alc880_presets[] = {
                .input_mux = &alc880_capture_source,
                .unsol_event = alc880_uniwill_p53_unsol_event,
                .setup = alc880_uniwill_p53_setup,
-               .init_hook = alc_automute_amp,
+               .init_hook = alc_hp_automute,
        },
        [ALC880_FUJITSU] = {
                .mixers = { alc880_fujitsu_mixer },
@@ -4900,7 +5134,7 @@ static struct alc_config_preset alc880_presets[] = {
                .input_mux = &alc880_capture_source,
                .unsol_event = alc880_uniwill_p53_unsol_event,
                .setup = alc880_uniwill_p53_setup,
-               .init_hook = alc_automute_amp,
+               .init_hook = alc_hp_automute,
        },
        [ALC880_CLEVO] = {
                .mixers = { alc880_three_stack_mixer },
@@ -4925,9 +5159,9 @@ static struct alc_config_preset alc880_presets[] = {
                .channel_mode = alc880_lg_ch_modes,
                .need_dac_fix = 1,
                .input_mux = &alc880_lg_capture_source,
-               .unsol_event = alc_automute_amp_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc880_lg_setup,
-               .init_hook = alc_automute_amp,
+               .init_hook = alc_hp_automute,
 #ifdef CONFIG_SND_HDA_POWER_SAVE
                .loopbacks = alc880_lg_loopbacks,
 #endif
@@ -4942,9 +5176,9 @@ static struct alc_config_preset alc880_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc880_lg_lw_modes),
                .channel_mode = alc880_lg_lw_modes,
                .input_mux = &alc880_lg_lw_capture_source,
-               .unsol_event = alc_automute_amp_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc880_lg_lw_setup,
-               .init_hook = alc_automute_amp,
+               .init_hook = alc_hp_automute,
        },
        [ALC880_MEDION_RIM] = {
                .mixers = { alc880_medion_rim_mixer },
@@ -4990,14 +5224,19 @@ static struct snd_kcontrol_new alc880_control_templates[] = {
        HDA_BIND_MUTE(NULL, 0, 0, 0),
 };
 
+static struct snd_kcontrol_new *alc_kcontrol_new(struct alc_spec *spec)
+{
+       snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32);
+       return snd_array_new(&spec->kctls);
+}
+
 /* add dynamic controls */
 static int add_control(struct alc_spec *spec, int type, const char *name,
                       int cidx, unsigned long val)
 {
        struct snd_kcontrol_new *knew;
 
-       snd_array_init(&spec->kctls, sizeof(*knew), 32);
-       knew = snd_array_new(&spec->kctls);
+       knew = alc_kcontrol_new(spec);
        if (!knew)
                return -ENOMEM;
        *knew = alc880_control_templates[type];
@@ -5078,10 +5317,13 @@ static int alc880_auto_fill_dac_nids(struct alc_spec *spec,
        return 0;
 }
 
-static const char *alc_get_line_out_pfx(const struct auto_pin_cfg *cfg,
+static const char *alc_get_line_out_pfx(struct alc_spec *spec,
                                        bool can_be_master)
 {
-       if (!cfg->hp_outs && !cfg->speaker_outs && can_be_master)
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+
+       if (cfg->line_outs == 1 && !spec->multi_ios &&
+           !cfg->hp_outs && !cfg->speaker_outs && can_be_master)
                return "Master";
 
        switch (cfg->line_out_type) {
@@ -5092,7 +5334,7 @@ static const char *alc_get_line_out_pfx(const struct auto_pin_cfg *cfg,
        case AUTO_PIN_HP_OUT:
                return "Headphone";
        default:
-               if (cfg->line_outs == 1)
+               if (cfg->line_outs == 1 && !spec->multi_ios)
                        return "PCM";
                break;
        }
@@ -5106,11 +5348,15 @@ static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec,
        static const char * const chname[4] = {
                "Front", "Surround", NULL /*CLFE*/, "Side"
        };
-       const char *pfx = alc_get_line_out_pfx(cfg, false);
+       const char *pfx = alc_get_line_out_pfx(spec, false);
        hda_nid_t nid;
-       int i, err;
+       int i, err, noutputs;
 
-       for (i = 0; i < cfg->line_outs; i++) {
+       noutputs = cfg->line_outs;
+       if (spec->multi_ios > 0)
+               noutputs += spec->multi_ios;
+
+       for (i = 0; i < noutputs; i++) {
                if (!spec->multiout.dac_nids[i])
                        continue;
                nid = alc880_idx_to_mixer(alc880_dac_to_idx(spec->multiout.dac_nids[i]));
@@ -5376,6 +5622,8 @@ static void alc880_auto_init_input_src(struct hda_codec *codec)
        }
 }
 
+static int alc_auto_add_multi_channel_mode(struct hda_codec *codec);
+
 /* parse the BIOS configuration and set up the alc_spec */
 /* return 1 if successful, 0 if the proper config is not found,
  * or a negative error code
@@ -5394,6 +5642,9 @@ static int alc880_parse_auto_config(struct hda_codec *codec)
                return 0; /* can't find valid BIOS pin config */
 
        err = alc880_auto_fill_dac_nids(spec, &spec->autocfg);
+       if (err < 0)
+               return err;
+       err = alc_auto_add_multi_channel_mode(codec);
        if (err < 0)
                return err;
        err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg);
@@ -5903,21 +6154,14 @@ static struct snd_kcontrol_new alc260_input_mixer[] = {
 };
 
 /* update HP, line and mono out pins according to the master switch */
-static void alc260_hp_master_update(struct hda_codec *codec,
-                                   hda_nid_t hp, hda_nid_t line,
-                                   hda_nid_t mono)
+static void alc260_hp_master_update(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       unsigned int val = spec->master_sw ? PIN_HP : 0;
-       /* change HP and line-out pins */
-       snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-                           val);
-       snd_hda_codec_write(codec, line, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-                           val);
-       /* mono (speaker) depending on the HP jack sense */
-       val = (val && !spec->jack_present) ? PIN_OUT : 0;
-       snd_hda_codec_write(codec, mono, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-                           val);
+
+       /* change HP pins */
+       do_automute(codec, ARRAY_SIZE(spec->autocfg.hp_pins),
+                   spec->autocfg.hp_pins, spec->master_mute, true);
+       update_speakers(codec);
 }
 
 static int alc260_hp_master_sw_get(struct snd_kcontrol *kcontrol,
@@ -5925,7 +6169,7 @@ static int alc260_hp_master_sw_get(struct snd_kcontrol *kcontrol,
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct alc_spec *spec = codec->spec;
-       *ucontrol->value.integer.value = spec->master_sw;
+       *ucontrol->value.integer.value = !spec->master_mute;
        return 0;
 }
 
@@ -5934,16 +6178,12 @@ static int alc260_hp_master_sw_put(struct snd_kcontrol *kcontrol,
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct alc_spec *spec = codec->spec;
-       int val = !!*ucontrol->value.integer.value;
-       hda_nid_t hp, line, mono;
+       int val = !*ucontrol->value.integer.value;
 
-       if (val == spec->master_sw)
+       if (val == spec->master_mute)
                return 0;
-       spec->master_sw = val;
-       hp = (kcontrol->private_value >> 16) & 0xff;
-       line = (kcontrol->private_value >> 8) & 0xff;
-       mono = kcontrol->private_value & 0xff;
-       alc260_hp_master_update(codec, hp, line, mono);
+       spec->master_mute = val;
+       alc260_hp_master_update(codec);
        return 1;
 }
 
@@ -5955,7 +6195,6 @@ static struct snd_kcontrol_new alc260_hp_output_mixer[] = {
                .info = snd_ctl_boolean_mono_info,
                .get = alc260_hp_master_sw_get,
                .put = alc260_hp_master_sw_put,
-               .private_value = (0x0f << 16) | (0x10 << 8) | 0x11
        },
        HDA_CODEC_VOLUME("Front Playback Volume", 0x08, 0x0, HDA_OUTPUT),
        HDA_BIND_MUTE("Front Playback Switch", 0x08, 2, HDA_INPUT),
@@ -5972,18 +6211,15 @@ static struct hda_verb alc260_hp_unsol_verbs[] = {
        {},
 };
 
-static void alc260_hp_automute(struct hda_codec *codec)
+static void alc260_hp_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
-       spec->jack_present = snd_hda_jack_detect(codec, 0x10);
-       alc260_hp_master_update(codec, 0x0f, 0x10, 0x11);
-}
-
-static void alc260_hp_unsol_event(struct hda_codec *codec, unsigned int res)
-{
-       if ((res >> 26) == ALC880_HP_EVENT)
-               alc260_hp_automute(codec);
+       spec->autocfg.hp_pins[0] = 0x0f;
+       spec->autocfg.speaker_pins[0] = 0x10;
+       spec->autocfg.speaker_pins[1] = 0x11;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_PIN;
 }
 
 static struct snd_kcontrol_new alc260_hp_3013_mixer[] = {
@@ -5994,7 +6230,6 @@ static struct snd_kcontrol_new alc260_hp_3013_mixer[] = {
                .info = snd_ctl_boolean_mono_info,
                .get = alc260_hp_master_sw_get,
                .put = alc260_hp_master_sw_put,
-               .private_value = (0x15 << 16) | (0x10 << 8) | 0x11
        },
        HDA_CODEC_VOLUME("Front Playback Volume", 0x09, 0x0, HDA_OUTPUT),
        HDA_CODEC_MUTE("Front Playback Switch", 0x10, 0x0, HDA_OUTPUT),
@@ -6007,6 +6242,17 @@ static struct snd_kcontrol_new alc260_hp_3013_mixer[] = {
        { } /* end */
 };
 
+static void alc260_hp_3013_setup(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+
+       spec->autocfg.hp_pins[0] = 0x15;
+       spec->autocfg.speaker_pins[0] = 0x10;
+       spec->autocfg.speaker_pins[1] = 0x11;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_PIN;
+}
+
 static struct hda_bind_ctls alc260_dc7600_bind_master_vol = {
        .ops = &snd_hda_bind_vol,
        .values = {
@@ -6039,38 +6285,16 @@ static struct hda_verb alc260_hp_3013_unsol_verbs[] = {
        {},
 };
 
-static void alc260_hp_3013_automute(struct hda_codec *codec)
+static void alc260_hp_3012_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
-       spec->jack_present = snd_hda_jack_detect(codec, 0x15);
-       alc260_hp_master_update(codec, 0x15, 0x10, 0x11);
-}
-
-static void alc260_hp_3013_unsol_event(struct hda_codec *codec,
-                                      unsigned int res)
-{
-       if ((res >> 26) == ALC880_HP_EVENT)
-               alc260_hp_3013_automute(codec);
-}
-
-static void alc260_hp_3012_automute(struct hda_codec *codec)
-{
-       unsigned int bits = snd_hda_jack_detect(codec, 0x10) ? 0 : PIN_OUT;
-
-       snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-                           bits);
-       snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-                           bits);
-       snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-                           bits);
-}
-
-static void alc260_hp_3012_unsol_event(struct hda_codec *codec,
-                                      unsigned int res)
-{
-       if ((res >> 26) == ALC880_HP_EVENT)
-               alc260_hp_3012_automute(codec);
+       spec->autocfg.hp_pins[0] = 0x10;
+       spec->autocfg.speaker_pins[0] = 0x0f;
+       spec->autocfg.speaker_pins[1] = 0x11;
+       spec->autocfg.speaker_pins[2] = 0x15;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_PIN;
 }
 
 /* Fujitsu S702x series laptops.  ALC260 pin usage: Mic/Line jack = 0x12,
@@ -7195,8 +7419,9 @@ static struct alc_config_preset alc260_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc260_modes),
                .channel_mode = alc260_modes,
                .input_mux = &alc260_capture_source,
-               .unsol_event = alc260_hp_unsol_event,
-               .init_hook = alc260_hp_automute,
+               .unsol_event = alc_sku_unsol_event,
+               .setup = alc260_hp_setup,
+               .init_hook = alc_inithook,
        },
        [ALC260_HP_DC7600] = {
                .mixers = { alc260_hp_dc7600_mixer,
@@ -7210,8 +7435,9 @@ static struct alc_config_preset alc260_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc260_modes),
                .channel_mode = alc260_modes,
                .input_mux = &alc260_capture_source,
-               .unsol_event = alc260_hp_3012_unsol_event,
-               .init_hook = alc260_hp_3012_automute,
+               .unsol_event = alc_sku_unsol_event,
+               .setup = alc260_hp_3012_setup,
+               .init_hook = alc_inithook,
        },
        [ALC260_HP_3013] = {
                .mixers = { alc260_hp_3013_mixer,
@@ -7225,8 +7451,9 @@ static struct alc_config_preset alc260_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc260_modes),
                .channel_mode = alc260_modes,
                .input_mux = &alc260_capture_source,
-               .unsol_event = alc260_hp_3013_unsol_event,
-               .init_hook = alc260_hp_3013_automute,
+               .unsol_event = alc_sku_unsol_event,
+               .setup = alc260_hp_3013_setup,
+               .init_hook = alc_inithook,
        },
        [ALC260_FUJITSU_S702X] = {
                .mixers = { alc260_fujitsu_mixer },
@@ -7384,6 +7611,7 @@ static int patch_alc260(struct hda_codec *codec)
        codec->patch_ops = alc_patch_ops;
        if (board_config == ALC260_AUTO)
                spec->init_hook = alc260_auto_init;
+       spec->shutup = alc_eapd_shutup;
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        if (!spec->loopback.amplist)
                spec->loopback.amplist = alc260_loopbacks;
@@ -8600,6 +8828,8 @@ static void alc885_imac24_setup(struct hda_codec *codec)
        spec->autocfg.hp_pins[0] = 0x14;
        spec->autocfg.speaker_pins[0] = 0x18;
        spec->autocfg.speaker_pins[1] = 0x1a;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 #define alc885_mb5_setup       alc885_imac24_setup
@@ -8612,6 +8842,8 @@ static void alc885_mba21_setup(struct hda_codec *codec)
 
        spec->autocfg.hp_pins[0] = 0x14;
        spec->autocfg.speaker_pins[0] = 0x18;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 
@@ -8622,6 +8854,8 @@ static void alc885_mbp3_setup(struct hda_codec *codec)
 
        spec->autocfg.hp_pins[0] = 0x15;
        spec->autocfg.speaker_pins[0] = 0x14;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 static void alc885_imac91_setup(struct hda_codec *codec)
@@ -8631,6 +8865,8 @@ static void alc885_imac91_setup(struct hda_codec *codec)
        spec->autocfg.hp_pins[0] = 0x14;
        spec->autocfg.speaker_pins[0] = 0x18;
        spec->autocfg.speaker_pins[1] = 0x1a;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 static struct hda_verb alc882_targa_verbs[] = {
@@ -8652,7 +8888,7 @@ static struct hda_verb alc882_targa_verbs[] = {
 static void alc882_targa_automute(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       alc_automute_amp(codec);
+       alc_hp_automute(codec);
        snd_hda_codec_write_cache(codec, 1, 0, AC_VERB_SET_GPIO_DATA,
                                  spec->jack_present ? 1 : 3);
 }
@@ -8663,6 +8899,8 @@ static void alc882_targa_setup(struct hda_codec *codec)
 
        spec->autocfg.hp_pins[0] = 0x14;
        spec->autocfg.speaker_pins[0] = 0x1b;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 static void alc882_targa_unsol_event(struct hda_codec *codec, unsigned int res)
@@ -8750,7 +8988,7 @@ static void alc885_macpro_init_hook(struct hda_codec *codec)
 static void alc885_imac24_init_hook(struct hda_codec *codec)
 {
        alc885_macpro_init_hook(codec);
-       alc_automute_amp(codec);
+       alc_hp_automute(codec);
 }
 
 /*
@@ -9108,6 +9346,8 @@ static void alc883_medion_wim2160_setup(struct hda_codec *codec)
 
        spec->autocfg.hp_pins[0] = 0x1a;
        spec->autocfg.speaker_pins[0] = 0x15;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 static struct snd_kcontrol_new alc883_acer_aspire_mixer[] = {
@@ -9260,6 +9500,8 @@ static void alc883_mitac_setup(struct hda_codec *codec)
        spec->autocfg.hp_pins[0] = 0x15;
        spec->autocfg.speaker_pins[0] = 0x14;
        spec->autocfg.speaker_pins[1] = 0x17;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 static struct hda_verb alc883_mitac_verbs[] = {
@@ -9424,6 +9666,8 @@ static void alc888_3st_hp_setup(struct hda_codec *codec)
        spec->autocfg.speaker_pins[0] = 0x14;
        spec->autocfg.speaker_pins[1] = 0x16;
        spec->autocfg.speaker_pins[2] = 0x18;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 static struct hda_verb alc888_3st_hp_verbs[] = {
@@ -9476,33 +9720,15 @@ static struct hda_channel_mode alc888_3st_hp_modes[3] = {
        { 6, alc888_3st_hp_6ch_init },
 };
 
-/* toggle front-jack and RCA according to the hp-jack state */
-static void alc888_lenovo_ms7195_front_automute(struct hda_codec *codec)
-{
-       unsigned int present = snd_hda_jack_detect(codec, 0x1b);
-
-       snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-       snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-}
-
-/* toggle RCA according to the front-jack state */
-static void alc888_lenovo_ms7195_rca_automute(struct hda_codec *codec)
+static void alc888_lenovo_ms7195_setup(struct hda_codec *codec)
 {
-       unsigned int present = snd_hda_jack_detect(codec, 0x14);
-
-       snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-}
+       struct alc_spec *spec = codec->spec;
 
-static void alc883_lenovo_ms7195_unsol_event(struct hda_codec *codec,
-                                            unsigned int res)
-{
-       if ((res >> 26) == ALC880_HP_EVENT)
-               alc888_lenovo_ms7195_front_automute(codec);
-       if ((res >> 26) == ALC880_FRONT_EVENT)
-               alc888_lenovo_ms7195_rca_automute(codec);
+       spec->autocfg.hp_pins[0] = 0x1b;
+       spec->autocfg.line_out_pins[0] = 0x14;
+       spec->autocfg.speaker_pins[0] = 0x15;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 /* toggle speaker-output according to the hp-jack state */
@@ -9512,6 +9738,8 @@ static void alc883_lenovo_nb0763_setup(struct hda_codec *codec)
 
        spec->autocfg.hp_pins[0] = 0x14;
        spec->autocfg.speaker_pins[0] = 0x15;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 /* toggle speaker-output according to the hp-jack state */
@@ -9524,11 +9752,13 @@ static void alc883_clevo_m720_setup(struct hda_codec *codec)
 
        spec->autocfg.hp_pins[0] = 0x15;
        spec->autocfg.speaker_pins[0] = 0x14;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 static void alc883_clevo_m720_init_hook(struct hda_codec *codec)
 {
-       alc_automute_amp(codec);
+       alc_hp_automute(codec);
        alc88x_simple_mic_automute(codec);
 }
 
@@ -9540,7 +9770,7 @@ static void alc883_clevo_m720_unsol_event(struct hda_codec *codec,
                alc88x_simple_mic_automute(codec);
                break;
        default:
-               alc_automute_amp_unsol_event(codec, res);
+               alc_sku_unsol_event(codec, res);
                break;
        }
 }
@@ -9552,6 +9782,8 @@ static void alc883_2ch_fujitsu_pi2515_setup(struct hda_codec *codec)
 
        spec->autocfg.hp_pins[0] = 0x14;
        spec->autocfg.speaker_pins[0] = 0x15;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 static void alc883_haier_w66_setup(struct hda_codec *codec)
@@ -9560,43 +9792,33 @@ static void alc883_haier_w66_setup(struct hda_codec *codec)
 
        spec->autocfg.hp_pins[0] = 0x1b;
        spec->autocfg.speaker_pins[0] = 0x14;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
-static void alc883_lenovo_101e_ispeaker_automute(struct hda_codec *codec)
+static void alc883_lenovo_101e_setup(struct hda_codec *codec)
 {
-       int bits = snd_hda_jack_detect(codec, 0x14) ? HDA_AMP_MUTE : 0;
+       struct alc_spec *spec = codec->spec;
 
-       snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, bits);
+       spec->autocfg.hp_pins[0] = 0x1b;
+       spec->autocfg.line_out_pins[0] = 0x14;
+       spec->autocfg.speaker_pins[0] = 0x15;
+       spec->automute = 1;
+       spec->detect_line = 1;
+       spec->automute_lines = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
-static void alc883_lenovo_101e_all_automute(struct hda_codec *codec)
+/* toggle speaker-output according to the hp-jack state */
+static void alc883_acer_aspire_setup(struct hda_codec *codec)
 {
-       int bits = snd_hda_jack_detect(codec, 0x1b) ? HDA_AMP_MUTE : 0;
-
-       snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, bits);
-       snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, bits);
-}
-
-static void alc883_lenovo_101e_unsol_event(struct hda_codec *codec,
-                                          unsigned int res)
-{
-       if ((res >> 26) == ALC880_HP_EVENT)
-               alc883_lenovo_101e_all_automute(codec);
-       if ((res >> 26) == ALC880_FRONT_EVENT)
-               alc883_lenovo_101e_ispeaker_automute(codec);
-}
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc883_acer_aspire_setup(struct hda_codec *codec)
-{
-       struct alc_spec *spec = codec->spec;
+       struct alc_spec *spec = codec->spec;
 
        spec->autocfg.hp_pins[0] = 0x14;
        spec->autocfg.speaker_pins[0] = 0x15;
        spec->autocfg.speaker_pins[1] = 0x16;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 static struct hda_verb alc883_acer_eapd_verbs[] = {
@@ -9626,6 +9848,8 @@ static void alc888_6st_dell_setup(struct hda_codec *codec)
        spec->autocfg.speaker_pins[1] = 0x15;
        spec->autocfg.speaker_pins[2] = 0x16;
        spec->autocfg.speaker_pins[3] = 0x17;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 static void alc888_lenovo_sky_setup(struct hda_codec *codec)
@@ -9638,6 +9862,8 @@ static void alc888_lenovo_sky_setup(struct hda_codec *codec)
        spec->autocfg.speaker_pins[2] = 0x16;
        spec->autocfg.speaker_pins[3] = 0x17;
        spec->autocfg.speaker_pins[4] = 0x1a;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 static void alc883_vaiott_setup(struct hda_codec *codec)
@@ -9647,6 +9873,8 @@ static void alc883_vaiott_setup(struct hda_codec *codec)
        spec->autocfg.hp_pins[0] = 0x15;
        spec->autocfg.speaker_pins[0] = 0x14;
        spec->autocfg.speaker_pins[1] = 0x17;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 static struct hda_verb alc888_asus_m90v_verbs[] = {
@@ -9672,6 +9900,8 @@ static void alc883_mode2_setup(struct hda_codec *codec)
        spec->ext_mic.mux_idx = 0;
        spec->int_mic.mux_idx = 1;
        spec->auto_mic = 1;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 static struct hda_verb alc888_asus_eee1601_verbs[] = {
@@ -9693,7 +9923,7 @@ static void alc883_eee1601_inithook(struct hda_codec *codec)
 
        spec->autocfg.hp_pins[0] = 0x14;
        spec->autocfg.speaker_pins[0] = 0x1b;
-       alc_automute_pin(codec);
+       alc_hp_automute(codec);
 }
 
 static struct hda_verb alc889A_mb31_verbs[] = {
@@ -10015,9 +10245,9 @@ static struct alc_config_preset alc882_presets[] = {
                        .channel_mode = alc885_mba21_ch_modes,
                        .num_channel_mode = ARRAY_SIZE(alc885_mba21_ch_modes),
                        .input_mux = &alc882_capture_source,
-                       .unsol_event = alc_automute_amp_unsol_event,
+                       .unsol_event = alc_sku_unsol_event,
                        .setup = alc885_mba21_setup,
-                       .init_hook = alc_automute_amp,
+                       .init_hook = alc_hp_automute,
        },
        [ALC885_MBP3] = {
                .mixers = { alc885_mbp3_mixer, alc882_chmode_mixer },
@@ -10031,9 +10261,9 @@ static struct alc_config_preset alc882_presets[] = {
                .input_mux = &alc882_capture_source,
                .dig_out_nid = ALC882_DIGOUT_NID,
                .dig_in_nid = ALC882_DIGIN_NID,
-               .unsol_event = alc_automute_amp_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc885_mbp3_setup,
-               .init_hook = alc_automute_amp,
+               .init_hook = alc_hp_automute,
        },
        [ALC885_MB5] = {
                .mixers = { alc885_mb5_mixer, alc882_chmode_mixer },
@@ -10046,9 +10276,9 @@ static struct alc_config_preset alc882_presets[] = {
                .input_mux = &mb5_capture_source,
                .dig_out_nid = ALC882_DIGOUT_NID,
                .dig_in_nid = ALC882_DIGIN_NID,
-               .unsol_event = alc_automute_amp_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc885_mb5_setup,
-               .init_hook = alc_automute_amp,
+               .init_hook = alc_hp_automute,
        },
        [ALC885_MACMINI3] = {
                .mixers = { alc885_macmini3_mixer, alc882_chmode_mixer },
@@ -10061,9 +10291,9 @@ static struct alc_config_preset alc882_presets[] = {
                .input_mux = &macmini3_capture_source,
                .dig_out_nid = ALC882_DIGOUT_NID,
                .dig_in_nid = ALC882_DIGIN_NID,
-               .unsol_event = alc_automute_amp_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc885_macmini3_setup,
-               .init_hook = alc_automute_amp,
+               .init_hook = alc_hp_automute,
        },
        [ALC885_MACPRO] = {
                .mixers = { alc882_macpro_mixer },
@@ -10087,7 +10317,7 @@ static struct alc_config_preset alc882_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc882_ch_modes),
                .channel_mode = alc882_ch_modes,
                .input_mux = &alc882_capture_source,
-               .unsol_event = alc_automute_amp_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc885_imac24_setup,
                .init_hook = alc885_imac24_init_hook,
        },
@@ -10102,9 +10332,9 @@ static struct alc_config_preset alc882_presets[] = {
                .input_mux = &alc889A_imac91_capture_source,
                .dig_out_nid = ALC882_DIGOUT_NID,
                .dig_in_nid = ALC882_DIGIN_NID,
-               .unsol_event = alc_automute_amp_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc885_imac91_setup,
-               .init_hook = alc_automute_amp,
+               .init_hook = alc_hp_automute,
        },
        [ALC882_TARGA] = {
                .mixers = { alc882_targa_mixer, alc882_chmode_mixer },
@@ -10120,7 +10350,7 @@ static struct alc_config_preset alc882_presets[] = {
                .channel_mode = alc882_3ST_6ch_modes,
                .need_dac_fix = 1,
                .input_mux = &alc882_capture_source,
-               .unsol_event = alc882_targa_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc882_targa_setup,
                .init_hook = alc882_targa_automute,
        },
@@ -10214,8 +10444,8 @@ static struct alc_config_preset alc882_presets[] = {
                .capsrc_nids = alc889_capsrc_nids,
                .input_mux = &alc889_capture_source,
                .setup = alc889_automute_setup,
-               .init_hook = alc_automute_amp,
-               .unsol_event = alc_automute_amp_unsol_event,
+               .init_hook = alc_hp_automute,
+               .unsol_event = alc_sku_unsol_event,
                .need_dac_fix = 1,
        },
        [ALC889_INTEL] = {
@@ -10235,7 +10465,7 @@ static struct alc_config_preset alc882_presets[] = {
                .input_mux = &alc889_capture_source,
                .setup = alc889_automute_setup,
                .init_hook = alc889_intel_init_hook,
-               .unsol_event = alc_automute_amp_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .need_dac_fix = 1,
        },
        [ALC883_6ST_DIG] = {
@@ -10324,9 +10554,9 @@ static struct alc_config_preset alc882_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
                .channel_mode = alc883_3ST_2ch_modes,
                .input_mux = &alc883_capture_source,
-               .unsol_event = alc_automute_amp_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc883_acer_aspire_setup,
-               .init_hook = alc_automute_amp,
+               .init_hook = alc_hp_automute,
        },
        [ALC888_ACER_ASPIRE_4930G] = {
                .mixers = { alc888_acer_aspire_4930g_mixer,
@@ -10346,9 +10576,9 @@ static struct alc_config_preset alc882_presets[] = {
                .num_mux_defs =
                        ARRAY_SIZE(alc888_2_capture_sources),
                .input_mux = alc888_2_capture_sources,
-               .unsol_event = alc_automute_amp_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc888_acer_aspire_4930g_setup,
-               .init_hook = alc_automute_amp,
+               .init_hook = alc_hp_automute,
        },
        [ALC888_ACER_ASPIRE_6530G] = {
                .mixers = { alc888_acer_aspire_6530_mixer },
@@ -10365,9 +10595,9 @@ static struct alc_config_preset alc882_presets[] = {
                .num_mux_defs =
                        ARRAY_SIZE(alc888_2_capture_sources),
                .input_mux = alc888_acer_aspire_6530_sources,
-               .unsol_event = alc_automute_amp_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc888_acer_aspire_6530g_setup,
-               .init_hook = alc_automute_amp,
+               .init_hook = alc_hp_automute,
        },
        [ALC888_ACER_ASPIRE_8930G] = {
                .mixers = { alc889_acer_aspire_8930g_mixer,
@@ -10388,9 +10618,9 @@ static struct alc_config_preset alc882_presets[] = {
                .num_mux_defs =
                        ARRAY_SIZE(alc889_capture_sources),
                .input_mux = alc889_capture_sources,
-               .unsol_event = alc_automute_amp_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc889_acer_aspire_8930g_setup,
-               .init_hook = alc_automute_amp,
+               .init_hook = alc_hp_automute,
 #ifdef CONFIG_SND_HDA_POWER_SAVE
                .power_hook = alc_power_eapd,
 #endif
@@ -10411,9 +10641,9 @@ static struct alc_config_preset alc882_presets[] = {
                .need_dac_fix = 1,
                .const_channel_count = 6,
                .input_mux = &alc883_capture_source,
-               .unsol_event = alc_automute_amp_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc888_acer_aspire_7730g_setup,
-               .init_hook = alc_automute_amp,
+               .init_hook = alc_hp_automute,
        },
        [ALC883_MEDION] = {
                .mixers = { alc883_fivestack_mixer,
@@ -10440,9 +10670,9 @@ static struct alc_config_preset alc882_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
                .channel_mode = alc883_3ST_2ch_modes,
                .input_mux = &alc883_capture_source,
-               .unsol_event = alc_automute_amp_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc883_medion_wim2160_setup,
-               .init_hook = alc_automute_amp,
+               .init_hook = alc_hp_automute,
        },
        [ALC883_LAPTOP_EAPD] = {
                .mixers = { alc883_base_mixer },
@@ -10492,8 +10722,9 @@ static struct alc_config_preset alc882_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
                .channel_mode = alc883_3ST_2ch_modes,
                .input_mux = &alc883_lenovo_101e_capture_source,
-               .unsol_event = alc883_lenovo_101e_unsol_event,
-               .init_hook = alc883_lenovo_101e_all_automute,
+               .setup = alc883_lenovo_101e_setup,
+               .unsol_event = alc_sku_unsol_event,
+               .init_hook = alc_inithook,
        },
        [ALC883_LENOVO_NB0763] = {
                .mixers = { alc883_lenovo_nb0763_mixer },
@@ -10504,9 +10735,9 @@ static struct alc_config_preset alc882_presets[] = {
                .channel_mode = alc883_3ST_2ch_modes,
                .need_dac_fix = 1,
                .input_mux = &alc883_lenovo_nb0763_capture_source,
-               .unsol_event = alc_automute_amp_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc883_lenovo_nb0763_setup,
-               .init_hook = alc_automute_amp,
+               .init_hook = alc_hp_automute,
        },
        [ALC888_LENOVO_MS7195_DIG] = {
                .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
@@ -10518,8 +10749,9 @@ static struct alc_config_preset alc882_presets[] = {
                .channel_mode = alc883_3ST_6ch_modes,
                .need_dac_fix = 1,
                .input_mux = &alc883_capture_source,
-               .unsol_event = alc883_lenovo_ms7195_unsol_event,
-               .init_hook = alc888_lenovo_ms7195_front_automute,
+               .unsol_event = alc_sku_unsol_event,
+               .setup = alc888_lenovo_ms7195_setup,
+               .init_hook = alc_inithook,
        },
        [ALC883_HAIER_W66] = {
                .mixers = { alc883_targa_2ch_mixer},
@@ -10530,9 +10762,9 @@ static struct alc_config_preset alc882_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
                .channel_mode = alc883_3ST_2ch_modes,
                .input_mux = &alc883_capture_source,
-               .unsol_event = alc_automute_amp_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc883_haier_w66_setup,
-               .init_hook = alc_automute_amp,
+               .init_hook = alc_hp_automute,
        },
        [ALC888_3ST_HP] = {
                .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
@@ -10543,9 +10775,9 @@ static struct alc_config_preset alc882_presets[] = {
                .channel_mode = alc888_3st_hp_modes,
                .need_dac_fix = 1,
                .input_mux = &alc883_capture_source,
-               .unsol_event = alc_automute_amp_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc888_3st_hp_setup,
-               .init_hook = alc_automute_amp,
+               .init_hook = alc_hp_automute,
        },
        [ALC888_6ST_DELL] = {
                .mixers = { alc883_base_mixer, alc883_chmode_mixer },
@@ -10557,9 +10789,9 @@ static struct alc_config_preset alc882_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
                .channel_mode = alc883_sixstack_modes,
                .input_mux = &alc883_capture_source,
-               .unsol_event = alc_automute_amp_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc888_6st_dell_setup,
-               .init_hook = alc_automute_amp,
+               .init_hook = alc_hp_automute,
        },
        [ALC883_MITAC] = {
                .mixers = { alc883_mitac_mixer },
@@ -10569,9 +10801,9 @@ static struct alc_config_preset alc882_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
                .channel_mode = alc883_3ST_2ch_modes,
                .input_mux = &alc883_capture_source,
-               .unsol_event = alc_automute_amp_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc883_mitac_setup,
-               .init_hook = alc_automute_amp,
+               .init_hook = alc_hp_automute,
        },
        [ALC883_FUJITSU_PI2515] = {
                .mixers = { alc883_2ch_fujitsu_pi2515_mixer },
@@ -10583,9 +10815,9 @@ static struct alc_config_preset alc882_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
                .channel_mode = alc883_3ST_2ch_modes,
                .input_mux = &alc883_fujitsu_pi2515_capture_source,
-               .unsol_event = alc_automute_amp_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc883_2ch_fujitsu_pi2515_setup,
-               .init_hook = alc_automute_amp,
+               .init_hook = alc_hp_automute,
        },
        [ALC888_FUJITSU_XA3530] = {
                .mixers = { alc888_base_mixer, alc883_chmode_mixer },
@@ -10602,9 +10834,9 @@ static struct alc_config_preset alc882_presets[] = {
                .num_mux_defs =
                        ARRAY_SIZE(alc888_2_capture_sources),
                .input_mux = alc888_2_capture_sources,
-               .unsol_event = alc_automute_amp_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc888_fujitsu_xa3530_setup,
-               .init_hook = alc_automute_amp,
+               .init_hook = alc_hp_automute,
        },
        [ALC888_LENOVO_SKY] = {
                .mixers = { alc888_lenovo_sky_mixer, alc883_chmode_mixer },
@@ -10616,9 +10848,9 @@ static struct alc_config_preset alc882_presets[] = {
                .channel_mode = alc883_sixstack_modes,
                .need_dac_fix = 1,
                .input_mux = &alc883_lenovo_sky_capture_source,
-               .unsol_event = alc_automute_amp_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc888_lenovo_sky_setup,
-               .init_hook = alc_automute_amp,
+               .init_hook = alc_hp_automute,
        },
        [ALC888_ASUS_M90V] = {
                .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
@@ -10686,9 +10918,9 @@ static struct alc_config_preset alc882_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
                .channel_mode = alc883_3ST_2ch_modes,
                .input_mux = &alc883_capture_source,
-               .unsol_event = alc_automute_amp_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc883_vaiott_setup,
-               .init_hook = alc_automute_amp,
+               .init_hook = alc_hp_automute,
        },
 };
 
@@ -10842,6 +11074,11 @@ static void alc882_auto_init_input_src(struct hda_codec *codec)
                const struct hda_input_mux *imux;
                int conns, mute, idx, item;
 
+               /* mute ADC */
+               snd_hda_codec_write(codec, spec->adc_nids[c], 0,
+                                   AC_VERB_SET_AMP_GAIN_MUTE,
+                                   AMP_IN_MUTE(0));
+
                conns = snd_hda_get_connections(codec, nid, conn_list,
                                                ARRAY_SIZE(conn_list));
                if (conns < 0)
@@ -10932,6 +11169,9 @@ static int alc882_parse_auto_config(struct hda_codec *codec)
                return 0; /* can't find valid BIOS pin config */
 
        err = alc880_auto_fill_dac_nids(spec, &spec->autocfg);
+       if (err < 0)
+               return err;
+       err = alc_auto_add_multi_channel_mode(codec);
        if (err < 0)
                return err;
        err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg);
@@ -11163,71 +11403,30 @@ static struct snd_kcontrol_new alc262_base_mixer[] = {
 };
 
 /* update HP, line and mono-out pins according to the master switch */
-static void alc262_hp_master_update(struct hda_codec *codec)
-{
-       struct alc_spec *spec = codec->spec;
-       int val = spec->master_sw;
-
-       /* HP & line-out */
-       snd_hda_codec_write_cache(codec, 0x1b, 0,
-                                 AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                 val ? PIN_HP : 0);
-       snd_hda_codec_write_cache(codec, 0x15, 0,
-                                 AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                 val ? PIN_HP : 0);
-       /* mono (speaker) depending on the HP jack sense */
-       val = val && !spec->jack_present;
-       snd_hda_codec_write_cache(codec, 0x16, 0,
-                                 AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                 val ? PIN_OUT : 0);
-}
+#define alc262_hp_master_update                alc260_hp_master_update
 
-static void alc262_hp_bpc_automute(struct hda_codec *codec)
+static void alc262_hp_bpc_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
-       spec->jack_present = snd_hda_jack_detect(codec, 0x1b);
-       alc262_hp_master_update(codec);
-}
-
-static void alc262_hp_bpc_unsol_event(struct hda_codec *codec, unsigned int res)
-{
-       if ((res >> 26) != ALC880_HP_EVENT)
-               return;
-       alc262_hp_bpc_automute(codec);
+       spec->autocfg.hp_pins[0] = 0x1b;
+       spec->autocfg.speaker_pins[0] = 0x16;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_PIN;
 }
 
-static void alc262_hp_wildwest_automute(struct hda_codec *codec)
+static void alc262_hp_wildwest_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
-       spec->jack_present = snd_hda_jack_detect(codec, 0x15);
-       alc262_hp_master_update(codec);
-}
-
-static void alc262_hp_wildwest_unsol_event(struct hda_codec *codec,
-                                          unsigned int res)
-{
-       if ((res >> 26) != ALC880_HP_EVENT)
-               return;
-       alc262_hp_wildwest_automute(codec);
+       spec->autocfg.hp_pins[0] = 0x15;
+       spec->autocfg.speaker_pins[0] = 0x16;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_PIN;
 }
 
 #define alc262_hp_master_sw_get                alc260_hp_master_sw_get
-
-static int alc262_hp_master_sw_put(struct snd_kcontrol *kcontrol,
-                                  struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       struct alc_spec *spec = codec->spec;
-       int val = !!*ucontrol->value.integer.value;
-
-       if (val == spec->master_sw)
-               return 0;
-       spec->master_sw = val;
-       alc262_hp_master_update(codec);
-       return 1;
-}
+#define alc262_hp_master_sw_put                alc260_hp_master_sw_put
 
 #define ALC262_HP_MASTER_SWITCH                                        \
        {                                                       \
@@ -11302,6 +11501,8 @@ static void alc262_hp_t5735_setup(struct hda_codec *codec)
 
        spec->autocfg.hp_pins[0] = 0x15;
        spec->autocfg.speaker_pins[0] = 0x14;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_PIN;
 }
 
 static struct snd_kcontrol_new alc262_hp_t5735_mixer[] = {
@@ -11355,44 +11556,9 @@ static struct hda_input_mux alc262_hp_rp5700_capture_source = {
 };
 
 /* bind hp and internal speaker mute (with plug check) as master switch */
-static void alc262_hippo_master_update(struct hda_codec *codec)
-{
-       struct alc_spec *spec = codec->spec;
-       hda_nid_t hp_nid = spec->autocfg.hp_pins[0];
-       hda_nid_t line_nid = spec->autocfg.line_out_pins[0];
-       hda_nid_t speaker_nid = spec->autocfg.speaker_pins[0];
-       unsigned int mute;
-
-       /* HP */
-       mute = spec->master_sw ? 0 : HDA_AMP_MUTE;
-       snd_hda_codec_amp_stereo(codec, hp_nid, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, mute);
-       /* mute internal speaker per jack sense */
-       if (spec->jack_present)
-               mute = HDA_AMP_MUTE;
-       if (line_nid)
-               snd_hda_codec_amp_stereo(codec, line_nid, HDA_OUTPUT, 0,
-                                        HDA_AMP_MUTE, mute);
-       if (speaker_nid && speaker_nid != line_nid)
-               snd_hda_codec_amp_stereo(codec, speaker_nid, HDA_OUTPUT, 0,
-                                        HDA_AMP_MUTE, mute);
-}
-
+#define alc262_hippo_master_update     alc262_hp_master_update
 #define alc262_hippo_master_sw_get     alc262_hp_master_sw_get
-
-static int alc262_hippo_master_sw_put(struct snd_kcontrol *kcontrol,
-                                     struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       struct alc_spec *spec = codec->spec;
-       int val = !!*ucontrol->value.integer.value;
-
-       if (val == spec->master_sw)
-               return 0;
-       spec->master_sw = val;
-       alc262_hippo_master_update(codec);
-       return 1;
-}
+#define alc262_hippo_master_sw_put     alc262_hp_master_sw_put
 
 #define ALC262_HIPPO_MASTER_SWITCH                             \
        {                                                       \
@@ -11443,28 +11609,14 @@ static struct snd_kcontrol_new alc262_hippo1_mixer[] = {
 };
 
 /* mute/unmute internal speaker according to the hp jack and mute state */
-static void alc262_hippo_automute(struct hda_codec *codec)
-{
-       struct alc_spec *spec = codec->spec;
-       hda_nid_t hp_nid = spec->autocfg.hp_pins[0];
-
-       spec->jack_present = snd_hda_jack_detect(codec, hp_nid);
-       alc262_hippo_master_update(codec);
-}
-
-static void alc262_hippo_unsol_event(struct hda_codec *codec, unsigned int res)
-{
-       if ((res >> 26) != ALC880_HP_EVENT)
-               return;
-       alc262_hippo_automute(codec);
-}
-
 static void alc262_hippo_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
        spec->autocfg.hp_pins[0] = 0x15;
        spec->autocfg.speaker_pins[0] = 0x14;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 static void alc262_hippo1_setup(struct hda_codec *codec)
@@ -11473,6 +11625,8 @@ static void alc262_hippo1_setup(struct hda_codec *codec)
 
        spec->autocfg.hp_pins[0] = 0x1b;
        spec->autocfg.speaker_pins[0] = 0x14;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 
@@ -11535,6 +11689,8 @@ static void alc262_tyan_setup(struct hda_codec *codec)
 
        spec->autocfg.hp_pins[0] = 0x1b;
        spec->autocfg.speaker_pins[0] = 0x15;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 
@@ -11678,6 +11834,8 @@ static void alc262_toshiba_s06_setup(struct hda_codec *codec)
        spec->int_mic.pin = 0x12;
        spec->int_mic.mux_idx = 9;
        spec->auto_mic = 1;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_PIN;
 }
 
 /*
@@ -11774,40 +11932,15 @@ static struct hda_input_mux alc262_HP_D7000_capture_source = {
        },
 };
 
-/* mute/unmute internal speaker according to the hp jacks and mute state */
-static void alc262_fujitsu_automute(struct hda_codec *codec, int force)
+static void alc262_fujitsu_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       unsigned int mute;
-
-       if (force || !spec->sense_updated) {
-               spec->jack_present = snd_hda_jack_detect(codec, 0x14) ||
-                                    snd_hda_jack_detect(codec, 0x1b);
-               spec->sense_updated = 1;
-       }
-       /* unmute internal speaker only if both HPs are unplugged and
-        * master switch is on
-        */
-       if (spec->jack_present)
-               mute = HDA_AMP_MUTE;
-       else
-               mute = snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0);
-       snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, mute);
-}
-
-/* unsolicited event for HP jack sensing */
-static void alc262_fujitsu_unsol_event(struct hda_codec *codec,
-                                      unsigned int res)
-{
-       if ((res >> 26) != ALC_HP_EVENT)
-               return;
-       alc262_fujitsu_automute(codec, 1);
-}
 
-static void alc262_fujitsu_init_hook(struct hda_codec *codec)
-{
-       alc262_fujitsu_automute(codec, 1);
+       spec->autocfg.hp_pins[0] = 0x14;
+       spec->autocfg.hp_pins[1] = 0x1b;
+       spec->autocfg.speaker_pins[0] = 0x15;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 /* bind volumes of both NID 0x0c and 0x0d */
@@ -11820,78 +11953,15 @@ static struct hda_bind_ctls alc262_fujitsu_bind_master_vol = {
        },
 };
 
-/* mute/unmute internal speaker according to the hp jack and mute state */
-static void alc262_lenovo_3000_automute(struct hda_codec *codec, int force)
-{
-       struct alc_spec *spec = codec->spec;
-       unsigned int mute;
-
-       if (force || !spec->sense_updated) {
-               spec->jack_present = snd_hda_jack_detect(codec, 0x1b);
-               spec->sense_updated = 1;
-       }
-       if (spec->jack_present) {
-               /* mute internal speaker */
-               snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-                                        HDA_AMP_MUTE, HDA_AMP_MUTE);
-               snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
-                                        HDA_AMP_MUTE, HDA_AMP_MUTE);
-       } else {
-               /* unmute internal speaker if necessary */
-               mute = snd_hda_codec_amp_read(codec, 0x1b, 0, HDA_OUTPUT, 0);
-               snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-                                        HDA_AMP_MUTE, mute);
-               snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
-                                        HDA_AMP_MUTE, mute);
-       }
-}
-
-/* unsolicited event for HP jack sensing */
-static void alc262_lenovo_3000_unsol_event(struct hda_codec *codec,
-                                      unsigned int res)
-{
-       if ((res >> 26) != ALC_HP_EVENT)
-               return;
-       alc262_lenovo_3000_automute(codec, 1);
-}
-
-static int amp_stereo_mute_update(struct hda_codec *codec, hda_nid_t nid,
-                                 int dir, int idx, long *valp)
-{
-       int i, change = 0;
-
-       for (i = 0; i < 2; i++, valp++)
-               change |= snd_hda_codec_amp_update(codec, nid, i, dir, idx,
-                                                  HDA_AMP_MUTE,
-                                                  *valp ? 0 : HDA_AMP_MUTE);
-       return change;
-}
-
-/* bind hp and internal speaker mute (with plug check) */
-static int alc262_fujitsu_master_sw_put(struct snd_kcontrol *kcontrol,
-                                        struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       long *valp = ucontrol->value.integer.value;
-       int change;
-
-       change = amp_stereo_mute_update(codec, 0x14, HDA_OUTPUT, 0, valp);
-       change |= amp_stereo_mute_update(codec, 0x1b, HDA_OUTPUT, 0, valp);
-       if (change)
-               alc262_fujitsu_automute(codec, 0);
-       return change;
-}
-
 static struct snd_kcontrol_new alc262_fujitsu_mixer[] = {
        HDA_BIND_VOL("Master Playback Volume", &alc262_fujitsu_bind_master_vol),
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Master Playback Switch",
-               .subdevice = HDA_SUBDEV_AMP_FLAG,
-               .info = snd_hda_mixer_amp_switch_info,
-               .get = snd_hda_mixer_amp_switch_get,
-               .put = alc262_fujitsu_master_sw_put,
-               .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
+               .subdevice = HDA_SUBDEV_NID_FLAG | 0x14,
+               .info = snd_ctl_boolean_mono_info,
+               .get = alc262_hp_master_sw_get,
+               .put = alc262_hp_master_sw_put,
        },
        {
                .iface = NID_MAPPING,
@@ -11909,18 +11979,15 @@ static struct snd_kcontrol_new alc262_fujitsu_mixer[] = {
        { } /* end */
 };
 
-/* bind hp and internal speaker mute (with plug check) */
-static int alc262_lenovo_3000_master_sw_put(struct snd_kcontrol *kcontrol,
-                                        struct snd_ctl_elem_value *ucontrol)
+static void alc262_lenovo_3000_setup(struct hda_codec *codec)
 {
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       long *valp = ucontrol->value.integer.value;
-       int change;
+       struct alc_spec *spec = codec->spec;
 
-       change = amp_stereo_mute_update(codec, 0x1b, HDA_OUTPUT, 0, valp);
-       if (change)
-               alc262_lenovo_3000_automute(codec, 0);
-       return change;
+       spec->autocfg.hp_pins[0] = 0x1b;
+       spec->autocfg.speaker_pins[0] = 0x14;
+       spec->autocfg.speaker_pins[1] = 0x16;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 static struct snd_kcontrol_new alc262_lenovo_3000_mixer[] = {
@@ -11928,11 +11995,10 @@ static struct snd_kcontrol_new alc262_lenovo_3000_mixer[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Master Playback Switch",
-               .subdevice = HDA_SUBDEV_AMP_FLAG,
-               .info = snd_hda_mixer_amp_switch_info,
-               .get = snd_hda_mixer_amp_switch_get,
-               .put = alc262_lenovo_3000_master_sw_put,
-               .private_value = HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
+               .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
+               .info = snd_ctl_boolean_mono_info,
+               .get = alc262_hp_master_sw_get,
+               .put = alc262_hp_master_sw_put,
        },
        HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
        HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
@@ -12150,7 +12216,7 @@ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec,
        spec->multiout.dac_nids = spec->private_dac_nids;
        spec->multiout.dac_nids[0] = 2;
 
-       pfx = alc_get_line_out_pfx(cfg, true);
+       pfx = alc_get_line_out_pfx(spec, true);
        if (!pfx)
                pfx = "Front";
        for (i = 0; i < 2; i++) {
@@ -12682,9 +12748,9 @@ static struct alc_config_preset alc262_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc262_modes),
                .channel_mode = alc262_modes,
                .input_mux = &alc262_capture_source,
-               .unsol_event = alc262_hippo_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc262_hippo_setup,
-               .init_hook = alc262_hippo_automute,
+               .init_hook = alc_inithook,
        },
        [ALC262_HIPPO_1] = {
                .mixers = { alc262_hippo1_mixer },
@@ -12696,9 +12762,9 @@ static struct alc_config_preset alc262_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc262_modes),
                .channel_mode = alc262_modes,
                .input_mux = &alc262_capture_source,
-               .unsol_event = alc262_hippo_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc262_hippo1_setup,
-               .init_hook = alc262_hippo_automute,
+               .init_hook = alc_inithook,
        },
        [ALC262_FUJITSU] = {
                .mixers = { alc262_fujitsu_mixer },
@@ -12711,8 +12777,9 @@ static struct alc_config_preset alc262_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc262_modes),
                .channel_mode = alc262_modes,
                .input_mux = &alc262_fujitsu_capture_source,
-               .unsol_event = alc262_fujitsu_unsol_event,
-               .init_hook = alc262_fujitsu_init_hook,
+               .unsol_event = alc_sku_unsol_event,
+               .setup = alc262_fujitsu_setup,
+               .init_hook = alc_inithook,
        },
        [ALC262_HP_BPC] = {
                .mixers = { alc262_HP_BPC_mixer },
@@ -12723,8 +12790,9 @@ static struct alc_config_preset alc262_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc262_modes),
                .channel_mode = alc262_modes,
                .input_mux = &alc262_HP_capture_source,
-               .unsol_event = alc262_hp_bpc_unsol_event,
-               .init_hook = alc262_hp_bpc_automute,
+               .unsol_event = alc_sku_unsol_event,
+               .setup = alc262_hp_bpc_setup,
+               .init_hook = alc_inithook,
        },
        [ALC262_HP_BPC_D7000_WF] = {
                .mixers = { alc262_HP_BPC_WildWest_mixer },
@@ -12735,8 +12803,9 @@ static struct alc_config_preset alc262_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc262_modes),
                .channel_mode = alc262_modes,
                .input_mux = &alc262_HP_D7000_capture_source,
-               .unsol_event = alc262_hp_wildwest_unsol_event,
-               .init_hook = alc262_hp_wildwest_automute,
+               .unsol_event = alc_sku_unsol_event,
+               .setup = alc262_hp_wildwest_setup,
+               .init_hook = alc_inithook,
        },
        [ALC262_HP_BPC_D7000_WL] = {
                .mixers = { alc262_HP_BPC_WildWest_mixer,
@@ -12748,8 +12817,9 @@ static struct alc_config_preset alc262_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc262_modes),
                .channel_mode = alc262_modes,
                .input_mux = &alc262_HP_D7000_capture_source,
-               .unsol_event = alc262_hp_wildwest_unsol_event,
-               .init_hook = alc262_hp_wildwest_automute,
+               .unsol_event = alc_sku_unsol_event,
+               .setup = alc262_hp_wildwest_setup,
+               .init_hook = alc_inithook,
        },
        [ALC262_HP_TC_T5735] = {
                .mixers = { alc262_hp_t5735_mixer },
@@ -12792,9 +12862,9 @@ static struct alc_config_preset alc262_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc262_modes),
                .channel_mode = alc262_modes,
                .input_mux = &alc262_capture_source,
-               .unsol_event = alc262_hippo_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc262_hippo_setup,
-               .init_hook = alc262_hippo_automute,
+               .init_hook = alc_inithook,
        },
        [ALC262_BENQ_T31] = {
                .mixers = { alc262_benq_t31_mixer },
@@ -12806,9 +12876,9 @@ static struct alc_config_preset alc262_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc262_modes),
                .channel_mode = alc262_modes,
                .input_mux = &alc262_capture_source,
-               .unsol_event = alc262_hippo_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc262_hippo_setup,
-               .init_hook = alc262_hippo_automute,
+               .init_hook = alc_inithook,
        },
        [ALC262_ULTRA] = {
                .mixers = { alc262_ultra_mixer },
@@ -12837,7 +12907,9 @@ static struct alc_config_preset alc262_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc262_modes),
                .channel_mode = alc262_modes,
                .input_mux = &alc262_fujitsu_capture_source,
-               .unsol_event = alc262_lenovo_3000_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
+               .setup = alc262_lenovo_3000_setup,
+               .init_hook = alc_inithook,
        },
        [ALC262_NEC] = {
                .mixers = { alc262_nec_mixer },
@@ -12874,9 +12946,9 @@ static struct alc_config_preset alc262_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc262_modes),
                .channel_mode = alc262_modes,
                .input_mux = &alc262_capture_source,
-               .unsol_event = alc262_hippo_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc262_hippo_setup,
-               .init_hook = alc262_hippo_automute,
+               .init_hook = alc_inithook,
        },
        [ALC262_TYAN] = {
                .mixers = { alc262_tyan_mixer },
@@ -12888,9 +12960,9 @@ static struct alc_config_preset alc262_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc262_modes),
                .channel_mode = alc262_modes,
                .input_mux = &alc262_capture_source,
-               .unsol_event = alc_automute_amp_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc262_tyan_setup,
-               .init_hook = alc_automute_amp,
+               .init_hook = alc_hp_automute,
        },
 };
 
@@ -13011,6 +13083,7 @@ static int patch_alc262(struct hda_codec *codec)
        codec->patch_ops = alc_patch_ops;
        if (board_config == ALC262_AUTO)
                spec->init_hook = alc262_auto_init;
+       spec->shutup = alc_eapd_shutup;
 
        alc_init_jacks(codec);
 #ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -13106,38 +13179,18 @@ static struct hda_bind_ctls alc268_acer_bind_master_vol = {
        },
 };
 
-/* mute/unmute internal speaker according to the hp jack and mute state */
-static void alc268_acer_automute(struct hda_codec *codec, int force)
+static void alc268_acer_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       unsigned int mute;
 
-       if (force || !spec->sense_updated) {
-               spec->jack_present = snd_hda_jack_detect(codec, 0x14);
-               spec->sense_updated = 1;
-       }
-       if (spec->jack_present)
-               mute = HDA_AMP_MUTE; /* mute internal speaker */
-       else /* unmute internal speaker if necessary */
-               mute = snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0);
-       snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, mute);
+       spec->autocfg.hp_pins[0] = 0x14;
+       spec->autocfg.speaker_pins[0] = 0x15;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
-
-/* bind hp and internal speaker mute (with plug check) */
-static int alc268_acer_master_sw_put(struct snd_kcontrol *kcontrol,
-                                    struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       long *valp = ucontrol->value.integer.value;
-       int change;
-
-       change = amp_stereo_mute_update(codec, 0x14, HDA_OUTPUT, 0, valp);
-       if (change)
-               alc268_acer_automute(codec, 0);
-       return change;
-}
+#define alc268_acer_master_sw_get      alc262_hp_master_sw_get
+#define alc268_acer_master_sw_put      alc262_hp_master_sw_put
 
 static struct snd_kcontrol_new alc268_acer_aspire_one_mixer[] = {
        /* output mixer control */
@@ -13145,11 +13198,10 @@ static struct snd_kcontrol_new alc268_acer_aspire_one_mixer[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Master Playback Switch",
-               .subdevice = HDA_SUBDEV_AMP_FLAG,
-               .info = snd_hda_mixer_amp_switch_info,
-               .get = snd_hda_mixer_amp_switch_get,
+               .subdevice = HDA_SUBDEV_NID_FLAG | 0x15,
+               .info = snd_ctl_boolean_mono_info,
+               .get = alc268_acer_master_sw_get,
                .put = alc268_acer_master_sw_put,
-               .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
        },
        HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x18, 0, HDA_INPUT),
        { }
@@ -13161,11 +13213,10 @@ static struct snd_kcontrol_new alc268_acer_mixer[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Master Playback Switch",
-               .subdevice = HDA_SUBDEV_AMP_FLAG,
-               .info = snd_hda_mixer_amp_switch_info,
-               .get = snd_hda_mixer_amp_switch_get,
+               .subdevice = HDA_SUBDEV_NID_FLAG | 0x14,
+               .info = snd_ctl_boolean_mono_info,
+               .get = alc268_acer_master_sw_get,
                .put = alc268_acer_master_sw_put,
-               .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
        },
        HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
        HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x19, 0, HDA_INPUT),
@@ -13179,11 +13230,10 @@ static struct snd_kcontrol_new alc268_acer_dmic_mixer[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Master Playback Switch",
-               .subdevice = HDA_SUBDEV_AMP_FLAG,
-               .info = snd_hda_mixer_amp_switch_info,
-               .get = snd_hda_mixer_amp_switch_get,
+               .subdevice = HDA_SUBDEV_NID_FLAG | 0x14,
+               .info = snd_ctl_boolean_mono_info,
+               .get = alc268_acer_master_sw_get,
                .put = alc268_acer_master_sw_put,
-               .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
        },
        HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0, HDA_INPUT),
        HDA_CODEC_VOLUME("Line In Boost Volume", 0x1a, 0, HDA_INPUT),
@@ -13212,53 +13262,16 @@ static struct hda_verb alc268_acer_verbs[] = {
 };
 
 /* unsolicited event for HP jack sensing */
-#define alc268_toshiba_unsol_event     alc262_hippo_unsol_event
 #define alc268_toshiba_setup           alc262_hippo_setup
-#define alc268_toshiba_automute                alc262_hippo_automute
-
-static void alc268_acer_unsol_event(struct hda_codec *codec,
-                                      unsigned int res)
-{
-       if ((res >> 26) != ALC880_HP_EVENT)
-               return;
-       alc268_acer_automute(codec, 1);
-}
-
-static void alc268_acer_init_hook(struct hda_codec *codec)
-{
-       alc268_acer_automute(codec, 1);
-}
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc268_aspire_one_speaker_automute(struct hda_codec *codec)
-{
-       unsigned int present;
-       unsigned char bits;
-
-       present = snd_hda_jack_detect(codec, 0x15);
-       bits = present ? HDA_AMP_MUTE : 0;
-       snd_hda_codec_amp_stereo(codec, 0x0f, HDA_INPUT, 0,
-                                HDA_AMP_MUTE, bits);
-       snd_hda_codec_amp_stereo(codec, 0x0f, HDA_INPUT, 1,
-                                HDA_AMP_MUTE, bits);
-}
-
-static void alc268_acer_lc_unsol_event(struct hda_codec *codec,
-                                   unsigned int res)
-{
-       switch (res >> 26) {
-       case ALC880_HP_EVENT:
-               alc268_aspire_one_speaker_automute(codec);
-               break;
-       case ALC880_MIC_EVENT:
-               alc_mic_automute(codec);
-               break;
-       }
-}
 
 static void alc268_acer_lc_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
+       spec->autocfg.hp_pins[0] = 0x15;
+       spec->autocfg.speaker_pins[0] = 0x14;
+       spec->automute_mixer_nid[0] = 0x0f;
+       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;
@@ -13266,12 +13279,6 @@ static void alc268_acer_lc_setup(struct hda_codec *codec)
        spec->auto_mic = 1;
 }
 
-static void alc268_acer_lc_init_hook(struct hda_codec *codec)
-{
-       alc268_aspire_one_speaker_automute(codec);
-       alc_mic_automute(codec);
-}
-
 static struct snd_kcontrol_new alc268_dell_mixer[] = {
        /* output mixer control */
        HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
@@ -13303,6 +13310,8 @@ static void alc268_dell_setup(struct hda_codec *codec)
        spec->int_mic.pin = 0x19;
        spec->int_mic.mux_idx = 1;
        spec->auto_mic = 1;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_PIN;
 }
 
 static struct snd_kcontrol_new alc267_quanta_il1_mixer[] = {
@@ -13333,6 +13342,8 @@ static void alc267_quanta_il1_setup(struct hda_codec *codec)
        spec->int_mic.pin = 0x19;
        spec->int_mic.mux_idx = 1;
        spec->auto_mic = 1;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_PIN;
 }
 
 /*
@@ -13874,9 +13885,9 @@ static struct alc_config_preset alc268_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc268_modes),
                .channel_mode = alc268_modes,
                .input_mux = &alc268_capture_source,
-               .unsol_event = alc268_toshiba_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc268_toshiba_setup,
-               .init_hook = alc268_toshiba_automute,
+               .init_hook = alc_inithook,
        },
        [ALC268_ACER] = {
                .mixers = { alc268_acer_mixer, alc268_capture_alt_mixer,
@@ -13892,8 +13903,9 @@ static struct alc_config_preset alc268_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc268_modes),
                .channel_mode = alc268_modes,
                .input_mux = &alc268_acer_capture_source,
-               .unsol_event = alc268_acer_unsol_event,
-               .init_hook = alc268_acer_init_hook,
+               .unsol_event = alc_sku_unsol_event,
+               .setup = alc268_acer_setup,
+               .init_hook = alc_inithook,
        },
        [ALC268_ACER_DMIC] = {
                .mixers = { alc268_acer_dmic_mixer, alc268_capture_alt_mixer,
@@ -13909,8 +13921,9 @@ static struct alc_config_preset alc268_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc268_modes),
                .channel_mode = alc268_modes,
                .input_mux = &alc268_acer_dmic_capture_source,
-               .unsol_event = alc268_acer_unsol_event,
-               .init_hook = alc268_acer_init_hook,
+               .unsol_event = alc_sku_unsol_event,
+               .setup = alc268_acer_setup,
+               .init_hook = alc_inithook,
        },
        [ALC268_ACER_ASPIRE_ONE] = {
                .mixers = { alc268_acer_aspire_one_mixer,
@@ -13926,9 +13939,9 @@ static struct alc_config_preset alc268_presets[] = {
                .hp_nid = 0x03,
                .num_channel_mode = ARRAY_SIZE(alc268_modes),
                .channel_mode = alc268_modes,
-               .unsol_event = alc268_acer_lc_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc268_acer_lc_setup,
-               .init_hook = alc268_acer_lc_init_hook,
+               .init_hook = alc_inithook,
        },
        [ALC268_DELL] = {
                .mixers = { alc268_dell_mixer, alc268_beep_mixer,
@@ -13962,8 +13975,9 @@ static struct alc_config_preset alc268_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc268_modes),
                .channel_mode = alc268_modes,
                .input_mux = &alc268_capture_source,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc268_toshiba_setup,
-               .init_hook = alc268_toshiba_automute,
+               .init_hook = alc_inithook,
        },
 #ifdef CONFIG_SND_DEBUG
        [ALC268_TEST] = {
@@ -14085,6 +14099,7 @@ static int patch_alc268(struct hda_codec *codec)
        codec->patch_ops = alc_patch_ops;
        if (board_config == ALC268_AUTO)
                spec->init_hook = alc268_auto_init;
+       spec->shutup = alc_eapd_shutup;
 
        alc_init_jacks(codec);
 
@@ -14267,15 +14282,7 @@ static struct hda_verb alc269_lifebook_verbs[] = {
 /* toggle speaker-output according to the hp-jack state */
 static void alc269_quanta_fl1_speaker_automute(struct hda_codec *codec)
 {
-       unsigned int present;
-       unsigned char bits;
-
-       present = snd_hda_jack_detect(codec, 0x15);
-       bits = present ? HDA_AMP_MUTE : 0;
-       snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
-                                HDA_AMP_MUTE, bits);
-       snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
-                                HDA_AMP_MUTE, bits);
+       alc_hp_automute(codec);
 
        snd_hda_codec_write(codec, 0x20, 0,
                        AC_VERB_SET_COEF_INDEX, 0x0c);
@@ -14288,34 +14295,8 @@ static void alc269_quanta_fl1_speaker_automute(struct hda_codec *codec)
                        AC_VERB_SET_PROC_COEF, 0x480);
 }
 
-/* toggle speaker-output according to the hp-jacks state */
-static void alc269_lifebook_speaker_automute(struct hda_codec *codec)
-{
-       unsigned int present;
-       unsigned char bits;
-
-       /* Check laptop headphone socket */
-       present = snd_hda_jack_detect(codec, 0x15);
-
-       /* Check port replicator headphone socket */
-       present |= snd_hda_jack_detect(codec, 0x1a);
-
-       bits = present ? HDA_AMP_MUTE : 0;
-       snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
-                                HDA_AMP_MUTE, bits);
-       snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
-                                HDA_AMP_MUTE, bits);
-
-       snd_hda_codec_write(codec, 0x20, 0,
-                       AC_VERB_SET_COEF_INDEX, 0x0c);
-       snd_hda_codec_write(codec, 0x20, 0,
-                       AC_VERB_SET_PROC_COEF, 0x680);
-
-       snd_hda_codec_write(codec, 0x20, 0,
-                       AC_VERB_SET_COEF_INDEX, 0x0c);
-       snd_hda_codec_write(codec, 0x20, 0,
-                       AC_VERB_SET_PROC_COEF, 0x480);
-}
+#define alc269_lifebook_speaker_automute \
+       alc269_quanta_fl1_speaker_automute
 
 static void alc269_lifebook_mic_autoswitch(struct hda_codec *codec)
 {
@@ -14364,6 +14345,9 @@ static void alc269_quanta_fl1_setup(struct hda_codec *codec)
        struct alc_spec *spec = codec->spec;
        spec->autocfg.hp_pins[0] = 0x15;
        spec->autocfg.speaker_pins[0] = 0x14;
+       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;
@@ -14377,6 +14361,17 @@ static void alc269_quanta_fl1_init_hook(struct hda_codec *codec)
        alc_mic_automute(codec);
 }
 
+static void alc269_lifebook_setup(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       spec->autocfg.hp_pins[0] = 0x15;
+       spec->autocfg.hp_pins[1] = 0x1a;
+       spec->autocfg.speaker_pins[0] = 0x14;
+       spec->automute_mixer_nid[0] = 0x0c;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_MIXER;
+}
+
 static void alc269_lifebook_init_hook(struct hda_codec *codec)
 {
        alc269_lifebook_speaker_automute(codec);
@@ -14440,42 +14435,14 @@ static struct hda_verb alc271_acer_dmic_verbs[] = {
        { }
 };
 
-/* toggle speaker-output according to the hp-jack state */
-static void alc269_speaker_automute(struct hda_codec *codec)
-{
-       struct alc_spec *spec = codec->spec;
-       unsigned int nid = spec->autocfg.hp_pins[0];
-       unsigned int present;
-       unsigned char bits;
-
-       present = snd_hda_jack_detect(codec, nid);
-       bits = present ? HDA_AMP_MUTE : 0;
-       snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
-                                HDA_AMP_MUTE, bits);
-       snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
-                                HDA_AMP_MUTE, bits);
-       snd_hda_input_jack_report(codec, nid);
-}
-
-/* unsolicited event for HP jack sensing */
-static void alc269_laptop_unsol_event(struct hda_codec *codec,
-                                    unsigned int res)
-{
-       switch (res >> 26) {
-       case ALC880_HP_EVENT:
-               alc269_speaker_automute(codec);
-               break;
-       case ALC880_MIC_EVENT:
-               alc_mic_automute(codec);
-               break;
-       }
-}
-
 static void alc269_laptop_amic_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
        spec->autocfg.hp_pins[0] = 0x15;
        spec->autocfg.speaker_pins[0] = 0x14;
+       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;
@@ -14488,6 +14455,9 @@ static void alc269_laptop_dmic_setup(struct hda_codec *codec)
        struct alc_spec *spec = codec->spec;
        spec->autocfg.hp_pins[0] = 0x15;
        spec->autocfg.speaker_pins[0] = 0x14;
+       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;
@@ -14500,6 +14470,9 @@ static void alc269vb_laptop_amic_setup(struct hda_codec *codec)
        struct alc_spec *spec = codec->spec;
        spec->autocfg.hp_pins[0] = 0x21;
        spec->autocfg.speaker_pins[0] = 0x14;
+       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;
@@ -14512,6 +14485,9 @@ static void alc269vb_laptop_dmic_setup(struct hda_codec *codec)
        struct alc_spec *spec = codec->spec;
        spec->autocfg.hp_pins[0] = 0x21;
        spec->autocfg.speaker_pins[0] = 0x14;
+       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;
@@ -14519,12 +14495,6 @@ static void alc269vb_laptop_dmic_setup(struct hda_codec *codec)
        spec->auto_mic = 1;
 }
 
-static void alc269_laptop_inithook(struct hda_codec *codec)
-{
-       alc269_speaker_automute(codec);
-       alc_mic_automute(codec);
-}
-
 /*
  * generic initialization of ADC, input mixers and output mixers
  */
@@ -14796,7 +14766,6 @@ static void alc269_auto_init(struct hda_codec *codec)
                alc_inithook(codec);
 }
 
-#ifdef SND_HDA_NEEDS_RESUME
 static void alc269_toggle_power_output(struct hda_codec *codec, int power_up)
 {
        int val = alc_read_coef_idx(codec, 0x04);
@@ -14807,25 +14776,17 @@ static void alc269_toggle_power_output(struct hda_codec *codec, int power_up)
        alc_write_coef_idx(codec, 0x04, val);
 }
 
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static int alc269_suspend(struct hda_codec *codec, pm_message_t state)
+static void alc269_shutup(struct hda_codec *codec)
 {
-       struct alc_spec *spec = codec->spec;
-
        if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x017)
                alc269_toggle_power_output(codec, 0);
        if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) {
                alc269_toggle_power_output(codec, 0);
                msleep(150);
        }
-
-       alc_shutup(codec);
-       if (spec && spec->power_hook)
-               spec->power_hook(codec);
-       return 0;
 }
-#endif /* CONFIG_SND_HDA_POWER_SAVE */
 
+#ifdef SND_HDA_NEEDS_RESUME
 static int alc269_resume(struct hda_codec *codec)
 {
        if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) {
@@ -15070,9 +15031,9 @@ static struct alc_config_preset alc269_presets[] = {
                .hp_nid = 0x03,
                .num_channel_mode = ARRAY_SIZE(alc269_modes),
                .channel_mode = alc269_modes,
-               .unsol_event = alc269_laptop_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc269_laptop_amic_setup,
-               .init_hook = alc269_laptop_inithook,
+               .init_hook = alc_inithook,
        },
        [ALC269_DMIC] = {
                .mixers = { alc269_laptop_mixer },
@@ -15084,9 +15045,9 @@ static struct alc_config_preset alc269_presets[] = {
                .hp_nid = 0x03,
                .num_channel_mode = ARRAY_SIZE(alc269_modes),
                .channel_mode = alc269_modes,
-               .unsol_event = alc269_laptop_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc269_laptop_dmic_setup,
-               .init_hook = alc269_laptop_inithook,
+               .init_hook = alc_inithook,
        },
        [ALC269VB_AMIC] = {
                .mixers = { alc269vb_laptop_mixer },
@@ -15098,9 +15059,9 @@ static struct alc_config_preset alc269_presets[] = {
                .hp_nid = 0x03,
                .num_channel_mode = ARRAY_SIZE(alc269_modes),
                .channel_mode = alc269_modes,
-               .unsol_event = alc269_laptop_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc269vb_laptop_amic_setup,
-               .init_hook = alc269_laptop_inithook,
+               .init_hook = alc_inithook,
        },
        [ALC269VB_DMIC] = {
                .mixers = { alc269vb_laptop_mixer },
@@ -15112,9 +15073,9 @@ static struct alc_config_preset alc269_presets[] = {
                .hp_nid = 0x03,
                .num_channel_mode = ARRAY_SIZE(alc269_modes),
                .channel_mode = alc269_modes,
-               .unsol_event = alc269_laptop_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc269vb_laptop_dmic_setup,
-               .init_hook = alc269_laptop_inithook,
+               .init_hook = alc_inithook,
        },
        [ALC269_FUJITSU] = {
                .mixers = { alc269_fujitsu_mixer },
@@ -15126,9 +15087,9 @@ static struct alc_config_preset alc269_presets[] = {
                .hp_nid = 0x03,
                .num_channel_mode = ARRAY_SIZE(alc269_modes),
                .channel_mode = alc269_modes,
-               .unsol_event = alc269_laptop_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc269_laptop_dmic_setup,
-               .init_hook = alc269_laptop_inithook,
+               .init_hook = alc_inithook,
        },
        [ALC269_LIFEBOOK] = {
                .mixers = { alc269_lifebook_mixer },
@@ -15140,6 +15101,7 @@ static struct alc_config_preset alc269_presets[] = {
                .channel_mode = alc269_modes,
                .input_mux = &alc269_capture_source,
                .unsol_event = alc269_lifebook_unsol_event,
+               .setup = alc269_lifebook_setup,
                .init_hook = alc269_lifebook_init_hook,
        },
        [ALC271_ACER] = {
@@ -15313,14 +15275,12 @@ static int patch_alc269(struct hda_codec *codec)
        spec->vmaster_nid = 0x02;
 
        codec->patch_ops = alc_patch_ops;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-       codec->patch_ops.suspend = alc269_suspend;
-#endif
 #ifdef SND_HDA_NEEDS_RESUME
        codec->patch_ops.resume = alc269_resume;
 #endif
        if (board_config == ALC269_AUTO)
                spec->init_hook = alc269_auto_init;
+       spec->shutup = alc269_shutup;
 
        alc_init_jacks(codec);
 #ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -16014,11 +15974,15 @@ static int alc861_auto_create_multi_out_ctls(struct hda_codec *codec,
        static const char * const chname[4] = {
                "Front", "Surround", NULL /*CLFE*/, "Side"
        };
-       const char *pfx = alc_get_line_out_pfx(cfg, true);
+       const char *pfx = alc_get_line_out_pfx(spec, true);
        hda_nid_t nid;
-       int i, err;
+       int i, err, noutputs;
 
-       for (i = 0; i < cfg->line_outs; i++) {
+       noutputs = cfg->line_outs;
+       if (spec->multi_ios > 0)
+               noutputs += spec->multi_ios;
+
+       for (i = 0; i < noutputs; i++) {
                nid = spec->multiout.dac_nids[i];
                if (!nid)
                        continue;
@@ -16161,6 +16125,9 @@ static int alc861_parse_auto_config(struct hda_codec *codec)
                return 0; /* can't find valid BIOS pin config */
 
        err = alc861_auto_fill_dac_nids(codec, &spec->autocfg);
+       if (err < 0)
+               return err;
+       err = alc_auto_add_multi_channel_mode(codec);
        if (err < 0)
                return err;
        err = alc861_auto_create_multi_out_ctls(codec, &spec->autocfg);
@@ -16835,11 +16802,13 @@ static void alc861vd_lenovo_setup(struct hda_codec *codec)
        struct alc_spec *spec = codec->spec;
        spec->autocfg.hp_pins[0] = 0x1b;
        spec->autocfg.speaker_pins[0] = 0x14;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 static void alc861vd_lenovo_init_hook(struct hda_codec *codec)
 {
-       alc_automute_amp(codec);
+       alc_hp_automute(codec);
        alc88x_simple_mic_automute(codec);
 }
 
@@ -16851,7 +16820,7 @@ static void alc861vd_lenovo_unsol_event(struct hda_codec *codec,
                alc88x_simple_mic_automute(codec);
                break;
        default:
-               alc_automute_amp_unsol_event(codec, res);
+               alc_sku_unsol_event(codec, res);
                break;
        }
 }
@@ -16908,6 +16877,8 @@ static void alc861vd_dallas_setup(struct hda_codec *codec)
 
        spec->autocfg.hp_pins[0] = 0x15;
        spec->autocfg.speaker_pins[0] = 0x14;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -17032,9 +17003,9 @@ static struct alc_config_preset alc861vd_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
                .channel_mode = alc861vd_3stack_2ch_modes,
                .input_mux = &alc861vd_dallas_capture_source,
-               .unsol_event = alc_automute_amp_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc861vd_dallas_setup,
-               .init_hook = alc_automute_amp,
+               .init_hook = alc_hp_automute,
        },
        [ALC861VD_HP] = {
                .mixers = { alc861vd_hp_mixer },
@@ -17045,9 +17016,9 @@ static struct alc_config_preset alc861vd_presets[] = {
                .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
                .channel_mode = alc861vd_3stack_2ch_modes,
                .input_mux = &alc861vd_hp_capture_source,
-               .unsol_event = alc_automute_amp_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc861vd_dallas_setup,
-               .init_hook = alc_automute_amp,
+               .init_hook = alc_hp_automute,
        },
        [ALC660VD_ASUS_V1S] = {
                .mixers = { alc861vd_lenovo_mixer },
@@ -17146,11 +17117,15 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec,
        static const char * const chname[4] = {
                "Front", "Surround", "CLFE", "Side"
        };
-       const char *pfx = alc_get_line_out_pfx(cfg, true);
+       const char *pfx = alc_get_line_out_pfx(spec, true);
        hda_nid_t nid_v, nid_s;
-       int i, err;
+       int i, err, noutputs;
 
-       for (i = 0; i < cfg->line_outs; i++) {
+       noutputs = cfg->line_outs;
+       if (spec->multi_ios > 0)
+               noutputs += spec->multi_ios;
+
+       for (i = 0; i < noutputs; i++) {
                if (!spec->multiout.dac_nids[i])
                        continue;
                nid_v = alc861vd_idx_to_mixer_vol(
@@ -17273,6 +17248,9 @@ static int alc861vd_parse_auto_config(struct hda_codec *codec)
                return 0; /* can't find valid BIOS pin config */
 
        err = alc880_auto_fill_dac_nids(spec, &spec->autocfg);
+       if (err < 0)
+               return err;
+       err = alc_auto_add_multi_channel_mode(codec);
        if (err < 0)
                return err;
        err = alc861vd_auto_create_multi_out_ctls(spec, &spec->autocfg);
@@ -17426,6 +17404,7 @@ static int patch_alc861vd(struct hda_codec *codec)
 
        if (board_config == ALC861VD_AUTO)
                spec->init_hook = alc861vd_auto_init;
+       spec->shutup = alc_eapd_shutup;
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        if (!spec->loopback.amplist)
                spec->loopback.amplist = alc861vd_loopbacks;
@@ -17448,8 +17427,8 @@ static int patch_alc861vd(struct hda_codec *codec)
 #define ALC662_DIGOUT_NID      0x06
 #define ALC662_DIGIN_NID       0x0a
 
-static hda_nid_t alc662_dac_nids[4] = {
-       /* front, rear, clfe, rear_surr */
+static hda_nid_t alc662_dac_nids[3] = {
+       /* front, rear, clfe */
        0x02, 0x03, 0x04
 };
 
@@ -17938,32 +17917,13 @@ static struct hda_verb alc662_init_verbs[] = {
        {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
        {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 
-       /* always trun on EAPD */
-       {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
-       {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
-
-       { }
-};
-
-static struct hda_verb alc663_init_verbs[] = {
-       {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-       {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
        { }
 };
 
-static struct hda_verb alc272_init_verbs[] = {
-       {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-       {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+static struct hda_verb alc662_eapd_init_verbs[] = {
+       /* always trun on EAPD */
+       {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
+       {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
        { }
 };
 
@@ -18179,49 +18139,17 @@ static struct snd_kcontrol_new alc272_auto_capture_mixer[] = {
        { } /* end */
 };
 
-static void alc662_lenovo_101e_ispeaker_automute(struct hda_codec *codec)
-{
-       unsigned int present;
-       unsigned char bits;
-
-       present = snd_hda_jack_detect(codec, 0x14);
-       bits = present ? HDA_AMP_MUTE : 0;
-
-       snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, bits);
-}
-
-static void alc662_lenovo_101e_all_automute(struct hda_codec *codec)
-{
-       unsigned int present;
-       unsigned char bits;
-
-       present = snd_hda_jack_detect(codec, 0x1b);
-       bits = present ? HDA_AMP_MUTE : 0;
-
-       snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, bits);
-       snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, bits);
-}
-
-static void alc662_lenovo_101e_unsol_event(struct hda_codec *codec,
-                                          unsigned int res)
+static void alc662_lenovo_101e_setup(struct hda_codec *codec)
 {
-       if ((res >> 26) == ALC880_HP_EVENT)
-               alc662_lenovo_101e_all_automute(codec);
-       if ((res >> 26) == ALC880_FRONT_EVENT)
-               alc662_lenovo_101e_ispeaker_automute(codec);
-}
+       struct alc_spec *spec = codec->spec;
 
-/* unsolicited event for HP jack sensing */
-static void alc662_eeepc_unsol_event(struct hda_codec *codec,
-                                    unsigned int res)
-{
-       if ((res >> 26) == ALC880_MIC_EVENT)
-               alc_mic_automute(codec);
-       else
-               alc262_hippo_unsol_event(codec, res);
+       spec->autocfg.hp_pins[0] = 0x1b;
+       spec->autocfg.line_out_pins[0] = 0x14;
+       spec->autocfg.speaker_pins[0] = 0x15;
+       spec->automute = 1;
+       spec->detect_line = 1;
+       spec->automute_lines = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 static void alc662_eeepc_setup(struct hda_codec *codec)
@@ -18236,180 +18164,24 @@ static void alc662_eeepc_setup(struct hda_codec *codec)
        spec->auto_mic = 1;
 }
 
-static void alc662_eeepc_inithook(struct hda_codec *codec)
-{
-       alc262_hippo_automute(codec);
-       alc_mic_automute(codec);
-}
-
 static void alc662_eeepc_ep20_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
        spec->autocfg.hp_pins[0] = 0x14;
        spec->autocfg.speaker_pins[0] = 0x1b;
-}
-
-#define alc662_eeepc_ep20_inithook     alc262_hippo_master_update
-
-static void alc663_m51va_speaker_automute(struct hda_codec *codec)
-{
-       unsigned int present;
-       unsigned char bits;
-
-       present = snd_hda_jack_detect(codec, 0x21);
-       bits = present ? HDA_AMP_MUTE : 0;
-       snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
-                                HDA_AMP_MUTE, bits);
-       snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
-                                HDA_AMP_MUTE, bits);
-}
-
-static void alc663_21jd_two_speaker_automute(struct hda_codec *codec)
-{
-       unsigned int present;
-       unsigned char bits;
-
-       present = snd_hda_jack_detect(codec, 0x21);
-       bits = present ? HDA_AMP_MUTE : 0;
-       snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
-                                HDA_AMP_MUTE, bits);
-       snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
-                                HDA_AMP_MUTE, bits);
-       snd_hda_codec_amp_stereo(codec, 0x0e, HDA_INPUT, 0,
-                                HDA_AMP_MUTE, bits);
-       snd_hda_codec_amp_stereo(codec, 0x0e, HDA_INPUT, 1,
-                                HDA_AMP_MUTE, bits);
-}
-
-static void alc663_15jd_two_speaker_automute(struct hda_codec *codec)
-{
-       unsigned int present;
-       unsigned char bits;
-
-       present = snd_hda_jack_detect(codec, 0x15);
-       bits = present ? HDA_AMP_MUTE : 0;
-       snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
-                                HDA_AMP_MUTE, bits);
-       snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
-                                HDA_AMP_MUTE, bits);
-       snd_hda_codec_amp_stereo(codec, 0x0e, HDA_INPUT, 0,
-                                HDA_AMP_MUTE, bits);
-       snd_hda_codec_amp_stereo(codec, 0x0e, HDA_INPUT, 1,
-                                HDA_AMP_MUTE, bits);
-}
-
-static void alc662_f5z_speaker_automute(struct hda_codec *codec)
-{
-       unsigned int present;
-       unsigned char bits;
-
-       present = snd_hda_jack_detect(codec, 0x1b);
-       bits = present ? 0 : PIN_OUT;
-       snd_hda_codec_write(codec, 0x14, 0,
-                        AC_VERB_SET_PIN_WIDGET_CONTROL, bits);
-}
-
-static void alc663_two_hp_m1_speaker_automute(struct hda_codec *codec)
-{
-       unsigned int present1, present2;
-
-       present1 = snd_hda_jack_detect(codec, 0x21);
-       present2 = snd_hda_jack_detect(codec, 0x15);
-
-       if (present1 || present2) {
-               snd_hda_codec_write_cache(codec, 0x14, 0,
-                       AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
-       } else {
-               snd_hda_codec_write_cache(codec, 0x14, 0,
-                       AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
-       }
-}
-
-static void alc663_two_hp_m2_speaker_automute(struct hda_codec *codec)
-{
-       unsigned int present1, present2;
-
-       present1 = snd_hda_jack_detect(codec, 0x1b);
-       present2 = snd_hda_jack_detect(codec, 0x15);
-
-       if (present1 || present2) {
-               snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
-                                        HDA_AMP_MUTE, HDA_AMP_MUTE);
-               snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
-                                        HDA_AMP_MUTE, HDA_AMP_MUTE);
-       } else {
-               snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
-                                        HDA_AMP_MUTE, 0);
-               snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
-                                        HDA_AMP_MUTE, 0);
-       }
-}
-
-static void alc663_two_hp_m7_speaker_automute(struct hda_codec *codec)
-{
-       unsigned int present1, present2;
-
-       present1 = snd_hda_codec_read(codec, 0x1b, 0,
-                       AC_VERB_GET_PIN_SENSE, 0)
-                       & AC_PINSENSE_PRESENCE;
-       present2 = snd_hda_codec_read(codec, 0x21, 0,
-                       AC_VERB_GET_PIN_SENSE, 0)
-                       & AC_PINSENSE_PRESENCE;
-
-       if (present1 || present2) {
-               snd_hda_codec_write_cache(codec, 0x14, 0,
-                       AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
-               snd_hda_codec_write_cache(codec, 0x17, 0,
-                       AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
-       } else {
-               snd_hda_codec_write_cache(codec, 0x14, 0,
-                       AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
-               snd_hda_codec_write_cache(codec, 0x17, 0,
-                       AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
-       }
-}
-
-static void alc663_two_hp_m8_speaker_automute(struct hda_codec *codec)
-{
-       unsigned int present1, present2;
-
-       present1 = snd_hda_codec_read(codec, 0x21, 0,
-                       AC_VERB_GET_PIN_SENSE, 0)
-                       & AC_PINSENSE_PRESENCE;
-       present2 = snd_hda_codec_read(codec, 0x15, 0,
-                       AC_VERB_GET_PIN_SENSE, 0)
-                       & AC_PINSENSE_PRESENCE;
-
-       if (present1 || present2) {
-               snd_hda_codec_write_cache(codec, 0x14, 0,
-                       AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
-               snd_hda_codec_write_cache(codec, 0x17, 0,
-                       AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
-       } else {
-               snd_hda_codec_write_cache(codec, 0x14, 0,
-                       AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
-               snd_hda_codec_write_cache(codec, 0x17, 0,
-                       AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
-       }
-}
-
-static void alc663_m51va_unsol_event(struct hda_codec *codec,
-                                          unsigned int res)
-{
-       switch (res >> 26) {
-       case ALC880_HP_EVENT:
-               alc663_m51va_speaker_automute(codec);
-               break;
-       case ALC880_MIC_EVENT:
-               alc_mic_automute(codec);
-               break;
-       }
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 static void alc663_m51va_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
+       spec->autocfg.hp_pins[0] = 0x21;
+       spec->autocfg.speaker_pins[0] = 0x14;
+       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;
@@ -18417,18 +18189,15 @@ static void alc663_m51va_setup(struct hda_codec *codec)
        spec->auto_mic = 1;
 }
 
-static void alc663_m51va_inithook(struct hda_codec *codec)
-{
-       alc663_m51va_speaker_automute(codec);
-       alc_mic_automute(codec);
-}
-
 /* ***************** Mode1 ******************************/
-#define alc663_mode1_unsol_event       alc663_m51va_unsol_event
-
 static void alc663_mode1_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
+       spec->autocfg.hp_pins[0] = 0x21;
+       spec->autocfg.speaker_pins[0] = 0x14;
+       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;
@@ -18436,228 +18205,143 @@ static void alc663_mode1_setup(struct hda_codec *codec)
        spec->auto_mic = 1;
 }
 
-#define alc663_mode1_inithook          alc663_m51va_inithook
-
 /* ***************** Mode2 ******************************/
-static void alc662_mode2_unsol_event(struct hda_codec *codec,
-                                          unsigned int res)
+static void alc662_mode2_setup(struct hda_codec *codec)
 {
-       switch (res >> 26) {
-       case ALC880_HP_EVENT:
-               alc662_f5z_speaker_automute(codec);
-               break;
-       case ALC880_MIC_EVENT:
-               alc_mic_automute(codec);
-               break;
-       }
+       struct alc_spec *spec = codec->spec;
+       spec->autocfg.hp_pins[0] = 0x1b;
+       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->auto_mic = 1;
 }
 
-#define alc662_mode2_setup     alc663_mode1_setup
-
-static void alc662_mode2_inithook(struct hda_codec *codec)
-{
-       alc662_f5z_speaker_automute(codec);
-       alc_mic_automute(codec);
-}
 /* ***************** Mode3 ******************************/
-static void alc663_mode3_unsol_event(struct hda_codec *codec,
-                                          unsigned int res)
+static void alc663_mode3_setup(struct hda_codec *codec)
 {
-       switch (res >> 26) {
-       case ALC880_HP_EVENT:
-               alc663_two_hp_m1_speaker_automute(codec);
-               break;
-       case ALC880_MIC_EVENT:
-               alc_mic_automute(codec);
-               break;
-       }
+       struct alc_spec *spec = codec->spec;
+       spec->autocfg.hp_pins[0] = 0x21;
+       spec->autocfg.hp_pins[0] = 0x15;
+       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->auto_mic = 1;
 }
 
-#define alc663_mode3_setup     alc663_mode1_setup
-
-static void alc663_mode3_inithook(struct hda_codec *codec)
-{
-       alc663_two_hp_m1_speaker_automute(codec);
-       alc_mic_automute(codec);
-}
 /* ***************** Mode4 ******************************/
-static void alc663_mode4_unsol_event(struct hda_codec *codec,
-                                          unsigned int res)
+static void alc663_mode4_setup(struct hda_codec *codec)
 {
-       switch (res >> 26) {
-       case ALC880_HP_EVENT:
-               alc663_21jd_two_speaker_automute(codec);
-               break;
-       case ALC880_MIC_EVENT:
-               alc_mic_automute(codec);
-               break;
-       }
+       struct alc_spec *spec = codec->spec;
+       spec->autocfg.hp_pins[0] = 0x21;
+       spec->autocfg.speaker_pins[0] = 0x14;
+       spec->autocfg.speaker_pins[1] = 0x16;
+       spec->automute_mixer_nid[0] = 0x0c;
+       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->auto_mic = 1;
 }
 
-#define alc663_mode4_setup     alc663_mode1_setup
-
-static void alc663_mode4_inithook(struct hda_codec *codec)
-{
-       alc663_21jd_two_speaker_automute(codec);
-       alc_mic_automute(codec);
-}
 /* ***************** Mode5 ******************************/
-static void alc663_mode5_unsol_event(struct hda_codec *codec,
-                                          unsigned int res)
+static void alc663_mode5_setup(struct hda_codec *codec)
 {
-       switch (res >> 26) {
-       case ALC880_HP_EVENT:
-               alc663_15jd_two_speaker_automute(codec);
-               break;
-       case ALC880_MIC_EVENT:
-               alc_mic_automute(codec);
-               break;
-       }
+       struct alc_spec *spec = codec->spec;
+       spec->autocfg.hp_pins[0] = 0x15;
+       spec->autocfg.speaker_pins[0] = 0x14;
+       spec->autocfg.speaker_pins[1] = 0x16;
+       spec->automute_mixer_nid[0] = 0x0c;
+       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->auto_mic = 1;
 }
 
-#define alc663_mode5_setup     alc663_mode1_setup
-
-static void alc663_mode5_inithook(struct hda_codec *codec)
-{
-       alc663_15jd_two_speaker_automute(codec);
-       alc_mic_automute(codec);
-}
 /* ***************** Mode6 ******************************/
-static void alc663_mode6_unsol_event(struct hda_codec *codec,
-                                          unsigned int res)
+static void alc663_mode6_setup(struct hda_codec *codec)
 {
-       switch (res >> 26) {
-       case ALC880_HP_EVENT:
-               alc663_two_hp_m2_speaker_automute(codec);
-               break;
-       case ALC880_MIC_EVENT:
-               alc_mic_automute(codec);
-               break;
-       }
-}
-
-#define alc663_mode6_setup     alc663_mode1_setup
-
-static void alc663_mode6_inithook(struct hda_codec *codec)
-{
-       alc663_two_hp_m2_speaker_automute(codec);
-       alc_mic_automute(codec);
+       struct alc_spec *spec = codec->spec;
+       spec->autocfg.hp_pins[0] = 0x1b;
+       spec->autocfg.hp_pins[0] = 0x15;
+       spec->autocfg.speaker_pins[0] = 0x14;
+       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->auto_mic = 1;
 }
 
 /* ***************** Mode7 ******************************/
-static void alc663_mode7_unsol_event(struct hda_codec *codec,
-                                          unsigned int res)
+static void alc663_mode7_setup(struct hda_codec *codec)
 {
-       switch (res >> 26) {
-       case ALC880_HP_EVENT:
-               alc663_two_hp_m7_speaker_automute(codec);
-               break;
-       case ALC880_MIC_EVENT:
-               alc_mic_automute(codec);
-               break;
-       }
-}
-
-#define alc663_mode7_setup     alc663_mode1_setup
-
-static void alc663_mode7_inithook(struct hda_codec *codec)
-{
-       alc663_two_hp_m7_speaker_automute(codec);
-       alc_mic_automute(codec);
+       struct alc_spec *spec = codec->spec;
+       spec->autocfg.hp_pins[0] = 0x1b;
+       spec->autocfg.hp_pins[0] = 0x21;
+       spec->autocfg.speaker_pins[0] = 0x14;
+       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->auto_mic = 1;
 }
 
 /* ***************** Mode8 ******************************/
-static void alc663_mode8_unsol_event(struct hda_codec *codec,
-                                          unsigned int res)
-{
-       switch (res >> 26) {
-       case ALC880_HP_EVENT:
-               alc663_two_hp_m8_speaker_automute(codec);
-               break;
-       case ALC880_MIC_EVENT:
-               alc_mic_automute(codec);
-               break;
-       }
-}
-
-#define alc663_mode8_setup     alc663_m51va_setup
-
-static void alc663_mode8_inithook(struct hda_codec *codec)
-{
-       alc663_two_hp_m8_speaker_automute(codec);
-       alc_mic_automute(codec);
-}
-
-static void alc663_g71v_hp_automute(struct hda_codec *codec)
-{
-       unsigned int present;
-       unsigned char bits;
-
-       present = snd_hda_jack_detect(codec, 0x21);
-       bits = present ? HDA_AMP_MUTE : 0;
-       snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, bits);
-       snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, bits);
-}
-
-static void alc663_g71v_front_automute(struct hda_codec *codec)
+static void alc663_mode8_setup(struct hda_codec *codec)
 {
-       unsigned int present;
-       unsigned char bits;
-
-       present = snd_hda_jack_detect(codec, 0x15);
-       bits = present ? HDA_AMP_MUTE : 0;
-       snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, bits);
-}
-
-static void alc663_g71v_unsol_event(struct hda_codec *codec,
-                                          unsigned int res)
-{
-       switch (res >> 26) {
-       case ALC880_HP_EVENT:
-               alc663_g71v_hp_automute(codec);
-               break;
-       case ALC880_FRONT_EVENT:
-               alc663_g71v_front_automute(codec);
-               break;
-       case ALC880_MIC_EVENT:
-               alc_mic_automute(codec);
-               break;
-       }
-}
-
-#define alc663_g71v_setup      alc663_m51va_setup
-
-static void alc663_g71v_inithook(struct hda_codec *codec)
-{
-       alc663_g71v_front_automute(codec);
-       alc663_g71v_hp_automute(codec);
-       alc_mic_automute(codec);
+       struct alc_spec *spec = codec->spec;
+       spec->autocfg.hp_pins[0] = 0x21;
+       spec->autocfg.hp_pins[1] = 0x15;
+       spec->autocfg.speaker_pins[0] = 0x14;
+       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->auto_mic = 1;
 }
 
-static void alc663_g50v_unsol_event(struct hda_codec *codec,
-                                          unsigned int res)
+static void alc663_g71v_setup(struct hda_codec *codec)
 {
-       switch (res >> 26) {
-       case ALC880_HP_EVENT:
-               alc663_m51va_speaker_automute(codec);
-               break;
-       case ALC880_MIC_EVENT:
-               alc_mic_automute(codec);
-               break;
-       }
+       struct alc_spec *spec = codec->spec;
+       spec->autocfg.hp_pins[0] = 0x21;
+       spec->autocfg.line_out_pins[0] = 0x15;
+       spec->autocfg.speaker_pins[0] = 0x14;
+       spec->automute = 1;
+       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->auto_mic = 1;
 }
 
 #define alc663_g50v_setup      alc663_m51va_setup
 
-static void alc663_g50v_inithook(struct hda_codec *codec)
-{
-       alc663_m51va_speaker_automute(codec);
-       alc_mic_automute(codec);
-}
-
 static struct snd_kcontrol_new alc662_ecs_mixer[] = {
        HDA_CODEC_VOLUME("Master Playback Volume", 0x02, 0x0, HDA_OUTPUT),
        ALC262_HIPPO_MASTER_SWITCH,
@@ -18707,7 +18391,7 @@ static const char * const alc662_models[ALC662_MODEL_LAST] = {
        [ALC662_3ST_2ch_DIG]    = "3stack-dig",
        [ALC662_3ST_6ch_DIG]    = "3stack-6ch-dig",
        [ALC662_3ST_6ch]        = "3stack-6ch",
-       [ALC662_5ST_DIG]        = "6stack-dig",
+       [ALC662_5ST_DIG]        = "5stack-dig",
        [ALC662_LENOVO_101E]    = "lenovo-101e",
        [ALC662_ASUS_EEEPC_P701] = "eeepc-p701",
        [ALC662_ASUS_EEEPC_EP20] = "eeepc-ep20",
@@ -18815,7 +18499,7 @@ static struct snd_pci_quirk alc662_cfg_tbl[] = {
 static struct alc_config_preset alc662_presets[] = {
        [ALC662_3ST_2ch_DIG] = {
                .mixers = { alc662_3ST_2ch_mixer },
-               .init_verbs = { alc662_init_verbs },
+               .init_verbs = { alc662_init_verbs, alc662_eapd_init_verbs },
                .num_dacs = ARRAY_SIZE(alc662_dac_nids),
                .dac_nids = alc662_dac_nids,
                .dig_out_nid = ALC662_DIGOUT_NID,
@@ -18826,7 +18510,7 @@ static struct alc_config_preset alc662_presets[] = {
        },
        [ALC662_3ST_6ch_DIG] = {
                .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer },
-               .init_verbs = { alc662_init_verbs },
+               .init_verbs = { alc662_init_verbs, alc662_eapd_init_verbs },
                .num_dacs = ARRAY_SIZE(alc662_dac_nids),
                .dac_nids = alc662_dac_nids,
                .dig_out_nid = ALC662_DIGOUT_NID,
@@ -18838,7 +18522,7 @@ static struct alc_config_preset alc662_presets[] = {
        },
        [ALC662_3ST_6ch] = {
                .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer },
-               .init_verbs = { alc662_init_verbs },
+               .init_verbs = { alc662_init_verbs, alc662_eapd_init_verbs },
                .num_dacs = ARRAY_SIZE(alc662_dac_nids),
                .dac_nids = alc662_dac_nids,
                .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes),
@@ -18848,7 +18532,7 @@ static struct alc_config_preset alc662_presets[] = {
        },
        [ALC662_5ST_DIG] = {
                .mixers = { alc662_base_mixer, alc662_chmode_mixer },
-               .init_verbs = { alc662_init_verbs },
+               .init_verbs = { alc662_init_verbs, alc662_eapd_init_verbs },
                .num_dacs = ARRAY_SIZE(alc662_dac_nids),
                .dac_nids = alc662_dac_nids,
                .dig_out_nid = ALC662_DIGOUT_NID,
@@ -18859,104 +18543,120 @@ static struct alc_config_preset alc662_presets[] = {
        },
        [ALC662_LENOVO_101E] = {
                .mixers = { alc662_lenovo_101e_mixer },
-               .init_verbs = { alc662_init_verbs, alc662_sue_init_verbs },
+               .init_verbs = { alc662_init_verbs,
+                               alc662_eapd_init_verbs,
+                               alc662_sue_init_verbs },
                .num_dacs = ARRAY_SIZE(alc662_dac_nids),
                .dac_nids = alc662_dac_nids,
                .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
                .channel_mode = alc662_3ST_2ch_modes,
                .input_mux = &alc662_lenovo_101e_capture_source,
-               .unsol_event = alc662_lenovo_101e_unsol_event,
-               .init_hook = alc662_lenovo_101e_all_automute,
+               .unsol_event = alc_sku_unsol_event,
+               .setup = alc662_lenovo_101e_setup,
+               .init_hook = alc_inithook,
        },
        [ALC662_ASUS_EEEPC_P701] = {
                .mixers = { alc662_eeepc_p701_mixer },
                .init_verbs = { alc662_init_verbs,
+                               alc662_eapd_init_verbs,
                                alc662_eeepc_sue_init_verbs },
                .num_dacs = ARRAY_SIZE(alc662_dac_nids),
                .dac_nids = alc662_dac_nids,
                .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
                .channel_mode = alc662_3ST_2ch_modes,
-               .unsol_event = alc662_eeepc_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc662_eeepc_setup,
-               .init_hook = alc662_eeepc_inithook,
+               .init_hook = alc_inithook,
        },
        [ALC662_ASUS_EEEPC_EP20] = {
                .mixers = { alc662_eeepc_ep20_mixer,
                            alc662_chmode_mixer },
                .init_verbs = { alc662_init_verbs,
+                               alc662_eapd_init_verbs,
                                alc662_eeepc_ep20_sue_init_verbs },
                .num_dacs = ARRAY_SIZE(alc662_dac_nids),
                .dac_nids = alc662_dac_nids,
                .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes),
                .channel_mode = alc662_3ST_6ch_modes,
                .input_mux = &alc662_lenovo_101e_capture_source,
-               .unsol_event = alc662_eeepc_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc662_eeepc_ep20_setup,
-               .init_hook = alc662_eeepc_ep20_inithook,
+               .init_hook = alc_inithook,
        },
        [ALC662_ECS] = {
                .mixers = { alc662_ecs_mixer },
                .init_verbs = { alc662_init_verbs,
+                               alc662_eapd_init_verbs,
                                alc662_ecs_init_verbs },
                .num_dacs = ARRAY_SIZE(alc662_dac_nids),
                .dac_nids = alc662_dac_nids,
                .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
                .channel_mode = alc662_3ST_2ch_modes,
-               .unsol_event = alc662_eeepc_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc662_eeepc_setup,
-               .init_hook = alc662_eeepc_inithook,
+               .init_hook = alc_inithook,
        },
        [ALC663_ASUS_M51VA] = {
                .mixers = { alc663_m51va_mixer },
-               .init_verbs = { alc662_init_verbs, alc663_m51va_init_verbs },
+               .init_verbs = { alc662_init_verbs,
+                               alc662_eapd_init_verbs,
+                               alc663_m51va_init_verbs },
                .num_dacs = ARRAY_SIZE(alc662_dac_nids),
                .dac_nids = alc662_dac_nids,
                .dig_out_nid = ALC662_DIGOUT_NID,
                .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
                .channel_mode = alc662_3ST_2ch_modes,
-               .unsol_event = alc663_m51va_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc663_m51va_setup,
-               .init_hook = alc663_m51va_inithook,
+               .init_hook = alc_inithook,
        },
        [ALC663_ASUS_G71V] = {
                .mixers = { alc663_g71v_mixer },
-               .init_verbs = { alc662_init_verbs, alc663_g71v_init_verbs },
+               .init_verbs = { alc662_init_verbs,
+                               alc662_eapd_init_verbs,
+                               alc663_g71v_init_verbs },
                .num_dacs = ARRAY_SIZE(alc662_dac_nids),
                .dac_nids = alc662_dac_nids,
                .dig_out_nid = ALC662_DIGOUT_NID,
                .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
                .channel_mode = alc662_3ST_2ch_modes,
-               .unsol_event = alc663_g71v_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc663_g71v_setup,
-               .init_hook = alc663_g71v_inithook,
+               .init_hook = alc_inithook,
        },
        [ALC663_ASUS_H13] = {
                .mixers = { alc663_m51va_mixer },
-               .init_verbs = { alc662_init_verbs, alc663_m51va_init_verbs },
+               .init_verbs = { alc662_init_verbs,
+                               alc662_eapd_init_verbs,
+                               alc663_m51va_init_verbs },
                .num_dacs = ARRAY_SIZE(alc662_dac_nids),
                .dac_nids = alc662_dac_nids,
                .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
                .channel_mode = alc662_3ST_2ch_modes,
-               .unsol_event = alc663_m51va_unsol_event,
-               .init_hook = alc663_m51va_inithook,
+               .setup = alc663_m51va_setup,
+               .unsol_event = alc_sku_unsol_event,
+               .init_hook = alc_inithook,
        },
        [ALC663_ASUS_G50V] = {
                .mixers = { alc663_g50v_mixer },
-               .init_verbs = { alc662_init_verbs, alc663_g50v_init_verbs },
+               .init_verbs = { alc662_init_verbs,
+                               alc662_eapd_init_verbs,
+                               alc663_g50v_init_verbs },
                .num_dacs = ARRAY_SIZE(alc662_dac_nids),
                .dac_nids = alc662_dac_nids,
                .dig_out_nid = ALC662_DIGOUT_NID,
                .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes),
                .channel_mode = alc662_3ST_6ch_modes,
                .input_mux = &alc663_capture_source,
-               .unsol_event = alc663_g50v_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc663_g50v_setup,
-               .init_hook = alc663_g50v_inithook,
+               .init_hook = alc_inithook,
        },
        [ALC663_ASUS_MODE1] = {
                .mixers = { alc663_m51va_mixer },
                .cap_mixer = alc662_auto_capture_mixer,
                .init_verbs = { alc662_init_verbs,
+                               alc662_eapd_init_verbs,
                                alc663_21jd_amic_init_verbs },
                .num_dacs = ARRAY_SIZE(alc662_dac_nids),
                .hp_nid = 0x03,
@@ -18964,28 +18664,30 @@ static struct alc_config_preset alc662_presets[] = {
                .dig_out_nid = ALC662_DIGOUT_NID,
                .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
                .channel_mode = alc662_3ST_2ch_modes,
-               .unsol_event = alc663_mode1_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc663_mode1_setup,
-               .init_hook = alc663_mode1_inithook,
+               .init_hook = alc_inithook,
        },
        [ALC662_ASUS_MODE2] = {
                .mixers = { alc662_1bjd_mixer },
                .cap_mixer = alc662_auto_capture_mixer,
                .init_verbs = { alc662_init_verbs,
+                               alc662_eapd_init_verbs,
                                alc662_1bjd_amic_init_verbs },
                .num_dacs = ARRAY_SIZE(alc662_dac_nids),
                .dac_nids = alc662_dac_nids,
                .dig_out_nid = ALC662_DIGOUT_NID,
                .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
                .channel_mode = alc662_3ST_2ch_modes,
-               .unsol_event = alc662_mode2_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc662_mode2_setup,
-               .init_hook = alc662_mode2_inithook,
+               .init_hook = alc_inithook,
        },
        [ALC663_ASUS_MODE3] = {
                .mixers = { alc663_two_hp_m1_mixer },
                .cap_mixer = alc662_auto_capture_mixer,
                .init_verbs = { alc662_init_verbs,
+                               alc662_eapd_init_verbs,
                                alc663_two_hp_amic_m1_init_verbs },
                .num_dacs = ARRAY_SIZE(alc662_dac_nids),
                .hp_nid = 0x03,
@@ -18993,14 +18695,15 @@ static struct alc_config_preset alc662_presets[] = {
                .dig_out_nid = ALC662_DIGOUT_NID,
                .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
                .channel_mode = alc662_3ST_2ch_modes,
-               .unsol_event = alc663_mode3_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc663_mode3_setup,
-               .init_hook = alc663_mode3_inithook,
+               .init_hook = alc_inithook,
        },
        [ALC663_ASUS_MODE4] = {
                .mixers = { alc663_asus_21jd_clfe_mixer },
                .cap_mixer = alc662_auto_capture_mixer,
                .init_verbs = { alc662_init_verbs,
+                               alc662_eapd_init_verbs,
                                alc663_21jd_amic_init_verbs},
                .num_dacs = ARRAY_SIZE(alc662_dac_nids),
                .hp_nid = 0x03,
@@ -19008,14 +18711,15 @@ static struct alc_config_preset alc662_presets[] = {
                .dig_out_nid = ALC662_DIGOUT_NID,
                .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
                .channel_mode = alc662_3ST_2ch_modes,
-               .unsol_event = alc663_mode4_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc663_mode4_setup,
-               .init_hook = alc663_mode4_inithook,
+               .init_hook = alc_inithook,
        },
        [ALC663_ASUS_MODE5] = {
                .mixers = { alc663_asus_15jd_clfe_mixer },
                .cap_mixer = alc662_auto_capture_mixer,
                .init_verbs = { alc662_init_verbs,
+                               alc662_eapd_init_verbs,
                                alc663_15jd_amic_init_verbs },
                .num_dacs = ARRAY_SIZE(alc662_dac_nids),
                .hp_nid = 0x03,
@@ -19023,14 +18727,15 @@ static struct alc_config_preset alc662_presets[] = {
                .dig_out_nid = ALC662_DIGOUT_NID,
                .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
                .channel_mode = alc662_3ST_2ch_modes,
-               .unsol_event = alc663_mode5_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc663_mode5_setup,
-               .init_hook = alc663_mode5_inithook,
+               .init_hook = alc_inithook,
        },
        [ALC663_ASUS_MODE6] = {
                .mixers = { alc663_two_hp_m2_mixer },
                .cap_mixer = alc662_auto_capture_mixer,
                .init_verbs = { alc662_init_verbs,
+                               alc662_eapd_init_verbs,
                                alc663_two_hp_amic_m2_init_verbs },
                .num_dacs = ARRAY_SIZE(alc662_dac_nids),
                .hp_nid = 0x03,
@@ -19038,14 +18743,15 @@ static struct alc_config_preset alc662_presets[] = {
                .dig_out_nid = ALC662_DIGOUT_NID,
                .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
                .channel_mode = alc662_3ST_2ch_modes,
-               .unsol_event = alc663_mode6_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc663_mode6_setup,
-               .init_hook = alc663_mode6_inithook,
+               .init_hook = alc_inithook,
        },
        [ALC663_ASUS_MODE7] = {
                .mixers = { alc663_mode7_mixer },
                .cap_mixer = alc662_auto_capture_mixer,
                .init_verbs = { alc662_init_verbs,
+                               alc662_eapd_init_verbs,
                                alc663_mode7_init_verbs },
                .num_dacs = ARRAY_SIZE(alc662_dac_nids),
                .hp_nid = 0x03,
@@ -19053,14 +18759,15 @@ static struct alc_config_preset alc662_presets[] = {
                .dig_out_nid = ALC662_DIGOUT_NID,
                .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
                .channel_mode = alc662_3ST_2ch_modes,
-               .unsol_event = alc663_mode7_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc663_mode7_setup,
-               .init_hook = alc663_mode7_inithook,
+               .init_hook = alc_inithook,
        },
        [ALC663_ASUS_MODE8] = {
                .mixers = { alc663_mode8_mixer },
                .cap_mixer = alc662_auto_capture_mixer,
                .init_verbs = { alc662_init_verbs,
+                               alc662_eapd_init_verbs,
                                alc663_mode8_init_verbs },
                .num_dacs = ARRAY_SIZE(alc662_dac_nids),
                .hp_nid = 0x03,
@@ -19068,52 +18775,57 @@ static struct alc_config_preset alc662_presets[] = {
                .dig_out_nid = ALC662_DIGOUT_NID,
                .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
                .channel_mode = alc662_3ST_2ch_modes,
-               .unsol_event = alc663_mode8_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc663_mode8_setup,
-               .init_hook = alc663_mode8_inithook,
+               .init_hook = alc_inithook,
        },
        [ALC272_DELL] = {
                .mixers = { alc663_m51va_mixer },
                .cap_mixer = alc272_auto_capture_mixer,
-               .init_verbs = { alc662_init_verbs, alc272_dell_init_verbs },
+               .init_verbs = { alc662_init_verbs,
+                               alc662_eapd_init_verbs,
+                               alc272_dell_init_verbs },
                .num_dacs = ARRAY_SIZE(alc272_dac_nids),
-               .dac_nids = alc662_dac_nids,
+               .dac_nids = alc272_dac_nids,
                .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
                .adc_nids = alc272_adc_nids,
                .num_adc_nids = ARRAY_SIZE(alc272_adc_nids),
                .capsrc_nids = alc272_capsrc_nids,
                .channel_mode = alc662_3ST_2ch_modes,
-               .unsol_event = alc663_m51va_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc663_m51va_setup,
-               .init_hook = alc663_m51va_inithook,
+               .init_hook = alc_inithook,
        },
        [ALC272_DELL_ZM1] = {
                .mixers = { alc663_m51va_mixer },
                .cap_mixer = alc662_auto_capture_mixer,
-               .init_verbs = { alc662_init_verbs, alc272_dell_zm1_init_verbs },
+               .init_verbs = { alc662_init_verbs,
+                               alc662_eapd_init_verbs,
+                               alc272_dell_zm1_init_verbs },
                .num_dacs = ARRAY_SIZE(alc272_dac_nids),
-               .dac_nids = alc662_dac_nids,
+               .dac_nids = alc272_dac_nids,
                .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
                .adc_nids = alc662_adc_nids,
                .num_adc_nids = 1,
                .capsrc_nids = alc662_capsrc_nids,
                .channel_mode = alc662_3ST_2ch_modes,
-               .unsol_event = alc663_m51va_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc663_m51va_setup,
-               .init_hook = alc663_m51va_inithook,
+               .init_hook = alc_inithook,
        },
        [ALC272_SAMSUNG_NC10] = {
                .mixers = { alc272_nc10_mixer },
                .init_verbs = { alc662_init_verbs,
+                               alc662_eapd_init_verbs,
                                alc663_21jd_amic_init_verbs },
                .num_dacs = ARRAY_SIZE(alc272_dac_nids),
                .dac_nids = alc272_dac_nids,
                .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
                .channel_mode = alc662_3ST_2ch_modes,
                /*.input_mux = &alc272_nc10_capture_source,*/
-               .unsol_event = alc663_mode4_unsol_event,
+               .unsol_event = alc_sku_unsol_event,
                .setup = alc663_mode4_setup,
-               .init_hook = alc663_mode4_inithook,
+               .init_hook = alc_inithook,
        },
 };
 
@@ -19123,45 +18835,79 @@ static struct alc_config_preset alc662_presets[] = {
  */
 
 /* convert from MIX nid to DAC */
-static inline hda_nid_t alc662_mix_to_dac(hda_nid_t nid)
-{
-       if (nid == 0x0f)
-               return 0x02;
-       else if (nid >= 0x0c && nid <= 0x0e)
-               return nid - 0x0c + 0x02;
-       else if (nid == 0x26) /* ALC887-VD has this DAC too */
-               return 0x25;
-       else
-               return 0;
+static hda_nid_t alc_auto_mix_to_dac(struct hda_codec *codec, hda_nid_t nid)
+{
+       hda_nid_t list[5];
+       int i, num;
+
+       num = snd_hda_get_connections(codec, nid, list, ARRAY_SIZE(list));
+       for (i = 0; i < num; i++) {
+               if (get_wcaps_type(get_wcaps(codec, list[i])) == AC_WID_AUD_OUT)
+                       return list[i];
+       }
+       return 0;
+}
+
+/* go down to the selector widget before the mixer */
+static hda_nid_t alc_go_down_to_selector(struct hda_codec *codec, hda_nid_t pin)
+{
+       hda_nid_t srcs[5];
+       int num = snd_hda_get_connections(codec, pin, srcs,
+                                         ARRAY_SIZE(srcs));
+       if (num != 1 ||
+           get_wcaps_type(get_wcaps(codec, srcs[0])) != AC_WID_AUD_SEL)
+               return pin;
+       return srcs[0];
 }
 
 /* get MIX nid connected to the given pin targeted to DAC */
-static hda_nid_t alc662_dac_to_mix(struct hda_codec *codec, hda_nid_t pin,
+static hda_nid_t alc_auto_dac_to_mix(struct hda_codec *codec, hda_nid_t pin,
                                   hda_nid_t dac)
 {
        hda_nid_t mix[5];
        int i, num;
 
+       pin = alc_go_down_to_selector(codec, pin);
        num = snd_hda_get_connections(codec, pin, mix, ARRAY_SIZE(mix));
        for (i = 0; i < num; i++) {
-               if (alc662_mix_to_dac(mix[i]) == dac)
+               if (alc_auto_mix_to_dac(codec, mix[i]) == dac)
                        return mix[i];
        }
        return 0;
 }
 
+/* select the connection from pin to DAC if needed */
+static int alc_auto_select_dac(struct hda_codec *codec, hda_nid_t pin,
+                              hda_nid_t dac)
+{
+       hda_nid_t mix[5];
+       int i, num;
+
+       pin = alc_go_down_to_selector(codec, pin);
+       num = snd_hda_get_connections(codec, pin, mix, ARRAY_SIZE(mix));
+       if (num < 2)
+               return 0;
+       for (i = 0; i < num; i++) {
+               if (alc_auto_mix_to_dac(codec, mix[i]) == dac) {
+                       snd_hda_codec_update_cache(codec, pin, 0,
+                                                  AC_VERB_SET_CONNECT_SEL, i);
+                       return 0;
+               }
+       }
+       return 0;
+}
+
 /* look for an empty DAC slot */
-static hda_nid_t alc662_look_for_dac(struct hda_codec *codec, hda_nid_t pin)
+static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin)
 {
        struct alc_spec *spec = codec->spec;
        hda_nid_t srcs[5];
        int i, j, num;
 
+       pin = alc_go_down_to_selector(codec, pin);
        num = snd_hda_get_connections(codec, pin, srcs, ARRAY_SIZE(srcs));
-       if (num < 0)
-               return 0;
        for (i = 0; i < num; i++) {
-               hda_nid_t nid = alc662_mix_to_dac(srcs[i]);
+               hda_nid_t nid = alc_auto_mix_to_dac(codec, srcs[i]);
                if (!nid)
                        continue;
                for (j = 0; j < spec->multiout.num_dacs; j++)
@@ -19183,7 +18929,7 @@ static int alc662_auto_fill_dac_nids(struct hda_codec *codec,
 
        spec->multiout.dac_nids = spec->private_dac_nids;
        for (i = 0; i < cfg->line_outs; i++) {
-               dac = alc662_look_for_dac(codec, cfg->line_out_pins[i]);
+               dac = alc_auto_look_for_dac(codec, cfg->line_out_pins[i]);
                if (!dac)
                        continue;
                spec->multiout.dac_nids[spec->multiout.num_dacs++] = dac;
@@ -19222,15 +18968,23 @@ static int alc662_auto_create_multi_out_ctls(struct hda_codec *codec,
        static const char * const chname[4] = {
                "Front", "Surround", NULL /*CLFE*/, "Side"
        };
-       const char *pfx = alc_get_line_out_pfx(cfg, true);
-       hda_nid_t nid, mix;
-       int i, err;
+       const char *pfx = alc_get_line_out_pfx(spec, true);
+       hda_nid_t nid, mix, pin;
+       int i, err, noutputs;
 
-       for (i = 0; i < cfg->line_outs; i++) {
+       noutputs = cfg->line_outs;
+       if (spec->multi_ios > 0)
+               noutputs += spec->multi_ios;
+
+       for (i = 0; i < noutputs; i++) {
                nid = spec->multiout.dac_nids[i];
                if (!nid)
                        continue;
-               mix = alc662_dac_to_mix(codec, cfg->line_out_pins[i], nid);
+               if (i >= cfg->line_outs)
+                       pin = spec->multi_io[i - 1].pin;
+               else
+                       pin = cfg->line_out_pins[i];
+               mix = alc_auto_dac_to_mix(codec, pin, nid);
                if (!mix)
                        continue;
                if (!pfx && i == 2) {
@@ -19276,7 +19030,7 @@ static int alc662_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
 
        if (!pin)
                return 0;
-       nid = alc662_look_for_dac(codec, pin);
+       nid = alc_auto_look_for_dac(codec, pin);
        if (!nid) {
                /* the corresponding DAC is already occupied */
                if (!(get_wcaps(codec, pin) & AC_WCAP_OUT_AMP))
@@ -19286,7 +19040,7 @@ static int alc662_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
                                   HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
        }
 
-       mix = alc662_dac_to_mix(codec, pin, nid);
+       mix = alc_auto_dac_to_mix(codec, pin, nid);
        if (!mix)
                return 0;
        err = alc662_add_vol_ctl(spec, pfx, nid, 3);
@@ -19310,14 +19064,21 @@ static void alc662_auto_set_output_and_unmute(struct hda_codec *codec,
        hda_nid_t srcs[HDA_MAX_CONNECTIONS];
 
        alc_set_pin_output(codec, nid, pin_type);
-       /* need the manual connection? */
        num = snd_hda_get_connections(codec, nid, srcs, ARRAY_SIZE(srcs));
-       if (num <= 1)
-               return;
        for (i = 0; i < num; i++) {
-               if (alc662_mix_to_dac(srcs[i]) != dac)
+               if (alc_auto_mix_to_dac(codec, srcs[i]) != dac)
                        continue;
-               snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, i);
+               /* need the manual connection? */
+               if (num > 1)
+                       snd_hda_codec_write(codec, nid, 0,
+                                           AC_VERB_SET_CONNECT_SEL, i);
+               /* unmute mixer widget inputs */
+               snd_hda_codec_write(codec, srcs[i], 0,
+                                   AC_VERB_SET_AMP_GAIN_MUTE,
+                                   AMP_IN_UNMUTE(0));
+               snd_hda_codec_write(codec, srcs[i], 0,
+                                   AC_VERB_SET_AMP_GAIN_MUTE,
+                                   AMP_IN_UNMUTE(1));
                return;
        }
 }
@@ -19374,6 +19135,159 @@ static void alc662_auto_init_analog_input(struct hda_codec *codec)
 
 #define alc662_auto_init_input_src     alc882_auto_init_input_src
 
+/*
+ * multi-io helper
+ */
+static int alc_auto_fill_multi_ios(struct hda_codec *codec,
+                                  unsigned int location)
+{
+       struct alc_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       int type, i, num_pins = 0;
+
+       for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) {
+               for (i = 0; i < cfg->num_inputs; i++) {
+                       hda_nid_t nid = cfg->inputs[i].pin;
+                       hda_nid_t dac;
+                       unsigned int defcfg, caps;
+                       if (cfg->inputs[i].type != type)
+                               continue;
+                       defcfg = snd_hda_codec_get_pincfg(codec, nid);
+                       if (get_defcfg_connect(defcfg) != AC_JACK_PORT_COMPLEX)
+                               continue;
+                       if (location && get_defcfg_location(defcfg) != location)
+                               continue;
+                       caps = snd_hda_query_pin_caps(codec, nid);
+                       if (!(caps & AC_PINCAP_OUT))
+                               continue;
+                       dac = alc_auto_look_for_dac(codec, nid);
+                       if (!dac)
+                               continue;
+                       spec->multi_io[num_pins].pin = nid;
+                       spec->multi_io[num_pins].dac = dac;
+                       num_pins++;
+                       spec->multiout.dac_nids[spec->multiout.num_dacs++] = dac;
+               }
+       }
+       spec->multiout.num_dacs = 1;
+       if (num_pins < 2)
+               return 0;
+       return num_pins;
+}
+
+static int alc_auto_ch_mode_info(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_info *uinfo)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct alc_spec *spec = codec->spec;
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = spec->multi_ios + 1;
+       if (uinfo->value.enumerated.item > spec->multi_ios)
+               uinfo->value.enumerated.item = spec->multi_ios;
+       sprintf(uinfo->value.enumerated.name, "%dch",
+               (uinfo->value.enumerated.item + 1) * 2);
+       return 0;
+}
+
+static int alc_auto_ch_mode_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct alc_spec *spec = codec->spec;
+       ucontrol->value.enumerated.item[0] = (spec->ext_channel_count - 1) / 2;
+       return 0;
+}
+
+static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output)
+{
+       struct alc_spec *spec = codec->spec;
+       hda_nid_t nid = spec->multi_io[idx].pin;
+
+       if (!spec->multi_io[idx].ctl_in)
+               spec->multi_io[idx].ctl_in =
+                       snd_hda_codec_read(codec, nid, 0,
+                                          AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+       if (output) {
+               snd_hda_codec_update_cache(codec, nid, 0,
+                                          AC_VERB_SET_PIN_WIDGET_CONTROL,
+                                          PIN_OUT);
+               if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
+                       snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
+                                                HDA_AMP_MUTE, 0);
+               alc_auto_select_dac(codec, nid, spec->multi_io[idx].dac);
+       } else {
+               if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
+                       snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
+                                                HDA_AMP_MUTE, HDA_AMP_MUTE);
+               snd_hda_codec_update_cache(codec, nid, 0,
+                                          AC_VERB_SET_PIN_WIDGET_CONTROL,
+                                          spec->multi_io[idx].ctl_in);
+       }
+       return 0;
+}
+
+static int alc_auto_ch_mode_put(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct alc_spec *spec = codec->spec;
+       int i, ch;
+
+       ch = ucontrol->value.enumerated.item[0];
+       if (ch < 0 || ch > spec->multi_ios)
+               return -EINVAL;
+       if (ch == (spec->ext_channel_count - 1) / 2)
+               return 0;
+       spec->ext_channel_count = (ch + 1) * 2;
+       for (i = 0; i < spec->multi_ios; i++)
+               alc_set_multi_io(codec, i, i < ch);
+       spec->multiout.max_channels = spec->ext_channel_count;
+       return 1;
+}
+
+static struct snd_kcontrol_new alc_auto_channel_mode_enum = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "Channel Mode",
+       .info = alc_auto_ch_mode_info,
+       .get = alc_auto_ch_mode_get,
+       .put = alc_auto_ch_mode_put,
+};
+
+static int alc_auto_add_multi_channel_mode(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       unsigned int location, defcfg;
+       int num_pins;
+
+       if (cfg->line_outs != 1 ||
+           cfg->line_out_type != AUTO_PIN_LINE_OUT)
+               return 0;
+
+       defcfg = snd_hda_codec_get_pincfg(codec, cfg->line_out_pins[0]);
+       location = get_defcfg_location(defcfg);
+
+       num_pins = alc_auto_fill_multi_ios(codec, location);
+       if (num_pins > 0) {
+               struct snd_kcontrol_new *knew;
+
+               knew = alc_kcontrol_new(spec);
+               if (!knew)
+                       return -ENOMEM;
+               *knew = alc_auto_channel_mode_enum;
+               knew->name = kstrdup("Channel Mode", GFP_KERNEL);
+               if (!knew->name)
+                       return -ENOMEM;
+
+               spec->multi_ios = num_pins;
+               spec->ext_channel_count = 2;
+               spec->multiout.num_dacs = num_pins + 1;
+       }
+       return 0;
+}
+
 static int alc662_parse_auto_config(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
@@ -19388,6 +19302,9 @@ static int alc662_parse_auto_config(struct hda_codec *codec)
                return 0; /* can't find valid BIOS pin config */
 
        err = alc662_auto_fill_dac_nids(codec, &spec->autocfg);
+       if (err < 0)
+               return err;
+       err = alc_auto_add_multi_channel_mode(codec);
        if (err < 0)
                return err;
        err = alc662_auto_create_multi_out_ctls(codec, &spec->autocfg);
@@ -19420,14 +19337,6 @@ static int alc662_parse_auto_config(struct hda_codec *codec)
        spec->num_mux_defs = 1;
        spec->input_mux = &spec->private_imux[0];
 
-       add_verb(spec, alc662_init_verbs);
-       if (codec->vendor_id == 0x10ec0272 || codec->vendor_id == 0x10ec0663 ||
-           codec->vendor_id == 0x10ec0665 || codec->vendor_id == 0x10ec0670)
-               add_verb(spec, alc663_init_verbs);
-
-       if (codec->vendor_id == 0x10ec0272)
-               add_verb(spec, alc272_init_verbs);
-
        err = alc_auto_add_mic_boost(codec);
        if (err < 0)
                return err;
@@ -19626,6 +19535,7 @@ static int patch_alc662(struct hda_codec *codec)
        codec->patch_ops = alc_patch_ops;
        if (board_config == ALC662_AUTO)
                spec->init_hook = alc662_auto_init;
+       spec->shutup = alc_eapd_shutup;
 
        alc_init_jacks(codec);
 
@@ -19824,20 +19734,22 @@ static void alc680_base_setup(struct hda_codec *codec)
        spec->autocfg.inputs[0].type = AUTO_PIN_MIC;
        spec->autocfg.inputs[1].pin = 0x19;
        spec->autocfg.inputs[1].type = AUTO_PIN_LINE_IN;
+       spec->automute = 1;
+       spec->automute_mode = ALC_AUTOMUTE_AMP;
 }
 
 static void alc680_unsol_event(struct hda_codec *codec,
                                           unsigned int res)
 {
        if ((res >> 26) == ALC880_HP_EVENT)
-               alc_automute_amp(codec);
+               alc_hp_automute(codec);
        if ((res >> 26) == ALC880_MIC_EVENT)
                alc680_rec_autoswitch(codec);
 }
 
 static void alc680_inithook(struct hda_codec *codec)
 {
-       alc_automute_amp(codec);
+       alc_hp_automute(codec);
        alc680_rec_autoswitch(codec);
 }