ALSA: hda - VIA: Add missing support for VT1718S in A-A path
[linux-flexiantxendom0-natty.git] / sound / pci / hda / patch_via.c
index a294060..23e58f0 100644 (file)
@@ -41,6 +41,7 @@
 /* 2009-04-14  Lydai Wang  Add support for VT1828S and VT2020               */
 /* 2009-07-08  Lydia Wang  Add support for VT2002P                          */
 /* 2009-07-21  Lydia Wang  Add support for VT1812                           */
+/* 2009-09-19  Lydia Wang  Add support for VT1818S                          */
 /*                                                                          */
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
@@ -53,6 +54,8 @@
 #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)
@@ -156,6 +159,19 @@ struct via_spec {
 #endif
 };
 
+static struct via_spec * via_new_spec(struct hda_codec *codec)
+{
+       struct via_spec *spec;
+
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (spec == NULL)
+               return NULL;
+
+       codec->spec = spec;
+       spec->codec = codec;
+       return spec;
+}
+
 static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
 {
        u32 vendor_id = codec->vendor_id;
@@ -195,6 +211,8 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
                codec_type = VT2002P;
        else if (dev_id == 0x0448)
                codec_type = VT1812;
+       else if (dev_id == 0x0440)
+               codec_type = VT1708S;
        else
                codec_type = UNKNOWN;
        return codec_type;
@@ -245,8 +263,7 @@ static void vt1708_stop_hp_work(struct via_spec *spec)
                return;
        snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
                            !spec->vt1708_jack_detectect);
-       cancel_delayed_work(&spec->vt1708_hp_work);
-       flush_scheduled_work();
+       cancel_delayed_work_sync(&spec->vt1708_hp_work);
 }
 
 
@@ -426,8 +443,8 @@ static hda_nid_t vt1812_adc_nids[2] = {
 
 
 /* add dynamic controls */
-static int via_add_control(struct via_spec *spec, int type, const char *name,
-                          unsigned long val)
+static int __via_add_control(struct via_spec *spec, int type, const char *name,
+                            int idx, unsigned long val)
 {
        struct snd_kcontrol_new *knew;
 
@@ -439,10 +456,31 @@ static int via_add_control(struct via_spec *spec, int type, const char *name,
        knew->name = kstrdup(name, GFP_KERNEL);
        if (!knew->name)
                return -ENOMEM;
+       if (get_amp_nid_(val))
+               knew->subdevice = HDA_SUBDEV_AMP_FLAG;
        knew->private_value = val;
        return 0;
 }
 
+#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,
+                                               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;
+}
+
 static void via_free_kctls(struct hda_codec *codec)
 {
        struct via_spec *spec = codec->spec;
@@ -458,18 +496,18 @@ static void via_free_kctls(struct hda_codec *codec)
 
 /* create input playback/capture controls for the given pin */
 static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
-                               int idx, int mix_nid)
+                               int type_idx, int idx, int mix_nid)
 {
        char name[32];
        int err;
 
        sprintf(name, "%s Playback Volume", ctlname);
-       err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
+       err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
                              HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
        if (err < 0)
                return err;
        sprintf(name, "%s Playback Switch", ctlname);
-       err = via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name,
+       err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
                              HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
        if (err < 0)
                return err;
@@ -516,24 +554,28 @@ static void via_auto_init_hp_out(struct hda_codec *codec)
        }
 }
 
+static int is_smart51_pins(struct via_spec *spec, 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;
+       unsigned int ctl;
        int i;
 
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               hda_nid_t nid = spec->autocfg.input_pins[i];
-
+       for (i = 0; i < cfg->num_inputs; i++) {
+               hda_nid_t nid = cfg->inputs[i].pin;
+               if (spec->smart51_enabled && is_smart51_pins(spec, nid))
+                       ctl = PIN_OUT;
+               else if (cfg->inputs[i].type == AUTO_PIN_MIC)
+                       ctl = PIN_VREF50;
+               else
+                       ctl = PIN_IN;
                snd_hda_codec_write(codec, nid, 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                   (i <= AUTO_PIN_FRONT_MIC ?
-                                    PIN_VREF50 : PIN_IN));
-
+                                   AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
        }
 }
 
-static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin);
-
 static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
                                unsigned int *affected_parm)
 {
@@ -542,8 +584,7 @@ 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_codec_read(codec, nid, 0,
-                                             AC_VERB_GET_PIN_SENSE, 0) >> 31;
+       unsigned present = snd_hda_jack_detect(codec, nid);
        struct via_spec *spec = codec->spec;
        if ((spec->smart51_enabled && is_smart51_pins(spec, nid))
            || ((no_presence || present)
@@ -623,6 +664,8 @@ static void set_jack_power_state(struct hda_codec *codec)
                /* PW0 (19h), SW1 (18h), AOW1 (11h) */
                parm = AC_PWRST_D3;
                set_pin_power_state(codec, 0x19, &parm);
+               if (spec->smart51_enabled)
+                       parm = AC_PWRST_D0;
                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,
@@ -632,6 +675,8 @@ static void set_jack_power_state(struct hda_codec *codec)
                if (is_8ch) {
                        parm = AC_PWRST_D3;
                        set_pin_power_state(codec, 0x22, &parm);
+                       if (spec->smart51_enabled)
+                               parm = AC_PWRST_D0;
                        snd_hda_codec_write(codec, 0x26, 0,
                                            AC_VERB_SET_POWER_STATE, parm);
                        snd_hda_codec_write(codec, 0x24, 0,
@@ -781,14 +826,11 @@ static void set_jack_power_state(struct hda_codec *codec)
 
                /* Mono out */
                /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
-               present = snd_hda_codec_read(
-                       codec, 0x1c, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+               present = snd_hda_jack_detect(codec, 0x1c);
                if (present)
                        mono_out = 0;
                else {
-                       present = snd_hda_codec_read(
-                               codec, 0x1d, 0, AC_VERB_GET_PIN_SENSE, 0)
-                               & 0x80000000;
+                       present = snd_hda_jack_detect(codec, 0x1d);
                        if (!spec->hp_independent_mode && present)
                                mono_out = 0;
                        else
@@ -867,8 +909,7 @@ static void set_jack_power_state(struct hda_codec *codec)
 
                /* Class-D */
                /* PW0 (24h), MW0(18h), MUX0(34h) */
-               present = snd_hda_codec_read(
-                       codec, 0x25, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+               present = snd_hda_jack_detect(codec, 0x25);
                parm = AC_PWRST_D3;
                set_pin_power_state(codec, 0x24, &parm);
                if (present) {
@@ -889,8 +930,7 @@ static void set_jack_power_state(struct hda_codec *codec)
 
                /* Mono Out */
                /* PW15 (31h), MW8(17h), MUX8(3bh) */
-               present = snd_hda_codec_read(
-                       codec, 0x26, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+               present = snd_hda_jack_detect(codec, 0x26);
                parm = AC_PWRST_D3;
                set_pin_power_state(codec, 0x31, &parm);
                if (present) {
@@ -968,8 +1008,7 @@ static void set_jack_power_state(struct hda_codec *codec)
 
                /* Internal Speaker */
                /* PW0 (24h), MW0(14h), MUX0(34h) */
-               present = snd_hda_codec_read(
-                       codec, 0x25, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+               present = snd_hda_jack_detect(codec, 0x25);
                parm = AC_PWRST_D3;
                set_pin_power_state(codec, 0x24, &parm);
                if (present) {
@@ -989,8 +1028,7 @@ static void set_jack_power_state(struct hda_codec *codec)
                }
                /* Mono Out */
                /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
-               present = snd_hda_codec_read(
-                       codec, 0x28, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+               present = snd_hda_jack_detect(codec, 0x28);
                parm = AC_PWRST_D3;
                set_pin_power_state(codec, 0x31, &parm);
                if (present) {
@@ -1063,6 +1101,7 @@ static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
        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;
 
        if (!spec->mux_nids[adc_idx])
                return -EINVAL;
@@ -1071,12 +1110,14 @@ static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
                               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);
-       /* update jack power state */
-       set_jack_power_state(codec);
 
-       return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
+       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 */
+       set_jack_power_state(codec);
+
+       return ret;
 }
 
 static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
@@ -1091,24 +1132,9 @@ static int via_independent_hp_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;
-       hda_nid_t nid;
+       hda_nid_t nid = kcontrol->private_value;
        unsigned int pinsel;
 
-       switch (spec->codec_type) {
-       case VT1718S:
-               nid = 0x34;
-               break;
-       case VT2002P:
-               nid = 0x35;
-               break;
-       case VT1812:
-               nid = 0x3d;
-               break;
-       default:
-               nid = spec->autocfg.hp_pins[0];
-               break;
-       }
        /* use !! to translate conn sel 2 for VT1718S */
        pinsel = !!snd_hda_codec_read(codec, nid, 0,
                                      AC_VERB_GET_CONNECT_SEL,
@@ -1130,29 +1156,24 @@ static void activate_ctl(struct hda_codec *codec, const char *name, int active)
        }
 }
 
+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;
+       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 = spec->hp_independent_mode
                ? AMP_OUT_MUTE : AMP_OUT_UNMUTE;
-       hda_nid_t sw3;
-
-       switch (spec->codec_type) {
-       case VT1708:
-               sw3 = 0x1b;
-               break;
-       case VT1709_10CH:
-               sw3 = 0x29;
-               break;
-       case VT1708B_8CH:
-       case VT1708S:
-               sw3 = 0x27;
-               break;
-       default:
-               sw3 = 0;
-               break;
-       }
+       hda_nid_t sw3 = side_mute_channel(spec);
 
        if (sw3)
                snd_hda_codec_write(codec, sw3, 0, AC_VERB_SET_AMP_GAIN_MUTE,
@@ -1165,30 +1186,21 @@ 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 = spec->autocfg.hp_pins[0];
+       hda_nid_t nid = kcontrol->private_value;
        unsigned int pinsel = ucontrol->value.enumerated.item[0];
        /* 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);
+       else
+               snd_hda_codec_write(codec, nid, 0,
+                                   AC_VERB_SET_CONNECT_SEL, pinsel);
 
-       switch (spec->codec_type) {
-       case VT1718S:
-               nid = 0x34;
-               pinsel = pinsel ? 2 : 0; /* indep HP use AOW4 (index 2) */
-               spec->multiout.num_dacs = 4;
-               break;
-       case VT2002P:
-               nid = 0x35;
-               break;
-       case VT1812:
-               nid = 0x3d;
-               break;
-       default:
-               nid = spec->autocfg.hp_pins[0];
-               break;
-       }
-       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,
@@ -1207,21 +1219,67 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
                activate_ctl(codec, "Headphone Playback Switch",
                             spec->hp_independent_mode);
        }
+       /* update jack power state */
+       set_jack_power_state(codec);
        return 0;
 }
 
-static struct snd_kcontrol_new via_hp_mixer[] = {
+static struct snd_kcontrol_new via_hp_mixer[2] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Independent HP",
-               .count = 1,
                .info = via_independent_hp_info,
                .get = via_independent_hp_get,
                .put = via_independent_hp_put,
        },
-       { } /* end */
+       {
+               .iface = NID_MAPPING,
+               .name = "Independent HP",
+       },
 };
 
+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:
+               nid = 0x35;
+               break;
+       case VT1812:
+               nid = 0x3d;
+               break;
+       default:
+               nid = spec->autocfg.hp_pins[0];
+               break;
+       }
+
+       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]);
+       if (knew == NULL)
+               return -ENOMEM;
+
+       knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
+       knew->private_value = nid;
+
+       knew = via_clone_control(spec, &via_hp_mixer[1]);
+       if (knew == NULL)
+               return -ENOMEM;
+       knew->subdevice = side_mute_channel(spec);
+
+       return 0;
+}
+
 static void notify_aa_path_ctls(struct hda_codec *codec)
 {
        int i;
@@ -1265,6 +1323,11 @@ static void mute_aa_path(struct hda_codec *codec, int mute)
                start_idx = 2;
                end_idx = 4;
                break;
+       case VT1718S:
+               nid_mixer = 0x21;
+               start_idx = 1;
+               end_idx = 3;
+               break;
        default:
                return;
        }
@@ -1277,15 +1340,14 @@ static void mute_aa_path(struct hda_codec *codec, int mute)
 }
 static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin)
 {
-       int res = 0;
-       int index;
-       for (index = AUTO_PIN_MIC; index < AUTO_PIN_FRONT_LINE; index++) {
-               if (pin == spec->autocfg.input_pins[index]) {
-                       res = 1;
-                       break;
-               }
+       const struct auto_pin_cfg *cfg = &spec->autocfg;
+       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 res;
+       return 0;
 }
 
 static int via_smart51_info(struct snd_kcontrol *kcontrol,
@@ -1303,25 +1365,21 @@ static int via_smart51_get(struct snd_kcontrol *kcontrol,
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct via_spec *spec = codec->spec;
-       int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
+       const struct auto_pin_cfg *cfg = &spec->autocfg;
        int on = 1;
        int i;
 
-       for (i = 0; i < ARRAY_SIZE(index); i++) {
-               hda_nid_t nid = spec->autocfg.input_pins[index[i]];
-               if (nid) {
-                       int ctl =
-                           snd_hda_codec_read(codec, nid, 0,
-                                              AC_VERB_GET_PIN_WIDGET_CONTROL,
-                                              0);
-                       if (i == AUTO_PIN_FRONT_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;
-               }
+       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;
        return 0;
@@ -1332,36 +1390,38 @@ 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 index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
        int i;
 
-       for (i = 0; i < ARRAY_SIZE(index); i++) {
-               hda_nid_t nid = spec->autocfg.input_pins[index[i]];
-               if (i == AUTO_PIN_FRONT_MIC
-                   && spec->hp_independent_mode
-                   && spec->codec_type != VT1718S)
+       for (i = 0; i < cfg->num_inputs; i++) {
+               hda_nid_t nid = cfg->inputs[i].pin;
+               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 */
-               if (nid) {
-                       unsigned int parm = snd_hda_codec_read(
-                               codec, nid, 0,
-                               AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
-                       parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
-                       parm |= out_in;
-                       snd_hda_codec_write(codec, nid, 0,
-                                           AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                           parm);
-                       if (out_in == AC_PINCTL_OUT_EN) {
-                               mute_aa_path(codec, 1);
-                               notify_aa_path_ctls(codec);
-                       }
-                       if (spec->codec_type == VT1718S)
-                               snd_hda_codec_amp_stereo(
+
+               parm = snd_hda_codec_read(codec, nid, 0,
+                                         AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+               parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
+               parm |= out_in;
+               snd_hda_codec_write(codec, nid, 0,
+                                   AC_VERB_SET_PIN_WIDGET_CONTROL,
+                                   parm);
+               if (out_in == AC_PINCTL_OUT_EN) {
+                       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 (i == AUTO_PIN_FRONT_MIC) {
+               if (cfg->inputs[i].type == AUTO_PIN_MIC) {
                        if (spec->codec_type == VT1708S
                            || spec->codec_type == VT1716S) {
                                /* input = index 1 (AOW3) */
@@ -1379,7 +1439,7 @@ static int via_smart51_put(struct snd_kcontrol *kcontrol,
        return 1;
 }
 
-static struct snd_kcontrol_new via_smart51_mixer[] = {
+static struct snd_kcontrol_new via_smart51_mixer[2] = {
        {
         .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
         .name = "Smart 5.1",
@@ -1388,9 +1448,37 @@ static struct snd_kcontrol_new via_smart51_mixer[] = {
         .get = via_smart51_get,
         .put = via_smart51_put,
         },
-       {}                      /* end */
+       {
+        .iface = NID_MAPPING,
+        .name = "Smart 5.1",
+       }
 };
 
+static int via_smart51_build(struct via_spec *spec)
+{
+       struct snd_kcontrol_new *knew;
+       const struct auto_pin_cfg *cfg = &spec->autocfg;
+       hda_nid_t nid;
+       int i;
+
+       knew = via_clone_control(spec, &via_smart51_mixer[0]);
+       if (knew == NULL)
+               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 struct snd_kcontrol_new vt1708_capture_mixer[] = {
        HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT),
@@ -1822,8 +1910,9 @@ static struct hda_pcm_stream vt1708_pcm_digital_capture = {
 static int via_build_controls(struct hda_codec *codec)
 {
        struct via_spec *spec = codec->spec;
-       int err;
-       int i;
+       struct snd_kcontrol *kctl;
+       struct snd_kcontrol_new *knew;
+       int err, i;
 
        for (i = 0; i < spec->num_mixers; i++) {
                err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
@@ -1848,6 +1937,27 @@ static int via_build_controls(struct hda_codec *codec)
                        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++) {
+               err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
+               if (err < 0)
+                       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_jack_power_state(codec);
        analog_low_current_mode(codec, 1);
@@ -1915,8 +2025,7 @@ static void via_hp_automute(struct hda_codec *codec)
        unsigned int present = 0;
        struct via_spec *spec = codec->spec;
 
-       present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
 
        if (!spec->hp_independent_mode) {
                struct snd_ctl_elem_id id;
@@ -1942,9 +2051,8 @@ static void via_mono_automute(struct hda_codec *codec)
        if (spec->codec_type != VT1716S)
                return;
 
-       lineout_present = snd_hda_codec_read(
-               codec, spec->autocfg.line_out_pins[0], 0,
-               AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       lineout_present = snd_hda_jack_detect(codec,
+                                             spec->autocfg.line_out_pins[0]);
 
        /* Mute Mono Out if Line Out is plugged */
        if (lineout_present) {
@@ -1953,9 +2061,7 @@ static void via_mono_automute(struct hda_codec *codec)
                return;
        }
 
-       hp_present = snd_hda_codec_read(
-               codec, spec->autocfg.hp_pins[0], 0,
-               AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
 
        if (!spec->hp_independent_mode)
                snd_hda_codec_amp_stereo(
@@ -2020,8 +2126,7 @@ static void via_speaker_automute(struct hda_codec *codec)
        if (spec->codec_type != VT2002P && spec->codec_type != VT1812)
                return;
 
-       hp_present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
 
        if (!spec->hp_independent_mode) {
                struct snd_ctl_elem_id id;
@@ -2040,18 +2145,19 @@ static void via_speaker_automute(struct hda_codec *codec)
 /* mute line-out and internal speaker if HP is plugged */
 static void via_hp_bind_automute(struct hda_codec *codec)
 {
-       unsigned int hp_present, present = 0;
+       /* 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_codec_read(codec, spec->autocfg.hp_pins[0], 0,
-                                       AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
 
-       present = snd_hda_codec_read(codec, spec->autocfg.line_out_pins[0], 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       present = snd_hda_jack_detect(codec, spec->autocfg.line_out_pins[0]);
 
        if (!spec->hp_independent_mode) {
                /* Mute Line-Outs */
@@ -2193,7 +2299,9 @@ static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec,
                                             const struct auto_pin_cfg *cfg)
 {
        char name[32];
-       static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
+       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;
 
@@ -2282,16 +2390,11 @@ static void create_hp_imux(struct via_spec *spec)
 {
        int i;
        struct hda_input_mux *imux = &spec->private_imux[1];
-       static const char *texts[] = { "OFF", "ON", NULL};
+       static const char * const texts[] = { "OFF", "ON", NULL};
 
        /* for hp mode select */
-       i = 0;
-       while (texts[i] != NULL) {
-               imux->items[imux->num_items].label =  texts[i];
-               imux->items[imux->num_items].index = i;
-               imux->num_items++;
-               i++;
-       }
+       for (i = 0; texts[i]; i++)
+               snd_hda_add_imux_item(imux, texts[i], i, NULL);
 
        spec->hp_mux = &spec->private_imux[1];
 }
@@ -2323,51 +2426,53 @@ static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
 }
 
 /* create playback/capture controls for input pins */
-static int vt1708_auto_create_analog_input_ctls(struct via_spec *spec,
-                                               const struct auto_pin_cfg *cfg)
+static int vt_auto_create_analog_input_ctls(struct hda_codec *codec,
+                                           const struct auto_pin_cfg *cfg,
+                                           hda_nid_t cap_nid,
+                                           hda_nid_t pin_idxs[], int num_idxs)
 {
-       static char *labels[] = {
-               "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
-       };
+       struct via_spec *spec = codec->spec;
        struct hda_input_mux *imux = &spec->private_imux[0];
-       int i, err, idx = 0;
+       int i, err, idx, type, type_idx = 0;
 
        /* for internal loopback recording select */
-       imux->items[imux->num_items].label = "Stereo Mixer";
-       imux->items[imux->num_items].index = idx;
-       imux->num_items++;
-
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               if (!cfg->input_pins[i])
-                       continue;
-
-               switch (cfg->input_pins[i]) {
-               case 0x1d: /* Mic */
-                       idx = 2;
-                       break;
-
-               case 0x1e: /* Line In */
-                       idx = 3;
-                       break;
-
-               case 0x21: /* Front Mic */
-                       idx = 4;
-                       break;
-
-               case 0x24: /* CD */
-                       idx = 1;
+       for (idx = 0; idx < num_idxs; idx++) {
+               if (pin_idxs[idx] == 0xff) {
+                       snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL);
                        break;
                }
-               err = via_new_analog_input(spec, labels[i], idx, 0x17);
+       }
+
+       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);
+               err = via_new_analog_input(spec, label, type_idx, idx, cap_nid);
                if (err < 0)
                        return err;
-               imux->items[imux->num_items].label = labels[i];
-               imux->items[imux->num_items].index = idx;
-               imux->num_items++;
+               snd_hda_add_imux_item(imux, label, idx, NULL);
        }
        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)
+{
+       static 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));
+}
+
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 static struct hda_amp_list vt1708_loopbacks[] = {
        { 0x17, HDA_INPUT, 1 },
@@ -2464,7 +2569,7 @@ static int vt1708_parse_auto_config(struct hda_codec *codec)
        err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
        if (err < 0)
                return err;
-       err = vt1708_auto_create_analog_input_ctls(spec, &spec->autocfg);
+       err = vt1708_auto_create_analog_input_ctls(codec, &spec->autocfg);
        if (err < 0)
                return err;
        /* add jack detect on/off control */
@@ -2488,9 +2593,9 @@ static int vt1708_parse_auto_config(struct hda_codec *codec)
        spec->input_mux = &spec->private_imux[0];
 
        if (spec->hp_mux)
-               spec->mixers[spec->num_mixers++] = via_hp_mixer;
+               via_hp_build(codec);
 
-       spec->mixers[spec->num_mixers++] = via_smart51_mixer;
+       via_smart51_build(spec);
        return 1;
 }
 
@@ -2521,8 +2626,7 @@ static void vt1708_update_hp_jack_state(struct work_struct *work)
                return;
        /* if jack state toggled */
        if (spec->vt1708_hp_present
-           != (snd_hda_codec_read(spec->codec, spec->autocfg.hp_pins[0], 0,
-                                  AC_VERB_GET_PIN_SENSE, 0) >> 31)) {
+           != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
                spec->vt1708_hp_present ^= 1;
                via_hp_automute(spec->codec);
        }
@@ -2562,12 +2666,10 @@ static int patch_vt1708(struct hda_codec *codec)
        int err;
 
        /* create a codec specific record */
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       spec = via_new_spec(codec);
        if (spec == NULL)
                return -ENOMEM;
 
-       codec->spec = spec;
-
        /* automatic parse from the BIOS config */
        err = vt1708_parse_auto_config(codec);
        if (err < 0) {
@@ -2605,7 +2707,6 @@ static int patch_vt1708(struct hda_codec *codec)
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        spec->loopback.amplist = vt1708_loopbacks;
 #endif
-       spec->codec = codec;
        INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
        return 0;
 }
@@ -2809,7 +2910,9 @@ static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec,
                                             const struct auto_pin_cfg *cfg)
 {
        char name[32];
-       static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
+       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;
 
@@ -2935,49 +3038,12 @@ static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
 }
 
 /* create playback/capture controls for input pins */
-static int vt1709_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1709_auto_create_analog_input_ctls(struct hda_codec *codec,
                                                const struct auto_pin_cfg *cfg)
 {
-       static char *labels[] = {
-               "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
-       };
-       struct hda_input_mux *imux = &spec->private_imux[0];
-       int i, err, idx = 0;
-
-       /* for internal loopback recording select */
-       imux->items[imux->num_items].label = "Stereo Mixer";
-       imux->items[imux->num_items].index = idx;
-       imux->num_items++;
-
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               if (!cfg->input_pins[i])
-                       continue;
-
-               switch (cfg->input_pins[i]) {
-               case 0x1d: /* Mic */
-                       idx = 2;
-                       break;
-
-               case 0x1e: /* Line In */
-                       idx = 3;
-                       break;
-
-               case 0x21: /* Front Mic */
-                       idx = 4;
-                       break;
-
-               case 0x23: /* CD */
-                       idx = 1;
-                       break;
-               }
-               err = via_new_analog_input(spec, labels[i], idx, 0x18);
-               if (err < 0)
-                       return err;
-               imux->items[imux->num_items].label = labels[i];
-               imux->items[imux->num_items].index = idx;
-               imux->num_items++;
-       }
-       return 0;
+       static 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)
@@ -3000,7 +3066,7 @@ static int vt1709_parse_auto_config(struct hda_codec *codec)
        err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
        if (err < 0)
                return err;
-       err = vt1709_auto_create_analog_input_ctls(spec, &spec->autocfg);
+       err = vt1709_auto_create_analog_input_ctls(codec, &spec->autocfg);
        if (err < 0)
                return err;
 
@@ -3018,9 +3084,9 @@ static int vt1709_parse_auto_config(struct hda_codec *codec)
        spec->input_mux = &spec->private_imux[0];
 
        if (spec->hp_mux)
-               spec->mixers[spec->num_mixers++] = via_hp_mixer;
+               via_hp_build(codec);
 
-       spec->mixers[spec->num_mixers++] = via_smart51_mixer;
+       via_smart51_build(spec);
        return 1;
 }
 
@@ -3040,12 +3106,10 @@ static int patch_vt1709_10ch(struct hda_codec *codec)
        int err;
 
        /* create a codec specific record */
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       spec = via_new_spec(codec);
        if (spec == NULL)
                return -ENOMEM;
 
-       codec->spec = spec;
-
        err = vt1709_parse_auto_config(codec);
        if (err < 0) {
                via_free(codec);
@@ -3134,12 +3198,10 @@ static int patch_vt1709_6ch(struct hda_codec *codec)
        int err;
 
        /* create a codec specific record */
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       spec = via_new_spec(codec);
        if (spec == NULL)
                return -ENOMEM;
 
-       codec->spec = spec;
-
        err = vt1709_parse_auto_config(codec);
        if (err < 0) {
                via_free(codec);
@@ -3393,7 +3455,9 @@ static int vt1708B_auto_create_multi_out_ctls(struct via_spec *spec,
                                             const struct auto_pin_cfg *cfg)
 {
        char name[32];
-       static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
+       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;
@@ -3506,49 +3570,12 @@ static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
 }
 
 /* create playback/capture controls for input pins */
-static int vt1708B_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1708B_auto_create_analog_input_ctls(struct hda_codec *codec,
                                                const struct auto_pin_cfg *cfg)
 {
-       static char *labels[] = {
-               "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
-       };
-       struct hda_input_mux *imux = &spec->private_imux[0];
-       int i, err, idx = 0;
-
-       /* for internal loopback recording select */
-       imux->items[imux->num_items].label = "Stereo Mixer";
-       imux->items[imux->num_items].index = idx;
-       imux->num_items++;
-
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               if (!cfg->input_pins[i])
-                       continue;
-
-               switch (cfg->input_pins[i]) {
-               case 0x1a: /* Mic */
-                       idx = 2;
-                       break;
-
-               case 0x1b: /* Line In */
-                       idx = 3;
-                       break;
-
-               case 0x1e: /* Front Mic */
-                       idx = 4;
-                       break;
-
-               case 0x1f: /* CD */
-                       idx = 1;
-                       break;
-               }
-               err = via_new_analog_input(spec, labels[i], idx, 0x16);
-               if (err < 0)
-                       return err;
-               imux->items[imux->num_items].label = labels[i];
-               imux->items[imux->num_items].index = idx;
-               imux->num_items++;
-       }
-       return 0;
+       static 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)
@@ -3571,7 +3598,7 @@ static int vt1708B_parse_auto_config(struct hda_codec *codec)
        err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
        if (err < 0)
                return err;
-       err = vt1708B_auto_create_analog_input_ctls(spec, &spec->autocfg);
+       err = vt1708B_auto_create_analog_input_ctls(codec, &spec->autocfg);
        if (err < 0)
                return err;
 
@@ -3589,9 +3616,9 @@ static int vt1708B_parse_auto_config(struct hda_codec *codec)
        spec->input_mux = &spec->private_imux[0];
 
        if (spec->hp_mux)
-               spec->mixers[spec->num_mixers++] = via_hp_mixer;
+               via_hp_build(codec);
 
-       spec->mixers[spec->num_mixers++] = via_smart51_mixer;
+       via_smart51_build(spec);
        return 1;
 }
 
@@ -3613,12 +3640,10 @@ static int patch_vt1708B_8ch(struct hda_codec *codec)
        if (get_codec_type(codec) == VT1708BCE)
                return patch_vt1708S(codec);
        /* create a codec specific record */
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       spec = via_new_spec(codec);
        if (spec == NULL)
                return -ENOMEM;
 
-       codec->spec = spec;
-
        /* automatic parse from the BIOS config */
        err = vt1708B_parse_auto_config(codec);
        if (err < 0) {
@@ -3665,12 +3690,10 @@ static int patch_vt1708B_4ch(struct hda_codec *codec)
        int err;
 
        /* create a codec specific record */
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       spec = via_new_spec(codec);
        if (spec == NULL)
                return -ENOMEM;
 
-       codec->spec = spec;
-
        /* automatic parse from the BIOS config */
        err = vt1708B_parse_auto_config(codec);
        if (err < 0) {
@@ -3847,6 +3870,13 @@ static int vt1708S_auto_fill_dac_nids(struct via_spec *spec,
                }
        }
 
+       /* for Smart 5.1, line/mic inputs double as output pins */
+       if (cfg->line_outs == 1) {
+               spec->multiout.num_dacs = 3;
+               spec->multiout.dac_nids[AUTO_SEQ_SURROUND] = 0x11;
+               spec->multiout.dac_nids[AUTO_SEQ_CENLFE] = 0x24;
+       }
+
        return 0;
 }
 
@@ -3855,7 +3885,9 @@ static int vt1708S_auto_create_multi_out_ctls(struct via_spec *spec,
                                             const struct auto_pin_cfg *cfg)
 {
        char name[32];
-       static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
+       static const char * const chname[4] = {
+               "Front", "Surround", "C/LFE", "Side"
+       };
        hda_nid_t nid_vols[] = {0x10, 0x11, 0x24, 0x25};
        hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x26, 0x27};
        hda_nid_t nid, nid_vol, nid_mute;
@@ -3864,7 +3896,8 @@ static int vt1708S_auto_create_multi_out_ctls(struct via_spec *spec,
        for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
                nid = cfg->line_out_pins[i];
 
-               if (!nid)
+               /* for Smart 5.1, there are always at least six channels */
+               if (!nid && i > AUTO_SEQ_CENLFE)
                        continue;
 
                nid_vol = nid_vols[i];
@@ -3975,49 +4008,12 @@ static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
 }
 
 /* create playback/capture controls for input pins */
-static int vt1708S_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1708S_auto_create_analog_input_ctls(struct hda_codec *codec,
                                                const struct auto_pin_cfg *cfg)
 {
-       static char *labels[] = {
-               "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
-       };
-       struct hda_input_mux *imux = &spec->private_imux[0];
-       int i, err, idx = 0;
-
-       /* for internal loopback recording select */
-       imux->items[imux->num_items].label = "Stereo Mixer";
-       imux->items[imux->num_items].index = 5;
-       imux->num_items++;
-
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               if (!cfg->input_pins[i])
-                       continue;
-
-               switch (cfg->input_pins[i]) {
-               case 0x1a: /* Mic */
-                       idx = 2;
-                       break;
-
-               case 0x1b: /* Line In */
-                       idx = 3;
-                       break;
-
-               case 0x1e: /* Front Mic */
-                       idx = 4;
-                       break;
-
-               case 0x1f: /* CD */
-                       idx = 1;
-                       break;
-               }
-               err = via_new_analog_input(spec, labels[i], idx, 0x16);
-               if (err < 0)
-                       return err;
-               imux->items[imux->num_items].label = labels[i];
-               imux->items[imux->num_items].index = idx-1;
-               imux->num_items++;
-       }
-       return 0;
+       static 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 */
@@ -4065,7 +4061,7 @@ static int vt1708S_parse_auto_config(struct hda_codec *codec)
        err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
        if (err < 0)
                return err;
-       err = vt1708S_auto_create_analog_input_ctls(spec, &spec->autocfg);
+       err = vt1708S_auto_create_analog_input_ctls(codec, &spec->autocfg);
        if (err < 0)
                return err;
 
@@ -4079,9 +4075,9 @@ static int vt1708S_parse_auto_config(struct hda_codec *codec)
        spec->input_mux = &spec->private_imux[0];
 
        if (spec->hp_mux)
-               spec->mixers[spec->num_mixers++] = via_hp_mixer;
+               via_hp_build(codec);
 
-       spec->mixers[spec->num_mixers++] = via_smart51_mixer;
+       via_smart51_build(spec);
        return 1;
 }
 
@@ -4111,12 +4107,10 @@ static int patch_vt1708S(struct hda_codec *codec)
        int err;
 
        /* create a codec specific record */
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       spec = via_new_spec(codec);
        if (spec == NULL)
                return -ENOMEM;
 
-       codec->spec = spec;
-
        /* automatic parse from the BIOS config */
        err = vt1708S_parse_auto_config(codec);
        if (err < 0) {
@@ -4130,11 +4124,17 @@ static int patch_vt1708S(struct hda_codec *codec)
        spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs;
        spec->init_verbs[spec->num_iverbs++] = vt1708S_uniwill_init_verbs;
 
-       spec->stream_name_analog = "VT1708S Analog";
+       if (codec->vendor_id == 0x11060440)
+               spec->stream_name_analog = "VT1818S Analog";
+       else
+               spec->stream_name_analog = "VT1708S Analog";
        spec->stream_analog_playback = &vt1708S_pcm_analog_playback;
        spec->stream_analog_capture = &vt1708S_pcm_analog_capture;
 
-       spec->stream_name_digital = "VT1708S Digital";
+       if (codec->vendor_id == 0x11060440)
+               spec->stream_name_digital = "VT1818S Digital";
+       else
+               spec->stream_name_digital = "VT1708S Digital";
        spec->stream_digital_playback = &vt1708S_pcm_digital_playback;
 
        if (!spec->adc_nids && spec->input_mux) {
@@ -4330,7 +4330,7 @@ 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 *texts[] = { "ON", "OFF", NULL};
+       static const char * const texts[] = { "ON", "OFF", NULL};
        if (!pin)
                return 0;
        spec->multiout.hp_nid = 0x1D;
@@ -4351,58 +4351,20 @@ static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
        imux = &spec->private_imux[1];
 
        /* for hp mode select */
-       i = 0;
-       while (texts[i] != NULL)        {
-               imux->items[imux->num_items].label =  texts[i];
-               imux->items[imux->num_items].index = i;
-               imux->num_items++;
-               i++;
-       }
+       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 via_spec *spec,
+static int vt1702_auto_create_analog_input_ctls(struct hda_codec *codec,
                                                const struct auto_pin_cfg *cfg)
 {
-       static char *labels[] = {
-               "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
-       };
-       struct hda_input_mux *imux = &spec->private_imux[0];
-       int i, err, idx = 0;
-
-       /* for internal loopback recording select */
-       imux->items[imux->num_items].label = "Stereo Mixer";
-       imux->items[imux->num_items].index = 3;
-       imux->num_items++;
-
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               if (!cfg->input_pins[i])
-                       continue;
-
-               switch (cfg->input_pins[i]) {
-               case 0x14: /* Mic */
-                       idx = 1;
-                       break;
-
-               case 0x15: /* Line In */
-                       idx = 2;
-                       break;
-
-               case 0x18: /* Front Mic */
-                       idx = 3;
-                       break;
-               }
-               err = via_new_analog_input(spec, labels[i], idx, 0x1A);
-               if (err < 0)
-                       return err;
-               imux->items[imux->num_items].label = labels[i];
-               imux->items[imux->num_items].index = idx-1;
-               imux->num_items++;
-       }
-       return 0;
+       static 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)
@@ -4431,7 +4393,7 @@ static int vt1702_parse_auto_config(struct hda_codec *codec)
                                  (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
                                  (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
                                  (1 << AC_AMPCAP_MUTE_SHIFT));
-       err = vt1702_auto_create_analog_input_ctls(spec, &spec->autocfg);
+       err = vt1702_auto_create_analog_input_ctls(codec, &spec->autocfg);
        if (err < 0)
                return err;
 
@@ -4445,7 +4407,7 @@ static int vt1702_parse_auto_config(struct hda_codec *codec)
        spec->input_mux = &spec->private_imux[0];
 
        if (spec->hp_mux)
-               spec->mixers[spec->num_mixers++] = via_hp_mixer;
+               via_hp_build(codec);
 
        return 1;
 }
@@ -4466,12 +4428,10 @@ static int patch_vt1702(struct hda_codec *codec)
        int err;
 
        /* create a codec specific record */
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       spec = via_new_spec(codec);
        if (spec == NULL)
                return -ENOMEM;
 
-       codec->spec = spec;
-
        /* automatic parse from the BIOS config */
        err = vt1702_parse_auto_config(codec);
        if (err < 0) {
@@ -4626,7 +4586,6 @@ static struct hda_pcm_stream vt1718S_pcm_digital_playback = {
        .substreams = 2,
        .channels_min = 2,
        .channels_max = 2,
-       .rates = SNDRV_PCM_RATE_48000,
        /* NID is set in via_build_pcms */
        .ops = {
                .open = via_dig_playback_pcm_open,
@@ -4682,7 +4641,9 @@ static int vt1718S_auto_create_multi_out_ctls(struct via_spec *spec,
                                             const struct auto_pin_cfg *cfg)
 {
        char name[32];
-       static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
+       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;
@@ -4785,49 +4746,12 @@ static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
 }
 
 /* create playback/capture controls for input pins */
-static int vt1718S_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1718S_auto_create_analog_input_ctls(struct hda_codec *codec,
                                                const struct auto_pin_cfg *cfg)
 {
-       static char *labels[] = {
-               "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
-       };
-       struct hda_input_mux *imux = &spec->private_imux[0];
-       int i, err, idx = 0;
-
-       /* for internal loopback recording select */
-       imux->items[imux->num_items].label = "Stereo Mixer";
-       imux->items[imux->num_items].index = 5;
-       imux->num_items++;
-
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               if (!cfg->input_pins[i])
-                       continue;
-
-               switch (cfg->input_pins[i]) {
-               case 0x2b: /* Mic */
-                       idx = 1;
-                       break;
-
-               case 0x2a: /* Line In */
-                       idx = 2;
-                       break;
-
-               case 0x29: /* Front Mic */
-                       idx = 3;
-                       break;
-
-               case 0x2c: /* CD */
-                       idx = 0;
-                       break;
-               }
-               err = via_new_analog_input(spec, labels[i], idx, 0x21);
-               if (err < 0)
-                       return err;
-               imux->items[imux->num_items].label = labels[i];
-               imux->items[imux->num_items].index = idx;
-               imux->num_items++;
-       }
-       return 0;
+       static 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));
 }
 
 static int vt1718S_parse_auto_config(struct hda_codec *codec)
@@ -4851,7 +4775,7 @@ static int vt1718S_parse_auto_config(struct hda_codec *codec)
        err = vt1718S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
        if (err < 0)
                return err;
-       err = vt1718S_auto_create_analog_input_ctls(spec, &spec->autocfg);
+       err = vt1718S_auto_create_analog_input_ctls(codec, &spec->autocfg);
        if (err < 0)
                return err;
 
@@ -4868,9 +4792,9 @@ static int vt1718S_parse_auto_config(struct hda_codec *codec)
        spec->input_mux = &spec->private_imux[0];
 
        if (spec->hp_mux)
-               spec->mixers[spec->num_mixers++] = via_hp_mixer;
+               via_hp_build(codec);
 
-       spec->mixers[spec->num_mixers++] = via_smart51_mixer;
+       via_smart51_build(spec);
 
        return 1;
 }
@@ -4891,12 +4815,10 @@ static int patch_vt1718S(struct hda_codec *codec)
        int err;
 
        /* create a codec specific record */
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       spec = via_new_spec(codec);
        if (spec == NULL)
                return -ENOMEM;
 
-       codec->spec = spec;
-
        /* automatic parse from the BIOS config */
        err = vt1718S_parse_auto_config(codec);
        if (err < 0) {
@@ -5017,6 +4939,7 @@ static struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
        {
         .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,
@@ -5124,7 +5047,6 @@ static struct hda_pcm_stream vt1716S_pcm_digital_playback = {
        .substreams = 2,
        .channels_min = 2,
        .channels_max = 2,
-       .rates = SNDRV_PCM_RATE_48000,
        /* NID is set in via_build_pcms */
        .ops = {
                .open = via_dig_playback_pcm_open,
@@ -5170,7 +5092,9 @@ static int vt1716S_auto_create_multi_out_ctls(struct via_spec *spec,
                                              const struct auto_pin_cfg *cfg)
 {
        char name[32];
-       static const char *chname[3] = { "Front", "Surround", "C/LFE" };
+       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;
@@ -5286,49 +5210,12 @@ static int vt1716S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
 }
 
 /* create playback/capture controls for input pins */
-static int vt1716S_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1716S_auto_create_analog_input_ctls(struct hda_codec *codec,
                                                const struct auto_pin_cfg *cfg)
 {
-       static char *labels[] = {
-               "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
-       };
-       struct hda_input_mux *imux = &spec->private_imux[0];
-       int i, err, idx = 0;
-
-       /* for internal loopback recording select */
-       imux->items[imux->num_items].label = "Stereo Mixer";
-       imux->items[imux->num_items].index = 5;
-       imux->num_items++;
-
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               if (!cfg->input_pins[i])
-                       continue;
-
-               switch (cfg->input_pins[i]) {
-               case 0x1a: /* Mic */
-                       idx = 2;
-                       break;
-
-               case 0x1b: /* Line In */
-                       idx = 3;
-                       break;
-
-               case 0x1e: /* Front Mic */
-                       idx = 4;
-                       break;
-
-               case 0x1f: /* CD */
-                       idx = 1;
-                       break;
-               }
-               err = via_new_analog_input(spec, labels[i], idx, 0x16);
-               if (err < 0)
-                       return err;
-               imux->items[imux->num_items].label = labels[i];
-               imux->items[imux->num_items].index = idx-1;
-               imux->num_items++;
-       }
-       return 0;
+       static 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)
@@ -5351,7 +5238,7 @@ static int vt1716S_parse_auto_config(struct hda_codec *codec)
        err = vt1716S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
        if (err < 0)
                return err;
-       err = vt1716S_auto_create_analog_input_ctls(spec, &spec->autocfg);
+       err = vt1716S_auto_create_analog_input_ctls(codec, &spec->autocfg);
        if (err < 0)
                return err;
 
@@ -5365,9 +5252,9 @@ static int vt1716S_parse_auto_config(struct hda_codec *codec)
        spec->input_mux = &spec->private_imux[0];
 
        if (spec->hp_mux)
-               spec->mixers[spec->num_mixers++] = via_hp_mixer;
+               via_hp_build(codec);
 
-       spec->mixers[spec->num_mixers++] = via_smart51_mixer;
+       via_smart51_build(spec);
 
        return 1;
 }
@@ -5388,12 +5275,10 @@ static int patch_vt1716S(struct hda_codec *codec)
        int err;
 
        /* create a codec specific record */
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       spec = via_new_spec(codec);
        if (spec == NULL)
                return -ENOMEM;
 
-       codec->spec = spec;
-
        /* automatic parse from the BIOS config */
        err = vt1716S_parse_auto_config(codec);
        if (err < 0) {
@@ -5561,7 +5446,6 @@ static struct hda_pcm_stream vt2002P_pcm_digital_playback = {
        .substreams = 1,
        .channels_min = 2,
        .channels_max = 2,
-       .rates = SNDRV_PCM_RATE_48000,
        /* NID is set in via_build_pcms */
        .ops = {
                .open = via_dig_playback_pcm_open,
@@ -5635,54 +5519,25 @@ static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
 }
 
 /* create playback/capture controls for input pins */
-static int vt2002P_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt2002P_auto_create_analog_input_ctls(struct hda_codec *codec,
                                                const struct auto_pin_cfg *cfg)
 {
-       static char *labels[] = {
-               "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
-       };
+       struct via_spec *spec = codec->spec;
        struct hda_input_mux *imux = &spec->private_imux[0];
-       int i, err, idx = 0;
-
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               if (!cfg->input_pins[i])
-                       continue;
-
-               switch (cfg->input_pins[i]) {
-               case 0x2b: /* Mic */
-                       idx = 0;
-                       break;
-
-               case 0x2a: /* Line In */
-                       idx = 1;
-                       break;
-
-               case 0x29: /* Front Mic */
-                       idx = 2;
-                       break;
-               }
-               err = via_new_analog_input(spec, labels[i], idx, 0x21);
-               if (err < 0)
-                       return err;
-               imux->items[imux->num_items].label = labels[i];
-               imux->items[imux->num_items].index = idx;
-               imux->num_items++;
-       }
+       static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 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", 3, 0x21);
+       err = via_new_analog_input(spec, "Stereo Mixer", 0, 3, 0x21);
        if (err < 0)
                return err;
 
-       /* for internal loopback recording select */
-       imux->items[imux->num_items].label = "Stereo Mixer";
-       imux->items[imux->num_items].index = 3;
-       imux->num_items++;
-
        /* for digital mic select */
-       imux->items[imux->num_items].label = "Digital Mic";
-       imux->items[imux->num_items].index = 4;
-       imux->num_items++;
+       snd_hda_add_imux_item(imux, "Digital Mic", 4, NULL);
 
        return 0;
 }
@@ -5710,7 +5565,7 @@ static int vt2002P_parse_auto_config(struct hda_codec *codec)
        err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
        if (err < 0)
                return err;
-       err = vt2002P_auto_create_analog_input_ctls(spec, &spec->autocfg);
+       err = vt2002P_auto_create_analog_input_ctls(codec, &spec->autocfg);
        if (err < 0)
                return err;
 
@@ -5724,7 +5579,7 @@ static int vt2002P_parse_auto_config(struct hda_codec *codec)
        spec->input_mux = &spec->private_imux[0];
 
        if (spec->hp_mux)
-               spec->mixers[spec->num_mixers++] = via_hp_mixer;
+               via_hp_build(codec);
 
        return 1;
 }
@@ -5746,12 +5601,10 @@ static int patch_vt2002P(struct hda_codec *codec)
        int err;
 
        /* create a codec specific record */
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       spec = via_new_spec(codec);
        if (spec == NULL)
                return -ENOMEM;
 
-       codec->spec = spec;
-
        /* automatic parse from the BIOS config */
        err = vt2002P_parse_auto_config(codec);
        if (err < 0) {
@@ -5914,7 +5767,6 @@ static struct hda_pcm_stream vt1812_pcm_digital_playback = {
        .substreams = 1,
        .channels_min = 2,
        .channels_max = 2,
-       .rates = SNDRV_PCM_RATE_48000,
        /* NID is set in via_build_pcms */
        .ops = {
                .open = via_dig_playback_pcm_open,
@@ -5946,12 +5798,12 @@ static int vt1812_auto_create_multi_out_ctls(struct via_spec *spec,
 
        /* Line-Out: PortE */
        err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
-                             "Master Front Playback Volume",
+                             "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",
+                             "Front Playback Switch",
                              HDA_COMPOSE_AMP_VAL(0x28, 3, 0, HDA_OUTPUT));
        if (err < 0)
                return err;
@@ -5988,53 +5840,26 @@ static int vt1812_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
 }
 
 /* create playback/capture controls for input pins */
-static int vt1812_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1812_auto_create_analog_input_ctls(struct hda_codec *codec,
                                                const struct auto_pin_cfg *cfg)
 {
-       static char *labels[] = {
-               "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
-       };
+       struct via_spec *spec = codec->spec;
        struct hda_input_mux *imux = &spec->private_imux[0];
-       int i, err, idx = 0;
-
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               if (!cfg->input_pins[i])
-                       continue;
-
-               switch (cfg->input_pins[i]) {
-               case 0x2b: /* Mic */
-                       idx = 0;
-                       break;
+       static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0, 0, 0xff };
+       int err;
 
-               case 0x2a: /* Line In */
-                       idx = 1;
-                       break;
+       err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
+                                              ARRAY_SIZE(pin_idxs));
+       if (err < 0)
+               return err;
 
-               case 0x29: /* Front Mic */
-                       idx = 2;
-                       break;
-               }
-               err = via_new_analog_input(spec, labels[i], idx, 0x21);
-               if (err < 0)
-                       return err;
-               imux->items[imux->num_items].label = labels[i];
-               imux->items[imux->num_items].index = idx;
-               imux->num_items++;
-       }
        /* build volume/mute control of loopback */
-       err = via_new_analog_input(spec, "Stereo Mixer", 5, 0x21);
+       err = via_new_analog_input(spec, "Stereo Mixer", 0, 5, 0x21);
        if (err < 0)
                return err;
 
-       /* for internal loopback recording select */
-       imux->items[imux->num_items].label = "Stereo Mixer";
-       imux->items[imux->num_items].index = 5;
-       imux->num_items++;
-
        /* for digital mic select */
-       imux->items[imux->num_items].label = "Digital Mic";
-       imux->items[imux->num_items].index = 6;
-       imux->num_items++;
+       snd_hda_add_imux_item(imux, "Digital Mic", 6, NULL);
 
        return 0;
 }
@@ -6062,7 +5887,7 @@ static int vt1812_parse_auto_config(struct hda_codec *codec)
        err = vt1812_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
        if (err < 0)
                return err;
-       err = vt1812_auto_create_analog_input_ctls(spec, &spec->autocfg);
+       err = vt1812_auto_create_analog_input_ctls(codec, &spec->autocfg);
        if (err < 0)
                return err;
 
@@ -6076,7 +5901,7 @@ static int vt1812_parse_auto_config(struct hda_codec *codec)
        spec->input_mux = &spec->private_imux[0];
 
        if (spec->hp_mux)
-               spec->mixers[spec->num_mixers++] = via_hp_mixer;
+               via_hp_build(codec);
 
        return 1;
 }
@@ -6098,12 +5923,10 @@ static int patch_vt1812(struct hda_codec *codec)
        int err;
 
        /* create a codec specific record */
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       spec = via_new_spec(codec);
        if (spec == NULL)
                return -ENOMEM;
 
-       codec->spec = spec;
-
        /* automatic parse from the BIOS config */
        err = vt1812_parse_auto_config(codec);
        if (err < 0) {
@@ -6235,6 +6058,8 @@ static struct hda_codec_preset snd_hda_preset_via[] = {
        { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
        { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
        { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
+       { .id = 0x11060440, .name = "VT1818S",
+         .patch = patch_vt1708S},
        {} /* terminator */
 };