ALSA: hda - Add alc_init_jacks() call to other codecs
[pandora-kernel.git] / sound / pci / hda / patch_realtek.c
index 596ea2f..2363f18 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/slab.h>
 #include <linux/pci.h>
 #include <sound/core.h>
+#include <sound/jack.h>
 #include "hda_codec.h"
 #include "hda_local.h"
 #include "hda_beep.h"
@@ -137,6 +138,7 @@ enum {
        ALC269VB_DMIC,
        ALC269_FUJITSU,
        ALC269_LIFEBOOK,
+       ALC271_ACER,
        ALC269_AUTO,
        ALC269_MODEL_LAST /* last tag */
 };
@@ -256,6 +258,13 @@ enum {
        ALC882_MODEL_LAST,
 };
 
+/* ALC680 models */
+enum {
+       ALC680_BASE,
+       ALC680_AUTO,
+       ALC680_MODEL_LAST,
+};
+
 /* for GPIO Poll */
 #define GPIO_MASK      0x03
 
@@ -274,6 +283,12 @@ struct alc_mic_route {
        unsigned char amix_idx;
 };
 
+struct alc_jack {
+       hda_nid_t nid;
+       int type;
+       struct snd_jack *jack;
+};
+
 #define MUX_IDX_UNDEF  ((unsigned char)-1)
 
 struct alc_customize_define {
@@ -286,6 +301,7 @@ struct alc_customize_define {
        unsigned int  platform_type:1;
        unsigned int  swap:1;
        unsigned int  override:1;
+       unsigned int  fixup:1; /* Means that this sku is set by driver, not read from hw */
 };
 
 struct alc_spec {
@@ -326,6 +342,12 @@ struct alc_spec {
        hda_nid_t *capsrc_nids;
        hda_nid_t dig_in_nid;           /* digital-in NID; optional */
 
+       /* capture setup for dynamic dual-adc switch */
+       unsigned int cur_adc_idx;
+       hda_nid_t cur_adc;
+       unsigned int cur_adc_stream_tag;
+       unsigned int cur_adc_format;
+
        /* capture source */
        unsigned int num_mux_defs;
        const struct hda_input_mux *input_mux;
@@ -343,6 +365,9 @@ struct alc_spec {
        /* PCM information */
        struct hda_pcm pcm_rec[3];      /* used in alc_build_pcms() */
 
+       /* jack detection */
+       struct snd_array jacks;
+
        /* dynamic controls, init_verbs and input_mux */
        struct auto_pin_cfg autocfg;
        struct alc_customize_define cdefine;
@@ -367,7 +392,9 @@ struct alc_spec {
 
        /* other flags */
        unsigned int no_analog :1; /* digital I/O only */
+       unsigned int dual_adc_switch:1; /* switch ADCs (for ALC275) */
        int init_amp;
+       int codec_variant;      /* flag for other variants */
 
        /* for virtual master */
        hda_nid_t vmaster_nid;
@@ -831,11 +858,15 @@ static void alc_set_input_pin(struct hda_codec *codec, hda_nid_t nid,
 {
        unsigned int val = PIN_IN;
 
-       if (auto_pin_type <= AUTO_PIN_FRONT_MIC) {
+       if (auto_pin_type == AUTO_PIN_MIC) {
                unsigned int pincap;
+               unsigned int oldval;
+               oldval = snd_hda_codec_read(codec, nid, 0,
+                                           AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
                pincap = snd_hda_query_pin_caps(codec, nid);
                pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
-               if (pincap & AC_PINCAP_VREF_80)
+               /* if the default pin setup is vref50, we give it priority */
+               if ((pincap & AC_PINCAP_VREF_80) && oldval != PIN_VREF50)
                        val = PIN_VREF80;
                else if (pincap & AC_PINCAP_VREF_50)
                        val = PIN_VREF50;
@@ -847,6 +878,28 @@ static void alc_set_input_pin(struct hda_codec *codec, hda_nid_t nid,
        snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, val);
 }
 
+static void alc_fixup_autocfg_pin_nums(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+
+       if (!cfg->line_outs) {
+               while (cfg->line_outs < AUTO_CFG_MAX_OUTS &&
+                      cfg->line_out_pins[cfg->line_outs])
+                       cfg->line_outs++;
+       }
+       if (!cfg->speaker_outs) {
+               while (cfg->speaker_outs < AUTO_CFG_MAX_OUTS &&
+                      cfg->speaker_pins[cfg->speaker_outs])
+                       cfg->speaker_outs++;
+       }
+       if (!cfg->hp_outs) {
+               while (cfg->hp_outs < AUTO_CFG_MAX_OUTS &&
+                      cfg->hp_pins[cfg->hp_outs])
+                       cfg->hp_outs++;
+       }
+}
+
 /*
  */
 static void add_mixer(struct alc_spec *spec, struct snd_kcontrol_new *mix)
@@ -915,6 +968,8 @@ static void setup_preset(struct hda_codec *codec,
 
        if (preset->setup)
                preset->setup(codec);
+
+       alc_fixup_autocfg_pin_nums(codec);
 }
 
 /* Enable GPIO mask and set output */
@@ -971,25 +1026,136 @@ static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid,
        alc_fix_pll(codec);
 }
 
-static void alc_automute_pin(struct hda_codec *codec)
+#ifdef CONFIG_SND_HDA_INPUT_JACK
+static void alc_free_jack_priv(struct snd_jack *jack)
+{
+       struct alc_jack *jacks = jack->private_data;
+       jacks->nid = 0;
+       jacks->jack = NULL;
+}
+
+static int alc_add_jack(struct hda_codec *codec,
+               hda_nid_t nid, int type)
+{
+       struct alc_spec *spec;
+       struct alc_jack *jack;
+       const char *name;
+       int err;
+
+       spec = codec->spec;
+       snd_array_init(&spec->jacks, sizeof(*jack), 32);
+       jack = snd_array_new(&spec->jacks);
+       if (!jack)
+               return -ENOMEM;
+
+       jack->nid = nid;
+       jack->type = type;
+       name = (type == SND_JACK_HEADPHONE) ? "Headphone" : "Mic" ;
+
+       err = snd_jack_new(codec->bus->card, name, type, &jack->jack);
+       if (err < 0)
+               return err;
+       jack->jack->private_data = jack;
+       jack->jack->private_free = alc_free_jack_priv;
+       return 0;
+}
+
+static void alc_report_jack(struct hda_codec *codec, hda_nid_t nid)
 {
        struct alc_spec *spec = codec->spec;
-       unsigned int nid = spec->autocfg.hp_pins[0];
+       struct alc_jack *jacks = spec->jacks.list;
+
+       if (jacks) {
+               int i;
+               for (i = 0; i < spec->jacks.used; i++) {
+                       if (jacks->nid == nid) {
+                               unsigned int present;
+                               present = snd_hda_jack_detect(codec, nid);
+
+                               present = (present) ? jacks->type : 0;
+
+                               snd_jack_report(jacks->jack, present);
+                       }
+                       jacks++;
+               }
+       }
+}
+
+static int alc_init_jacks(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       int err;
+       unsigned int hp_nid = spec->autocfg.hp_pins[0];
+       unsigned int mic_nid = spec->ext_mic.pin;
+
+       if (hp_nid) {
+               err = alc_add_jack(codec, hp_nid, SND_JACK_HEADPHONE);
+               if (err < 0)
+                       return err;
+               alc_report_jack(codec, hp_nid);
+       }
+
+       if (mic_nid) {
+               err = alc_add_jack(codec, mic_nid, SND_JACK_MICROPHONE);
+               if (err < 0)
+                       return err;
+               alc_report_jack(codec, mic_nid);
+       }
+
+       return 0;
+}
+#else
+static inline void alc_report_jack(struct hda_codec *codec, hda_nid_t nid)
+{
+}
+
+static inline int alc_init_jacks(struct hda_codec *codec)
+{
+       return 0;
+}
+#endif
+
+static void alc_automute_speaker(struct hda_codec *codec, int pinctl)
+{
+       struct alc_spec *spec = codec->spec;
+       unsigned int mute;
+       hda_nid_t nid;
        int i;
 
-       if (!nid)
-               return;
-       spec->jack_present = snd_hda_jack_detect(codec, nid);
+       spec->jack_present = 0;
+       for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) {
+               nid = spec->autocfg.hp_pins[i];
+               if (!nid)
+                       break;
+               if (snd_hda_jack_detect(codec, nid)) {
+                       spec->jack_present = 1;
+                       break;
+               }
+               alc_report_jack(codec, spec->autocfg.hp_pins[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];
                if (!nid)
                        break;
-               snd_hda_codec_write(codec, nid, 0,
+               if (pinctl) {
+                       snd_hda_codec_write(codec, nid, 0,
                                    AC_VERB_SET_PIN_WIDGET_CONTROL,
                                    spec->jack_present ? 0 : PIN_OUT);
+               } else {
+                       snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
+                                        HDA_AMP_MUTE, mute);
+               }
        }
 }
 
+static void alc_automute_pin(struct hda_codec *codec)
+{
+       alc_automute_speaker(codec, 1);
+}
+
 static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
                                hda_nid_t nid)
 {
@@ -1003,6 +1169,29 @@ static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
        return -1;
 }
 
+/* switch the current ADC according to the jack state */
+static void alc_dual_mic_adc_auto_switch(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       unsigned int present;
+       hda_nid_t new_adc;
+
+       present = snd_hda_jack_detect(codec, spec->ext_mic.pin);
+       if (present)
+               spec->cur_adc_idx = 1;
+       else
+               spec->cur_adc_idx = 0;
+       new_adc = spec->adc_nids[spec->cur_adc_idx];
+       if (spec->cur_adc && spec->cur_adc != new_adc) {
+               /* stream is running, let's swap the current ADC */
+               __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
+               spec->cur_adc = new_adc;
+               snd_hda_codec_setup_stream(codec, new_adc,
+                                          spec->cur_adc_stream_tag, 0,
+                                          spec->cur_adc_format);
+       }
+}
+
 static void alc_mic_automute(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
@@ -1017,6 +1206,11 @@ static void alc_mic_automute(struct hda_codec *codec)
        if (snd_BUG_ON(!spec->adc_nids))
                return;
 
+       if (spec->dual_adc_switch) {
+               alc_dual_mic_adc_auto_switch(codec);
+               return;
+       }
+
        cap_nid = spec->capsrc_nids ? spec->capsrc_nids[0] : spec->adc_nids[0];
 
        present = snd_hda_jack_detect(codec, spec->ext_mic.pin);
@@ -1043,6 +1237,7 @@ static void alc_mic_automute(struct hda_codec *codec)
                                          AC_VERB_SET_CONNECT_SEL,
                                          alive->mux_idx);
        }
+       alc_report_jack(codec, spec->ext_mic.pin);
 
        /* FIXME: analog mixer */
 }
@@ -1189,24 +1384,35 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type)
 static void alc_init_auto_hp(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       int i;
 
-       if (!spec->autocfg.hp_pins[0])
-               return;
+       if (!cfg->hp_pins[0]) {
+               if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+                       return;
+       }
 
-       if (!spec->autocfg.speaker_pins[0]) {
-               if (spec->autocfg.line_out_pins[0] &&
-                   spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
-                       spec->autocfg.speaker_pins[0] =
-                               spec->autocfg.line_out_pins[0];
-               else
+       if (!cfg->speaker_pins[0]) {
+               if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
                        return;
+               memcpy(cfg->speaker_pins, cfg->line_out_pins,
+                      sizeof(cfg->speaker_pins));
+               cfg->speaker_outs = cfg->line_outs;
+       }
+
+       if (!cfg->hp_pins[0]) {
+               memcpy(cfg->hp_pins, cfg->line_out_pins,
+                      sizeof(cfg->hp_pins));
+               cfg->hp_outs = cfg->line_outs;
        }
 
-       snd_printdd("realtek: Enable HP auto-muting on NID 0x%x\n",
-                   spec->autocfg.hp_pins[0]);
-       snd_hda_codec_write_cache(codec, spec->autocfg.hp_pins[0], 0,
+       for (i = 0; i < cfg->hp_outs; i++) {
+               snd_printdd("realtek: Enable HP auto-muting on NID 0x%x\n",
+                           cfg->hp_pins[i]);
+               snd_hda_codec_write_cache(codec, cfg->hp_pins[i], 0,
                                  AC_VERB_SET_UNSOLICITED_ENABLE,
                                  AC_USRSP_EN | ALC880_HP_EVENT);
+       }
        spec->unsol_event = alc_sku_unsol_event;
 }
 
@@ -1218,30 +1424,28 @@ static void alc_init_auto_mic(struct hda_codec *codec)
        int i;
 
        /* there must be only two mic inputs exclusively */
-       for (i = AUTO_PIN_LINE; i < AUTO_PIN_LAST; i++)
-               if (cfg->input_pins[i])
+       for (i = 0; i < cfg->num_inputs; i++)
+               if (cfg->inputs[i].type >= AUTO_PIN_LINE_IN)
                        return;
 
        fixed = ext = 0;
-       for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++) {
-               hda_nid_t nid = cfg->input_pins[i];
+       for (i = 0; i < cfg->num_inputs; i++) {
+               hda_nid_t nid = cfg->inputs[i].pin;
                unsigned int defcfg;
-               if (!nid)
-                       return;
                defcfg = snd_hda_codec_get_pincfg(codec, nid);
-               switch (get_defcfg_connect(defcfg)) {
-               case AC_JACK_PORT_FIXED:
+               switch (snd_hda_get_input_pin_attr(defcfg)) {
+               case INPUT_PIN_ATTR_INT:
                        if (fixed)
                                return; /* already occupied */
                        fixed = nid;
                        break;
-               case AC_JACK_PORT_COMPLEX:
+               case INPUT_PIN_ATTR_UNUSED:
+                       return; /* invalid entry */
+               default:
                        if (ext)
                                return; /* already occupied */
                        ext = nid;
                        break;
-               default:
-                       return; /* invalid entry */
                }
        }
        if (!ext || !fixed)
@@ -1261,6 +1465,11 @@ static void alc_init_auto_mic(struct hda_codec *codec)
        spec->unsol_event = alc_sku_unsol_event;
 }
 
+/* Could be any non-zero and even value. When used as fixup, tells
+ * the driver to ignore any present sku defines.
+ */
+#define ALC_FIXUP_SKU_IGNORE (2)
+
 static int alc_auto_parse_customize_define(struct hda_codec *codec)
 {
        unsigned int ass, tmp, i;
@@ -1269,6 +1478,13 @@ static int alc_auto_parse_customize_define(struct hda_codec *codec)
 
        spec->cdefine.enable_pcbeep = 1; /* assume always enabled */
 
+       if (spec->cdefine.fixup) {
+               ass = spec->cdefine.sku_cfg;
+               if (ass == ALC_FIXUP_SKU_IGNORE)
+                       return -1;
+               goto do_sku;
+       }
+
        ass = codec->subsystem_id & 0xffff;
        if (ass != codec->bus->pci->subsystem_device && (ass & 1))
                goto do_sku;
@@ -1336,6 +1552,13 @@ static int alc_subsystem_id(struct hda_codec *codec,
        unsigned nid;
        struct alc_spec *spec = codec->spec;
 
+       if (spec->cdefine.fixup) {
+               ass = spec->cdefine.sku_cfg;
+               if (ass == ALC_FIXUP_SKU_IGNORE)
+                       return 0;
+               goto do_sku;
+       }
+
        ass = codec->subsystem_id & 0xffff;
        if ((ass != codec->bus->pci->subsystem_device) && (ass & 1))
                goto do_sku;
@@ -1455,6 +1678,7 @@ struct alc_pincfg {
 };
 
 struct alc_fixup {
+       unsigned int sku;
        const struct alc_pincfg *pins;
        const struct hda_verb *verbs;
 };
@@ -1465,12 +1689,22 @@ static void alc_pick_fixup(struct hda_codec *codec,
                           int pre_init)
 {
        const struct alc_pincfg *cfg;
+       struct alc_spec *spec;
 
        quirk = snd_pci_quirk_lookup(codec->bus->pci, quirk);
        if (!quirk)
                return;
        fix += quirk->value;
        cfg = fix->pins;
+       if (pre_init && fix->sku) {
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+               snd_printdd(KERN_INFO "hda_codec: %s: Apply sku override for %s\n",
+                           codec->chip_name, quirk->name);
+#endif
+               spec = codec->spec;
+               spec->cdefine.sku_cfg = fix->sku;
+               spec->cdefine.fixup = 1;
+       }
        if (pre_init && cfg) {
 #ifdef CONFIG_SND_DEBUG_VERBOSE
                snd_printdd(KERN_INFO "hda_codec: %s: Apply pincfg for %s\n",
@@ -1499,6 +1733,82 @@ static int alc_read_coef_idx(struct hda_codec *codec,
        return val;
 }
 
+static void alc_write_coef_idx(struct hda_codec *codec, unsigned int coef_idx,
+                                                       unsigned int coef_val)
+{
+       snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX,
+                           coef_idx);
+       snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF,
+                           coef_val);
+}
+
+/* set right pin controls for digital I/O */
+static void alc_auto_init_digital(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       int i;
+       hda_nid_t pin;
+
+       for (i = 0; i < spec->autocfg.dig_outs; i++) {
+               pin = spec->autocfg.dig_out_pins[i];
+               if (pin) {
+                       snd_hda_codec_write(codec, pin, 0,
+                                           AC_VERB_SET_PIN_WIDGET_CONTROL,
+                                           PIN_OUT);
+               }
+       }
+       pin = spec->autocfg.dig_in_pin;
+       if (pin)
+               snd_hda_codec_write(codec, pin, 0,
+                                   AC_VERB_SET_PIN_WIDGET_CONTROL,
+                                   PIN_IN);
+}
+
+/* parse digital I/Os and set up NIDs in BIOS auto-parse mode */
+static void alc_auto_parse_digital(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       int i, err;
+       hda_nid_t dig_nid;
+
+       /* support multiple SPDIFs; the secondary is set up as a slave */
+       for (i = 0; i < spec->autocfg.dig_outs; i++) {
+               err = snd_hda_get_connections(codec,
+                                             spec->autocfg.dig_out_pins[i],
+                                             &dig_nid, 1);
+               if (err < 0)
+                       continue;
+               if (!i) {
+                       spec->multiout.dig_out_nid = dig_nid;
+                       spec->dig_out_type = spec->autocfg.dig_out_type[0];
+               } else {
+                       spec->multiout.slave_dig_outs = spec->slave_dig_outs;
+                       if (i >= ARRAY_SIZE(spec->slave_dig_outs) - 1)
+                               break;
+                       spec->slave_dig_outs[i - 1] = dig_nid;
+               }
+       }
+
+       if (spec->autocfg.dig_in_pin) {
+               dig_nid = codec->start_nid;
+               for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
+                       unsigned int wcaps = get_wcaps(codec, dig_nid);
+                       if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
+                               continue;
+                       if (!(wcaps & AC_WCAP_DIGITAL))
+                               continue;
+                       if (!(wcaps & AC_WCAP_CONN_LIST))
+                               continue;
+                       err = get_connection_index(codec, dig_nid,
+                                                  spec->autocfg.dig_in_pin);
+                       if (err >= 0) {
+                               spec->dig_in_nid = dig_nid;
+                               break;
+                       }
+               }
+       }
+}
+
 /*
  * ALC888
  */
@@ -1609,31 +1919,7 @@ static struct hda_verb alc888_fujitsu_xa3530_verbs[] = {
 
 static void alc_automute_amp(struct hda_codec *codec)
 {
-       struct alc_spec *spec = codec->spec;
-       unsigned int mute;
-       hda_nid_t nid;
-       int i;
-
-       spec->jack_present = 0;
-       for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) {
-               nid = spec->autocfg.hp_pins[i];
-               if (!nid)
-                       break;
-               if (snd_hda_jack_detect(codec, nid)) {
-                       spec->jack_present = 1;
-                       break;
-               }
-       }
-
-       mute = spec->jack_present ? HDA_AMP_MUTE : 0;
-       /* Toggle internal speakers muting */
-       for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) {
-               nid = spec->autocfg.speaker_pins[i];
-               if (!nid)
-                       break;
-               snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
-                                        HDA_AMP_MUTE, mute);
-       }
+       alc_automute_speaker(codec, 0);
 }
 
 static void alc_automute_amp_unsol_event(struct hda_codec *codec,
@@ -3488,10 +3774,7 @@ static int alc_init(struct hda_codec *codec)
        if (spec->init_hook)
                spec->init_hook(codec);
 
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-       if (codec->patch_ops.check_power_status)
-               codec->patch_ops.check_power_status(codec, 0x01);
-#endif
+       hda_call_check_power_status(codec, 0x01);
        return 0;
 }
 
@@ -3607,6 +3890,41 @@ static int alc880_alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
        return 0;
 }
 
+/* analog capture with dynamic dual-adc changes */
+static int dualmic_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+                                      struct hda_codec *codec,
+                                      unsigned int stream_tag,
+                                      unsigned int format,
+                                      struct snd_pcm_substream *substream)
+{
+       struct alc_spec *spec = codec->spec;
+       spec->cur_adc = spec->adc_nids[spec->cur_adc_idx];
+       spec->cur_adc_stream_tag = stream_tag;
+       spec->cur_adc_format = format;
+       snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
+       return 0;
+}
+
+static int dualmic_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                                      struct hda_codec *codec,
+                                      struct snd_pcm_substream *substream)
+{
+       struct alc_spec *spec = codec->spec;
+       snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
+       spec->cur_adc = 0;
+       return 0;
+}
+
+static struct hda_pcm_stream dualmic_pcm_analog_capture = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+       .nid = 0, /* fill later */
+       .ops = {
+               .prepare = dualmic_capture_pcm_prepare,
+               .cleanup = dualmic_capture_pcm_cleanup
+       },
+};
 
 /*
  */
@@ -3852,10 +4170,7 @@ static int alc_resume(struct hda_codec *codec)
        codec->patch_ops.init(codec);
        snd_hda_codec_resume_amp(codec);
        snd_hda_codec_resume_cache(codec);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-       if (codec->patch_ops.check_power_status)
-               codec->patch_ops.check_power_status(codec, 0x01);
-#endif
+       hda_call_check_power_status(codec, 0x01);
        return 0;
 }
 #endif
@@ -4580,7 +4895,7 @@ static struct snd_kcontrol_new alc880_control_templates[] = {
 
 /* add dynamic controls */
 static int add_control(struct alc_spec *spec, int type, const char *name,
-                      unsigned long val)
+                      int cidx, unsigned long val)
 {
        struct snd_kcontrol_new *knew;
 
@@ -4592,6 +4907,7 @@ static int add_control(struct alc_spec *spec, int type, const char *name,
        knew->name = kstrdup(name, GFP_KERNEL);
        if (!knew->name)
                return -ENOMEM;
+       knew->index = cidx;
        if (get_amp_nid_(val))
                knew->subdevice = HDA_SUBDEV_AMP_FLAG;
        knew->private_value = val;
@@ -4600,17 +4916,21 @@ static int add_control(struct alc_spec *spec, int type, const char *name,
 
 static int add_control_with_pfx(struct alc_spec *spec, int type,
                                const char *pfx, const char *dir,
-                               const char *sfx, unsigned long val)
+                               const char *sfx, int cidx, unsigned long val)
 {
        char name[32];
        snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx);
-       return add_control(spec, type, name, val);
+       return add_control(spec, type, name, cidx, val);
 }
 
-#define add_pb_vol_ctrl(spec, type, pfx, val) \
-       add_control_with_pfx(spec, type, pfx, "Playback", "Volume", val)
-#define add_pb_sw_ctrl(spec, type, pfx, val) \
-       add_control_with_pfx(spec, type, pfx, "Playback", "Switch", val)
+#define add_pb_vol_ctrl(spec, type, pfx, val)                  \
+       add_control_with_pfx(spec, type, pfx, "Playback", "Volume", 0, val)
+#define add_pb_sw_ctrl(spec, type, pfx, val)                   \
+       add_control_with_pfx(spec, type, pfx, "Playback", "Switch", 0, val)
+#define __add_pb_vol_ctrl(spec, type, pfx, cidx, val)                  \
+       add_control_with_pfx(spec, type, pfx, "Playback", "Volume", cidx, val)
+#define __add_pb_sw_ctrl(spec, type, pfx, cidx, val)                   \
+       add_control_with_pfx(spec, type, pfx, "Playback", "Switch", cidx, val)
 
 #define alc880_is_fixed_pin(nid)       ((nid) >= 0x14 && (nid) <= 0x17)
 #define alc880_fixed_pin_idx(nid)      ((nid) - 0x14)
@@ -4763,16 +5083,16 @@ static int alc880_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin,
 
 /* create input playback/capture controls for the given pin */
 static int new_analog_input(struct alc_spec *spec, hda_nid_t pin,
-                           const char *ctlname,
+                           const char *ctlname, int ctlidx,
                            int idx, hda_nid_t mix_nid)
 {
        int err;
 
-       err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname,
+       err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, ctlidx,
                          HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
        if (err < 0)
                return err;
-       err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname,
+       err = __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, ctlidx,
                          HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
        if (err < 0)
                return err;
@@ -4793,20 +5113,27 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec,
 {
        struct alc_spec *spec = codec->spec;
        struct hda_input_mux *imux = &spec->private_imux[0];
-       int i, err, idx;
+       int i, err, idx, type, type_idx = 0;
 
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
+       for (i = 0; i < cfg->num_inputs; i++) {
                hda_nid_t pin;
+               const char *label;
 
-               pin = cfg->input_pins[i];
+               pin = cfg->inputs[i].pin;
                if (!alc_is_input_pin(codec, pin))
                        continue;
 
+               type = cfg->inputs[i].type;
+               if (i > 0 && type == cfg->inputs[i - 1].type)
+                       type_idx++;
+               else
+                       type_idx = 0;
+               label = hda_get_autocfg_input_label(codec, cfg, i);
                if (mixer) {
                        idx = get_connection_index(codec, mixer, pin);
                        if (idx >= 0) {
                                err = new_analog_input(spec, pin,
-                                                      auto_pin_cfg_labels[i],
+                                                      label, type_idx,
                                                       idx, mixer);
                                if (err < 0)
                                        return err;
@@ -4818,12 +5145,8 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec,
                idx = get_connection_index(codec, cap1, pin);
                if (idx < 0 && cap2)
                        idx = get_connection_index(codec, cap2, pin);
-               if (idx >= 0) {
-                       imux->items[imux->num_items].label =
-                               auto_pin_cfg_labels[i];
-                       imux->items[imux->num_items].index = idx;
-                       imux->num_items++;
-               }
+               if (idx >= 0)
+                       snd_hda_add_imux_item(imux, label, idx, NULL);
        }
        return 0;
 }
@@ -4895,12 +5218,13 @@ static void alc880_auto_init_extra_out(struct hda_codec *codec)
 static void alc880_auto_init_analog_input(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
        int i;
 
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               hda_nid_t nid = spec->autocfg.input_pins[i];
+       for (i = 0; i < cfg->num_inputs; i++) {
+               hda_nid_t nid = cfg->inputs[i].pin;
                if (alc_is_input_pin(codec, nid)) {
-                       alc_set_input_pin(codec, nid, i);
+                       alc_set_input_pin(codec, nid, cfg->inputs[i].type);
                        if (nid != ALC880_PIN_CD_NID &&
                            (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
                                snd_hda_codec_write(codec, nid, 0,
@@ -4936,7 +5260,7 @@ static void alc880_auto_init_input_src(struct hda_codec *codec)
 static int alc880_parse_auto_config(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       int i, err;
+       int err;
        static hda_nid_t alc880_ignore[] = { 0x1d, 0 };
 
        err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
@@ -4967,25 +5291,7 @@ static int alc880_parse_auto_config(struct hda_codec *codec)
 
        spec->multiout.max_channels = spec->multiout.num_dacs * 2;
 
-       /* check multiple SPDIF-out (for recent codecs) */
-       for (i = 0; i < spec->autocfg.dig_outs; i++) {
-               hda_nid_t dig_nid;
-               err = snd_hda_get_connections(codec,
-                                             spec->autocfg.dig_out_pins[i],
-                                             &dig_nid, 1);
-               if (err < 0)
-                       continue;
-               if (!i)
-                       spec->multiout.dig_out_nid = dig_nid;
-               else {
-                       spec->multiout.slave_dig_outs = spec->slave_dig_outs;
-                       if (i >= ARRAY_SIZE(spec->slave_dig_outs) - 1)
-                               break;
-                       spec->slave_dig_outs[i - 1] = dig_nid;
-               }
-       }
-       if (spec->autocfg.dig_in_pin)
-               spec->dig_in_nid = ALC880_DIGIN_NID;
+       alc_auto_parse_digital(codec);
 
        if (spec->kctls.list)
                add_mixer(spec, spec->kctls.list);
@@ -5008,6 +5314,7 @@ static void alc880_auto_init(struct hda_codec *codec)
        alc880_auto_init_extra_out(codec);
        alc880_auto_init_analog_input(codec);
        alc880_auto_init_input_src(codec);
+       alc_auto_init_digital(codec);
        if (spec->unsol_event)
                alc_inithook(codec);
 }
@@ -5045,24 +5352,25 @@ static void fixup_automic_adc(struct hda_codec *codec)
        spec->auto_mic = 0; /* disable auto-mic to be sure */
 }
 
-/* choose the ADC/MUX containing the input pin and initialize the setup */
-static void fixup_single_adc(struct hda_codec *codec)
+/* select or unmute the given capsrc route */
+static void select_or_unmute_capsrc(struct hda_codec *codec, hda_nid_t cap,
+                                   int idx)
 {
-       struct alc_spec *spec = codec->spec;
-       hda_nid_t pin = 0;
-       int i;
-
-       /* search for the input pin; there must be only one */
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               if (spec->autocfg.input_pins[i]) {
-                       pin = spec->autocfg.input_pins[i];
-                       break;
-               }
+       if (get_wcaps_type(get_wcaps(codec, cap)) == AC_WID_AUD_MIX) {
+               snd_hda_codec_amp_stereo(codec, cap, HDA_INPUT, idx,
+                                        HDA_AMP_MUTE, 0);
+       } else {
+               snd_hda_codec_write_cache(codec, cap, 0,
+                                         AC_VERB_SET_CONNECT_SEL, idx);
        }
-       if (!pin)
-               return;
+}
+
+/* set the default connection to that pin */
+static int init_capsrc_for_pin(struct hda_codec *codec, hda_nid_t pin)
+{
+       struct alc_spec *spec = codec->spec;
+       int i;
 
-       /* set the default connection to that pin */
        for (i = 0; i < spec->num_adc_nids; i++) {
                hda_nid_t cap = spec->capsrc_nids ?
                        spec->capsrc_nids[i] : spec->adc_nids[i];
@@ -5071,23 +5379,40 @@ static void fixup_single_adc(struct hda_codec *codec)
                idx = get_connection_index(codec, cap, pin);
                if (idx < 0)
                        continue;
+               select_or_unmute_capsrc(codec, cap, idx);
+               return i; /* return the found index */
+       }
+       return -1; /* not found */
+}
+
+/* choose the ADC/MUX containing the input pin and initialize the setup */
+static void fixup_single_adc(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       int i;
+
+       /* search for the input pin; there must be only one */
+       if (cfg->num_inputs != 1)
+               return;
+       i = init_capsrc_for_pin(codec, cfg->inputs[0].pin);
+       if (i >= 0) {
                /* use only this ADC */
                if (spec->capsrc_nids)
                        spec->capsrc_nids += i;
                spec->adc_nids += i;
                spec->num_adc_nids = 1;
-               /* select or unmute this route */
-               if (get_wcaps_type(get_wcaps(codec, cap)) == AC_WID_AUD_MIX) {
-                       snd_hda_codec_amp_stereo(codec, cap, HDA_INPUT, idx,
-                                                HDA_AMP_MUTE, 0);
-               } else {
-                       snd_hda_codec_write_cache(codec, cap, 0,
-                                         AC_VERB_SET_CONNECT_SEL, idx);
-               }
-               return;
        }
 }
 
+/* initialize dual adcs */
+static void fixup_dual_adc_switch(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       init_capsrc_for_pin(codec, spec->ext_mic.pin);
+       init_capsrc_for_pin(codec, spec->int_mic.pin);
+}
+
 static void set_capture_mixer(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
@@ -5101,7 +5426,10 @@ static void set_capture_mixer(struct hda_codec *codec)
        };
        if (spec->num_adc_nids > 0 && spec->num_adc_nids <= 3) {
                int mux = 0;
-               if (spec->auto_mic)
+               int num_adcs = spec->num_adc_nids;
+               if (spec->dual_adc_switch)
+                       fixup_dual_adc_switch(codec);
+               else if (spec->auto_mic)
                        fixup_automic_adc(codec);
                else if (spec->input_mux) {
                        if (spec->input_mux->num_items > 1)
@@ -5109,7 +5437,9 @@ static void set_capture_mixer(struct hda_codec *codec)
                        else if (spec->input_mux->num_items == 1)
                                fixup_single_adc(codec);
                }
-               spec->cap_mixer = caps[mux][spec->num_adc_nids - 1];
+               if (spec->dual_adc_switch)
+                       num_adcs = 1;
+               spec->cap_mixer = caps[mux][num_adcs - 1];
        }
 }
 
@@ -5118,6 +5448,7 @@ static void fillup_priv_adc_nids(struct hda_codec *codec, hda_nid_t *nids,
                                 int num_nids)
 {
        struct alc_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
        int n;
        hda_nid_t fallback_adc = 0, fallback_cap = 0;
 
@@ -5143,10 +5474,8 @@ static void fillup_priv_adc_nids(struct hda_codec *codec, hda_nid_t *nids,
                        fallback_adc = adc;
                        fallback_cap = cap;
                }
-               for (i = 0; i < AUTO_PIN_LAST; i++) {
-                       hda_nid_t nid = spec->autocfg.input_pins[i];
-                       if (!nid)
-                               continue;
+               for (i = 0; i < cfg->num_inputs; i++) {
+                       hda_nid_t nid = cfg->inputs[i].pin;
                        for (j = 0; j < nconns; j++) {
                                if (conn[j] == nid)
                                        break;
@@ -5154,7 +5483,7 @@ static void fillup_priv_adc_nids(struct hda_codec *codec, hda_nid_t *nids,
                        if (j >= nconns)
                                break;
                }
-               if (i >= AUTO_PIN_LAST) {
+               if (i >= cfg->num_inputs) {
                        int num_adcs = spec->num_adc_nids;
                        spec->private_adc_nids[num_adcs] = adc;
                        spec->private_capsrc_nids[num_adcs] = cap;
@@ -5183,6 +5512,8 @@ static void fillup_priv_adc_nids(struct hda_codec *codec, hda_nid_t *nids,
 
 static struct snd_pci_quirk beep_white_list[] = {
        SND_PCI_QUIRK(0x1043, 0x829f, "ASUS", 1),
+       SND_PCI_QUIRK(0x1043, 0x83ce, "EeePC", 1),
+       SND_PCI_QUIRK(0x8086, 0xd613, "Intel", 1),
        {}
 };
 
@@ -6520,12 +6851,13 @@ static void alc260_auto_init_multi_out(struct hda_codec *codec)
 static void alc260_auto_init_analog_input(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
        int i;
 
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               hda_nid_t nid = spec->autocfg.input_pins[i];
+       for (i = 0; i < cfg->num_inputs; i++) {
+               hda_nid_t nid = cfg->inputs[i].pin;
                if (nid >= 0x12) {
-                       alc_set_input_pin(codec, nid, i);
+                       alc_set_input_pin(codec, nid, cfg->inputs[i].type);
                        if (nid != ALC260_PIN_CD_NID &&
                            (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
                                snd_hda_codec_write(codec, nid, 0,
@@ -6624,6 +6956,7 @@ static void alc260_auto_init(struct hda_codec *codec)
        alc260_auto_init_multi_out(codec);
        alc260_auto_init_analog_input(codec);
        alc260_auto_init_input_src(codec);
+       alc_auto_init_digital(codec);
        if (spec->unsol_event)
                alc_inithook(codec);
 }
@@ -6639,6 +6972,27 @@ static struct hda_amp_list alc260_loopbacks[] = {
 };
 #endif
 
+/*
+ * Pin config fixes
+ */
+enum {
+       PINFIX_HP_DC5750,
+};
+
+static const struct alc_fixup alc260_fixups[] = {
+       [PINFIX_HP_DC5750] = {
+               .pins = (const struct alc_pincfg[]) {
+                       { 0x11, 0x90130110 }, /* speaker */
+                       { }
+               }
+       },
+};
+
+static struct snd_pci_quirk alc260_fixup_tbl[] = {
+       SND_PCI_QUIRK(0x103c, 0x280a, "HP dc5750", PINFIX_HP_DC5750),
+       {}
+};
+
 /*
  * ALC260 configurations
  */
@@ -6838,6 +7192,9 @@ static int patch_alc260(struct hda_codec *codec)
                board_config = ALC260_AUTO;
        }
 
+       if (board_config == ALC260_AUTO)
+               alc_pick_fixup(codec, alc260_fixup_tbl, alc260_fixups, 1);
+
        if (board_config == ALC260_AUTO) {
                /* automatic parse from the BIOS config */
                err = alc260_parse_auto_config(codec);
@@ -6863,6 +7220,7 @@ static int patch_alc260(struct hda_codec *codec)
 
        spec->stream_analog_playback = &alc260_pcm_analog_playback;
        spec->stream_analog_capture = &alc260_pcm_analog_capture;
+       spec->stream_analog_alt_capture = &alc260_pcm_analog_capture;
 
        spec->stream_digital_playback = &alc260_pcm_digital_playback;
        spec->stream_digital_capture = &alc260_pcm_digital_capture;
@@ -6883,6 +7241,9 @@ static int patch_alc260(struct hda_codec *codec)
        set_capture_mixer(codec);
        set_beep_amp(spec, 0x07, 0x05, HDA_INPUT);
 
+       if (board_config == ALC260_AUTO)
+               alc_pick_fixup(codec, alc260_fixup_tbl, alc260_fixups, 0);
+
        spec->vmaster_nid = 0x08;
 
        codec->patch_ops = alc_patch_ops;
@@ -7003,7 +7364,7 @@ static struct hda_input_mux alc883_lenovo_nb0763_capture_source = {
        .num_items = 4,
        .items = {
                { "Mic", 0x0 },
-               { "iMic", 0x1 },
+               { "Int Mic", 0x1 },
                { "Line", 0x2 },
                { "CD", 0x4 },
        },
@@ -8573,8 +8934,8 @@ static struct snd_kcontrol_new alc883_lenovo_nb0763_mixer[] = {
        HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
        HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
        HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("iMic Playback Volume", 0x0b, 0x1, HDA_INPUT),
-       HDA_CODEC_MUTE("iMic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+       HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+       HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
        { } /* end */
 };
 
@@ -10265,24 +10626,35 @@ static struct alc_config_preset alc882_presets[] = {
  * Pin config fixes
  */
 enum {
-       PINFIX_ABIT_AW9D_MAX
-};
-
-static struct alc_pincfg alc882_abit_aw9d_pinfix[] = {
-       { 0x15, 0x01080104 }, /* side */
-       { 0x16, 0x01011012 }, /* rear */
-       { 0x17, 0x01016011 }, /* clfe */
-       { }
+       PINFIX_ABIT_AW9D_MAX,
+       PINFIX_PB_M5210,
+       PINFIX_ACER_ASPIRE_7736,
 };
 
 static const struct alc_fixup alc882_fixups[] = {
        [PINFIX_ABIT_AW9D_MAX] = {
-               .pins = alc882_abit_aw9d_pinfix
+               .pins = (const struct alc_pincfg[]) {
+                       { 0x15, 0x01080104 }, /* side */
+                       { 0x16, 0x01011012 }, /* rear */
+                       { 0x17, 0x01016011 }, /* clfe */
+                       { }
+               }
+       },
+       [PINFIX_PB_M5210] = {
+               .verbs = (const struct hda_verb[]) {
+                       { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 },
+                       {}
+               }
+       },
+       [PINFIX_ACER_ASPIRE_7736] = {
+               .sku = ALC_FIXUP_SKU_IGNORE,
        },
 };
 
 static struct snd_pci_quirk alc882_fixup_tbl[] = {
+       SND_PCI_QUIRK(0x1025, 0x0155, "Packard-Bell M5120", PINFIX_PB_M5210),
        SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", PINFIX_ABIT_AW9D_MAX),
+       SND_PCI_QUIRK(0x1025, 0x0296, "Acer Aspire 7736z", PINFIX_ACER_ASPIRE_7736),
        {}
 };
 
@@ -10331,16 +10703,21 @@ static void alc882_auto_init_hp_out(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
        hda_nid_t pin, dac;
+       int i;
 
-       pin = spec->autocfg.hp_pins[0];
-       if (pin) {
+       for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) {
+               pin = spec->autocfg.hp_pins[i];
+               if (!pin)
+                       break;
                dac = spec->multiout.hp_nid;
                if (!dac)
                        dac = spec->multiout.dac_nids[0]; /* to front */
                alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, dac);
        }
-       pin = spec->autocfg.speaker_pins[0];
-       if (pin) {
+       for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) {
+               pin = spec->autocfg.speaker_pins[i];
+               if (!pin)
+                       break;
                dac = spec->multiout.extra_out_nid[0];
                if (!dac)
                        dac = spec->multiout.dac_nids[0]; /* to front */
@@ -10351,13 +10728,12 @@ static void alc882_auto_init_hp_out(struct hda_codec *codec)
 static void alc882_auto_init_analog_input(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
        int i;
 
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               hda_nid_t nid = spec->autocfg.input_pins[i];
-               if (!nid)
-                       continue;
-               alc_set_input_pin(codec, nid, i);
+       for (i = 0; i < cfg->num_inputs; i++) {
+               hda_nid_t nid = cfg->inputs[i].pin;
+               alc_set_input_pin(codec, nid, cfg->inputs[i].type);
                if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
                        snd_hda_codec_write(codec, nid, 0,
                                            AC_VERB_SET_AMP_GAIN_MUTE,
@@ -10419,24 +10795,23 @@ static void alc882_auto_init_input_src(struct hda_codec *codec)
 static int alc_auto_add_mic_boost(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       int err;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       int i, err;
        hda_nid_t nid;
 
-       nid = spec->autocfg.input_pins[AUTO_PIN_MIC];
-       if (nid && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) {
-               err = add_control(spec, ALC_CTL_WIDGET_VOL,
-                                 "Mic Boost",
-                                 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
-               if (err < 0)
-                       return err;
-       }
-       nid = spec->autocfg.input_pins[AUTO_PIN_FRONT_MIC];
-       if (nid && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) {
-               err = add_control(spec, ALC_CTL_WIDGET_VOL,
-                                 "Front Mic Boost",
+       for (i = 0; i < cfg->num_inputs; i++) {
+               if (cfg->inputs[i].type > AUTO_PIN_MIC)
+                       break;
+               nid = cfg->inputs[i].pin;
+               if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) {
+                       char label[32];
+                       snprintf(label, sizeof(label), "%s Boost",
+                                hda_get_autocfg_input_label(codec, cfg, i));
+                       err = add_control(spec, ALC_CTL_WIDGET_VOL, label, 0,
                                  HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
-               if (err < 0)
-                       return err;
+                       if (err < 0)
+                               return err;
+               }
        }
        return 0;
 }
@@ -10446,7 +10821,7 @@ static int alc882_parse_auto_config(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
        static hda_nid_t alc882_ignore[] = { 0x1d, 0 };
-       int i, err;
+       int err;
 
        err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
                                           alc882_ignore);
@@ -10476,25 +10851,7 @@ static int alc882_parse_auto_config(struct hda_codec *codec)
 
        spec->multiout.max_channels = spec->multiout.num_dacs * 2;
 
-       /* check multiple SPDIF-out (for recent codecs) */
-       for (i = 0; i < spec->autocfg.dig_outs; i++) {
-               hda_nid_t dig_nid;
-               err = snd_hda_get_connections(codec,
-                                             spec->autocfg.dig_out_pins[i],
-                                             &dig_nid, 1);
-               if (err < 0)
-                       continue;
-               if (!i)
-                       spec->multiout.dig_out_nid = dig_nid;
-               else {
-                       spec->multiout.slave_dig_outs = spec->slave_dig_outs;
-                       if (i >= ARRAY_SIZE(spec->slave_dig_outs) - 1)
-                               break;
-                       spec->slave_dig_outs[i - 1] = dig_nid;
-               }
-       }
-       if (spec->autocfg.dig_in_pin)
-               spec->dig_in_nid = ALC880_DIGIN_NID;
+       alc_auto_parse_digital(codec);
 
        if (spec->kctls.list)
                add_mixer(spec, spec->kctls.list);
@@ -10524,6 +10881,7 @@ static void alc882_auto_init(struct hda_codec *codec)
        alc882_auto_init_hp_out(codec);
        alc882_auto_init_analog_input(codec);
        alc882_auto_init_input_src(codec);
+       alc_auto_init_digital(codec);
        if (spec->unsol_event)
                alc_inithook(codec);
 }
@@ -10539,8 +10897,6 @@ static int patch_alc882(struct hda_codec *codec)
 
        codec->spec = spec;
 
-       alc_auto_parse_customize_define(codec);
-
        switch (codec->vendor_id) {
        case 0x10ec0882:
        case 0x10ec0885:
@@ -10568,6 +10924,8 @@ static int patch_alc882(struct hda_codec *codec)
        if (board_config == ALC882_AUTO)
                alc_pick_fixup(codec, alc882_fixup_tbl, alc882_fixups, 1);
 
+       alc_auto_parse_customize_define(codec);
+
        if (board_config == ALC882_AUTO) {
                /* automatic parse from the BIOS config */
                err = alc882_parse_auto_config(codec);
@@ -10648,6 +11006,8 @@ static int patch_alc882(struct hda_codec *codec)
        codec->patch_ops = alc_patch_ops;
        if (board_config == ALC882_AUTO)
                spec->init_hook = alc882_auto_init;
+
+       alc_init_jacks(codec);
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        if (!spec->loopback.amplist)
                spec->loopback.amplist = alc882_loopbacks;
@@ -11644,7 +12004,7 @@ static int alc262_check_volbit(hda_nid_t nid)
 }
 
 static int alc262_add_out_vol_ctl(struct alc_spec *spec, hda_nid_t nid,
-                                 const char *pfx, int *vbits)
+                                 const char *pfx, int *vbits, int idx)
 {
        unsigned long val;
        int vbit;
@@ -11659,11 +12019,11 @@ static int alc262_add_out_vol_ctl(struct alc_spec *spec, hda_nid_t nid,
                val = HDA_COMPOSE_AMP_VAL(0x0e, 2, 0, HDA_OUTPUT);
        else
                val = HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT);
-       return add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, val);
+       return __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, idx, val);
 }
 
 static int alc262_add_out_sw_ctl(struct alc_spec *spec, hda_nid_t nid,
-                                const char *pfx)
+                                const char *pfx, int idx)
 {
        unsigned long val;
 
@@ -11673,7 +12033,7 @@ static int alc262_add_out_sw_ctl(struct alc_spec *spec, hda_nid_t nid,
                val = HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT);
        else
                val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
-       return add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, val);
+       return __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, idx, val);
 }
 
 /* add playback controls from the parsed DAC table */
@@ -11682,7 +12042,7 @@ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec,
 {
        const char *pfx;
        int vbits;
-       int err;
+       int i, err;
 
        spec->multiout.num_dacs = 1;    /* only use one dac */
        spec->multiout.dac_nids = spec->private_dac_nids;
@@ -11692,39 +12052,52 @@ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec,
                pfx = "Master";
        else if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
                pfx = "Speaker";
+       else if (cfg->line_out_type == AUTO_PIN_HP_OUT)
+               pfx = "Headphone";
        else
                pfx = "Front";
-       err = alc262_add_out_sw_ctl(spec, cfg->line_out_pins[0], pfx);
-       if (err < 0)
-               return err;
-       err = alc262_add_out_sw_ctl(spec, cfg->speaker_pins[0], "Speaker");
-       if (err < 0)
-               return err;
-       err = alc262_add_out_sw_ctl(spec, cfg->hp_pins[0], "Headphone");
-       if (err < 0)
-               return err;
+       for (i = 0; i < 2; i++) {
+               err = alc262_add_out_sw_ctl(spec, cfg->line_out_pins[i], pfx, i);
+               if (err < 0)
+                       return err;
+               if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+                       err = alc262_add_out_sw_ctl(spec, cfg->speaker_pins[i],
+                                                   "Speaker", i);
+                       if (err < 0)
+                               return err;
+               }
+               if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
+                       err = alc262_add_out_sw_ctl(spec, cfg->hp_pins[i],
+                                                   "Headphone", i);
+                       if (err < 0)
+                               return err;
+               }
+       }
 
        vbits = alc262_check_volbit(cfg->line_out_pins[0]) |
                alc262_check_volbit(cfg->speaker_pins[0]) |
                alc262_check_volbit(cfg->hp_pins[0]);
        if (vbits == 1 || vbits == 2)
                pfx = "Master"; /* only one mixer is used */
-       else if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
-               pfx = "Speaker";
-       else
-               pfx = "Front";
        vbits = 0;
-       err = alc262_add_out_vol_ctl(spec, cfg->line_out_pins[0], pfx, &vbits);
-       if (err < 0)
-               return err;
-       err = alc262_add_out_vol_ctl(spec, cfg->speaker_pins[0], "Speaker",
-                                    &vbits);
-       if (err < 0)
-               return err;
-       err = alc262_add_out_vol_ctl(spec, cfg->hp_pins[0], "Headphone",
-                                    &vbits);
-       if (err < 0)
-               return err;
+       for (i = 0; i < 2; i++) {
+               err = alc262_add_out_vol_ctl(spec, cfg->line_out_pins[i], pfx,
+                                            &vbits, i);
+               if (err < 0)
+                       return err;
+               if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+                       err = alc262_add_out_vol_ctl(spec, cfg->speaker_pins[i],
+                                                    "Speaker", &vbits, i);
+                       if (err < 0)
+                               return err;
+               }
+               if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
+                       err = alc262_add_out_vol_ctl(spec, cfg->hp_pins[i],
+                                                    "Headphone", &vbits, i);
+                       if (err < 0)
+                               return err;
+               }
+       }
        return 0;
 }
 
@@ -12012,6 +12385,35 @@ static struct hda_verb alc262_toshiba_rx1_unsol_verbs[] = {
        {}
 };
 
+/*
+ * Pin config fixes
+ */
+enum {
+       PINFIX_FSC_H270,
+};
+
+static const struct alc_fixup alc262_fixups[] = {
+       [PINFIX_FSC_H270] = {
+               .pins = (const struct alc_pincfg[]) {
+                       { 0x14, 0x99130110 }, /* speaker */
+                       { 0x15, 0x0221142f }, /* front HP */
+                       { 0x1b, 0x0121141f }, /* rear HP */
+                       { }
+               }
+       },
+       [PINFIX_PB_M5210] = {
+               .verbs = (const struct hda_verb[]) {
+                       { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 },
+                       {}
+               }
+       },
+};
+
+static struct snd_pci_quirk alc262_fixup_tbl[] = {
+       SND_PCI_QUIRK(0x1734, 0x1147, "FSC Celsius H270", PINFIX_FSC_H270),
+       {}
+};
+
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #define alc262_loopbacks       alc880_loopbacks
@@ -12054,12 +12456,7 @@ static int alc262_parse_auto_config(struct hda_codec *codec)
        spec->multiout.max_channels = spec->multiout.num_dacs * 2;
 
  dig_only:
-       if (spec->autocfg.dig_outs) {
-               spec->multiout.dig_out_nid = ALC262_DIGOUT_NID;
-               spec->dig_out_type = spec->autocfg.dig_out_type[0];
-       }
-       if (spec->autocfg.dig_in_pin)
-               spec->dig_in_nid = ALC262_DIGIN_NID;
+       alc_auto_parse_digital(codec);
 
        if (spec->kctls.list)
                add_mixer(spec, spec->kctls.list);
@@ -12091,6 +12488,7 @@ static void alc262_auto_init(struct hda_codec *codec)
        alc262_auto_init_hp_out(codec);
        alc262_auto_init_analog_input(codec);
        alc262_auto_init_input_src(codec);
+       alc_auto_init_digital(codec);
        if (spec->unsol_event)
                alc_inithook(codec);
 }
@@ -12439,6 +12837,9 @@ static int patch_alc262(struct hda_codec *codec)
                board_config = ALC262_AUTO;
        }
 
+       if (board_config == ALC262_AUTO)
+               alc_pick_fixup(codec, alc262_fixup_tbl, alc262_fixups, 1);
+
        if (board_config == ALC262_AUTO) {
                /* automatic parse from the BIOS config */
                err = alc262_parse_auto_config(codec);
@@ -12507,11 +12908,16 @@ static int patch_alc262(struct hda_codec *codec)
        if (!spec->no_analog && has_cdefine_beep(codec))
                set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
 
+       if (board_config == ALC262_AUTO)
+               alc_pick_fixup(codec, alc262_fixup_tbl, alc262_fixups, 0);
+
        spec->vmaster_nid = 0x0c;
 
        codec->patch_ops = alc_patch_ops;
        if (board_config == ALC262_AUTO)
                spec->init_hook = alc262_auto_init;
+
+       alc_init_jacks(codec);
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        if (!spec->loopback.amplist)
                spec->loopback.amplist = alc262_loopbacks;
@@ -13024,10 +13430,14 @@ static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid,
                dac = 0x02;
                break;
        case 0x15:
+       case 0x1a: /* ALC259/269 only */
+       case 0x1b: /* ALC259/269 only */
        case 0x21: /* ALC269vb has this pin, too */
                dac = 0x03;
                break;
        default:
+               snd_printd(KERN_WARNING "hda_codec: "
+                          "ignoring pin 0x%x as unknown\n", nid);
                return 0;
        }
        if (spec->multiout.dac_nids[0] != dac &&
@@ -13078,7 +13488,7 @@ static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec,
                                  HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
                if (err < 0)
                        return err;
-       } else {
+       } else if (nid) {
                err = alc268_new_analog_output(spec, nid, "Speaker", 0);
                if (err < 0)
                        return err;
@@ -13123,8 +13533,10 @@ static void alc268_auto_set_output_and_unmute(struct hda_codec *codec,
 static void alc268_auto_init_multi_out(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       hda_nid_t nid = spec->autocfg.line_out_pins[0];
-       if (nid) {
+       int i;
+
+       for (i = 0; i < spec->autocfg.line_outs; i++) {
+               hda_nid_t nid = spec->autocfg.line_out_pins[i];
                int pin_type = get_pin_type(spec->autocfg.line_out_type);
                alc268_auto_set_output_and_unmute(codec, nid, pin_type);
        }
@@ -13134,13 +13546,19 @@ static void alc268_auto_init_hp_out(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
        hda_nid_t pin;
+       int i;
 
-       pin = spec->autocfg.hp_pins[0];
-       if (pin)
+       for (i = 0; i < spec->autocfg.hp_outs; i++) {
+               pin = spec->autocfg.hp_pins[i];
                alc268_auto_set_output_and_unmute(codec, pin, PIN_HP);
-       pin = spec->autocfg.speaker_pins[0];
-       if (pin)
+       }
+       for (i = 0; i < spec->autocfg.speaker_outs; i++) {
+               pin = spec->autocfg.speaker_pins[i];
                alc268_auto_set_output_and_unmute(codec, pin, PIN_OUT);
+       }
+       if (spec->autocfg.mono_out_pin)
+               snd_hda_codec_write(codec, spec->autocfg.mono_out_pin, 0,
+                                   AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
 }
 
 static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec)
@@ -13227,10 +13645,7 @@ static int alc268_parse_auto_config(struct hda_codec *codec)
 
  dig_only:
        /* digital only support output */
-       if (spec->autocfg.dig_outs) {
-               spec->multiout.dig_out_nid = ALC268_DIGOUT_NID;
-               spec->dig_out_type = spec->autocfg.dig_out_type[0];
-       }
+       alc_auto_parse_digital(codec);
        if (spec->kctls.list)
                add_mixer(spec, spec->kctls.list);
 
@@ -13260,6 +13675,7 @@ static void alc268_auto_init(struct hda_codec *codec)
        alc268_auto_init_hp_out(codec);
        alc268_auto_init_mono_speaker_out(codec);
        alc268_auto_init_analog_input(codec);
+       alc_auto_init_digital(codec);
        if (spec->unsol_event)
                alc_inithook(codec);
 }
@@ -13303,7 +13719,6 @@ static struct snd_pci_quirk alc268_cfg_tbl[] = {
        SND_PCI_QUIRK(0x14c0, 0x0025, "COMPAL IFL90/JFL-92", ALC268_TOSHIBA),
        SND_PCI_QUIRK(0x152d, 0x0763, "Diverse (CPR2000)", ALC268_ACER),
        SND_PCI_QUIRK(0x152d, 0x0771, "Quanta IL1", ALC267_QUANTA_IL1),
-       SND_PCI_QUIRK(0x1854, 0x1775, "LG R510", ALC268_DELL),
        {}
 };
 
@@ -13582,6 +13997,8 @@ static int patch_alc268(struct hda_codec *codec)
        if (board_config == ALC268_AUTO)
                spec->init_hook = alc268_auto_init;
 
+       alc_init_jacks(codec);
+
        return 0;
 }
 
@@ -13694,6 +14111,12 @@ static struct snd_kcontrol_new alc269vb_laptop_mixer[] = {
        { } /* end */
 };
 
+static struct snd_kcontrol_new alc269_asus_mixer[] = {
+       HDA_CODEC_VOLUME("Master Playback Volume", 0x02, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Master Playback Switch", 0x0c, 0x0, HDA_INPUT),
+       { } /* end */
+};
+
 /* capture mixer elements */
 static struct snd_kcontrol_new alc269_laptop_analog_capture_mixer[] = {
        HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
@@ -13914,6 +14337,20 @@ static struct hda_verb alc269vb_laptop_amic_init_verbs[] = {
        {}
 };
 
+static struct hda_verb alc271_acer_dmic_verbs[] = {
+       {0x20, AC_VERB_SET_COEF_INDEX, 0x0d},
+       {0x20, AC_VERB_SET_PROC_COEF, 0x4000},
+       {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x21, AC_VERB_SET_CONNECT_SEL, 0x00},
+       {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+       {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+       {0x22, AC_VERB_SET_CONNECT_SEL, 6},
+       { }
+};
+
 /* toggle speaker-output according to the hp-jack state */
 static void alc269_speaker_automute(struct hda_codec *codec)
 {
@@ -13928,6 +14365,7 @@ static void alc269_speaker_automute(struct hda_codec *codec)
                                 HDA_AMP_MUTE, bits);
        snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
                                 HDA_AMP_MUTE, bits);
+       alc_report_jack(codec, nid);
 }
 
 /* unsolicited event for HP jack sensing */
@@ -14152,6 +14590,43 @@ static int alc269_mic2_mute_check_ps(struct hda_codec *codec, hda_nid_t nid)
 }
 #endif /* CONFIG_SND_HDA_POWER_SAVE */
 
+static int alc275_setup_dual_adc(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+
+       if (codec->vendor_id != 0x10ec0275 || !spec->auto_mic)
+               return 0;
+       if ((spec->ext_mic.pin >= 0x18 && spec->int_mic.pin <= 0x13) ||
+           (spec->ext_mic.pin <= 0x12 && spec->int_mic.pin >= 0x18)) {
+               if (spec->ext_mic.pin <= 0x12) {
+                       spec->private_adc_nids[0] = 0x08;
+                       spec->private_adc_nids[1] = 0x11;
+                       spec->private_capsrc_nids[0] = 0x23;
+                       spec->private_capsrc_nids[1] = 0x22;
+               } else {
+                       spec->private_adc_nids[0] = 0x11;
+                       spec->private_adc_nids[1] = 0x08;
+                       spec->private_capsrc_nids[0] = 0x22;
+                       spec->private_capsrc_nids[1] = 0x23;
+               }
+               spec->adc_nids = spec->private_adc_nids;
+               spec->capsrc_nids = spec->private_capsrc_nids;
+               spec->num_adc_nids = 2;
+               spec->dual_adc_switch = 1;
+               snd_printdd("realtek: enabling dual ADC switchg (%02x:%02x)\n",
+                           spec->adc_nids[0], spec->adc_nids[1]);
+               return 1;
+       }
+       return 0;
+}
+
+/* different alc269-variants */
+enum {
+       ALC269_TYPE_NORMAL,
+       ALC269_TYPE_ALC259,
+       ALC269_TYPE_ALC271X,
+};
+
 /*
  * BIOS auto configuration
  */
@@ -14169,19 +14644,22 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
        err = alc269_auto_create_multi_out_ctls(spec, &spec->autocfg);
        if (err < 0)
                return err;
-       err = alc269_auto_create_input_ctls(codec, &spec->autocfg);
+       if (spec->codec_variant == ALC269_TYPE_NORMAL)
+               err = alc269_auto_create_input_ctls(codec, &spec->autocfg);
+       else
+               err = alc_auto_create_input_ctls(codec, &spec->autocfg, 0,
+                                                0x22, 0);
        if (err < 0)
                return err;
 
        spec->multiout.max_channels = spec->multiout.num_dacs * 2;
 
-       if (spec->autocfg.dig_outs)
-               spec->multiout.dig_out_nid = ALC269_DIGOUT_NID;
+       alc_auto_parse_digital(codec);
 
        if (spec->kctls.list)
                add_mixer(spec, spec->kctls.list);
 
-       if ((alc_read_coef_idx(codec, 0) & 0x00f0) == 0x0010) {
+       if (spec->codec_variant != ALC269_TYPE_NORMAL) {
                add_verb(spec, alc269vb_init_verbs);
                alc_ssid_check(codec, 0, 0x1b, 0x14, 0x21);
        } else {
@@ -14191,13 +14669,15 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
 
        spec->num_mux_defs = 1;
        spec->input_mux = &spec->private_imux[0];
-       fillup_priv_adc_nids(codec, alc269_adc_candidates,
-                            sizeof(alc269_adc_candidates));
+
+       if (!alc275_setup_dual_adc(codec))
+               fillup_priv_adc_nids(codec, alc269_adc_candidates,
+                                    sizeof(alc269_adc_candidates));
 
        /* set default input source */
-       snd_hda_codec_write_cache(codec, spec->capsrc_nids[0],
-                                 0, AC_VERB_SET_CONNECT_SEL,
-                                 spec->input_mux->items[0].index);
+       if (!spec->dual_adc_switch)
+               select_or_unmute_capsrc(codec, spec->capsrc_nids[0],
+                                       spec->input_mux->items[0].index);
 
        err = alc_auto_add_mic_boost(codec);
        if (err < 0)
@@ -14221,27 +14701,90 @@ static void alc269_auto_init(struct hda_codec *codec)
        alc269_auto_init_multi_out(codec);
        alc269_auto_init_hp_out(codec);
        alc269_auto_init_analog_input(codec);
+       alc_auto_init_digital(codec);
        if (spec->unsol_event)
                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);
+       if (power_up)
+               val |= 1 << 11;
+       else
+               val &= ~(1 << 11);
+       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)
+{
+       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 */
+
+static int alc269_resume(struct hda_codec *codec)
+{
+       if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) {
+               alc269_toggle_power_output(codec, 0);
+               msleep(150);
+       }
+
+       codec->patch_ops.init(codec);
+
+       if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x017) {
+               alc269_toggle_power_output(codec, 1);
+               msleep(200);
+       }
+
+       if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018)
+               alc269_toggle_power_output(codec, 1);
+
+       snd_hda_codec_resume_amp(codec);
+       snd_hda_codec_resume_cache(codec);
+       hda_call_check_power_status(codec, 0x01);
+       return 0;
+}
+#endif /* SND_HDA_NEEDS_RESUME */
+
 enum {
        ALC269_FIXUP_SONY_VAIO,
-};
-
-static const struct hda_verb alc269_sony_vaio_fixup_verbs[] = {
-       {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREFGRD},
-       {}
+       ALC269_FIXUP_DELL_M101Z,
 };
 
 static const struct alc_fixup alc269_fixups[] = {
        [ALC269_FIXUP_SONY_VAIO] = {
-               .verbs = alc269_sony_vaio_fixup_verbs
+               .verbs = (const struct hda_verb[]) {
+                       {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREFGRD},
+                       {}
+               }
+       },
+       [ALC269_FIXUP_DELL_M101Z] = {
+               .verbs = (const struct hda_verb[]) {
+                       /* Enables internal speaker */
+                       {0x20, AC_VERB_SET_COEF_INDEX, 13},
+                       {0x20, AC_VERB_SET_PROC_COEF, 0x4040},
+                       {}
+               }
        },
 };
 
 static struct snd_pci_quirk alc269_fixup_tbl[] = {
-       SND_PCI_QUIRK(0x104d, 0x9071, "Sony VAIO", ALC269_FIXUP_SONY_VAIO),
+       SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO),
+       SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z),
        {}
 };
 
@@ -14261,6 +14804,7 @@ static const char *alc269_models[ALC269_MODEL_LAST] = {
 
 static struct snd_pci_quirk alc269_cfg_tbl[] = {
        SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_QUANTA_FL1),
+       SND_PCI_QUIRK(0x1025, 0x047c, "ACER ZGA", ALC271_ACER),
        SND_PCI_QUIRK(0x1043, 0x8330, "ASUS Eeepc P703 P900A",
                      ALC269_AMIC),
        SND_PCI_QUIRK(0x1043, 0x1013, "ASUS N61Da", ALC269VB_AMIC),
@@ -14422,15 +14966,66 @@ static struct alc_config_preset alc269_presets[] = {
                .unsol_event = alc269_lifebook_unsol_event,
                .init_hook = alc269_lifebook_init_hook,
        },
-};
-
-static int patch_alc269(struct hda_codec *codec)
-{
-       struct alc_spec *spec;
-       int board_config;
-       int err;
-       int is_alc269vb = 0;
-
+       [ALC271_ACER] = {
+               .mixers = { alc269_asus_mixer },
+               .cap_mixer = alc269vb_laptop_digital_capture_mixer,
+               .init_verbs = { alc269_init_verbs, alc271_acer_dmic_verbs },
+               .num_dacs = ARRAY_SIZE(alc269_dac_nids),
+               .dac_nids = alc269_dac_nids,
+               .adc_nids = alc262_dmic_adc_nids,
+               .num_adc_nids = ARRAY_SIZE(alc262_dmic_adc_nids),
+               .capsrc_nids = alc262_dmic_capsrc_nids,
+               .num_channel_mode = ARRAY_SIZE(alc269_modes),
+               .channel_mode = alc269_modes,
+               .input_mux = &alc269_capture_source,
+               .dig_out_nid = ALC880_DIGOUT_NID,
+               .unsol_event = alc_sku_unsol_event,
+               .setup = alc269vb_laptop_dmic_setup,
+               .init_hook = alc_inithook,
+       },
+};
+
+static int alc269_fill_coef(struct hda_codec *codec)
+{
+       int val;
+
+       if ((alc_read_coef_idx(codec, 0) & 0x00ff) < 0x015) {
+               alc_write_coef_idx(codec, 0xf, 0x960b);
+               alc_write_coef_idx(codec, 0xe, 0x8817);
+       }
+
+       if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x016) {
+               alc_write_coef_idx(codec, 0xf, 0x960b);
+               alc_write_coef_idx(codec, 0xe, 0x8814);
+       }
+
+       if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x017) {
+               val = alc_read_coef_idx(codec, 0x04);
+               /* Power up output pin */
+               alc_write_coef_idx(codec, 0x04, val | (1<<11));
+       }
+
+       if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) {
+               val = alc_read_coef_idx(codec, 0xd);
+               if ((val & 0x0c00) >> 10 != 0x1) {
+                       /* Capless ramp up clock control */
+                       alc_write_coef_idx(codec, 0xd, val | 1<<10);
+               }
+               val = alc_read_coef_idx(codec, 0x17);
+               if ((val & 0x01c0) >> 6 != 0x4) {
+                       /* Class D power on reset */
+                       alc_write_coef_idx(codec, 0x17, val | 1<<7);
+               }
+       }
+       return 0;
+}
+
+static int patch_alc269(struct hda_codec *codec)
+{
+       struct alc_spec *spec;
+       int board_config;
+       int err;
+
        spec = kzalloc(sizeof(*spec), GFP_KERNEL);
        if (spec == NULL)
                return -ENOMEM;
@@ -14441,14 +15036,18 @@ static int patch_alc269(struct hda_codec *codec)
 
        if ((alc_read_coef_idx(codec, 0) & 0x00f0) == 0x0010){
                if (codec->bus->pci->subsystem_vendor == 0x1025 &&
-                   spec->cdefine.platform_type == 1)
+                   spec->cdefine.platform_type == 1) {
                        alc_codec_rename(codec, "ALC271X");
-               else
+                       spec->codec_variant = ALC269_TYPE_ALC271X;
+               } else {
                        alc_codec_rename(codec, "ALC259");
-               is_alc269vb = 1;
+                       spec->codec_variant = ALC269_TYPE_ALC259;
+               }
        } else
                alc_fix_pll_init(codec, 0x20, 0x04, 15);
 
+       alc269_fill_coef(codec);
+
        board_config = snd_hda_check_board_config(codec, ALC269_MODEL_LAST,
                                                  alc269_models,
                                                  alc269_cfg_tbl);
@@ -14493,6 +15092,10 @@ static int patch_alc269(struct hda_codec *codec)
                 */
                spec->stream_analog_playback = &alc269_44k_pcm_analog_playback;
                spec->stream_analog_capture = &alc269_44k_pcm_analog_capture;
+       } else if (spec->dual_adc_switch) {
+               spec->stream_analog_playback = &alc269_pcm_analog_playback;
+               /* switch ADC dynamically */
+               spec->stream_analog_capture = &dualmic_pcm_analog_capture;
        } else {
                spec->stream_analog_playback = &alc269_pcm_analog_playback;
                spec->stream_analog_capture = &alc269_pcm_analog_capture;
@@ -14501,7 +15104,7 @@ static int patch_alc269(struct hda_codec *codec)
        spec->stream_digital_capture = &alc269_pcm_digital_capture;
 
        if (!spec->adc_nids) { /* wasn't filled automatically? use default */
-               if (!is_alc269vb) {
+               if (spec->codec_variant != ALC269_TYPE_NORMAL) {
                        spec->adc_nids = alc269_adc_nids;
                        spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids);
                        spec->capsrc_nids = alc269_capsrc_nids;
@@ -14523,8 +15126,16 @@ 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;
+
+       alc_init_jacks(codec);
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        if (!spec->loopback.amplist)
                spec->loopback.amplist = alc269_loopbacks;
@@ -15337,12 +15948,13 @@ static void alc861_auto_init_hp_out(struct hda_codec *codec)
 static void alc861_auto_init_analog_input(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
        int i;
 
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               hda_nid_t nid = spec->autocfg.input_pins[i];
+       for (i = 0; i < cfg->num_inputs; i++) {
+               hda_nid_t nid = cfg->inputs[i].pin;
                if (nid >= 0x0c && nid <= 0x11)
-                       alc_set_input_pin(codec, nid, i);
+                       alc_set_input_pin(codec, nid, cfg->inputs[i].type);
        }
 }
 
@@ -15378,8 +15990,7 @@ static int alc861_parse_auto_config(struct hda_codec *codec)
 
        spec->multiout.max_channels = spec->multiout.num_dacs * 2;
 
-       if (spec->autocfg.dig_outs)
-               spec->multiout.dig_out_nid = ALC861_DIGOUT_NID;
+       alc_auto_parse_digital(codec);
 
        if (spec->kctls.list)
                add_mixer(spec, spec->kctls.list);
@@ -15405,6 +16016,7 @@ static void alc861_auto_init(struct hda_codec *codec)
        alc861_auto_init_multi_out(codec);
        alc861_auto_init_hp_out(codec);
        alc861_auto_init_analog_input(codec);
+       alc_auto_init_digital(codec);
        if (spec->unsol_event)
                alc_inithook(codec);
 }
@@ -15571,15 +16183,13 @@ enum {
        PINFIX_FSC_AMILO_PI1505,
 };
 
-static struct alc_pincfg alc861_fsc_amilo_pi1505_pinfix[] = {
-       { 0x0b, 0x0221101f }, /* HP */
-       { 0x0f, 0x90170310 }, /* speaker */
-       { }
-};
-
 static const struct alc_fixup alc861_fixups[] = {
        [PINFIX_FSC_AMILO_PI1505] = {
-               .pins = alc861_fsc_amilo_pi1505_pinfix
+               .pins = (const struct alc_pincfg[]) {
+                       { 0x0b, 0x0221101f }, /* HP */
+                       { 0x0f, 0x90170310 }, /* speaker */
+                       { }
+               }
        },
 };
 
@@ -16331,12 +16941,13 @@ static void alc861vd_auto_init_hp_out(struct hda_codec *codec)
 static void alc861vd_auto_init_analog_input(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
        int i;
 
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               hda_nid_t nid = spec->autocfg.input_pins[i];
+       for (i = 0; i < cfg->num_inputs; i++) {
+               hda_nid_t nid = cfg->inputs[i].pin;
                if (alc_is_input_pin(codec, nid)) {
-                       alc_set_input_pin(codec, nid, i);
+                       alc_set_input_pin(codec, nid, cfg->inputs[i].type);
                        if (nid != ALC861VD_PIN_CD_NID &&
                            (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
                                snd_hda_codec_write(codec, nid, 0,
@@ -16509,8 +17120,7 @@ static int alc861vd_parse_auto_config(struct hda_codec *codec)
 
        spec->multiout.max_channels = spec->multiout.num_dacs * 2;
 
-       if (spec->autocfg.dig_outs)
-               spec->multiout.dig_out_nid = ALC861VD_DIGOUT_NID;
+       alc_auto_parse_digital(codec);
 
        if (spec->kctls.list)
                add_mixer(spec, spec->kctls.list);
@@ -16537,6 +17147,7 @@ static void alc861vd_auto_init(struct hda_codec *codec)
        alc861vd_auto_init_hp_out(codec);
        alc861vd_auto_init_analog_input(codec);
        alc861vd_auto_init_input_src(codec);
+       alc_auto_init_digital(codec);
        if (spec->unsol_event)
                alc_inithook(codec);
 }
@@ -16546,16 +17157,14 @@ enum {
 };
 
 /* reset GPIO1 */
-static const struct hda_verb alc660vd_fix_asus_gpio1_verbs[] = {
-       {0x01, AC_VERB_SET_GPIO_MASK, 0x03},
-       {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01},
-       {0x01, AC_VERB_SET_GPIO_DATA, 0x01},
-       { }
-};
-
 static const struct alc_fixup alc861vd_fixups[] = {
        [ALC660VD_FIX_ASUS_GPIO1] = {
-               .verbs = alc660vd_fix_asus_gpio1_verbs,
+               .verbs = (const struct hda_verb[]) {
+                       {0x01, AC_VERB_SET_GPIO_MASK, 0x03},
+                       {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01},
+                       {0x01, AC_VERB_SET_GPIO_DATA, 0x01},
+                       { }
+               }
        },
 };
 
@@ -18520,7 +19129,7 @@ static void alc662_auto_set_output_and_unmute(struct hda_codec *codec,
                                              hda_nid_t dac)
 {
        int i, num;
-       hda_nid_t srcs[4];
+       hda_nid_t srcs[HDA_MAX_CONNECTIONS];
 
        alc_set_pin_output(codec, nid, pin_type);
        /* need the manual connection? */
@@ -18569,12 +19178,13 @@ static void alc662_auto_init_hp_out(struct hda_codec *codec)
 static void alc662_auto_init_analog_input(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
        int i;
 
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               hda_nid_t nid = spec->autocfg.input_pins[i];
+       for (i = 0; i < cfg->num_inputs; i++) {
+               hda_nid_t nid = cfg->inputs[i].pin;
                if (alc_is_input_pin(codec, nid)) {
-                       alc_set_input_pin(codec, nid, i);
+                       alc_set_input_pin(codec, nid, cfg->inputs[i].type);
                        if (nid != ALC662_PIN_CD_NID &&
                            (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
                                snd_hda_codec_write(codec, nid, 0,
@@ -18624,8 +19234,7 @@ static int alc662_parse_auto_config(struct hda_codec *codec)
 
        spec->multiout.max_channels = spec->multiout.num_dacs * 2;
 
-       if (spec->autocfg.dig_outs)
-               spec->multiout.dig_out_nid = ALC880_DIGOUT_NID;
+       alc_auto_parse_digital(codec);
 
        if (spec->kctls.list)
                add_mixer(spec, spec->kctls.list);
@@ -18635,7 +19244,7 @@ static int alc662_parse_auto_config(struct hda_codec *codec)
 
        add_verb(spec, alc662_init_verbs);
        if (codec->vendor_id == 0x10ec0272 || codec->vendor_id == 0x10ec0663 ||
-           codec->vendor_id == 0x10ec0665)
+           codec->vendor_id == 0x10ec0665 || codec->vendor_id == 0x10ec0670)
                add_verb(spec, alc663_init_verbs);
 
        if (codec->vendor_id == 0x10ec0272)
@@ -18662,10 +19271,40 @@ static void alc662_auto_init(struct hda_codec *codec)
        alc662_auto_init_hp_out(codec);
        alc662_auto_init_analog_input(codec);
        alc662_auto_init_input_src(codec);
+       alc_auto_init_digital(codec);
        if (spec->unsol_event)
                alc_inithook(codec);
 }
 
+enum {
+       ALC662_FIXUP_ASPIRE,
+       ALC662_FIXUP_IDEAPAD,
+};
+
+static const struct alc_fixup alc662_fixups[] = {
+       [ALC662_FIXUP_ASPIRE] = {
+               .pins = (const struct alc_pincfg[]) {
+                       { 0x15, 0x99130112 }, /* subwoofer */
+                       { }
+               }
+       },
+       [ALC662_FIXUP_IDEAPAD] = {
+               .pins = (const struct alc_pincfg[]) {
+                       { 0x17, 0x99130112 }, /* subwoofer */
+                       { }
+               }
+       },
+};
+
+static struct snd_pci_quirk alc662_fixup_tbl[] = {
+       SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE),
+       SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo Ideapad Y550P", ALC662_FIXUP_IDEAPAD),
+       SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Ideapad Y550", ALC662_FIXUP_IDEAPAD),
+       {}
+};
+
+
+
 static int patch_alc662(struct hda_codec *codec)
 {
        struct alc_spec *spec;
@@ -18698,6 +19337,7 @@ static int patch_alc662(struct hda_codec *codec)
        }
 
        if (board_config == ALC662_AUTO) {
+               alc_pick_fixup(codec, alc662_fixup_tbl, alc662_fixups, 1);
                /* automatic parse from the BIOS config */
                err = alc662_parse_auto_config(codec);
                if (err < 0) {
@@ -18756,8 +19396,13 @@ static int patch_alc662(struct hda_codec *codec)
        spec->vmaster_nid = 0x02;
 
        codec->patch_ops = alc_patch_ops;
-       if (board_config == ALC662_AUTO)
+       if (board_config == ALC662_AUTO) {
                spec->init_hook = alc662_auto_init;
+               alc_pick_fixup(codec, alc662_fixup_tbl, alc662_fixups, 0);
+       }
+
+       alc_init_jacks(codec);
+
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        if (!spec->loopback.amplist)
                spec->loopback.amplist = alc662_loopbacks;
@@ -18780,6 +19425,453 @@ static int patch_alc888(struct hda_codec *codec)
        return patch_alc882(codec);
 }
 
+/*
+ * ALC680 support
+ */
+#define ALC680_DIGIN_NID       ALC880_DIGIN_NID
+#define ALC680_DIGOUT_NID      ALC880_DIGOUT_NID
+#define alc680_modes           alc260_modes
+
+static hda_nid_t alc680_dac_nids[3] = {
+       /* Lout1, Lout2, hp */
+       0x02, 0x03, 0x04
+};
+
+static hda_nid_t alc680_adc_nids[3] = {
+       /* ADC0-2 */
+       /* DMIC, MIC, Line-in*/
+       0x07, 0x08, 0x09
+};
+
+/*
+ * Analog capture ADC cgange
+ */
+static void alc680_rec_autoswitch(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       int pin_found = 0;
+       int type_found = AUTO_PIN_LAST;
+       hda_nid_t nid;
+       int i;
+
+       for (i = 0; i < cfg->num_inputs; i++) {
+               nid = cfg->inputs[i].pin;
+               if (!(snd_hda_query_pin_caps(codec, nid) &
+                     AC_PINCAP_PRES_DETECT))
+                       continue;
+               if (snd_hda_jack_detect(codec, nid)) {
+                       if (cfg->inputs[i].type < type_found) {
+                               type_found = cfg->inputs[i].type;
+                               pin_found = nid;
+                       }
+               }
+       }
+
+       nid = 0x07;
+       if (pin_found)
+               snd_hda_get_connections(codec, pin_found, &nid, 1);
+
+       if (nid != spec->cur_adc)
+               __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
+       spec->cur_adc = nid;
+       snd_hda_codec_setup_stream(codec, nid, spec->cur_adc_stream_tag, 0,
+                                  spec->cur_adc_format);
+}
+
+static int alc680_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+                                     struct hda_codec *codec,
+                                     unsigned int stream_tag,
+                                     unsigned int format,
+                                     struct snd_pcm_substream *substream)
+{
+       struct alc_spec *spec = codec->spec;
+
+       spec->cur_adc = 0x07;
+       spec->cur_adc_stream_tag = stream_tag;
+       spec->cur_adc_format = format;
+
+       alc680_rec_autoswitch(codec);
+       return 0;
+}
+
+static int alc680_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                                     struct hda_codec *codec,
+                                     struct snd_pcm_substream *substream)
+{
+       snd_hda_codec_cleanup_stream(codec, 0x07);
+       snd_hda_codec_cleanup_stream(codec, 0x08);
+       snd_hda_codec_cleanup_stream(codec, 0x09);
+       return 0;
+}
+
+static struct hda_pcm_stream alc680_pcm_analog_auto_capture = {
+       .substreams = 1, /* can be overridden */
+       .channels_min = 2,
+       .channels_max = 2,
+       /* NID is set in alc_build_pcms */
+       .ops = {
+               .prepare = alc680_capture_pcm_prepare,
+               .cleanup = alc680_capture_pcm_cleanup
+       },
+};
+
+static struct snd_kcontrol_new alc680_base_mixer[] = {
+       /* output mixer control */
+       HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Headphone Playback Volume", 0x4, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Headphone Playback Switch", 0x16, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Int Mic Boost", 0x12, 0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Line In Boost", 0x19, 0, HDA_INPUT),
+       { }
+};
+
+static struct hda_bind_ctls alc680_bind_cap_vol = {
+       .ops = &snd_hda_bind_vol,
+       .values = {
+               HDA_COMPOSE_AMP_VAL(0x07, 3, 0, HDA_INPUT),
+               HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT),
+               HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT),
+               0
+       },
+};
+
+static struct hda_bind_ctls alc680_bind_cap_switch = {
+       .ops = &snd_hda_bind_sw,
+       .values = {
+               HDA_COMPOSE_AMP_VAL(0x07, 3, 0, HDA_INPUT),
+               HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT),
+               HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT),
+               0
+       },
+};
+
+static struct snd_kcontrol_new alc680_master_capture_mixer[] = {
+       HDA_BIND_VOL("Capture Volume", &alc680_bind_cap_vol),
+       HDA_BIND_SW("Capture Switch", &alc680_bind_cap_switch),
+       { } /* end */
+};
+
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static struct hda_verb alc680_init_verbs[] = {
+       {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+
+       {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+
+       {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT   | AC_USRSP_EN},
+       {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT  | AC_USRSP_EN},
+       {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT  | AC_USRSP_EN},
+
+       { }
+};
+
+/* toggle speaker-output according to the hp-jack state */
+static void alc680_base_setup(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+
+       spec->autocfg.hp_pins[0] = 0x16;
+       spec->autocfg.speaker_pins[0] = 0x14;
+       spec->autocfg.speaker_pins[1] = 0x15;
+       spec->autocfg.num_inputs = 2;
+       spec->autocfg.inputs[0].pin = 0x18;
+       spec->autocfg.inputs[0].type = AUTO_PIN_MIC;
+       spec->autocfg.inputs[1].pin = 0x19;
+       spec->autocfg.inputs[1].type = AUTO_PIN_LINE_IN;
+}
+
+static void alc680_unsol_event(struct hda_codec *codec,
+                                          unsigned int res)
+{
+       if ((res >> 26) == ALC880_HP_EVENT)
+               alc_automute_amp(codec);
+       if ((res >> 26) == ALC880_MIC_EVENT)
+               alc680_rec_autoswitch(codec);
+}
+
+static void alc680_inithook(struct hda_codec *codec)
+{
+       alc_automute_amp(codec);
+       alc680_rec_autoswitch(codec);
+}
+
+/* create input playback/capture controls for the given pin */
+static int alc680_new_analog_output(struct alc_spec *spec, hda_nid_t nid,
+                                   const char *ctlname, int idx)
+{
+       hda_nid_t dac;
+       int err;
+
+       switch (nid) {
+       case 0x14:
+               dac = 0x02;
+               break;
+       case 0x15:
+               dac = 0x03;
+               break;
+       case 0x16:
+               dac = 0x04;
+               break;
+       default:
+               return 0;
+       }
+       if (spec->multiout.dac_nids[0] != dac &&
+           spec->multiout.dac_nids[1] != dac) {
+               err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname,
+                                 HDA_COMPOSE_AMP_VAL(dac, 3, idx,
+                                                     HDA_OUTPUT));
+               if (err < 0)
+                       return err;
+
+               err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname,
+                         HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_OUTPUT));
+
+               if (err < 0)
+                       return err;
+               spec->multiout.dac_nids[spec->multiout.num_dacs++] = dac;
+       }
+
+       return 0;
+}
+
+/* add playback controls from the parsed DAC table */
+static int alc680_auto_create_multi_out_ctls(struct alc_spec *spec,
+                                            const struct auto_pin_cfg *cfg)
+{
+       hda_nid_t nid;
+       int err;
+
+       spec->multiout.dac_nids = spec->private_dac_nids;
+
+       nid = cfg->line_out_pins[0];
+       if (nid) {
+               const char *name;
+               if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
+                       name = "Speaker";
+               else
+                       name = "Front";
+               err = alc680_new_analog_output(spec, nid, name, 0);
+               if (err < 0)
+                       return err;
+       }
+
+       nid = cfg->speaker_pins[0];
+       if (nid) {
+               err = alc680_new_analog_output(spec, nid, "Speaker", 0);
+               if (err < 0)
+                       return err;
+       }
+       nid = cfg->hp_pins[0];
+       if (nid) {
+               err = alc680_new_analog_output(spec, nid, "Headphone", 0);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
+static void alc680_auto_set_output_and_unmute(struct hda_codec *codec,
+                                             hda_nid_t nid, int pin_type)
+{
+       alc_set_pin_output(codec, nid, pin_type);
+}
+
+static void alc680_auto_init_multi_out(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       hda_nid_t nid = spec->autocfg.line_out_pins[0];
+       if (nid) {
+               int pin_type = get_pin_type(spec->autocfg.line_out_type);
+               alc680_auto_set_output_and_unmute(codec, nid, pin_type);
+       }
+}
+
+static void alc680_auto_init_hp_out(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       hda_nid_t pin;
+
+       pin = spec->autocfg.hp_pins[0];
+       if (pin)
+               alc680_auto_set_output_and_unmute(codec, pin, PIN_HP);
+       pin = spec->autocfg.speaker_pins[0];
+       if (pin)
+               alc680_auto_set_output_and_unmute(codec, pin, PIN_OUT);
+}
+
+/* pcm configuration: identical with ALC880 */
+#define alc680_pcm_analog_playback     alc880_pcm_analog_playback
+#define alc680_pcm_analog_capture      alc880_pcm_analog_capture
+#define alc680_pcm_analog_alt_capture  alc880_pcm_analog_alt_capture
+#define alc680_pcm_digital_playback    alc880_pcm_digital_playback
+#define alc680_pcm_digital_capture     alc880_pcm_digital_capture
+
+/*
+ * BIOS auto configuration
+ */
+static int alc680_parse_auto_config(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       int err;
+       static hda_nid_t alc680_ignore[] = { 0 };
+
+       err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
+                                          alc680_ignore);
+       if (err < 0)
+               return err;
+
+       if (!spec->autocfg.line_outs) {
+               if (spec->autocfg.dig_outs || spec->autocfg.dig_in_pin) {
+                       spec->multiout.max_channels = 2;
+                       spec->no_analog = 1;
+                       goto dig_only;
+               }
+               return 0; /* can't find valid BIOS pin config */
+       }
+       err = alc680_auto_create_multi_out_ctls(spec, &spec->autocfg);
+       if (err < 0)
+               return err;
+
+       spec->multiout.max_channels = 2;
+
+ dig_only:
+       /* digital only support output */
+       alc_auto_parse_digital(codec);
+       if (spec->kctls.list)
+               add_mixer(spec, spec->kctls.list);
+
+       add_verb(spec, alc680_init_verbs);
+
+       err = alc_auto_add_mic_boost(codec);
+       if (err < 0)
+               return err;
+
+       return 1;
+}
+
+#define alc680_auto_init_analog_input  alc882_auto_init_analog_input
+
+/* init callback for auto-configuration model -- overriding the default init */
+static void alc680_auto_init(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       alc680_auto_init_multi_out(codec);
+       alc680_auto_init_hp_out(codec);
+       alc680_auto_init_analog_input(codec);
+       alc_auto_init_digital(codec);
+       if (spec->unsol_event)
+               alc_inithook(codec);
+}
+
+/*
+ * configuration and preset
+ */
+static const char *alc680_models[ALC680_MODEL_LAST] = {
+       [ALC680_BASE]           = "base",
+       [ALC680_AUTO]           = "auto",
+};
+
+static struct snd_pci_quirk alc680_cfg_tbl[] = {
+       SND_PCI_QUIRK(0x1043, 0x12f3, "ASUS NX90", ALC680_BASE),
+       {}
+};
+
+static struct alc_config_preset alc680_presets[] = {
+       [ALC680_BASE] = {
+               .mixers = { alc680_base_mixer },
+               .cap_mixer =  alc680_master_capture_mixer,
+               .init_verbs = { alc680_init_verbs },
+               .num_dacs = ARRAY_SIZE(alc680_dac_nids),
+               .dac_nids = alc680_dac_nids,
+               .dig_out_nid = ALC680_DIGOUT_NID,
+               .num_channel_mode = ARRAY_SIZE(alc680_modes),
+               .channel_mode = alc680_modes,
+               .unsol_event = alc680_unsol_event,
+               .setup = alc680_base_setup,
+               .init_hook = alc680_inithook,
+
+       },
+};
+
+static int patch_alc680(struct hda_codec *codec)
+{
+       struct alc_spec *spec;
+       int board_config;
+       int err;
+
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (spec == NULL)
+               return -ENOMEM;
+
+       codec->spec = spec;
+
+       board_config = snd_hda_check_board_config(codec, ALC680_MODEL_LAST,
+                                                 alc680_models,
+                                                 alc680_cfg_tbl);
+
+       if (board_config < 0 || board_config >= ALC680_MODEL_LAST) {
+               printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+                      codec->chip_name);
+               board_config = ALC680_AUTO;
+       }
+
+       if (board_config == ALC680_AUTO) {
+               /* automatic parse from the BIOS config */
+               err = alc680_parse_auto_config(codec);
+               if (err < 0) {
+                       alc_free(codec);
+                       return err;
+               } else if (!err) {
+                       printk(KERN_INFO
+                              "hda_codec: Cannot set up configuration "
+                              "from BIOS.  Using base mode...\n");
+                       board_config = ALC680_BASE;
+               }
+       }
+
+       if (board_config != ALC680_AUTO)
+               setup_preset(codec, &alc680_presets[board_config]);
+
+       spec->stream_analog_playback = &alc680_pcm_analog_playback;
+       spec->stream_analog_capture = &alc680_pcm_analog_auto_capture;
+       spec->stream_digital_playback = &alc680_pcm_digital_playback;
+       spec->stream_digital_capture = &alc680_pcm_digital_capture;
+
+       if (!spec->adc_nids) {
+               spec->adc_nids = alc680_adc_nids;
+               spec->num_adc_nids = ARRAY_SIZE(alc680_adc_nids);
+       }
+
+       if (!spec->cap_mixer)
+               set_capture_mixer(codec);
+
+       spec->vmaster_nid = 0x02;
+
+       codec->patch_ops = alc_patch_ops;
+       if (board_config == ALC680_AUTO)
+               spec->init_hook = alc680_auto_init;
+
+       return 0;
+}
+
 /*
  * patch entries
  */
@@ -18804,6 +19896,7 @@ static struct hda_codec_preset snd_hda_preset_realtek[] = {
        { .id = 0x10ec0663, .name = "ALC663", .patch = patch_alc662 },
        { .id = 0x10ec0665, .name = "ALC665", .patch = patch_alc662 },
        { .id = 0x10ec0670, .name = "ALC670", .patch = patch_alc662 },
+       { .id = 0x10ec0680, .name = "ALC680", .patch = patch_alc680 },
        { .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 },
        { .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 },
        { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc882 },