Merge branch 'fix/hda' into topic/hda
authorTakashi Iwai <tiwai@suse.de>
Wed, 29 Jun 2011 06:02:09 +0000 (08:02 +0200)
committerTakashi Iwai <tiwai@suse.de>
Wed, 29 Jun 2011 06:02:09 +0000 (08:02 +0200)
17 files changed:
sound/pci/hda/Kconfig
sound/pci/hda/Makefile
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_codec.h
sound/pci/hda/hda_eld.c
sound/pci/hda/hda_intel.c
sound/pci/hda/hda_local.h
sound/pci/hda/patch_analog.c
sound/pci/hda/patch_ca0110.c
sound/pci/hda/patch_ca0132.c [new file with mode: 0644]
sound/pci/hda/patch_cirrus.c
sound/pci/hda/patch_cmedia.c
sound/pci/hda/patch_conexant.c
sound/pci/hda/patch_hdmi.c
sound/pci/hda/patch_realtek.c
sound/pci/hda/patch_sigmatel.c
sound/pci/hda/patch_via.c

index 0ea5cc6..85217bd 100644 (file)
@@ -171,6 +171,19 @@ config SND_HDA_CODEC_CA0110
          snd-hda-codec-ca0110.
          This module is automatically loaded at probing.
 
+config SND_HDA_CODEC_CA0132
+       bool "Build Creative CA0132 codec support"
+       depends on SND_HDA_INTEL
+       default y
+       help
+         Say Y here to include Creative CA0132 codec support in
+         snd-hda-intel driver.
+
+         When the HD-audio driver is built as a module, the codec
+         support code is also built as another module,
+         snd-hda-codec-ca0132.
+         This module is automatically loaded at probing.
+
 config SND_HDA_CODEC_CMEDIA
        bool "Build C-Media HD-audio codec support"
        default y
index 17ef365..87365d5 100644 (file)
@@ -13,6 +13,7 @@ snd-hda-codec-idt-objs :=     patch_sigmatel.o
 snd-hda-codec-si3054-objs :=   patch_si3054.o
 snd-hda-codec-cirrus-objs :=   patch_cirrus.o
 snd-hda-codec-ca0110-objs :=   patch_ca0110.o
+snd-hda-codec-ca0132-objs :=   patch_ca0132.o
 snd-hda-codec-conexant-objs := patch_conexant.o
 snd-hda-codec-via-objs :=      patch_via.o
 snd-hda-codec-hdmi-objs :=     patch_hdmi.o hda_eld.o
@@ -42,6 +43,9 @@ endif
 ifdef CONFIG_SND_HDA_CODEC_CA0110
 obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-ca0110.o
 endif
+ifdef CONFIG_SND_HDA_CODEC_CA0132
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-ca0132.o
+endif
 ifdef CONFIG_SND_HDA_CODEC_CONEXANT
 obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-conexant.o
 endif
index 45b4a8d..7f85023 100644 (file)
@@ -243,7 +243,8 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
 {
        unsigned cmd = make_codec_cmd(codec, nid, direct, verb, parm);
        unsigned int res;
-       codec_exec_verb(codec, cmd, &res);
+       if (codec_exec_verb(codec, cmd, &res))
+               return -1;
        return res;
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_read);
@@ -310,35 +311,35 @@ EXPORT_SYMBOL_HDA(snd_hda_get_sub_nodes);
 static int _hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
                                hda_nid_t *conn_list, int max_conns);
 static bool add_conn_list(struct snd_array *array, hda_nid_t nid);
-static int copy_conn_list(hda_nid_t nid, hda_nid_t *dst, int max_dst,
-                         hda_nid_t *src, int len);
 
 /**
  * snd_hda_get_connections - get connection list
  * @codec: the HDA codec
  * @nid: NID to parse
- * @conn_list: connection list array
- * @max_conns: max. number of connections to store
+ * @listp: the pointer to store NID list
  *
  * Parses the connection list of the given widget and stores the list
  * of NIDs.
  *
  * Returns the number of connections, or a negative error code.
  */
-int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
-                            hda_nid_t *conn_list, int max_conns)
+int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid,
+                         const hda_nid_t **listp)
 {
        struct snd_array *array = &codec->conn_lists;
        int i, len, old_used;
        hda_nid_t list[HDA_MAX_CONNECTIONS];
+       hda_nid_t *p;
 
        /* look up the cached results */
        for (i = 0; i < array->used; ) {
-               hda_nid_t *p = snd_array_elem(array, i);
+               p = snd_array_elem(array, i);
                len = p[1];
-               if (nid == *p)
-                       return copy_conn_list(nid, conn_list, max_conns,
-                                             p + 2, len);
+               if (nid == *p) {
+                       if (listp)
+                               *listp = p + 2;
+                       return len;
+               }
                i += len + 2;
        }
 
@@ -354,12 +355,46 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
                if (!add_conn_list(array, list[i]))
                        goto error_add;
 
-       return copy_conn_list(nid, conn_list, max_conns, list, len);
+       p = snd_array_elem(array, old_used);
+       if (listp)
+               *listp = p + 2;
+       return len;
                
  error_add:
        array->used = old_used;
        return -ENOMEM;
 }
+EXPORT_SYMBOL_HDA(snd_hda_get_conn_list);
+
+/**
+ * snd_hda_get_connections - copy connection list
+ * @codec: the HDA codec
+ * @nid: NID to parse
+ * @conn_list: connection list array
+ * @max_conns: max. number of connections to store
+ *
+ * Parses the connection list of the given widget and stores the list
+ * of NIDs.
+ *
+ * Returns the number of connections, or a negative error code.
+ */
+int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
+                            hda_nid_t *conn_list, int max_conns)
+{
+       const hda_nid_t *list;
+       int len = snd_hda_get_conn_list(codec, nid, &list);
+
+       if (len <= 0)
+               return len;
+       if (len > max_conns) {
+               snd_printk(KERN_ERR "hda_codec: "
+                          "Too many connections %d for NID 0x%x\n",
+                          len, nid);
+               return -EINVAL;
+       }
+       memcpy(conn_list, list, len * sizeof(hda_nid_t));
+       return len;
+}
 EXPORT_SYMBOL_HDA(snd_hda_get_connections);
 
 static int _hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
@@ -376,11 +411,8 @@ static int _hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
 
        wcaps = get_wcaps(codec, nid);
        if (!(wcaps & AC_WCAP_CONN_LIST) &&
-           get_wcaps_type(wcaps) != AC_WID_VOL_KNB) {
-               snd_printk(KERN_WARNING "hda_codec: "
-                          "connection list not available for 0x%x\n", nid);
-               return -EINVAL;
-       }
+           get_wcaps_type(wcaps) != AC_WID_VOL_KNB)
+               return 0;
 
        parm = snd_hda_param_read(codec, nid, AC_PAR_CONNLIST_LEN);
        if (parm & AC_CLIST_LONG) {
@@ -470,18 +502,40 @@ static bool add_conn_list(struct snd_array *array, hda_nid_t nid)
        return true;
 }
 
-static int copy_conn_list(hda_nid_t nid, hda_nid_t *dst, int max_dst,
-                         hda_nid_t *src, int len)
+/**
+ * snd_hda_get_conn_index - get the connection index of the given NID
+ * @codec: the HDA codec
+ * @mux: NID containing the list
+ * @nid: NID to select
+ * @recursive: 1 when searching NID recursively, otherwise 0
+ *
+ * Parses the connection list of the widget @mux and checks whether the
+ * widget @nid is present.  If it is, return the connection index.
+ * Otherwise it returns -1.
+ */
+int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
+                          hda_nid_t nid, int recursive)
 {
-       if (len > max_dst) {
-               snd_printk(KERN_ERR "hda_codec: "
-                          "Too many connections %d for NID 0x%x\n",
-                          len, nid);
-               return -EINVAL;
+       hda_nid_t conn[HDA_MAX_NUM_INPUTS];
+       int i, nums;
+
+       nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
+       for (i = 0; i < nums; i++)
+               if (conn[i] == nid)
+                       return i;
+       if (!recursive)
+               return -1;
+       if (recursive > 5) {
+               snd_printd("hda_codec: too deep connection for 0x%x\n", nid);
+               return -1;
        }
-       memcpy(dst, src, len * sizeof(hda_nid_t));
-       return len;
+       recursive++;
+       for (i = 0; i < nums; i++)
+               if (snd_hda_get_conn_index(codec, conn[i], nid, recursive) >= 0)
+                       return i;
+       return -1;
 }
+EXPORT_SYMBOL_HDA(snd_hda_get_conn_index);
 
 /**
  * snd_hda_queue_unsol_event - add an unsolicited event to queue
@@ -1083,6 +1137,7 @@ static void snd_hda_codec_free(struct hda_codec *codec)
        snd_array_free(&codec->mixers);
        snd_array_free(&codec->nids);
        snd_array_free(&codec->conn_lists);
+       snd_array_free(&codec->spdif_out);
        codec->bus->caddr_tbl[codec->addr] = NULL;
        if (codec->patch_ops.free)
                codec->patch_ops.free(codec);
@@ -1144,6 +1199,7 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus,
        snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16);
        snd_array_init(&codec->cvt_setups, sizeof(struct hda_cvt_setup), 8);
        snd_array_init(&codec->conn_lists, sizeof(hda_nid_t), 64);
+       snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16);
        if (codec->bus->modelname) {
                codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL);
                if (!codec->modelname) {
@@ -2555,11 +2611,13 @@ static int snd_hda_spdif_default_get(struct snd_kcontrol *kcontrol,
                                     struct snd_ctl_elem_value *ucontrol)
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       int idx = kcontrol->private_value;
+       struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx);
 
-       ucontrol->value.iec958.status[0] = codec->spdif_status & 0xff;
-       ucontrol->value.iec958.status[1] = (codec->spdif_status >> 8) & 0xff;
-       ucontrol->value.iec958.status[2] = (codec->spdif_status >> 16) & 0xff;
-       ucontrol->value.iec958.status[3] = (codec->spdif_status >> 24) & 0xff;
+       ucontrol->value.iec958.status[0] = spdif->status & 0xff;
+       ucontrol->value.iec958.status[1] = (spdif->status >> 8) & 0xff;
+       ucontrol->value.iec958.status[2] = (spdif->status >> 16) & 0xff;
+       ucontrol->value.iec958.status[3] = (spdif->status >> 24) & 0xff;
 
        return 0;
 }
@@ -2644,23 +2702,23 @@ static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol,
                                     struct snd_ctl_elem_value *ucontrol)
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       hda_nid_t nid = kcontrol->private_value;
+       int idx = kcontrol->private_value;
+       struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx);
+       hda_nid_t nid = spdif->nid;
        unsigned short val;
        int change;
 
        mutex_lock(&codec->spdif_mutex);
-       codec->spdif_status = ucontrol->value.iec958.status[0] |
+       spdif->status = ucontrol->value.iec958.status[0] |
                ((unsigned int)ucontrol->value.iec958.status[1] << 8) |
                ((unsigned int)ucontrol->value.iec958.status[2] << 16) |
                ((unsigned int)ucontrol->value.iec958.status[3] << 24);
-       val = convert_from_spdif_status(codec->spdif_status);
-       val |= codec->spdif_ctls & 1;
-       change = codec->spdif_ctls != val;
-       codec->spdif_ctls = val;
-
-       if (change)
+       val = convert_from_spdif_status(spdif->status);
+       val |= spdif->ctls & 1;
+       change = spdif->ctls != val;
+       spdif->ctls = val;
+       if (change && nid != (u16)-1)
                set_dig_out_convert(codec, nid, val & 0xff, (val >> 8) & 0xff);
-
        mutex_unlock(&codec->spdif_mutex);
        return change;
 }
@@ -2671,33 +2729,42 @@ static int snd_hda_spdif_out_switch_get(struct snd_kcontrol *kcontrol,
                                        struct snd_ctl_elem_value *ucontrol)
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       int idx = kcontrol->private_value;
+       struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx);
 
-       ucontrol->value.integer.value[0] = codec->spdif_ctls & AC_DIG1_ENABLE;
+       ucontrol->value.integer.value[0] = spdif->ctls & AC_DIG1_ENABLE;
        return 0;
 }
 
+static inline void set_spdif_ctls(struct hda_codec *codec, hda_nid_t nid,
+                                 int dig1, int dig2)
+{
+       set_dig_out_convert(codec, nid, dig1, dig2);
+       /* unmute amp switch (if any) */
+       if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) &&
+           (dig1 & AC_DIG1_ENABLE))
+               snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
+                                           HDA_AMP_MUTE, 0);
+}
+
 static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol,
                                        struct snd_ctl_elem_value *ucontrol)
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       hda_nid_t nid = kcontrol->private_value;
+       int idx = kcontrol->private_value;
+       struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx);
+       hda_nid_t nid = spdif->nid;
        unsigned short val;
        int change;
 
        mutex_lock(&codec->spdif_mutex);
-       val = codec->spdif_ctls & ~AC_DIG1_ENABLE;
+       val = spdif->ctls & ~AC_DIG1_ENABLE;
        if (ucontrol->value.integer.value[0])
                val |= AC_DIG1_ENABLE;
-       change = codec->spdif_ctls != val;
-       if (change) {
-               codec->spdif_ctls = val;
-               set_dig_out_convert(codec, nid, val & 0xff, -1);
-               /* unmute amp switch (if any) */
-               if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) &&
-                   (val & AC_DIG1_ENABLE))
-                       snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
-                                                HDA_AMP_MUTE, 0);
-       }
+       change = spdif->ctls != val;
+       spdif->ctls = val;
+       if (change && nid != (u16)-1)
+               set_spdif_ctls(codec, nid, val & 0xff, -1);
        mutex_unlock(&codec->spdif_mutex);
        return change;
 }
@@ -2744,36 +2811,79 @@ static struct snd_kcontrol_new dig_mixes[] = {
  *
  * Returns 0 if successful, or a negative error code.
  */
-int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
+int snd_hda_create_spdif_out_ctls(struct hda_codec *codec,
+                                 hda_nid_t associated_nid,
+                                 hda_nid_t cvt_nid)
 {
        int err;
        struct snd_kcontrol *kctl;
        struct snd_kcontrol_new *dig_mix;
        int idx;
+       struct hda_spdif_out *spdif;
 
        idx = find_empty_mixer_ctl_idx(codec, "IEC958 Playback Switch");
        if (idx < 0) {
                printk(KERN_ERR "hda_codec: too many IEC958 outputs\n");
                return -EBUSY;
        }
+       spdif = snd_array_new(&codec->spdif_out);
        for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) {
                kctl = snd_ctl_new1(dig_mix, codec);
                if (!kctl)
                        return -ENOMEM;
                kctl->id.index = idx;
-               kctl->private_value = nid;
-               err = snd_hda_ctl_add(codec, nid, kctl);
+               kctl->private_value = codec->spdif_out.used - 1;
+               err = snd_hda_ctl_add(codec, associated_nid, kctl);
                if (err < 0)
                        return err;
        }
-       codec->spdif_ctls =
-               snd_hda_codec_read(codec, nid, 0,
-                                  AC_VERB_GET_DIGI_CONVERT_1, 0);
-       codec->spdif_status = convert_to_spdif_status(codec->spdif_ctls);
+       spdif->nid = cvt_nid;
+       spdif->ctls = snd_hda_codec_read(codec, cvt_nid, 0,
+                                        AC_VERB_GET_DIGI_CONVERT_1, 0);
+       spdif->status = convert_to_spdif_status(spdif->ctls);
        return 0;
 }
 EXPORT_SYMBOL_HDA(snd_hda_create_spdif_out_ctls);
 
+struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec,
+                                              hda_nid_t nid)
+{
+       int i;
+       for (i = 0; i < codec->spdif_out.used; i++) {
+               struct hda_spdif_out *spdif =
+                               snd_array_elem(&codec->spdif_out, i);
+               if (spdif->nid == nid)
+                       return spdif;
+       }
+       return NULL;
+}
+EXPORT_SYMBOL_HDA(snd_hda_spdif_out_of_nid);
+
+void snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int idx)
+{
+       struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx);
+
+       mutex_lock(&codec->spdif_mutex);
+       spdif->nid = (u16)-1;
+       mutex_unlock(&codec->spdif_mutex);
+}
+EXPORT_SYMBOL_HDA(snd_hda_spdif_ctls_unassign);
+
+void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid)
+{
+       struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx);
+       unsigned short val;
+
+       mutex_lock(&codec->spdif_mutex);
+       if (spdif->nid != nid) {
+               spdif->nid = nid;
+               val = spdif->ctls;
+               set_spdif_ctls(codec, nid, val & 0xff, (val >> 8) & 0xff);
+       }
+       mutex_unlock(&codec->spdif_mutex);
+}
+EXPORT_SYMBOL_HDA(snd_hda_spdif_ctls_assign);
+
 /*
  * SPDIF sharing with analog output
  */
@@ -3356,7 +3466,7 @@ static unsigned int query_stream_param(struct hda_codec *codec, hda_nid_t nid)
  *
  * Returns 0 if successful, otherwise a negative error code.
  */
-static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
+int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
                                u32 *ratesp, u64 *formatsp, unsigned int *bpsp)
 {
        unsigned int i, val, wcaps;
@@ -3448,6 +3558,7 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
 
        return 0;
 }
+EXPORT_SYMBOL_HDA(snd_hda_query_supported_pcm);
 
 /**
  * snd_hda_is_supported_format - Check the validity of the format
@@ -4177,10 +4288,12 @@ EXPORT_SYMBOL_HDA(snd_hda_input_mux_put);
 static void setup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid,
                                 unsigned int stream_tag, unsigned int format)
 {
+       struct hda_spdif_out *spdif = snd_hda_spdif_out_of_nid(codec, nid);
+
        /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
-       if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
+       if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE))
                set_dig_out_convert(codec, nid,
-                                   codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff,
+                                   spdif->ctls & ~AC_DIG1_ENABLE & 0xff,
                                    -1);
        snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
        if (codec->slave_dig_outs) {
@@ -4190,9 +4303,9 @@ static void setup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid,
                                                   format);
        }
        /* turn on again (if needed) */
-       if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
+       if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE))
                set_dig_out_convert(codec, nid,
-                                   codec->spdif_ctls & 0xff, -1);
+                                   spdif->ctls & 0xff, -1);
 }
 
 static void cleanup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid)
@@ -4348,6 +4461,8 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
 {
        const hda_nid_t *nids = mout->dac_nids;
        int chs = substream->runtime->channels;
+       struct hda_spdif_out *spdif =
+                       snd_hda_spdif_out_of_nid(codec, mout->dig_out_nid);
        int i;
 
        mutex_lock(&codec->spdif_mutex);
@@ -4356,7 +4471,7 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
                if (chs == 2 &&
                    snd_hda_is_supported_format(codec, mout->dig_out_nid,
                                                format) &&
-                   !(codec->spdif_status & IEC958_AES0_NONAUDIO)) {
+                   !(spdif->status & IEC958_AES0_NONAUDIO)) {
                        mout->dig_out_used = HDA_DIG_ANALOG_DUP;
                        setup_dig_out_stream(codec, mout->dig_out_nid,
                                             stream_tag, format);
@@ -4528,7 +4643,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
                unsigned int wid_caps = get_wcaps(codec, nid);
                unsigned int wid_type = get_wcaps_type(wid_caps);
                unsigned int def_conf;
-               short assoc, loc;
+               short assoc, loc, conn, dev;
 
                /* read all default configuration for pin complex */
                if (wid_type != AC_WID_PIN)
@@ -4538,10 +4653,19 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
                        continue;
 
                def_conf = snd_hda_codec_get_pincfg(codec, nid);
-               if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
+               conn = get_defcfg_connect(def_conf);
+               if (conn == AC_JACK_PORT_NONE)
                        continue;
                loc = get_defcfg_location(def_conf);
-               switch (get_defcfg_device(def_conf)) {
+               dev = get_defcfg_device(def_conf);
+
+               /* workaround for buggy BIOS setups */
+               if (dev == AC_JACK_LINE_OUT) {
+                       if (conn == AC_JACK_PORT_FIXED)
+                               dev = AC_JACK_SPEAKER;
+               }
+
+               switch (dev) {
                case AC_JACK_LINE_OUT:
                        seq = get_defcfg_sequence(def_conf);
                        assoc = get_defcfg_association(def_conf);
index 59c9730..10d500d 100644 (file)
@@ -829,8 +829,7 @@ struct hda_codec {
 
        struct mutex spdif_mutex;
        struct mutex control_mutex;
-       unsigned int spdif_status;      /* IEC958 status bits */
-       unsigned short spdif_ctls;      /* SPDIF control bits */
+       struct snd_array spdif_out;
        unsigned int spdif_in_enable;   /* SPDIF input enable? */
        const hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */
        struct snd_array init_pins;     /* initial (BIOS) pin configurations */
@@ -904,6 +903,12 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid,
                          hda_nid_t *start_id);
 int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
                            hda_nid_t *conn_list, int max_conns);
+int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid,
+                         const hda_nid_t **listp);
+int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
+                          hda_nid_t nid, int recursive);
+int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
+                               u32 *ratesp, u64 *formatsp, unsigned int *bpsp);
 
 struct hda_verb {
        hda_nid_t nid;
@@ -947,6 +952,17 @@ int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list,
                       hda_nid_t nid, unsigned int cfg); /* for hwdep */
 void snd_hda_shutup_pins(struct hda_codec *codec);
 
+/* SPDIF controls */
+struct hda_spdif_out {
+       hda_nid_t nid;          /* Converter nid values relate to */
+       unsigned int status;    /* IEC958 status bits */
+       unsigned short ctls;    /* SPDIF control bits */
+};
+struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec,
+                                              hda_nid_t nid);
+void snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int idx);
+void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid);
+
 /*
  * Mixer
  */
@@ -997,17 +1013,15 @@ int snd_hda_suspend(struct hda_bus *bus);
 int snd_hda_resume(struct hda_bus *bus);
 #endif
 
-#ifdef CONFIG_SND_HDA_POWER_SAVE
 static inline
 int hda_call_check_power_status(struct hda_codec *codec, hda_nid_t nid)
 {
+#ifdef CONFIG_SND_HDA_POWER_SAVE
        if (codec->patch_ops.check_power_status)
                return codec->patch_ops.check_power_status(codec, nid);
+#endif
        return 0;
 }
-#else  
-#define hda_call_check_power_status(codec, nid)                0
-#endif
 
 /*
  * get widget information
index b05f7be..473cfa1 100644 (file)
@@ -580,43 +580,45 @@ void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld)
 #endif /* CONFIG_PROC_FS */
 
 /* update PCM info based on ELD */
-void hdmi_eld_update_pcm_info(struct hdmi_eld *eld, struct hda_pcm_stream *pcm,
-                             struct hda_pcm_stream *codec_pars)
+void snd_hdmi_eld_update_pcm_info(struct hdmi_eld *eld,
+                             struct hda_pcm_stream *hinfo)
 {
+       u32 rates;
+       u64 formats;
+       unsigned int maxbps;
+       unsigned int channels_max;
        int i;
 
        /* assume basic audio support (the basic audio flag is not in ELD;
         * however, all audio capable sinks are required to support basic
         * audio) */
-       pcm->rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000;
-       pcm->formats = SNDRV_PCM_FMTBIT_S16_LE;
-       pcm->maxbps = 16;
-       pcm->channels_max = 2;
+       rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+               SNDRV_PCM_RATE_48000;
+       formats = SNDRV_PCM_FMTBIT_S16_LE;
+       maxbps = 16;
+       channels_max = 2;
        for (i = 0; i < eld->sad_count; i++) {
                struct cea_sad *a = &eld->sad[i];
-               pcm->rates |= a->rates;
-               if (a->channels > pcm->channels_max)
-                       pcm->channels_max = a->channels;
+               rates |= a->rates;
+               if (a->channels > channels_max)
+                       channels_max = a->channels;
                if (a->format == AUDIO_CODING_TYPE_LPCM) {
                        if (a->sample_bits & AC_SUPPCM_BITS_20) {
-                               pcm->formats |= SNDRV_PCM_FMTBIT_S32_LE;
-                               if (pcm->maxbps < 20)
-                                       pcm->maxbps = 20;
+                               formats |= SNDRV_PCM_FMTBIT_S32_LE;
+                               if (maxbps < 20)
+                                       maxbps = 20;
                        }
                        if (a->sample_bits & AC_SUPPCM_BITS_24) {
-                               pcm->formats |= SNDRV_PCM_FMTBIT_S32_LE;
-                               if (pcm->maxbps < 24)
-                                       pcm->maxbps = 24;
+                               formats |= SNDRV_PCM_FMTBIT_S32_LE;
+                               if (maxbps < 24)
+                                       maxbps = 24;
                        }
                }
        }
 
-       if (!codec_pars)
-               return;
-
        /* restrict the parameters by the values the codec provides */
-       pcm->rates &= codec_pars->rates;
-       pcm->formats &= codec_pars->formats;
-       pcm->channels_max = min(pcm->channels_max, codec_pars->channels_max);
-       pcm->maxbps = min(pcm->maxbps, codec_pars->maxbps);
+       hinfo->rates &= rates;
+       hinfo->formats &= formats;
+       hinfo->maxbps = min(hinfo->maxbps, maxbps);
+       hinfo->channels_max = min(hinfo->channels_max, channels_max);
 }
index 486f6de..25619cd 100644 (file)
@@ -177,7 +177,8 @@ MODULE_DESCRIPTION("Intel HDA driver");
 #define ICH6_REG_INTCTL                        0x20
 #define ICH6_REG_INTSTS                        0x24
 #define ICH6_REG_WALLCLK               0x30    /* 24Mhz source */
-#define ICH6_REG_SYNC                  0x34    
+#define ICH6_REG_OLD_SSYNC             0x34    /* SSYNC for old ICH */
+#define ICH6_REG_SSYNC                 0x38
 #define ICH6_REG_CORBLBASE             0x40
 #define ICH6_REG_CORBUBASE             0x44
 #define ICH6_REG_CORBWP                        0x48
@@ -479,6 +480,7 @@ enum {
 #define AZX_DCAPS_POSFIX_VIA   (1 << 17)       /* Use VIACOMBO as default */
 #define AZX_DCAPS_NO_64BIT     (1 << 18)       /* No 64bit address */
 #define AZX_DCAPS_SYNC_WRITE   (1 << 19)       /* sync each cmd write */
+#define AZX_DCAPS_OLD_SSYNC    (1 << 20)       /* Old SSYNC reg for ICH */
 
 /* quirks for ATI SB / AMD Hudson */
 #define AZX_DCAPS_PRESET_ATI_SB \
@@ -1706,13 +1708,16 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
        struct snd_pcm_runtime *runtime = substream->runtime;
        unsigned int bufsize, period_bytes, format_val, stream_tag;
        int err;
+       struct hda_spdif_out *spdif =
+               snd_hda_spdif_out_of_nid(apcm->codec, hinfo->nid);
+       unsigned short ctls = spdif ? spdif->ctls : 0;
 
        azx_stream_reset(chip, azx_dev);
        format_val = snd_hda_calc_stream_format(runtime->rate,
                                                runtime->channels,
                                                runtime->format,
                                                hinfo->maxbps,
-                                               apcm->codec->spdif_ctls);
+                                               ctls);
        if (!format_val) {
                snd_printk(KERN_ERR SFX
                           "invalid format_val, rate=%d, ch=%d, format=%d\n",
@@ -1792,7 +1797,11 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
        spin_lock(&chip->reg_lock);
        if (nsync > 1) {
                /* first, set SYNC bits of corresponding streams */
-               azx_writel(chip, SYNC, azx_readl(chip, SYNC) | sbits);
+               if (chip->driver_caps & AZX_DCAPS_OLD_SSYNC)
+                       azx_writel(chip, OLD_SSYNC,
+                                  azx_readl(chip, OLD_SSYNC) | sbits);
+               else
+                       azx_writel(chip, SSYNC, azx_readl(chip, SSYNC) | sbits);
        }
        snd_pcm_group_for_each_entry(s, substream) {
                if (s->pcm->card != substream->pcm->card)
@@ -1848,7 +1857,11 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
        if (nsync > 1) {
                spin_lock(&chip->reg_lock);
                /* reset SYNC bits */
-               azx_writel(chip, SYNC, azx_readl(chip, SYNC) & ~sbits);
+               if (chip->driver_caps & AZX_DCAPS_OLD_SSYNC)
+                       azx_writel(chip, OLD_SSYNC,
+                                  azx_readl(chip, OLD_SSYNC) & ~sbits);
+               else
+                       azx_writel(chip, SSYNC, azx_readl(chip, SSYNC) & ~sbits);
                spin_unlock(&chip->reg_lock);
        }
        return 0;
@@ -1863,7 +1876,7 @@ static unsigned int azx_via_get_position(struct azx *chip,
        unsigned int fifo_size;
 
        link_pos = azx_sd_readl(azx_dev, SD_LPIB);
-       if (azx_dev->index >= 4) {
+       if (azx_dev->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
                /* Playback, no problem using link position */
                return link_pos;
        }
@@ -1927,6 +1940,17 @@ static unsigned int azx_get_position(struct azx *chip,
        default:
                /* use the position buffer */
                pos = le32_to_cpu(*azx_dev->posbuf);
+               if (chip->position_fix[stream] == POS_FIX_AUTO) {
+                       if (!pos || pos == (u32)-1) {
+                               printk(KERN_WARNING
+                                      "hda-intel: Invalid position buffer, "
+                                      "using LPIB read method instead.\n");
+                               chip->position_fix[stream] = POS_FIX_LPIB;
+                               pos = azx_sd_readl(azx_dev, SD_LPIB);
+                       } else
+                               chip->position_fix[stream] = POS_FIX_POSBUF;
+               }
+               break;
        }
 
        if (pos >= azx_dev->bufsize)
@@ -1964,16 +1988,6 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev)
 
        stream = azx_dev->substream->stream;
        pos = azx_get_position(chip, azx_dev);
-       if (chip->position_fix[stream] == POS_FIX_AUTO) {
-               if (!pos) {
-                       printk(KERN_WARNING
-                              "hda-intel: Invalid position buffer, "
-                              "using LPIB read method instead.\n");
-                       chip->position_fix[stream] = POS_FIX_LPIB;
-                       pos = azx_get_position(chip, azx_dev);
-               } else
-                       chip->position_fix[stream] = POS_FIX_POSBUF;
-       }
 
        if (WARN_ONCE(!azx_dev->period_bytes,
                      "hda-intel: zero azx_dev->period_bytes"))
@@ -2347,28 +2361,20 @@ static int azx_dev_free(struct snd_device *device)
  * white/black-listing for position_fix
  */
 static struct snd_pci_quirk position_fix_list[] __devinitdata = {
-       SND_PCI_QUIRK(0x1025, 0x009f, "Acer Aspire 5110", POS_FIX_LPIB),
-       SND_PCI_QUIRK(0x1025, 0x026f, "Acer Aspire 5538", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", POS_FIX_LPIB),
-       SND_PCI_QUIRK(0x1028, 0x01f6, "Dell Latitude 131L", POS_FIX_LPIB),
-       SND_PCI_QUIRK(0x1028, 0x0470, "Dell Inspiron 1120", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x103c, 0x306d, "HP dv3", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS M2V", POS_FIX_LPIB),
-       SND_PCI_QUIRK(0x1043, 0x8410, "ASUS", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x104d, 0x9069, "Sony VPCS11V9E", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x1106, 0x3288, "ASUS M2V-MX SE", POS_FIX_LPIB),
-       SND_PCI_QUIRK(0x1179, 0xff10, "Toshiba A100-259", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x1297, 0x3166, "Shuttle", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x1458, 0xa022, "ga-ma770-ud3", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x1462, 0x1002, "MSI Wind U115", POS_FIX_LPIB),
-       SND_PCI_QUIRK(0x1565, 0x820f, "Biostar Microtech", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x1565, 0x8218, "Biostar Microtech", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x1849, 0x0888, "775Dual-VSTA", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x8086, 0x2503, "DG965OT AAD63733-203", POS_FIX_LPIB),
-       SND_PCI_QUIRK(0x8086, 0xd601, "eMachines T5212", POS_FIX_LPIB),
        {}
 };
 
@@ -2815,6 +2821,22 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
        /* SCH */
        { PCI_DEVICE(0x8086, 0x811b),
          .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_SCH_SNOOP },
+       { PCI_DEVICE(0x8086, 0x2668),
+         .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC },  /* ICH6 */
+       { PCI_DEVICE(0x8086, 0x27d8),
+         .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC },  /* ICH7 */
+       { PCI_DEVICE(0x8086, 0x269a),
+         .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC },  /* ESB2 */
+       { PCI_DEVICE(0x8086, 0x284b),
+         .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC },  /* ICH8 */
+       { PCI_DEVICE(0x8086, 0x293e),
+         .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC },  /* ICH9 */
+       { PCI_DEVICE(0x8086, 0x293f),
+         .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC },  /* ICH9 */
+       { PCI_DEVICE(0x8086, 0x3a3e),
+         .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC },  /* ICH10 */
+       { PCI_DEVICE(0x8086, 0x3a6e),
+         .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC },  /* ICH10 */
        /* Generic Intel */
        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_ANY_ID),
          .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
index 08ec073..88b277e 100644 (file)
@@ -212,7 +212,9 @@ int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag,
 /*
  * SPDIF I/O
  */
-int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid);
+int snd_hda_create_spdif_out_ctls(struct hda_codec *codec,
+                                 hda_nid_t associated_nid,
+                                 hda_nid_t cvt_nid);
 int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid);
 
 /*
@@ -563,7 +565,6 @@ int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key)
  * power-management
  */
 
-#ifdef CONFIG_SND_HDA_POWER_SAVE
 void snd_hda_schedule_power_save(struct hda_codec *codec);
 
 struct hda_amp_list {
@@ -580,7 +581,6 @@ struct hda_loopback_check {
 int snd_hda_check_amp_list_power(struct hda_codec *codec,
                                 struct hda_loopback_check *check,
                                 hda_nid_t nid);
-#endif /* CONFIG_SND_HDA_POWER_SAVE */
 
 /*
  * AMP control callbacks
@@ -639,8 +639,8 @@ struct hdmi_eld {
 int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid);
 int snd_hdmi_get_eld(struct hdmi_eld *, struct hda_codec *, hda_nid_t);
 void snd_hdmi_show_eld(struct hdmi_eld *eld);
-void hdmi_eld_update_pcm_info(struct hdmi_eld *eld, struct hda_pcm_stream *pcm,
-                             struct hda_pcm_stream *codec_pars);
+void snd_hdmi_eld_update_pcm_info(struct hdmi_eld *eld,
+                             struct hda_pcm_stream *hinfo);
 
 #ifdef CONFIG_PROC_FS
 int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld,
index d694e9d..1362c8b 100644 (file)
@@ -213,7 +213,9 @@ static int ad198x_build_controls(struct hda_codec *codec)
                        return err;
        }
        if (spec->multiout.dig_out_nid) {
-               err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
+               err = snd_hda_create_spdif_out_ctls(codec,
+                                                   spec->multiout.dig_out_nid,
+                                                   spec->multiout.dig_out_nid);
                if (err < 0)
                        return err;
                err = snd_hda_create_spdif_share_sw(codec,
@@ -1920,7 +1922,8 @@ static int patch_ad1981(struct hda_codec *codec)
                spec->mixers[0] = ad1981_hp_mixers;
                spec->num_init_verbs = 2;
                spec->init_verbs[1] = ad1981_hp_init_verbs;
-               spec->multiout.dig_out_nid = 0;
+               if (!is_jack_available(codec, 0x0a))
+                       spec->multiout.dig_out_nid = 0;
                spec->input_mux = &ad1981_hp_capture_source;
 
                codec->patch_ops.init = ad1981_hp_init;
index 61b9263..6b40684 100644 (file)
@@ -240,7 +240,8 @@ static int ca0110_build_controls(struct hda_codec *codec)
        }
 
        if (spec->dig_out) {
-               err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out);
+               err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out,
+                                                   spec->dig_out);
                if (err < 0)
                        return err;
                err = snd_hda_create_spdif_share_sw(codec, &spec->multiout);
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
new file mode 100644 (file)
index 0000000..d9a2254
--- /dev/null
@@ -0,0 +1,1097 @@
+/*
+ * HD audio interface patch for Creative CA0132 chip
+ *
+ * Copyright (c) 2011, Creative Technology Ltd.
+ *
+ * Based on patch_ca0110.c
+ * Copyright (c) 2008 Takashi Iwai <tiwai@suse.de>
+ *
+ *  This driver is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This driver is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/mutex.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+#define WIDGET_CHIP_CTRL      0x15
+#define WIDGET_DSP_CTRL       0x16
+
+#define WUH_MEM_CONNID        10
+#define DSP_MEM_CONNID        16
+
+enum hda_cmd_vendor_io {
+       /* for DspIO node */
+       VENDOR_DSPIO_SCP_WRITE_DATA_LOW      = 0x000,
+       VENDOR_DSPIO_SCP_WRITE_DATA_HIGH     = 0x100,
+
+       VENDOR_DSPIO_STATUS                  = 0xF01,
+       VENDOR_DSPIO_SCP_POST_READ_DATA      = 0x702,
+       VENDOR_DSPIO_SCP_READ_DATA           = 0xF02,
+       VENDOR_DSPIO_DSP_INIT                = 0x703,
+       VENDOR_DSPIO_SCP_POST_COUNT_QUERY    = 0x704,
+       VENDOR_DSPIO_SCP_READ_COUNT          = 0xF04,
+
+       /* for ChipIO node */
+       VENDOR_CHIPIO_ADDRESS_LOW            = 0x000,
+       VENDOR_CHIPIO_ADDRESS_HIGH           = 0x100,
+       VENDOR_CHIPIO_STREAM_FORMAT          = 0x200,
+       VENDOR_CHIPIO_DATA_LOW               = 0x300,
+       VENDOR_CHIPIO_DATA_HIGH              = 0x400,
+
+       VENDOR_CHIPIO_GET_PARAMETER          = 0xF00,
+       VENDOR_CHIPIO_STATUS                 = 0xF01,
+       VENDOR_CHIPIO_HIC_POST_READ          = 0x702,
+       VENDOR_CHIPIO_HIC_READ_DATA          = 0xF03,
+
+       VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE   = 0x70A,
+
+       VENDOR_CHIPIO_PLL_PMU_WRITE          = 0x70C,
+       VENDOR_CHIPIO_PLL_PMU_READ           = 0xF0C,
+       VENDOR_CHIPIO_8051_ADDRESS_LOW       = 0x70D,
+       VENDOR_CHIPIO_8051_ADDRESS_HIGH      = 0x70E,
+       VENDOR_CHIPIO_FLAG_SET               = 0x70F,
+       VENDOR_CHIPIO_FLAGS_GET              = 0xF0F,
+       VENDOR_CHIPIO_PARAMETER_SET          = 0x710,
+       VENDOR_CHIPIO_PARAMETER_GET          = 0xF10,
+
+       VENDOR_CHIPIO_PORT_ALLOC_CONFIG_SET  = 0x711,
+       VENDOR_CHIPIO_PORT_ALLOC_SET         = 0x712,
+       VENDOR_CHIPIO_PORT_ALLOC_GET         = 0xF12,
+       VENDOR_CHIPIO_PORT_FREE_SET          = 0x713,
+
+       VENDOR_CHIPIO_PARAMETER_EX_ID_GET    = 0xF17,
+       VENDOR_CHIPIO_PARAMETER_EX_ID_SET    = 0x717,
+       VENDOR_CHIPIO_PARAMETER_EX_VALUE_GET = 0xF18,
+       VENDOR_CHIPIO_PARAMETER_EX_VALUE_SET = 0x718
+};
+
+/*
+ *  Control flag IDs
+ */
+enum control_flag_id {
+       /* Connection manager stream setup is bypassed/enabled */
+       CONTROL_FLAG_C_MGR                  = 0,
+       /* DSP DMA is bypassed/enabled */
+       CONTROL_FLAG_DMA                    = 1,
+       /* 8051 'idle' mode is disabled/enabled */
+       CONTROL_FLAG_IDLE_ENABLE            = 2,
+       /* Tracker for the SPDIF-in path is bypassed/enabled */
+       CONTROL_FLAG_TRACKER                = 3,
+       /* DigitalOut to Spdif2Out connection is disabled/enabled */
+       CONTROL_FLAG_SPDIF2OUT              = 4,
+       /* Digital Microphone is disabled/enabled */
+       CONTROL_FLAG_DMIC                   = 5,
+       /* ADC_B rate is 48 kHz/96 kHz */
+       CONTROL_FLAG_ADC_B_96KHZ            = 6,
+       /* ADC_C rate is 48 kHz/96 kHz */
+       CONTROL_FLAG_ADC_C_96KHZ            = 7,
+       /* DAC rate is 48 kHz/96 kHz (affects all DACs) */
+       CONTROL_FLAG_DAC_96KHZ              = 8,
+       /* DSP rate is 48 kHz/96 kHz */
+       CONTROL_FLAG_DSP_96KHZ              = 9,
+       /* SRC clock is 98 MHz/196 MHz (196 MHz forces rate to 96 KHz) */
+       CONTROL_FLAG_SRC_CLOCK_196MHZ       = 10,
+       /* SRC rate is 48 kHz/96 kHz (48 kHz disabled when clock is 196 MHz) */
+       CONTROL_FLAG_SRC_RATE_96KHZ         = 11,
+       /* Decode Loop (DSP->SRC->DSP) is disabled/enabled */
+       CONTROL_FLAG_DECODE_LOOP            = 12,
+       /* De-emphasis filter on DAC-1 disabled/enabled */
+       CONTROL_FLAG_DAC1_DEEMPHASIS        = 13,
+       /* De-emphasis filter on DAC-2 disabled/enabled */
+       CONTROL_FLAG_DAC2_DEEMPHASIS        = 14,
+       /* De-emphasis filter on DAC-3 disabled/enabled */
+       CONTROL_FLAG_DAC3_DEEMPHASIS        = 15,
+       /* High-pass filter on ADC_B disabled/enabled */
+       CONTROL_FLAG_ADC_B_HIGH_PASS        = 16,
+       /* High-pass filter on ADC_C disabled/enabled */
+       CONTROL_FLAG_ADC_C_HIGH_PASS        = 17,
+       /* Common mode on Port_A disabled/enabled */
+       CONTROL_FLAG_PORT_A_COMMON_MODE     = 18,
+       /* Common mode on Port_D disabled/enabled */
+       CONTROL_FLAG_PORT_D_COMMON_MODE     = 19,
+       /* Impedance for ramp generator on Port_A 16 Ohm/10K Ohm */
+       CONTROL_FLAG_PORT_A_10KOHM_LOAD     = 20,
+       /* Impedance for ramp generator on Port_D, 16 Ohm/10K Ohm */
+       CONTROL_FLAG_PORT_D_10K0HM_LOAD     = 21,
+       /* ASI rate is 48kHz/96kHz */
+       CONTROL_FLAG_ASI_96KHZ              = 22,
+       /* DAC power settings able to control attached ports no/yes */
+       CONTROL_FLAG_DACS_CONTROL_PORTS     = 23,
+       /* Clock Stop OK reporting is disabled/enabled */
+       CONTROL_FLAG_CONTROL_STOP_OK_ENABLE = 24,
+       /* Number of control flags */
+       CONTROL_FLAGS_MAX = (CONTROL_FLAG_CONTROL_STOP_OK_ENABLE+1)
+};
+
+/*
+ * Control parameter IDs
+ */
+enum control_parameter_id {
+       /* 0: force HDA, 1: allow DSP if HDA Spdif1Out stream is idle */
+       CONTROL_PARAM_SPDIF1_SOURCE            = 2,
+
+       /* Stream Control */
+
+       /* Select stream with the given ID */
+       CONTROL_PARAM_STREAM_ID                = 24,
+       /* Source connection point for the selected stream */
+       CONTROL_PARAM_STREAM_SOURCE_CONN_POINT = 25,
+       /* Destination connection point for the selected stream */
+       CONTROL_PARAM_STREAM_DEST_CONN_POINT   = 26,
+       /* Number of audio channels in the selected stream */
+       CONTROL_PARAM_STREAMS_CHANNELS         = 27,
+       /*Enable control for the selected stream */
+       CONTROL_PARAM_STREAM_CONTROL           = 28,
+
+       /* Connection Point Control */
+
+       /* Select connection point with the given ID */
+       CONTROL_PARAM_CONN_POINT_ID            = 29,
+       /* Connection point sample rate */
+       CONTROL_PARAM_CONN_POINT_SAMPLE_RATE   = 30,
+
+       /* Node Control */
+
+       /* Select HDA node with the given ID */
+       CONTROL_PARAM_NODE_ID                  = 31
+};
+
+/*
+ *  Dsp Io Status codes
+ */
+enum hda_vendor_status_dspio {
+       /* Success */
+       VENDOR_STATUS_DSPIO_OK                       = 0x00,
+       /* Busy, unable to accept new command, the host must retry */
+       VENDOR_STATUS_DSPIO_BUSY                     = 0x01,
+       /* SCP command queue is full */
+       VENDOR_STATUS_DSPIO_SCP_COMMAND_QUEUE_FULL   = 0x02,
+       /* SCP response queue is empty */
+       VENDOR_STATUS_DSPIO_SCP_RESPONSE_QUEUE_EMPTY = 0x03
+};
+
+/*
+ *  Chip Io Status codes
+ */
+enum hda_vendor_status_chipio {
+       /* Success */
+       VENDOR_STATUS_CHIPIO_OK   = 0x00,
+       /* Busy, unable to accept new command, the host must retry */
+       VENDOR_STATUS_CHIPIO_BUSY = 0x01
+};
+
+/*
+ *  CA0132 sample rate
+ */
+enum ca0132_sample_rate {
+       SR_6_000        = 0x00,
+       SR_8_000        = 0x01,
+       SR_9_600        = 0x02,
+       SR_11_025       = 0x03,
+       SR_16_000       = 0x04,
+       SR_22_050       = 0x05,
+       SR_24_000       = 0x06,
+       SR_32_000       = 0x07,
+       SR_44_100       = 0x08,
+       SR_48_000       = 0x09,
+       SR_88_200       = 0x0A,
+       SR_96_000       = 0x0B,
+       SR_144_000      = 0x0C,
+       SR_176_400      = 0x0D,
+       SR_192_000      = 0x0E,
+       SR_384_000      = 0x0F,
+
+       SR_COUNT        = 0x10,
+
+       SR_RATE_UNKNOWN = 0x1F
+};
+
+/*
+ *  Scp Helper function
+ */
+enum get_set {
+       IS_SET = 0,
+       IS_GET = 1,
+};
+
+/*
+ * Duplicated from ca0110 codec
+ */
+
+static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
+{
+       if (pin) {
+               snd_hda_codec_write(codec, pin, 0,
+                                   AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
+               if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
+                       snd_hda_codec_write(codec, pin, 0,
+                                           AC_VERB_SET_AMP_GAIN_MUTE,
+                                           AMP_OUT_UNMUTE);
+       }
+       if (dac)
+               snd_hda_codec_write(codec, dac, 0,
+                                   AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO);
+}
+
+static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc)
+{
+       if (pin) {
+               snd_hda_codec_write(codec, pin, 0,
+                                   AC_VERB_SET_PIN_WIDGET_CONTROL,
+                                   PIN_VREF80);
+               if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP)
+                       snd_hda_codec_write(codec, pin, 0,
+                                           AC_VERB_SET_AMP_GAIN_MUTE,
+                                           AMP_IN_UNMUTE(0));
+       }
+       if (adc)
+               snd_hda_codec_write(codec, adc, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+                                   AMP_IN_UNMUTE(0));
+}
+
+static char *dirstr[2] = { "Playback", "Capture" };
+
+static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
+                      int chan, int dir)
+{
+       char namestr[44];
+       int type = dir ? HDA_INPUT : HDA_OUTPUT;
+       struct snd_kcontrol_new knew =
+               HDA_CODEC_MUTE_MONO(namestr, nid, chan, 0, type);
+       sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]);
+       return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+}
+
+static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
+                      int chan, int dir)
+{
+       char namestr[44];
+       int type = dir ? HDA_INPUT : HDA_OUTPUT;
+       struct snd_kcontrol_new knew =
+               HDA_CODEC_VOLUME_MONO(namestr, nid, chan, 0, type);
+       sprintf(namestr, "%s %s Volume", pfx, dirstr[dir]);
+       return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+}
+
+#define add_out_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 0)
+#define add_out_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 0)
+#define add_in_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 1)
+#define add_in_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 1)
+#define add_mono_switch(codec, nid, pfx, chan) \
+       _add_switch(codec, nid, pfx, chan, 0)
+#define add_mono_volume(codec, nid, pfx, chan) \
+       _add_volume(codec, nid, pfx, chan, 0)
+#define add_in_mono_switch(codec, nid, pfx, chan) \
+       _add_switch(codec, nid, pfx, chan, 1)
+#define add_in_mono_volume(codec, nid, pfx, chan) \
+       _add_volume(codec, nid, pfx, chan, 1)
+
+
+/*
+ * CA0132 specific
+ */
+
+struct ca0132_spec {
+       struct auto_pin_cfg autocfg;
+       struct hda_multi_out multiout;
+       hda_nid_t out_pins[AUTO_CFG_MAX_OUTS];
+       hda_nid_t dacs[AUTO_CFG_MAX_OUTS];
+       hda_nid_t hp_dac;
+       hda_nid_t input_pins[AUTO_PIN_LAST];
+       hda_nid_t adcs[AUTO_PIN_LAST];
+       hda_nid_t dig_out;
+       hda_nid_t dig_in;
+       unsigned int num_inputs;
+       long curr_hp_switch;
+       long curr_hp_volume[2];
+       long curr_speaker_switch;
+       struct mutex chipio_mutex;
+       const char *input_labels[AUTO_PIN_LAST];
+       struct hda_pcm pcm_rec[2]; /* PCM information */
+};
+
+/* Chip access helper function */
+static int chipio_send(struct hda_codec *codec,
+                      unsigned int reg,
+                      unsigned int data)
+{
+       unsigned int res;
+       int retry = 50;
+
+       /* send bits of data specified by reg */
+       do {
+               res = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0,
+                                        reg, data);
+               if (res == VENDOR_STATUS_CHIPIO_OK)
+                       return 0;
+       } while (--retry);
+       return -EIO;
+}
+
+/*
+ * Write chip address through the vendor widget -- NOT protected by the Mutex!
+ */
+static int chipio_write_address(struct hda_codec *codec,
+                               unsigned int chip_addx)
+{
+       int res;
+
+       /* send low 16 bits of the address */
+       res = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_LOW,
+                         chip_addx & 0xffff);
+
+       if (res != -EIO) {
+               /* send high 16 bits of the address */
+               res = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_HIGH,
+                                 chip_addx >> 16);
+       }
+
+       return res;
+}
+
+/*
+ * Write data through the vendor widget -- NOT protected by the Mutex!
+ */
+
+static int chipio_write_data(struct hda_codec *codec, unsigned int data)
+{
+       int res;
+
+       /* send low 16 bits of the data */
+       res = chipio_send(codec, VENDOR_CHIPIO_DATA_LOW, data & 0xffff);
+
+       if (res != -EIO) {
+               /* send high 16 bits of the data */
+               res = chipio_send(codec, VENDOR_CHIPIO_DATA_HIGH,
+                                 data >> 16);
+       }
+
+       return res;
+}
+
+/*
+ * Read data through the vendor widget -- NOT protected by the Mutex!
+ */
+static int chipio_read_data(struct hda_codec *codec, unsigned int *data)
+{
+       int res;
+
+       /* post read */
+       res = chipio_send(codec, VENDOR_CHIPIO_HIC_POST_READ, 0);
+
+       if (res != -EIO) {
+               /* read status */
+               res = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0);
+       }
+
+       if (res != -EIO) {
+               /* read data */
+               *data = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0,
+                                          VENDOR_CHIPIO_HIC_READ_DATA,
+                                          0);
+       }
+
+       return res;
+}
+
+/*
+ * Write given value to the given address through the chip I/O widget.
+ * protected by the Mutex
+ */
+static int chipio_write(struct hda_codec *codec,
+               unsigned int chip_addx, const unsigned int data)
+{
+       struct ca0132_spec *spec = codec->spec;
+       int err;
+
+       mutex_lock(&spec->chipio_mutex);
+
+       /* write the address, and if successful proceed to write data */
+       err = chipio_write_address(codec, chip_addx);
+       if (err < 0)
+               goto exit;
+
+       err = chipio_write_data(codec, data);
+       if (err < 0)
+               goto exit;
+
+exit:
+       mutex_unlock(&spec->chipio_mutex);
+       return err;
+}
+
+/*
+ * Read the given address through the chip I/O widget
+ * protected by the Mutex
+ */
+static int chipio_read(struct hda_codec *codec,
+               unsigned int chip_addx, unsigned int *data)
+{
+       struct ca0132_spec *spec = codec->spec;
+       int err;
+
+       mutex_lock(&spec->chipio_mutex);
+
+       /* write the address, and if successful proceed to write data */
+       err = chipio_write_address(codec, chip_addx);
+       if (err < 0)
+               goto exit;
+
+       err = chipio_read_data(codec, data);
+       if (err < 0)
+               goto exit;
+
+exit:
+       mutex_unlock(&spec->chipio_mutex);
+       return err;
+}
+
+/*
+ * PCM stuffs
+ */
+static void ca0132_setup_stream(struct hda_codec *codec, hda_nid_t nid,
+                                u32 stream_tag,
+                                int channel_id, int format)
+{
+       unsigned int oldval, newval;
+
+       if (!nid)
+               return;
+
+       snd_printdd("ca0132_setup_stream: "
+               "NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n",
+               nid, stream_tag, channel_id, format);
+
+       /* update the format-id if changed */
+       oldval = snd_hda_codec_read(codec, nid, 0,
+                                   AC_VERB_GET_STREAM_FORMAT,
+                                   0);
+       if (oldval != format) {
+               msleep(20);
+               snd_hda_codec_write(codec, nid, 0,
+                                   AC_VERB_SET_STREAM_FORMAT,
+                                   format);
+       }
+
+       oldval = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
+       newval = (stream_tag << 4) | channel_id;
+       if (oldval != newval) {
+               snd_hda_codec_write(codec, nid, 0,
+                                   AC_VERB_SET_CHANNEL_STREAMID,
+                                   newval);
+       }
+}
+
+static void ca0132_cleanup_stream(struct hda_codec *codec, hda_nid_t nid)
+{
+       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0);
+       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
+}
+
+/*
+ * PCM callbacks
+ */
+static int ca0132_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+                       struct hda_codec *codec,
+                       unsigned int stream_tag,
+                       unsigned int format,
+                       struct snd_pcm_substream *substream)
+{
+       struct ca0132_spec *spec = codec->spec;
+
+       ca0132_setup_stream(codec, spec->dacs[0], stream_tag, 0, format);
+
+       return 0;
+}
+
+static int ca0132_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                       struct hda_codec *codec,
+                       struct snd_pcm_substream *substream)
+{
+       struct ca0132_spec *spec = codec->spec;
+
+       ca0132_cleanup_stream(codec, spec->dacs[0]);
+
+       return 0;
+}
+
+/*
+ * Digital out
+ */
+static int ca0132_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+                       struct hda_codec *codec,
+                       unsigned int stream_tag,
+                       unsigned int format,
+                       struct snd_pcm_substream *substream)
+{
+       struct ca0132_spec *spec = codec->spec;
+
+       ca0132_setup_stream(codec, spec->dig_out, stream_tag, 0, format);
+
+       return 0;
+}
+
+static int ca0132_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                       struct hda_codec *codec,
+                       struct snd_pcm_substream *substream)
+{
+       struct ca0132_spec *spec = codec->spec;
+
+       ca0132_cleanup_stream(codec, spec->dig_out);
+
+       return 0;
+}
+
+/*
+ * Analog capture
+ */
+static int ca0132_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 ca0132_spec *spec = codec->spec;
+
+       ca0132_setup_stream(codec, spec->adcs[substream->number],
+                            stream_tag, 0, format);
+
+       return 0;
+}
+
+static int ca0132_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                       struct hda_codec *codec,
+                       struct snd_pcm_substream *substream)
+{
+       struct ca0132_spec *spec = codec->spec;
+
+       ca0132_cleanup_stream(codec, spec->adcs[substream->number]);
+
+       return 0;
+}
+
+/*
+ * Digital capture
+ */
+static int ca0132_dig_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 ca0132_spec *spec = codec->spec;
+
+       ca0132_setup_stream(codec, spec->dig_in, stream_tag, 0, format);
+
+       return 0;
+}
+
+static int ca0132_dig_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                       struct hda_codec *codec,
+                       struct snd_pcm_substream *substream)
+{
+       struct ca0132_spec *spec = codec->spec;
+
+       ca0132_cleanup_stream(codec, spec->dig_in);
+
+       return 0;
+}
+
+/*
+ */
+static struct hda_pcm_stream ca0132_pcm_analog_playback = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+       .ops = {
+               .prepare = ca0132_playback_pcm_prepare,
+               .cleanup = ca0132_playback_pcm_cleanup
+       },
+};
+
+static struct hda_pcm_stream ca0132_pcm_analog_capture = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+       .ops = {
+               .prepare = ca0132_capture_pcm_prepare,
+               .cleanup = ca0132_capture_pcm_cleanup
+       },
+};
+
+static struct hda_pcm_stream ca0132_pcm_digital_playback = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+       .ops = {
+               .prepare = ca0132_dig_playback_pcm_prepare,
+               .cleanup = ca0132_dig_playback_pcm_cleanup
+       },
+};
+
+static struct hda_pcm_stream ca0132_pcm_digital_capture = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+       .ops = {
+               .prepare = ca0132_dig_capture_pcm_prepare,
+               .cleanup = ca0132_dig_capture_pcm_cleanup
+       },
+};
+
+static int ca0132_build_pcms(struct hda_codec *codec)
+{
+       struct ca0132_spec *spec = codec->spec;
+       struct hda_pcm *info = spec->pcm_rec;
+
+       codec->pcm_info = info;
+       codec->num_pcms = 0;
+
+       info->name = "CA0132 Analog";
+       info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0132_pcm_analog_playback;
+       info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0];
+       info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
+               spec->multiout.max_channels;
+       info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture;
+       info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_inputs;
+       info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0];
+       codec->num_pcms++;
+
+       if (!spec->dig_out && !spec->dig_in)
+               return 0;
+
+       info++;
+       info->name = "CA0132 Digital";
+       info->pcm_type = HDA_PCM_TYPE_SPDIF;
+       if (spec->dig_out) {
+               info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
+                       ca0132_pcm_digital_playback;
+               info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dig_out;
+       }
+       if (spec->dig_in) {
+               info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+                       ca0132_pcm_digital_capture;
+               info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in;
+       }
+       codec->num_pcms++;
+
+       return 0;
+}
+
+#define REG_CODEC_MUTE         0x18b014
+#define REG_CODEC_HP_VOL_L     0x18b070
+#define REG_CODEC_HP_VOL_R     0x18b074
+
+static int ca0132_hp_switch_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct ca0132_spec *spec = codec->spec;
+       long *valp = ucontrol->value.integer.value;
+
+       *valp = spec->curr_hp_switch;
+       return 0;
+}
+
+static int ca0132_hp_switch_put(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct ca0132_spec *spec = codec->spec;
+       long *valp = ucontrol->value.integer.value;
+       unsigned int data;
+       int err;
+
+       /* any change? */
+       if (spec->curr_hp_switch == *valp)
+               return 0;
+
+       snd_hda_power_up(codec);
+
+       err = chipio_read(codec, REG_CODEC_MUTE, &data);
+       if (err < 0)
+               return err;
+
+       /* *valp 0 is mute, 1 is unmute */
+       data = (data & 0x7f) | (*valp ? 0 : 0x80);
+       chipio_write(codec, REG_CODEC_MUTE, data);
+       if (err < 0)
+               return err;
+
+       spec->curr_hp_switch = *valp;
+
+       snd_hda_power_down(codec);
+       return 1;
+}
+
+static int ca0132_speaker_switch_get(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct ca0132_spec *spec = codec->spec;
+       long *valp = ucontrol->value.integer.value;
+
+       *valp = spec->curr_speaker_switch;
+       return 0;
+}
+
+static int ca0132_speaker_switch_put(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct ca0132_spec *spec = codec->spec;
+       long *valp = ucontrol->value.integer.value;
+       unsigned int data;
+       int err;
+
+       /* any change? */
+       if (spec->curr_speaker_switch == *valp)
+               return 0;
+
+       snd_hda_power_up(codec);
+
+       err = chipio_read(codec, REG_CODEC_MUTE, &data);
+       if (err < 0)
+               return err;
+
+       /* *valp 0 is mute, 1 is unmute */
+       data = (data & 0xef) | (*valp ? 0 : 0x10);
+       chipio_write(codec, REG_CODEC_MUTE, data);
+       if (err < 0)
+               return err;
+
+       spec->curr_speaker_switch = *valp;
+
+       snd_hda_power_down(codec);
+       return 1;
+}
+
+static int ca0132_hp_volume_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct ca0132_spec *spec = codec->spec;
+       long *valp = ucontrol->value.integer.value;
+
+       *valp++ = spec->curr_hp_volume[0];
+       *valp = spec->curr_hp_volume[1];
+       return 0;
+}
+
+static int ca0132_hp_volume_put(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct ca0132_spec *spec = codec->spec;
+       long *valp = ucontrol->value.integer.value;
+       long left_vol, right_vol;
+       unsigned int data;
+       int val;
+       int err;
+
+       left_vol = *valp++;
+       right_vol = *valp;
+
+       /* any change? */
+       if ((spec->curr_hp_volume[0] == left_vol) &&
+               (spec->curr_hp_volume[1] == right_vol))
+               return 0;
+
+       snd_hda_power_up(codec);
+
+       err = chipio_read(codec, REG_CODEC_HP_VOL_L, &data);
+       if (err < 0)
+               return err;
+
+       val = 31 - left_vol;
+       data = (data & 0xe0) | val;
+       chipio_write(codec, REG_CODEC_HP_VOL_L, data);
+       if (err < 0)
+               return err;
+
+       val = 31 - right_vol;
+       data = (data & 0xe0) | val;
+       chipio_write(codec, REG_CODEC_HP_VOL_R, data);
+       if (err < 0)
+               return err;
+
+       spec->curr_hp_volume[0] = left_vol;
+       spec->curr_hp_volume[1] = right_vol;
+
+       snd_hda_power_down(codec);
+       return 1;
+}
+
+static int add_hp_switch(struct hda_codec *codec, hda_nid_t nid)
+{
+       struct snd_kcontrol_new knew =
+               HDA_CODEC_MUTE_MONO("Headphone Playback Switch",
+                                    nid, 1, 0, HDA_OUTPUT);
+       knew.get = ca0132_hp_switch_get;
+       knew.put = ca0132_hp_switch_put;
+       return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+}
+
+static int add_hp_volume(struct hda_codec *codec, hda_nid_t nid)
+{
+       struct snd_kcontrol_new knew =
+               HDA_CODEC_VOLUME_MONO("Headphone Playback Volume",
+                                      nid, 3, 0, HDA_OUTPUT);
+       knew.get = ca0132_hp_volume_get;
+       knew.put = ca0132_hp_volume_put;
+       return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+}
+
+static int add_speaker_switch(struct hda_codec *codec, hda_nid_t nid)
+{
+       struct snd_kcontrol_new knew =
+               HDA_CODEC_MUTE_MONO("Speaker Playback Switch",
+                                    nid, 1, 0, HDA_OUTPUT);
+       knew.get = ca0132_speaker_switch_get;
+       knew.put = ca0132_speaker_switch_put;
+       return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+}
+
+static void ca0132_fix_hp_caps(struct hda_codec *codec)
+{
+       struct ca0132_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       unsigned int caps;
+
+       /* set mute-capable, 1db step, 32 steps, ofs 6 */
+       caps = 0x80031f06;
+       snd_hda_override_amp_caps(codec, cfg->hp_pins[0], HDA_OUTPUT, caps);
+}
+
+static int ca0132_build_controls(struct hda_codec *codec)
+{
+       struct ca0132_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       int i, err;
+
+       if (spec->multiout.num_dacs) {
+               err = add_speaker_switch(codec, spec->out_pins[0]);
+               if (err < 0)
+                       return err;
+       }
+
+       if (cfg->hp_outs) {
+               ca0132_fix_hp_caps(codec);
+               err = add_hp_switch(codec, cfg->hp_pins[0]);
+               if (err < 0)
+                       return err;
+               err = add_hp_volume(codec, cfg->hp_pins[0]);
+               if (err < 0)
+                       return err;
+       }
+
+       for (i = 0; i < spec->num_inputs; i++) {
+               const char *label = spec->input_labels[i];
+
+               err = add_in_switch(codec, spec->adcs[i], label);
+               if (err < 0)
+                       return err;
+               err = add_in_volume(codec, spec->adcs[i], label);
+               if (err < 0)
+                       return err;
+               if (cfg->inputs[i].type == AUTO_PIN_MIC) {
+                       /* add Mic-Boost */
+                       err = add_in_mono_volume(codec, spec->input_pins[i],
+                                                "Mic Boost", 1);
+                       if (err < 0)
+                               return err;
+               }
+       }
+
+       if (spec->dig_out) {
+               err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out,
+                                                   spec->dig_out);
+               if (err < 0)
+                       return err;
+               err = add_out_volume(codec, spec->dig_out, "IEC958");
+               if (err < 0)
+                       return err;
+       }
+
+       if (spec->dig_in) {
+               err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in);
+               if (err < 0)
+                       return err;
+               err = add_in_volume(codec, spec->dig_in, "IEC958");
+       }
+       return 0;
+}
+
+
+static void ca0132_set_ct_ext(struct hda_codec *codec, int enable)
+{
+       /* Set Creative extension */
+       snd_printdd("SET CREATIVE EXTENSION\n");
+       snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+                           VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE,
+                           enable);
+       msleep(20);
+}
+
+
+static void ca0132_config(struct hda_codec *codec)
+{
+       struct ca0132_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+
+       /* line-outs */
+       cfg->line_outs = 1;
+       cfg->line_out_pins[0] = 0x0b; /* front */
+       cfg->line_out_type = AUTO_PIN_LINE_OUT;
+
+       spec->dacs[0] = 0x02;
+       spec->out_pins[0] = 0x0b;
+       spec->multiout.dac_nids = spec->dacs;
+       spec->multiout.num_dacs = 1;
+       spec->multiout.max_channels = 2;
+
+       /* headphone */
+       cfg->hp_outs = 1;
+       cfg->hp_pins[0] = 0x0f;
+
+       spec->hp_dac = 0;
+       spec->multiout.hp_nid = 0;
+
+       /* inputs */
+       cfg->num_inputs = 2;  /* Mic-in and line-in */
+       cfg->inputs[0].pin = 0x12;
+       cfg->inputs[0].type = AUTO_PIN_MIC;
+       cfg->inputs[1].pin = 0x11;
+       cfg->inputs[1].type = AUTO_PIN_LINE_IN;
+
+       /* Mic-in */
+       spec->input_pins[0] = 0x12;
+       spec->input_labels[0] = "Mic-In";
+       spec->adcs[0] = 0x07;
+
+       /* Line-In */
+       spec->input_pins[1] = 0x11;
+       spec->input_labels[1] = "Line-In";
+       spec->adcs[1] = 0x08;
+       spec->num_inputs = 2;
+}
+
+static void ca0132_init_chip(struct hda_codec *codec)
+{
+       struct ca0132_spec *spec = codec->spec;
+
+       mutex_init(&spec->chipio_mutex);
+}
+
+static void ca0132_exit_chip(struct hda_codec *codec)
+{
+       /* put any chip cleanup stuffs here. */
+}
+
+static int ca0132_init(struct hda_codec *codec)
+{
+       struct ca0132_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       int i;
+
+       for (i = 0; i < spec->multiout.num_dacs; i++) {
+               init_output(codec, spec->out_pins[i],
+                           spec->multiout.dac_nids[i]);
+       }
+       init_output(codec, cfg->hp_pins[0], spec->hp_dac);
+       init_output(codec, cfg->dig_out_pins[0], spec->dig_out);
+
+       for (i = 0; i < spec->num_inputs; i++)
+               init_input(codec, spec->input_pins[i], spec->adcs[i]);
+
+       init_input(codec, cfg->dig_in_pin, spec->dig_in);
+
+       ca0132_set_ct_ext(codec, 1);
+
+       return 0;
+}
+
+
+static void ca0132_free(struct hda_codec *codec)
+{
+       ca0132_set_ct_ext(codec, 0);
+       ca0132_exit_chip(codec);
+       kfree(codec->spec);
+}
+
+static struct hda_codec_ops ca0132_patch_ops = {
+       .build_controls = ca0132_build_controls,
+       .build_pcms = ca0132_build_pcms,
+       .init = ca0132_init,
+       .free = ca0132_free,
+};
+
+
+
+static int patch_ca0132(struct hda_codec *codec)
+{
+       struct ca0132_spec *spec;
+
+       snd_printdd("patch_ca0132\n");
+
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (!spec)
+               return -ENOMEM;
+       codec->spec = spec;
+
+       ca0132_init_chip(codec);
+
+       ca0132_config(codec);
+
+       codec->patch_ops = ca0132_patch_ops;
+
+       return 0;
+}
+
+/*
+ * patch entries
+ */
+static struct hda_codec_preset snd_hda_preset_ca0132[] = {
+       { .id = 0x11020011, .name = "CA0132",     .patch = patch_ca0132 },
+       {} /* terminator */
+};
+
+MODULE_ALIAS("snd-hda-codec-id:11020011");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Creative CA0132, CA0132 HD-audio codec");
+
+static struct hda_codec_preset_list ca0132_list = {
+       .preset = snd_hda_preset_ca0132,
+       .owner = THIS_MODULE,
+};
+
+static int __init patch_ca0132_init(void)
+{
+       return snd_hda_add_codec_preset(&ca0132_list);
+}
+
+static void __exit patch_ca0132_exit(void)
+{
+       snd_hda_delete_codec_preset(&ca0132_list);
+}
+
+module_init(patch_ca0132_init)
+module_exit(patch_ca0132_exit)
index 26a1521..7f93739 100644 (file)
@@ -346,21 +346,15 @@ static hda_nid_t get_adc(struct hda_codec *codec, hda_nid_t pin,
 
        nid = codec->start_nid;
        for (i = 0; i < codec->num_nodes; i++, nid++) {
-               hda_nid_t pins[2];
                unsigned int type;
-               int j, nums;
+               int idx;
                type = get_wcaps_type(get_wcaps(codec, nid));
                if (type != AC_WID_AUD_IN)
                        continue;
-               nums = snd_hda_get_connections(codec, nid, pins,
-                                              ARRAY_SIZE(pins));
-               if (nums <= 0)
-                       continue;
-               for (j = 0; j < nums; j++) {
-                       if (pins[j] == pin) {
-                               *idxp = j;
-                               return nid;
-                       }
+               idx = snd_hda_get_conn_index(codec, nid, pin, 0);
+               if (idx >= 0) {
+                       *idxp = idx;
+                       return nid;
                }
        }
        return 0;
@@ -821,7 +815,8 @@ static int build_digital_output(struct hda_codec *codec)
        if (!spec->multiout.dig_out_nid)
                return 0;
 
-       err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
+       err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid,
+                                           spec->multiout.dig_out_nid);
        if (err < 0)
                return err;
        err = snd_hda_create_spdif_share_sw(codec, &spec->multiout);
index ab3308d..08af484 100644 (file)
@@ -327,7 +327,9 @@ static int cmi9880_build_controls(struct hda_codec *codec)
                        return err;
        }
        if (spec->multiout.dig_out_nid) {
-               err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
+               err = snd_hda_create_spdif_out_ctls(codec,
+                                                   spec->multiout.dig_out_nid,
+                                                   spec->multiout.dig_out_nid);
                if (err < 0)
                        return err;
                err = snd_hda_create_spdif_share_sw(codec,
@@ -401,7 +403,6 @@ static int cmi9880_fill_multi_init(struct hda_codec *codec, const struct auto_pi
        /* clear the table, only one c-media dac assumed here */
        memset(spec->multi_init, 0, sizeof(spec->multi_init));
        for (j = 0, i = 0; i < cfg->line_outs; i++) {
-               hda_nid_t conn[4];
                nid = cfg->line_out_pins[i];
                /* set as output */
                spec->multi_init[j].nid = nid;
@@ -414,12 +415,10 @@ static int cmi9880_fill_multi_init(struct hda_codec *codec, const struct auto_pi
                        spec->multi_init[j].verb = AC_VERB_SET_CONNECT_SEL;
                        spec->multi_init[j].param = 0;
                        /* find the index in connect list */
-                       len = snd_hda_get_connections(codec, nid, conn, 4);
-                       for (k = 0; k < len; k++)
-                               if (conn[k] == spec->dac_nids[i]) {
-                                       spec->multi_init[j].param = k;
-                                       break;
-                               }
+                       k = snd_hda_get_conn_index(codec, nid,
+                                                  spec->dac_nids[i], 0);
+                       if (k >= 0)
+                               spec->multi_init[j].param = k;
                        j++;
                }
        }
index 7bbc5f2..6e90b6b 100644 (file)
@@ -510,6 +510,7 @@ static int conexant_build_controls(struct hda_codec *codec)
        }
        if (spec->multiout.dig_out_nid) {
                err = snd_hda_create_spdif_out_ctls(codec,
+                                                   spec->multiout.dig_out_nid,
                                                    spec->multiout.dig_out_nid);
                if (err < 0)
                        return err;
@@ -3308,19 +3309,8 @@ static const struct hda_pcm_stream cx_auto_pcm_analog_capture = {
 
 static const hda_nid_t cx_auto_adc_nids[] = { 0x14 };
 
-/* get the connection index of @nid in the widget @mux */
-static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
-                               hda_nid_t nid)
-{
-       hda_nid_t conn[HDA_MAX_NUM_INPUTS];
-       int i, nums;
-
-       nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
-       for (i = 0; i < nums; i++)
-               if (conn[i] == nid)
-                       return i;
-       return -1;
-}
+#define get_connection_index(codec, mux, nid)\
+       snd_hda_get_conn_index(codec, mux, nid, 0)
 
 /* get an unassigned DAC from the given list.
  * Return the nid if found and reduce the DAC list, or return zero if
index bd0ae69..19cb72d 100644 (file)
@@ -43,7 +43,7 @@ MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info");
 
 /*
  * The HDMI/DisplayPort configuration can be highly dynamic. A graphics device
- * could support two independent pipes, each of them can be connected to one or
+ * could support N independent pipes, each of them can be connected to one or
  * more ports (DVI, HDMI or DisplayPort).
  *
  * The HDA correspondence of pipes/ports are converter/pin nodes.
@@ -51,30 +51,33 @@ MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info");
 #define MAX_HDMI_CVTS  4
 #define MAX_HDMI_PINS  4
 
-struct hdmi_spec {
-       int num_cvts;
-       int num_pins;
-       hda_nid_t cvt[MAX_HDMI_CVTS+1];  /* audio sources */
-       hda_nid_t pin[MAX_HDMI_PINS+1];  /* audio sinks */
+struct hdmi_spec_per_cvt {
+       hda_nid_t cvt_nid;
+       int assigned;
+       unsigned int channels_min;
+       unsigned int channels_max;
+       u32 rates;
+       u64 formats;
+       unsigned int maxbps;
+};
 
-       /*
-        * source connection for each pin
-        */
-       hda_nid_t pin_cvt[MAX_HDMI_PINS+1];
+struct hdmi_spec_per_pin {
+       hda_nid_t pin_nid;
+       int num_mux_nids;
+       hda_nid_t mux_nids[HDA_MAX_CONNECTIONS];
+       struct hdmi_eld sink_eld;
+};
 
-       /*
-        * HDMI sink attached to each pin
-        */
-       struct hdmi_eld sink_eld[MAX_HDMI_PINS];
+struct hdmi_spec {
+       int num_cvts;
+       struct hdmi_spec_per_cvt cvts[MAX_HDMI_CVTS];
 
-       /*
-        * export one pcm per pipe
-        */
-       struct hda_pcm  pcm_rec[MAX_HDMI_CVTS];
-       struct hda_pcm_stream codec_pcm_pars[MAX_HDMI_CVTS];
+       int num_pins;
+       struct hdmi_spec_per_pin pins[MAX_HDMI_PINS];
+       struct hda_pcm pcm_rec[MAX_HDMI_PINS];
 
        /*
-        * ati/nvhdmi specific
+        * Non-generic ATI/NVIDIA specific
         */
        struct hda_multi_out multiout;
        const struct hda_pcm_stream *pcm_playback;
@@ -284,15 +287,40 @@ static struct cea_channel_speaker_allocation channel_allocations[] = {
  * HDMI routines
  */
 
-static int hda_node_index(hda_nid_t *nids, hda_nid_t nid)
+static int pin_nid_to_pin_index(struct hdmi_spec *spec, hda_nid_t pin_nid)
 {
-       int i;
+       int pin_idx;
 
-       for (i = 0; nids[i]; i++)
-               if (nids[i] == nid)
-                       return i;
+       for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++)
+               if (spec->pins[pin_idx].pin_nid == pin_nid)
+                       return pin_idx;
 
-       snd_printk(KERN_WARNING "HDMI: nid %d not registered\n", nid);
+       snd_printk(KERN_WARNING "HDMI: pin nid %d not registered\n", pin_nid);
+       return -EINVAL;
+}
+
+static int hinfo_to_pin_index(struct hdmi_spec *spec,
+                             struct hda_pcm_stream *hinfo)
+{
+       int pin_idx;
+
+       for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++)
+               if (&spec->pcm_rec[pin_idx].stream[0] == hinfo)
+                       return pin_idx;
+
+       snd_printk(KERN_WARNING "HDMI: hinfo %p not registered\n", hinfo);
+       return -EINVAL;
+}
+
+static int cvt_nid_to_cvt_index(struct hdmi_spec *spec, hda_nid_t cvt_nid)
+{
+       int cvt_idx;
+
+       for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++)
+               if (spec->cvts[cvt_idx].cvt_nid == cvt_nid)
+                       return cvt_idx;
+
+       snd_printk(KERN_WARNING "HDMI: cvt nid %d not registered\n", cvt_nid);
        return -EINVAL;
 }
 
@@ -326,28 +354,28 @@ static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t pin_nid,
        snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val);
 }
 
-static void hdmi_enable_output(struct hda_codec *codec, hda_nid_t pin_nid)
+static void hdmi_init_pin(struct hda_codec *codec, hda_nid_t pin_nid)
 {
        /* Unmute */
        if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP)
                snd_hda_codec_write(codec, pin_nid, 0,
                                AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
-       /* Enable pin out */
+       /* Disable pin out until stream is active*/
        snd_hda_codec_write(codec, pin_nid, 0,
-                           AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+                           AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
 }
 
-static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t nid)
+static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t cvt_nid)
 {
-       return 1 + snd_hda_codec_read(codec, nid, 0,
+       return 1 + snd_hda_codec_read(codec, cvt_nid, 0,
                                        AC_VERB_GET_CVT_CHAN_COUNT, 0);
 }
 
 static void hdmi_set_channel_count(struct hda_codec *codec,
-                                  hda_nid_t nid, int chs)
+                                  hda_nid_t cvt_nid, int chs)
 {
-       if (chs != hdmi_get_channel_count(codec, nid))
-               snd_hda_codec_write(codec, nid, 0,
+       if (chs != hdmi_get_channel_count(codec, cvt_nid))
+               snd_hda_codec_write(codec, cvt_nid, 0,
                                    AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
 }
 
@@ -384,11 +412,8 @@ static void init_channel_allocations(void)
  *
  * TODO: it could select the wrong CA from multiple candidates.
 */
-static int hdmi_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
-                                  int channels)
+static int hdmi_channel_allocation(struct hdmi_eld *eld, int channels)
 {
-       struct hdmi_spec *spec = codec->spec;
-       struct hdmi_eld *eld;
        int i;
        int ca = 0;
        int spk_mask = 0;
@@ -400,19 +425,6 @@ static int hdmi_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
        if (channels <= 2)
                return 0;
 
-       i = hda_node_index(spec->pin_cvt, nid);
-       if (i < 0)
-               return 0;
-       eld = &spec->sink_eld[i];
-
-       /*
-        * HDMI sink's ELD info cannot always be retrieved for now, e.g.
-        * in console or for audio devices. Assume the highest speakers
-        * configuration, to _not_ prohibit multi-channel audio playback.
-        */
-       if (!eld->spk_alloc)
-               eld->spk_alloc = 0xffff;
-
        /*
         * expand ELD's speaker allocation mask
         *
@@ -608,67 +620,63 @@ static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
        return true;
 }
 
-static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid,
+static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx,
                                        struct snd_pcm_substream *substream)
 {
        struct hdmi_spec *spec = codec->spec;
-       hda_nid_t pin_nid;
+       struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
+       hda_nid_t pin_nid = per_pin->pin_nid;
        int channels = substream->runtime->channels;
+       struct hdmi_eld *eld;
        int ca;
-       int i;
        union audio_infoframe ai;
 
-       ca = hdmi_channel_allocation(codec, nid, channels);
-
-       for (i = 0; i < spec->num_pins; i++) {
-               if (spec->pin_cvt[i] != nid)
-                       continue;
-               if (!spec->sink_eld[i].monitor_present)
-                       continue;
+       eld = &spec->pins[pin_idx].sink_eld;
+       if (!eld->monitor_present)
+               return;
 
-               pin_nid = spec->pin[i];
-
-               memset(&ai, 0, sizeof(ai));
-               if (spec->sink_eld[i].conn_type == 0) { /* HDMI */
-                       struct hdmi_audio_infoframe *hdmi_ai = &ai.hdmi;
-
-                       hdmi_ai->type           = 0x84;
-                       hdmi_ai->ver            = 0x01;
-                       hdmi_ai->len            = 0x0a;
-                       hdmi_ai->CC02_CT47      = channels - 1;
-                       hdmi_ai->CA             = ca;
-                       hdmi_checksum_audio_infoframe(hdmi_ai);
-               } else if (spec->sink_eld[i].conn_type == 1) { /* DisplayPort */
-                       struct dp_audio_infoframe *dp_ai = &ai.dp;
-
-                       dp_ai->type             = 0x84;
-                       dp_ai->len              = 0x1b;
-                       dp_ai->ver              = 0x11 << 2;
-                       dp_ai->CC02_CT47        = channels - 1;
-                       dp_ai->CA               = ca;
-               } else {
-                       snd_printd("HDMI: unknown connection type at pin %d\n",
-                                  pin_nid);
-                       continue;
-               }
+       ca = hdmi_channel_allocation(eld, channels);
+
+       memset(&ai, 0, sizeof(ai));
+       if (eld->conn_type == 0) { /* HDMI */
+               struct hdmi_audio_infoframe *hdmi_ai = &ai.hdmi;
+
+               hdmi_ai->type           = 0x84;
+               hdmi_ai->ver            = 0x01;
+               hdmi_ai->len            = 0x0a;
+               hdmi_ai->CC02_CT47      = channels - 1;
+               hdmi_ai->CA             = ca;
+               hdmi_checksum_audio_infoframe(hdmi_ai);
+       } else if (eld->conn_type == 1) { /* DisplayPort */
+               struct dp_audio_infoframe *dp_ai = &ai.dp;
+
+               dp_ai->type             = 0x84;
+               dp_ai->len              = 0x1b;
+               dp_ai->ver              = 0x11 << 2;
+               dp_ai->CC02_CT47        = channels - 1;
+               dp_ai->CA               = ca;
+       } else {
+               snd_printd("HDMI: unknown connection type at pin %d\n",
+                           pin_nid);
+               return;
+       }
 
-               /*
-                * sizeof(ai) is used instead of sizeof(*hdmi_ai) or
-                * sizeof(*dp_ai) to avoid partial match/update problems when
-                * the user switches between HDMI/DP monitors.
-                */
-               if (!hdmi_infoframe_uptodate(codec, pin_nid, ai.bytes,
-                                            sizeof(ai))) {
-                       snd_printdd("hdmi_setup_audio_infoframe: "
-                                   "cvt=%d pin=%d channels=%d\n",
-                                   nid, pin_nid,
-                                   channels);
-                       hdmi_setup_channel_mapping(codec, pin_nid, ca);
-                       hdmi_stop_infoframe_trans(codec, pin_nid);
-                       hdmi_fill_audio_infoframe(codec, pin_nid,
-                                                 ai.bytes, sizeof(ai));
-                       hdmi_start_infoframe_trans(codec, pin_nid);
-               }
+       /*
+        * sizeof(ai) is used instead of sizeof(*hdmi_ai) or
+        * sizeof(*dp_ai) to avoid partial match/update problems when
+        * the user switches between HDMI/DP monitors.
+        */
+       if (!hdmi_infoframe_uptodate(codec, pin_nid, ai.bytes,
+                                       sizeof(ai))) {
+               snd_printdd("hdmi_setup_audio_infoframe: "
+                           "pin=%d channels=%d\n",
+                           pin_nid,
+                           channels);
+               hdmi_setup_channel_mapping(codec, pin_nid, ca);
+               hdmi_stop_infoframe_trans(codec, pin_nid);
+               hdmi_fill_audio_infoframe(codec, pin_nid,
+                                           ai.bytes, sizeof(ai));
+               hdmi_start_infoframe_trans(codec, pin_nid);
        }
 }
 
@@ -686,17 +694,27 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
        int pin_nid = res >> AC_UNSOL_RES_TAG_SHIFT;
        int pd = !!(res & AC_UNSOL_RES_PD);
        int eldv = !!(res & AC_UNSOL_RES_ELDV);
-       int index;
+       int pin_idx;
+       struct hdmi_eld *eld;
 
        printk(KERN_INFO
-               "HDMI hot plug event: Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
-               pin_nid, pd, eldv);
+               "HDMI hot plug event: Codec=%d Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
+               codec->addr, pin_nid, pd, eldv);
 
-       index = hda_node_index(spec->pin, pin_nid);
-       if (index < 0)
+       pin_idx = pin_nid_to_pin_index(spec, pin_nid);
+       if (pin_idx < 0)
                return;
+       eld = &spec->pins[pin_idx].sink_eld;
 
-       hdmi_present_sense(codec, pin_nid, &spec->sink_eld[index]);
+       hdmi_present_sense(codec, pin_nid, eld);
+
+       /*
+        * HDMI sink's ELD info cannot always be retrieved for now, e.g.
+        * in console or for audio devices. Assume the highest speakers
+        * configuration, to _not_ prohibit multi-channel audio playback.
+        */
+       if (!eld->spk_alloc)
+               eld->spk_alloc = 0xffff;
 }
 
 static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
@@ -707,7 +725,8 @@ static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
        int cp_ready = !!(res & AC_UNSOL_RES_CP_READY);
 
        printk(KERN_INFO
-               "HDMI CP event: PIN=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n",
+               "HDMI CP event: CODEC=%d PIN=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n",
+               codec->addr,
                tag,
                subtag,
                cp_state,
@@ -727,7 +746,7 @@ static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
        int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
        int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
 
-       if (hda_node_index(spec->pin, tag) < 0) {
+       if (pin_nid_to_pin_index(spec, tag) < 0) {
                snd_printd(KERN_INFO "Unexpected HDMI event tag 0x%x\n", tag);
                return;
        }
@@ -746,21 +765,14 @@ static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
 #define is_hbr_format(format) \
        ((format & AC_FMT_TYPE_NON_PCM) && (format & AC_FMT_CHAN_MASK) == 7)
 
-static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid,
-                             u32 stream_tag, int format)
+static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
+                             hda_nid_t pin_nid, u32 stream_tag, int format)
 {
-       struct hdmi_spec *spec = codec->spec;
        int pinctl;
        int new_pinctl = 0;
-       int i;
-
-       for (i = 0; i < spec->num_pins; i++) {
-               if (spec->pin_cvt[i] != nid)
-                       continue;
-               if (!(snd_hda_query_pin_caps(codec, spec->pin[i]) & AC_PINCAP_HBR))
-                       continue;
 
-               pinctl = snd_hda_codec_read(codec, spec->pin[i], 0,
+       if (snd_hda_query_pin_caps(codec, pin_nid) & AC_PINCAP_HBR) {
+               pinctl = snd_hda_codec_read(codec, pin_nid, 0,
                                            AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
 
                new_pinctl = pinctl & ~AC_PINCTL_EPT;
@@ -771,22 +783,22 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid,
 
                snd_printdd("hdmi_setup_stream: "
                            "NID=0x%x, %spinctl=0x%x\n",
-                           spec->pin[i],
+                           pin_nid,
                            pinctl == new_pinctl ? "" : "new-",
                            new_pinctl);
 
                if (pinctl != new_pinctl)
-                       snd_hda_codec_write(codec, spec->pin[i], 0,
+                       snd_hda_codec_write(codec, pin_nid, 0,
                                            AC_VERB_SET_PIN_WIDGET_CONTROL,
                                            new_pinctl);
-       }
 
+       }
        if (is_hbr_format(format) && !new_pinctl) {
                snd_printdd("hdmi_setup_stream: HBR is not supported\n");
                return -EINVAL;
        }
 
-       snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
+       snd_hda_codec_setup_stream(codec, cvt_nid, stream_tag, 0, format);
        return 0;
 }
 
@@ -798,37 +810,70 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
                         struct snd_pcm_substream *substream)
 {
        struct hdmi_spec *spec = codec->spec;
-       struct hdmi_eld *eld;
-       struct hda_pcm_stream *codec_pars;
        struct snd_pcm_runtime *runtime = substream->runtime;
-       unsigned int idx;
+       int pin_idx, cvt_idx, mux_idx = 0;
+       struct hdmi_spec_per_pin *per_pin;
+       struct hdmi_eld *eld;
+       struct hdmi_spec_per_cvt *per_cvt = NULL;
+       int pinctl;
 
-       for (idx = 0; idx < spec->num_cvts; idx++)
-               if (hinfo->nid == spec->cvt[idx])
-                       break;
-       if (snd_BUG_ON(idx >= spec->num_cvts) ||
-           snd_BUG_ON(idx >= spec->num_pins))
+       /* Validate hinfo */
+       pin_idx = hinfo_to_pin_index(spec, hinfo);
+       if (snd_BUG_ON(pin_idx < 0))
                return -EINVAL;
+       per_pin = &spec->pins[pin_idx];
+       eld = &per_pin->sink_eld;
 
-       /* save the PCM info the codec provides */
-       codec_pars = &spec->codec_pcm_pars[idx];
-       if (!codec_pars->rates)
-               *codec_pars = *hinfo;
+       /* Dynamically assign converter to stream */
+       for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) {
+               per_cvt = &spec->cvts[cvt_idx];
 
-       eld = &spec->sink_eld[idx];
-       if (!static_hdmi_pcm && eld->eld_valid && eld->sad_count > 0) {
-               hdmi_eld_update_pcm_info(eld, hinfo, codec_pars);
+               /* Must not already be assigned */
+               if (per_cvt->assigned)
+                       continue;
+               /* Must be in pin's mux's list of converters */
+               for (mux_idx = 0; mux_idx < per_pin->num_mux_nids; mux_idx++)
+                       if (per_pin->mux_nids[mux_idx] == per_cvt->cvt_nid)
+                               break;
+               /* Not in mux list */
+               if (mux_idx == per_pin->num_mux_nids)
+                       continue;
+               break;
+       }
+       /* No free converters */
+       if (cvt_idx == spec->num_cvts)
+               return -ENODEV;
+
+       /* Claim converter */
+       per_cvt->assigned = 1;
+       hinfo->nid = per_cvt->cvt_nid;
+
+       snd_hda_codec_write(codec, per_pin->pin_nid, 0,
+                           AC_VERB_SET_CONNECT_SEL,
+                           mux_idx);
+       pinctl = snd_hda_codec_read(codec, per_pin->pin_nid, 0,
+                                   AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+       snd_hda_codec_write(codec, per_pin->pin_nid, 0,
+                           AC_VERB_SET_PIN_WIDGET_CONTROL,
+                           pinctl | PIN_OUT);
+       snd_hda_spdif_ctls_assign(codec, pin_idx, per_cvt->cvt_nid);
+
+       /* Initially set the converter's capabilities */
+       hinfo->channels_min = per_cvt->channels_min;
+       hinfo->channels_max = per_cvt->channels_max;
+       hinfo->rates = per_cvt->rates;
+       hinfo->formats = per_cvt->formats;
+       hinfo->maxbps = per_cvt->maxbps;
+
+       /* Restrict capabilities by ELD if this isn't disabled */
+       if (!static_hdmi_pcm && eld->eld_valid) {
+               snd_hdmi_eld_update_pcm_info(eld, hinfo);
                if (hinfo->channels_min > hinfo->channels_max ||
                    !hinfo->rates || !hinfo->formats)
                        return -ENODEV;
-       } else {
-               /* fallback to the codec default */
-               hinfo->channels_max = codec_pars->channels_max;
-               hinfo->rates = codec_pars->rates;
-               hinfo->formats = codec_pars->formats;
-               hinfo->maxbps = codec_pars->maxbps;
        }
-       /* store the updated parameters */
+
+       /* Store the updated parameters */
        runtime->hw.channels_min = hinfo->channels_min;
        runtime->hw.channels_max = hinfo->channels_max;
        runtime->hw.formats = hinfo->formats;
@@ -842,12 +887,11 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
 /*
  * HDA/HDMI auto parsing
  */
-static int hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid)
+static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx)
 {
        struct hdmi_spec *spec = codec->spec;
-       hda_nid_t conn_list[HDA_MAX_CONNECTIONS];
-       int conn_len, curr;
-       int index;
+       struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
+       hda_nid_t pin_nid = per_pin->pin_nid;
 
        if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) {
                snd_printk(KERN_WARNING
@@ -857,19 +901,9 @@ static int hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid)
                return -EINVAL;
        }
 
-       conn_len = snd_hda_get_connections(codec, pin_nid, conn_list,
-                                          HDA_MAX_CONNECTIONS);
-       if (conn_len > 1)
-               curr = snd_hda_codec_read(codec, pin_nid, 0,
-                                         AC_VERB_GET_CONNECT_SEL, 0);
-       else
-               curr = 0;
-
-       index = hda_node_index(spec->pin, pin_nid);
-       if (index < 0)
-               return -EINVAL;
-
-       spec->pin_cvt[index] = conn_list[curr];
+       per_pin->num_mux_nids = snd_hda_get_connections(codec, pin_nid,
+                                                       per_pin->mux_nids,
+                                                       HDA_MAX_CONNECTIONS);
 
        return 0;
 }
@@ -896,8 +930,8 @@ static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid,
                eld->eld_valid  = 0;
 
        printk(KERN_INFO
-               "HDMI status: Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
-               pin_nid, eld->monitor_present, eld->eld_valid);
+               "HDMI status: Codec=%d Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
+               codec->addr, pin_nid, eld->monitor_present, eld->eld_valid);
 
        if (eld->eld_valid)
                if (!snd_hdmi_get_eld(eld, codec, pin_nid))
@@ -909,47 +943,75 @@ static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid,
 static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
 {
        struct hdmi_spec *spec = codec->spec;
+       unsigned int caps, config;
+       int pin_idx;
+       struct hdmi_spec_per_pin *per_pin;
+       struct hdmi_eld *eld;
        int err;
 
-       if (spec->num_pins >= MAX_HDMI_PINS) {
-               snd_printk(KERN_WARNING
-                          "HDMI: no space for pin %d\n", pin_nid);
+       caps = snd_hda_param_read(codec, pin_nid, AC_PAR_PIN_CAP);
+       if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP)))
+               return 0;
+
+       config = snd_hda_codec_read(codec, pin_nid, 0,
+                               AC_VERB_GET_CONFIG_DEFAULT, 0);
+       if (get_defcfg_connect(config) == AC_JACK_PORT_NONE)
+               return 0;
+
+       if (snd_BUG_ON(spec->num_pins >= MAX_HDMI_PINS))
                return -E2BIG;
-       }
+
+       pin_idx = spec->num_pins;
+       per_pin = &spec->pins[pin_idx];
+       eld = &per_pin->sink_eld;
+
+       per_pin->pin_nid = pin_nid;
 
        err = snd_hda_input_jack_add(codec, pin_nid,
                                     SND_JACK_VIDEOOUT, NULL);
        if (err < 0)
                return err;
 
-       hdmi_present_sense(codec, pin_nid, &spec->sink_eld[spec->num_pins]);
+       err = hdmi_read_pin_conn(codec, pin_idx);
+       if (err < 0)
+               return err;
 
-       spec->pin[spec->num_pins] = pin_nid;
        spec->num_pins++;
 
-       return hdmi_read_pin_conn(codec, pin_nid);
+       hdmi_present_sense(codec, pin_nid, eld);
+
+       return 0;
 }
 
-static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t nid)
+static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
 {
-       int i, found_pin = 0;
        struct hdmi_spec *spec = codec->spec;
-
-       for (i = 0; i < spec->num_pins; i++)
-               if (nid == spec->pin_cvt[i]) {
-                       found_pin = 1;
-                       break;
-               }
-
-       if (!found_pin) {
-               snd_printdd("HDMI: Skipping node %d (no connection)\n", nid);
-               return -EINVAL;
-       }
+       int cvt_idx;
+       struct hdmi_spec_per_cvt *per_cvt;
+       unsigned int chans;
+       int err;
 
        if (snd_BUG_ON(spec->num_cvts >= MAX_HDMI_CVTS))
                return -E2BIG;
 
-       spec->cvt[spec->num_cvts] = nid;
+       chans = get_wcaps(codec, cvt_nid);
+       chans = get_wcaps_channels(chans);
+
+       cvt_idx = spec->num_cvts;
+       per_cvt = &spec->cvts[cvt_idx];
+
+       per_cvt->cvt_nid = cvt_nid;
+       per_cvt->channels_min = 2;
+       if (chans <= 16)
+               per_cvt->channels_max = chans;
+
+       err = snd_hda_query_supported_pcm(codec, cvt_nid,
+                                         &per_cvt->rates,
+                                         &per_cvt->formats,
+                                         &per_cvt->maxbps);
+       if (err < 0)
+               return err;
+
        spec->num_cvts++;
 
        return 0;
@@ -959,8 +1021,6 @@ static int hdmi_parse_codec(struct hda_codec *codec)
 {
        hda_nid_t nid;
        int i, nodes;
-       int num_tmp_cvts = 0;
-       hda_nid_t tmp_cvt[MAX_HDMI_CVTS];
 
        nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
        if (!nid || nodes < 0) {
@@ -971,7 +1031,6 @@ static int hdmi_parse_codec(struct hda_codec *codec)
        for (i = 0; i < nodes; i++, nid++) {
                unsigned int caps;
                unsigned int type;
-               unsigned int config;
 
                caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
                type = get_wcaps_type(caps);
@@ -981,32 +1040,14 @@ static int hdmi_parse_codec(struct hda_codec *codec)
 
                switch (type) {
                case AC_WID_AUD_OUT:
-                       if (num_tmp_cvts >= MAX_HDMI_CVTS) {
-                               snd_printk(KERN_WARNING
-                                          "HDMI: no space for converter %d\n", nid);
-                               continue;
-                       }
-                       tmp_cvt[num_tmp_cvts] = nid;
-                       num_tmp_cvts++;
+                       hdmi_add_cvt(codec, nid);
                        break;
                case AC_WID_PIN:
-                       caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
-                       if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP)))
-                               continue;
-
-                       config = snd_hda_codec_read(codec, nid, 0,
-                                            AC_VERB_GET_CONFIG_DEFAULT, 0);
-                       if (get_defcfg_connect(config) == AC_JACK_PORT_NONE)
-                               continue;
-
                        hdmi_add_pin(codec, nid);
                        break;
                }
        }
 
-       for (i = 0; i < num_tmp_cvts; i++)
-               hdmi_add_cvt(codec, tmp_cvt[i]);
-
        /*
         * G45/IbexPeak don't support EPSS: the unsolicited pin hot plug event
         * can be lost and presence sense verb will become inaccurate if the
@@ -1023,7 +1064,7 @@ static int hdmi_parse_codec(struct hda_codec *codec)
 
 /*
  */
-static char *generic_hdmi_pcm_names[MAX_HDMI_CVTS] = {
+static char *generic_hdmi_pcm_names[MAX_HDMI_PINS] = {
        "HDMI 0",
        "HDMI 1",
        "HDMI 2",
@@ -1040,51 +1081,84 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
                                           unsigned int format,
                                           struct snd_pcm_substream *substream)
 {
-       hdmi_set_channel_count(codec, hinfo->nid,
-                              substream->runtime->channels);
+       hda_nid_t cvt_nid = hinfo->nid;
+       struct hdmi_spec *spec = codec->spec;
+       int pin_idx = hinfo_to_pin_index(spec, hinfo);
+       hda_nid_t pin_nid = spec->pins[pin_idx].pin_nid;
+
+       hdmi_set_channel_count(codec, cvt_nid, substream->runtime->channels);
 
-       hdmi_setup_audio_infoframe(codec, hinfo->nid, substream);
+       hdmi_setup_audio_infoframe(codec, pin_idx, substream);
 
-       return hdmi_setup_stream(codec, hinfo->nid, stream_tag, format);
+       return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format);
 }
 
-static const struct hda_pcm_stream generic_hdmi_pcm_playback = {
-       .substreams = 1,
-       .channels_min = 2,
-       .ops = {
-               .open = hdmi_pcm_open,
-               .prepare = generic_hdmi_playback_pcm_prepare,
-       },
+static int generic_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                                            struct hda_codec *codec,
+                                            struct snd_pcm_substream *substream)
+{
+       struct hdmi_spec *spec = codec->spec;
+       int cvt_idx, pin_idx;
+       struct hdmi_spec_per_cvt *per_cvt;
+       struct hdmi_spec_per_pin *per_pin;
+       int pinctl;
+
+       snd_hda_codec_cleanup_stream(codec, hinfo->nid);
+
+       if (hinfo->nid) {
+               cvt_idx = cvt_nid_to_cvt_index(spec, hinfo->nid);
+               if (snd_BUG_ON(cvt_idx < 0))
+                       return -EINVAL;
+               per_cvt = &spec->cvts[cvt_idx];
+
+               snd_BUG_ON(!per_cvt->assigned);
+               per_cvt->assigned = 0;
+               hinfo->nid = 0;
+
+               pin_idx = hinfo_to_pin_index(spec, hinfo);
+               if (snd_BUG_ON(pin_idx < 0))
+                       return -EINVAL;
+               per_pin = &spec->pins[pin_idx];
+
+               pinctl = snd_hda_codec_read(codec, per_pin->pin_nid, 0,
+                                           AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+               snd_hda_codec_write(codec, per_pin->pin_nid, 0,
+                                   AC_VERB_SET_PIN_WIDGET_CONTROL,
+                                   pinctl & ~PIN_OUT);
+               snd_hda_spdif_ctls_unassign(codec, pin_idx);
+       }
+
+       return 0;
+}
+
+static const struct hda_pcm_ops generic_ops = {
+       .open = hdmi_pcm_open,
+       .prepare = generic_hdmi_playback_pcm_prepare,
+       .cleanup = generic_hdmi_playback_pcm_cleanup,
 };
 
 static int generic_hdmi_build_pcms(struct hda_codec *codec)
 {
        struct hdmi_spec *spec = codec->spec;
-       struct hda_pcm *info = spec->pcm_rec;
-       int i;
+       int pin_idx;
 
-       codec->num_pcms = spec->num_cvts;
-       codec->pcm_info = info;
-
-       for (i = 0; i < codec->num_pcms; i++, info++) {
-               unsigned int chans;
+       for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+               struct hda_pcm *info;
                struct hda_pcm_stream *pstr;
 
-               chans = get_wcaps(codec, spec->cvt[i]);
-               chans = get_wcaps_channels(chans);
-
-               info->name = generic_hdmi_pcm_names[i];
+               info = &spec->pcm_rec[pin_idx];
+               info->name = generic_hdmi_pcm_names[pin_idx];
                info->pcm_type = HDA_PCM_TYPE_HDMI;
+
                pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
-               if (spec->pcm_playback)
-                       *pstr = *spec->pcm_playback;
-               else
-                       *pstr = generic_hdmi_pcm_playback;
-               pstr->nid = spec->cvt[i];
-               if (pstr->channels_max <= 2 && chans && chans <= 16)
-                       pstr->channels_max = chans;
+               pstr->substreams = 1;
+               pstr->ops = generic_ops;
+               /* other pstr fields are set in open */
        }
 
+       codec->num_pcms = spec->num_pins;
+       codec->pcm_info = spec->pcm_rec;
+
        return 0;
 }
 
@@ -1092,12 +1166,16 @@ static int generic_hdmi_build_controls(struct hda_codec *codec)
 {
        struct hdmi_spec *spec = codec->spec;
        int err;
-       int i;
+       int pin_idx;
 
-       for (i = 0; i < codec->num_pcms; i++) {
-               err = snd_hda_create_spdif_out_ctls(codec, spec->cvt[i]);
+       for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+               struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
+               err = snd_hda_create_spdif_out_ctls(codec,
+                                                   per_pin->pin_nid,
+                                                   per_pin->mux_nids[0]);
                if (err < 0)
                        return err;
+               snd_hda_spdif_ctls_unassign(codec, pin_idx);
        }
 
        return 0;
@@ -1106,13 +1184,19 @@ static int generic_hdmi_build_controls(struct hda_codec *codec)
 static int generic_hdmi_init(struct hda_codec *codec)
 {
        struct hdmi_spec *spec = codec->spec;
-       int i;
+       int pin_idx;
 
-       for (i = 0; spec->pin[i]; i++) {
-               hdmi_enable_output(codec, spec->pin[i]);
-               snd_hda_codec_write(codec, spec->pin[i], 0,
+       for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+               struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
+               hda_nid_t pin_nid = per_pin->pin_nid;
+               struct hdmi_eld *eld = &per_pin->sink_eld;
+
+               hdmi_init_pin(codec, pin_nid);
+               snd_hda_codec_write(codec, pin_nid, 0,
                                    AC_VERB_SET_UNSOLICITED_ENABLE,
-                                   AC_USRSP_EN | spec->pin[i]);
+                                   AC_USRSP_EN | pin_nid);
+
+               snd_hda_eld_proc_new(codec, eld, pin_idx);
        }
        return 0;
 }
@@ -1120,10 +1204,14 @@ static int generic_hdmi_init(struct hda_codec *codec)
 static void generic_hdmi_free(struct hda_codec *codec)
 {
        struct hdmi_spec *spec = codec->spec;
-       int i;
+       int pin_idx;
+
+       for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+               struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
+               struct hdmi_eld *eld = &per_pin->sink_eld;
 
-       for (i = 0; i < spec->num_pins; i++)
-               snd_hda_eld_proc_free(codec, &spec->sink_eld[i]);
+               snd_hda_eld_proc_free(codec, eld);
+       }
        snd_hda_input_jack_free(codec);
 
        kfree(spec);
@@ -1140,7 +1228,6 @@ static const struct hda_codec_ops generic_hdmi_patch_ops = {
 static int patch_generic_hdmi(struct hda_codec *codec)
 {
        struct hdmi_spec *spec;
-       int i;
 
        spec = kzalloc(sizeof(*spec), GFP_KERNEL);
        if (spec == NULL)
@@ -1154,14 +1241,68 @@ static int patch_generic_hdmi(struct hda_codec *codec)
        }
        codec->patch_ops = generic_hdmi_patch_ops;
 
-       for (i = 0; i < spec->num_pins; i++)
-               snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i);
-
        init_channel_allocations();
 
        return 0;
 }
 
+/*
+ * Shared non-generic implementations
+ */
+
+static int simple_playback_build_pcms(struct hda_codec *codec)
+{
+       struct hdmi_spec *spec = codec->spec;
+       struct hda_pcm *info = spec->pcm_rec;
+       int i;
+
+       codec->num_pcms = spec->num_cvts;
+       codec->pcm_info = info;
+
+       for (i = 0; i < codec->num_pcms; i++, info++) {
+               unsigned int chans;
+               struct hda_pcm_stream *pstr;
+
+               chans = get_wcaps(codec, spec->cvts[i].cvt_nid);
+               chans = get_wcaps_channels(chans);
+
+               info->name = generic_hdmi_pcm_names[i];
+               info->pcm_type = HDA_PCM_TYPE_HDMI;
+               pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
+               snd_BUG_ON(!spec->pcm_playback);
+               *pstr = *spec->pcm_playback;
+               pstr->nid = spec->cvts[i].cvt_nid;
+               if (pstr->channels_max <= 2 && chans && chans <= 16)
+                       pstr->channels_max = chans;
+       }
+
+       return 0;
+}
+
+static int simple_playback_build_controls(struct hda_codec *codec)
+{
+       struct hdmi_spec *spec = codec->spec;
+       int err;
+       int i;
+
+       for (i = 0; i < codec->num_pcms; i++) {
+               err = snd_hda_create_spdif_out_ctls(codec,
+                                                   spec->cvts[i].cvt_nid,
+                                                   spec->cvts[i].cvt_nid);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
+static void simple_playback_free(struct hda_codec *codec)
+{
+       struct hdmi_spec *spec = codec->spec;
+
+       kfree(spec);
+}
+
 /*
  * Nvidia specific implementations
  */
@@ -1352,6 +1493,9 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo,
        int chs;
        unsigned int dataDCC1, dataDCC2, channel_id;
        int i;
+       struct hdmi_spec *spec = codec->spec;
+       struct hda_spdif_out *spdif =
+               snd_hda_spdif_out_of_nid(codec, spec->cvts[0].cvt_nid);
 
        mutex_lock(&codec->spdif_mutex);
 
@@ -1361,12 +1505,12 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo,
        dataDCC2 = 0x2;
 
        /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
-       if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
+       if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE))
                snd_hda_codec_write(codec,
                                nvhdmi_master_con_nid_7x,
                                0,
                                AC_VERB_SET_DIGI_CONVERT_1,
-                               codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
+                               spdif->ctls & ~AC_DIG1_ENABLE & 0xff);
 
        /* set the stream id */
        snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
@@ -1378,12 +1522,12 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo,
 
        /* turn on again (if needed) */
        /* enable and set the channel status audio/data flag */
-       if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) {
+       if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE)) {
                snd_hda_codec_write(codec,
                                nvhdmi_master_con_nid_7x,
                                0,
                                AC_VERB_SET_DIGI_CONVERT_1,
-                               codec->spdif_ctls & 0xff);
+                               spdif->ctls & 0xff);
                snd_hda_codec_write(codec,
                                nvhdmi_master_con_nid_7x,
                                0,
@@ -1400,12 +1544,12 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo,
                 *otherwise the IEC958 bits won't be updated
                 */
                if (codec->spdif_status_reset &&
-               (codec->spdif_ctls & AC_DIG1_ENABLE))
+               (spdif->ctls & AC_DIG1_ENABLE))
                        snd_hda_codec_write(codec,
                                nvhdmi_con_nids_7x[i],
                                0,
                                AC_VERB_SET_DIGI_CONVERT_1,
-                               codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
+                               spdif->ctls & ~AC_DIG1_ENABLE & 0xff);
                /* set the stream id */
                snd_hda_codec_write(codec,
                                nvhdmi_con_nids_7x[i],
@@ -1421,12 +1565,12 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo,
                /* turn on again (if needed) */
                /* enable and set the channel status audio/data flag */
                if (codec->spdif_status_reset &&
-               (codec->spdif_ctls & AC_DIG1_ENABLE)) {
+               (spdif->ctls & AC_DIG1_ENABLE)) {
                        snd_hda_codec_write(codec,
                                        nvhdmi_con_nids_7x[i],
                                        0,
                                        AC_VERB_SET_DIGI_CONVERT_1,
-                                       codec->spdif_ctls & 0xff);
+                                       spdif->ctls & 0xff);
                        snd_hda_codec_write(codec,
                                        nvhdmi_con_nids_7x[i],
                                        0,
@@ -1471,17 +1615,17 @@ static const struct hda_pcm_stream nvhdmi_pcm_playback_2ch = {
 };
 
 static const struct hda_codec_ops nvhdmi_patch_ops_8ch_7x = {
-       .build_controls = generic_hdmi_build_controls,
-       .build_pcms = generic_hdmi_build_pcms,
+       .build_controls = simple_playback_build_controls,
+       .build_pcms = simple_playback_build_pcms,
        .init = nvhdmi_7x_init,
-       .free = generic_hdmi_free,
+       .free = simple_playback_free,
 };
 
 static const struct hda_codec_ops nvhdmi_patch_ops_2ch = {
-       .build_controls = generic_hdmi_build_controls,
-       .build_pcms = generic_hdmi_build_pcms,
+       .build_controls = simple_playback_build_controls,
+       .build_pcms = simple_playback_build_pcms,
        .init = nvhdmi_7x_init,
-       .free = generic_hdmi_free,
+       .free = simple_playback_free,
 };
 
 static int patch_nvhdmi_2ch(struct hda_codec *codec)
@@ -1498,7 +1642,7 @@ static int patch_nvhdmi_2ch(struct hda_codec *codec)
        spec->multiout.max_channels = 2;
        spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x;
        spec->num_cvts = 1;
-       spec->cvt[0] = nvhdmi_master_con_nid_7x;
+       spec->cvts[0].cvt_nid = nvhdmi_master_con_nid_7x;
        spec->pcm_playback = &nvhdmi_pcm_playback_2ch;
 
        codec->patch_ops = nvhdmi_patch_ops_2ch;
@@ -1549,11 +1693,11 @@ static int atihdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
                                          substream);
        if (err < 0)
                return err;
-       snd_hda_codec_write(codec, spec->cvt[0], 0, AC_VERB_SET_CVT_CHAN_COUNT,
-                           chans - 1);
+       snd_hda_codec_write(codec, spec->cvts[0].cvt_nid, 0,
+                           AC_VERB_SET_CVT_CHAN_COUNT, chans - 1);
        /* FIXME: XXX */
        for (i = 0; i < chans; i++) {
-               snd_hda_codec_write(codec, spec->cvt[0], 0,
+               snd_hda_codec_write(codec, spec->cvts[0].cvt_nid, 0,
                                    AC_VERB_SET_HDMI_CHAN_SLOT,
                                    (i << 4) | i);
        }
@@ -1584,18 +1728,18 @@ static int atihdmi_init(struct hda_codec *codec)
 
        snd_hda_sequence_write(codec, atihdmi_basic_init);
        /* SI codec requires to unmute the pin */
-       if (get_wcaps(codec, spec->pin[0]) & AC_WCAP_OUT_AMP)
-               snd_hda_codec_write(codec, spec->pin[0], 0,
+       if (get_wcaps(codec, spec->pins[0].pin_nid) & AC_WCAP_OUT_AMP)
+               snd_hda_codec_write(codec, spec->pins[0].pin_nid, 0,
                                    AC_VERB_SET_AMP_GAIN_MUTE,
                                    AMP_OUT_UNMUTE);
        return 0;
 }
 
 static const struct hda_codec_ops atihdmi_patch_ops = {
-       .build_controls = generic_hdmi_build_controls,
-       .build_pcms = generic_hdmi_build_pcms,
+       .build_controls = simple_playback_build_controls,
+       .build_pcms = simple_playback_build_pcms,
        .init = atihdmi_init,
-       .free = generic_hdmi_free,
+       .free = simple_playback_free,
 };
 
 
@@ -1613,8 +1757,8 @@ static int patch_atihdmi(struct hda_codec *codec)
        spec->multiout.max_channels = 2;
        spec->multiout.dig_out_nid = ATIHDMI_CVT_NID;
        spec->num_cvts = 1;
-       spec->cvt[0] = ATIHDMI_CVT_NID;
-       spec->pin[0] = ATIHDMI_PIN_NID;
+       spec->cvts[0].cvt_nid = ATIHDMI_CVT_NID;
+       spec->pins[0].pin_nid = ATIHDMI_PIN_NID;
        spec->pcm_playback = &atihdmi_pcm_digital_playback;
 
        codec->patch_ops = atihdmi_patch_ops;
index d21191d..7b96dce 100644 (file)
@@ -348,6 +348,7 @@ struct alc_spec {
        const hda_nid_t *adc_nids;
        const hda_nid_t *capsrc_nids;
        hda_nid_t dig_in_nid;           /* digital-in NID; optional */
+       hda_nid_t mixer_nid;            /* analog-mixer NID */
 
        /* capture setup for dynamic dual-adc switch */
        unsigned int cur_adc_idx;
@@ -1194,18 +1195,8 @@ static void alc_line_automute(struct hda_codec *codec)
        update_speakers(codec);
 }
 
-static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
-                               hda_nid_t nid)
-{
-       hda_nid_t conn[HDA_MAX_NUM_INPUTS];
-       int i, nums;
-
-       nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
-       for (i = 0; i < nums; i++)
-               if (conn[i] == nid)
-                       return i;
-       return -1;
-}
+#define get_connection_index(codec, mux, nid) \
+       snd_hda_get_conn_index(codec, mux, nid, 0)
 
 /* switch the current ADC according to the jack state */
 static void alc_dual_mic_adc_auto_switch(struct hda_codec *codec)
@@ -1365,28 +1356,12 @@ static void set_eapd(struct hda_codec *codec, hda_nid_t nid, int on)
 static void alc_auto_setup_eapd(struct hda_codec *codec, bool on)
 {
        /* We currently only handle front, HP */
-       switch (codec->vendor_id) {
-       case 0x10ec0260:
-               set_eapd(codec, 0x0f, on);
-               set_eapd(codec, 0x10, on);
-               break;
-       case 0x10ec0262:
-       case 0x10ec0267:
-       case 0x10ec0268:
-       case 0x10ec0269:
-       case 0x10ec0270:
-       case 0x10ec0272:
-       case 0x10ec0660:
-       case 0x10ec0662:
-       case 0x10ec0663:
-       case 0x10ec0665:
-       case 0x10ec0862:
-       case 0x10ec0889:
-       case 0x10ec0892:
-               set_eapd(codec, 0x14, on);
-               set_eapd(codec, 0x15, on);
-               break;
-       }
+       static hda_nid_t pins[] = {
+               0x0f, 0x10, 0x14, 0x15, 0
+       };
+       hda_nid_t *p;
+       for (p = pins; *p; p++)
+               set_eapd(codec, *p, on);
 }
 
 /* generic shutup callback;
@@ -1402,6 +1377,7 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type)
 {
        unsigned int tmp;
 
+       alc_auto_setup_eapd(codec, true);
        switch (type) {
        case ALC_INIT_GPIO1:
                snd_hda_sequence_write(codec, alc_gpio1_init_verbs);
@@ -1413,7 +1389,6 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type)
                snd_hda_sequence_write(codec, alc_gpio3_init_verbs);
                break;
        case ALC_INIT_DEFAULT:
-               alc_auto_setup_eapd(codec, true);
                switch (codec->vendor_id) {
                case 0x10ec0260:
                        snd_hda_codec_write(codec, 0x1a, 0,
@@ -1759,6 +1734,15 @@ do_sku:
        return 0;
 }
 
+static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
+{
+       int i;
+       for (i = 0; i < nums; i++)
+               if (list[i] == nid)
+                       return true;
+       return false;
+}
+
 /* check subsystem ID and set up device-specific initialization;
  * return 1 if initialized, 0 if invalid SSID
  */
@@ -1868,9 +1852,9 @@ do_sku:
                        nid = porti;
                else
                        return 1;
-               for (i = 0; i < spec->autocfg.line_outs; i++)
-                       if (spec->autocfg.line_out_pins[i] == nid)
-                               return 1;
+               if (found_in_nid_list(nid, spec->autocfg.line_out_pins,
+                                     spec->autocfg.line_outs))
+                       return 1;
                spec->autocfg.hp_pins[0] = nid;
        }
        return 1;
@@ -2061,15 +2045,23 @@ static void alc_auto_init_digital(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
        int i;
-       hda_nid_t pin;
+       hda_nid_t pin, dac;
 
        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);
-               }
+               if (!pin)
+                       continue;
+               snd_hda_codec_write(codec, pin, 0,
+                                   AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+               if (!i)
+                       dac = spec->multiout.dig_out_nid;
+               else
+                       dac = spec->slave_dig_outs[i - 1];
+               if (!dac || !(get_wcaps(codec, dac) & AC_WCAP_OUT_AMP))
+                       continue;
+               snd_hda_codec_write(codec, dac, 0,
+                                   AC_VERB_SET_AMP_GAIN_MUTE,
+                                   AMP_OUT_UNMUTE);
        }
        pin = spec->autocfg.dig_in_pin;
        if (pin)
@@ -3224,6 +3216,7 @@ static int alc_build_controls(struct hda_codec *codec)
        }
        if (spec->multiout.dig_out_nid) {
                err = snd_hda_create_spdif_out_ctls(codec,
+                                                   spec->multiout.dig_out_nid,
                                                    spec->multiout.dig_out_nid);
                if (err < 0)
                        return err;
@@ -5320,9 +5313,10 @@ static int add_control_with_pfx(struct alc_spec *spec, int type,
 #define ALC880_PIN_CD_NID              0x1c
 
 /* fill in the dac_nids table from the parsed pin configuration */
-static int alc880_auto_fill_dac_nids(struct alc_spec *spec,
-                                    const struct auto_pin_cfg *cfg)
+static int alc880_auto_fill_dac_nids(struct hda_codec *codec)
 {
+       struct alc_spec *spec = codec->spec;
+       const struct auto_pin_cfg *cfg = &spec->autocfg;
        hda_nid_t nid;
        int assigned[4];
        int i, j;
@@ -5358,11 +5352,15 @@ static int alc880_auto_fill_dac_nids(struct alc_spec *spec,
        return 0;
 }
 
-static const char *alc_get_line_out_pfx(struct alc_spec *spec,
-                                       bool can_be_master)
+static const char *alc_get_line_out_pfx(struct alc_spec *spec, int ch,
+                                       bool can_be_master, int *index)
 {
        struct auto_pin_cfg *cfg = &spec->autocfg;
+       static const char * const chname[4] = {
+               "Front", "Surround", NULL /*CLFE*/, "Side"
+       };
 
+       *index = 0;
        if (cfg->line_outs == 1 && !spec->multi_ios &&
            !cfg->hp_outs && !cfg->speaker_outs && can_be_master)
                return "Master";
@@ -5373,23 +5371,23 @@ static const char *alc_get_line_out_pfx(struct alc_spec *spec,
                        return "Speaker";
                break;
        case AUTO_PIN_HP_OUT:
+               /* for multi-io case, only the primary out */
+               if (ch && spec->multi_ios)
+                       break;
+               *index = ch;
                return "Headphone";
        default:
                if (cfg->line_outs == 1 && !spec->multi_ios)
                        return "PCM";
                break;
        }
-       return NULL;
+       return chname[ch];
 }
 
 /* add playback controls from the parsed DAC table */
 static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec,
                                             const struct auto_pin_cfg *cfg)
 {
-       static const char * const chname[4] = {
-               "Front", "Surround", NULL /*CLFE*/, "Side"
-       };
-       const char *pfx = alc_get_line_out_pfx(spec, false);
        hda_nid_t nid;
        int i, err, noutputs;
 
@@ -5398,10 +5396,13 @@ static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec,
                noutputs += spec->multi_ios;
 
        for (i = 0; i < noutputs; i++) {
+               const char *name;
+               int index;
                if (!spec->multiout.dac_nids[i])
                        continue;
                nid = alc880_idx_to_mixer(alc880_dac_to_idx(spec->multiout.dac_nids[i]));
-               if (!pfx && i == 2) {
+               name = alc_get_line_out_pfx(spec, i, false, &index);
+               if (!name) {
                        /* Center/LFE */
                        err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
                                              "Center",
@@ -5428,12 +5429,6 @@ static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec,
                        if (err < 0)
                                return err;
                } else {
-                       const char *name = pfx;
-                       int index = i;
-                       if (!name) {
-                               name = chname[i];
-                               index = 0;
-                       }
                        err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
                                                name, index,
                                          HDA_COMPOSE_AMP_VAL(nid, 3, 0,
@@ -5600,6 +5595,30 @@ static int get_pin_type(int line_out_type)
                return PIN_OUT;
 }
 
+static void alc880_auto_init_dac(struct hda_codec *codec, hda_nid_t dac)
+{
+       hda_nid_t nid, mix;
+
+       if (!dac)
+               return;
+       mix = alc880_idx_to_mixer(alc880_dac_to_idx(dac));
+       if (query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS)
+               nid = dac;
+       else if (query_amp_caps(codec, mix, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS)
+               nid = mix;
+       else
+               nid = 0;
+       if (nid)
+               snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+                                   AMP_OUT_ZERO);
+       if (query_amp_caps(codec, mix, HDA_INPUT) & AC_AMPCAP_MUTE) {
+               snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+                                   AMP_IN_UNMUTE(0));
+               snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+                                   AMP_IN_UNMUTE(1));
+       }
+}
+
 static void alc880_auto_init_multi_out(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
@@ -5610,12 +5629,16 @@ static void alc880_auto_init_multi_out(struct hda_codec *codec)
                int pin_type = get_pin_type(spec->autocfg.line_out_type);
                alc880_auto_set_output_and_unmute(codec, nid, pin_type, i);
        }
+       /* mute DACs */
+       for (i = 0; i < spec->multiout.num_dacs; i++)
+               alc880_auto_init_dac(codec, spec->multiout.dac_nids[i]);
 }
 
 static void alc880_auto_init_extra_out(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
        hda_nid_t pin;
+       int i;
 
        pin = spec->autocfg.speaker_pins[0];
        if (pin) /* connect to front */
@@ -5623,6 +5646,10 @@ static void alc880_auto_init_extra_out(struct hda_codec *codec)
        pin = spec->autocfg.hp_pins[0];
        if (pin) /* connect to front */
                alc880_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
+       /* mute DACs */
+       alc880_auto_init_dac(codec, spec->multiout.hp_nid);
+       for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++)
+               alc880_auto_init_dac(codec, spec->multiout.extra_out_nid[i]);
 }
 
 static void alc880_auto_init_analog_input(struct hda_codec *codec)
@@ -5635,13 +5662,21 @@ static void alc880_auto_init_analog_input(struct hda_codec *codec)
                hda_nid_t nid = cfg->inputs[i].pin;
                if (alc_is_input_pin(codec, nid)) {
                        alc_set_input_pin(codec, nid, cfg->inputs[i].type);
-                       if (nid != ALC880_PIN_CD_NID &&
-                           (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
+                       if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
                                snd_hda_codec_write(codec, nid, 0,
                                                    AC_VERB_SET_AMP_GAIN_MUTE,
                                                    AMP_OUT_MUTE);
                }
        }
+
+       /* mute all loopback inputs */
+       if (spec->mixer_nid) {
+               int nums = snd_hda_get_conn_list(codec, spec->mixer_nid, NULL);
+               for (i = 0; i < nums; i++)
+                       snd_hda_codec_write(codec, spec->mixer_nid, 0,
+                                           AC_VERB_SET_AMP_GAIN_MUTE,
+                                           AMP_IN_MUTE(i));
+       }
 }
 
 static void alc880_auto_init_input_src(struct hda_codec *codec)
@@ -5660,10 +5695,14 @@ static void alc880_auto_init_input_src(struct hda_codec *codec)
                        snd_hda_codec_write(codec, spec->adc_nids[c], 0,
                                            AC_VERB_SET_CONNECT_SEL,
                                            imux->items[0].index);
+               snd_hda_codec_write(codec, spec->adc_nids[c], 0,
+                                   AC_VERB_SET_AMP_GAIN_MUTE,
+                                   AMP_IN_MUTE(0));
        }
 }
 
-static int alc_auto_add_multi_channel_mode(struct hda_codec *codec);
+static int alc_auto_add_multi_channel_mode(struct hda_codec *codec,
+                                          int (*fill_dac)(struct hda_codec *));
 
 /* parse the BIOS configuration and set up the alc_spec */
 /* return 1 if successful, 0 if the proper config is not found,
@@ -5682,10 +5721,10 @@ static int alc880_parse_auto_config(struct hda_codec *codec)
        if (!spec->autocfg.line_outs)
                return 0; /* can't find valid BIOS pin config */
 
-       err = alc880_auto_fill_dac_nids(spec, &spec->autocfg);
+       err = alc880_auto_fill_dac_nids(codec);
        if (err < 0)
                return err;
-       err = alc_auto_add_multi_channel_mode(codec);
+       err = alc_auto_add_multi_channel_mode(codec, alc880_auto_fill_dac_nids);
        if (err < 0)
                return err;
        err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg);
@@ -5711,8 +5750,6 @@ static int alc880_parse_auto_config(struct hda_codec *codec)
        if (spec->kctls.list)
                add_mixer(spec, spec->kctls.list);
 
-       add_verb(spec, alc880_volume_init_verbs);
-
        spec->num_mux_defs = 1;
        spec->input_mux = &spec->private_imux[0];
 
@@ -5982,6 +6019,8 @@ static int patch_alc880(struct hda_codec *codec)
 
        codec->spec = spec;
 
+       spec->mixer_nid = 0x0b;
+
        board_config = snd_hda_check_board_config(codec, ALC880_MODEL_LAST,
                                                  alc880_models,
                                                  alc880_cfg_tbl);
@@ -7132,27 +7171,33 @@ static const struct hda_verb alc260_test_init_verbs[] = {
  * for BIOS auto-configuration
  */
 
+/* convert from pin to volume-mixer widget */
+static hda_nid_t alc260_pin_to_vol_mix(hda_nid_t nid)
+{
+       if (nid >= 0x0f && nid <= 0x11)
+               return nid - 0x7;
+       else if (nid >= 0x12 && nid <= 0x15)
+               return 0x08;
+       else
+               return 0;
+}
+
 static int alc260_add_playback_controls(struct alc_spec *spec, hda_nid_t nid,
                                        const char *pfx, int *vol_bits)
 {
        hda_nid_t nid_vol;
        unsigned long vol_val, sw_val;
-       int err;
+       int chs, err;
 
-       if (nid >= 0x0f && nid < 0x11) {
-               nid_vol = nid - 0x7;
-               vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT);
-               sw_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
-       } else if (nid == 0x11) {
-               nid_vol = nid - 0x7;
-               vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT);
-               sw_val = HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT);
-       } else if (nid >= 0x12 && nid <= 0x15) {
-               nid_vol = 0x08;
-               vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT);
-               sw_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
-       } else
+       nid_vol = alc260_pin_to_vol_mix(nid);
+       if (!nid_vol)
                return 0; /* N/A */
+       if (nid == 0x11)
+               chs = 2;
+       else
+               chs = 3;
+       vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, chs, 0, HDA_OUTPUT);
+       sw_val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT);
 
        if (!(*vol_bits & (1 << nid_vol))) {
                /* first control for the volume widget */
@@ -7182,12 +7227,8 @@ static int alc260_auto_create_multi_out_ctls(struct alc_spec *spec,
        nid = cfg->line_out_pins[0];
        if (nid) {
                const char *pfx;
-               if (!cfg->speaker_pins[0] && !cfg->hp_pins[0])
-                       pfx = "Master";
-               else if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
-                       pfx = "Speaker";
-               else
-                       pfx = "Front";
+               int index;
+               pfx = alc_get_line_out_pfx(spec, 0, true, &index);
                err = alc260_add_playback_controls(spec, nid, pfx, &vols);
                if (err < 0)
                        return err;
@@ -7221,6 +7262,8 @@ static void alc260_auto_set_output_and_unmute(struct hda_codec *codec,
                                              hda_nid_t nid, int pin_type,
                                              int sel_idx)
 {
+       hda_nid_t mix;
+
        alc_set_pin_output(codec, nid, pin_type);
        /* need the manual connection? */
        if (nid >= 0x12) {
@@ -7228,6 +7271,16 @@ static void alc260_auto_set_output_and_unmute(struct hda_codec *codec,
                snd_hda_codec_write(codec, idx + 0x0b, 0,
                                    AC_VERB_SET_CONNECT_SEL, sel_idx);
        }
+
+       mix = alc260_pin_to_vol_mix(nid);
+       if (!mix)
+               return;
+       snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+                           AMP_OUT_ZERO);
+       snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+                           AMP_IN_UNMUTE(0));
+       snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+                           AMP_IN_UNMUTE(1));
 }
 
 static void alc260_auto_init_multi_out(struct hda_codec *codec)
@@ -7250,72 +7303,9 @@ static void alc260_auto_init_multi_out(struct hda_codec *codec)
                alc260_auto_set_output_and_unmute(codec, nid, PIN_HP, 0);
 }
 
-#define ALC260_PIN_CD_NID              0x16
-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 < cfg->num_inputs; i++) {
-               hda_nid_t nid = cfg->inputs[i].pin;
-               if (nid >= 0x12) {
-                       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,
-                                                   AC_VERB_SET_AMP_GAIN_MUTE,
-                                                   AMP_OUT_MUTE);
-               }
-       }
-}
-
+#define alc260_auto_init_analog_input  alc880_auto_init_analog_input
 #define alc260_auto_init_input_src     alc880_auto_init_input_src
 
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static const struct hda_verb alc260_volume_init_verbs[] = {
-       /*
-        * Unmute ADC0-1 and set the default input to mic-in
-        */
-       {0x04, AC_VERB_SET_CONNECT_SEL, 0x00},
-       {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x05, AC_VERB_SET_CONNECT_SEL, 0x00},
-       {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-       /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
-        * mixer widget
-        * Note: PASD motherboards uses the Line In 2 as the input for
-        * front panel mic (mic 2)
-        */
-       /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
-       /* mute analog inputs */
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
-       /*
-        * Set up output mixers (0x08 - 0x0a)
-        */
-       /* set vol=0 to output mixers */
-       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       /* set up input amps for analog loopback */
-       /* Amp Indices: DAC = 0, mixer = 1 */
-       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
-       { }
-};
-
 static int alc260_parse_auto_config(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
@@ -7342,8 +7332,6 @@ static int alc260_parse_auto_config(struct hda_codec *codec)
        if (spec->kctls.list)
                add_mixer(spec, spec->kctls.list);
 
-       add_verb(spec, alc260_volume_init_verbs);
-
        spec->num_mux_defs = 1;
        spec->input_mux = &spec->private_imux[0];
 
@@ -7590,6 +7578,8 @@ static int patch_alc260(struct hda_codec *codec)
 
        codec->spec = spec;
 
+       spec->mixer_nid = 0x07;
+
        board_config = snd_hda_check_board_config(codec, ALC260_MODEL_LAST,
                                                  alc260_models,
                                                  alc260_cfg_tbl);
@@ -9037,48 +9027,6 @@ static void alc885_imac24_init_hook(struct hda_codec *codec)
        alc_hp_automute(codec);
 }
 
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static const struct hda_verb alc883_auto_init_verbs[] = {
-       /*
-        * Unmute ADC0-2 and set the default input to mic-in
-        */
-       {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
-       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
-       {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-       /*
-        * Set up output mixers (0x0c - 0x0f)
-        */
-       /* set vol=0 to output mixers */
-       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       /* set up input amps for analog loopback */
-       /* Amp Indices: DAC = 0, mixer = 1 */
-       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
-       /* FIXME: use matrix-type input source selection */
-       /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
-       /* Input mixer2 */
-       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       /* Input mixer3 */
-       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       { }
-};
-
 /* 2ch mode (Speaker:front, Subwoofer:CLFE, Line:input, Headphones:front) */
 static const struct hda_verb alc889A_mb31_ch2_init[] = {
        {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},             /* HP as front */
@@ -11038,6 +10986,9 @@ static void alc882_auto_set_output_and_unmute(struct hda_codec *codec,
        /* set as output */
        alc_set_pin_output(codec, nid, pin_type);
 
+       if (snd_hda_get_conn_list(codec, nid, NULL) < 2)
+               return;
+
        if (dac == 0x25)
                idx = 4;
        else if (dac >= 0x02 && dac <= 0x05)
@@ -11047,6 +10998,8 @@ static void alc882_auto_set_output_and_unmute(struct hda_codec *codec,
        snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx);
 }
 
+#define alc882_auto_init_dac   alc880_auto_init_dac
+
 static void alc882_auto_init_multi_out(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
@@ -11059,6 +11012,9 @@ static void alc882_auto_init_multi_out(struct hda_codec *codec)
                        alc882_auto_set_output_and_unmute(codec, nid, pin_type,
                                        spec->multiout.dac_nids[i]);
        }
+       /* mute DACs */
+       for (i = 0; i < spec->multiout.num_dacs; i++)
+               alc882_auto_init_dac(codec, spec->multiout.dac_nids[i]);
 }
 
 static void alc882_auto_init_hp_out(struct hda_codec *codec)
@@ -11090,49 +11046,47 @@ static void alc882_auto_init_hp_out(struct hda_codec *codec)
                        alc882_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac);
                }
        }
-}
-
-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 < 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,
-                                           AMP_OUT_MUTE);
-       }
+       /* mute DACs */
+       alc882_auto_init_dac(codec, spec->multiout.hp_nid);
+       for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++)
+               alc882_auto_init_dac(codec, spec->multiout.extra_out_nid[i]);
 }
 
+#define alc882_auto_init_analog_input  alc880_auto_init_analog_input
+
 static void alc882_auto_init_input_src(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
        int c;
 
        for (c = 0; c < spec->num_adc_nids; c++) {
-               hda_nid_t conn_list[HDA_MAX_NUM_INPUTS];
                hda_nid_t nid = spec->capsrc_nids[c];
                unsigned int mux_idx;
                const struct hda_input_mux *imux;
                int conns, mute, idx, item;
+               unsigned int wid_type;
 
                /* mute ADC */
-               snd_hda_codec_write(codec, spec->adc_nids[c], 0,
+               if (query_amp_caps(codec, spec->adc_nids[c], HDA_INPUT) &
+                   AC_AMPCAP_MUTE)
+                       snd_hda_codec_write(codec, spec->adc_nids[c], 0,
                                    AC_VERB_SET_AMP_GAIN_MUTE,
                                    AMP_IN_MUTE(0));
+               else if (query_amp_caps(codec, nid, HDA_OUTPUT) &
+                        AC_AMPCAP_MUTE)
+                       snd_hda_codec_write(codec, nid, 0,
+                                   AC_VERB_SET_AMP_GAIN_MUTE,
+                                   AMP_OUT_MUTE);
 
-               conns = snd_hda_get_connections(codec, nid, conn_list,
-                                               ARRAY_SIZE(conn_list));
-               if (conns < 0)
+               conns = snd_hda_get_conn_list(codec, nid, NULL);
+               if (conns <= 0)
                        continue;
                mux_idx = c >= spec->num_mux_defs ? 0 : c;
                imux = &spec->input_mux[mux_idx];
                if (!imux->num_items && mux_idx > 0)
                        imux = &spec->input_mux[0];
+               wid_type = get_wcaps_type(get_wcaps(codec, nid));
                for (idx = 0; idx < conns; idx++) {
                        /* if the current connection is the selected one,
                         * unmute it as default - otherwise mute it
@@ -11145,17 +11099,13 @@ static void alc882_auto_init_input_src(struct hda_codec *codec)
                                        break;
                                }
                        }
-                       /* check if we have a selector or mixer
-                        * we could check for the widget type instead, but
-                        * just check for Amp-In presence (in case of mixer
-                        * without amp-in there is something wrong, this
-                        * function shouldn't be used or capsrc nid is wrong)
-                        */
-                       if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)
+                       /* initialize the mute status if mute-amp is present */
+                       if (query_amp_caps(codec, nid, HDA_INPUT) & AC_AMPCAP_MUTE)
                                snd_hda_codec_write(codec, nid, 0,
                                                    AC_VERB_SET_AMP_GAIN_MUTE,
                                                    mute);
-                       else if (mute != AMP_IN_MUTE(idx))
+                       if (wid_type == AC_WID_AUD_SEL &&
+                           mute != AMP_IN_MUTE(idx))
                                snd_hda_codec_write(codec, nid, 0,
                                                    AC_VERB_SET_CONNECT_SEL,
                                                    idx);
@@ -11214,10 +11164,10 @@ static int alc882_parse_auto_config(struct hda_codec *codec)
        if (!spec->autocfg.line_outs)
                return 0; /* can't find valid BIOS pin config */
 
-       err = alc880_auto_fill_dac_nids(spec, &spec->autocfg);
+       err = alc880_auto_fill_dac_nids(codec);
        if (err < 0)
                return err;
-       err = alc_auto_add_multi_channel_mode(codec);
+       err = alc_auto_add_multi_channel_mode(codec, alc880_auto_fill_dac_nids);
        if (err < 0)
                return err;
        err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg);
@@ -11243,7 +11193,6 @@ static int alc882_parse_auto_config(struct hda_codec *codec)
        if (spec->kctls.list)
                add_mixer(spec, spec->kctls.list);
 
-       add_verb(spec, alc883_auto_init_verbs);
        /* if ADC 0x07 is available, initialize it, too */
        if (get_wcaps_type(get_wcaps(codec, 0x07)) == AC_WID_AUD_IN)
                add_verb(spec, alc882_adc1_init_verbs);
@@ -11284,6 +11233,8 @@ static int patch_alc882(struct hda_codec *codec)
 
        codec->spec = spec;
 
+       spec->mixer_nid = 0x0b;
+
        switch (codec->vendor_id) {
        case 0x10ec0882:
        case 0x10ec0885:
@@ -11355,7 +11306,6 @@ static int patch_alc882(struct hda_codec *codec)
                for (i = 0; i < ARRAY_SIZE(alc882_adc_nids); i++) {
                        const struct hda_input_mux *imux = spec->input_mux;
                        hda_nid_t cap;
-                       hda_nid_t items[16];
                        hda_nid_t nid = alc882_adc_nids[i];
                        unsigned int wcap = get_wcaps(codec, nid);
                        /* get type */
@@ -11366,8 +11316,7 @@ static int patch_alc882(struct hda_codec *codec)
                        err = snd_hda_get_connections(codec, nid, &cap, 1);
                        if (err < 0)
                                continue;
-                       err = snd_hda_get_connections(codec, cap, items,
-                                                     ARRAY_SIZE(items));
+                       err = snd_hda_get_conn_list(codec, cap, NULL);
                        if (err < 0)
                                continue;
                        for (j = 0; j < imux->num_items; j++)
@@ -12256,17 +12205,18 @@ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec,
 {
        const char *pfx;
        int vbits;
-       int i, err;
+       int i, index, err;
 
        spec->multiout.num_dacs = 1;    /* only use one dac */
        spec->multiout.dac_nids = spec->private_dac_nids;
        spec->private_dac_nids[0] = 2;
 
-       pfx = alc_get_line_out_pfx(spec, true);
-       if (!pfx)
-               pfx = "Front";
        for (i = 0; i < 2; i++) {
-               err = alc262_add_out_sw_ctl(spec, cfg->line_out_pins[i], pfx, i);
+               pfx = alc_get_line_out_pfx(spec, i, true, &index);
+               if (!pfx)
+                       pfx = "PCM";
+               err = alc262_add_out_sw_ctl(spec, cfg->line_out_pins[i], pfx,
+                                           index);
                if (err < 0)
                        return err;
                if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
@@ -12286,10 +12236,11 @@ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec,
        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 */
        vbits = 0;
        for (i = 0; i < 2; i++) {
+               pfx = alc_get_line_out_pfx(spec, i, true, &index);
+               if (!pfx)
+                       pfx = "PCM";
                err = alc262_add_out_vol_ctl(spec, cfg->line_out_pins[i], pfx,
                                             &vbits, i);
                if (err < 0)
@@ -12313,70 +12264,6 @@ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec,
 #define alc262_auto_create_input_ctls \
        alc882_auto_create_input_ctls
 
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static const struct hda_verb alc262_volume_init_verbs[] = {
-       /*
-        * Unmute ADC0-2 and set the default input to mic-in
-        */
-       {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
-       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
-       {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-       /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
-        * mixer widget
-        * Note: PASD motherboards uses the Line In 2 as the input for
-        * front panel mic (mic 2)
-        */
-       /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
-       /*
-        * Set up output mixers (0x0c - 0x0f)
-        */
-       /* set vol=0 to output mixers */
-       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
-       /* set up input amps for analog loopback */
-       /* Amp Indices: DAC = 0, mixer = 1 */
-       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
-       /* FIXME: use matrix-type input source selection */
-       /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
-       /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
-       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
-       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
-       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
-       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
-       /* Input mixer2 */
-       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
-       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
-       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
-       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
-       /* Input mixer3 */
-       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
-       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
-       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
-       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
-
-       { }
-};
-
 static const struct hda_verb alc262_HP_BPC_init_verbs[] = {
        /*
         * Unmute ADC0-2 and set the default input to mic-in
@@ -12674,7 +12561,6 @@ static int alc262_parse_auto_config(struct hda_codec *codec)
        if (spec->kctls.list)
                add_mixer(spec, spec->kctls.list);
 
-       add_verb(spec, alc262_volume_init_verbs);
        spec->num_mux_defs = 1;
        spec->input_mux = &spec->private_imux[0];
 
@@ -13034,6 +12920,9 @@ static int patch_alc262(struct hda_codec *codec)
                return -ENOMEM;
 
        codec->spec = spec;
+
+       spec->mixer_nid = 0x0b;
+
 #if 0
        /* pshou 07/11/05  set a zero PCM sample to DAC when FIFO is
         * under-run
@@ -13450,6 +13339,8 @@ static const struct hda_verb alc268_base_init_verbs[] = {
        { }
 };
 
+/* only for model=test */
+#ifdef CONFIG_SND_DEBUG
 /*
  * generic initialization of ADC, input mixers and output mixers
  */
@@ -13470,12 +13361,15 @@ static const struct hda_verb alc268_volume_init_verbs[] = {
 
        {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
        {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       { }
+};
+#endif /* CONFIG_SND_DEBUG */
 
-       /* set PCBEEP vol = 0, mute connections */
+/* set PCBEEP vol = 0, mute connections */
+static const struct hda_verb alc268_beep_init_verbs[] = {
        {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
        {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
        {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-
        { }
 };
 
@@ -13635,10 +13529,8 @@ static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec,
        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";
+               int index;
+               name = alc_get_line_out_pfx(spec, 0, true, &index);
                err = alc268_new_analog_output(spec, nid, name, 0);
                if (err < 0)
                        return err;
@@ -13685,6 +13577,8 @@ static void alc268_auto_set_output_and_unmute(struct hda_codec *codec,
        int idx;
 
        alc_set_pin_output(codec, nid, pin_type);
+       if (snd_hda_get_conn_list(codec, nid, NULL) <= 1)
+               return;
        if (nid == 0x14 || nid == 0x16)
                idx = 0;
        else
@@ -13692,6 +13586,14 @@ static void alc268_auto_set_output_and_unmute(struct hda_codec *codec,
        snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx);
 }
 
+static void alc268_auto_init_dac(struct hda_codec *codec, hda_nid_t nid)
+{
+       if (!nid)
+               return;
+       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+                           AMP_OUT_ZERO);
+}
+
 static void alc268_auto_init_multi_out(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
@@ -13702,6 +13604,9 @@ static void alc268_auto_init_multi_out(struct hda_codec *codec)
                int pin_type = get_pin_type(spec->autocfg.line_out_type);
                alc268_auto_set_output_and_unmute(codec, nid, pin_type);
        }
+       /* mute DACs */
+       for (i = 0; i < spec->multiout.num_dacs; i++)
+               alc268_auto_init_dac(codec, spec->multiout.dac_nids[i]);
 }
 
 static void alc268_auto_init_hp_out(struct hda_codec *codec)
@@ -13721,6 +13626,10 @@ static void alc268_auto_init_hp_out(struct hda_codec *codec)
        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);
+       /* mute DACs */
+       alc268_auto_init_dac(codec, spec->multiout.hp_nid);
+       for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++)
+               alc268_auto_init_dac(codec, spec->multiout.extra_out_nid[i]);
 }
 
 static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec)
@@ -13811,10 +13720,11 @@ static int alc268_parse_auto_config(struct hda_codec *codec)
        if (spec->kctls.list)
                add_mixer(spec, spec->kctls.list);
 
-       if (!spec->no_analog && spec->autocfg.speaker_pins[0] != 0x1d)
+       if (!spec->no_analog && spec->autocfg.speaker_pins[0] != 0x1d) {
                add_mixer(spec, alc268_beep_mixer);
+               add_verb(spec, alc268_beep_init_verbs);
+       }
 
-       add_verb(spec, alc268_volume_init_verbs);
        spec->num_mux_defs = 2;
        spec->input_mux = &spec->private_imux[0];
 
@@ -14039,7 +13949,8 @@ static const struct alc_config_preset alc268_presets[] = {
        [ALC268_TEST] = {
                .mixers = { alc268_test_mixer, alc268_capture_mixer },
                .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
-                               alc268_volume_init_verbs },
+                               alc268_volume_init_verbs,
+                               alc268_beep_init_verbs },
                .num_dacs = ARRAY_SIZE(alc268_dac_nids),
                .dac_nids = alc268_dac_nids,
                .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
@@ -14066,6 +13977,8 @@ static int patch_alc268(struct hda_codec *codec)
 
        codec->spec = spec;
 
+       /* ALC268 has no aa-loopback mixer */
+
        board_config = snd_hda_check_board_config(codec, ALC268_MODEL_LAST,
                                                  alc268_models,
                                                  alc268_cfg_tbl);
@@ -14777,13 +14690,10 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
        if (spec->kctls.list)
                add_mixer(spec, spec->kctls.list);
 
-       if (spec->codec_variant != ALC269_TYPE_NORMAL) {
-               add_verb(spec, alc269vb_init_verbs);
+       if (spec->codec_variant != ALC269_TYPE_NORMAL)
                alc_ssid_check(codec, 0, 0x1b, 0x14, 0x21);
-       } else {
-               add_verb(spec, alc269_init_verbs);
+       else
                alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
-       }
 
        spec->num_mux_defs = 1;
        spec->input_mux = &spec->private_imux[0];
@@ -15233,6 +15143,8 @@ static int patch_alc269(struct hda_codec *codec)
 
        codec->spec = spec;
 
+       spec->mixer_nid = 0x0b;
+
        alc_auto_parse_customize_define(codec);
 
        if (codec->vendor_id == 0x10ec0269) {
@@ -15860,58 +15772,6 @@ static const struct hda_verb alc861_asus_laptop_init_verbs[] = {
        { }
 };
 
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static const struct hda_verb alc861_auto_init_verbs[] = {
-       /*
-        * Unmute ADC0 and set the default input to mic-in
-        */
-       /* {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, */
-       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-       /* Unmute DAC0~3 & spdif out*/
-       {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
-       /* Unmute Mixer 14 (mic) 1c (Line in)*/
-       {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
-       /* Unmute Stereo Mixer 15 */
-       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
-       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c},
-
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-
-       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-
-       {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},  /* set Mic 1 */
-
-       { }
-};
-
 static const struct hda_verb alc861_toshiba_init_verbs[] = {
        {0x0f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
 
@@ -15979,7 +15839,7 @@ static hda_nid_t alc861_look_for_dac(struct hda_codec *codec, hda_nid_t pin)
 {
        struct alc_spec *spec = codec->spec;
        hda_nid_t mix, srcs[5];
-       int i, j, num;
+       int i, num;
 
        if (snd_hda_get_connections(codec, pin, &mix, 1) != 1)
                return 0;
@@ -15991,20 +15851,18 @@ static hda_nid_t alc861_look_for_dac(struct hda_codec *codec, hda_nid_t pin)
                type = get_wcaps_type(get_wcaps(codec, srcs[i]));
                if (type != AC_WID_AUD_OUT)
                        continue;
-               for (j = 0; j < spec->multiout.num_dacs; j++)
-                       if (spec->multiout.dac_nids[j] == srcs[i])
-                               break;
-               if (j >= spec->multiout.num_dacs)
+               if (!found_in_nid_list(srcs[i], spec->multiout.dac_nids,
+                                      spec->multiout.num_dacs))
                        return srcs[i];
        }
        return 0;
 }
 
 /* fill in the dac_nids table from the parsed pin configuration */
-static int alc861_auto_fill_dac_nids(struct hda_codec *codec,
-                                    const struct auto_pin_cfg *cfg)
+static int alc861_auto_fill_dac_nids(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
+       const struct auto_pin_cfg *cfg = &spec->autocfg;
        int i;
        hda_nid_t nid, dac;
 
@@ -16034,10 +15892,6 @@ static int alc861_auto_create_multi_out_ctls(struct hda_codec *codec,
                                             const struct auto_pin_cfg *cfg)
 {
        struct alc_spec *spec = codec->spec;
-       static const char * const chname[4] = {
-               "Front", "Surround", NULL /*CLFE*/, "Side"
-       };
-       const char *pfx = alc_get_line_out_pfx(spec, true);
        hda_nid_t nid;
        int i, err, noutputs;
 
@@ -16046,10 +15900,13 @@ static int alc861_auto_create_multi_out_ctls(struct hda_codec *codec,
                noutputs += spec->multi_ios;
 
        for (i = 0; i < noutputs; i++) {
+               const char *name;
+               int index;
                nid = spec->multiout.dac_nids[i];
                if (!nid)
                        continue;
-               if (!pfx && i == 2) {
+               name = alc_get_line_out_pfx(spec, i, true, &index);
+               if (!name) {
                        /* Center/LFE */
                        err = alc861_create_out_sw(codec, "Center", nid, 1);
                        if (err < 0)
@@ -16058,12 +15915,6 @@ static int alc861_auto_create_multi_out_ctls(struct hda_codec *codec,
                        if (err < 0)
                                return err;
                } else {
-                       const char *name = pfx;
-                       int index = i;
-                       if (!name) {
-                               name = chname[i];
-                               index = 0;
-                       }
                        err = __alc861_create_out_sw(codec, name, nid, index, 3);
                        if (err < 0)
                                return err;
@@ -16132,7 +15983,7 @@ static void alc861_auto_init_multi_out(struct hda_codec *codec)
        struct alc_spec *spec = codec->spec;
        int i;
 
-       for (i = 0; i < spec->autocfg.line_outs; i++) {
+       for (i = 0; i < spec->autocfg.line_outs + spec->multi_ios; i++) {
                hda_nid_t nid = spec->autocfg.line_out_pins[i];
                int pin_type = get_pin_type(spec->autocfg.line_out_type);
                if (nid)
@@ -16157,18 +16008,7 @@ static void alc861_auto_init_hp_out(struct hda_codec *codec)
                                                  spec->multiout.dac_nids[0]);
 }
 
-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 < cfg->num_inputs; i++) {
-               hda_nid_t nid = cfg->inputs[i].pin;
-               if (nid >= 0x0c && nid <= 0x11)
-                       alc_set_input_pin(codec, nid, cfg->inputs[i].type);
-       }
-}
+#define alc861_auto_init_analog_input  alc880_auto_init_analog_input
 
 /* parse the BIOS configuration and set up the alc_spec */
 /* return 1 if successful, 0 if the proper config is not found,
@@ -16187,10 +16027,10 @@ static int alc861_parse_auto_config(struct hda_codec *codec)
        if (!spec->autocfg.line_outs)
                return 0; /* can't find valid BIOS pin config */
 
-       err = alc861_auto_fill_dac_nids(codec, &spec->autocfg);
+       err = alc861_auto_fill_dac_nids(codec);
        if (err < 0)
                return err;
-       err = alc_auto_add_multi_channel_mode(codec);
+       err = alc_auto_add_multi_channel_mode(codec, alc861_auto_fill_dac_nids);
        if (err < 0)
                return err;
        err = alc861_auto_create_multi_out_ctls(codec, &spec->autocfg);
@@ -16210,8 +16050,6 @@ static int alc861_parse_auto_config(struct hda_codec *codec)
        if (spec->kctls.list)
                add_mixer(spec, spec->kctls.list);
 
-       add_verb(spec, alc861_auto_init_verbs);
-
        spec->num_mux_defs = 1;
        spec->input_mux = &spec->private_imux[0];
 
@@ -16426,6 +16264,8 @@ static int patch_alc861(struct hda_codec *codec)
 
        codec->spec = spec;
 
+       spec->mixer_nid = 0x15;
+
         board_config = snd_hda_check_board_config(codec, ALC861_MODEL_LAST,
                                                  alc861_models,
                                                  alc861_cfg_tbl);
@@ -17111,61 +16951,9 @@ static int alc861vd_auto_create_input_ctls(struct hda_codec *codec,
 }
 
 
-static void alc861vd_auto_set_output_and_unmute(struct hda_codec *codec,
-                               hda_nid_t nid, int pin_type, int dac_idx)
-{
-       alc_set_pin_output(codec, nid, pin_type);
-}
-
-static void alc861vd_auto_init_multi_out(struct hda_codec *codec)
-{
-       struct alc_spec *spec = codec->spec;
-       int i;
-
-       for (i = 0; i <= HDA_SIDE; i++) {
-               hda_nid_t nid = spec->autocfg.line_out_pins[i];
-               int pin_type = get_pin_type(spec->autocfg.line_out_type);
-               if (nid)
-                       alc861vd_auto_set_output_and_unmute(codec, nid,
-                                                           pin_type, i);
-       }
-}
-
-
-static void alc861vd_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) /* connect to front and use dac 0 */
-               alc861vd_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
-       pin = spec->autocfg.speaker_pins[0];
-       if (pin)
-               alc861vd_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
-}
-
-#define ALC861VD_PIN_CD_NID            ALC880_PIN_CD_NID
-
-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 < 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, 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,
-                                               AC_VERB_SET_AMP_GAIN_MUTE,
-                                               AMP_OUT_MUTE);
-               }
-       }
-}
-
+#define alc861vd_auto_init_multi_out   alc882_auto_init_multi_out
+#define alc861vd_auto_init_hp_out      alc882_auto_init_hp_out
+#define alc861vd_auto_init_analog_input        alc882_auto_init_analog_input
 #define alc861vd_auto_init_input_src   alc882_auto_init_input_src
 
 #define alc861vd_idx_to_mixer_vol(nid)         ((nid) + 0x02)
@@ -17177,10 +16965,6 @@ static void alc861vd_auto_init_analog_input(struct hda_codec *codec)
 static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec,
                                             const struct auto_pin_cfg *cfg)
 {
-       static const char * const chname[4] = {
-               "Front", "Surround", "CLFE", "Side"
-       };
-       const char *pfx = alc_get_line_out_pfx(spec, true);
        hda_nid_t nid_v, nid_s;
        int i, err, noutputs;
 
@@ -17189,6 +16973,8 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec,
                noutputs += spec->multi_ios;
 
        for (i = 0; i < noutputs; i++) {
+               const char *name;
+               int index;
                if (!spec->multiout.dac_nids[i])
                        continue;
                nid_v = alc861vd_idx_to_mixer_vol(
@@ -17198,7 +16984,8 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec,
                                alc880_dac_to_idx(
                                        spec->multiout.dac_nids[i]));
 
-               if (!pfx && i == 2) {
+               name = alc_get_line_out_pfx(spec, i, true, &index);
+               if (!name) {
                        /* Center/LFE */
                        err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
                                              "Center",
@@ -17225,12 +17012,6 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec,
                        if (err < 0)
                                return err;
                } else {
-                       const char *name = pfx;
-                       int index = i;
-                       if (!name) {
-                               name = chname[i];
-                               index = 0;
-                       }
                        err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
                                                name, index,
                                          HDA_COMPOSE_AMP_VAL(nid_v, 3, 0,
@@ -17310,10 +17091,10 @@ static int alc861vd_parse_auto_config(struct hda_codec *codec)
        if (!spec->autocfg.line_outs)
                return 0; /* can't find valid BIOS pin config */
 
-       err = alc880_auto_fill_dac_nids(spec, &spec->autocfg);
+       err = alc880_auto_fill_dac_nids(codec);
        if (err < 0)
                return err;
-       err = alc_auto_add_multi_channel_mode(codec);
+       err = alc_auto_add_multi_channel_mode(codec, alc880_auto_fill_dac_nids);
        if (err < 0)
                return err;
        err = alc861vd_auto_create_multi_out_ctls(spec, &spec->autocfg);
@@ -17340,8 +17121,6 @@ static int alc861vd_parse_auto_config(struct hda_codec *codec)
        if (spec->kctls.list)
                add_mixer(spec, spec->kctls.list);
 
-       add_verb(spec, alc861vd_volume_init_verbs);
-
        spec->num_mux_defs = 1;
        spec->input_mux = &spec->private_imux[0];
 
@@ -17400,6 +17179,8 @@ static int patch_alc861vd(struct hda_codec *codec)
 
        codec->spec = spec;
 
+       spec->mixer_nid = 0x0b;
+
        board_config = snd_hda_check_board_config(codec, ALC861VD_MODEL_LAST,
                                                  alc861vd_models,
                                                  alc861vd_cfg_tbl);
@@ -18965,7 +18746,7 @@ static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin)
 {
        struct alc_spec *spec = codec->spec;
        hda_nid_t srcs[5];
-       int i, j, num;
+       int i, num;
 
        pin = alc_go_down_to_selector(codec, pin);
        num = snd_hda_get_connections(codec, pin, srcs, ARRAY_SIZE(srcs));
@@ -18973,30 +18754,78 @@ static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin)
                hda_nid_t nid = alc_auto_mix_to_dac(codec, srcs[i]);
                if (!nid)
                        continue;
-               for (j = 0; j < spec->multiout.num_dacs; j++)
-                       if (spec->multiout.dac_nids[j] == nid)
-                               break;
-               if (j >= spec->multiout.num_dacs)
-                       return nid;
+               if (found_in_nid_list(nid, spec->multiout.dac_nids,
+                                     spec->multiout.num_dacs))
+                       continue;
+               if (spec->multiout.hp_nid == nid)
+                       continue;
+               if (found_in_nid_list(nid, spec->multiout.extra_out_nid,
+                                     ARRAY_SIZE(spec->multiout.extra_out_nid)))
+                   continue;
+               return nid;
        }
        return 0;
 }
 
+static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin)
+{
+       hda_nid_t sel = alc_go_down_to_selector(codec, pin);
+       if (snd_hda_get_conn_list(codec, sel, NULL) == 1)
+               return alc_auto_look_for_dac(codec, pin);
+       return 0;
+}
+
 /* fill in the dac_nids table from the parsed pin configuration */
-static int alc662_auto_fill_dac_nids(struct hda_codec *codec,
-                                    const struct auto_pin_cfg *cfg)
+static int alc662_auto_fill_dac_nids(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
+       const struct auto_pin_cfg *cfg = &spec->autocfg;
+       bool redone;
        int i;
-       hda_nid_t dac;
 
+ again:
+       spec->multiout.num_dacs = 0;
+       spec->multiout.hp_nid = 0;
+       spec->multiout.extra_out_nid[0] = 0;
+       memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids));
        spec->multiout.dac_nids = spec->private_dac_nids;
+
+       /* fill hard-wired DACs first */
+       if (!redone) {
+               for (i = 0; i < cfg->line_outs; i++)
+                       spec->private_dac_nids[i] =
+                               get_dac_if_single(codec, cfg->line_out_pins[i]);
+               if (cfg->hp_outs)
+                       spec->multiout.hp_nid =
+                               get_dac_if_single(codec, cfg->hp_pins[0]);
+               if (cfg->speaker_outs)
+                       spec->multiout.extra_out_nid[0] =
+                               get_dac_if_single(codec, cfg->speaker_pins[0]);
+       }
+
        for (i = 0; i < cfg->line_outs; i++) {
-               dac = alc_auto_look_for_dac(codec, cfg->line_out_pins[i]);
-               if (!dac)
+               hda_nid_t pin = cfg->line_out_pins[i];
+               if (spec->private_dac_nids[i])
                        continue;
-               spec->private_dac_nids[spec->multiout.num_dacs++] = dac;
+               spec->private_dac_nids[i] = alc_auto_look_for_dac(codec, pin);
+               if (!spec->private_dac_nids[i] && !redone) {
+                       /* if we can't find primary DACs, re-probe without
+                        * checking the hard-wired DACs
+                        */
+                       redone = true;
+                       goto again;
+               }
        }
+
+       for (i = 0; i < cfg->line_outs; i++) {
+               if (spec->private_dac_nids[i])
+                       spec->multiout.num_dacs++;
+               else
+                       memmove(spec->private_dac_nids + i,
+                               spec->private_dac_nids + i + 1,
+                               sizeof(hda_nid_t) * (cfg->line_outs - i - 1));
+       }
+
        return 0;
 }
 
@@ -19028,10 +18857,6 @@ static int alc662_auto_create_multi_out_ctls(struct hda_codec *codec,
                                             const struct auto_pin_cfg *cfg)
 {
        struct alc_spec *spec = codec->spec;
-       static const char * const chname[4] = {
-               "Front", "Surround", NULL /*CLFE*/, "Side"
-       };
-       const char *pfx = alc_get_line_out_pfx(spec, true);
        hda_nid_t nid, mix, pin;
        int i, err, noutputs;
 
@@ -19040,6 +18865,8 @@ static int alc662_auto_create_multi_out_ctls(struct hda_codec *codec,
                noutputs += spec->multi_ios;
 
        for (i = 0; i < noutputs; i++) {
+               const char *name;
+               int index;
                nid = spec->multiout.dac_nids[i];
                if (!nid)
                        continue;
@@ -19050,7 +18877,8 @@ static int alc662_auto_create_multi_out_ctls(struct hda_codec *codec,
                mix = alc_auto_dac_to_mix(codec, pin, nid);
                if (!mix)
                        continue;
-               if (!pfx && i == 2) {
+               name = alc_get_line_out_pfx(spec, i, true, &index);
+               if (!name) {
                        /* Center/LFE */
                        err = alc662_add_vol_ctl(spec, "Center", nid, 1);
                        if (err < 0)
@@ -19065,12 +18893,6 @@ static int alc662_auto_create_multi_out_ctls(struct hda_codec *codec,
                        if (err < 0)
                                return err;
                } else {
-                       const char *name = pfx;
-                       int index = i;
-                       if (!name) {
-                               name = chname[i];
-                               index = 0;
-                       }
                        err = __alc662_add_vol_ctl(spec, name, nid, index, 3);
                        if (err < 0)
                                return err;
@@ -19083,18 +18905,16 @@ static int alc662_auto_create_multi_out_ctls(struct hda_codec *codec,
 }
 
 /* add playback controls for speaker and HP outputs */
-/* return DAC nid if any new DAC is assigned */
 static int alc662_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
-                                       const char *pfx)
+                                       hda_nid_t dac, const char *pfx)
 {
        struct alc_spec *spec = codec->spec;
-       hda_nid_t nid, mix;
+       hda_nid_t mix;
        int err;
 
        if (!pin)
                return 0;
-       nid = alc_auto_look_for_dac(codec, pin);
-       if (!nid) {
+       if (!dac) {
                /* the corresponding DAC is already occupied */
                if (!(get_wcaps(codec, pin) & AC_WCAP_OUT_AMP))
                        return 0; /* no way */
@@ -19103,16 +18923,16 @@ static int alc662_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
                                   HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
        }
 
-       mix = alc_auto_dac_to_mix(codec, pin, nid);
+       mix = alc_auto_dac_to_mix(codec, pin, dac);
        if (!mix)
                return 0;
-       err = alc662_add_vol_ctl(spec, pfx, nid, 3);
+       err = alc662_add_vol_ctl(spec, pfx, dac, 3);
        if (err < 0)
                return err;
        err = alc662_add_sw_ctl(spec, pfx, mix, 3);
        if (err < 0)
                return err;
-       return nid;
+       return 0;
 }
 
 /* create playback/capture controls for input pins */
@@ -19175,27 +18995,7 @@ static void alc662_auto_init_hp_out(struct hda_codec *codec)
                                        spec->multiout.extra_out_nid[0]);
 }
 
-#define ALC662_PIN_CD_NID              ALC880_PIN_CD_NID
-
-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 < 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, 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,
-                                                   AC_VERB_SET_AMP_GAIN_MUTE,
-                                                   AMP_OUT_MUTE);
-               }
-       }
-}
-
+#define alc662_auto_init_analog_input  alc882_auto_init_analog_input
 #define alc662_auto_init_input_src     alc882_auto_init_input_src
 
 /*
@@ -19318,15 +19118,29 @@ static const struct snd_kcontrol_new alc_auto_channel_mode_enum = {
        .put = alc_auto_ch_mode_put,
 };
 
-static int alc_auto_add_multi_channel_mode(struct hda_codec *codec)
+static int alc_auto_add_multi_channel_mode(struct hda_codec *codec,
+                                          int (*fill_dac)(struct hda_codec *))
 {
        struct alc_spec *spec = codec->spec;
        struct auto_pin_cfg *cfg = &spec->autocfg;
        unsigned int location, defcfg;
        int num_pins;
 
+       if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && cfg->hp_outs == 1) {
+               /* use HP as primary out */
+               cfg->speaker_outs = cfg->line_outs;
+               memcpy(cfg->speaker_pins, cfg->line_out_pins,
+                      sizeof(cfg->speaker_pins));
+               cfg->line_outs = cfg->hp_outs;
+               memcpy(cfg->line_out_pins, cfg->hp_pins, sizeof(cfg->hp_pins));
+               cfg->hp_outs = 0;
+               memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
+               cfg->line_out_type = AUTO_PIN_HP_OUT;
+               if (fill_dac)
+                       fill_dac(codec);
+       }
        if (cfg->line_outs != 1 ||
-           cfg->line_out_type != AUTO_PIN_LINE_OUT)
+           cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
                return 0;
 
        defcfg = snd_hda_codec_get_pincfg(codec, cfg->line_out_pins[0]);
@@ -19364,10 +19178,10 @@ static int alc662_parse_auto_config(struct hda_codec *codec)
        if (!spec->autocfg.line_outs)
                return 0; /* can't find valid BIOS pin config */
 
-       err = alc662_auto_fill_dac_nids(codec, &spec->autocfg);
+       err = alc662_auto_fill_dac_nids(codec);
        if (err < 0)
                return err;
-       err = alc_auto_add_multi_channel_mode(codec);
+       err = alc_auto_add_multi_channel_mode(codec, alc662_auto_fill_dac_nids);
        if (err < 0)
                return err;
        err = alc662_auto_create_multi_out_ctls(codec, &spec->autocfg);
@@ -19375,17 +19189,15 @@ static int alc662_parse_auto_config(struct hda_codec *codec)
                return err;
        err = alc662_auto_create_extra_out(codec,
                                           spec->autocfg.speaker_pins[0],
+                                          spec->multiout.extra_out_nid[0],
                                           "Speaker");
        if (err < 0)
                return err;
-       if (err)
-               spec->multiout.extra_out_nid[0] = err;
        err = alc662_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
+                                          spec->multiout.hp_nid,
                                           "Headphone");
        if (err < 0)
                return err;
-       if (err)
-               spec->multiout.hp_nid = err;
        err = alc662_auto_create_input_ctls(codec, &spec->autocfg);
        if (err < 0)
                return err;
@@ -19509,6 +19321,8 @@ static int patch_alc662(struct hda_codec *codec)
 
        codec->spec = spec;
 
+       spec->mixer_nid = 0x0b;
+
        alc_auto_parse_customize_define(codec);
 
        alc_fix_pll_init(codec, 0x20, 0x04, 15);
@@ -19875,10 +19689,8 @@ static int alc680_auto_create_multi_out_ctls(struct alc_spec *spec,
        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";
+               int index;
+               name = alc_get_line_out_pfx(spec, 0, true, &index);
                err = alc680_new_analog_output(spec, nid, name, 0);
                if (err < 0)
                        return err;
@@ -19970,8 +19782,6 @@ static int alc680_parse_auto_config(struct hda_codec *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;
@@ -20035,6 +19845,8 @@ static int patch_alc680(struct hda_codec *codec)
 
        codec->spec = spec;
 
+       /* ALC680 has no aa-loopback mixer */
+
        board_config = snd_hda_check_board_config(codec, ALC680_MODEL_LAST,
                                                  alc680_models,
                                                  alc680_cfg_tbl);
index 7f81cc2..56425a5 100644 (file)
@@ -1112,7 +1112,9 @@ static int stac92xx_build_controls(struct hda_codec *codec)
        }
 
        if (spec->multiout.dig_out_nid) {
-               err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
+               err = snd_hda_create_spdif_out_ctls(codec,
+                                                   spec->multiout.dig_out_nid,
+                                                   spec->multiout.dig_out_nid);
                if (err < 0)
                        return err;
                err = snd_hda_create_spdif_share_sw(codec,
@@ -3406,30 +3408,9 @@ static hda_nid_t get_connected_node(struct hda_codec *codec, hda_nid_t mux,
        return 0;
 }
 
-static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
-                               hda_nid_t nid)
-{
-       hda_nid_t conn[HDA_MAX_NUM_INPUTS];
-       int i, nums;
-
-       if (!(get_wcaps(codec, mux) & AC_WCAP_CONN_LIST))
-               return -1;
-
-       nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
-       for (i = 0; i < nums; i++)
-               if (conn[i] == nid)
-                       return i;
-
-       for (i = 0; i < nums; i++) {
-               unsigned int wid_caps = get_wcaps(codec, conn[i]);
-               unsigned int wid_type = get_wcaps_type(wid_caps);
-
-               if (wid_type != AC_WID_PIN && wid_type != AC_WID_AUD_MIX)
-                       if (get_connection_index(codec, conn[i], nid) >= 0)
-                               return i;
-       }
-       return -1;
-}
+/* look for NID recursively */
+#define get_connection_index(codec, mux, nid) \
+       snd_hda_get_conn_index(codec, mux, nid, 1)
 
 /* create a volume assigned to the given pin (only if supported) */
 /* return 1 if the volume control is created */
index f43bb0e..76142c1 100644 (file)
 #include "hda_codec.h"
 #include "hda_local.h"
 
-#define NID_MAPPING            (-1)
-
-/* amp values */
-#define AMP_VAL_IDX_SHIFT      19
-#define AMP_VAL_IDX_MASK       (0x0f<<19)
-
 /* Pin Widget NID */
-#define VT1708_HP_NID          0x13
-#define VT1708_DIGOUT_NID      0x14
-#define VT1708_DIGIN_NID       0x16
-#define VT1708_DIGIN_PIN       0x26
 #define VT1708_HP_PIN_NID      0x20
 #define VT1708_CD_PIN_NID      0x24
 
-#define VT1709_HP_DAC_NID      0x28
-#define VT1709_DIGOUT_NID      0x13
-#define VT1709_DIGIN_NID       0x17
-#define VT1709_DIGIN_PIN       0x25
-
-#define VT1708B_HP_NID         0x25
-#define VT1708B_DIGOUT_NID     0x12
-#define VT1708B_DIGIN_NID      0x15
-#define VT1708B_DIGIN_PIN      0x21
-
-#define VT1708S_HP_NID         0x25
-#define VT1708S_DIGOUT_NID     0x12
-
-#define VT1702_HP_NID          0x17
-#define VT1702_DIGOUT_NID      0x11
-
 enum VIA_HDA_CODEC {
        UNKNOWN = -1,
        VT1708,
@@ -107,6 +81,32 @@ enum VIA_HDA_CODEC {
         (spec)->codec_type == VT1812 ||\
         (spec)->codec_type == VT1802)
 
+#define MAX_NID_PATH_DEPTH     5
+
+/* output-path: DAC -> ... -> pin
+ * idx[] contains the source index number of the next widget;
+ * e.g. idx[0] is the index of the DAC selected by path[1] widget
+ * multi[] indicates whether it's a selector widget with multi-connectors
+ * (i.e. the connection selection is mandatory)
+ * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
+ */
+struct nid_path {
+       int depth;
+       hda_nid_t path[MAX_NID_PATH_DEPTH];
+       unsigned char idx[MAX_NID_PATH_DEPTH];
+       unsigned char multi[MAX_NID_PATH_DEPTH];
+       unsigned int vol_ctl;
+       unsigned int mute_ctl;
+};
+
+/* input-path */
+struct via_input {
+       hda_nid_t pin;  /* input-pin or aa-mix */
+       int adc_idx;    /* ADC index to be used */
+       int mux_idx;    /* MUX index (if any) */
+       const char *label;      /* input-source label */
+};
+
 struct via_spec {
        /* codec parameterization */
        const struct snd_kcontrol_new *mixers[6];
@@ -115,57 +115,80 @@ struct via_spec {
        const struct hda_verb *init_verbs[5];
        unsigned int num_iverbs;
 
-       char *stream_name_analog;
+       char stream_name_analog[32];
+       char stream_name_hp[32];
        const struct hda_pcm_stream *stream_analog_playback;
        const struct hda_pcm_stream *stream_analog_capture;
 
-       char *stream_name_digital;
+       char stream_name_digital[32];
        const struct hda_pcm_stream *stream_digital_playback;
        const struct hda_pcm_stream *stream_digital_capture;
 
        /* playback */
        struct hda_multi_out multiout;
        hda_nid_t slave_dig_outs[2];
+       hda_nid_t hp_dac_nid;
+       int num_active_streams;
+
+       struct nid_path out_path[4];
+       struct nid_path hp_path;
+       struct nid_path hp_dep_path;
+       struct nid_path speaker_path;
 
        /* capture */
        unsigned int num_adc_nids;
-       const hda_nid_t *adc_nids;
+       hda_nid_t adc_nids[3];
        hda_nid_t mux_nids[3];
+       hda_nid_t aa_mix_nid;
        hda_nid_t dig_in_nid;
-       hda_nid_t dig_in_pin;
 
        /* capture source */
-       const struct hda_input_mux *input_mux;
+       bool dyn_adc_switch;
+       int num_inputs;
+       struct via_input inputs[AUTO_CFG_MAX_INS + 1];
        unsigned int cur_mux[3];
 
+       /* dynamic ADC switching */
+       hda_nid_t cur_adc;
+       unsigned int cur_adc_stream_tag;
+       unsigned int cur_adc_format;
+
        /* PCM information */
        struct hda_pcm pcm_rec[3];
 
        /* dynamic controls, init_verbs and input_mux */
        struct auto_pin_cfg autocfg;
        struct snd_array kctls;
-       struct hda_input_mux private_imux[2];
        hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
 
        /* HP mode source */
-       const struct hda_input_mux *hp_mux;
        unsigned int hp_independent_mode;
-       unsigned int hp_independent_mode_index;
-       unsigned int smart51_enabled;
        unsigned int dmic_enabled;
+       unsigned int no_pin_power_ctl;
        enum VIA_HDA_CODEC codec_type;
 
+       /* smart51 setup */
+       unsigned int smart51_nums;
+       hda_nid_t smart51_pins[2];
+       int smart51_idxs[2];
+       const char *smart51_labels[2];
+       unsigned int smart51_enabled;
+
        /* work to check hp jack state */
        struct hda_codec *codec;
        struct delayed_work vt1708_hp_work;
-       int vt1708_jack_detectect;
+       int vt1708_jack_detect;
        int vt1708_hp_present;
 
        void (*set_widgets_power_state)(struct hda_codec *codec);
 
-#ifdef CONFIG_SND_HDA_POWER_SAVE
        struct hda_loopback_check loopback;
-#endif
+       int num_loopbacks;
+       struct hda_amp_list loopback_list[8];
+
+       /* bind capture-volume */
+       struct hda_bind_ctls *bind_cap_vol;
+       struct hda_bind_ctls *bind_cap_sw;
 };
 
 static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
@@ -237,33 +260,23 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
 #define VIA_JACK_EVENT         0x20
 #define VIA_HP_EVENT           0x01
 #define VIA_GPIO_EVENT         0x02
-#define VIA_MONO_EVENT         0x03
-#define VIA_SPEAKER_EVENT      0x04
-#define VIA_BIND_HP_EVENT      0x05
+#define VIA_LINE_EVENT         0x03
 
 enum {
        VIA_CTL_WIDGET_VOL,
        VIA_CTL_WIDGET_MUTE,
        VIA_CTL_WIDGET_ANALOG_MUTE,
-       VIA_CTL_WIDGET_BIND_PIN_MUTE,
 };
 
-enum {
-       AUTO_SEQ_FRONT = 0,
-       AUTO_SEQ_SURROUND,
-       AUTO_SEQ_CENLFE,
-       AUTO_SEQ_SIDE
-};
-
-static void analog_low_current_mode(struct hda_codec *codec, int stream_idle);
-static int is_aa_path_mute(struct hda_codec *codec);
+static void analog_low_current_mode(struct hda_codec *codec);
+static bool is_aa_path_mute(struct hda_codec *codec);
 
 static void vt1708_start_hp_work(struct via_spec *spec)
 {
        if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
                return;
        snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
-                           !spec->vt1708_jack_detectect);
+                           !spec->vt1708_jack_detect);
        if (!delayed_work_pending(&spec->vt1708_hp_work))
                schedule_delayed_work(&spec->vt1708_hp_work,
                                      msecs_to_jiffies(100));
@@ -277,7 +290,7 @@ static void vt1708_stop_hp_work(struct via_spec *spec)
            && !is_aa_path_mute(spec->codec))
                return;
        snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
-                           !spec->vt1708_jack_detectect);
+                           !spec->vt1708_jack_detect);
        cancel_delayed_work_sync(&spec->vt1708_hp_work);
 }
 
@@ -295,7 +308,7 @@ static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 
        set_widgets_power_state(codec);
-       analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1);
+       analog_low_current_mode(snd_kcontrol_chip(kcontrol));
        if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
                if (is_aa_path_mute(codec))
                        vt1708_start_hp_work(codec->spec);
@@ -315,168 +328,44 @@ static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
                        .put = analog_input_switch_put,                 \
                        .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
 
-static void via_hp_bind_automute(struct hda_codec *codec);
-
-static int bind_pin_switch_put(struct snd_kcontrol *kcontrol,
-                              struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       struct via_spec *spec = codec->spec;
-       int i;
-       int change = 0;
-
-       long *valp = ucontrol->value.integer.value;
-       int lmute, rmute;
-       if (strstr(kcontrol->id.name, "Switch") == NULL) {
-               snd_printd("Invalid control!\n");
-               return change;
-       }
-       change = snd_hda_mixer_amp_switch_put(kcontrol,
-                                             ucontrol);
-       /* Get mute value */
-       lmute = *valp ? 0 : HDA_AMP_MUTE;
-       valp++;
-       rmute = *valp ? 0 : HDA_AMP_MUTE;
-
-       /* Set hp pins */
-       if (!spec->hp_independent_mode) {
-               for (i = 0; i < spec->autocfg.hp_outs; i++) {
-                       snd_hda_codec_amp_update(
-                               codec, spec->autocfg.hp_pins[i],
-                               0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
-                               lmute);
-                       snd_hda_codec_amp_update(
-                               codec, spec->autocfg.hp_pins[i],
-                               1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
-                               rmute);
-               }
-       }
-
-       if (!lmute && !rmute) {
-               /* Line Outs */
-               for (i = 0; i < spec->autocfg.line_outs; i++)
-                       snd_hda_codec_amp_stereo(
-                               codec, spec->autocfg.line_out_pins[i],
-                               HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
-               /* Speakers */
-               for (i = 0; i < spec->autocfg.speaker_outs; i++)
-                       snd_hda_codec_amp_stereo(
-                               codec, spec->autocfg.speaker_pins[i],
-                               HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
-               /* unmute */
-               via_hp_bind_automute(codec);
-
-       } else {
-               if (lmute) {
-                       /* Mute all left channels */
-                       for (i = 1; i < spec->autocfg.line_outs; i++)
-                               snd_hda_codec_amp_update(
-                                       codec,
-                                       spec->autocfg.line_out_pins[i],
-                                       0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
-                                       lmute);
-                       for (i = 0; i < spec->autocfg.speaker_outs; i++)
-                               snd_hda_codec_amp_update(
-                                       codec,
-                                       spec->autocfg.speaker_pins[i],
-                                       0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
-                                       lmute);
-               }
-               if (rmute) {
-                       /* mute all right channels */
-                       for (i = 1; i < spec->autocfg.line_outs; i++)
-                               snd_hda_codec_amp_update(
-                                       codec,
-                                       spec->autocfg.line_out_pins[i],
-                                       1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
-                                       rmute);
-                       for (i = 0; i < spec->autocfg.speaker_outs; i++)
-                               snd_hda_codec_amp_update(
-                                       codec,
-                                       spec->autocfg.speaker_pins[i],
-                                       1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
-                                       rmute);
-               }
-       }
-       return change;
-}
-
-#define BIND_PIN_MUTE                                                  \
-       {               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,            \
-                       .name = NULL,                                   \
-                       .index = 0,                                     \
-                       .info = snd_hda_mixer_amp_switch_info,          \
-                       .get = snd_hda_mixer_amp_switch_get,            \
-                       .put = bind_pin_switch_put,                     \
-                       .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
-
 static const struct snd_kcontrol_new via_control_templates[] = {
        HDA_CODEC_VOLUME(NULL, 0, 0, 0),
        HDA_CODEC_MUTE(NULL, 0, 0, 0),
        ANALOG_INPUT_MUTE,
-       BIND_PIN_MUTE,
 };
 
-static const hda_nid_t vt1708_adc_nids[2] = {
-       /* ADC1-2 */
-       0x15, 0x27
-};
-
-static const hda_nid_t vt1709_adc_nids[3] = {
-       /* ADC1-2 */
-       0x14, 0x15, 0x16
-};
-
-static const hda_nid_t vt1708B_adc_nids[2] = {
-       /* ADC1-2 */
-       0x13, 0x14
-};
-
-static const hda_nid_t vt1708S_adc_nids[2] = {
-       /* ADC1-2 */
-       0x13, 0x14
-};
-
-static const hda_nid_t vt1702_adc_nids[3] = {
-       /* ADC1-2 */
-       0x12, 0x20, 0x1F
-};
-
-static const hda_nid_t vt1718S_adc_nids[2] = {
-       /* ADC1-2 */
-       0x10, 0x11
-};
-
-static const hda_nid_t vt1716S_adc_nids[2] = {
-       /* ADC1-2 */
-       0x13, 0x14
-};
-
-static const hda_nid_t vt2002P_adc_nids[2] = {
-       /* ADC1-2 */
-       0x10, 0x11
-};
 
-static const hda_nid_t vt1812_adc_nids[2] = {
-       /* ADC1-2 */
-       0x10, 0x11
-};
+/* add dynamic controls */
+static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
+                               const struct snd_kcontrol_new *tmpl,
+                               const char *name)
+{
+       struct snd_kcontrol_new *knew;
 
+       snd_array_init(&spec->kctls, sizeof(*knew), 32);
+       knew = snd_array_new(&spec->kctls);
+       if (!knew)
+               return NULL;
+       *knew = *tmpl;
+       if (!name)
+               name = tmpl->name;
+       if (name) {
+               knew->name = kstrdup(name, GFP_KERNEL);
+               if (!knew->name)
+                       return NULL;
+       }
+       return knew;
+}
 
-/* add dynamic controls */
 static int __via_add_control(struct via_spec *spec, int type, const char *name,
                             int idx, unsigned long val)
 {
        struct snd_kcontrol_new *knew;
 
-       snd_array_init(&spec->kctls, sizeof(*knew), 32);
-       knew = snd_array_new(&spec->kctls);
+       knew = __via_clone_ctl(spec, &via_control_templates[type], name);
        if (!knew)
                return -ENOMEM;
-       *knew = via_control_templates[type];
-       knew->name = kstrdup(name, GFP_KERNEL);
-       if (!knew->name)
-               return -ENOMEM;
+       knew->index = idx;
        if (get_amp_nid_(val))
                knew->subdevice = HDA_SUBDEV_AMP_FLAG;
        knew->private_value = val;
@@ -486,21 +375,7 @@ static int __via_add_control(struct via_spec *spec, int type, const char *name,
 #define via_add_control(spec, type, name, val) \
        __via_add_control(spec, type, name, 0, val)
 
-static struct snd_kcontrol_new *via_clone_control(struct via_spec *spec,
-                               const struct snd_kcontrol_new *tmpl)
-{
-       struct snd_kcontrol_new *knew;
-
-       snd_array_init(&spec->kctls, sizeof(*knew), 32);
-       knew = snd_array_new(&spec->kctls);
-       if (!knew)
-               return NULL;
-       *knew = *tmpl;
-       knew->name = kstrdup(tmpl->name, GFP_KERNEL);
-       if (!knew->name)
-               return NULL;
-       return knew;
-}
+#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
 
 static void via_free_kctls(struct hda_codec *codec)
 {
@@ -535,58 +410,170 @@ static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
        return 0;
 }
 
-static void via_auto_set_output_and_unmute(struct hda_codec *codec,
-                                          hda_nid_t nid, int pin_type,
-                                          int dac_idx)
+#define get_connection_index(codec, mux, nid) \
+       snd_hda_get_conn_index(codec, mux, nid, 0)
+
+static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
+                          unsigned int mask)
+{
+       unsigned int caps;
+       if (!nid)
+               return false;
+       caps = get_wcaps(codec, nid);
+       if (dir == HDA_INPUT)
+               caps &= AC_WCAP_IN_AMP;
+       else
+               caps &= AC_WCAP_OUT_AMP;
+       if (!caps)
+               return false;
+       if (query_amp_caps(codec, nid, dir) & mask)
+               return true;
+       return false;
+}
+
+#define have_mute(codec, nid, dir) \
+       check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE)
+
+/* enable/disable the output-route */
+static void activate_output_path(struct hda_codec *codec, struct nid_path *path,
+                                bool enable, bool force)
+{
+       int i;
+       for (i = 0; i < path->depth; i++) {
+               hda_nid_t src, dst;
+               int idx = path->idx[i];
+               src = path->path[i];                    
+               if (i < path->depth - 1)
+                       dst = path->path[i + 1];
+               else
+                       dst = 0;
+               if (enable && path->multi[i])
+                       snd_hda_codec_write(codec, dst, 0,
+                                           AC_VERB_SET_CONNECT_SEL, idx);
+               if (have_mute(codec, dst, HDA_INPUT)) {
+                       int val = enable ? AMP_IN_UNMUTE(idx) :
+                               AMP_IN_MUTE(idx);
+                       snd_hda_codec_write(codec, dst, 0,
+                                           AC_VERB_SET_AMP_GAIN_MUTE, val);
+               }
+               if (!force && (src == path->vol_ctl || src == path->mute_ctl))
+                       continue;
+               if (have_mute(codec, src, HDA_OUTPUT)) {
+                       int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE;
+                       snd_hda_codec_write(codec, src, 0,
+                                           AC_VERB_SET_AMP_GAIN_MUTE, val);
+               }
+       }
+}
+
+/* set the given pin as output */
+static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
+                           int pin_type)
 {
-       /* set as output */
-       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+       if (!pin)
+               return;
+       snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
                            pin_type);
-       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-                           AMP_OUT_UNMUTE);
-       if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)
-               snd_hda_codec_write(codec, nid, 0,
+       if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
+               snd_hda_codec_write(codec, pin, 0,
                                    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
 }
 
+static void via_auto_init_output(struct hda_codec *codec,
+                                struct nid_path *path, int pin_type,
+                                bool force)
+{
+       struct via_spec *spec = codec->spec;
+       unsigned int caps;
+       hda_nid_t pin, nid;
+       int i, idx;
+
+       if (!path->depth)
+               return;
+       pin = path->path[path->depth - 1];
+
+       init_output_pin(codec, pin, pin_type);
+       caps = query_amp_caps(codec, pin, HDA_OUTPUT);
+       if (caps & AC_AMPCAP_MUTE) {
+               unsigned int val;
+               val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
+               snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+                                   AMP_OUT_MUTE | val);
+       }
+
+       activate_output_path(codec, path, true, force);
+
+       /* initialize the AA-path */
+       if (!spec->aa_mix_nid)
+               return;
+       for (i = path->depth - 1; i > 0; i--) {
+               nid = path->path[i];
+               idx = get_connection_index(codec, nid, spec->aa_mix_nid);
+               if (idx >= 0) {
+                       if (have_mute(codec, nid, HDA_INPUT))
+                               snd_hda_codec_write(codec, nid, 0,
+                                                   AC_VERB_SET_AMP_GAIN_MUTE,
+                                                   AMP_IN_UNMUTE(idx));
+                       break;
+               }
+       }
+}
 
 static void via_auto_init_multi_out(struct hda_codec *codec)
 {
        struct via_spec *spec = codec->spec;
        int i;
 
-       for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
-               hda_nid_t nid = spec->autocfg.line_out_pins[i];
-               if (nid)
-                       via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
-       }
+       for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++)
+               via_auto_init_output(codec, &spec->out_path[i], PIN_OUT, true);
 }
 
 static void via_auto_init_hp_out(struct hda_codec *codec)
 {
        struct via_spec *spec = codec->spec;
-       hda_nid_t pin;
-       int i;
 
-       for (i = 0; i < spec->autocfg.hp_outs; i++) {
-               pin = spec->autocfg.hp_pins[i];
-               if (pin) /* connect to front */
-                       via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
+       if (!spec->hp_dac_nid) {
+               via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, true);
+               return;
        }
+       if (spec->hp_independent_mode) {
+               activate_output_path(codec, &spec->hp_dep_path, false, false);
+               via_auto_init_output(codec, &spec->hp_path, PIN_HP, true);
+       } else {
+               activate_output_path(codec, &spec->hp_path, false, false);
+               via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, true);
+       }
+}
+
+static void via_auto_init_speaker_out(struct hda_codec *codec)
+{
+       struct via_spec *spec = codec->spec;
+
+       if (spec->autocfg.speaker_outs)
+               via_auto_init_output(codec, &spec->speaker_path, PIN_OUT, true);
 }
 
-static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin);
+static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
 
 static void via_auto_init_analog_input(struct hda_codec *codec)
 {
        struct via_spec *spec = codec->spec;
        const struct auto_pin_cfg *cfg = &spec->autocfg;
+       hda_nid_t conn[HDA_MAX_CONNECTIONS];
        unsigned int ctl;
-       int i;
+       int i, num_conns;
+
+       /* init ADCs */
+       for (i = 0; i < spec->num_adc_nids; i++) {
+               snd_hda_codec_write(codec, spec->adc_nids[i], 0,
+                                   AC_VERB_SET_AMP_GAIN_MUTE,
+                                   AMP_IN_UNMUTE(0));
+       }
 
+       /* init pins */
        for (i = 0; i < cfg->num_inputs; i++) {
                hda_nid_t nid = cfg->inputs[i].pin;
-               if (spec->smart51_enabled && is_smart51_pins(spec, nid))
+               if (spec->smart51_enabled && is_smart51_pins(codec, nid))
                        ctl = PIN_OUT;
                else if (cfg->inputs[i].type == AUTO_PIN_MIC)
                        ctl = PIN_VREF50;
@@ -595,6 +582,32 @@ static void via_auto_init_analog_input(struct hda_codec *codec)
                snd_hda_codec_write(codec, nid, 0,
                                    AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
        }
+
+       /* init input-src */
+       for (i = 0; i < spec->num_adc_nids; i++) {
+               int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx;
+               if (spec->mux_nids[adc_idx]) {
+                       int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx;
+                       snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
+                                           AC_VERB_SET_CONNECT_SEL,
+                                           mux_idx);
+               }
+               if (spec->dyn_adc_switch)
+                       break; /* only one input-src */
+       }
+
+       /* init aa-mixer */
+       if (!spec->aa_mix_nid)
+               return;
+       num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
+                                           ARRAY_SIZE(conn));
+       for (i = 0; i < num_conns; i++) {
+               unsigned int caps = get_wcaps(codec, conn[i]);
+               if (get_wcaps_type(caps) == AC_WID_PIN)
+                       snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
+                                           AC_VERB_SET_AMP_GAIN_MUTE,
+                                           AMP_IN_MUTE(i));
+       }
 }
 
 static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
@@ -605,9 +618,13 @@ static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
        unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
                >> AC_DEFCFG_MISC_SHIFT
                & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
-       unsigned present = snd_hda_jack_detect(codec, nid);
        struct via_spec *spec = codec->spec;
-       if ((spec->smart51_enabled && is_smart51_pins(spec, nid))
+       unsigned present = 0;
+
+       no_presence |= spec->no_pin_power_ctl;
+       if (!no_presence)
+               present = snd_hda_jack_detect(codec, nid);
+       if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
            || ((no_presence || present)
                && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
                *affected_parm = AC_PWRST_D0; /* if it's connected */
@@ -618,123 +635,77 @@ static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
        snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
 }
 
-/*
- * input MUX handling
- */
-static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
-                            struct snd_ctl_elem_info *uinfo)
+static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_info *uinfo)
 {
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       struct via_spec *spec = codec->spec;
-       return snd_hda_input_mux_info(spec->input_mux, uinfo);
+       static const char * const texts[] = {
+               "Disabled", "Enabled"
+       };
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 2;
+       if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+               uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+       strcpy(uinfo->value.enumerated.name,
+              texts[uinfo->value.enumerated.item]);
+       return 0;
 }
 
-static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
-                           struct snd_ctl_elem_value *ucontrol)
+static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct via_spec *spec = codec->spec;
-       unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-
-       ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
+       ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
        return 0;
 }
 
-static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
-                           struct snd_ctl_elem_value *ucontrol)
+static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct via_spec *spec = codec->spec;
-       unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-       int ret;
+       unsigned int val = !ucontrol->value.enumerated.item[0];
 
-       if (!spec->mux_nids[adc_idx])
-               return -EINVAL;
-       /* switch to D0 beofre change index */
-       if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0,
-                              AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
-               snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
-                                   AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
-
-       ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
-                                    spec->mux_nids[adc_idx],
-                                    &spec->cur_mux[adc_idx]);
-       /* update jack power state */
+       if (val == spec->no_pin_power_ctl)
+               return 0;
+       spec->no_pin_power_ctl = val;
        set_widgets_power_state(codec);
-
-       return ret;
+       return 1;
 }
 
+static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "Dynamic Power-Control",
+       .info = via_pin_power_ctl_info,
+       .get = via_pin_power_ctl_get,
+       .put = via_pin_power_ctl_put,
+};
+
+
 static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
                                   struct snd_ctl_elem_info *uinfo)
 {
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       struct via_spec *spec = codec->spec;
-       return snd_hda_input_mux_info(spec->hp_mux, uinfo);
+       static const char * const texts[] = { "OFF", "ON" };
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 2;
+       if (uinfo->value.enumerated.item >= 2)
+               uinfo->value.enumerated.item = 1;
+       strcpy(uinfo->value.enumerated.name,
+              texts[uinfo->value.enumerated.item]);
+       return 0;
 }
 
 static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
                                  struct snd_ctl_elem_value *ucontrol)
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       hda_nid_t nid = kcontrol->private_value;
-       unsigned int pinsel;
-
-       /* use !! to translate conn sel 2 for VT1718S */
-       pinsel = !!snd_hda_codec_read(codec, nid, 0,
-                                     AC_VERB_GET_CONNECT_SEL,
-                                     0x00);
-       ucontrol->value.enumerated.item[0] = pinsel;
-
-       return 0;
-}
-
-static void activate_ctl(struct hda_codec *codec, const char *name, int active)
-{
-       struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name);
-       if (ctl) {
-               ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
-               ctl->vd[0].access |= active
-                       ? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE;
-               snd_ctl_notify(codec->bus->card,
-                              SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id);
-       }
-}
-
-static hda_nid_t side_mute_channel(struct via_spec *spec)
-{
-       switch (spec->codec_type) {
-       case VT1708:            return 0x1b;
-       case VT1709_10CH:       return 0x29;
-       case VT1708B_8CH:       /* fall thru */
-       case VT1708S:           return 0x27;
-       case VT2002P:           return 0x19;
-       case VT1802:            return 0x15;
-       case VT1812:            return 0x15;
-       default:                return 0;
-       }
-}
-
-static int update_side_mute_status(struct hda_codec *codec)
-{
-       /* mute side channel */
        struct via_spec *spec = codec->spec;
-       unsigned int parm;
-       hda_nid_t sw3 = side_mute_channel(spec);
 
-       if (sw3) {
-               if (VT2002P_COMPATIBLE(spec))
-                       parm = spec->hp_independent_mode ?
-                              AMP_IN_MUTE(1) : AMP_IN_UNMUTE(1);
-               else
-                       parm = spec->hp_independent_mode ?
-                              AMP_OUT_MUTE : AMP_OUT_UNMUTE;
-               snd_hda_codec_write(codec, sw3, 0,
-                                   AC_VERB_SET_AMP_GAIN_MUTE, parm);
-               if (spec->codec_type == VT1812)
-                       snd_hda_codec_write(codec, 0x1d, 0,
-                                           AC_VERB_SET_AMP_GAIN_MUTE, parm);
-       }
+       ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
        return 0;
 }
 
@@ -743,66 +714,27 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct via_spec *spec = codec->spec;
-       hda_nid_t nid = kcontrol->private_value;
-       unsigned int pinsel = ucontrol->value.enumerated.item[0];
-       unsigned int parm0, parm1;
-       /* Get Independent Mode index of headphone pin widget */
-       spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel
-               ? 1 : 0;
-       if (spec->codec_type == VT1718S) {
-               snd_hda_codec_write(codec, nid, 0,
-                                   AC_VERB_SET_CONNECT_SEL, pinsel ? 2 : 0);
-               /* Set correct mute switch for MW3 */
-               parm0 = spec->hp_independent_mode ?
-                              AMP_IN_UNMUTE(0) : AMP_IN_MUTE(0);
-               parm1 = spec->hp_independent_mode ?
-                              AMP_IN_MUTE(1) : AMP_IN_UNMUTE(1);
-               snd_hda_codec_write(codec, 0x1b, 0,
-                                   AC_VERB_SET_AMP_GAIN_MUTE, parm0);
-               snd_hda_codec_write(codec, 0x1b, 0,
-                                   AC_VERB_SET_AMP_GAIN_MUTE, parm1);
-       }
-       else
-               snd_hda_codec_write(codec, nid, 0,
-                                   AC_VERB_SET_CONNECT_SEL, pinsel);
 
-       if (spec->codec_type == VT1812)
-               snd_hda_codec_write(codec, 0x35, 0,
-                                   AC_VERB_SET_CONNECT_SEL, pinsel);
-       if (spec->multiout.hp_nid && spec->multiout.hp_nid
-           != spec->multiout.dac_nids[HDA_FRONT])
-               snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid,
-                                          0, 0, 0);
-
-       update_side_mute_status(codec);
-       /* update HP volume/swtich active state */
-       if (spec->codec_type == VT1708S
-           || spec->codec_type == VT1702
-           || spec->codec_type == VT1718S
-           || spec->codec_type == VT1716S
-           || VT2002P_COMPATIBLE(spec)) {
-               activate_ctl(codec, "Headphone Playback Volume",
-                            spec->hp_independent_mode);
-               activate_ctl(codec, "Headphone Playback Switch",
-                            spec->hp_independent_mode);
+       spec->hp_independent_mode = !!ucontrol->value.enumerated.item[0];
+       if (spec->hp_independent_mode) {
+               activate_output_path(codec, &spec->hp_dep_path, false, false);
+               activate_output_path(codec, &spec->hp_path, true, false);
+       } else {
+               activate_output_path(codec, &spec->hp_path, false, false);
+               activate_output_path(codec, &spec->hp_dep_path, true, false);
        }
+
        /* update jack power state */
        set_widgets_power_state(codec);
        return 0;
 }
 
-static const struct snd_kcontrol_new via_hp_mixer[2] = {
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Independent HP",
-               .info = via_independent_hp_info,
-               .get = via_independent_hp_get,
-               .put = via_independent_hp_put,
-       },
-       {
-               .iface = NID_MAPPING,
-               .name = "Independent HP",
-       },
+static const struct snd_kcontrol_new via_hp_mixer = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "Independent HP",
+       .info = via_independent_hp_info,
+       .get = via_independent_hp_get,
+       .put = via_independent_hp_put,
 };
 
 static int via_hp_build(struct hda_codec *codec)
@@ -810,61 +742,28 @@ static int via_hp_build(struct hda_codec *codec)
        struct via_spec *spec = codec->spec;
        struct snd_kcontrol_new *knew;
        hda_nid_t nid;
-       int nums;
-       hda_nid_t conn[HDA_MAX_CONNECTIONS];
-
-       switch (spec->codec_type) {
-       case VT1718S:
-               nid = 0x34;
-               break;
-       case VT2002P:
-       case VT1802:
-               nid = 0x35;
-               break;
-       case VT1812:
-               nid = 0x3d;
-               break;
-       default:
-               nid = spec->autocfg.hp_pins[0];
-               break;
-       }
-
-       if (spec->codec_type != VT1708) {
-               nums = snd_hda_get_connections(codec, nid,
-                                              conn, HDA_MAX_CONNECTIONS);
-               if (nums <= 1)
-                       return 0;
-       }
 
-       knew = via_clone_control(spec, &via_hp_mixer[0]);
+       nid = spec->autocfg.hp_pins[0];
+       knew = via_clone_control(spec, &via_hp_mixer);
        if (knew == NULL)
                return -ENOMEM;
 
        knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
-       knew->private_value = nid;
-
-       nid = side_mute_channel(spec);
-       if (nid) {
-               knew = via_clone_control(spec, &via_hp_mixer[1]);
-               if (knew == NULL)
-                       return -ENOMEM;
-               knew->subdevice = nid;
-       }
 
        return 0;
 }
 
 static void notify_aa_path_ctls(struct hda_codec *codec)
 {
+       struct via_spec *spec = codec->spec;
        int i;
-       struct snd_ctl_elem_id id;
-       const char *labels[] = {"Mic", "Front Mic", "Line", "Rear Mic"};
-       struct snd_kcontrol *ctl;
-
-       memset(&id, 0, sizeof(id));
-       id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
-       for (i = 0; i < ARRAY_SIZE(labels); i++) {
-               sprintf(id.name, "%s Playback Volume", labels[i]);
+
+       for (i = 0; i < spec->smart51_nums; i++) {
+               struct snd_kcontrol *ctl;
+               struct snd_ctl_elem_id id;
+               memset(&id, 0, sizeof(id));
+               id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+               sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
                ctl = snd_hda_find_mixer_ctl(codec, id.name);
                if (ctl)
                        snd_ctl_notify(codec->bus->card,
@@ -876,90 +775,37 @@ static void notify_aa_path_ctls(struct hda_codec *codec)
 static void mute_aa_path(struct hda_codec *codec, int mute)
 {
        struct via_spec *spec = codec->spec;
-       hda_nid_t  nid_mixer;
-       int start_idx;
-       int end_idx;
+       int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
        int i;
-       /* get nid of MW0 and start & end index */
-       switch (spec->codec_type) {
-       case VT1708:
-               nid_mixer = 0x17;
-               start_idx = 2;
-               end_idx = 4;
-               break;
-       case VT1709_10CH:
-       case VT1709_6CH:
-               nid_mixer = 0x18;
-               start_idx = 2;
-               end_idx = 4;
-               break;
-       case VT1708B_8CH:
-       case VT1708B_4CH:
-       case VT1708S:
-       case VT1716S:
-               nid_mixer = 0x16;
-               start_idx = 2;
-               end_idx = 4;
-               break;
-       case VT1718S:
-               nid_mixer = 0x21;
-               start_idx = 1;
-               end_idx = 3;
-               break;
-       default:
-               return;
-       }
+
        /* check AA path's mute status */
-       for (i = start_idx; i <= end_idx; i++) {
-               int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
-               snd_hda_codec_amp_stereo(codec, nid_mixer, HDA_INPUT, i,
+       for (i = 0; i < spec->smart51_nums; i++) {
+               if (spec->smart51_idxs[i] < 0)
+                       continue;
+               snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
+                                        HDA_INPUT, spec->smart51_idxs[i],
                                         HDA_AMP_MUTE, val);
        }
 }
-static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin)
+
+static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
 {
-       const struct auto_pin_cfg *cfg = &spec->autocfg;
+       struct via_spec *spec = codec->spec;
        int i;
 
-       for (i = 0; i < cfg->num_inputs; i++) {
-               if (pin == cfg->inputs[i].pin)
-                       return cfg->inputs[i].type <= AUTO_PIN_LINE_IN;
-       }
-       return 0;
+       for (i = 0; i < spec->smart51_nums; i++)
+               if (spec->smart51_pins[i] == pin)
+                       return true;
+       return false;
 }
 
-static int via_smart51_info(struct snd_kcontrol *kcontrol,
-                           struct snd_ctl_elem_info *uinfo)
-{
-       uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
-       uinfo->count = 1;
-       uinfo->value.integer.min = 0;
-       uinfo->value.integer.max = 1;
-       return 0;
-}
-
-static int via_smart51_get(struct snd_kcontrol *kcontrol,
-                          struct snd_ctl_elem_value *ucontrol)
+static int via_smart51_get(struct snd_kcontrol *kcontrol,
+                          struct snd_ctl_elem_value *ucontrol)
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct via_spec *spec = codec->spec;
-       const struct auto_pin_cfg *cfg = &spec->autocfg;
-       int on = 1;
-       int i;
 
-       for (i = 0; i < cfg->num_inputs; i++) {
-               hda_nid_t nid = cfg->inputs[i].pin;
-               int ctl = snd_hda_codec_read(codec, nid, 0,
-                                            AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
-               if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
-                       continue;
-               if (cfg->inputs[i].type == AUTO_PIN_MIC &&
-                   spec->hp_independent_mode && spec->codec_type != VT1718S)
-                       continue; /* ignore FMic for independent HP */
-               if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN))
-                       on = 0;
-       }
-       *ucontrol->value.integer.value = on;
+       *ucontrol->value.integer.value = spec->smart51_enabled;
        return 0;
 }
 
@@ -968,21 +814,14 @@ static int via_smart51_put(struct snd_kcontrol *kcontrol,
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct via_spec *spec = codec->spec;
-       const struct auto_pin_cfg *cfg = &spec->autocfg;
        int out_in = *ucontrol->value.integer.value
                ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
        int i;
 
-       for (i = 0; i < cfg->num_inputs; i++) {
-               hda_nid_t nid = cfg->inputs[i].pin;
+       for (i = 0; i < spec->smart51_nums; i++) {
+               hda_nid_t nid = spec->smart51_pins[i];
                unsigned int parm;
 
-               if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
-                       continue;
-               if (cfg->inputs[i].type == AUTO_PIN_MIC &&
-                   spec->hp_independent_mode && spec->codec_type != VT1718S)
-                       continue; /* don't retask FMic for independent HP */
-
                parm = snd_hda_codec_read(codec, nid, 0,
                                          AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
                parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
@@ -994,171 +833,59 @@ static int via_smart51_put(struct snd_kcontrol *kcontrol,
                        mute_aa_path(codec, 1);
                        notify_aa_path_ctls(codec);
                }
-               if (spec->codec_type == VT1718S) {
-                       snd_hda_codec_amp_stereo(
-                                       codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE,
-                                       HDA_AMP_UNMUTE);
-               }
-               if (cfg->inputs[i].type == AUTO_PIN_MIC) {
-                       if (spec->codec_type == VT1708S
-                           || spec->codec_type == VT1716S) {
-                               /* input = index 1 (AOW3) */
-                               snd_hda_codec_write(
-                                       codec, nid, 0,
-                                       AC_VERB_SET_CONNECT_SEL, 1);
-                               snd_hda_codec_amp_stereo(
-                                       codec, nid, HDA_OUTPUT,
-                                       0, HDA_AMP_MUTE, HDA_AMP_UNMUTE);
-                       }
-               }
        }
        spec->smart51_enabled = *ucontrol->value.integer.value;
        set_widgets_power_state(codec);
        return 1;
 }
 
-static const struct snd_kcontrol_new via_smart51_mixer[2] = {
-       {
-        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-        .name = "Smart 5.1",
-        .count = 1,
-        .info = via_smart51_info,
-        .get = via_smart51_get,
-        .put = via_smart51_put,
-        },
-       {
-        .iface = NID_MAPPING,
-        .name = "Smart 5.1",
-       }
+static const struct snd_kcontrol_new via_smart51_mixer = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "Smart 5.1",
+       .count = 1,
+       .info = snd_ctl_boolean_mono_info,
+       .get = via_smart51_get,
+       .put = via_smart51_put,
 };
 
-static int via_smart51_build(struct via_spec *spec)
+static int via_smart51_build(struct hda_codec *codec)
 {
-       struct snd_kcontrol_new *knew;
-       const struct auto_pin_cfg *cfg = &spec->autocfg;
-       hda_nid_t nid;
-       int i;
+       struct via_spec *spec = codec->spec;
 
-       if (!cfg)
-               return 0;
-       if (cfg->line_outs > 2)
+       if (!spec->smart51_nums)
                return 0;
-
-       knew = via_clone_control(spec, &via_smart51_mixer[0]);
-       if (knew == NULL)
+       if (!via_clone_control(spec, &via_smart51_mixer))
                return -ENOMEM;
-
-       for (i = 0; i < cfg->num_inputs; i++) {
-               nid = cfg->inputs[i].pin;
-               if (cfg->inputs[i].type <= AUTO_PIN_LINE_IN) {
-                       knew = via_clone_control(spec, &via_smart51_mixer[1]);
-                       if (knew == NULL)
-                               return -ENOMEM;
-                       knew->subdevice = nid;
-                       break;
-               }
-       }
-
        return 0;
 }
 
-/* capture mixer elements */
-static const struct snd_kcontrol_new vt1708_capture_mixer[] = {
-       HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* The multiple "Capture Source" controls confuse alsamixer
-                * So call somewhat different..
-                */
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 1,
-               .info = via_mux_enum_info,
-               .get = via_mux_enum_get,
-               .put = via_mux_enum_put,
-       },
-       { } /* end */
-};
-
-/* check AA path's mute statue */
-static int is_aa_path_mute(struct hda_codec *codec)
+/* check AA path's mute status */
+static bool is_aa_path_mute(struct hda_codec *codec)
 {
-       int mute = 1;
-       hda_nid_t  nid_mixer;
-       int start_idx;
-       int end_idx;
-       int i;
        struct via_spec *spec = codec->spec;
-       /* get nid of MW0 and start & end index */
-       switch (spec->codec_type) {
-       case VT1708B_8CH:
-       case VT1708B_4CH:
-       case VT1708S:
-       case VT1716S:
-               nid_mixer = 0x16;
-               start_idx = 2;
-               end_idx = 4;
-               break;
-       case VT1702:
-               nid_mixer = 0x1a;
-               start_idx = 1;
-               end_idx = 3;
-               break;
-       case VT1718S:
-               nid_mixer = 0x21;
-               start_idx = 1;
-               end_idx = 3;
-               break;
-       case VT2002P:
-       case VT1812:
-       case VT1802:
-               nid_mixer = 0x21;
-               start_idx = 0;
-               end_idx = 2;
-               break;
-       default:
-               return 0;
-       }
-       /* check AA path's mute status */
-       for (i = start_idx; i <= end_idx; i++) {
-               unsigned int con_list = snd_hda_codec_read(
-                       codec, nid_mixer, 0, AC_VERB_GET_CONNECT_LIST, i/4*4);
-               int shift = 8 * (i % 4);
-               hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift;
-               unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin);
-               if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) {
-                       /* check mute status while the pin is connected */
-                       int mute_l = snd_hda_codec_amp_read(codec, nid_mixer, 0,
-                                                           HDA_INPUT, i) >> 7;
-                       int mute_r = snd_hda_codec_amp_read(codec, nid_mixer, 1,
-                                                           HDA_INPUT, i) >> 7;
-                       if (!mute_l || !mute_r) {
-                               mute = 0;
-                               break;
-                       }
+       const struct hda_amp_list *p;
+       int i, ch, v;
+
+       for (i = 0; i < spec->num_loopbacks; i++) {
+               p = &spec->loopback_list[i];
+               for (ch = 0; ch < 2; ch++) {
+                       v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
+                                                  p->idx);
+                       if (!(v & HDA_AMP_MUTE) && v > 0)
+                               return false;
                }
        }
-       return mute;
+       return true;
 }
 
 /* enter/exit analog low-current mode */
-static void analog_low_current_mode(struct hda_codec *codec, int stream_idle)
+static void analog_low_current_mode(struct hda_codec *codec)
 {
        struct via_spec *spec = codec->spec;
-       static int saved_stream_idle = 1; /* saved stream idle status */
-       int enable = is_aa_path_mute(codec);
-       unsigned int verb = 0;
-       unsigned int parm = 0;
+       bool enable;
+       unsigned int verb, parm;
 
-       if (stream_idle == -1)  /* stream status did not change */
-               enable = enable && saved_stream_idle;
-       else {
-               enable = enable && stream_idle;
-               saved_stream_idle = stream_idle;
-       }
+       enable = is_aa_path_mute(codec) && (spec->num_active_streams > 0);
 
        /* decide low current mode's verb & parameter */
        switch (spec->codec_type) {
@@ -1193,119 +920,74 @@ static void analog_low_current_mode(struct hda_codec *codec, int stream_idle)
 /*
  * generic initialization of ADC, input mixers and output mixers
  */
-static const struct hda_verb vt1708_volume_init_verbs[] = {
-       /*
-        * Unmute ADC0-1 and set the default input to mic-in
-        */
-       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-
-       /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
-        * mixer widget
-        */
-       /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
-       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
-       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
-       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
-
-       /*
-        * Set up output mixers (0x19 - 0x1b)
-        */
-       /* set vol=0 to output mixers */
-       {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
-       /* Setup default input MW0 to PW4 */
-       {0x20, AC_VERB_SET_CONNECT_SEL, 0},
-       /* PW9 Output enable */
-       {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+static const struct hda_verb vt1708_init_verbs[] = {
        /* power down jack detect function */
        {0x1, 0xf81, 0x1},
        { }
 };
 
-static int via_playback_pcm_open(struct hda_pcm_stream *hinfo,
+static void set_stream_active(struct hda_codec *codec, bool active)
+{
+       struct via_spec *spec = codec->spec;
+
+       if (active)
+               spec->num_active_streams++;
+       else
+               spec->num_active_streams--;
+       analog_low_current_mode(codec);
+}
+
+static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
                                 struct hda_codec *codec,
                                 struct snd_pcm_substream *substream)
 {
        struct via_spec *spec = codec->spec;
-       int idle = substream->pstr->substream_opened == 1
-               && substream->ref_count == 0;
-       analog_low_current_mode(codec, idle);
-       return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
-                                            hinfo);
+       int err;
+
+       if (!spec->hp_independent_mode)
+               spec->multiout.hp_nid = spec->hp_dac_nid;
+       set_stream_active(codec, true);
+       err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
+                                           hinfo);
+       if (err < 0) {
+               spec->multiout.hp_nid = 0;
+               set_stream_active(codec, false);
+               return err;
+       }
+       return 0;
 }
 
-static void playback_multi_pcm_prep_0(struct hda_codec *codec,
-                                     unsigned int stream_tag,
-                                     unsigned int format,
-                                     struct snd_pcm_substream *substream)
+static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
+                                 struct hda_codec *codec,
+                                 struct snd_pcm_substream *substream)
 {
        struct via_spec *spec = codec->spec;
-       struct hda_multi_out *mout = &spec->multiout;
-       const hda_nid_t *nids = mout->dac_nids;
-       int chs = substream->runtime->channels;
-       int i;
 
-       mutex_lock(&codec->spdif_mutex);
-       if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
-               if (chs == 2 &&
-                   snd_hda_is_supported_format(codec, mout->dig_out_nid,
-                                               format) &&
-                   !(codec->spdif_status & IEC958_AES0_NONAUDIO)) {
-                       mout->dig_out_used = HDA_DIG_ANALOG_DUP;
-                       /* turn off SPDIF once; otherwise the IEC958 bits won't
-                        * be updated */
-                       if (codec->spdif_ctls & AC_DIG1_ENABLE)
-                               snd_hda_codec_write(codec, mout->dig_out_nid, 0,
-                                                   AC_VERB_SET_DIGI_CONVERT_1,
-                                                   codec->spdif_ctls &
-                                                       ~AC_DIG1_ENABLE & 0xff);
-                       snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
-                                                  stream_tag, 0, format);
-                       /* turn on again (if needed) */
-                       if (codec->spdif_ctls & AC_DIG1_ENABLE)
-                               snd_hda_codec_write(codec, mout->dig_out_nid, 0,
-                                                   AC_VERB_SET_DIGI_CONVERT_1,
-                                                   codec->spdif_ctls & 0xff);
-               } else {
-                       mout->dig_out_used = 0;
-                       snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
-                                                  0, 0, 0);
-               }
-       }
-       mutex_unlock(&codec->spdif_mutex);
-
-       /* front */
-       snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag,
-                                  0, format);
-
-       if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT]
-           && !spec->hp_independent_mode)
-               /* headphone out will just decode front left/right (stereo) */
-               snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag,
-                                          0, format);
-
-       /* extra outputs copied from front */
-       for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
-               if (mout->extra_out_nid[i])
-                       snd_hda_codec_setup_stream(codec,
-                                                  mout->extra_out_nid[i],
-                                                  stream_tag, 0, format);
-
-       /* surrounds */
-       for (i = 1; i < mout->num_dacs; i++) {
-               if (chs >= (i + 1) * 2) /* independent out */
-                       snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
-                                                  i * 2, format);
-               else /* copy front */
-                       snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
-                                                  0, format);
-       }
+       spec->multiout.hp_nid = 0;
+       set_stream_active(codec, false);
+       return 0;
+}
+
+static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
+                                   struct hda_codec *codec,
+                                   struct snd_pcm_substream *substream)
+{
+       struct via_spec *spec = codec->spec;
+
+       if (snd_BUG_ON(!spec->hp_dac_nid))
+               return -EINVAL;
+       if (!spec->hp_independent_mode || spec->multiout.hp_nid)
+               return -EBUSY;
+       set_stream_active(codec, true);
+       return 0;
+}
+
+static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
+                                    struct hda_codec *codec,
+                                    struct snd_pcm_substream *substream)
+{
+       set_stream_active(codec, false);
+       return 0;
 }
 
 static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
@@ -1315,18 +997,23 @@ static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
                                          struct snd_pcm_substream *substream)
 {
        struct via_spec *spec = codec->spec;
-       struct hda_multi_out *mout = &spec->multiout;
-       const hda_nid_t *nids = mout->dac_nids;
 
-       if (substream->number == 0)
-               playback_multi_pcm_prep_0(codec, stream_tag, format,
-                                         substream);
-       else {
-               if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
-                   spec->hp_independent_mode)
-                       snd_hda_codec_setup_stream(codec, mout->hp_nid,
-                                                  stream_tag, 0, format);
-       }
+       snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
+                                        format, substream);
+       vt1708_start_hp_work(spec);
+       return 0;
+}
+
+static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
+                                      struct hda_codec *codec,
+                                      unsigned int stream_tag,
+                                      unsigned int format,
+                                      struct snd_pcm_substream *substream)
+{
+       struct via_spec *spec = codec->spec;
+
+       snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
+                                  stream_tag, 0, format);
        vt1708_start_hp_work(spec);
        return 0;
 }
@@ -1336,37 +1023,19 @@ static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
                                    struct snd_pcm_substream *substream)
 {
        struct via_spec *spec = codec->spec;
-       struct hda_multi_out *mout = &spec->multiout;
-       const hda_nid_t *nids = mout->dac_nids;
-       int i;
 
-       if (substream->number == 0) {
-               for (i = 0; i < mout->num_dacs; i++)
-                       snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0);
-
-               if (mout->hp_nid && !spec->hp_independent_mode)
-                       snd_hda_codec_setup_stream(codec, mout->hp_nid,
-                                                  0, 0, 0);
-
-               for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
-                       if (mout->extra_out_nid[i])
-                               snd_hda_codec_setup_stream(codec,
-                                                       mout->extra_out_nid[i],
-                                                       0, 0, 0);
-               mutex_lock(&codec->spdif_mutex);
-               if (mout->dig_out_nid &&
-                   mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
-                       snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
-                                                  0, 0, 0);
-                       mout->dig_out_used = 0;
-               }
-               mutex_unlock(&codec->spdif_mutex);
-       } else {
-               if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
-                   spec->hp_independent_mode)
-                       snd_hda_codec_setup_stream(codec, mout->hp_nid,
-                                                  0, 0, 0);
-       }
+       snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
+       vt1708_stop_hp_work(spec);
+       return 0;
+}
+
+static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                                      struct hda_codec *codec,
+                                      struct snd_pcm_substream *substream)
+{
+       struct via_spec *spec = codec->spec;
+
+       snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
        vt1708_stop_hp_work(spec);
        return 0;
 }
@@ -1435,47 +1104,120 @@ static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
        return 0;
 }
 
-static const struct hda_pcm_stream vt1708_pcm_analog_playback = {
-       .substreams = 2,
+/* analog capture with dynamic ADC switching */
+static int via_dyn_adc_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 via_spec *spec = codec->spec;
+       int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx;
+
+       spec->cur_adc = spec->adc_nids[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 via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                                          struct hda_codec *codec,
+                                          struct snd_pcm_substream *substream)
+{
+       struct via_spec *spec = codec->spec;
+
+       snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
+       spec->cur_adc = 0;
+       return 0;
+}
+
+/* re-setup the stream if running; called from input-src put */
+static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
+{
+       struct via_spec *spec = codec->spec;
+       int adc_idx = spec->inputs[cur].adc_idx;
+       hda_nid_t adc = spec->adc_nids[adc_idx];
+
+       if (spec->cur_adc && spec->cur_adc != adc) {
+               /* stream is running, let's swap the current ADC */
+               __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
+               spec->cur_adc = adc;
+               snd_hda_codec_setup_stream(codec, adc,
+                                          spec->cur_adc_stream_tag, 0,
+                                          spec->cur_adc_format);
+               return true;
+       }
+       return false;
+}
+
+static const struct hda_pcm_stream via_pcm_analog_playback = {
+       .substreams = 1,
        .channels_min = 2,
        .channels_max = 8,
-       .nid = 0x10, /* NID to query formats and rates */
+       /* NID is set in via_build_pcms */
        .ops = {
-               .open = via_playback_pcm_open,
+               .open = via_playback_multi_pcm_open,
+               .close = via_playback_multi_pcm_close,
                .prepare = via_playback_multi_pcm_prepare,
                .cleanup = via_playback_multi_pcm_cleanup
        },
 };
 
+static const struct hda_pcm_stream via_pcm_hp_playback = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+       /* NID is set in via_build_pcms */
+       .ops = {
+               .open = via_playback_hp_pcm_open,
+               .close = via_playback_hp_pcm_close,
+               .prepare = via_playback_hp_pcm_prepare,
+               .cleanup = via_playback_hp_pcm_cleanup
+       },
+};
+
 static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
-       .substreams = 2,
+       .substreams = 1,
        .channels_min = 2,
        .channels_max = 8,
-       .nid = 0x10, /* NID to query formats and rates */
+       /* NID is set in via_build_pcms */
        /* We got noisy outputs on the right channel on VT1708 when
         * 24bit samples are used.  Until any workaround is found,
         * disable the 24bit format, so far.
         */
        .formats = SNDRV_PCM_FMTBIT_S16_LE,
        .ops = {
-               .open = via_playback_pcm_open,
+               .open = via_playback_multi_pcm_open,
+               .close = via_playback_multi_pcm_close,
                .prepare = via_playback_multi_pcm_prepare,
                .cleanup = via_playback_multi_pcm_cleanup
        },
 };
 
-static const struct hda_pcm_stream vt1708_pcm_analog_capture = {
-       .substreams = 2,
+static const struct hda_pcm_stream via_pcm_analog_capture = {
+       .substreams = 1, /* will be changed in via_build_pcms() */
        .channels_min = 2,
        .channels_max = 2,
-       .nid = 0x15, /* NID to query formats and rates */
+       /* NID is set in via_build_pcms */
        .ops = {
                .prepare = via_capture_pcm_prepare,
                .cleanup = via_capture_pcm_cleanup
        },
 };
 
-static const struct hda_pcm_stream vt1708_pcm_digital_playback = {
+static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+       /* NID is set in via_build_pcms */
+       .ops = {
+               .prepare = via_dyn_adc_capture_pcm_prepare,
+               .cleanup = via_dyn_adc_capture_pcm_cleanup,
+       },
+};
+
+static const struct hda_pcm_stream via_pcm_digital_playback = {
        .substreams = 1,
        .channels_min = 2,
        .channels_max = 2,
@@ -1488,19 +1230,47 @@ static const struct hda_pcm_stream vt1708_pcm_digital_playback = {
        },
 };
 
-static const struct hda_pcm_stream vt1708_pcm_digital_capture = {
+static const struct hda_pcm_stream via_pcm_digital_capture = {
        .substreams = 1,
        .channels_min = 2,
        .channels_max = 2,
 };
 
+/*
+ * slave controls for virtual master
+ */
+static const char * const via_slave_vols[] = {
+       "Front Playback Volume",
+       "Surround Playback Volume",
+       "Center Playback Volume",
+       "LFE Playback Volume",
+       "Side Playback Volume",
+       "Headphone Playback Volume",
+       "Speaker Playback Volume",
+       NULL,
+};
+
+static const char * const via_slave_sws[] = {
+       "Front Playback Switch",
+       "Surround Playback Switch",
+       "Center Playback Switch",
+       "LFE Playback Switch",
+       "Side Playback Switch",
+       "Headphone Playback Switch",
+       "Speaker Playback Switch",
+       NULL,
+};
+
 static int via_build_controls(struct hda_codec *codec)
 {
        struct via_spec *spec = codec->spec;
        struct snd_kcontrol *kctl;
-       const struct snd_kcontrol_new *knew;
        int err, i;
 
+       if (spec->set_widgets_power_state)
+               if (!via_clone_control(spec, &via_pin_power_ctl_enum))
+                       return -ENOMEM;
+
        for (i = 0; i < spec->num_mixers; i++) {
                err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
                if (err < 0)
@@ -1509,6 +1279,7 @@ static int via_build_controls(struct hda_codec *codec)
 
        if (spec->multiout.dig_out_nid) {
                err = snd_hda_create_spdif_out_ctls(codec,
+                                                   spec->multiout.dig_out_nid,
                                                    spec->multiout.dig_out_nid);
                if (err < 0)
                        return err;
@@ -1524,6 +1295,23 @@ static int via_build_controls(struct hda_codec *codec)
                        return err;
        }
 
+       /* if we have no master control, let's create it */
+       if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
+               unsigned int vmaster_tlv[4];
+               snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
+                                       HDA_OUTPUT, vmaster_tlv);
+               err = snd_hda_add_vmaster(codec, "Master Playback Volume",
+                                         vmaster_tlv, via_slave_vols);
+               if (err < 0)
+                       return err;
+       }
+       if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
+               err = snd_hda_add_vmaster(codec, "Master Playback Switch",
+                                         NULL, via_slave_sws);
+               if (err < 0)
+                       return err;
+       }
+
        /* assign Capture Source enums to NID */
        kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
        for (i = 0; kctl && i < kctl->count; i++) {
@@ -1532,22 +1320,9 @@ static int via_build_controls(struct hda_codec *codec)
                        return err;
        }
 
-       /* other nid->control mapping */
-       for (i = 0; i < spec->num_mixers; i++) {
-               for (knew = spec->mixers[i]; knew->name; knew++) {
-                       if (knew->iface != NID_MAPPING)
-                               continue;
-                       kctl = snd_hda_find_mixer_ctl(codec, knew->name);
-                       if (kctl == NULL)
-                               continue;
-                       err = snd_hda_add_nid(codec, kctl, 0,
-                                             knew->subdevice);
-               }
-       }
-
        /* init power states */
        set_widgets_power_state(codec);
-       analog_low_current_mode(codec, 1);
+       analog_low_current_mode(codec);
 
        via_free_kctls(codec); /* no longer needed */
        return 0;
@@ -1561,36 +1336,71 @@ static int via_build_pcms(struct hda_codec *codec)
        codec->num_pcms = 1;
        codec->pcm_info = info;
 
+       snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
+                "%s Analog", codec->chip_name);
        info->name = spec->stream_name_analog;
+
+       if (!spec->stream_analog_playback)
+               spec->stream_analog_playback = &via_pcm_analog_playback;
        info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
-               *(spec->stream_analog_playback);
+               *spec->stream_analog_playback;
        info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
                spec->multiout.dac_nids[0];
-       info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
-       info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
-
        info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
                spec->multiout.max_channels;
 
+       if (!spec->stream_analog_capture) {
+               if (spec->dyn_adc_switch)
+                       spec->stream_analog_capture =
+                               &via_pcm_dyn_adc_analog_capture;
+               else
+                       spec->stream_analog_capture = &via_pcm_analog_capture;
+       }
+       info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+               *spec->stream_analog_capture;
+       info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
+       if (!spec->dyn_adc_switch)
+               info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
+                       spec->num_adc_nids;
+
        if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
                codec->num_pcms++;
                info++;
+               snprintf(spec->stream_name_digital,
+                        sizeof(spec->stream_name_digital),
+                        "%s Digital", codec->chip_name);
                info->name = spec->stream_name_digital;
                info->pcm_type = HDA_PCM_TYPE_SPDIF;
                if (spec->multiout.dig_out_nid) {
+                       if (!spec->stream_digital_playback)
+                               spec->stream_digital_playback =
+                                       &via_pcm_digital_playback;
                        info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
-                               *(spec->stream_digital_playback);
+                               *spec->stream_digital_playback;
                        info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
                                spec->multiout.dig_out_nid;
                }
                if (spec->dig_in_nid) {
+                       if (!spec->stream_digital_capture)
+                               spec->stream_digital_capture =
+                                       &via_pcm_digital_capture;
                        info->stream[SNDRV_PCM_STREAM_CAPTURE] =
-                               *(spec->stream_digital_capture);
+                               *spec->stream_digital_capture;
                        info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
                                spec->dig_in_nid;
                }
        }
 
+       if (spec->hp_dac_nid) {
+               codec->num_pcms++;
+               info++;
+               snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
+                        "%s HP", codec->chip_name);
+               info->name = spec->stream_name_hp;
+               info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
+               info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
+                       spec->hp_dac_nid;
+       }
        return 0;
 }
 
@@ -1603,57 +1413,55 @@ static void via_free(struct hda_codec *codec)
 
        via_free_kctls(codec);
        vt1708_stop_hp_work(spec);
-       kfree(codec->spec);
+       kfree(spec->bind_cap_vol);
+       kfree(spec->bind_cap_sw);
+       kfree(spec);
 }
 
-/* mute internal speaker if HP is plugged */
-static void via_hp_automute(struct hda_codec *codec)
+/* mute/unmute outputs */
+static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
+                               hda_nid_t *pins, bool mute)
 {
-       unsigned int present = 0;
-       struct via_spec *spec = codec->spec;
-
-       present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
-
-       if (!spec->hp_independent_mode) {
-               struct snd_ctl_elem_id id;
-               /* auto mute */
-               snd_hda_codec_amp_stereo(
-                       codec, spec->autocfg.line_out_pins[0], HDA_OUTPUT, 0,
-                       HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-               /* notify change */
-               memset(&id, 0, sizeof(id));
-               id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
-               strcpy(id.name, "Front Playback Switch");
-               snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
-                              &id);
-       }
+       int i;
+       for (i = 0; i < num_pins; i++)
+               snd_hda_codec_write(codec, pins[i], 0,
+                                   AC_VERB_SET_PIN_WIDGET_CONTROL,
+                                   mute ? 0 : PIN_OUT);
 }
 
-/* mute mono out if HP or Line out is plugged */
-static void via_mono_automute(struct hda_codec *codec)
+/* mute internal speaker if line-out is plugged */
+static void via_line_automute(struct hda_codec *codec, int present)
 {
-       unsigned int hp_present, lineout_present;
        struct via_spec *spec = codec->spec;
 
-       if (spec->codec_type != VT1716S)
+       if (!spec->autocfg.speaker_outs)
                return;
-
-       lineout_present = snd_hda_jack_detect(codec,
+       if (!present)
+               present = snd_hda_jack_detect(codec,
                                              spec->autocfg.line_out_pins[0]);
+       toggle_output_mutes(codec, spec->autocfg.speaker_outs,
+                           spec->autocfg.speaker_pins,
+                           present);
+}
 
-       /* Mute Mono Out if Line Out is plugged */
-       if (lineout_present) {
-               snd_hda_codec_amp_stereo(
-                       codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, HDA_AMP_MUTE);
-               return;
-       }
-
-       hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
+/* mute internal speaker if HP is plugged */
+static void via_hp_automute(struct hda_codec *codec)
+{
+       int present = 0;
+       struct via_spec *spec = codec->spec;
 
-       if (!spec->hp_independent_mode)
-               snd_hda_codec_amp_stereo(
-                       codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE,
-                       hp_present ? HDA_AMP_MUTE : 0);
+       if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0]) {
+               int nums;
+               present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
+               if (spec->smart51_enabled)
+                       nums = spec->autocfg.line_outs + spec->smart51_nums;
+               else
+                       nums = spec->autocfg.line_outs;
+               toggle_output_mutes(codec, nums,
+                                   spec->autocfg.line_out_pins,
+                                   present);
+       }
+       via_line_automute(codec, present);
 }
 
 static void via_gpio_control(struct hda_codec *codec)
@@ -1678,9 +1486,9 @@ static void via_gpio_control(struct hda_codec *codec)
 
        if (gpio_data == 0x02) {
                /* unmute line out */
-               snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0],
-                                        HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
-
+               snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
+                                   AC_VERB_SET_PIN_WIDGET_CONTROL,
+                                   PIN_OUT);
                if (vol_counter & 0x20) {
                        /* decrease volume */
                        if (vol > master_vol)
@@ -1697,73 +1505,12 @@ static void via_gpio_control(struct hda_codec *codec)
                }
        } else if (!(gpio_data & 0x02)) {
                /* mute line out */
-               snd_hda_codec_amp_stereo(codec,
-                                        spec->autocfg.line_out_pins[0],
-                                        HDA_OUTPUT, 0, HDA_AMP_MUTE,
-                                        HDA_AMP_MUTE);
-       }
-}
-
-/* mute Internal-Speaker if HP is plugged */
-static void via_speaker_automute(struct hda_codec *codec)
-{
-       unsigned int hp_present;
-       struct via_spec *spec = codec->spec;
-
-       if (!VT2002P_COMPATIBLE(spec))
-               return;
-
-       hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
-
-       if (!spec->hp_independent_mode) {
-               struct snd_ctl_elem_id id;
-               snd_hda_codec_amp_stereo(
-                       codec, spec->autocfg.speaker_pins[0], HDA_OUTPUT, 0,
-                       HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0);
-               /* notify change */
-               memset(&id, 0, sizeof(id));
-               id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
-               strcpy(id.name, "Speaker Playback Switch");
-               snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
-                              &id);
-       }
-}
-
-/* mute line-out and internal speaker if HP is plugged */
-static void via_hp_bind_automute(struct hda_codec *codec)
-{
-       /* use long instead of int below just to avoid an internal compiler
-        * error with gcc 4.0.x
-        */
-       unsigned long hp_present, present = 0;
-       struct via_spec *spec = codec->spec;
-       int i;
-
-       if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0])
-               return;
-
-       hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
-
-       present = snd_hda_jack_detect(codec, spec->autocfg.line_out_pins[0]);
-
-       if (!spec->hp_independent_mode) {
-               /* Mute Line-Outs */
-               for (i = 0; i < spec->autocfg.line_outs; i++)
-                       snd_hda_codec_amp_stereo(
-                               codec, spec->autocfg.line_out_pins[i],
-                               HDA_OUTPUT, 0,
-                               HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0);
-               if (hp_present)
-                       present = hp_present;
+               snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
+                                   AC_VERB_SET_PIN_WIDGET_CONTROL,
+                                   0);
        }
-       /* Speakers */
-       for (i = 0; i < spec->autocfg.speaker_outs; i++)
-               snd_hda_codec_amp_stereo(
-                       codec, spec->autocfg.speaker_pins[i], HDA_OUTPUT, 0,
-                       HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
 }
 
-
 /* unsolicited event for jack sensing */
 static void via_unsol_event(struct hda_codec *codec,
                                  unsigned int res)
@@ -1779,39 +1526,8 @@ static void via_unsol_event(struct hda_codec *codec,
                via_hp_automute(codec);
        else if (res == VIA_GPIO_EVENT)
                via_gpio_control(codec);
-       else if (res == VIA_MONO_EVENT)
-               via_mono_automute(codec);
-       else if (res == VIA_SPEAKER_EVENT)
-               via_speaker_automute(codec);
-       else if (res == VIA_BIND_HP_EVENT)
-               via_hp_bind_automute(codec);
-}
-
-static int via_init(struct hda_codec *codec)
-{
-       struct via_spec *spec = codec->spec;
-       int i;
-       for (i = 0; i < spec->num_iverbs; i++)
-               snd_hda_sequence_write(codec, spec->init_verbs[i]);
-
-       /* Lydia Add for EAPD enable */
-       if (!spec->dig_in_nid) { /* No Digital In connection */
-               if (spec->dig_in_pin) {
-                       snd_hda_codec_write(codec, spec->dig_in_pin, 0,
-                                           AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                           PIN_OUT);
-                       snd_hda_codec_write(codec, spec->dig_in_pin, 0,
-                                           AC_VERB_SET_EAPD_BTLENABLE, 0x02);
-               }
-       } else /* enable SPDIF-input pin */
-               snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
-
-       /* assign slave outs */
-       if (spec->slave_dig_outs[0])
-               codec->slave_dig_outs = spec->slave_dig_outs;
-
-       return 0;
+       else if (res == VIA_LINE_EVENT)
+               via_line_automute(codec, false);
 }
 
 #ifdef SND_HDA_NEEDS_RESUME
@@ -1833,11 +1549,15 @@ static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
 
 /*
  */
+
+static int via_init(struct hda_codec *codec);
+
 static const struct hda_codec_ops via_patch_ops = {
        .build_controls = via_build_controls,
        .build_pcms = via_build_pcms,
        .init = via_init,
        .free = via_free,
+       .unsol_event = via_unsol_event,
 #ifdef SND_HDA_NEEDS_RESUME
        .suspend = via_suspend,
 #endif
@@ -1846,372 +1566,869 @@ static const struct hda_codec_ops via_patch_ops = {
 #endif
 };
 
-/* fill in the dac_nids table from the parsed pin configuration */
-static int vt1708_auto_fill_dac_nids(struct via_spec *spec,
-                                    const struct auto_pin_cfg *cfg)
+static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
+{
+       struct via_spec *spec = codec->spec;
+       int i;
+
+       for (i = 0; i < spec->multiout.num_dacs; i++) {
+               if (spec->multiout.dac_nids[i] == dac)
+                       return false;
+       }
+       if (spec->hp_dac_nid == dac)
+               return false;
+       return true;
+}
+
+static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
+                             hda_nid_t target_dac, struct nid_path *path,
+                             int depth, int wid_type)
+{
+       hda_nid_t conn[8];
+       int i, nums;
+
+       nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
+       for (i = 0; i < nums; i++) {
+               if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
+                       continue;
+               if (conn[i] == target_dac || is_empty_dac(codec, conn[i]))
+                       goto found;
+       }
+       if (depth >= MAX_NID_PATH_DEPTH)
+               return false;
+       for (i = 0; i < nums; i++) {
+               unsigned int type;
+               type = get_wcaps_type(get_wcaps(codec, conn[i]));
+               if (type == AC_WID_AUD_OUT ||
+                   (wid_type != -1 && type != wid_type))
+                       continue;
+               if (__parse_output_path(codec, conn[i], target_dac,
+                                     path, depth + 1, AC_WID_AUD_SEL))
+                       goto found;
+       }
+       return false;
+
+ found:
+       path->path[path->depth] = conn[i];
+       path->idx[path->depth] = i;
+       if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
+               path->multi[path->depth] = 1;
+       path->depth++;
+       return true;
+}
+
+static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
+                             hda_nid_t target_dac, struct nid_path *path)
+{
+       if (__parse_output_path(codec, nid, target_dac, path, 1, -1)) {
+               path->path[path->depth] = nid;
+               path->depth++;
+               return true;
+       }
+       return false;
+}
+
+static int via_auto_fill_dac_nids(struct hda_codec *codec)
 {
+       struct via_spec *spec = codec->spec;
+       const struct auto_pin_cfg *cfg = &spec->autocfg;
        int i;
        hda_nid_t nid;
 
+       spec->multiout.dac_nids = spec->private_dac_nids;
        spec->multiout.num_dacs = cfg->line_outs;
+       for (i = 0; i < cfg->line_outs; i++) {
+               nid = cfg->line_out_pins[i];
+               if (!nid)
+                       continue;
+               if (parse_output_path(codec, nid, 0, &spec->out_path[i]))
+                       spec->private_dac_nids[i] = spec->out_path[i].path[0];
+       }
+       return 0;
+}
 
-       spec->multiout.dac_nids = spec->private_dac_nids;
+static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
+                         int chs, bool check_dac, struct nid_path *path)
+{
+       struct via_spec *spec = codec->spec;
+       char name[32];
+       hda_nid_t dac, pin, sel, nid;
+       int err;
 
-       for (i = 0; i < 4; i++) {
-               nid = cfg->line_out_pins[i];
-               if (nid) {
-                       /* config dac list */
-                       switch (i) {
-                       case AUTO_SEQ_FRONT:
-                               spec->private_dac_nids[i] = 0x10;
-                               break;
-                       case AUTO_SEQ_CENLFE:
-                               spec->private_dac_nids[i] = 0x12;
-                               break;
-                       case AUTO_SEQ_SURROUND:
-                               spec->private_dac_nids[i] = 0x11;
-                               break;
-                       case AUTO_SEQ_SIDE:
-                               spec->private_dac_nids[i] = 0x13;
-                               break;
-                       }
-               }
+       dac = check_dac ? path->path[0] : 0;
+       pin = path->path[path->depth - 1];
+       sel = path->depth > 1 ? path->path[1] : 0;
+
+       if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
+               nid = dac;
+       else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
+               nid = pin;
+       else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
+               nid = sel;
+       else
+               nid = 0;
+       if (nid) {
+               sprintf(name, "%s Playback Volume", pfx);
+               err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
+                             HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
+               if (err < 0)
+                       return err;
+               path->vol_ctl = nid;
        }
 
+       if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
+               nid = dac;
+       else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
+               nid = pin;
+       else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
+               nid = sel;
+       else
+               nid = 0;
+       if (nid) {
+               sprintf(name, "%s Playback Switch", pfx);
+               err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
+                             HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
+               if (err < 0)
+                       return err;
+               path->mute_ctl = nid;
+       }
        return 0;
 }
 
+static void mangle_smart51(struct hda_codec *codec)
+{
+       struct via_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       struct auto_pin_cfg_item *ins = cfg->inputs;
+       int i, j, nums, attr;
+       int pins[AUTO_CFG_MAX_INS];
+
+       for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
+               nums = 0;
+               for (i = 0; i < cfg->num_inputs; i++) {
+                       unsigned int def;
+                       if (ins[i].type > AUTO_PIN_LINE_IN)
+                               continue;
+                       def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
+                       if (snd_hda_get_input_pin_attr(def) != attr)
+                               continue;
+                       for (j = 0; j < nums; j++)
+                               if (ins[pins[j]].type < ins[i].type) {
+                                       memmove(pins + j + 1, pins + j,
+                                               (nums - j - 1) * sizeof(int));
+                                       break;
+                               }
+                       pins[j] = i;
+                       nums++;
+               }
+               if (cfg->line_outs + nums < 3)
+                       continue;
+               for (i = 0; i < nums; i++) {
+                       hda_nid_t pin = ins[pins[i]].pin;
+                       spec->smart51_pins[spec->smart51_nums++] = pin;
+                       cfg->line_out_pins[cfg->line_outs++] = pin;
+                       if (cfg->line_outs == 3)
+                               break;
+               }
+               return;
+       }
+}
+
 /* add playback controls from the parsed DAC table */
-static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec,
-                                            const struct auto_pin_cfg *cfg)
+static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
 {
-       char name[32];
+       struct via_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
        static const char * const chname[4] = {
                "Front", "Surround", "C/LFE", "Side"
        };
-       hda_nid_t nid, nid_vol, nid_vols[] = {0x17, 0x19, 0x1a, 0x1b};
-       int i, err;
-
-       for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
-               nid = cfg->line_out_pins[i];
+       int i, idx, err;
+       int old_line_outs;
 
-               if (!nid)
-                       continue;
+       /* check smart51 */
+       old_line_outs = cfg->line_outs;
+       if (cfg->line_outs == 1)
+               mangle_smart51(codec);
 
-               nid_vol = nid_vols[i];
-
-               if (i == AUTO_SEQ_CENLFE) {
-                       /* Center/LFE */
-                       err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
-                                       "Center Playback Volume",
-                                       HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
-                                                           HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-                       err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
-                                             "LFE Playback Volume",
-                                             HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
-                                                                 HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-                       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
-                                             "Center Playback Switch",
-                                             HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
-                                                                 HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-                       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
-                                             "LFE Playback Switch",
-                                             HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
-                                                                 HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-               } else if (i == AUTO_SEQ_FRONT) {
-                       /* add control to mixer index 0 */
-                       err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
-                                             "Master Front Playback Volume",
-                                             HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
-                                                                 HDA_INPUT));
-                       if (err < 0)
-                               return err;
-                       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
-                                             "Master Front Playback Switch",
-                                             HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
-                                                                 HDA_INPUT));
-                       if (err < 0)
-                               return err;
+       err = via_auto_fill_dac_nids(codec);
+       if (err < 0)
+               return err;
 
-                       /* add control to PW3 */
-                       sprintf(name, "%s Playback Volume", chname[i]);
-                       err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
-                                             HDA_COMPOSE_AMP_VAL(nid, 3, 0,
-                                                                 HDA_OUTPUT));
+       for (i = 0; i < cfg->line_outs; i++) {
+               hda_nid_t pin, dac;
+               pin = cfg->line_out_pins[i];
+               dac = spec->multiout.dac_nids[i];
+               if (!pin || !dac)
+                       continue;
+               if (i == HDA_CLFE) {
+                       err = create_ch_ctls(codec, "Center", 1, true,
+                                            &spec->out_path[i]);
                        if (err < 0)
                                return err;
-                       sprintf(name, "%s Playback Switch", chname[i]);
-                       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
-                                             HDA_COMPOSE_AMP_VAL(nid, 3, 0,
-                                                                 HDA_OUTPUT));
+                       err = create_ch_ctls(codec, "LFE", 2, true,
+                                            &spec->out_path[i]);
                        if (err < 0)
                                return err;
                } else {
-                       sprintf(name, "%s Playback Volume", chname[i]);
-                       err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
-                                             HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
-                                                                 HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-                       sprintf(name, "%s Playback Switch", chname[i]);
-                       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
-                                             HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
-                                                                 HDA_OUTPUT));
+                       const char *pfx = chname[i];
+                       if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
+                           cfg->line_outs == 1)
+                               pfx = "Speaker";
+                       err = create_ch_ctls(codec, pfx, 3, true,
+                                            &spec->out_path[i]);
                        if (err < 0)
                                return err;
                }
        }
 
-       return 0;
-}
-
-static void create_hp_imux(struct via_spec *spec)
-{
-       int i;
-       struct hda_input_mux *imux = &spec->private_imux[1];
-       static const char * const texts[] = { "OFF", "ON", NULL};
+       idx = get_connection_index(codec, spec->aa_mix_nid,
+                                  spec->multiout.dac_nids[0]);
+       if (idx >= 0) {
+               /* add control to mixer */
+               err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+                                     "PCM Playback Volume",
+                                     HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
+                                                         idx, HDA_INPUT));
+               if (err < 0)
+                       return err;
+               err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+                                     "PCM Playback Switch",
+                                     HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
+                                                         idx, HDA_INPUT));
+               if (err < 0)
+                       return err;
+       }
 
-       /* for hp mode select */
-       for (i = 0; texts[i]; i++)
-               snd_hda_add_imux_item(imux, texts[i], i, NULL);
+       cfg->line_outs = old_line_outs;
 
-       spec->hp_mux = &spec->private_imux[1];
+       return 0;
 }
 
-static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
+static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
 {
+       struct via_spec *spec = codec->spec;
+       struct nid_path *path;
        int err;
 
        if (!pin)
                return 0;
 
-       spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */
-       spec->hp_independent_mode_index = 1;
+       if (parse_output_path(codec, pin, 0, &spec->hp_path))
+               spec->hp_dac_nid = spec->hp_path.path[0];
 
-       err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
-                             "Headphone Playback Volume",
-                             HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
-       if (err < 0)
-               return err;
-       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
-                             "Headphone Playback Switch",
-                             HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
+       if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
+                              &spec->hp_dep_path) &&
+           !spec->hp_dac_nid)
+               return 0;
+
+       if (spec->hp_dac_nid)
+               path = &spec->hp_path;
+       else
+               path = &spec->hp_dep_path;
+       err = create_ch_ctls(codec, "Headphone", 3, false, path);
        if (err < 0)
                return err;
-
-       create_hp_imux(spec);
+       if (spec->hp_dac_nid) {
+               spec->hp_dep_path.vol_ctl = spec->hp_path.vol_ctl;
+               spec->hp_dep_path.mute_ctl = spec->hp_path.mute_ctl;
+       }
 
        return 0;
 }
 
-/* create playback/capture controls for input pins */
-static int vt_auto_create_analog_input_ctls(struct hda_codec *codec,
-                                           const struct auto_pin_cfg *cfg,
-                                           hda_nid_t cap_nid,
-                                           const hda_nid_t pin_idxs[],
-                                           int num_idxs)
+static int via_auto_create_speaker_ctls(struct hda_codec *codec)
 {
        struct via_spec *spec = codec->spec;
-       struct hda_input_mux *imux = &spec->private_imux[0];
-       int i, err, idx, type, type_idx = 0;
+       hda_nid_t pin, dac;
 
-       /* for internal loopback recording select */
-       for (idx = 0; idx < num_idxs; idx++) {
-               if (pin_idxs[idx] == 0xff) {
-                       snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL);
-                       break;
-               }
-       }
+       pin = spec->autocfg.speaker_pins[0];
+       if (!spec->autocfg.speaker_outs || !pin)
+               return 0;
 
-       for (i = 0; i < cfg->num_inputs; i++) {
-               const char *label;
-               type = cfg->inputs[i].type;
-               for (idx = 0; idx < num_idxs; idx++)
-                       if (pin_idxs[idx] == cfg->inputs[i].pin)
-                               break;
-               if (idx >= num_idxs)
-                       continue;
-               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 (spec->codec_type == VT1708S ||
-                   spec->codec_type == VT1702 ||
-                   spec->codec_type == VT1716S)
-                       err = via_new_analog_input(spec, label, type_idx,
-                                                  idx+1, cap_nid);
-               else
-                       err = via_new_analog_input(spec, label, type_idx,
-                                                  idx, cap_nid);
-               if (err < 0)
-                       return err;
-               snd_hda_add_imux_item(imux, label, idx, NULL);
+       if (parse_output_path(codec, pin, 0, &spec->speaker_path)) {
+               dac = spec->speaker_path.path[0];
+               spec->multiout.extra_out_nid[0] = dac;
+               return create_ch_ctls(codec, "Speaker", 3, true,
+                                     &spec->speaker_path);
        }
+       if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
+                             &spec->speaker_path))
+               return create_ch_ctls(codec, "Speaker", 3, false,
+                                     &spec->speaker_path);
+
        return 0;
 }
 
-/* create playback/capture controls for input pins */
-static int vt1708_auto_create_analog_input_ctls(struct hda_codec *codec,
-                                               const struct auto_pin_cfg *cfg)
+/* look for ADCs */
+static int via_fill_adcs(struct hda_codec *codec)
 {
-       static const hda_nid_t pin_idxs[] = { 0xff, 0x24, 0x1d, 0x1e, 0x21 };
-       return vt_auto_create_analog_input_ctls(codec, cfg, 0x17, pin_idxs,
-                                               ARRAY_SIZE(pin_idxs));
-}
+       struct via_spec *spec = codec->spec;
+       hda_nid_t nid = codec->start_nid;
+       int i;
 
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static const struct hda_amp_list vt1708_loopbacks[] = {
-       { 0x17, HDA_INPUT, 1 },
-       { 0x17, HDA_INPUT, 2 },
-       { 0x17, HDA_INPUT, 3 },
-       { 0x17, HDA_INPUT, 4 },
-       { } /* end */
-};
-#endif
+       for (i = 0; i < codec->num_nodes; i++, nid++) {
+               unsigned int wcaps = get_wcaps(codec, nid);
+               if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
+                       continue;
+               if (wcaps & AC_WCAP_DIGITAL)
+                       continue;
+               if (!(wcaps & AC_WCAP_CONN_LIST))
+                       continue;
+               if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
+                       return -ENOMEM;
+               spec->adc_nids[spec->num_adc_nids++] = nid;
+       }
+       return 0;
+}
 
-static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
+/* input-src control */
+static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_info *uinfo)
 {
-       unsigned int def_conf;
-       unsigned char seqassoc;
-
-       def_conf = snd_hda_codec_get_pincfg(codec, nid);
-       seqassoc = (unsigned char) get_defcfg_association(def_conf);
-       seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
-       if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
-           && (seqassoc == 0xf0 || seqassoc == 0xff)) {
-               def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
-               snd_hda_codec_set_pincfg(codec, nid, def_conf);
-       }
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct via_spec *spec = codec->spec;
 
-       return;
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = spec->num_inputs;
+       if (uinfo->value.enumerated.item >= spec->num_inputs)
+               uinfo->value.enumerated.item = spec->num_inputs - 1;
+       strcpy(uinfo->value.enumerated.name,
+              spec->inputs[uinfo->value.enumerated.item].label);
+       return 0;
 }
 
-static int vt1708_jack_detectect_get(struct snd_kcontrol *kcontrol,
-                                    struct snd_ctl_elem_value *ucontrol)
+static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
+                           struct snd_ctl_elem_value *ucontrol)
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct via_spec *spec = codec->spec;
+       unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 
-       if (spec->codec_type != VT1708)
-               return 0;
-       spec->vt1708_jack_detectect =
-               !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
-       ucontrol->value.integer.value[0] = spec->vt1708_jack_detectect;
+       ucontrol->value.enumerated.item[0] = spec->cur_mux[idx];
        return 0;
 }
 
-static int vt1708_jack_detectect_put(struct snd_kcontrol *kcontrol,
-                                    struct snd_ctl_elem_value *ucontrol)
+static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
+                           struct snd_ctl_elem_value *ucontrol)
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct via_spec *spec = codec->spec;
-       int change;
+       unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+       hda_nid_t mux;
+       int cur;
 
-       if (spec->codec_type != VT1708)
+       cur = ucontrol->value.enumerated.item[0];
+       if (cur < 0 || cur >= spec->num_inputs)
+               return -EINVAL;
+       if (spec->cur_mux[idx] == cur)
                return 0;
-       spec->vt1708_jack_detectect = ucontrol->value.integer.value[0];
-       change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
-               == !spec->vt1708_jack_detectect;
-       if (spec->vt1708_jack_detectect) {
-               mute_aa_path(codec, 1);
-               notify_aa_path_ctls(codec);
+       spec->cur_mux[idx] = cur;
+       if (spec->dyn_adc_switch) {
+               int adc_idx = spec->inputs[cur].adc_idx;
+               mux = spec->mux_nids[adc_idx];
+               via_dyn_adc_pcm_resetup(codec, cur);
+       } else {
+               mux = spec->mux_nids[idx];
+               if (snd_BUG_ON(!mux))
+                       return -EINVAL;
        }
-       return change;
+
+       if (mux) {
+               /* switch to D0 beofre change index */
+               if (snd_hda_codec_read(codec, mux, 0,
+                              AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
+                       snd_hda_codec_write(codec, mux, 0,
+                                   AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+               snd_hda_codec_write(codec, mux, 0,
+                                   AC_VERB_SET_CONNECT_SEL,
+                                   spec->inputs[cur].mux_idx);
+       }
+
+       /* update jack power state */
+       set_widgets_power_state(codec);
+       return 0;
 }
 
-static const struct snd_kcontrol_new vt1708_jack_detectect[] = {
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Jack Detect",
-               .count = 1,
-               .info = snd_ctl_boolean_mono_info,
-               .get = vt1708_jack_detectect_get,
-               .put = vt1708_jack_detectect_put,
-       },
-       {} /* end */
+static const struct snd_kcontrol_new via_input_src_ctl = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       /* The multiple "Capture Source" controls confuse alsamixer
+        * So call somewhat different..
+        */
+       /* .name = "Capture Source", */
+       .name = "Input Source",
+       .info = via_mux_enum_info,
+       .get = via_mux_enum_get,
+       .put = via_mux_enum_put,
 };
 
-static int vt1708_parse_auto_config(struct hda_codec *codec)
+static int create_input_src_ctls(struct hda_codec *codec, int count)
 {
        struct via_spec *spec = codec->spec;
-       int err;
-
-       /* Add HP and CD pin config connect bit re-config action */
-       vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
-       vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
-
-       err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
-       if (err < 0)
-               return err;
-       err = vt1708_auto_fill_dac_nids(spec, &spec->autocfg);
-       if (err < 0)
-               return err;
-       if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
-               return 0; /* can't find valid BIOS pin config */
-
-       err = vt1708_auto_create_multi_out_ctls(spec, &spec->autocfg);
-       if (err < 0)
-               return err;
-       err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
-       if (err < 0)
-               return err;
-       err = vt1708_auto_create_analog_input_ctls(codec, &spec->autocfg);
-       if (err < 0)
-               return err;
-       /* add jack detect on/off control */
-       err = snd_hda_add_new_ctls(codec, vt1708_jack_detectect);
-       if (err < 0)
-               return err;
-
-       spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
-       if (spec->autocfg.dig_outs)
-               spec->multiout.dig_out_nid = VT1708_DIGOUT_NID;
-       spec->dig_in_pin = VT1708_DIGIN_PIN;
-       if (spec->autocfg.dig_in_pin)
-               spec->dig_in_nid = VT1708_DIGIN_NID;
+       struct snd_kcontrol_new *knew;
 
-       if (spec->kctls.list)
-               spec->mixers[spec->num_mixers++] = spec->kctls.list;
+       if (spec->num_inputs <= 1 || !count)
+               return 0; /* no need for single src */
 
-       spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs;
+       knew = via_clone_control(spec, &via_input_src_ctl);
+       if (!knew)
+               return -ENOMEM;
+       knew->count = count;
+       return 0;
+}
 
-       spec->input_mux = &spec->private_imux[0];
+/* add the powersave loopback-list entry */
+static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
+{
+       struct hda_amp_list *list;
 
-       if (spec->hp_mux)
-               via_hp_build(codec);
+       if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
+               return;
+       list = spec->loopback_list + spec->num_loopbacks;
+       list->nid = mix;
+       list->dir = HDA_INPUT;
+       list->idx = idx;
+       spec->num_loopbacks++;
+       spec->loopback.amplist = spec->loopback_list;
+}
 
-       via_smart51_build(spec);
-       return 1;
+static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src,
+                            hda_nid_t dst)
+{
+       return snd_hda_get_conn_index(codec, src, dst, 1) >= 0;
 }
 
-/* init callback for auto-configuration model -- overriding the default init */
-static int via_auto_init(struct hda_codec *codec)
+/* add the input-route to the given pin */
+static bool add_input_route(struct hda_codec *codec, hda_nid_t pin)
 {
        struct via_spec *spec = codec->spec;
+       int c, idx;
 
-       via_init(codec);
-       via_auto_init_multi_out(codec);
-       via_auto_init_hp_out(codec);
-       via_auto_init_analog_input(codec);
-
-       if (VT2002P_COMPATIBLE(spec)) {
-               via_hp_bind_automute(codec);
-       } else {
-               via_hp_automute(codec);
-               via_speaker_automute(codec);
+       spec->inputs[spec->num_inputs].adc_idx = -1;
+       spec->inputs[spec->num_inputs].pin = pin;
+       for (c = 0; c < spec->num_adc_nids; c++) {
+               if (spec->mux_nids[c]) {
+                       idx = get_connection_index(codec, spec->mux_nids[c],
+                                                  pin);
+                       if (idx < 0)
+                               continue;
+                       spec->inputs[spec->num_inputs].mux_idx = idx;
+               } else {
+                       if (!is_reachable_nid(codec, spec->adc_nids[c], pin))
+                               continue;
+               }
+               spec->inputs[spec->num_inputs].adc_idx = c;
+               /* Can primary ADC satisfy all inputs? */
+               if (!spec->dyn_adc_switch &&
+                   spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) {
+                       snd_printd(KERN_INFO
+                                  "via: dynamic ADC switching enabled\n");
+                       spec->dyn_adc_switch = 1;
+               }
+               return true;
        }
-
-       return 0;
+       return false;
+}
+
+static int get_mux_nids(struct hda_codec *codec);
+
+/* parse input-routes; fill ADCs, MUXs and input-src entries */
+static int parse_analog_inputs(struct hda_codec *codec)
+{
+       struct via_spec *spec = codec->spec;
+       const struct auto_pin_cfg *cfg = &spec->autocfg;
+       int i, err;
+
+       err = via_fill_adcs(codec);
+       if (err < 0)
+               return err;
+       err = get_mux_nids(codec);
+       if (err < 0)
+               return err;
+
+       /* fill all input-routes */
+       for (i = 0; i < cfg->num_inputs; i++) {
+               if (add_input_route(codec, cfg->inputs[i].pin))
+                       spec->inputs[spec->num_inputs++].label =
+                               hda_get_autocfg_input_label(codec, cfg, i);
+       }
+
+       /* check for internal loopback recording */
+       if (spec->aa_mix_nid &&
+           add_input_route(codec, spec->aa_mix_nid))
+               spec->inputs[spec->num_inputs++].label = "Stereo Mixer";
+
+       return 0;
+}
+
+/* create analog-loopback volume/switch controls */
+static int create_loopback_ctls(struct hda_codec *codec)
+{
+       struct via_spec *spec = codec->spec;
+       const struct auto_pin_cfg *cfg = &spec->autocfg;
+       const char *prev_label = NULL;
+       int type_idx = 0;
+       int i, j, err, idx;
+
+       if (!spec->aa_mix_nid)
+               return 0;
+
+       for (i = 0; i < cfg->num_inputs; i++) {
+               hda_nid_t pin = cfg->inputs[i].pin;
+               const char *label = hda_get_autocfg_input_label(codec, cfg, i);
+
+               if (prev_label && !strcmp(label, prev_label))
+                       type_idx++;
+               else
+                       type_idx = 0;
+               prev_label = label;
+               idx = get_connection_index(codec, spec->aa_mix_nid, pin);
+               if (idx >= 0) {
+                       err = via_new_analog_input(spec, label, type_idx,
+                                                  idx, spec->aa_mix_nid);
+                       if (err < 0)
+                               return err;
+                       add_loopback_list(spec, spec->aa_mix_nid, idx);
+               }
+
+               /* remember the label for smart51 control */
+               for (j = 0; j < spec->smart51_nums; j++) {
+                       if (spec->smart51_pins[j] == pin) {
+                               spec->smart51_idxs[j] = idx;
+                               spec->smart51_labels[j] = label;
+                               break;
+                       }
+               }
+       }
+       return 0;
+}
+
+/* create mic-boost controls (if present) */
+static int create_mic_boost_ctls(struct hda_codec *codec)
+{
+       struct via_spec *spec = codec->spec;
+       const struct auto_pin_cfg *cfg = &spec->autocfg;
+       int i, err;
+
+       for (i = 0; i < cfg->num_inputs; i++) {
+               hda_nid_t pin = cfg->inputs[i].pin;
+               unsigned int caps;
+               const char *label;
+               char name[32];
+
+               if (cfg->inputs[i].type != AUTO_PIN_MIC)
+                       continue;
+               caps = query_amp_caps(codec, pin, HDA_INPUT);
+               if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
+                       continue;
+               label = hda_get_autocfg_input_label(codec, cfg, i);
+               snprintf(name, sizeof(name), "%s Boost Volume", label);
+               err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
+                             HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
+               if (err < 0)
+                       return err;
+       }
+       return 0;
+}
+
+/* create capture and input-src controls for multiple streams */
+static int create_multi_adc_ctls(struct hda_codec *codec)
+{
+       struct via_spec *spec = codec->spec;
+       int i, err;
+
+       /* create capture mixer elements */
+       for (i = 0; i < spec->num_adc_nids; i++) {
+               hda_nid_t adc = spec->adc_nids[i];
+               err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
+                                       "Capture Volume", i,
+                                       HDA_COMPOSE_AMP_VAL(adc, 3, 0,
+                                                           HDA_INPUT));
+               if (err < 0)
+                       return err;
+               err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+                                       "Capture Switch", i,
+                                       HDA_COMPOSE_AMP_VAL(adc, 3, 0,
+                                                           HDA_INPUT));
+               if (err < 0)
+                       return err;
+       }
+
+       /* input-source control */
+       for (i = 0; i < spec->num_adc_nids; i++)
+               if (!spec->mux_nids[i])
+                       break;
+       err = create_input_src_ctls(codec, i);
+       if (err < 0)
+               return err;
+       return 0;
+}
+
+/* bind capture volume/switch */
+static struct snd_kcontrol_new via_bind_cap_vol_ctl =
+       HDA_BIND_VOL("Capture Volume", 0);
+static struct snd_kcontrol_new via_bind_cap_sw_ctl =
+       HDA_BIND_SW("Capture Switch", 0);
+
+static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret,
+                        struct hda_ctl_ops *ops)
+{
+       struct hda_bind_ctls *ctl;
+       int i;
+
+       ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL);
+       if (!ctl)
+               return -ENOMEM;
+       ctl->ops = ops;
+       for (i = 0; i < spec->num_adc_nids; i++)
+               ctl->values[i] =
+                       HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT);
+       *ctl_ret = ctl;
+       return 0;
+}
+
+/* create capture and input-src controls for dynamic ADC-switch case */
+static int create_dyn_adc_ctls(struct hda_codec *codec)
+{
+       struct via_spec *spec = codec->spec;
+       struct snd_kcontrol_new *knew;
+       int err;
+
+       /* set up the bind capture ctls */
+       err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol);
+       if (err < 0)
+               return err;
+       err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw);
+       if (err < 0)
+               return err;
+
+       /* create capture mixer elements */
+       knew = via_clone_control(spec, &via_bind_cap_vol_ctl);
+       if (!knew)
+               return -ENOMEM;
+       knew->private_value = (long)spec->bind_cap_vol;
+
+       knew = via_clone_control(spec, &via_bind_cap_sw_ctl);
+       if (!knew)
+               return -ENOMEM;
+       knew->private_value = (long)spec->bind_cap_sw;
+
+       /* input-source control */
+       err = create_input_src_ctls(codec, 1);
+       if (err < 0)
+               return err;
+       return 0;
+}
+
+/* parse and create capture-related stuff */
+static int via_auto_create_analog_input_ctls(struct hda_codec *codec)
+{
+       struct via_spec *spec = codec->spec;
+       int err;
+
+       err = parse_analog_inputs(codec);
+       if (err < 0)
+               return err;
+       if (spec->dyn_adc_switch)
+               err = create_dyn_adc_ctls(codec);
+       else
+               err = create_multi_adc_ctls(codec);
+       if (err < 0)
+               return err;
+       err = create_loopback_ctls(codec);
+       if (err < 0)
+               return err;
+       err = create_mic_boost_ctls(codec);
+       if (err < 0)
+               return err;
+       return 0;
+}
+
+static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
+{
+       unsigned int def_conf;
+       unsigned char seqassoc;
+
+       def_conf = snd_hda_codec_get_pincfg(codec, nid);
+       seqassoc = (unsigned char) get_defcfg_association(def_conf);
+       seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
+       if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
+           && (seqassoc == 0xf0 || seqassoc == 0xff)) {
+               def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
+               snd_hda_codec_set_pincfg(codec, nid, def_conf);
+       }
+
+       return;
+}
+
+static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct via_spec *spec = codec->spec;
+
+       if (spec->codec_type != VT1708)
+               return 0;
+       spec->vt1708_jack_detect =
+               !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
+       ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
+       return 0;
+}
+
+static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct via_spec *spec = codec->spec;
+       int change;
+
+       if (spec->codec_type != VT1708)
+               return 0;
+       spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
+       change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
+               == !spec->vt1708_jack_detect;
+       if (spec->vt1708_jack_detect) {
+               mute_aa_path(codec, 1);
+               notify_aa_path_ctls(codec);
+       }
+       return change;
+}
+
+static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "Jack Detect",
+       .count = 1,
+       .info = snd_ctl_boolean_mono_info,
+       .get = vt1708_jack_detect_get,
+       .put = vt1708_jack_detect_put,
+};
+
+static void fill_dig_outs(struct hda_codec *codec);
+static void fill_dig_in(struct hda_codec *codec);
+
+static int via_parse_auto_config(struct hda_codec *codec)
+{
+       struct via_spec *spec = codec->spec;
+       int err;
+
+       err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
+       if (err < 0)
+               return err;
+       if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
+               return -EINVAL;
+
+       err = via_auto_create_multi_out_ctls(codec);
+       if (err < 0)
+               return err;
+       err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
+       if (err < 0)
+               return err;
+       err = via_auto_create_speaker_ctls(codec);
+       if (err < 0)
+               return err;
+       err = via_auto_create_analog_input_ctls(codec);
+       if (err < 0)
+               return err;
+
+       spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+
+       fill_dig_outs(codec);
+       fill_dig_in(codec);
+
+       if (spec->kctls.list)
+               spec->mixers[spec->num_mixers++] = spec->kctls.list;
+
+       spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
+
+       if (spec->hp_dac_nid && spec->hp_dep_path.depth) {
+               err = via_hp_build(codec);
+               if (err < 0)
+                       return err;
+       }
+
+       err = via_smart51_build(codec);
+       if (err < 0)
+               return err;
+
+       /* assign slave outs */
+       if (spec->slave_dig_outs[0])
+               codec->slave_dig_outs = spec->slave_dig_outs;
+
+       return 1;
+}
+
+static void via_auto_init_dig_outs(struct hda_codec *codec)
+{
+       struct via_spec *spec = codec->spec;
+       if (spec->multiout.dig_out_nid)
+               init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
+       if (spec->slave_dig_outs[0])
+               init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
+}
+
+static void via_auto_init_dig_in(struct hda_codec *codec)
+{
+       struct via_spec *spec = codec->spec;
+       if (!spec->dig_in_nid)
+               return;
+       snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
+                           AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
+}
+
+/* initialize the unsolicited events */
+static void via_auto_init_unsol_event(struct hda_codec *codec)
+{
+       struct via_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       unsigned int ev;
+       int i;
+
+       if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
+               snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
+                               AC_VERB_SET_UNSOLICITED_ENABLE,
+                               AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
+
+       if (cfg->speaker_pins[0])
+               ev = VIA_LINE_EVENT;
+       else
+               ev = 0;
+       for (i = 0; i < cfg->line_outs; i++) {
+               if (cfg->line_out_pins[i] &&
+                   is_jack_detectable(codec, cfg->line_out_pins[i]))
+                       snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
+                               AC_VERB_SET_UNSOLICITED_ENABLE,
+                               AC_USRSP_EN | ev | VIA_JACK_EVENT);
+       }
+
+       for (i = 0; i < cfg->num_inputs; i++) {
+               if (is_jack_detectable(codec, cfg->inputs[i].pin))
+                       snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
+                               AC_VERB_SET_UNSOLICITED_ENABLE,
+                               AC_USRSP_EN | VIA_JACK_EVENT);
+       }
+}
+
+static int via_init(struct hda_codec *codec)
+{
+       struct via_spec *spec = codec->spec;
+       int i;
+
+       for (i = 0; i < spec->num_iverbs; i++)
+               snd_hda_sequence_write(codec, spec->init_verbs[i]);
+
+       via_auto_init_multi_out(codec);
+       via_auto_init_hp_out(codec);
+       via_auto_init_speaker_out(codec);
+       via_auto_init_analog_input(codec);
+       via_auto_init_dig_outs(codec);
+       via_auto_init_dig_in(codec);
+
+       via_auto_init_unsol_event(codec);
+
+       via_hp_automute(codec);
+       via_line_automute(codec, false);
+
+       return 0;
 }
 
 static void vt1708_update_hp_jack_state(struct work_struct *work)
@@ -2266,437 +2483,34 @@ static int patch_vt1708(struct hda_codec *codec)
        if (spec == NULL)
                return -ENOMEM;
 
+       spec->aa_mix_nid = 0x17;
+
+       /* Add HP and CD pin config connect bit re-config action */
+       vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
+       vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
+
        /* automatic parse from the BIOS config */
-       err = vt1708_parse_auto_config(codec);
+       err = via_parse_auto_config(codec);
        if (err < 0) {
                via_free(codec);
                return err;
-       } else if (!err) {
-               printk(KERN_INFO "hda_codec: Cannot set up configuration "
-                      "from BIOS.  Using genenic mode...\n");
        }
 
+       /* add jack detect on/off control */
+       if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
+               return -ENOMEM;
 
-       spec->stream_name_analog = "VT1708 Analog";
-       spec->stream_analog_playback = &vt1708_pcm_analog_playback;
        /* disable 32bit format on VT1708 */
        if (codec->vendor_id == 0x11061708)
                spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
-       spec->stream_analog_capture = &vt1708_pcm_analog_capture;
-
-       spec->stream_name_digital = "VT1708 Digital";
-       spec->stream_digital_playback = &vt1708_pcm_digital_playback;
-       spec->stream_digital_capture = &vt1708_pcm_digital_capture;
 
+       codec->patch_ops = via_patch_ops;
 
-       if (!spec->adc_nids && spec->input_mux) {
-               spec->adc_nids = vt1708_adc_nids;
-               spec->num_adc_nids = ARRAY_SIZE(vt1708_adc_nids);
-               get_mux_nids(codec);
-               spec->mixers[spec->num_mixers] = vt1708_capture_mixer;
-               spec->num_mixers++;
-       }
-
-       codec->patch_ops = via_patch_ops;
-
-       codec->patch_ops.init = via_auto_init;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-       spec->loopback.amplist = vt1708_loopbacks;
-#endif
        INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
        return 0;
 }
 
-/* capture mixer elements */
-static const struct snd_kcontrol_new vt1709_capture_mixer[] = {
-       HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 0x0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* The multiple "Capture Source" controls confuse alsamixer
-                * So call somewhat different..
-                */
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 1,
-               .info = via_mux_enum_info,
-               .get = via_mux_enum_get,
-               .put = via_mux_enum_put,
-       },
-       { } /* end */
-};
-
-static const struct hda_verb vt1709_uniwill_init_verbs[] = {
-       {0x20, AC_VERB_SET_UNSOLICITED_ENABLE,
-        AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
-       { }
-};
-
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static const struct hda_verb vt1709_10ch_volume_init_verbs[] = {
-       /*
-        * Unmute ADC0-2 and set the default input to mic-in
-        */
-       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-
-       /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
-        * mixer widget
-        */
-       /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
-       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
-       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
-       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
-
-       /*
-        * Set up output selector (0x1a, 0x1b, 0x29)
-        */
-       /* set vol=0 to output mixers */
-       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
-       /*
-        *  Unmute PW3 and PW4
-        */
-       {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
-       /* Set input of PW4 as MW0 */
-       {0x20, AC_VERB_SET_CONNECT_SEL, 0},
-       /* PW9 Output enable */
-       {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
-       { }
-};
-
-static const struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = {
-       .substreams = 1,
-       .channels_min = 2,
-       .channels_max = 10,
-       .nid = 0x10, /* NID to query formats and rates */
-       .ops = {
-               .open = via_playback_pcm_open,
-               .prepare = via_playback_multi_pcm_prepare,
-               .cleanup = via_playback_multi_pcm_cleanup,
-       },
-};
-
-static const struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = {
-       .substreams = 1,
-       .channels_min = 2,
-       .channels_max = 6,
-       .nid = 0x10, /* NID to query formats and rates */
-       .ops = {
-               .open = via_playback_pcm_open,
-               .prepare = via_playback_multi_pcm_prepare,
-               .cleanup = via_playback_multi_pcm_cleanup,
-       },
-};
-
-static const struct hda_pcm_stream vt1709_pcm_analog_capture = {
-       .substreams = 2,
-       .channels_min = 2,
-       .channels_max = 2,
-       .nid = 0x14, /* NID to query formats and rates */
-       .ops = {
-               .prepare = via_capture_pcm_prepare,
-               .cleanup = via_capture_pcm_cleanup
-       },
-};
-
-static const struct hda_pcm_stream vt1709_pcm_digital_playback = {
-       .substreams = 1,
-       .channels_min = 2,
-       .channels_max = 2,
-       /* NID is set in via_build_pcms */
-       .ops = {
-               .open = via_dig_playback_pcm_open,
-               .close = via_dig_playback_pcm_close
-       },
-};
-
-static const struct hda_pcm_stream vt1709_pcm_digital_capture = {
-       .substreams = 1,
-       .channels_min = 2,
-       .channels_max = 2,
-};
-
-static int vt1709_auto_fill_dac_nids(struct via_spec *spec,
-                                    const struct auto_pin_cfg *cfg)
-{
-       int i;
-       hda_nid_t nid;
-
-       if (cfg->line_outs == 4)  /* 10 channels */
-               spec->multiout.num_dacs = cfg->line_outs+1; /* AOW0~AOW4 */
-       else if (cfg->line_outs == 3) /* 6 channels */
-               spec->multiout.num_dacs = cfg->line_outs; /* AOW0~AOW2 */
-
-       spec->multiout.dac_nids = spec->private_dac_nids;
-
-       if (cfg->line_outs == 4) { /* 10 channels */
-               for (i = 0; i < cfg->line_outs; i++) {
-                       nid = cfg->line_out_pins[i];
-                       if (nid) {
-                               /* config dac list */
-                               switch (i) {
-                               case AUTO_SEQ_FRONT:
-                                       /* AOW0 */
-                                       spec->private_dac_nids[i] = 0x10;
-                                       break;
-                               case AUTO_SEQ_CENLFE:
-                                       /* AOW2 */
-                                       spec->private_dac_nids[i] = 0x12;
-                                       break;
-                               case AUTO_SEQ_SURROUND:
-                                       /* AOW3 */
-                                       spec->private_dac_nids[i] = 0x11;
-                                       break;
-                               case AUTO_SEQ_SIDE:
-                                       /* AOW1 */
-                                       spec->private_dac_nids[i] = 0x27;
-                                       break;
-                               default:
-                                       break;
-                               }
-                       }
-               }
-               spec->private_dac_nids[cfg->line_outs] = 0x28; /* AOW4 */
-
-       } else if (cfg->line_outs == 3) { /* 6 channels */
-               for (i = 0; i < cfg->line_outs; i++) {
-                       nid = cfg->line_out_pins[i];
-                       if (nid) {
-                               /* config dac list */
-                               switch (i) {
-                               case AUTO_SEQ_FRONT:
-                                       /* AOW0 */
-                                       spec->private_dac_nids[i] = 0x10;
-                                       break;
-                               case AUTO_SEQ_CENLFE:
-                                       /* AOW2 */
-                                       spec->private_dac_nids[i] = 0x12;
-                                       break;
-                               case AUTO_SEQ_SURROUND:
-                                       /* AOW1 */
-                                       spec->private_dac_nids[i] = 0x11;
-                                       break;
-                               default:
-                                       break;
-                               }
-                       }
-               }
-       }
-
-       return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec,
-                                            const struct auto_pin_cfg *cfg)
-{
-       char name[32];
-       static const char * const chname[4] = {
-               "Front", "Surround", "C/LFE", "Side"
-       };
-       hda_nid_t nid, nid_vol, nid_vols[] = {0x18, 0x1a, 0x1b, 0x29};
-       int i, err;
-
-       for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
-               nid = cfg->line_out_pins[i];
-
-               if (!nid)
-                       continue;
-
-               nid_vol = nid_vols[i];
-
-               if (i == AUTO_SEQ_CENLFE) {
-                       /* Center/LFE */
-                       err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
-                                             "Center Playback Volume",
-                                             HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
-                                                                 HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-                       err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
-                                             "LFE Playback Volume",
-                                             HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
-                                                                 HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-                       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
-                                             "Center Playback Switch",
-                                             HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
-                                                                 HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-                       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
-                                             "LFE Playback Switch",
-                                             HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
-                                                                 HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-               } else if (i == AUTO_SEQ_FRONT) {
-                       /* ADD control to mixer index 0 */
-                       err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
-                                             "Master Front Playback Volume",
-                                             HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
-                                                                 HDA_INPUT));
-                       if (err < 0)
-                               return err;
-                       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
-                                             "Master Front Playback Switch",
-                                             HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
-                                                                 HDA_INPUT));
-                       if (err < 0)
-                               return err;
-
-                       /* add control to PW3 */
-                       sprintf(name, "%s Playback Volume", chname[i]);
-                       err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
-                                             HDA_COMPOSE_AMP_VAL(nid, 3, 0,
-                                                                 HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-                       sprintf(name, "%s Playback Switch", chname[i]);
-                       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
-                                             HDA_COMPOSE_AMP_VAL(nid, 3, 0,
-                                                                 HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-               } else if (i == AUTO_SEQ_SURROUND) {
-                       sprintf(name, "%s Playback Volume", chname[i]);
-                       err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
-                                             HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
-                                                                 HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-                       sprintf(name, "%s Playback Switch", chname[i]);
-                       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
-                                             HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
-                                                                 HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-               } else if (i == AUTO_SEQ_SIDE) {
-                       sprintf(name, "%s Playback Volume", chname[i]);
-                       err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
-                                             HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
-                                                                 HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-                       sprintf(name, "%s Playback Switch", chname[i]);
-                       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
-                                             HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
-                                                                 HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-               }
-       }
-
-       return 0;
-}
-
-static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
-{
-       int err;
-
-       if (!pin)
-               return 0;
-
-       if (spec->multiout.num_dacs == 5) /* 10 channels */
-               spec->multiout.hp_nid = VT1709_HP_DAC_NID;
-       else if (spec->multiout.num_dacs == 3) /* 6 channels */
-               spec->multiout.hp_nid = 0;
-       spec->hp_independent_mode_index = 1;
-
-       err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
-                             "Headphone Playback Volume",
-                             HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
-       if (err < 0)
-               return err;
-       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
-                             "Headphone Playback Switch",
-                             HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
-       if (err < 0)
-               return err;
-
-       return 0;
-}
-
-/* create playback/capture controls for input pins */
-static int vt1709_auto_create_analog_input_ctls(struct hda_codec *codec,
-                                               const struct auto_pin_cfg *cfg)
-{
-       static const hda_nid_t pin_idxs[] = { 0xff, 0x23, 0x1d, 0x1e, 0x21 };
-       return vt_auto_create_analog_input_ctls(codec, cfg, 0x18, pin_idxs,
-                                               ARRAY_SIZE(pin_idxs));
-}
-
-static int vt1709_parse_auto_config(struct hda_codec *codec)
-{
-       struct via_spec *spec = codec->spec;
-       int err;
-
-       err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
-       if (err < 0)
-               return err;
-       err = vt1709_auto_fill_dac_nids(spec, &spec->autocfg);
-       if (err < 0)
-               return err;
-       if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
-               return 0; /* can't find valid BIOS pin config */
-
-       err = vt1709_auto_create_multi_out_ctls(spec, &spec->autocfg);
-       if (err < 0)
-               return err;
-       err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
-       if (err < 0)
-               return err;
-       err = vt1709_auto_create_analog_input_ctls(codec, &spec->autocfg);
-       if (err < 0)
-               return err;
-
-       spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
-       if (spec->autocfg.dig_outs)
-               spec->multiout.dig_out_nid = VT1709_DIGOUT_NID;
-       spec->dig_in_pin = VT1709_DIGIN_PIN;
-       if (spec->autocfg.dig_in_pin)
-               spec->dig_in_nid = VT1709_DIGIN_NID;
-
-       if (spec->kctls.list)
-               spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
-       spec->input_mux = &spec->private_imux[0];
-
-       if (spec->hp_mux)
-               via_hp_build(codec);
-
-       via_smart51_build(spec);
-       return 1;
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static const struct hda_amp_list vt1709_loopbacks[] = {
-       { 0x18, HDA_INPUT, 1 },
-       { 0x18, HDA_INPUT, 2 },
-       { 0x18, HDA_INPUT, 3 },
-       { 0x18, HDA_INPUT, 4 },
-       { } /* end */
-};
-#endif
-
-static int patch_vt1709_10ch(struct hda_codec *codec)
+static int patch_vt1709(struct hda_codec *codec)
 {
        struct via_spec *spec;
        int err;
@@ -2706,1897 +2520,330 @@ static int patch_vt1709_10ch(struct hda_codec *codec)
        if (spec == NULL)
                return -ENOMEM;
 
-       err = vt1709_parse_auto_config(codec);
-       if (err < 0) {
-               via_free(codec);
-               return err;
-       } else if (!err) {
-               printk(KERN_INFO "hda_codec: Cannot set up configuration.  "
-                      "Using genenic mode...\n");
-       }
-
-       spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs;
-       spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
-
-       spec->stream_name_analog = "VT1709 Analog";
-       spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback;
-       spec->stream_analog_capture = &vt1709_pcm_analog_capture;
-
-       spec->stream_name_digital = "VT1709 Digital";
-       spec->stream_digital_playback = &vt1709_pcm_digital_playback;
-       spec->stream_digital_capture = &vt1709_pcm_digital_capture;
-
-
-       if (!spec->adc_nids && spec->input_mux) {
-               spec->adc_nids = vt1709_adc_nids;
-               spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids);
-               get_mux_nids(codec);
-               spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
-               spec->num_mixers++;
-       }
-
-       codec->patch_ops = via_patch_ops;
-
-       codec->patch_ops.init = via_auto_init;
-       codec->patch_ops.unsol_event = via_unsol_event;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-       spec->loopback.amplist = vt1709_loopbacks;
-#endif
-
-       return 0;
-}
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static const struct hda_verb vt1709_6ch_volume_init_verbs[] = {
-       /*
-        * Unmute ADC0-2 and set the default input to mic-in
-        */
-       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-
-       /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
-        * mixer widget
-        */
-       /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
-       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
-       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
-       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
-
-       /*
-        * Set up output selector (0x1a, 0x1b, 0x29)
-        */
-       /* set vol=0 to output mixers */
-       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
-       /*
-        *  Unmute PW3 and PW4
-        */
-       {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
-       /* Set input of PW4 as MW0 */
-       {0x20, AC_VERB_SET_CONNECT_SEL, 0},
-       /* PW9 Output enable */
-       {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
-       { }
-};
-
-static int patch_vt1709_6ch(struct hda_codec *codec)
-{
-       struct via_spec *spec;
-       int err;
-
-       /* create a codec specific record */
-       spec = via_new_spec(codec);
-       if (spec == NULL)
-               return -ENOMEM;
+       spec->aa_mix_nid = 0x18;
 
-       err = vt1709_parse_auto_config(codec);
+       err = via_parse_auto_config(codec);
        if (err < 0) {
                via_free(codec);
                return err;
-       } else if (!err) {
-               printk(KERN_INFO "hda_codec: Cannot set up configuration.  "
-                      "Using genenic mode...\n");
-       }
-
-       spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs;
-       spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
-
-       spec->stream_name_analog = "VT1709 Analog";
-       spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback;
-       spec->stream_analog_capture = &vt1709_pcm_analog_capture;
-
-       spec->stream_name_digital = "VT1709 Digital";
-       spec->stream_digital_playback = &vt1709_pcm_digital_playback;
-       spec->stream_digital_capture = &vt1709_pcm_digital_capture;
-
-
-       if (!spec->adc_nids && spec->input_mux) {
-               spec->adc_nids = vt1709_adc_nids;
-               spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids);
-               get_mux_nids(codec);
-               spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
-               spec->num_mixers++;
        }
 
        codec->patch_ops = via_patch_ops;
 
-       codec->patch_ops.init = via_auto_init;
-       codec->patch_ops.unsol_event = via_unsol_event;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-       spec->loopback.amplist = vt1709_loopbacks;
-#endif
        return 0;
 }
 
-/* capture mixer elements */
-static const struct snd_kcontrol_new vt1708B_capture_mixer[] = {
-       HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* The multiple "Capture Source" controls confuse alsamixer
-                * So call somewhat different..
-                */
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 1,
-               .info = via_mux_enum_info,
-               .get = via_mux_enum_get,
-               .put = via_mux_enum_put,
-       },
-       { } /* end */
-};
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static const struct hda_verb vt1708B_8ch_volume_init_verbs[] = {
-       /*
-        * Unmute ADC0-1 and set the default input to mic-in
-        */
-       {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-
-       /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
-        * mixer widget
-        */
-       /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
-
-       /*
-        * Set up output mixers
-        */
-       /* set vol=0 to output mixers */
-       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
-       /* Setup default input to PW4 */
-       {0x1d, AC_VERB_SET_CONNECT_SEL, 0},
-       /* PW9 Output enable */
-       {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
-       /* PW10 Input enable */
-       {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
-       { }
-};
-
-static const struct hda_verb vt1708B_4ch_volume_init_verbs[] = {
-       /*
-        * Unmute ADC0-1 and set the default input to mic-in
-        */
-       {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-
-       /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
-        * mixer widget
-        */
-       /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
-
-       /*
-        * Set up output mixers
-        */
-       /* set vol=0 to output mixers */
-       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
-       /* Setup default input of PW4 to MW0 */
-       {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
-       /* PW9 Output enable */
-       {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
-       /* PW10 Input enable */
-       {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
-       { }
-};
-
-static const struct hda_verb vt1708B_uniwill_init_verbs[] = {
-       {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
-        AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
-       {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       { }
-};
-
-static int via_pcm_open_close(struct hda_pcm_stream *hinfo,
-                             struct hda_codec *codec,
-                             struct snd_pcm_substream *substream)
-{
-       int idle = substream->pstr->substream_opened == 1
-               && substream->ref_count == 0;
-
-       analog_low_current_mode(codec, idle);
-       return 0;
-}
-
-static const struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = {
-       .substreams = 2,
-       .channels_min = 2,
-       .channels_max = 8,
-       .nid = 0x10, /* NID to query formats and rates */
-       .ops = {
-               .open = via_playback_pcm_open,
-               .prepare = via_playback_multi_pcm_prepare,
-               .cleanup = via_playback_multi_pcm_cleanup,
-               .close = via_pcm_open_close
-       },
-};
-
-static const struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = {
-       .substreams = 2,
-       .channels_min = 2,
-       .channels_max = 4,
-       .nid = 0x10, /* NID to query formats and rates */
-       .ops = {
-               .open = via_playback_pcm_open,
-               .prepare = via_playback_multi_pcm_prepare,
-               .cleanup = via_playback_multi_pcm_cleanup
-       },
-};
-
-static const struct hda_pcm_stream vt1708B_pcm_analog_capture = {
-       .substreams = 2,
-       .channels_min = 2,
-       .channels_max = 2,
-       .nid = 0x13, /* NID to query formats and rates */
-       .ops = {
-               .open = via_pcm_open_close,
-               .prepare = via_capture_pcm_prepare,
-               .cleanup = via_capture_pcm_cleanup,
-               .close = via_pcm_open_close
-       },
-};
-
-static const struct hda_pcm_stream vt1708B_pcm_digital_playback = {
-       .substreams = 1,
-       .channels_min = 2,
-       .channels_max = 2,
-       /* NID is set in via_build_pcms */
-       .ops = {
-               .open = via_dig_playback_pcm_open,
-               .close = via_dig_playback_pcm_close,
-               .prepare = via_dig_playback_pcm_prepare,
-               .cleanup = via_dig_playback_pcm_cleanup
-       },
-};
-
-static const struct hda_pcm_stream vt1708B_pcm_digital_capture = {
-       .substreams = 1,
-       .channels_min = 2,
-       .channels_max = 2,
-};
-
-/* fill in the dac_nids table from the parsed pin configuration */
-static int vt1708B_auto_fill_dac_nids(struct via_spec *spec,
-                                    const struct auto_pin_cfg *cfg)
-{
-       int i;
-       hda_nid_t nid;
-
-       spec->multiout.num_dacs = cfg->line_outs;
-
-       spec->multiout.dac_nids = spec->private_dac_nids;
-
-       for (i = 0; i < 4; i++) {
-               nid = cfg->line_out_pins[i];
-               if (nid) {
-                       /* config dac list */
-                       switch (i) {
-                       case AUTO_SEQ_FRONT:
-                               spec->private_dac_nids[i] = 0x10;
-                               break;
-                       case AUTO_SEQ_CENLFE:
-                               spec->private_dac_nids[i] = 0x24;
-                               break;
-                       case AUTO_SEQ_SURROUND:
-                               spec->private_dac_nids[i] = 0x11;
-                               break;
-                       case AUTO_SEQ_SIDE:
-                               spec->private_dac_nids[i] = 0x25;
-                               break;
-                       }
-               }
-       }
-
-       return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int vt1708B_auto_create_multi_out_ctls(struct via_spec *spec,
-                                            const struct auto_pin_cfg *cfg)
-{
-       char name[32];
-       static const char * const chname[4] = {
-               "Front", "Surround", "C/LFE", "Side"
-       };
-       hda_nid_t nid_vols[] = {0x16, 0x18, 0x26, 0x27};
-       hda_nid_t nid, nid_vol = 0;
-       int i, err;
-
-       for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
-               nid = cfg->line_out_pins[i];
-
-               if (!nid)
-                       continue;
-
-               nid_vol = nid_vols[i];
-
-               if (i == AUTO_SEQ_CENLFE) {
-                       /* Center/LFE */
-                       err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
-                                             "Center Playback Volume",
-                                             HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
-                                                                 HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-                       err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
-                                             "LFE Playback Volume",
-                                             HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
-                                                                 HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-                       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
-                                             "Center Playback Switch",
-                                             HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
-                                                                 HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-                       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
-                                             "LFE Playback Switch",
-                                             HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
-                                                                 HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-               } else if (i == AUTO_SEQ_FRONT) {
-                       /* add control to mixer index 0 */
-                       err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
-                                             "Master Front Playback Volume",
-                                             HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
-                                                                 HDA_INPUT));
-                       if (err < 0)
-                               return err;
-                       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
-                                             "Master Front Playback Switch",
-                                             HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
-                                                                 HDA_INPUT));
-                       if (err < 0)
-                               return err;
-
-                       /* add control to PW3 */
-                       sprintf(name, "%s Playback Volume", chname[i]);
-                       err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
-                                             HDA_COMPOSE_AMP_VAL(nid, 3, 0,
-                                                                 HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-                       sprintf(name, "%s Playback Switch", chname[i]);
-                       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
-                                             HDA_COMPOSE_AMP_VAL(nid, 3, 0,
-                                                                 HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-               } else {
-                       sprintf(name, "%s Playback Volume", chname[i]);
-                       err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
-                                             HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
-                                                                 HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-                       sprintf(name, "%s Playback Switch", chname[i]);
-                       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
-                                             HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
-                                                                 HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-               }
-       }
-
-       return 0;
-}
-
-static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
-{
-       int err;
-
-       if (!pin)
-               return 0;
-
-       spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */
-       spec->hp_independent_mode_index = 1;
-
-       err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
-                             "Headphone Playback Volume",
-                             HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
-       if (err < 0)
-               return err;
-       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
-                             "Headphone Playback Switch",
-                             HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
-       if (err < 0)
-               return err;
-
-       create_hp_imux(spec);
-
-       return 0;
-}
-
-/* create playback/capture controls for input pins */
-static int vt1708B_auto_create_analog_input_ctls(struct hda_codec *codec,
-                                               const struct auto_pin_cfg *cfg)
-{
-       static const hda_nid_t pin_idxs[] = { 0xff, 0x1f, 0x1a, 0x1b, 0x1e };
-       return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
-                                               ARRAY_SIZE(pin_idxs));
-}
-
-static int vt1708B_parse_auto_config(struct hda_codec *codec)
-{
-       struct via_spec *spec = codec->spec;
-       int err;
-
-       err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
-       if (err < 0)
-               return err;
-       err = vt1708B_auto_fill_dac_nids(spec, &spec->autocfg);
-       if (err < 0)
-               return err;
-       if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
-               return 0; /* can't find valid BIOS pin config */
-
-       err = vt1708B_auto_create_multi_out_ctls(spec, &spec->autocfg);
-       if (err < 0)
-               return err;
-       err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
-       if (err < 0)
-               return err;
-       err = vt1708B_auto_create_analog_input_ctls(codec, &spec->autocfg);
-       if (err < 0)
-               return err;
-
-       spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
-       if (spec->autocfg.dig_outs)
-               spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID;
-       spec->dig_in_pin = VT1708B_DIGIN_PIN;
-       if (spec->autocfg.dig_in_pin)
-               spec->dig_in_nid = VT1708B_DIGIN_NID;
-
-       if (spec->kctls.list)
-               spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
-       spec->input_mux = &spec->private_imux[0];
-
-       if (spec->hp_mux)
-               via_hp_build(codec);
-
-       via_smart51_build(spec);
-       return 1;
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static const struct hda_amp_list vt1708B_loopbacks[] = {
-       { 0x16, HDA_INPUT, 1 },
-       { 0x16, HDA_INPUT, 2 },
-       { 0x16, HDA_INPUT, 3 },
-       { 0x16, HDA_INPUT, 4 },
-       { } /* end */
-};
-#endif
-
-static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
-{
-       struct via_spec *spec = codec->spec;
-       int imux_is_smixer;
-       unsigned int parm;
-       int is_8ch = 0;
-       if ((spec->codec_type != VT1708B_4CH) &&
-           (codec->vendor_id != 0x11064397))
-               is_8ch = 1;
-
-       /* SW0 (17h) = stereo mixer */
-       imux_is_smixer =
-       (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
-        == ((spec->codec_type == VT1708S) ? 5 : 0));
-       /* inputs */
-       /* PW 1/2/5 (1ah/1bh/1eh) */
-       parm = AC_PWRST_D3;
-       set_pin_power_state(codec, 0x1a, &parm);
-       set_pin_power_state(codec, 0x1b, &parm);
-       set_pin_power_state(codec, 0x1e, &parm);
-       if (imux_is_smixer)
-               parm = AC_PWRST_D0;
-       /* SW0 (17h), AIW 0/1 (13h/14h) */
-       snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
-       snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
-       snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
-
-       /* outputs */
-       /* PW0 (19h), SW1 (18h), AOW1 (11h) */
-       parm = AC_PWRST_D3;
-       set_pin_power_state(codec, 0x19, &parm);
-       if (spec->smart51_enabled)
-               set_pin_power_state(codec, 0x1b, &parm);
-       snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
-       snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
-
-       /* PW6 (22h), SW2 (26h), AOW2 (24h) */
-       if (is_8ch) {
-               parm = AC_PWRST_D3;
-               set_pin_power_state(codec, 0x22, &parm);
-               if (spec->smart51_enabled)
-                       set_pin_power_state(codec, 0x1a, &parm);
-               snd_hda_codec_write(codec, 0x26, 0,
-                                   AC_VERB_SET_POWER_STATE, parm);
-               snd_hda_codec_write(codec, 0x24, 0,
-                                   AC_VERB_SET_POWER_STATE, parm);
-       } else if (codec->vendor_id == 0x11064397) {
-               /* PW7(23h), SW2(27h), AOW2(25h) */
-               parm = AC_PWRST_D3;
-               set_pin_power_state(codec, 0x23, &parm);
-               if (spec->smart51_enabled)
-                       set_pin_power_state(codec, 0x1a, &parm);
-               snd_hda_codec_write(codec, 0x27, 0,
-                                   AC_VERB_SET_POWER_STATE, parm);
-               snd_hda_codec_write(codec, 0x25, 0,
-                                   AC_VERB_SET_POWER_STATE, parm);
-       }
-
-       /* PW 3/4/7 (1ch/1dh/23h) */
-       parm = AC_PWRST_D3;
-       /* force to D0 for internal Speaker */
-       set_pin_power_state(codec, 0x1c, &parm);
-       set_pin_power_state(codec, 0x1d, &parm);
-       if (is_8ch)
-               set_pin_power_state(codec, 0x23, &parm);
-
-       /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
-       snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
-                           imux_is_smixer ? AC_PWRST_D0 : parm);
-       snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
-       if (is_8ch) {
-               snd_hda_codec_write(codec, 0x25, 0,
-                                   AC_VERB_SET_POWER_STATE, parm);
-               snd_hda_codec_write(codec, 0x27, 0,
-                                   AC_VERB_SET_POWER_STATE, parm);
-       } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
-               snd_hda_codec_write(codec, 0x25, 0,
-                                   AC_VERB_SET_POWER_STATE, parm);
-}
-
-static int patch_vt1708S(struct hda_codec *codec);
-static int patch_vt1708B_8ch(struct hda_codec *codec)
-{
-       struct via_spec *spec;
-       int err;
-
-       if (get_codec_type(codec) == VT1708BCE)
-               return patch_vt1708S(codec);
-       /* create a codec specific record */
-       spec = via_new_spec(codec);
-       if (spec == NULL)
-               return -ENOMEM;
-
-       /* automatic parse from the BIOS config */
-       err = vt1708B_parse_auto_config(codec);
-       if (err < 0) {
-               via_free(codec);
-               return err;
-       } else if (!err) {
-               printk(KERN_INFO "hda_codec: Cannot set up configuration "
-                      "from BIOS.  Using genenic mode...\n");
-       }
-
-       spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs;
-       spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
-
-       spec->stream_name_analog = "VT1708B Analog";
-       spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback;
-       spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
-
-       spec->stream_name_digital = "VT1708B Digital";
-       spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
-       spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
-
-       if (!spec->adc_nids && spec->input_mux) {
-               spec->adc_nids = vt1708B_adc_nids;
-               spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
-               get_mux_nids(codec);
-               spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
-               spec->num_mixers++;
-       }
-
-       codec->patch_ops = via_patch_ops;
-
-       codec->patch_ops.init = via_auto_init;
-       codec->patch_ops.unsol_event = via_unsol_event;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-       spec->loopback.amplist = vt1708B_loopbacks;
-#endif
-
-       spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
-
-       return 0;
-}
-
-static int patch_vt1708B_4ch(struct hda_codec *codec)
-{
-       struct via_spec *spec;
-       int err;
-
-       /* create a codec specific record */
-       spec = via_new_spec(codec);
-       if (spec == NULL)
-               return -ENOMEM;
-
-       /* automatic parse from the BIOS config */
-       err = vt1708B_parse_auto_config(codec);
-       if (err < 0) {
-               via_free(codec);
-               return err;
-       } else if (!err) {
-               printk(KERN_INFO "hda_codec: Cannot set up configuration "
-                      "from BIOS.  Using genenic mode...\n");
-       }
-
-       spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs;
-       spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
-
-       spec->stream_name_analog = "VT1708B Analog";
-       spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback;
-       spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
-
-       spec->stream_name_digital = "VT1708B Digital";
-       spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
-       spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
-
-       if (!spec->adc_nids && spec->input_mux) {
-               spec->adc_nids = vt1708B_adc_nids;
-               spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
-               get_mux_nids(codec);
-               spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
-               spec->num_mixers++;
-       }
-
-       codec->patch_ops = via_patch_ops;
-
-       codec->patch_ops.init = via_auto_init;
-       codec->patch_ops.unsol_event = via_unsol_event;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-       spec->loopback.amplist = vt1708B_loopbacks;
-#endif
-
-       spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
-
-       return 0;
-}
-
-/* Patch for VT1708S */
-
-/* capture mixer elements */
-static const struct snd_kcontrol_new vt1708S_capture_mixer[] = {
-       HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0,
-                        HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* The multiple "Capture Source" controls confuse alsamixer
-                * So call somewhat different..
-                */
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 1,
-               .info = via_mux_enum_info,
-               .get = via_mux_enum_get,
-               .put = via_mux_enum_put,
-       },
-       { } /* end */
-};
-
-static const struct hda_verb vt1708S_volume_init_verbs[] = {
-       /* Unmute ADC0-1 and set the default input to mic-in */
-       {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-       /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the
-        * analog-loopback mixer widget */
-       /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
-
-       /* Setup default input of PW4 to MW0 */
-       {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
-       /* PW9, PW10  Output enable */
-       {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
-       {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
-       /* Enable Mic Boost Volume backdoor */
-       {0x1, 0xf98, 0x1},
-       /* don't bybass mixer */
-       {0x1, 0xf88, 0xc0},
-       { }
-};
-
-static const struct hda_verb vt1708S_uniwill_init_verbs[] = {
-       {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
-        AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
-       {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       { }
-};
-
-static const struct hda_verb vt1705_uniwill_init_verbs[] = {
-       {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
-        AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
-       {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       { }
-};
-
-static const struct hda_pcm_stream vt1708S_pcm_analog_playback = {
-       .substreams = 2,
-       .channels_min = 2,
-       .channels_max = 8,
-       .nid = 0x10, /* NID to query formats and rates */
-       .ops = {
-               .open = via_playback_pcm_open,
-               .prepare = via_playback_multi_pcm_prepare,
-               .cleanup = via_playback_multi_pcm_cleanup,
-               .close = via_pcm_open_close
-       },
-};
-
-static const struct hda_pcm_stream vt1705_pcm_analog_playback = {
-       .substreams = 2,
-       .channels_min = 2,
-       .channels_max = 6,
-       .nid = 0x10, /* NID to query formats and rates */
-       .ops = {
-               .open = via_playback_pcm_open,
-               .prepare = via_playback_multi_pcm_prepare,
-               .cleanup = via_playback_multi_pcm_cleanup,
-               .close = via_pcm_open_close
-       },
-};
-
-static const struct hda_pcm_stream vt1708S_pcm_analog_capture = {
-       .substreams = 2,
-       .channels_min = 2,
-       .channels_max = 2,
-       .nid = 0x13, /* NID to query formats and rates */
-       .ops = {
-               .open = via_pcm_open_close,
-               .prepare = via_capture_pcm_prepare,
-               .cleanup = via_capture_pcm_cleanup,
-               .close = via_pcm_open_close
-       },
-};
-
-static const struct hda_pcm_stream vt1708S_pcm_digital_playback = {
-       .substreams = 1,
-       .channels_min = 2,
-       .channels_max = 2,
-       /* NID is set in via_build_pcms */
-       .ops = {
-               .open = via_dig_playback_pcm_open,
-               .close = via_dig_playback_pcm_close,
-               .prepare = via_dig_playback_pcm_prepare,
-               .cleanup = via_dig_playback_pcm_cleanup
-       },
-};
-
-/* fill in the dac_nids table from the parsed pin configuration */
-static int vt1708S_auto_fill_dac_nids(struct via_spec *spec,
-                                    const struct auto_pin_cfg *cfg)
-{
-       int i;
-       hda_nid_t nid;
-
-       spec->multiout.num_dacs = cfg->line_outs;
-
-       spec->multiout.dac_nids = spec->private_dac_nids;
-
-       for (i = 0; i < 4; i++) {
-               nid = cfg->line_out_pins[i];
-               if (nid) {
-                       /* config dac list */
-                       switch (i) {
-                       case AUTO_SEQ_FRONT:
-                               spec->private_dac_nids[i] = 0x10;
-                               break;
-                       case AUTO_SEQ_CENLFE:
-                               if (spec->codec->vendor_id == 0x11064397)
-                                       spec->private_dac_nids[i] = 0x25;
-                               else
-                                       spec->private_dac_nids[i] = 0x24;
-                               break;
-                       case AUTO_SEQ_SURROUND:
-                               spec->private_dac_nids[i] = 0x11;
-                               break;
-                       case AUTO_SEQ_SIDE:
-                               spec->private_dac_nids[i] = 0x25;
-                               break;
-                       }
-               }
-       }
-
-       /* for Smart 5.1, line/mic inputs double as output pins */
-       if (cfg->line_outs == 1) {
-               spec->multiout.num_dacs = 3;
-               spec->private_dac_nids[AUTO_SEQ_SURROUND] = 0x11;
-               if (spec->codec->vendor_id == 0x11064397)
-                       spec->private_dac_nids[AUTO_SEQ_CENLFE] = 0x25;
-               else
-                       spec->private_dac_nids[AUTO_SEQ_CENLFE] = 0x24;
-       }
-
-       return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int vt1708S_auto_create_multi_out_ctls(struct hda_codec *codec,
-                                            const struct auto_pin_cfg *cfg)
-{
-       struct via_spec *spec = codec->spec;
-       char name[32];
-       static const char * const chname[4] = {
-               "Front", "Surround", "C/LFE", "Side"
-       };
-       hda_nid_t nid_vols[2][4] = { {0x10, 0x11, 0x24, 0x25},
-                                    {0x10, 0x11, 0x25, 0} };
-       hda_nid_t nid_mutes[2][4] = { {0x1C, 0x18, 0x26, 0x27},
-                                     {0x1C, 0x18, 0x27, 0} };
-       hda_nid_t nid, nid_vol, nid_mute;
-       int i, err;
-
-       for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
-               nid = cfg->line_out_pins[i];
-
-               /* for Smart 5.1, there are always at least six channels */
-               if (!nid && i > AUTO_SEQ_CENLFE)
-                       continue;
-
-               if (codec->vendor_id == 0x11064397) {
-                       nid_vol = nid_vols[1][i];
-                       nid_mute = nid_mutes[1][i];
-               } else {
-                       nid_vol = nid_vols[0][i];
-                       nid_mute = nid_mutes[0][i];
-               }
-               if (!nid_vol && !nid_mute)
-                       continue;
-
-               if (i == AUTO_SEQ_CENLFE) {
-                       /* Center/LFE */
-                       err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
-                                             "Center Playback Volume",
-                                             HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
-                                                                 HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-                       err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
-                                             "LFE Playback Volume",
-                                             HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
-                                                                 HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-                       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
-                                             "Center Playback Switch",
-                                             HDA_COMPOSE_AMP_VAL(nid_mute,
-                                                                 1, 0,
-                                                                 HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-                       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
-                                             "LFE Playback Switch",
-                                             HDA_COMPOSE_AMP_VAL(nid_mute,
-                                                                 2, 0,
-                                                                 HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-               } else if (i == AUTO_SEQ_FRONT) {
-                       /* add control to mixer index 0 */
-                       err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
-                                             "Master Front Playback Volume",
-                                             HDA_COMPOSE_AMP_VAL(0x16, 3, 0,
-                                                                 HDA_INPUT));
-                       if (err < 0)
-                               return err;
-                       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
-                                             "Master Front Playback Switch",
-                                             HDA_COMPOSE_AMP_VAL(0x16, 3, 0,
-                                                                 HDA_INPUT));
-                       if (err < 0)
-                               return err;
-
-                       /* Front */
-                       sprintf(name, "%s Playback Volume", chname[i]);
-                       err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
-                                             HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
-                                                                 HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-                       sprintf(name, "%s Playback Switch", chname[i]);
-                       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
-                                             HDA_COMPOSE_AMP_VAL(nid_mute,
-                                                                 3, 0,
-                                                                 HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-               } else {
-                       sprintf(name, "%s Playback Volume", chname[i]);
-                       err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
-                                             HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
-                                                                 HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-                       sprintf(name, "%s Playback Switch", chname[i]);
-                       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
-                                             HDA_COMPOSE_AMP_VAL(nid_mute,
-                                                                 3, 0,
-                                                                 HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-               }
-       }
-
-       return 0;
-}
-
-static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
-{
-       int err;
-
-       if (!pin)
-               return 0;
-
-       spec->multiout.hp_nid = VT1708S_HP_NID; /* AOW3 */
-       spec->hp_independent_mode_index = 1;
-
-       err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
-                             "Headphone Playback Volume",
-                             HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
-       if (err < 0)
-               return err;
-
-       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
-                             "Headphone Playback Switch",
-                             HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
-       if (err < 0)
-               return err;
-
-       create_hp_imux(spec);
-
-       return 0;
-}
-
-/* create playback/capture controls for input pins */
-static int vt1708S_auto_create_analog_input_ctls(struct hda_codec *codec,
-                                               const struct auto_pin_cfg *cfg)
-{
-       static const hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff };
-       return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
-                                               ARRAY_SIZE(pin_idxs));
-}
-
-/* fill out digital output widgets; one for master and one for slave outputs */
-static void fill_dig_outs(struct hda_codec *codec)
-{
-       struct via_spec *spec = codec->spec;
-       int i;
-
-       for (i = 0; i < spec->autocfg.dig_outs; i++) {
-               hda_nid_t nid;
-               int conn;
-
-               nid = spec->autocfg.dig_out_pins[i];
-               if (!nid)
-                       continue;
-               conn = snd_hda_get_connections(codec, nid, &nid, 1);
-               if (conn < 1)
-                       continue;
-               if (!spec->multiout.dig_out_nid)
-                       spec->multiout.dig_out_nid = nid;
-               else {
-                       spec->slave_dig_outs[0] = nid;
-                       break; /* at most two dig outs */
-               }
-       }
-}
-
-static int vt1708S_parse_auto_config(struct hda_codec *codec)
-{
-       struct via_spec *spec = codec->spec;
-       int err;
-
-       err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
-       if (err < 0)
-               return err;
-       err = vt1708S_auto_fill_dac_nids(spec, &spec->autocfg);
-       if (err < 0)
-               return err;
-       if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
-               return 0; /* can't find valid BIOS pin config */
-
-       err = vt1708S_auto_create_multi_out_ctls(codec, &spec->autocfg);
-       if (err < 0)
-               return err;
-       err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
-       if (err < 0)
-               return err;
-       err = vt1708S_auto_create_analog_input_ctls(codec, &spec->autocfg);
-       if (err < 0)
-               return err;
-
-       spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
-       fill_dig_outs(codec);
-
-       if (spec->kctls.list)
-               spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
-       spec->input_mux = &spec->private_imux[0];
-
-       if (spec->hp_mux)
-               via_hp_build(codec);
-
-       via_smart51_build(spec);
-       return 1;
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static const struct hda_amp_list vt1708S_loopbacks[] = {
-       { 0x16, HDA_INPUT, 1 },
-       { 0x16, HDA_INPUT, 2 },
-       { 0x16, HDA_INPUT, 3 },
-       { 0x16, HDA_INPUT, 4 },
-       { } /* end */
-};
-#endif
-
-static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
-                              int offset, int num_steps, int step_size)
-{
-       snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
-                                 (offset << AC_AMPCAP_OFFSET_SHIFT) |
-                                 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
-                                 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
-                                 (0 << AC_AMPCAP_MUTE_SHIFT));
-}
-
-static int patch_vt1708S(struct hda_codec *codec)
-{
-       struct via_spec *spec;
-       int err;
-
-       /* create a codec specific record */
-       spec = via_new_spec(codec);
-       if (spec == NULL)
-               return -ENOMEM;
-
-       /* automatic parse from the BIOS config */
-       err = vt1708S_parse_auto_config(codec);
-       if (err < 0) {
-               via_free(codec);
-               return err;
-       } else if (!err) {
-               printk(KERN_INFO "hda_codec: Cannot set up configuration "
-                      "from BIOS.  Using genenic mode...\n");
-       }
-
-       spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs;
-       if (codec->vendor_id == 0x11064397)
-               spec->init_verbs[spec->num_iverbs++] =
-                       vt1705_uniwill_init_verbs;
-       else
-               spec->init_verbs[spec->num_iverbs++] =
-                       vt1708S_uniwill_init_verbs;
-
-       if (codec->vendor_id == 0x11060440)
-               spec->stream_name_analog = "VT1818S Analog";
-       else if (codec->vendor_id == 0x11064397)
-               spec->stream_name_analog = "VT1705 Analog";
-       else
-               spec->stream_name_analog = "VT1708S Analog";
-       if (codec->vendor_id == 0x11064397)
-               spec->stream_analog_playback = &vt1705_pcm_analog_playback;
-       else
-               spec->stream_analog_playback = &vt1708S_pcm_analog_playback;
-       spec->stream_analog_capture = &vt1708S_pcm_analog_capture;
-
-       if (codec->vendor_id == 0x11060440)
-               spec->stream_name_digital = "VT1818S Digital";
-       else if (codec->vendor_id == 0x11064397)
-               spec->stream_name_digital = "VT1705 Digital";
-       else
-               spec->stream_name_digital = "VT1708S Digital";
-       spec->stream_digital_playback = &vt1708S_pcm_digital_playback;
-
-       if (!spec->adc_nids && spec->input_mux) {
-               spec->adc_nids = vt1708S_adc_nids;
-               spec->num_adc_nids = ARRAY_SIZE(vt1708S_adc_nids);
-               get_mux_nids(codec);
-               override_mic_boost(codec, 0x1a, 0, 3, 40);
-               override_mic_boost(codec, 0x1e, 0, 3, 40);
-               spec->mixers[spec->num_mixers] = vt1708S_capture_mixer;
-               spec->num_mixers++;
-       }
-
-       codec->patch_ops = via_patch_ops;
-
-       codec->patch_ops.init = via_auto_init;
-       codec->patch_ops.unsol_event = via_unsol_event;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-       spec->loopback.amplist = vt1708S_loopbacks;
-#endif
-
-       /* correct names for VT1708BCE */
-       if (get_codec_type(codec) == VT1708BCE) {
-               kfree(codec->chip_name);
-               codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
-               snprintf(codec->bus->card->mixername,
-                        sizeof(codec->bus->card->mixername),
-                        "%s %s", codec->vendor_name, codec->chip_name);
-               spec->stream_name_analog = "VT1708BCE Analog";
-               spec->stream_name_digital = "VT1708BCE Digital";
-       }
-       /* correct names for VT1818S */
-       if (codec->vendor_id == 0x11060440) {
-               spec->stream_name_analog = "VT1818S Analog";
-               spec->stream_name_digital = "VT1818S Digital";
-       }
-       /* correct names for VT1705 */
-       if (codec->vendor_id == 0x11064397)     {
-               kfree(codec->chip_name);
-               codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
-               snprintf(codec->bus->card->mixername,
-                        sizeof(codec->bus->card->mixername),
-                        "%s %s", codec->vendor_name, codec->chip_name);
-       }
-       spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
-       return 0;
-}
-
-/* Patch for VT1702 */
-
-/* capture mixer elements */
-static const struct snd_kcontrol_new vt1702_capture_mixer[] = {
-       HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x20, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x20, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x1F, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x1F, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Digital Mic Boost Capture Volume", 0x1E, 0x0,
-                        HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* The multiple "Capture Source" controls confuse alsamixer
-                * So call somewhat different..
-                */
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 1,
-               .info = via_mux_enum_info,
-               .get = via_mux_enum_get,
-               .put = via_mux_enum_put,
-       },
-       { } /* end */
-};
-
-static const struct hda_verb vt1702_volume_init_verbs[] = {
-       /*
-        * Unmute ADC0-1 and set the default input to mic-in
-        */
-       {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-
-       /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
-        * mixer widget
-        */
-       /* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */
-       {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
-       {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
-       {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
-       /* Setup default input of PW4 to MW0 */
-       {0x17, AC_VERB_SET_CONNECT_SEL, 0x1},
-       /* PW6 PW7 Output enable */
-       {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
-       {0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
-       /* mixer enable */
-       {0x1, 0xF88, 0x3},
-       /* GPIO 0~2 */
-       {0x1, 0xF82, 0x3F},
-       { }
-};
-
-static const struct hda_verb vt1702_uniwill_init_verbs[] = {
-       {0x17, AC_VERB_SET_UNSOLICITED_ENABLE,
-        AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
-       {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       { }
-};
-
-static const struct hda_pcm_stream vt1702_pcm_analog_playback = {
-       .substreams = 2,
-       .channels_min = 2,
-       .channels_max = 2,
-       .nid = 0x10, /* NID to query formats and rates */
-       .ops = {
-               .open = via_playback_pcm_open,
-               .prepare = via_playback_multi_pcm_prepare,
-               .cleanup = via_playback_multi_pcm_cleanup,
-               .close = via_pcm_open_close
-       },
-};
-
-static const struct hda_pcm_stream vt1702_pcm_analog_capture = {
-       .substreams = 3,
-       .channels_min = 2,
-       .channels_max = 2,
-       .nid = 0x12, /* NID to query formats and rates */
-       .ops = {
-               .open = via_pcm_open_close,
-               .prepare = via_capture_pcm_prepare,
-               .cleanup = via_capture_pcm_cleanup,
-               .close = via_pcm_open_close
-       },
-};
-
-static const struct hda_pcm_stream vt1702_pcm_digital_playback = {
-       .substreams = 2,
-       .channels_min = 2,
-       .channels_max = 2,
-       /* NID is set in via_build_pcms */
-       .ops = {
-               .open = via_dig_playback_pcm_open,
-               .close = via_dig_playback_pcm_close,
-               .prepare = via_dig_playback_pcm_prepare,
-               .cleanup = via_dig_playback_pcm_cleanup
-       },
-};
-
-/* fill in the dac_nids table from the parsed pin configuration */
-static int vt1702_auto_fill_dac_nids(struct via_spec *spec,
-                                    const struct auto_pin_cfg *cfg)
-{
-       spec->multiout.num_dacs = 1;
-       spec->multiout.dac_nids = spec->private_dac_nids;
-
-       if (cfg->line_out_pins[0]) {
-               /* config dac list */
-               spec->private_dac_nids[0] = 0x10;
-       }
-
-       return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int vt1702_auto_create_line_out_ctls(struct via_spec *spec,
-                                            const struct auto_pin_cfg *cfg)
-{
-       int err;
-
-       if (!cfg->line_out_pins[0])
-               return -1;
-
-       /* add control to mixer index 0 */
-       err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
-                             "Master Front Playback Volume",
-                             HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT));
-       if (err < 0)
-               return err;
-       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
-                             "Master Front Playback Switch",
-                             HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT));
-       if (err < 0)
-               return err;
-
-       /* Front */
-       err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
-                             "Front Playback Volume",
-                             HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT));
-       if (err < 0)
-               return err;
-       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
-                             "Front Playback Switch",
-                             HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT));
-       if (err < 0)
-               return err;
-
-       return 0;
-}
-
-static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
-{
-       int err, i;
-       struct hda_input_mux *imux;
-       static const char * const texts[] = { "ON", "OFF", NULL};
-       if (!pin)
-               return 0;
-       spec->multiout.hp_nid = 0x1D;
-       spec->hp_independent_mode_index = 0;
-
-       err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
-                             "Headphone Playback Volume",
-                             HDA_COMPOSE_AMP_VAL(0x1D, 3, 0, HDA_OUTPUT));
-       if (err < 0)
-               return err;
-
-       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
-                             "Headphone Playback Switch",
-                             HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
-       if (err < 0)
-               return err;
-
-       imux = &spec->private_imux[1];
-
-       /* for hp mode select */
-       for (i = 0; texts[i]; i++)
-               snd_hda_add_imux_item(imux, texts[i], i, NULL);
-
-       spec->hp_mux = &spec->private_imux[1];
-       return 0;
-}
-
-/* create playback/capture controls for input pins */
-static int vt1702_auto_create_analog_input_ctls(struct hda_codec *codec,
-                                               const struct auto_pin_cfg *cfg)
-{
-       static const hda_nid_t pin_idxs[] = { 0x14, 0x15, 0x18, 0xff };
-       return vt_auto_create_analog_input_ctls(codec, cfg, 0x1a, pin_idxs,
-                                               ARRAY_SIZE(pin_idxs));
-}
-
-static int vt1702_parse_auto_config(struct hda_codec *codec)
-{
-       struct via_spec *spec = codec->spec;
-       int err;
-
-       err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
-       if (err < 0)
-               return err;
-       err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg);
-       if (err < 0)
-               return err;
-       if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
-               return 0; /* can't find valid BIOS pin config */
-
-       err = vt1702_auto_create_line_out_ctls(spec, &spec->autocfg);
-       if (err < 0)
-               return err;
-       err = vt1702_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
-       if (err < 0)
-               return err;
-       /* limit AA path volume to 0 dB */
-       snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
-                                 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
-                                 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
-                                 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
-                                 (1 << AC_AMPCAP_MUTE_SHIFT));
-       err = vt1702_auto_create_analog_input_ctls(codec, &spec->autocfg);
-       if (err < 0)
-               return err;
-
-       spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
-       fill_dig_outs(codec);
-
-       if (spec->kctls.list)
-               spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
-       spec->input_mux = &spec->private_imux[0];
-
-       if (spec->hp_mux)
-               via_hp_build(codec);
-
-       return 1;
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static const struct hda_amp_list vt1702_loopbacks[] = {
-       { 0x1A, HDA_INPUT, 1 },
-       { 0x1A, HDA_INPUT, 2 },
-       { 0x1A, HDA_INPUT, 3 },
-       { 0x1A, HDA_INPUT, 4 },
-       { } /* end */
-};
-#endif
-
-static void set_widgets_power_state_vt1702(struct hda_codec *codec)
+static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
 {
-       int imux_is_smixer =
-       snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
+       struct via_spec *spec = codec->spec;
+       int imux_is_smixer;
        unsigned int parm;
+       int is_8ch = 0;
+       if ((spec->codec_type != VT1708B_4CH) &&
+           (codec->vendor_id != 0x11064397))
+               is_8ch = 1;
+
+       /* SW0 (17h) = stereo mixer */
+       imux_is_smixer =
+       (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
+        == ((spec->codec_type == VT1708S) ? 5 : 0));
        /* inputs */
-       /* PW 1/2/5 (14h/15h/18h) */
+       /* PW 1/2/5 (1ah/1bh/1eh) */
        parm = AC_PWRST_D3;
-       set_pin_power_state(codec, 0x14, &parm);
-       set_pin_power_state(codec, 0x15, &parm);
-       set_pin_power_state(codec, 0x18, &parm);
+       set_pin_power_state(codec, 0x1a, &parm);
+       set_pin_power_state(codec, 0x1b, &parm);
+       set_pin_power_state(codec, 0x1e, &parm);
        if (imux_is_smixer)
-               parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
-       /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
+               parm = AC_PWRST_D0;
+       /* SW0 (17h), AIW 0/1 (13h/14h) */
+       snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
        snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
-       snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
-       snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
-       snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
+       snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
 
        /* outputs */
-       /* PW 3/4 (16h/17h) */
+       /* PW0 (19h), SW1 (18h), AOW1 (11h) */
        parm = AC_PWRST_D3;
-       set_pin_power_state(codec, 0x17, &parm);
-       set_pin_power_state(codec, 0x16, &parm);
-       /* MW0 (1ah), AOW 0/1 (10h/1dh) */
-       snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
+       set_pin_power_state(codec, 0x19, &parm);
+       if (spec->smart51_enabled)
+               set_pin_power_state(codec, 0x1b, &parm);
+       snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
+       snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
+
+       /* PW6 (22h), SW2 (26h), AOW2 (24h) */
+       if (is_8ch) {
+               parm = AC_PWRST_D3;
+               set_pin_power_state(codec, 0x22, &parm);
+               if (spec->smart51_enabled)
+                       set_pin_power_state(codec, 0x1a, &parm);
+               snd_hda_codec_write(codec, 0x26, 0,
+                                   AC_VERB_SET_POWER_STATE, parm);
+               snd_hda_codec_write(codec, 0x24, 0,
+                                   AC_VERB_SET_POWER_STATE, parm);
+       } else if (codec->vendor_id == 0x11064397) {
+               /* PW7(23h), SW2(27h), AOW2(25h) */
+               parm = AC_PWRST_D3;
+               set_pin_power_state(codec, 0x23, &parm);
+               if (spec->smart51_enabled)
+                       set_pin_power_state(codec, 0x1a, &parm);
+               snd_hda_codec_write(codec, 0x27, 0,
+                                   AC_VERB_SET_POWER_STATE, parm);
+               snd_hda_codec_write(codec, 0x25, 0,
+                                   AC_VERB_SET_POWER_STATE, parm);
+       }
+
+       /* PW 3/4/7 (1ch/1dh/23h) */
+       parm = AC_PWRST_D3;
+       /* force to D0 for internal Speaker */
+       set_pin_power_state(codec, 0x1c, &parm);
+       set_pin_power_state(codec, 0x1d, &parm);
+       if (is_8ch)
+               set_pin_power_state(codec, 0x23, &parm);
+
+       /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
+       snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
                            imux_is_smixer ? AC_PWRST_D0 : parm);
        snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
-       snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
+       if (is_8ch) {
+               snd_hda_codec_write(codec, 0x25, 0,
+                                   AC_VERB_SET_POWER_STATE, parm);
+               snd_hda_codec_write(codec, 0x27, 0,
+                                   AC_VERB_SET_POWER_STATE, parm);
+       } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
+               snd_hda_codec_write(codec, 0x25, 0,
+                                   AC_VERB_SET_POWER_STATE, parm);
 }
 
-static int patch_vt1702(struct hda_codec *codec)
+static int patch_vt1708S(struct hda_codec *codec);
+static int patch_vt1708B(struct hda_codec *codec)
 {
        struct via_spec *spec;
        int err;
 
+       if (get_codec_type(codec) == VT1708BCE)
+               return patch_vt1708S(codec);
+
        /* create a codec specific record */
        spec = via_new_spec(codec);
        if (spec == NULL)
                return -ENOMEM;
 
+       spec->aa_mix_nid = 0x16;
+
        /* automatic parse from the BIOS config */
-       err = vt1702_parse_auto_config(codec);
+       err = via_parse_auto_config(codec);
        if (err < 0) {
                via_free(codec);
                return err;
-       } else if (!err) {
-               printk(KERN_INFO "hda_codec: Cannot set up configuration "
-                      "from BIOS.  Using genenic mode...\n");
-       }
-
-       spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs;
-       spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs;
-
-       spec->stream_name_analog = "VT1702 Analog";
-       spec->stream_analog_playback = &vt1702_pcm_analog_playback;
-       spec->stream_analog_capture = &vt1702_pcm_analog_capture;
-
-       spec->stream_name_digital = "VT1702 Digital";
-       spec->stream_digital_playback = &vt1702_pcm_digital_playback;
-
-       if (!spec->adc_nids && spec->input_mux) {
-               spec->adc_nids = vt1702_adc_nids;
-               spec->num_adc_nids = ARRAY_SIZE(vt1702_adc_nids);
-               get_mux_nids(codec);
-               spec->mixers[spec->num_mixers] = vt1702_capture_mixer;
-               spec->num_mixers++;
        }
 
        codec->patch_ops = via_patch_ops;
 
-       codec->patch_ops.init = via_auto_init;
-       codec->patch_ops.unsol_event = via_unsol_event;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-       spec->loopback.amplist = vt1702_loopbacks;
-#endif
+       spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
 
-       spec->set_widgets_power_state =  set_widgets_power_state_vt1702;
        return 0;
 }
 
-/* Patch for VT1718S */
-
-/* capture mixer elements */
-static const struct snd_kcontrol_new vt1718S_capture_mixer[] = {
-       HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0,
-                        HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* The multiple "Capture Source" controls confuse alsamixer
-                * So call somewhat different..
-                */
-               .name = "Input Source",
-               .count = 2,
-               .info = via_mux_enum_info,
-               .get = via_mux_enum_get,
-               .put = via_mux_enum_put,
-       },
-       { } /* end */
-};
-
-static const struct hda_verb vt1718S_volume_init_verbs[] = {
-       /*
-        * Unmute ADC0-1 and set the default input to mic-in
-        */
-       {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-       /* Enable MW0 adjust Gain 5 */
-       {0x1, 0xfb2, 0x10},
-       /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
-        * mixer widget
-        */
-       /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
-       {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-       {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-       {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)},
-       /* PW9 PW10 Output enable */
-       {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
-       {0x2e, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
-       /* PW11 Input enable */
-       {0x2f, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_IN_EN},
-       /* Enable Boost Volume backdoor */
-       {0x1, 0xf88, 0x8},
-       /* MW0/1/2/3/4: un-mute index 0 (AOWx), mute index 1 (MW9) */
-       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       /* set MUX1 = 2 (AOW4), MUX2 = 1 (AOW3) */
-       {0x34, AC_VERB_SET_CONNECT_SEL, 0x2},
-       {0x35, AC_VERB_SET_CONNECT_SEL, 0x1},
-       { }
-};
-
-
-static const struct hda_verb vt1718S_uniwill_init_verbs[] = {
-       {0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
-        AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
-       {0x24, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       {0x27, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
+/* Patch for VT1708S */
+static const struct hda_verb vt1708S_init_verbs[] = {
+       /* Enable Mic Boost Volume backdoor */
+       {0x1, 0xf98, 0x1},
+       /* don't bybass mixer */
+       {0x1, 0xf88, 0xc0},
        { }
 };
 
-static const struct hda_pcm_stream vt1718S_pcm_analog_playback = {
-       .substreams = 2,
-       .channels_min = 2,
-       .channels_max = 10,
-       .nid = 0x8, /* NID to query formats and rates */
-       .ops = {
-               .open = via_playback_pcm_open,
-               .prepare = via_playback_multi_pcm_prepare,
-               .cleanup = via_playback_multi_pcm_cleanup,
-               .close = via_pcm_open_close,
-       },
-};
-
-static const struct hda_pcm_stream vt1718S_pcm_analog_capture = {
-       .substreams = 2,
-       .channels_min = 2,
-       .channels_max = 2,
-       .nid = 0x10, /* NID to query formats and rates */
-       .ops = {
-               .open = via_pcm_open_close,
-               .prepare = via_capture_pcm_prepare,
-               .cleanup = via_capture_pcm_cleanup,
-               .close = via_pcm_open_close,
-       },
-};
-
-static const struct hda_pcm_stream vt1718S_pcm_digital_playback = {
-       .substreams = 2,
-       .channels_min = 2,
-       .channels_max = 2,
-       /* NID is set in via_build_pcms */
-       .ops = {
-               .open = via_dig_playback_pcm_open,
-               .close = via_dig_playback_pcm_close,
-               .prepare = via_dig_playback_pcm_prepare,
-               .cleanup = via_dig_playback_pcm_cleanup
-       },
-};
-
-static const struct hda_pcm_stream vt1718S_pcm_digital_capture = {
-       .substreams = 1,
-       .channels_min = 2,
-       .channels_max = 2,
-};
-
-/* fill in the dac_nids table from the parsed pin configuration */
-static int vt1718S_auto_fill_dac_nids(struct via_spec *spec,
-                                    const struct auto_pin_cfg *cfg)
+/* fill out digital output widgets; one for master and one for slave outputs */
+static void fill_dig_outs(struct hda_codec *codec)
 {
+       struct via_spec *spec = codec->spec;
        int i;
-       hda_nid_t nid;
 
-       spec->multiout.num_dacs = cfg->line_outs;
-
-       spec->multiout.dac_nids = spec->private_dac_nids;
+       for (i = 0; i < spec->autocfg.dig_outs; i++) {
+               hda_nid_t nid;
+               int conn;
 
-       for (i = 0; i < 4; i++) {
-               nid = cfg->line_out_pins[i];
-               if (nid) {
-                       /* config dac list */
-                       switch (i) {
-                       case AUTO_SEQ_FRONT:
-                               spec->private_dac_nids[i] = 0x8;
-                               break;
-                       case AUTO_SEQ_CENLFE:
-                               spec->private_dac_nids[i] = 0xa;
-                               break;
-                       case AUTO_SEQ_SURROUND:
-                               spec->private_dac_nids[i] = 0x9;
-                               break;
-                       case AUTO_SEQ_SIDE:
-                               spec->private_dac_nids[i] = 0xb;
-                               break;
-                       }
+               nid = spec->autocfg.dig_out_pins[i];
+               if (!nid)
+                       continue;
+               conn = snd_hda_get_connections(codec, nid, &nid, 1);
+               if (conn < 1)
+                       continue;
+               if (!spec->multiout.dig_out_nid)
+                       spec->multiout.dig_out_nid = nid;
+               else {
+                       spec->slave_dig_outs[0] = nid;
+                       break; /* at most two dig outs */
                }
        }
-
-       return 0;
 }
 
-/* add playback controls from the parsed DAC table */
-static int vt1718S_auto_create_multi_out_ctls(struct via_spec *spec,
-                                            const struct auto_pin_cfg *cfg)
+static void fill_dig_in(struct hda_codec *codec)
 {
-       char name[32];
-       static const char * const chname[4] = {
-               "Front", "Surround", "C/LFE", "Side"
-       };
-       hda_nid_t nid_vols[] = {0x8, 0x9, 0xa, 0xb};
-       hda_nid_t nid_mutes[] = {0x24, 0x25, 0x26, 0x27};
-       hda_nid_t nid, nid_vol, nid_mute = 0;
+       struct via_spec *spec = codec->spec;
+       hda_nid_t dig_nid;
        int i, err;
 
-       for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
-               nid = cfg->line_out_pins[i];
+       if (!spec->autocfg.dig_in_pin)
+               return;
 
-               if (!nid)
+       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;
-               nid_vol = nid_vols[i];
-               nid_mute = nid_mutes[i];
-
-               if (i == AUTO_SEQ_CENLFE) {
-                       /* Center/LFE */
-                       err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
-                                             "Center Playback Volume",
-                                             HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
-                                                                 HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-                       err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
-                                             "LFE Playback Volume",
-                                             HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
-                                                                 HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-                       err = via_add_control(
-                               spec, VIA_CTL_WIDGET_MUTE,
-                               "Center Playback Switch",
-                               HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0,
-                                                   HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-                       err = via_add_control(
-                               spec, VIA_CTL_WIDGET_MUTE,
-                               "LFE Playback Switch",
-                               HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0,
-                                                   HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-               } else if (i == AUTO_SEQ_FRONT) {
-                       /* add control to mixer index 0 */
-                       err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
-                                             "Master Front Playback Volume",
-                                             HDA_COMPOSE_AMP_VAL(0x21, 3, 5,
-                                                                 HDA_INPUT));
-                       if (err < 0)
-                               return err;
-                       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
-                                             "Master Front Playback Switch",
-                                             HDA_COMPOSE_AMP_VAL(0x21, 3, 5,
-                                                                 HDA_INPUT));
-                       if (err < 0)
-                               return err;
-                       /* Front */
-                       sprintf(name, "%s Playback Volume", chname[i]);
-                       err = via_add_control(
-                               spec, VIA_CTL_WIDGET_VOL, name,
-                               HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-                       sprintf(name, "%s Playback Switch", chname[i]);
-                       err = via_add_control(
-                               spec, VIA_CTL_WIDGET_MUTE, name,
-                               HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
-                                                   HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-               } else {
-                       sprintf(name, "%s Playback Volume", chname[i]);
-                       err = via_add_control(
-                               spec, VIA_CTL_WIDGET_VOL, name,
-                               HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-                       sprintf(name, "%s Playback Switch", chname[i]);
-                       err = via_add_control(
-                               spec, VIA_CTL_WIDGET_MUTE, name,
-                               HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
-                                                   HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
+               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;
                }
        }
-       return 0;
 }
 
-static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
+static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
+                              int offset, int num_steps, int step_size)
+{
+       snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
+                                 (offset << AC_AMPCAP_OFFSET_SHIFT) |
+                                 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
+                                 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
+                                 (0 << AC_AMPCAP_MUTE_SHIFT));
+}
+
+static int patch_vt1708S(struct hda_codec *codec)
 {
+       struct via_spec *spec;
        int err;
 
-       if (!pin)
-               return 0;
+       /* create a codec specific record */
+       spec = via_new_spec(codec);
+       if (spec == NULL)
+               return -ENOMEM;
 
-       spec->multiout.hp_nid = 0xc; /* AOW4 */
-       spec->hp_independent_mode_index = 1;
+       spec->aa_mix_nid = 0x16;
+       override_mic_boost(codec, 0x1a, 0, 3, 40);
+       override_mic_boost(codec, 0x1e, 0, 3, 40);
 
-       err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
-                             "Headphone Playback Volume",
-                             HDA_COMPOSE_AMP_VAL(0xc, 3, 0, HDA_OUTPUT));
-       if (err < 0)
+       /* automatic parse from the BIOS config */
+       err = via_parse_auto_config(codec);
+       if (err < 0) {
+               via_free(codec);
                return err;
+       }
 
-       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
-                             "Headphone Playback Switch",
-                             HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
-       if (err < 0)
-               return err;
+       spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
 
-       create_hp_imux(spec);
+       codec->patch_ops = via_patch_ops;
+
+       /* correct names for VT1708BCE */
+       if (get_codec_type(codec) == VT1708BCE) {
+               kfree(codec->chip_name);
+               codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
+               snprintf(codec->bus->card->mixername,
+                        sizeof(codec->bus->card->mixername),
+                        "%s %s", codec->vendor_name, codec->chip_name);
+       }
+       /* correct names for VT1705 */
+       if (codec->vendor_id == 0x11064397)     {
+               kfree(codec->chip_name);
+               codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
+               snprintf(codec->bus->card->mixername,
+                        sizeof(codec->bus->card->mixername),
+                        "%s %s", codec->vendor_name, codec->chip_name);
+       }
+       spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
        return 0;
 }
 
-/* create playback/capture controls for input pins */
-static int vt1718S_auto_create_analog_input_ctls(struct hda_codec *codec,
-                                               const struct auto_pin_cfg *cfg)
+/* Patch for VT1702 */
+
+static const struct hda_verb vt1702_init_verbs[] = {
+       /* mixer enable */
+       {0x1, 0xF88, 0x3},
+       /* GPIO 0~2 */
+       {0x1, 0xF82, 0x3F},
+       { }
+};
+
+static void set_widgets_power_state_vt1702(struct hda_codec *codec)
 {
-       static const hda_nid_t pin_idxs[] = { 0x2c, 0x2b, 0x2a, 0x29, 0, 0xff };
-       return vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
-                                               ARRAY_SIZE(pin_idxs));
+       int imux_is_smixer =
+       snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
+       unsigned int parm;
+       /* inputs */
+       /* PW 1/2/5 (14h/15h/18h) */
+       parm = AC_PWRST_D3;
+       set_pin_power_state(codec, 0x14, &parm);
+       set_pin_power_state(codec, 0x15, &parm);
+       set_pin_power_state(codec, 0x18, &parm);
+       if (imux_is_smixer)
+               parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
+       /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
+       snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
+       snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
+       snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
+       snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
+
+       /* outputs */
+       /* PW 3/4 (16h/17h) */
+       parm = AC_PWRST_D3;
+       set_pin_power_state(codec, 0x17, &parm);
+       set_pin_power_state(codec, 0x16, &parm);
+       /* MW0 (1ah), AOW 0/1 (10h/1dh) */
+       snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
+                           imux_is_smixer ? AC_PWRST_D0 : parm);
+       snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
+       snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
 }
 
-static int vt1718S_parse_auto_config(struct hda_codec *codec)
+static int patch_vt1702(struct hda_codec *codec)
 {
-       struct via_spec *spec = codec->spec;
+       struct via_spec *spec;
        int err;
 
-       err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
-
-       if (err < 0)
-               return err;
-       err = vt1718S_auto_fill_dac_nids(spec, &spec->autocfg);
-       if (err < 0)
-               return err;
-       if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
-               return 0; /* can't find valid BIOS pin config */
-
-       err = vt1718S_auto_create_multi_out_ctls(spec, &spec->autocfg);
-       if (err < 0)
-               return err;
-       err = vt1718S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
-       if (err < 0)
-               return err;
-       err = vt1718S_auto_create_analog_input_ctls(codec, &spec->autocfg);
-       if (err < 0)
-               return err;
+       /* create a codec specific record */
+       spec = via_new_spec(codec);
+       if (spec == NULL)
+               return -ENOMEM;
 
-       spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+       spec->aa_mix_nid = 0x1a;
 
-       fill_dig_outs(codec);
+       /* limit AA path volume to 0 dB */
+       snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
+                                 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
+                                 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+                                 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+                                 (1 << AC_AMPCAP_MUTE_SHIFT));
 
-       if (spec->autocfg.dig_in_pin && codec->vendor_id == 0x11060428)
-               spec->dig_in_nid = 0x13;
+       /* automatic parse from the BIOS config */
+       err = via_parse_auto_config(codec);
+       if (err < 0) {
+               via_free(codec);
+               return err;
+       }
 
-       if (spec->kctls.list)
-               spec->mixers[spec->num_mixers++] = spec->kctls.list;
+       spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
 
-       spec->input_mux = &spec->private_imux[0];
+       codec->patch_ops = via_patch_ops;
 
-       if (spec->hp_mux)
-               via_hp_build(codec);
+       spec->set_widgets_power_state =  set_widgets_power_state_vt1702;
+       return 0;
+}
 
-       via_smart51_build(spec);
+/* Patch for VT1718S */
 
-       return 1;
-}
+static const struct hda_verb vt1718S_init_verbs[] = {
+       /* Enable MW0 adjust Gain 5 */
+       {0x1, 0xfb2, 0x10},
+       /* Enable Boost Volume backdoor */
+       {0x1, 0xf88, 0x8},
 
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static const struct hda_amp_list vt1718S_loopbacks[] = {
-       { 0x21, HDA_INPUT, 1 },
-       { 0x21, HDA_INPUT, 2 },
-       { 0x21, HDA_INPUT, 3 },
-       { 0x21, HDA_INPUT, 4 },
-       { } /* end */
+       { }
 };
-#endif
 
 static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
 {
@@ -4674,57 +2921,21 @@ static int patch_vt1718S(struct hda_codec *codec)
        if (spec == NULL)
                return -ENOMEM;
 
+       spec->aa_mix_nid = 0x21;
+       override_mic_boost(codec, 0x2b, 0, 3, 40);
+       override_mic_boost(codec, 0x29, 0, 3, 40);
+
        /* automatic parse from the BIOS config */
-       err = vt1718S_parse_auto_config(codec);
+       err = via_parse_auto_config(codec);
        if (err < 0) {
                via_free(codec);
                return err;
-       } else if (!err) {
-               printk(KERN_INFO "hda_codec: Cannot set up configuration "
-                      "from BIOS.  Using genenic mode...\n");
        }
 
-       spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs;
-       spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs;
-
-       if (codec->vendor_id == 0x11060441)
-               spec->stream_name_analog = "VT2020 Analog";
-       else if (codec->vendor_id == 0x11064441)
-               spec->stream_name_analog = "VT1828S Analog";
-       else
-               spec->stream_name_analog = "VT1718S Analog";
-       spec->stream_analog_playback = &vt1718S_pcm_analog_playback;
-       spec->stream_analog_capture = &vt1718S_pcm_analog_capture;
-
-       if (codec->vendor_id == 0x11060441)
-               spec->stream_name_digital = "VT2020 Digital";
-       else if (codec->vendor_id == 0x11064441)
-               spec->stream_name_digital = "VT1828S Digital";
-       else
-               spec->stream_name_digital = "VT1718S Digital";
-       spec->stream_digital_playback = &vt1718S_pcm_digital_playback;
-       if (codec->vendor_id == 0x11060428 || codec->vendor_id == 0x11060441)
-               spec->stream_digital_capture = &vt1718S_pcm_digital_capture;
-
-       if (!spec->adc_nids && spec->input_mux) {
-               spec->adc_nids = vt1718S_adc_nids;
-               spec->num_adc_nids = ARRAY_SIZE(vt1718S_adc_nids);
-               get_mux_nids(codec);
-               override_mic_boost(codec, 0x2b, 0, 3, 40);
-               override_mic_boost(codec, 0x29, 0, 3, 40);
-               spec->mixers[spec->num_mixers] = vt1718S_capture_mixer;
-               spec->num_mixers++;
-       }
+       spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
 
        codec->patch_ops = via_patch_ops;
 
-       codec->patch_ops.init = via_auto_init;
-       codec->patch_ops.unsol_event = via_unsol_event;
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-       spec->loopback.amplist = vt1718S_loopbacks;
-#endif
-
        spec->set_widgets_power_state =  set_widgets_power_state_vt1718S;
 
        return 0;
@@ -4748,382 +2959,58 @@ static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        int index = 0;
 
-       index = snd_hda_codec_read(codec, 0x26, 0,
-                                              AC_VERB_GET_CONNECT_SEL, 0);
-       if (index != -1)
-               *ucontrol->value.integer.value = index;
-
-       return 0;
-}
-
-static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
-                          struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       struct via_spec *spec = codec->spec;
-       int index = *ucontrol->value.integer.value;
-
-       snd_hda_codec_write(codec, 0x26, 0,
-                                              AC_VERB_SET_CONNECT_SEL, index);
-       spec->dmic_enabled = index;
-       set_widgets_power_state(codec);
-       return 1;
-}
-
-/* capture mixer elements */
-static const struct snd_kcontrol_new vt1716S_capture_mixer[] = {
-       HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0,
-                        HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Input Source",
-               .count = 1,
-               .info = via_mux_enum_info,
-               .get = via_mux_enum_get,
-               .put = via_mux_enum_put,
-       },
-       { } /* end */
-};
-
-static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
-       HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
-       {
-        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-        .name = "Digital Mic Capture Switch",
-        .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
-        .count = 1,
-        .info = vt1716s_dmic_info,
-        .get = vt1716s_dmic_get,
-        .put = vt1716s_dmic_put,
-        },
-       {}                      /* end */
-};
-
-
-/* mono-out mixer elements */
-static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
-       HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
-       { } /* end */
-};
-
-static const struct hda_verb vt1716S_volume_init_verbs[] = {
-       /*
-        * Unmute ADC0-1 and set the default input to mic-in
-        */
-       {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-
-       /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
-        * mixer widget
-        */
-       /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
-       /* MUX Indices: Stereo Mixer = 5 */
-       {0x17, AC_VERB_SET_CONNECT_SEL, 0x5},
-
-       /* Setup default input of PW4 to MW0 */
-       {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
-
-       /* Setup default input of SW1 as MW0 */
-       {0x18, AC_VERB_SET_CONNECT_SEL, 0x1},
-
-       /* Setup default input of SW4 as AOW0 */
-       {0x28, AC_VERB_SET_CONNECT_SEL, 0x1},
-
-       /* PW9 PW10 Output enable */
-       {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
-       {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
-
-       /* Unmute SW1, PW12 */
-       {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       /* PW12 Output enable */
-       {0x2a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
-       /* Enable Boost Volume backdoor */
-       {0x1, 0xf8a, 0x80},
-       /* don't bybass mixer */
-       {0x1, 0xf88, 0xc0},
-       /* Enable mono output */
-       {0x1, 0xf90, 0x08},
-       { }
-};
-
-
-static const struct hda_verb vt1716S_uniwill_init_verbs[] = {
-       {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
-        AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
-       {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE,
-        AC_USRSP_EN | VIA_MONO_EVENT | VIA_JACK_EVENT},
-       {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       { }
-};
-
-static const struct hda_pcm_stream vt1716S_pcm_analog_playback = {
-       .substreams = 2,
-       .channels_min = 2,
-       .channels_max = 6,
-       .nid = 0x10, /* NID to query formats and rates */
-       .ops = {
-               .open = via_playback_pcm_open,
-               .prepare = via_playback_multi_pcm_prepare,
-               .cleanup = via_playback_multi_pcm_cleanup,
-               .close = via_pcm_open_close,
-       },
-};
-
-static const struct hda_pcm_stream vt1716S_pcm_analog_capture = {
-       .substreams = 2,
-       .channels_min = 2,
-       .channels_max = 2,
-       .nid = 0x13, /* NID to query formats and rates */
-       .ops = {
-               .open = via_pcm_open_close,
-               .prepare = via_capture_pcm_prepare,
-               .cleanup = via_capture_pcm_cleanup,
-               .close = via_pcm_open_close,
-       },
-};
-
-static const struct hda_pcm_stream vt1716S_pcm_digital_playback = {
-       .substreams = 2,
-       .channels_min = 2,
-       .channels_max = 2,
-       /* NID is set in via_build_pcms */
-       .ops = {
-               .open = via_dig_playback_pcm_open,
-               .close = via_dig_playback_pcm_close,
-               .prepare = via_dig_playback_pcm_prepare,
-               .cleanup = via_dig_playback_pcm_cleanup
-       },
-};
-
-/* fill in the dac_nids table from the parsed pin configuration */
-static int vt1716S_auto_fill_dac_nids(struct via_spec *spec,
-                                     const struct auto_pin_cfg *cfg)
-{      int i;
-       hda_nid_t nid;
-
-       spec->multiout.num_dacs = cfg->line_outs;
-
-       spec->multiout.dac_nids = spec->private_dac_nids;
-
-       for (i = 0; i < 3; i++) {
-               nid = cfg->line_out_pins[i];
-               if (nid) {
-                       /* config dac list */
-                       switch (i) {
-                       case AUTO_SEQ_FRONT:
-                               spec->private_dac_nids[i] = 0x10;
-                               break;
-                       case AUTO_SEQ_CENLFE:
-                               spec->private_dac_nids[i] = 0x25;
-                               break;
-                       case AUTO_SEQ_SURROUND:
-                               spec->private_dac_nids[i] = 0x11;
-                               break;
-                       }
-               }
-       }
-
-       return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int vt1716S_auto_create_multi_out_ctls(struct via_spec *spec,
-                                             const struct auto_pin_cfg *cfg)
-{
-       char name[32];
-       static const char * const chname[3] = {
-               "Front", "Surround", "C/LFE"
-       };
-       hda_nid_t nid_vols[] = {0x10, 0x11, 0x25};
-       hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x27};
-       hda_nid_t nid, nid_vol, nid_mute;
-       int i, err;
-
-       for (i = 0; i <= AUTO_SEQ_CENLFE; i++) {
-               nid = cfg->line_out_pins[i];
-
-               if (!nid)
-                       continue;
-
-               nid_vol = nid_vols[i];
-               nid_mute = nid_mutes[i];
-
-               if (i == AUTO_SEQ_CENLFE) {
-                       err = via_add_control(
-                               spec, VIA_CTL_WIDGET_VOL,
-                               "Center Playback Volume",
-                               HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-                       err = via_add_control(
-                               spec, VIA_CTL_WIDGET_VOL,
-                               "LFE Playback Volume",
-                               HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-                       err = via_add_control(
-                               spec, VIA_CTL_WIDGET_MUTE,
-                               "Center Playback Switch",
-                               HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0,
-                                                   HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-                       err = via_add_control(
-                               spec, VIA_CTL_WIDGET_MUTE,
-                               "LFE Playback Switch",
-                               HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0,
-                                                   HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-               } else if (i == AUTO_SEQ_FRONT) {
-
-                       err = via_add_control(
-                               spec, VIA_CTL_WIDGET_VOL,
-                               "Master Front Playback Volume",
-                               HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT));
-                       if (err < 0)
-                               return err;
-                       err = via_add_control(
-                               spec, VIA_CTL_WIDGET_MUTE,
-                               "Master Front Playback Switch",
-                               HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT));
-                       if (err < 0)
-                               return err;
-
-                       sprintf(name, "%s Playback Volume", chname[i]);
-                       err = via_add_control(
-                               spec, VIA_CTL_WIDGET_VOL, name,
-                               HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-                       sprintf(name, "%s Playback Switch", chname[i]);
-                       err = via_add_control(
-                               spec, VIA_CTL_WIDGET_MUTE, name,
-                               HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
-                                                   HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-               } else {
-                       sprintf(name, "%s Playback Volume", chname[i]);
-                       err = via_add_control(
-                               spec, VIA_CTL_WIDGET_VOL, name,
-                               HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-                       sprintf(name, "%s Playback Switch", chname[i]);
-                       err = via_add_control(
-                               spec, VIA_CTL_WIDGET_MUTE, name,
-                               HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
-                                                   HDA_OUTPUT));
-                       if (err < 0)
-                               return err;
-               }
-       }
-       return 0;
-}
-
-static int vt1716S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
-{
-       int err;
-
-       if (!pin)
-               return 0;
-
-       spec->multiout.hp_nid = 0x25; /* AOW3 */
-       spec->hp_independent_mode_index = 1;
-
-       err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
-                             "Headphone Playback Volume",
-                             HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
-       if (err < 0)
-               return err;
-
-       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
-                             "Headphone Playback Switch",
-                             HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
-       if (err < 0)
-               return err;
+       index = snd_hda_codec_read(codec, 0x26, 0,
+                                              AC_VERB_GET_CONNECT_SEL, 0);
+       if (index != -1)
+               *ucontrol->value.integer.value = index;
 
-       create_hp_imux(spec);
        return 0;
 }
 
-/* create playback/capture controls for input pins */
-static int vt1716S_auto_create_analog_input_ctls(struct hda_codec *codec,
-                                               const struct auto_pin_cfg *cfg)
-{
-       static const hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff };
-       return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
-                                               ARRAY_SIZE(pin_idxs));
-}
-
-static int vt1716S_parse_auto_config(struct hda_codec *codec)
+static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
+                          struct snd_ctl_elem_value *ucontrol)
 {
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct via_spec *spec = codec->spec;
-       int err;
-
-       err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
-       if (err < 0)
-               return err;
-       err = vt1716S_auto_fill_dac_nids(spec, &spec->autocfg);
-       if (err < 0)
-               return err;
-       if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
-               return 0; /* can't find valid BIOS pin config */
-
-       err = vt1716S_auto_create_multi_out_ctls(spec, &spec->autocfg);
-       if (err < 0)
-               return err;
-       err = vt1716S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
-       if (err < 0)
-               return err;
-       err = vt1716S_auto_create_analog_input_ctls(codec, &spec->autocfg);
-       if (err < 0)
-               return err;
-
-       spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
-       fill_dig_outs(codec);
-
-       if (spec->kctls.list)
-               spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
-       spec->input_mux = &spec->private_imux[0];
-
-       if (spec->hp_mux)
-               via_hp_build(codec);
-
-       via_smart51_build(spec);
+       int index = *ucontrol->value.integer.value;
 
+       snd_hda_codec_write(codec, 0x26, 0,
+                                              AC_VERB_SET_CONNECT_SEL, index);
+       spec->dmic_enabled = index;
+       set_widgets_power_state(codec);
        return 1;
 }
 
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static const struct hda_amp_list vt1716S_loopbacks[] = {
-       { 0x16, HDA_INPUT, 1 },
-       { 0x16, HDA_INPUT, 2 },
-       { 0x16, HDA_INPUT, 3 },
-       { 0x16, HDA_INPUT, 4 },
+static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
+       HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
+       {
+        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name = "Digital Mic Capture Switch",
+        .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
+        .count = 1,
+        .info = vt1716s_dmic_info,
+        .get = vt1716s_dmic_get,
+        .put = vt1716s_dmic_put,
+        },
+       {}                      /* end */
+};
+
+
+/* mono-out mixer elements */
+static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
+       HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
        { } /* end */
 };
-#endif
+
+static const struct hda_verb vt1716S_init_verbs[] = {
+       /* Enable Boost Volume backdoor */
+       {0x1, 0xf8a, 0x80},
+       /* don't bybass mixer */
+       {0x1, 0xf88, 0xc0},
+       /* Enable mono output */
+       {0x1, 0xf90, 0x08},
+       { }
+};
 
 static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
 {
@@ -5176,442 +3063,103 @@ static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
        /* Smart 5.1 PW1(1ah) */
        if (spec->smart51_enabled)
                set_pin_power_state(codec, 0x1a, &parm);
-       snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
-
-       /* Smart 5.1 PW5(1eh) */
-       if (spec->smart51_enabled)
-               set_pin_power_state(codec, 0x1e, &parm);
-       snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
-
-       /* Mono out */
-       /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
-       present = snd_hda_jack_detect(codec, 0x1c);
-
-       if (present)
-               mono_out = 0;
-       else {
-               present = snd_hda_jack_detect(codec, 0x1d);
-               if (!spec->hp_independent_mode && present)
-                       mono_out = 0;
-               else
-                       mono_out = 1;
-       }
-       parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
-       snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
-       snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
-       snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
-
-       /* PW 3/4 (1ch/1dh) */
-       parm = AC_PWRST_D3;
-       set_pin_power_state(codec, 0x1c, &parm);
-       set_pin_power_state(codec, 0x1d, &parm);
-       /* HP Independent Mode, power on AOW3 */
-       if (spec->hp_independent_mode)
-               snd_hda_codec_write(codec, 0x25, 0,
-                                   AC_VERB_SET_POWER_STATE, parm);
-
-       /* force to D0 for internal Speaker */
-       /* MW0 (16h), AOW0 (10h) */
-       snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
-                           imux_is_smixer ? AC_PWRST_D0 : parm);
-       snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
-                           mono_out ? AC_PWRST_D0 : parm);
-}
-
-static int patch_vt1716S(struct hda_codec *codec)
-{
-       struct via_spec *spec;
-       int err;
-
-       /* create a codec specific record */
-       spec = via_new_spec(codec);
-       if (spec == NULL)
-               return -ENOMEM;
-
-       /* automatic parse from the BIOS config */
-       err = vt1716S_parse_auto_config(codec);
-       if (err < 0) {
-               via_free(codec);
-               return err;
-       } else if (!err) {
-               printk(KERN_INFO "hda_codec: Cannot set up configuration "
-                      "from BIOS.  Using genenic mode...\n");
-       }
-
-       spec->init_verbs[spec->num_iverbs++]  = vt1716S_volume_init_verbs;
-       spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs;
-
-       spec->stream_name_analog = "VT1716S Analog";
-       spec->stream_analog_playback = &vt1716S_pcm_analog_playback;
-       spec->stream_analog_capture = &vt1716S_pcm_analog_capture;
-
-       spec->stream_name_digital = "VT1716S Digital";
-       spec->stream_digital_playback = &vt1716S_pcm_digital_playback;
-
-       if (!spec->adc_nids && spec->input_mux) {
-               spec->adc_nids = vt1716S_adc_nids;
-               spec->num_adc_nids = ARRAY_SIZE(vt1716S_adc_nids);
-               get_mux_nids(codec);
-               override_mic_boost(codec, 0x1a, 0, 3, 40);
-               override_mic_boost(codec, 0x1e, 0, 3, 40);
-               spec->mixers[spec->num_mixers] = vt1716S_capture_mixer;
-               spec->num_mixers++;
-       }
-
-       spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
-       spec->num_mixers++;
-
-       spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
-
-       codec->patch_ops = via_patch_ops;
-
-       codec->patch_ops.init = via_auto_init;
-       codec->patch_ops.unsol_event = via_unsol_event;
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-       spec->loopback.amplist = vt1716S_loopbacks;
-#endif
-
-       spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
-       return 0;
-}
-
-/* for vt2002P */
-
-/* capture mixer elements */
-static const struct snd_kcontrol_new vt2002P_capture_mixer[] = {
-       HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0,
-                        HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* The multiple "Capture Source" controls confuse alsamixer
-                * So call somewhat different..
-                */
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 2,
-               .info = via_mux_enum_info,
-               .get = via_mux_enum_get,
-               .put = via_mux_enum_put,
-       },
-       { } /* end */
-};
-
-static const struct hda_verb vt2002P_volume_init_verbs[] = {
-       /* Class-D speaker related verbs */
-       {0x1, 0xfe0, 0x4},
-       {0x1, 0xfe9, 0x80},
-       {0x1, 0xfe2, 0x22},
-       /*
-        * Unmute ADC0-1 and set the default input to mic-in
-        */
-       {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-
-       /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
-        * mixer widget
-        */
-       /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
-       {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-       {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-       {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
-       /* MUX Indices: Mic = 0 */
-       {0x1e, AC_VERB_SET_CONNECT_SEL, 0},
-       {0x1f, AC_VERB_SET_CONNECT_SEL, 0},
-
-       /* PW9 Output enable */
-       {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
-
-       /* Enable Boost Volume backdoor */
-       {0x1, 0xfb9, 0x24},
-
-       /* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
-       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
-       /* set MUX0/1/4/8 = 0 (AOW0) */
-       {0x34, AC_VERB_SET_CONNECT_SEL, 0},
-       {0x35, AC_VERB_SET_CONNECT_SEL, 0},
-       {0x37, AC_VERB_SET_CONNECT_SEL, 0},
-       {0x3b, AC_VERB_SET_CONNECT_SEL, 0},
-
-       /* set PW0 index=0 (MW0) */
-       {0x24, AC_VERB_SET_CONNECT_SEL, 0},
-
-       /* Enable AOW0 to MW9 */
-       {0x1, 0xfb8, 0x88},
-       { }
-};
-static const struct hda_verb vt1802_volume_init_verbs[] = {
-       /*
-        * Unmute ADC0-1 and set the default input to mic-in
-        */
-       {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-
-       /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
-        * mixer widget
-        */
-       /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
-       {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-       {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-       {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
-       /* MUX Indices: Mic = 0 */
-       {0x1e, AC_VERB_SET_CONNECT_SEL, 0},
-       {0x1f, AC_VERB_SET_CONNECT_SEL, 0},
-
-       /* PW9 Output enable */
-       {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
-
-       /* Enable Boost Volume backdoor */
-       {0x1, 0xfb9, 0x24},
-
-       /* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
-       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
-       /* set MUX0/1/4/8 = 0 (AOW0) */
-       {0x34, AC_VERB_SET_CONNECT_SEL, 0},
-       {0x35, AC_VERB_SET_CONNECT_SEL, 0},
-       {0x38, AC_VERB_SET_CONNECT_SEL, 0},
-       {0x3c, AC_VERB_SET_CONNECT_SEL, 0},
-
-       /* set PW0 index=0 (MW0) */
-       {0x24, AC_VERB_SET_CONNECT_SEL, 0},
-
-       /* Enable AOW0 to MW9 */
-       {0x1, 0xfb8, 0x88},
-       { }
-};
-
-
-static const struct hda_verb vt2002P_uniwill_init_verbs[] = {
-       {0x25, AC_VERB_SET_UNSOLICITED_ENABLE,
-        AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
-       {0x26, AC_VERB_SET_UNSOLICITED_ENABLE,
-        AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
-       {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       { }
-};
-static const struct hda_verb vt1802_uniwill_init_verbs[] = {
-       {0x25, AC_VERB_SET_UNSOLICITED_ENABLE,
-        AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
-       {0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
-        AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
-       {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       { }
-};
-
-static const struct hda_pcm_stream vt2002P_pcm_analog_playback = {
-       .substreams = 2,
-       .channels_min = 2,
-       .channels_max = 2,
-       .nid = 0x8, /* NID to query formats and rates */
-       .ops = {
-               .open = via_playback_pcm_open,
-               .prepare = via_playback_multi_pcm_prepare,
-               .cleanup = via_playback_multi_pcm_cleanup,
-               .close = via_pcm_open_close,
-       },
-};
-
-static const struct hda_pcm_stream vt2002P_pcm_analog_capture = {
-       .substreams = 2,
-       .channels_min = 2,
-       .channels_max = 2,
-       .nid = 0x10, /* NID to query formats and rates */
-       .ops = {
-               .open = via_pcm_open_close,
-               .prepare = via_capture_pcm_prepare,
-               .cleanup = via_capture_pcm_cleanup,
-               .close = via_pcm_open_close,
-       },
-};
-
-static const struct hda_pcm_stream vt2002P_pcm_digital_playback = {
-       .substreams = 1,
-       .channels_min = 2,
-       .channels_max = 2,
-       /* NID is set in via_build_pcms */
-       .ops = {
-               .open = via_dig_playback_pcm_open,
-               .close = via_dig_playback_pcm_close,
-               .prepare = via_dig_playback_pcm_prepare,
-               .cleanup = via_dig_playback_pcm_cleanup
-       },
-};
-
-/* fill in the dac_nids table from the parsed pin configuration */
-static int vt2002P_auto_fill_dac_nids(struct via_spec *spec,
-                                     const struct auto_pin_cfg *cfg)
-{
-       spec->multiout.num_dacs = 1;
-       spec->multiout.dac_nids = spec->private_dac_nids;
-       if (cfg->line_out_pins[0])
-               spec->private_dac_nids[0] = 0x8;
-       return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int vt2002P_auto_create_multi_out_ctls(struct via_spec *spec,
-                                            const struct auto_pin_cfg *cfg)
-{
-       int err;
-       hda_nid_t sw_nid;
-
-       if (!cfg->line_out_pins[0])
-               return -1;
-
-       if (spec->codec_type == VT1802)
-               sw_nid = 0x28;
-       else
-               sw_nid = 0x26;
-
-       /* Line-Out: PortE */
-       err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
-                             "Master Front Playback Volume",
-                             HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT));
-       if (err < 0)
-               return err;
-       err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE,
-                             "Master Front Playback Switch",
-                             HDA_COMPOSE_AMP_VAL(sw_nid, 3, 0, HDA_OUTPUT));
-       if (err < 0)
-               return err;
-
-       return 0;
-}
-
-static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
-{
-       int err;
-
-       if (!pin)
-               return 0;
-
-       spec->multiout.hp_nid = 0x9;
-       spec->hp_independent_mode_index = 1;
-
-       err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
-                             "Headphone Playback Volume",
-                             HDA_COMPOSE_AMP_VAL(
-                                     spec->multiout.hp_nid, 3, 0, HDA_OUTPUT));
-       if (err < 0)
-               return err;
-
-       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
-                             "Headphone Playback Switch",
-                             HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
-       if (err < 0)
-               return err;
+       snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
 
-       create_hp_imux(spec);
-       return 0;
-}
+       /* Smart 5.1 PW5(1eh) */
+       if (spec->smart51_enabled)
+               set_pin_power_state(codec, 0x1e, &parm);
+       snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
 
-/* create playback/capture controls for input pins */
-static int vt2002P_auto_create_analog_input_ctls(struct hda_codec *codec,
-                                               const struct auto_pin_cfg *cfg)
-{
-       struct via_spec *spec = codec->spec;
-       struct hda_input_mux *imux = &spec->private_imux[0];
-       static const hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0xff };
-       int err;
+       /* Mono out */
+       /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
+       present = snd_hda_jack_detect(codec, 0x1c);
 
-       err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
-                                              ARRAY_SIZE(pin_idxs));
-       if (err < 0)
-               return err;
-       /* build volume/mute control of loopback */
-       err = via_new_analog_input(spec, "Stereo Mixer", 0, 3, 0x21);
-       if (err < 0)
-               return err;
+       if (present)
+               mono_out = 0;
+       else {
+               present = snd_hda_jack_detect(codec, 0x1d);
+               if (!spec->hp_independent_mode && present)
+                       mono_out = 0;
+               else
+                       mono_out = 1;
+       }
+       parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
+       snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
+       snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
+       snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
 
-       /* for digital mic select */
-       snd_hda_add_imux_item(imux, "Digital Mic", 4, NULL);
+       /* PW 3/4 (1ch/1dh) */
+       parm = AC_PWRST_D3;
+       set_pin_power_state(codec, 0x1c, &parm);
+       set_pin_power_state(codec, 0x1d, &parm);
+       /* HP Independent Mode, power on AOW3 */
+       if (spec->hp_independent_mode)
+               snd_hda_codec_write(codec, 0x25, 0,
+                                   AC_VERB_SET_POWER_STATE, parm);
 
-       return 0;
+       /* force to D0 for internal Speaker */
+       /* MW0 (16h), AOW0 (10h) */
+       snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
+                           imux_is_smixer ? AC_PWRST_D0 : parm);
+       snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
+                           mono_out ? AC_PWRST_D0 : parm);
 }
 
-static int vt2002P_parse_auto_config(struct hda_codec *codec)
+static int patch_vt1716S(struct hda_codec *codec)
 {
-       struct via_spec *spec = codec->spec;
+       struct via_spec *spec;
        int err;
 
+       /* create a codec specific record */
+       spec = via_new_spec(codec);
+       if (spec == NULL)
+               return -ENOMEM;
 
-       err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
-       if (err < 0)
-               return err;
+       spec->aa_mix_nid = 0x16;
+       override_mic_boost(codec, 0x1a, 0, 3, 40);
+       override_mic_boost(codec, 0x1e, 0, 3, 40);
 
-       err = vt2002P_auto_fill_dac_nids(spec, &spec->autocfg);
-       if (err < 0)
+       /* automatic parse from the BIOS config */
+       err = via_parse_auto_config(codec);
+       if (err < 0) {
+               via_free(codec);
                return err;
+       }
 
-       if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
-               return 0; /* can't find valid BIOS pin config */
-
-       err = vt2002P_auto_create_multi_out_ctls(spec, &spec->autocfg);
-       if (err < 0)
-               return err;
-       err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
-       if (err < 0)
-               return err;
-       err = vt2002P_auto_create_analog_input_ctls(codec, &spec->autocfg);
-       if (err < 0)
-               return err;
+       spec->init_verbs[spec->num_iverbs++]  = vt1716S_init_verbs;
 
-       spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+       spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
+       spec->num_mixers++;
 
-       fill_dig_outs(codec);
+       spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
 
-       if (spec->kctls.list)
-               spec->mixers[spec->num_mixers++] = spec->kctls.list;
+       codec->patch_ops = via_patch_ops;
 
-       spec->input_mux = &spec->private_imux[0];
+       spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
+       return 0;
+}
 
-       if (spec->hp_mux)
-               via_hp_build(codec);
+/* for vt2002P */
 
-       return 1;
-}
+static const struct hda_verb vt2002P_init_verbs[] = {
+       /* Class-D speaker related verbs */
+       {0x1, 0xfe0, 0x4},
+       {0x1, 0xfe9, 0x80},
+       {0x1, 0xfe2, 0x22},
+       /* Enable Boost Volume backdoor */
+       {0x1, 0xfb9, 0x24},
+       /* Enable AOW0 to MW9 */
+       {0x1, 0xfb8, 0x88},
+       { }
+};
 
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static const struct hda_amp_list vt2002P_loopbacks[] = {
-       { 0x21, HDA_INPUT, 0 },
-       { 0x21, HDA_INPUT, 1 },
-       { 0x21, HDA_INPUT, 2 },
-       { } /* end */
+static const struct hda_verb vt1802_init_verbs[] = {
+       /* Enable Boost Volume backdoor */
+       {0x1, 0xfb9, 0x24},
+       /* Enable AOW0 to MW9 */
+       {0x1, 0xfb8, 0x88},
+       { }
 };
-#endif
 
 static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
 {
@@ -5735,334 +3283,38 @@ static int patch_vt2002P(struct hda_codec *codec)
        if (spec == NULL)
                return -ENOMEM;
 
+       spec->aa_mix_nid = 0x21;
+       override_mic_boost(codec, 0x2b, 0, 3, 40);
+       override_mic_boost(codec, 0x29, 0, 3, 40);
+
        /* automatic parse from the BIOS config */
-       err = vt2002P_parse_auto_config(codec);
+       err = via_parse_auto_config(codec);
        if (err < 0) {
                via_free(codec);
                return err;
-       } else if (!err) {
-               printk(KERN_INFO "hda_codec: Cannot set up configuration "
-                      "from BIOS.  Using genenic mode...\n");
        }
 
        if (spec->codec_type == VT1802)
-               spec->init_verbs[spec->num_iverbs++]  =
-                       vt1802_volume_init_verbs;
-       else
-               spec->init_verbs[spec->num_iverbs++]  =
-                       vt2002P_volume_init_verbs;
-
-       if (spec->codec_type == VT1802)
-               spec->init_verbs[spec->num_iverbs++] =
-                       vt1802_uniwill_init_verbs;
+               spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
        else
-               spec->init_verbs[spec->num_iverbs++] =
-                       vt2002P_uniwill_init_verbs;
-
-       if (spec->codec_type == VT1802)
-               spec->stream_name_analog = "VT1802 Analog";
-       else
-               spec->stream_name_analog = "VT2002P Analog";
-       spec->stream_analog_playback = &vt2002P_pcm_analog_playback;
-       spec->stream_analog_capture = &vt2002P_pcm_analog_capture;
-
-       if (spec->codec_type == VT1802)
-               spec->stream_name_digital = "VT1802 Digital";
-       else
-               spec->stream_name_digital = "VT2002P Digital";
-       spec->stream_digital_playback = &vt2002P_pcm_digital_playback;
-
-       if (!spec->adc_nids && spec->input_mux) {
-               spec->adc_nids = vt2002P_adc_nids;
-               spec->num_adc_nids = ARRAY_SIZE(vt2002P_adc_nids);
-               get_mux_nids(codec);
-               override_mic_boost(codec, 0x2b, 0, 3, 40);
-               override_mic_boost(codec, 0x29, 0, 3, 40);
-               spec->mixers[spec->num_mixers] = vt2002P_capture_mixer;
-               spec->num_mixers++;
-       }
+               spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
 
        codec->patch_ops = via_patch_ops;
 
-       codec->patch_ops.init = via_auto_init;
-       codec->patch_ops.unsol_event = via_unsol_event;
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-       spec->loopback.amplist = vt2002P_loopbacks;
-#endif
-
        spec->set_widgets_power_state =  set_widgets_power_state_vt2002P;
        return 0;
 }
 
 /* for vt1812 */
 
-/* capture mixer elements */
-static const struct snd_kcontrol_new vt1812_capture_mixer[] = {
-       HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Front Mic Boost Capture Volume", 0x29, 0x0,
-                      HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* The multiple "Capture Source" controls confuse alsamixer
-                * So call somewhat different..
-                */
-               .name = "Input Source",
-               .count = 2,
-               .info = via_mux_enum_info,
-               .get = via_mux_enum_get,
-               .put = via_mux_enum_put,
-       },
-       { } /* end */
-};
-
-static const struct hda_verb vt1812_volume_init_verbs[] = {
-       /*
-        * Unmute ADC0-1 and set the default input to mic-in
-        */
-       {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-
-       /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
-        * mixer widget
-        */
-       /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
-       {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-       {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-       {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
-       /* MUX Indices: Mic = 0 */
-       {0x1e, AC_VERB_SET_CONNECT_SEL, 0},
-       {0x1f, AC_VERB_SET_CONNECT_SEL, 0},
-
-       /* PW9 Output enable */
-       {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
-
+static const struct hda_verb vt1812_init_verbs[] = {
        /* Enable Boost Volume backdoor */
        {0x1, 0xfb9, 0x24},
-
-       /* MW0/1/4/13/15: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
-       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
-       /* set MUX0/1/4/13/15 = 0 (AOW0) */
-       {0x34, AC_VERB_SET_CONNECT_SEL, 0},
-       {0x35, AC_VERB_SET_CONNECT_SEL, 0},
-       {0x38, AC_VERB_SET_CONNECT_SEL, 0},
-       {0x3c, AC_VERB_SET_CONNECT_SEL, 0},
-       {0x3d, AC_VERB_SET_CONNECT_SEL, 0},
-
        /* Enable AOW0 to MW9 */
        {0x1, 0xfb8, 0xa8},
        { }
 };
 
-
-static const struct hda_verb vt1812_uniwill_init_verbs[] = {
-       {0x33, AC_VERB_SET_UNSOLICITED_ENABLE,
-        AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
-       {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT },
-       {0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
-        AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
-       {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
-       { }
-};
-
-static const struct hda_pcm_stream vt1812_pcm_analog_playback = {
-       .substreams = 2,
-       .channels_min = 2,
-       .channels_max = 2,
-       .nid = 0x8, /* NID to query formats and rates */
-       .ops = {
-               .open = via_playback_pcm_open,
-               .prepare = via_playback_multi_pcm_prepare,
-               .cleanup = via_playback_multi_pcm_cleanup,
-               .close = via_pcm_open_close,
-       },
-};
-
-static const struct hda_pcm_stream vt1812_pcm_analog_capture = {
-       .substreams = 2,
-       .channels_min = 2,
-       .channels_max = 2,
-       .nid = 0x10, /* NID to query formats and rates */
-       .ops = {
-               .open = via_pcm_open_close,
-               .prepare = via_capture_pcm_prepare,
-               .cleanup = via_capture_pcm_cleanup,
-               .close = via_pcm_open_close,
-       },
-};
-
-static const struct hda_pcm_stream vt1812_pcm_digital_playback = {
-       .substreams = 1,
-       .channels_min = 2,
-       .channels_max = 2,
-       /* NID is set in via_build_pcms */
-       .ops = {
-               .open = via_dig_playback_pcm_open,
-               .close = via_dig_playback_pcm_close,
-               .prepare = via_dig_playback_pcm_prepare,
-               .cleanup = via_dig_playback_pcm_cleanup
-       },
-};
-/* fill in the dac_nids table from the parsed pin configuration */
-static int vt1812_auto_fill_dac_nids(struct via_spec *spec,
-                                    const struct auto_pin_cfg *cfg)
-{
-       spec->multiout.num_dacs = 1;
-       spec->multiout.dac_nids = spec->private_dac_nids;
-       if (cfg->line_out_pins[0])
-               spec->private_dac_nids[0] = 0x8;
-       return 0;
-}
-
-
-/* add playback controls from the parsed DAC table */
-static int vt1812_auto_create_multi_out_ctls(struct via_spec *spec,
-                                            const struct auto_pin_cfg *cfg)
-{
-       int err;
-
-       if (!cfg->line_out_pins[0])
-               return -1;
-
-       /* Line-Out: PortE */
-       err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
-                             "Front Playback Volume",
-                             HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT));
-       if (err < 0)
-               return err;
-       err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE,
-                             "Front Playback Switch",
-                             HDA_COMPOSE_AMP_VAL(0x28, 3, 0, HDA_OUTPUT));
-       if (err < 0)
-               return err;
-
-       return 0;
-}
-
-static int vt1812_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
-{
-       int err;
-
-       if (!pin)
-               return 0;
-
-       spec->multiout.hp_nid = 0x9;
-       spec->hp_independent_mode_index = 1;
-
-
-       err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
-                             "Headphone Playback Volume",
-                             HDA_COMPOSE_AMP_VAL(
-                                     spec->multiout.hp_nid, 3, 0, HDA_OUTPUT));
-       if (err < 0)
-               return err;
-
-       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
-                             "Headphone Playback Switch",
-                             HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
-       if (err < 0)
-               return err;
-
-       create_hp_imux(spec);
-       return 0;
-}
-
-/* create playback/capture controls for input pins */
-static int vt1812_auto_create_analog_input_ctls(struct hda_codec *codec,
-                                               const struct auto_pin_cfg *cfg)
-{
-       struct via_spec *spec = codec->spec;
-       struct hda_input_mux *imux = &spec->private_imux[0];
-       static const hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0, 0, 0xff };
-       int err;
-
-       err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
-                                              ARRAY_SIZE(pin_idxs));
-       if (err < 0)
-               return err;
-
-       /* build volume/mute control of loopback */
-       err = via_new_analog_input(spec, "Stereo Mixer", 0, 5, 0x21);
-       if (err < 0)
-               return err;
-
-       /* for digital mic select */
-       snd_hda_add_imux_item(imux, "Digital Mic", 6, NULL);
-
-       return 0;
-}
-
-static int vt1812_parse_auto_config(struct hda_codec *codec)
-{
-       struct via_spec *spec = codec->spec;
-       int err;
-
-
-       err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
-       if (err < 0)
-               return err;
-       fill_dig_outs(codec);
-       err = vt1812_auto_fill_dac_nids(spec, &spec->autocfg);
-       if (err < 0)
-               return err;
-
-       if (!spec->autocfg.line_outs && !spec->autocfg.hp_outs)
-               return 0; /* can't find valid BIOS pin config */
-
-       err = vt1812_auto_create_multi_out_ctls(spec, &spec->autocfg);
-       if (err < 0)
-               return err;
-       err = vt1812_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
-       if (err < 0)
-               return err;
-       err = vt1812_auto_create_analog_input_ctls(codec, &spec->autocfg);
-       if (err < 0)
-               return err;
-
-       spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
-       fill_dig_outs(codec);
-
-       if (spec->kctls.list)
-               spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
-       spec->input_mux = &spec->private_imux[0];
-
-       if (spec->hp_mux)
-               via_hp_build(codec);
-
-       return 1;
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static const struct hda_amp_list vt1812_loopbacks[] = {
-       { 0x21, HDA_INPUT, 0 },
-       { 0x21, HDA_INPUT, 1 },
-       { 0x21, HDA_INPUT, 2 },
-       { } /* end */
-};
-#endif
-
 static void set_widgets_power_state_vt1812(struct hda_codec *codec)
 {
        struct via_spec *spec = codec->spec;
@@ -6166,47 +3418,21 @@ static int patch_vt1812(struct hda_codec *codec)
        if (spec == NULL)
                return -ENOMEM;
 
+       spec->aa_mix_nid = 0x21;
+       override_mic_boost(codec, 0x2b, 0, 3, 40);
+       override_mic_boost(codec, 0x29, 0, 3, 40);
+
        /* automatic parse from the BIOS config */
-       err = vt1812_parse_auto_config(codec);
+       err = via_parse_auto_config(codec);
        if (err < 0) {
                via_free(codec);
                return err;
-       } else if (!err) {
-               printk(KERN_INFO "hda_codec: Cannot set up configuration "
-                      "from BIOS.  Using genenic mode...\n");
        }
 
-
-       spec->init_verbs[spec->num_iverbs++]  = vt1812_volume_init_verbs;
-       spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs;
-
-       spec->stream_name_analog = "VT1812 Analog";
-       spec->stream_analog_playback = &vt1812_pcm_analog_playback;
-       spec->stream_analog_capture = &vt1812_pcm_analog_capture;
-
-       spec->stream_name_digital = "VT1812 Digital";
-       spec->stream_digital_playback = &vt1812_pcm_digital_playback;
-
-
-       if (!spec->adc_nids && spec->input_mux) {
-               spec->adc_nids = vt1812_adc_nids;
-               spec->num_adc_nids = ARRAY_SIZE(vt1812_adc_nids);
-               get_mux_nids(codec);
-               override_mic_boost(codec, 0x2b, 0, 3, 40);
-               override_mic_boost(codec, 0x29, 0, 3, 40);
-               spec->mixers[spec->num_mixers] = vt1812_capture_mixer;
-               spec->num_mixers++;
-       }
+       spec->init_verbs[spec->num_iverbs++]  = vt1812_init_verbs;
 
        codec->patch_ops = via_patch_ops;
 
-       codec->patch_ops.init = via_auto_init;
-       codec->patch_ops.unsol_event = via_unsol_event;
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-       spec->loopback.amplist = vt1812_loopbacks;
-#endif
-
        spec->set_widgets_power_state =  set_widgets_power_state_vt1812;
        return 0;
 }
@@ -6220,37 +3446,37 @@ static const struct hda_codec_preset snd_hda_preset_via[] = {
        { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
        { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
        { .id = 0x1106e710, .name = "VT1709 10-Ch",
-         .patch = patch_vt1709_10ch},
+         .patch = patch_vt1709},
        { .id = 0x1106e711, .name = "VT1709 10-Ch",
-         .patch = patch_vt1709_10ch},
+         .patch = patch_vt1709},
        { .id = 0x1106e712, .name = "VT1709 10-Ch",
-         .patch = patch_vt1709_10ch},
+         .patch = patch_vt1709},
        { .id = 0x1106e713, .name = "VT1709 10-Ch",
-         .patch = patch_vt1709_10ch},
+         .patch = patch_vt1709},
        { .id = 0x1106e714, .name = "VT1709 6-Ch",
-         .patch = patch_vt1709_6ch},
+         .patch = patch_vt1709},
        { .id = 0x1106e715, .name = "VT1709 6-Ch",
-         .patch = patch_vt1709_6ch},
+         .patch = patch_vt1709},
        { .id = 0x1106e716, .name = "VT1709 6-Ch",
-         .patch = patch_vt1709_6ch},
+         .patch = patch_vt1709},
        { .id = 0x1106e717, .name = "VT1709 6-Ch",
-         .patch = patch_vt1709_6ch},
+         .patch = patch_vt1709},
        { .id = 0x1106e720, .name = "VT1708B 8-Ch",
-         .patch = patch_vt1708B_8ch},
+         .patch = patch_vt1708B},
        { .id = 0x1106e721, .name = "VT1708B 8-Ch",
-         .patch = patch_vt1708B_8ch},
+         .patch = patch_vt1708B},
        { .id = 0x1106e722, .name = "VT1708B 8-Ch",
-         .patch = patch_vt1708B_8ch},
+         .patch = patch_vt1708B},
        { .id = 0x1106e723, .name = "VT1708B 8-Ch",
-         .patch = patch_vt1708B_8ch},
+         .patch = patch_vt1708B},
        { .id = 0x1106e724, .name = "VT1708B 4-Ch",
-         .patch = patch_vt1708B_4ch},
+         .patch = patch_vt1708B},
        { .id = 0x1106e725, .name = "VT1708B 4-Ch",
-         .patch = patch_vt1708B_4ch},
+         .patch = patch_vt1708B},
        { .id = 0x1106e726, .name = "VT1708B 4-Ch",
-         .patch = patch_vt1708B_4ch},
+         .patch = patch_vt1708B},
        { .id = 0x1106e727, .name = "VT1708B 4-Ch",
-         .patch = patch_vt1708B_4ch},
+         .patch = patch_vt1708B},
        { .id = 0x11060397, .name = "VT1708S",
          .patch = patch_vt1708S},
        { .id = 0x11061397, .name = "VT1708S",