]> git.karo-electronics.de Git - mv-sheeva.git/commitdiff
Merge branch 'topic/beep-rename' into topic/hda
authorTakashi Iwai <tiwai@suse.de>
Mon, 16 Nov 2009 10:33:41 +0000 (11:33 +0100)
committerTakashi Iwai <tiwai@suse.de>
Mon, 16 Nov 2009 10:33:41 +0000 (11:33 +0100)
13 files changed:
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_codec.h
sound/pci/hda/hda_eld.c
sound/pci/hda/hda_hwdep.c
sound/pci/hda/hda_intel.c
sound/pci/hda/hda_local.h
sound/pci/hda/hda_proc.c
sound/pci/hda/patch_conexant.c
sound/pci/hda/patch_intelhdmi.c
sound/pci/hda/patch_nvhdmi.c
sound/pci/hda/patch_realtek.c
sound/pci/hda/patch_sigmatel.c
sound/pci/hda/patch_via.c

index af989f660cca243c54ad7d35c82ee968f9bec928..444d9039c1ace6df37ae3f7b1cb1d77f6b6a0e6f 100644 (file)
@@ -515,6 +515,7 @@ static int snd_hda_bus_dev_register(struct snd_device *device)
        struct hda_codec *codec;
        list_for_each_entry(codec, &bus->codec_list, list) {
                snd_hda_hwdep_add_sysfs(codec);
+               snd_hda_hwdep_add_power_sysfs(codec);
        }
        return 0;
 }
@@ -2452,9 +2453,11 @@ static void hda_call_codec_suspend(struct hda_codec *codec)
                            codec->afg ? codec->afg : codec->mfg,
                            AC_PWRST_D3);
 #ifdef CONFIG_SND_HDA_POWER_SAVE
+       snd_hda_update_power_acct(codec);
        cancel_delayed_work(&codec->power_work);
        codec->power_on = 0;
        codec->power_transition = 0;
+       codec->power_jiffies = jiffies;
 #endif
 }
 
@@ -2877,51 +2880,35 @@ static int set_pcm_default_values(struct hda_codec *codec,
        return 0;
 }
 
+const char *snd_hda_pcm_type_name[HDA_PCM_NTYPES] = {
+       "Audio", "SPDIF", "HDMI", "Modem"
+};
+
 /*
  * get the empty PCM device number to assign
  */
 static int get_empty_pcm_device(struct hda_bus *bus, int type)
 {
-       static const char *dev_name[HDA_PCM_NTYPES] = {
-               "Audio", "SPDIF", "HDMI", "Modem"
+       /* audio device indices; not linear to keep compatibility */
+       static int audio_idx[HDA_PCM_NTYPES][5] = {
+               [HDA_PCM_TYPE_AUDIO] = { 0, 2, 4, 5, -1 },
+               [HDA_PCM_TYPE_SPDIF] = { 1, -1 },
+               [HDA_PCM_TYPE_HDMI]  = { 3, 7, 8, 9, -1 },
+               [HDA_PCM_TYPE_MODEM] = { 6, -1 },
        };
-       /* starting device index for each PCM type */
-       static int dev_idx[HDA_PCM_NTYPES] = {
-               [HDA_PCM_TYPE_AUDIO] = 0,
-               [HDA_PCM_TYPE_SPDIF] = 1,
-               [HDA_PCM_TYPE_HDMI] = 3,
-               [HDA_PCM_TYPE_MODEM] = 6
-       };
-       /* normal audio device indices; not linear to keep compatibility */
-       static int audio_idx[4] = { 0, 2, 4, 5 };
-       int i, dev;
-
-       switch (type) {
-       case HDA_PCM_TYPE_AUDIO:
-               for (i = 0; i < ARRAY_SIZE(audio_idx); i++) {
-                       dev = audio_idx[i];
-                       if (!test_bit(dev, bus->pcm_dev_bits))
-                               goto ok;
-               }
-               snd_printk(KERN_WARNING "Too many audio devices\n");
-               return -EAGAIN;
-       case HDA_PCM_TYPE_SPDIF:
-       case HDA_PCM_TYPE_HDMI:
-       case HDA_PCM_TYPE_MODEM:
-               dev = dev_idx[type];
-               if (test_bit(dev, bus->pcm_dev_bits)) {
-                       snd_printk(KERN_WARNING "%s already defined\n",
-                                  dev_name[type]);
-                       return -EAGAIN;
-               }
-               break;
-       default:
+       int i;
+
+       if (type >= HDA_PCM_NTYPES) {
                snd_printk(KERN_WARNING "Invalid PCM type %d\n", type);
                return -EINVAL;
        }
- ok:
-       set_bit(dev, bus->pcm_dev_bits);
-       return dev;
+
+       for (i = 0; audio_idx[type][i] >= 0 ; i++)
+               if (!test_and_set_bit(audio_idx[type][i], bus->pcm_dev_bits))
+                       return audio_idx[type][i];
+
+       snd_printk(KERN_WARNING "Too many %s devices\n", snd_hda_pcm_type_name[type]);
+       return -EAGAIN;
 }
 
 /*
@@ -3207,6 +3194,17 @@ static void hda_keep_power_on(struct hda_codec *codec)
 {
        codec->power_count++;
        codec->power_on = 1;
+       codec->power_jiffies = jiffies;
+}
+
+void snd_hda_update_power_acct(struct hda_codec *codec)
+{
+       unsigned long delta = jiffies - codec->power_jiffies;
+       if (codec->power_on)
+               codec->power_on_acct += delta;
+       else
+               codec->power_off_acct += delta;
+       codec->power_jiffies += delta;
 }
 
 void snd_hda_power_up(struct hda_codec *codec)
@@ -3217,7 +3215,9 @@ void snd_hda_power_up(struct hda_codec *codec)
        if (codec->power_on || codec->power_transition)
                return;
 
+       snd_hda_update_power_acct(codec);
        codec->power_on = 1;
+       codec->power_jiffies = jiffies;
        if (bus->ops.pm_notify)
                bus->ops.pm_notify(bus);
        hda_call_codec_resume(codec);
@@ -3421,6 +3421,24 @@ static void cleanup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid)
        }
 }
 
+/* call each reboot notifier */
+void snd_hda_bus_reboot_notify(struct hda_bus *bus)
+{
+       struct hda_codec *codec;
+
+       if (!bus)
+               return;
+       list_for_each_entry(codec, &bus->codec_list, list) {
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+               if (!codec->power_on)
+                       continue;
+#endif
+               if (codec->patch_ops.reboot_notify)
+                       codec->patch_ops.reboot_notify(codec);
+       }
+}
+EXPORT_SYMBOL_HDA(snd_hda_bus_reboot_notify);
+
 /*
  * open the digital out in the exclusive mode
  */
index 99552fb5f75699d09461bebfbfd997e37c685f75..b16678cade18ce3ebb2f0f470c94519ac7934a13 100644 (file)
@@ -674,6 +674,7 @@ struct hda_codec_ops {
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        int (*check_power_status)(struct hda_codec *codec, hda_nid_t nid);
 #endif
+       void (*reboot_notify)(struct hda_codec *codec);
 };
 
 /* record for amp information cache */
@@ -811,6 +812,9 @@ struct hda_codec {
        unsigned int power_transition :1; /* power-state in transition */
        int power_count;        /* current (global) power refcount */
        struct delayed_work power_work; /* delayed task for powerdown */
+       unsigned long power_on_acct;
+       unsigned long power_off_acct;
+       unsigned long power_jiffies;
 #endif
 
        /* codec-specific additional proc output */
@@ -893,6 +897,7 @@ int snd_hda_codec_build_controls(struct hda_codec *codec);
 /*
  * PCM
  */
+extern const char *snd_hda_pcm_type_name[];
 int snd_hda_build_pcms(struct hda_bus *bus);
 int snd_hda_codec_build_pcms(struct hda_codec *codec);
 void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
@@ -910,6 +915,7 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
  * Misc
  */
 void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen);
+void snd_hda_bus_reboot_notify(struct hda_bus *bus);
 
 /*
  * power management
@@ -933,6 +939,7 @@ const char *snd_hda_get_jack_location(u32 cfg);
 void snd_hda_power_up(struct hda_codec *codec);
 void snd_hda_power_down(struct hda_codec *codec);
 #define snd_hda_codec_needs_resume(codec) codec->power_count
+void snd_hda_update_power_acct(struct hda_codec *codec);
 #else
 static inline void snd_hda_power_up(struct hda_codec *codec) {}
 static inline void snd_hda_power_down(struct hda_codec *codec) {}
index 9446a5abea1314da3888ca586594010cabea4cc4..20fa6aee29c0e2caf282dee87c1aa30c83cfb229 100644 (file)
@@ -560,13 +560,14 @@ static void hdmi_write_eld_info(struct snd_info_entry *entry,
 }
 
 
-int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld)
+int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld,
+                        int index)
 {
        char name[32];
        struct snd_info_entry *entry;
        int err;
 
-       snprintf(name, sizeof(name), "eld#%d", codec->addr);
+       snprintf(name, sizeof(name), "eld#%d.%d", codec->addr, index);
        err = snd_card_proc_new(codec->bus->card, name, &entry);
        if (err < 0)
                return err;
index cc24e6721d74ad12516595a1cf7904a51bc3fa55..d24328661c6a8f27a935f244d31234726fd906df 100644 (file)
@@ -154,6 +154,44 @@ int /*__devinit*/ snd_hda_create_hwdep(struct hda_codec *codec)
        return 0;
 }
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static ssize_t power_on_acct_show(struct device *dev,
+                                 struct device_attribute *attr,
+                                 char *buf)
+{
+       struct snd_hwdep *hwdep = dev_get_drvdata(dev);
+       struct hda_codec *codec = hwdep->private_data;
+       snd_hda_update_power_acct(codec);
+       return sprintf(buf, "%u\n", jiffies_to_msecs(codec->power_on_acct));
+}
+
+static ssize_t power_off_acct_show(struct device *dev,
+                                  struct device_attribute *attr,
+                                  char *buf)
+{
+       struct snd_hwdep *hwdep = dev_get_drvdata(dev);
+       struct hda_codec *codec = hwdep->private_data;
+       snd_hda_update_power_acct(codec);
+       return sprintf(buf, "%u\n", jiffies_to_msecs(codec->power_off_acct));
+}
+
+static struct device_attribute power_attrs[] = {
+       __ATTR_RO(power_on_acct),
+       __ATTR_RO(power_off_acct),
+};
+
+int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec)
+{
+       struct snd_hwdep *hwdep = codec->hwdep;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(power_attrs); i++)
+               snd_add_device_sysfs_file(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card,
+                                         hwdep->device, &power_attrs[i]);
+       return 0;
+}
+#endif /* CONFIG_SND_HDA_POWER_SAVE */
+
 #ifdef CONFIG_SND_HDA_RECONFIG
 
 /*
index c9ad182e1b4b6a3ddd23b422f2e4f821bde90604..e73e395e76019b5d437a9ee0f4c486c096f38a3e 100644 (file)
@@ -60,7 +60,7 @@ static int bdl_pos_adj[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
 static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
 static int probe_only[SNDRV_CARDS];
 static int single_cmd;
-static int enable_msi;
+static int enable_msi = -1;
 #ifdef CONFIG_SND_HDA_PATCH_LOADER
 static char *patch[SNDRV_CARDS];
 #endif
@@ -677,6 +677,14 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus,
                }
        }
 
+       if (!chip->polling_mode) {
+               snd_printk(KERN_WARNING SFX "azx_get_response timeout, "
+                          "switching to polling mode: last cmd=0x%08x\n",
+                          chip->last_cmd[addr]);
+               chip->polling_mode = 1;
+               goto again;
+       }
+
        if (chip->msi) {
                snd_printk(KERN_WARNING SFX "No response from codec, "
                           "disabling MSI: last cmd=0x%08x\n",
@@ -692,14 +700,6 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus,
                goto again;
        }
 
-       if (!chip->polling_mode) {
-               snd_printk(KERN_WARNING SFX "azx_get_response timeout, "
-                          "switching to polling mode: last cmd=0x%08x\n",
-                          chip->last_cmd[addr]);
-               chip->polling_mode = 1;
-               goto again;
-       }
-
        if (chip->probing) {
                /* If this critical timeout happens during the codec probing
                 * phase, this is likely an access to a non-existing codec
@@ -722,9 +722,10 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus,
                   chip->last_cmd[addr]);
        chip->single_cmd = 1;
        bus->response_reset = 0;
-       /* re-initialize CORB/RIRB */
+       /* release CORB/RIRB */
        azx_free_cmd_io(chip);
-       azx_init_cmd_io(chip);
+       /* disable unsolicited responses */
+       azx_writel(chip, GCTL, azx_readl(chip, GCTL) & ~ICH6_GCTL_UNSOL);
        return -1;
 }
 
@@ -865,7 +866,9 @@ static int azx_reset(struct azx *chip)
        }
 
        /* Accept unsolicited responses */
-       azx_writel(chip, GCTL, azx_readl(chip, GCTL) | ICH6_GCTL_UNSOL);
+       if (!chip->single_cmd)
+               azx_writel(chip, GCTL, azx_readl(chip, GCTL) |
+                          ICH6_GCTL_UNSOL);
 
        /* detect codecs */
        if (!chip->codec_mask) {
@@ -980,7 +983,8 @@ static void azx_init_chip(struct azx *chip)
        azx_int_enable(chip);
 
        /* initialize the codec command I/O */
-       azx_init_cmd_io(chip);
+       if (!chip->single_cmd)
+               azx_init_cmd_io(chip);
 
        /* program the position buffer */
        azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr);
@@ -2150,6 +2154,7 @@ static int azx_resume(struct pci_dev *pci)
 static int azx_halt(struct notifier_block *nb, unsigned long event, void *buf)
 {
        struct azx *chip = container_of(nb, struct azx, reboot_notifier);
+       snd_hda_bus_reboot_notify(chip->bus);
        azx_stop_chip(chip);
        return NOTIFY_OK;
 }
@@ -2300,11 +2305,9 @@ static void __devinit check_probe_mask(struct azx *chip, int dev)
 }
 
 /*
- * white-list for enable_msi
+ * white/black-list for enable_msi
  */
-static struct snd_pci_quirk msi_white_list[] __devinitdata = {
-       SND_PCI_QUIRK(0x103c, 0x30f7, "HP Pavilion dv4t-1300", 1),
-       SND_PCI_QUIRK(0x103c, 0x3607, "HP Compa CQ40", 1),
+static struct snd_pci_quirk msi_black_list[] __devinitdata = {
        {}
 };
 
@@ -2312,10 +2315,12 @@ static void __devinit check_msi(struct azx *chip)
 {
        const struct snd_pci_quirk *q;
 
-       chip->msi = enable_msi;
-       if (chip->msi)
+       if (enable_msi >= 0) {
+               chip->msi = !!enable_msi;
                return;
-       q = snd_pci_quirk_lookup(chip->pci, msi_white_list);
+       }
+       chip->msi = 1;  /* enable MSI as default */
+       q = snd_pci_quirk_lookup(chip->pci, msi_black_list);
        if (q) {
                printk(KERN_INFO
                       "hda_intel: msi for device %04x:%04x set to %d\n",
@@ -2674,6 +2679,7 @@ static struct pci_device_id azx_ids[] = {
        { PCI_DEVICE(0x10de, 0x044b), .driver_data = AZX_DRIVER_NVIDIA },
        { PCI_DEVICE(0x10de, 0x055c), .driver_data = AZX_DRIVER_NVIDIA },
        { PCI_DEVICE(0x10de, 0x055d), .driver_data = AZX_DRIVER_NVIDIA },
+       { PCI_DEVICE(0x10de, 0x0590), .driver_data = AZX_DRIVER_NVIDIA },
        { PCI_DEVICE(0x10de, 0x0774), .driver_data = AZX_DRIVER_NVIDIA },
        { PCI_DEVICE(0x10de, 0x0775), .driver_data = AZX_DRIVER_NVIDIA },
        { PCI_DEVICE(0x10de, 0x0776), .driver_data = AZX_DRIVER_NVIDIA },
index 5f1dcc59002b4a610f0bff8b6455e9d1205f42a9..c1ca3182e6a4b3c4e6a0b6b44a7bf8c4f86cb62d 100644 (file)
@@ -437,6 +437,15 @@ int snd_hda_create_hwdep(struct hda_codec *codec);
 static inline int snd_hda_create_hwdep(struct hda_codec *codec) { return 0; }
 #endif
 
+#if defined(CONFIG_SND_HDA_POWER_SAVE) && defined(CONFIG_SND_HDA_HWDEP)
+int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec);
+#else
+static inline int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec)
+{
+       return 0;
+}
+#endif
+
 #ifdef CONFIG_SND_HDA_RECONFIG
 int snd_hda_hwdep_add_sysfs(struct hda_codec *codec);
 #else
@@ -541,11 +550,13 @@ int snd_hdmi_get_eld(struct hdmi_eld *, struct hda_codec *, hda_nid_t);
 void snd_hdmi_show_eld(struct hdmi_eld *eld);
 
 #ifdef CONFIG_PROC_FS
-int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld);
+int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld,
+                        int index);
 void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld);
 #else
 static inline int snd_hda_eld_proc_new(struct hda_codec *codec,
-                                      struct hdmi_eld *eld)
+                                      struct hdmi_eld *eld,
+                                      int index)
 {
        return 0;
 }
index 95f24e4729f83c5eede0328f26bef18f2021a68c..f5b783ce450db48d13fed2b2e462f28e6b6db763 100644 (file)
@@ -309,7 +309,21 @@ static void print_audio_io(struct snd_info_buffer *buffer,
                           struct hda_codec *codec, hda_nid_t nid,
                           unsigned int wid_type)
 {
-       int conv = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
+       int pcm, conv;
+       for (pcm = 0; pcm < codec->num_pcms; pcm++) {
+               int type;
+               struct hda_pcm *cpcm = &codec->pcm_info[pcm];
+               for (type = 0; type < 2; type++) {
+                       if (cpcm->stream[type].nid != nid || cpcm->pcm == NULL)
+                               continue;
+                       snd_iprintf(buffer, "  Device: name=\"%s\", "
+                                   "type=\"%s\", device=%i\n",
+                                   cpcm->name,
+                                   snd_hda_pcm_type_name[cpcm->pcm_type],
+                                   cpcm->pcm->device);
+               }
+       }
+       conv = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
        snd_iprintf(buffer,
                    "  Converter: stream=%d, channel=%d\n",
                    (conv & AC_CONV_STREAM) >> AC_CONV_STREAM_SHIFT,
index 3fbbc8c01e703845e590345ca0f564cd02499d12..905859d4f4dfdca60561e9bafd52492e8a053933 100644 (file)
@@ -110,6 +110,7 @@ struct conexant_spec {
 
        unsigned int dell_automute;
        unsigned int port_d_mode;
+       unsigned char ext_mic_bias;
 };
 
 static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo,
@@ -1927,6 +1928,11 @@ static hda_nid_t cxt5066_adc_nids[3] = { 0x14, 0x15, 0x16 };
 static hda_nid_t cxt5066_capsrc_nids[1] = { 0x17 };
 #define CXT5066_SPDIF_OUT      0x21
 
+/* OLPC's microphone port is DC coupled for use with external sensors,
+ * therefore we use a 50% mic bias in order to center the input signal with
+ * the DC input range of the codec. */
+#define CXT5066_OLPC_EXT_MIC_BIAS PIN_VREF50
+
 static struct hda_channel_mode cxt5066_modes[1] = {
        { 2, NULL },
 };
@@ -1980,9 +1986,10 @@ static int cxt5066_hp_master_sw_put(struct snd_kcontrol *kcontrol,
 /* toggle input of built-in and mic jack appropriately */
 static void cxt5066_automic(struct hda_codec *codec)
 {
-       static struct hda_verb ext_mic_present[] = {
+       struct conexant_spec *spec = codec->spec;
+       struct hda_verb ext_mic_present[] = {
                /* enable external mic, port B */
-               {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+               {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, spec->ext_mic_bias},
 
                /* switch to external mic input */
                {0x17, AC_VERB_SET_CONNECT_SEL, 0},
@@ -2235,7 +2242,7 @@ static struct hda_verb cxt5066_init_verbs_olpc[] = {
        {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
 
        /* Port B: external microphone */
-       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, CXT5066_OLPC_EXT_MIC_BIAS},
 
        /* Port C: internal microphone */
        {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
@@ -2325,6 +2332,7 @@ static struct snd_pci_quirk cxt5066_cfg_tbl[] = {
                      CXT5066_LAPTOP),
        SND_PCI_QUIRK(0x1028, 0x02f5, "Dell",
                      CXT5066_DELL_LAPTOP),
+       SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5),
        {}
 };
 
@@ -2352,6 +2360,7 @@ static int patch_cxt5066(struct hda_codec *codec)
        spec->input_mux = &cxt5066_capture_source;
 
        spec->port_d_mode = PIN_HP;
+       spec->ext_mic_bias = PIN_VREF80;
 
        spec->num_init_verbs = 1;
        spec->init_verbs[0] = cxt5066_init_verbs;
@@ -2383,6 +2392,7 @@ static int patch_cxt5066(struct hda_codec *codec)
                spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc;
                spec->mixers[spec->num_mixers++] = cxt5066_mixers;
                spec->port_d_mode = 0;
+               spec->ext_mic_bias = CXT5066_OLPC_EXT_MIC_BIAS;
 
                /* no S/PDIF out */
                spec->multiout.dig_out_nid = 0;
index 01a18ed475ace8730a86e3a3ff3ca664a4acfcb7..4f25f08d332bc1a1e581c3dc4d578559f7e5d8ef 100644 (file)
 #include "hda_codec.h"
 #include "hda_local.h"
 
-static hda_nid_t cvt_nid;      /* audio converter */
-static hda_nid_t pin_nid;      /* HDMI output pin */
+/*
+ * 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
+ * more ports (DVI, HDMI or DisplayPort).
+ *
+ * The HDA correspondence of pipes/ports are converter/pin nodes.
+ */
+#define INTEL_HDMI_CVTS        2
+#define INTEL_HDMI_PINS        3
 
-#define INTEL_HDMI_EVENT_TAG           0x08
+static char *intel_hdmi_pcm_names[INTEL_HDMI_CVTS] = {
+       "INTEL HDMI 0",
+       "INTEL HDMI 1",
+};
 
 struct intel_hdmi_spec {
-       struct hda_multi_out multiout;
-       struct hda_pcm pcm_rec;
-       struct hdmi_eld sink_eld;
+       int num_cvts;
+       int num_pins;
+       hda_nid_t cvt[INTEL_HDMI_CVTS+1];  /* audio sources */
+       hda_nid_t pin[INTEL_HDMI_PINS+1];  /* audio sinks */
+
+       /*
+        * source connection for each pin
+        */
+       hda_nid_t pin_cvt[INTEL_HDMI_PINS+1];
+
+       /*
+        * HDMI sink attached to each pin
+        */
+       bool            sink_present[INTEL_HDMI_PINS];
+       bool            sink_eldv[INTEL_HDMI_PINS];
+       struct hdmi_eld sink_eld[INTEL_HDMI_PINS];
+
+       /*
+        * export one pcm per pipe
+        */
+       struct hda_pcm  pcm_rec[INTEL_HDMI_CVTS];
 };
 
 struct hdmi_audio_infoframe {
@@ -184,40 +212,165 @@ static struct cea_channel_speaker_allocation channel_allocations[] = {
 { .ca_index = 0x31,  .speakers = { FRW,  FLW,  RR,  RL,  FC,  LFE,  FR,  FL } },
 };
 
+
+/*
+ * HDA/HDMI auto parsing
+ */
+
+static int hda_node_index(hda_nid_t *nids, hda_nid_t nid)
+{
+       int i;
+
+       for (i = 0; nids[i]; i++)
+               if (nids[i] == nid)
+                       return i;
+
+       snd_printk(KERN_WARNING "HDMI: nid %d not registered\n", nid);
+       return -EINVAL;
+}
+
+static int intel_hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid)
+{
+       struct intel_hdmi_spec *spec = codec->spec;
+       hda_nid_t conn_list[HDA_MAX_CONNECTIONS];
+       int conn_len, curr;
+       int index;
+
+       if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) {
+               snd_printk(KERN_WARNING
+                          "HDMI: pin %d wcaps %#x "
+                          "does not support connection list\n",
+                          pin_nid, get_wcaps(codec, 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];
+
+       return 0;
+}
+
+static int intel_hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
+{
+       struct intel_hdmi_spec *spec = codec->spec;
+
+       if (spec->num_pins >= INTEL_HDMI_PINS) {
+               snd_printk(KERN_WARNING
+                          "HDMI: no space for pin %d \n", pin_nid);
+               return -EINVAL;
+       }
+
+       spec->pin[spec->num_pins] = pin_nid;
+       spec->num_pins++;
+
+       /*
+        * It is assumed that converter nodes come first in the node list and
+        * hence have been registered and usable now.
+        */
+       return intel_hdmi_read_pin_conn(codec, pin_nid);
+}
+
+static int intel_hdmi_add_cvt(struct hda_codec *codec, hda_nid_t nid)
+{
+       struct intel_hdmi_spec *spec = codec->spec;
+
+       if (spec->num_cvts >= INTEL_HDMI_CVTS) {
+               snd_printk(KERN_WARNING
+                          "HDMI: no space for converter %d \n", nid);
+               return -EINVAL;
+       }
+
+       spec->cvt[spec->num_cvts] = nid;
+       spec->num_cvts++;
+
+       return 0;
+}
+
+static int intel_hdmi_parse_codec(struct hda_codec *codec)
+{
+       hda_nid_t nid;
+       int i, nodes;
+
+       nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
+       if (!nid || nodes < 0) {
+               snd_printk(KERN_WARNING "HDMI: failed to get afg sub nodes\n");
+               return -EINVAL;
+       }
+
+       for (i = 0; i < nodes; i++, nid++) {
+               unsigned int caps;
+               unsigned int type;
+
+               caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
+               type = get_wcaps_type(caps);
+
+               if (!(caps & AC_WCAP_DIGITAL))
+                       continue;
+
+               switch (type) {
+               case AC_WID_AUD_OUT:
+                       if (intel_hdmi_add_cvt(codec, nid) < 0)
+                               return -EINVAL;
+                       break;
+               case AC_WID_PIN:
+                       caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
+                       if (!(caps & AC_PINCAP_HDMI))
+                               continue;
+                       if (intel_hdmi_add_pin(codec, nid) < 0)
+                               return -EINVAL;
+                       break;
+               }
+       }
+
+       return 0;
+}
+
 /*
  * HDMI routines
  */
 
 #ifdef BE_PARANOID
-static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t nid,
+static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t pin_nid,
                                int *packet_index, int *byte_index)
 {
        int val;
 
-       val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_INDEX, 0);
+       val = snd_hda_codec_read(codec, pin_nid, 0,
+                                AC_VERB_GET_HDMI_DIP_INDEX, 0);
 
        *packet_index = val >> 5;
        *byte_index = val & 0x1f;
 }
 #endif
 
-static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t nid,
+static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t pin_nid,
                                int packet_index, int byte_index)
 {
        int val;
 
        val = (packet_index << 5) | (byte_index & 0x1f);
 
-       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val);
+       snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val);
 }
 
-static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t nid,
+static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t pin_nid,
                                unsigned char val)
 {
-       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val);
+       snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val);
 }
 
-static void hdmi_enable_output(struct hda_codec *codec)
+static void hdmi_enable_output(struct hda_codec *codec, hda_nid_t pin_nid)
 {
        /* Unmute */
        if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP)
@@ -231,7 +384,8 @@ static void hdmi_enable_output(struct hda_codec *codec)
 /*
  * Enable Audio InfoFrame Transmission
  */
-static void hdmi_start_infoframe_trans(struct hda_codec *codec)
+static void hdmi_start_infoframe_trans(struct hda_codec *codec,
+                                      hda_nid_t pin_nid)
 {
        hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
        snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
@@ -241,37 +395,42 @@ static void hdmi_start_infoframe_trans(struct hda_codec *codec)
 /*
  * Disable Audio InfoFrame Transmission
  */
-static void hdmi_stop_infoframe_trans(struct hda_codec *codec)
+static void hdmi_stop_infoframe_trans(struct hda_codec *codec,
+                                     hda_nid_t pin_nid)
 {
        hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
        snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
                                                AC_DIPXMIT_DISABLE);
 }
 
-static int hdmi_get_channel_count(struct hda_codec *codec)
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t nid)
 {
-       return 1 + snd_hda_codec_read(codec, cvt_nid, 0,
+       return 1 + snd_hda_codec_read(codec, nid, 0,
                                        AC_VERB_GET_CVT_CHAN_COUNT, 0);
 }
+#endif
 
-static void hdmi_set_channel_count(struct hda_codec *codec, int chs)
+static void hdmi_set_channel_count(struct hda_codec *codec,
+                                  hda_nid_t nid, int chs)
 {
-       snd_hda_codec_write(codec, cvt_nid, 0,
-                                       AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
+       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
 
-       if (chs != hdmi_get_channel_count(codec))
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+       if (chs != hdmi_get_channel_count(codec, nid))
                snd_printd(KERN_INFO "HDMI channel count: expect %d, get %d\n",
-                                       chs, hdmi_get_channel_count(codec));
+                          chs, hdmi_get_channel_count(codec, nid));
+#endif
 }
 
-static void hdmi_debug_channel_mapping(struct hda_codec *codec)
+static void hdmi_debug_channel_mapping(struct hda_codec *codec, hda_nid_t nid)
 {
 #ifdef CONFIG_SND_DEBUG_VERBOSE
        int i;
        int slot;
 
        for (i = 0; i < 8; i++) {
-               slot = snd_hda_codec_read(codec, cvt_nid, 0,
+               slot = snd_hda_codec_read(codec, nid, 0,
                                                AC_VERB_GET_HDMI_CHAN_SLOT, i);
                printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n",
                                                slot >> 4, slot & 0x7);
@@ -279,12 +438,12 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec)
 #endif
 }
 
-static void hdmi_parse_eld(struct hda_codec *codec)
+static void hdmi_parse_eld(struct hda_codec *codec, int index)
 {
        struct intel_hdmi_spec *spec = codec->spec;
-       struct hdmi_eld *eld = &spec->sink_eld;
+       struct hdmi_eld *eld = &spec->sink_eld[index];
 
-       if (!snd_hdmi_get_eld(eld, codec, pin_nid))
+       if (!snd_hdmi_get_eld(eld, codec, spec->pin[index]))
                snd_hdmi_show_eld(eld);
 }
 
@@ -293,7 +452,7 @@ static void hdmi_parse_eld(struct hda_codec *codec)
  * Audio InfoFrame routines
  */
 
-static void hdmi_debug_dip_size(struct hda_codec *codec)
+static void hdmi_debug_dip_size(struct hda_codec *codec, hda_nid_t pin_nid)
 {
 #ifdef CONFIG_SND_DEBUG_VERBOSE
        int i;
@@ -310,7 +469,7 @@ static void hdmi_debug_dip_size(struct hda_codec *codec)
 #endif
 }
 
-static void hdmi_clear_dip_buffers(struct hda_codec *codec)
+static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid)
 {
 #ifdef BE_PARANOID
        int i, j;
@@ -340,14 +499,15 @@ static void hdmi_clear_dip_buffers(struct hda_codec *codec)
 }
 
 static void hdmi_fill_audio_infoframe(struct hda_codec *codec,
-                                       struct hdmi_audio_infoframe *ai)
+                                     hda_nid_t pin_nid,
+                                     struct hdmi_audio_infoframe *ai)
 {
        u8 *params = (u8 *)ai;
        u8 sum = 0;
        int i;
 
-       hdmi_debug_dip_size(codec);
-       hdmi_clear_dip_buffers(codec); /* be paranoid */
+       hdmi_debug_dip_size(codec, pin_nid);
+       hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */
 
        for (i = 0; i < sizeof(ai); i++)
                sum += params[i];
@@ -386,11 +546,11 @@ static void init_channel_allocations(void)
  *
  * TODO: it could select the wrong CA from multiple candidates.
 */
-static int hdmi_setup_channel_allocation(struct hda_codec *codec,
+static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
                                         struct hdmi_audio_infoframe *ai)
 {
        struct intel_hdmi_spec *spec = codec->spec;
-       struct hdmi_eld *eld = &spec->sink_eld;
+       struct hdmi_eld *eld;
        int i;
        int spk_mask = 0;
        int channels = 1 + (ai->CC02_CT47 & 0x7);
@@ -402,6 +562,11 @@ static int hdmi_setup_channel_allocation(struct hda_codec *codec,
        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
@@ -439,8 +604,8 @@ static int hdmi_setup_channel_allocation(struct hda_codec *codec,
        return ai->CA;
 }
 
-static void hdmi_setup_channel_mapping(struct hda_codec *codec,
-                                       struct hdmi_audio_infoframe *ai)
+static void hdmi_setup_channel_mapping(struct hda_codec *codec, hda_nid_t nid,
+                                      struct hdmi_audio_infoframe *ai)
 {
        int i;
 
@@ -453,17 +618,20 @@ static void hdmi_setup_channel_mapping(struct hda_codec *codec,
         */
 
        for (i = 0; i < 8; i++)
-               snd_hda_codec_write(codec, cvt_nid, 0,
+               snd_hda_codec_write(codec, nid, 0,
                                    AC_VERB_SET_HDMI_CHAN_SLOT,
                                    (i << 4) | i);
 
-       hdmi_debug_channel_mapping(codec);
+       hdmi_debug_channel_mapping(codec, nid);
 }
 
 
-static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
+static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid,
                                        struct snd_pcm_substream *substream)
 {
+       struct intel_hdmi_spec *spec = codec->spec;
+       hda_nid_t pin_nid;
+       int i;
        struct hdmi_audio_infoframe ai = {
                .type           = 0x84,
                .ver            = 0x01,
@@ -471,11 +639,19 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
                .CC02_CT47      = substream->runtime->channels - 1,
        };
 
-       hdmi_setup_channel_allocation(codec, &ai);
-       hdmi_setup_channel_mapping(codec, &ai);
+       hdmi_setup_channel_allocation(codec, nid, &ai);
+       hdmi_setup_channel_mapping(codec, nid, &ai);
+
+       for (i = 0; i < spec->num_pins; i++) {
+               if (spec->pin_cvt[i] != nid)
+                       continue;
+               if (spec->sink_present[i] != true)
+                       continue;
 
-       hdmi_fill_audio_infoframe(codec, &ai);
-       hdmi_start_infoframe_trans(codec);
+               pin_nid = spec->pin[i];
+               hdmi_fill_audio_infoframe(codec, pin_nid, &ai);
+               hdmi_start_infoframe_trans(codec, pin_nid);
+       }
 }
 
 
@@ -485,27 +661,39 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
 
 static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
 {
+       struct intel_hdmi_spec *spec = codec->spec;
+       int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
        int pind = !!(res & AC_UNSOL_RES_PD);
        int eldv = !!(res & AC_UNSOL_RES_ELDV);
+       int index;
 
        printk(KERN_INFO
-               "HDMI hot plug event: Presence_Detect=%d ELD_Valid=%d\n",
-               pind, eldv);
+               "HDMI hot plug event: Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
+               tag, pind, eldv);
+
+       index = hda_node_index(spec->pin, tag);
+       if (index < 0)
+               return;
+
+       spec->sink_present[index] = pind;
+       spec->sink_eldv[index] = eldv;
 
        if (pind && eldv) {
-               hdmi_parse_eld(codec);
+               hdmi_parse_eld(codec, index);
                /* TODO: do real things about ELD */
        }
 }
 
 static void hdmi_non_intrinsic_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;
        int cp_state = !!(res & AC_UNSOL_RES_CP_STATE);
        int cp_ready = !!(res & AC_UNSOL_RES_CP_READY);
 
        printk(KERN_INFO
-               "HDMI content protection event: SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n",
+               "HDMI CP event: PIN=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n",
+               tag,
                subtag,
                cp_state,
                cp_ready);
@@ -520,10 +708,11 @@ static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
 
 static void intel_hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
 {
+       struct intel_hdmi_spec *spec = codec->spec;
        int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
        int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
 
-       if (tag != INTEL_HDMI_EVENT_TAG) {
+       if (hda_node_index(spec->pin, tag) < 0) {
                snd_printd(KERN_INFO "Unexpected HDMI event tag 0x%x\n", tag);
                return;
        }
@@ -538,69 +727,70 @@ static void intel_hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
  * Callbacks
  */
 
-static int intel_hdmi_playback_pcm_open(struct hda_pcm_stream *hinfo,
-                                       struct hda_codec *codec,
-                                       struct snd_pcm_substream *substream)
-{
-       struct intel_hdmi_spec *spec = codec->spec;
-
-       return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
-
-static int intel_hdmi_playback_pcm_close(struct hda_pcm_stream *hinfo,
-                                        struct hda_codec *codec,
-                                        struct snd_pcm_substream *substream)
+static int intel_hdmi_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 intel_hdmi_spec *spec = codec->spec;
+       hdmi_set_channel_count(codec, hinfo->nid,
+                              substream->runtime->channels);
 
-       hdmi_stop_infoframe_trans(codec);
+       hdmi_setup_audio_infoframe(codec, hinfo->nid, substream);
 
-       return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+       snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format);
+       return 0;
 }
 
-static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+static int intel_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
                                           struct hda_codec *codec,
-                                          unsigned int stream_tag,
-                                          unsigned int format,
                                           struct snd_pcm_substream *substream)
 {
        struct intel_hdmi_spec *spec = codec->spec;
+       int i;
 
-       snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
-                                            format, substream);
-
-       hdmi_set_channel_count(codec, substream->runtime->channels);
+       for (i = 0; i < spec->num_pins; i++) {
+               if (spec->pin_cvt[i] != hinfo->nid)
+                       continue;
 
-       hdmi_setup_audio_infoframe(codec, substream);
+               hdmi_stop_infoframe_trans(codec, spec->pin[i]);
+       }
 
+       snd_hda_codec_cleanup_stream(codec, hinfo->nid);
        return 0;
 }
 
 static struct hda_pcm_stream intel_hdmi_pcm_playback = {
        .substreams = 1,
        .channels_min = 2,
-       .channels_max = 8,
        .ops = {
-               .open    = intel_hdmi_playback_pcm_open,
-               .close   = intel_hdmi_playback_pcm_close,
-               .prepare = intel_hdmi_playback_pcm_prepare
+               .prepare = intel_hdmi_playback_pcm_prepare,
+               .cleanup = intel_hdmi_playback_pcm_cleanup,
        },
 };
 
 static int intel_hdmi_build_pcms(struct hda_codec *codec)
 {
        struct intel_hdmi_spec *spec = codec->spec;
-       struct hda_pcm *info = &spec->pcm_rec;
+       struct hda_pcm *info = spec->pcm_rec;
+       int i;
 
-       codec->num_pcms = 1;
+       codec->num_pcms = spec->num_cvts;
        codec->pcm_info = info;
 
-       /* NID to query formats and rates and setup streams */
-       intel_hdmi_pcm_playback.nid = cvt_nid;
+       for (i = 0; i < codec->num_pcms; i++, info++) {
+               unsigned int chans;
+
+               chans = get_wcaps(codec, spec->cvt[i]);
+               chans = get_wcaps_channels(chans);
 
-       info->name = "INTEL HDMI";
-       info->pcm_type = HDA_PCM_TYPE_HDMI;
-       info->stream[SNDRV_PCM_STREAM_PLAYBACK] = intel_hdmi_pcm_playback;
+               info->name = intel_hdmi_pcm_names[i];
+               info->pcm_type = HDA_PCM_TYPE_HDMI;
+               info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
+                                                       intel_hdmi_pcm_playback;
+               info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->cvt[i];
+               info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans;
+       }
 
        return 0;
 }
@@ -609,29 +799,39 @@ static int intel_hdmi_build_controls(struct hda_codec *codec)
 {
        struct intel_hdmi_spec *spec = codec->spec;
        int err;
+       int i;
 
-       err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
-       if (err < 0)
-               return err;
+       for (i = 0; i < codec->num_pcms; i++) {
+               err = snd_hda_create_spdif_out_ctls(codec, spec->cvt[i]);
+               if (err < 0)
+                       return err;
+       }
 
        return 0;
 }
 
 static int intel_hdmi_init(struct hda_codec *codec)
 {
-       hdmi_enable_output(codec);
+       struct intel_hdmi_spec *spec = codec->spec;
+       int i;
 
-       snd_hda_codec_write(codec, pin_nid, 0,
-                           AC_VERB_SET_UNSOLICITED_ENABLE,
-                           AC_USRSP_EN | INTEL_HDMI_EVENT_TAG);
+       for (i = 0; spec->pin[i]; i++) {
+               hdmi_enable_output(codec, spec->pin[i]);
+               snd_hda_codec_write(codec, spec->pin[i], 0,
+                                   AC_VERB_SET_UNSOLICITED_ENABLE,
+                                   AC_USRSP_EN | spec->pin[i]);
+       }
        return 0;
 }
 
 static void intel_hdmi_free(struct hda_codec *codec)
 {
        struct intel_hdmi_spec *spec = codec->spec;
+       int i;
+
+       for (i = 0; i < spec->num_pins; i++)
+               snd_hda_eld_proc_free(codec, &spec->sink_eld[i]);
 
-       snd_hda_eld_proc_free(codec, &spec->sink_eld);
        kfree(spec);
 }
 
@@ -643,49 +843,38 @@ static struct hda_codec_ops intel_hdmi_patch_ops = {
        .unsol_event            = intel_hdmi_unsol_event,
 };
 
-static int do_patch_intel_hdmi(struct hda_codec *codec)
+static int patch_intel_hdmi(struct hda_codec *codec)
 {
        struct intel_hdmi_spec *spec;
+       int i;
 
        spec = kzalloc(sizeof(*spec), GFP_KERNEL);
        if (spec == NULL)
                return -ENOMEM;
 
-       spec->multiout.num_dacs = 0;      /* no analog */
-       spec->multiout.max_channels = 8;
-       spec->multiout.dig_out_nid = cvt_nid;
-
        codec->spec = spec;
+       if (intel_hdmi_parse_codec(codec) < 0) {
+               codec->spec = NULL;
+               kfree(spec);
+               return -EINVAL;
+       }
        codec->patch_ops = intel_hdmi_patch_ops;
 
-       snd_hda_eld_proc_new(codec, &spec->sink_eld);
+       for (i = 0; i < spec->num_pins; i++)
+               snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i);
 
        init_channel_allocations();
 
        return 0;
 }
 
-static int patch_intel_hdmi(struct hda_codec *codec)
-{
-       cvt_nid = 0x02;
-       pin_nid = 0x03;
-       return do_patch_intel_hdmi(codec);
-}
-
-static int patch_intel_hdmi_ibexpeak(struct hda_codec *codec)
-{
-       cvt_nid = 0x02;
-       pin_nid = 0x04;
-       return do_patch_intel_hdmi(codec);
-}
-
 static struct hda_codec_preset snd_hda_preset_intelhdmi[] = {
        { .id = 0x808629fb, .name = "G45 DEVCL",  .patch = patch_intel_hdmi },
        { .id = 0x80862801, .name = "G45 DEVBLC", .patch = patch_intel_hdmi },
        { .id = 0x80862802, .name = "G45 DEVCTG", .patch = patch_intel_hdmi },
        { .id = 0x80862803, .name = "G45 DEVELK", .patch = patch_intel_hdmi },
        { .id = 0x80862804, .name = "G45 DEVIBX", .patch = patch_intel_hdmi },
-       { .id = 0x80860054, .name = "Q57 DEVIBX", .patch = patch_intel_hdmi_ibexpeak },
+       { .id = 0x80860054, .name = "Q57 DEVIBX", .patch = patch_intel_hdmi },
        { .id = 0x10951392, .name = "SiI1392 HDMI",     .patch = patch_intel_hdmi },
        {} /* terminator */
 };
index 9fb60276f5c9d6782d5d00cf030dff7266b91bdf..6afdab09bab77ae9e81737b12f1f81d3d476b9ce 100644 (file)
@@ -397,6 +397,7 @@ static int patch_nvhdmi_2ch(struct hda_codec *codec)
 static struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
        { .id = 0x10de0002, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
        { .id = 0x10de0003, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
+       { .id = 0x10de0005, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
        { .id = 0x10de0006, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
        { .id = 0x10de0007, .name = "MCP7A HDMI", .patch = patch_nvhdmi_8ch },
        { .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
@@ -406,6 +407,7 @@ static struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
 
 MODULE_ALIAS("snd-hda-codec-id:10de0002");
 MODULE_ALIAS("snd-hda-codec-id:10de0003");
+MODULE_ALIAS("snd-hda-codec-id:10de0005");
 MODULE_ALIAS("snd-hda-codec-id:10de0006");
 MODULE_ALIAS("snd-hda-codec-id:10de0007");
 MODULE_ALIAS("snd-hda-codec-id:10de0067");
index 08a5b8a554088caa18f8be4a132e3c2530525638..49de107db16bc860025ac53f24ceff7730eaf3b7 100644 (file)
@@ -965,6 +965,8 @@ static void alc_automute_pin(struct hda_codec *codec)
        unsigned int nid = spec->autocfg.hp_pins[0];
        int i;
 
+       if (!nid)
+               return;
        pincap = snd_hda_query_pin_caps(codec, nid);
        if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */
                snd_hda_codec_read(codec, nid, 0, AC_VERB_SET_PIN_SENSE, 0);
@@ -4324,6 +4326,20 @@ static int add_control(struct alc_spec *spec, int type, const char *name,
        return 0;
 }
 
+static int add_control_with_pfx(struct alc_spec *spec, int type,
+                               const char *pfx, const char *dir,
+                               const char *sfx, unsigned long val)
+{
+       char name[32];
+       snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx);
+       return add_control(spec, type, name, val);
+}
+
+#define add_pb_vol_ctrl(spec, type, pfx, val) \
+       add_control_with_pfx(spec, type, pfx, "Playback", "Volume", val)
+#define add_pb_sw_ctrl(spec, type, pfx, val) \
+       add_control_with_pfx(spec, type, pfx, "Playback", "Switch", val)
+
 #define alc880_is_fixed_pin(nid)       ((nid) >= 0x14 && (nid) <= 0x17)
 #define alc880_fixed_pin_idx(nid)      ((nid) - 0x14)
 #define alc880_is_multi_pin(nid)       ((nid) >= 0x18)
@@ -4377,7 +4393,6 @@ static int alc880_auto_fill_dac_nids(struct alc_spec *spec,
 static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec,
                                             const struct auto_pin_cfg *cfg)
 {
-       char name[32];
        static const char *chname[4] = {
                "Front", "Surround", NULL /*CLFE*/, "Side"
        };
@@ -4390,26 +4405,26 @@ static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec,
                nid = alc880_idx_to_mixer(alc880_dac_to_idx(spec->multiout.dac_nids[i]));
                if (i == 2) {
                        /* Center/LFE */
-                       err = add_control(spec, ALC_CTL_WIDGET_VOL,
-                                         "Center Playback Volume",
+                       err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
+                                             "Center",
                                          HDA_COMPOSE_AMP_VAL(nid, 1, 0,
                                                              HDA_OUTPUT));
                        if (err < 0)
                                return err;
-                       err = add_control(spec, ALC_CTL_WIDGET_VOL,
-                                         "LFE Playback Volume",
+                       err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
+                                             "LFE",
                                          HDA_COMPOSE_AMP_VAL(nid, 2, 0,
                                                              HDA_OUTPUT));
                        if (err < 0)
                                return err;
-                       err = add_control(spec, ALC_CTL_BIND_MUTE,
-                                         "Center Playback Switch",
+                       err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE,
+                                            "Center",
                                          HDA_COMPOSE_AMP_VAL(nid, 1, 2,
                                                              HDA_INPUT));
                        if (err < 0)
                                return err;
-                       err = add_control(spec, ALC_CTL_BIND_MUTE,
-                                         "LFE Playback Switch",
+                       err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE,
+                                            "LFE",
                                          HDA_COMPOSE_AMP_VAL(nid, 2, 2,
                                                              HDA_INPUT));
                        if (err < 0)
@@ -4421,14 +4436,12 @@ static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec,
                                pfx = "Speaker";
                        else
                                pfx = chname[i];
-                       sprintf(name, "%s Playback Volume", pfx);
-                       err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
+                       err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx,
                                          HDA_COMPOSE_AMP_VAL(nid, 3, 0,
                                                              HDA_OUTPUT));
                        if (err < 0)
                                return err;
-                       sprintf(name, "%s Playback Switch", pfx);
-                       err = add_control(spec, ALC_CTL_BIND_MUTE, name,
+                       err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx,
                                          HDA_COMPOSE_AMP_VAL(nid, 3, 2,
                                                              HDA_INPUT));
                        if (err < 0)
@@ -4444,7 +4457,6 @@ static int alc880_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin,
 {
        hda_nid_t nid;
        int err;
-       char name[32];
 
        if (!pin)
                return 0;
@@ -4458,21 +4470,18 @@ static int alc880_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin,
                        spec->multiout.extra_out_nid[0] = nid;
                /* control HP volume/switch on the output mixer amp */
                nid = alc880_idx_to_mixer(alc880_fixed_pin_idx(pin));
-               sprintf(name, "%s Playback Volume", pfx);
-               err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
+               err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx,
                                  HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
                if (err < 0)
                        return err;
-               sprintf(name, "%s Playback Switch", pfx);
-               err = add_control(spec, ALC_CTL_BIND_MUTE, name,
+               err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx,
                                  HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT));
                if (err < 0)
                        return err;
        } else if (alc880_is_multi_pin(pin)) {
                /* set manual connection */
                /* we have only a switch on HP-out PIN */
-               sprintf(name, "%s Playback Switch", pfx);
-               err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+               err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx,
                                  HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
                if (err < 0)
                        return err;
@@ -4485,16 +4494,13 @@ static int new_analog_input(struct alc_spec *spec, hda_nid_t pin,
                            const char *ctlname,
                            int idx, hda_nid_t mix_nid)
 {
-       char name[32];
        int err;
 
-       sprintf(name, "%s Playback Volume", ctlname);
-       err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
+       err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname,
                          HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
        if (err < 0)
                return err;
-       sprintf(name, "%s Playback Switch", ctlname);
-       err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+       err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname,
                          HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
        if (err < 0)
                return err;
@@ -4682,9 +4688,9 @@ static int alc880_parse_auto_config(struct hda_codec *codec)
                        spec->multiout.dig_out_nid = dig_nid;
                else {
                        spec->multiout.slave_dig_outs = spec->slave_dig_outs;
-                       spec->slave_dig_outs[i - 1] = dig_nid;
-                       if (i == ARRAY_SIZE(spec->slave_dig_outs) - 1)
+                       if (i >= ARRAY_SIZE(spec->slave_dig_outs) - 1)
                                break;
+                       spec->slave_dig_outs[i - 1] = dig_nid;
                }
        }
        if (spec->autocfg.dig_in_pin)
@@ -5987,7 +5993,6 @@ static int alc260_add_playback_controls(struct alc_spec *spec, hda_nid_t nid,
 {
        hda_nid_t nid_vol;
        unsigned long vol_val, sw_val;
-       char name[32];
        int err;
 
        if (nid >= 0x0f && nid < 0x11) {
@@ -6007,14 +6012,12 @@ static int alc260_add_playback_controls(struct alc_spec *spec, hda_nid_t nid,
 
        if (!(*vol_bits & (1 << nid_vol))) {
                /* first control for the volume widget */
-               snprintf(name, sizeof(name), "%s Playback Volume", pfx);
-               err = add_control(spec, ALC_CTL_WIDGET_VOL, name, vol_val);
+               err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, vol_val);
                if (err < 0)
                        return err;
                *vol_bits |= (1 << nid_vol);
        }
-       snprintf(name, sizeof(name), "%s Playback Switch", pfx);
-       err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, sw_val);
+       err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, sw_val);
        if (err < 0)
                return err;
        return 1;
@@ -6247,7 +6250,7 @@ static struct snd_pci_quirk alc260_cfg_tbl[] = {
        SND_PCI_QUIRK(0x1025, 0x008f, "Acer", ALC260_ACER),
        SND_PCI_QUIRK(0x1509, 0x4540, "Favorit 100XS", ALC260_FAVORIT100),
        SND_PCI_QUIRK(0x103c, 0x2808, "HP d5700", ALC260_HP_3013),
-       SND_PCI_QUIRK(0x103c, 0x280a, "HP d5750", ALC260_HP_3013),
+       SND_PCI_QUIRK(0x103c, 0x280a, "HP d5750", ALC260_AUTO), /* no quirk */
        SND_PCI_QUIRK(0x103c, 0x3010, "HP", ALC260_HP_3013),
        SND_PCI_QUIRK(0x103c, 0x3011, "HP", ALC260_HP_3013),
        SND_PCI_QUIRK(0x103c, 0x3012, "HP", ALC260_HP_DC7600),
@@ -8909,10 +8912,11 @@ static struct snd_pci_quirk alc882_ssid_cfg_tbl[] = {
        SND_PCI_QUIRK(0x106b, 0x3800, "MacbookPro 4,1", ALC885_MBP3),
        SND_PCI_QUIRK(0x106b, 0x3e00, "iMac 24 Aluminum", ALC885_IMAC24),
        SND_PCI_QUIRK(0x106b, 0x3f00, "Macbook 5,1", ALC885_MB5),
-       /* FIXME: HP jack sense seems not working for MBP 5,1, so apparently
-        * no perfect solution yet
+       /* FIXME: HP jack sense seems not working for MBP 5,1 or 5,2,
+        * so apparently no perfect solution yet
         */
        SND_PCI_QUIRK(0x106b, 0x4000, "MacbookPro 5,1", ALC885_MB5),
+       SND_PCI_QUIRK(0x106b, 0x4600, "MacbookPro 5,2", ALC885_MB5),
        {} /* terminator */
 };
 
@@ -9811,9 +9815,9 @@ static int alc882_parse_auto_config(struct hda_codec *codec)
                        spec->multiout.dig_out_nid = dig_nid;
                else {
                        spec->multiout.slave_dig_outs = spec->slave_dig_outs;
-                       spec->slave_dig_outs[i - 1] = dig_nid;
-                       if (i == ARRAY_SIZE(spec->slave_dig_outs) - 1)
+                       if (i >= ARRAY_SIZE(spec->slave_dig_outs) - 1)
                                break;
+                       spec->slave_dig_outs[i - 1] = dig_nid;
                }
        }
        if (spec->autocfg.dig_in_pin)
@@ -10953,7 +10957,6 @@ static int alc262_check_volbit(hda_nid_t nid)
 static int alc262_add_out_vol_ctl(struct alc_spec *spec, hda_nid_t nid,
                                  const char *pfx, int *vbits)
 {
-       char name[32];
        unsigned long val;
        int vbit;
 
@@ -10963,28 +10966,25 @@ static int alc262_add_out_vol_ctl(struct alc_spec *spec, hda_nid_t nid,
        if (*vbits & vbit) /* a volume control for this mixer already there */
                return 0;
        *vbits |= vbit;
-       snprintf(name, sizeof(name), "%s Playback Volume", pfx);
        if (vbit == 2)
                val = HDA_COMPOSE_AMP_VAL(0x0e, 2, 0, HDA_OUTPUT);
        else
                val = HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT);
-       return add_control(spec, ALC_CTL_WIDGET_VOL, name, val);
+       return add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, val);
 }
 
 static int alc262_add_out_sw_ctl(struct alc_spec *spec, hda_nid_t nid,
                                 const char *pfx)
 {
-       char name[32];
        unsigned long val;
 
        if (!nid)
                return 0;
-       snprintf(name, sizeof(name), "%s Playback Switch", pfx);
        if (nid == 0x16)
                val = HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT);
        else
                val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
-       return add_control(spec, ALC_CTL_WIDGET_MUTE, name, val);
+       return add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, val);
 }
 
 /* add playback controls from the parsed DAC table */
@@ -11458,6 +11458,7 @@ static struct snd_pci_quirk alc262_cfg_tbl[] = {
        SND_PCI_QUIRK(0x104d, 0x820f, "Sony ASSAMD", ALC262_SONY_ASSAMD),
        SND_PCI_QUIRK(0x104d, 0x9016, "Sony VAIO", ALC262_AUTO), /* dig-only */
        SND_PCI_QUIRK(0x104d, 0x9025, "Sony VAIO Z21MN", ALC262_TOSHIBA_S06),
+       SND_PCI_QUIRK(0x104d, 0x9035, "Sony VAIO VGN-FW170J", ALC262_AUTO),
        SND_PCI_QUIRK_MASK(0x104d, 0xff00, 0x9000, "Sony VAIO",
                           ALC262_SONY_ASSAMD),
        SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba dynabook SS RX1",
@@ -12322,11 +12323,9 @@ static struct snd_kcontrol_new alc268_test_mixer[] = {
 static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid,
                                    const char *ctlname, int idx)
 {
-       char name[32];
        hda_nid_t dac;
        int err;
 
-       sprintf(name, "%s Playback Volume", ctlname);
        switch (nid) {
        case 0x14:
        case 0x16:
@@ -12340,7 +12339,7 @@ static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid,
        }
        if (spec->multiout.dac_nids[0] != dac &&
            spec->multiout.dac_nids[1] != dac) {
-               err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
+               err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname,
                                  HDA_COMPOSE_AMP_VAL(dac, 3, idx,
                                                      HDA_OUTPUT));
                if (err < 0)
@@ -12348,12 +12347,11 @@ static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid,
                spec->multiout.dac_nids[spec->multiout.num_dacs++] = dac;
        }
 
-       sprintf(name, "%s Playback Switch", ctlname);
        if (nid != 0x16)
-               err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+               err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname,
                          HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_OUTPUT));
        else /* mono */
-               err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+               err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname,
                          HDA_COMPOSE_AMP_VAL(nid, 2, idx, HDA_OUTPUT));
        if (err < 0)
                return err;
@@ -12383,8 +12381,7 @@ static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec,
 
        nid = cfg->speaker_pins[0];
        if (nid == 0x1d) {
-               err = add_control(spec, ALC_CTL_WIDGET_VOL,
-                                 "Speaker Playback Volume",
+               err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, "Speaker",
                                  HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
                if (err < 0)
                        return err;
@@ -12402,8 +12399,7 @@ static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec,
 
        nid = cfg->line_out_pins[1] | cfg->line_out_pins[2];
        if (nid == 0x16) {
-               err = add_control(spec, ALC_CTL_WIDGET_MUTE,
-                                 "Mono Playback Switch",
+               err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, "Mono",
                                  HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT));
                if (err < 0)
                        return err;
@@ -12602,7 +12598,8 @@ static struct snd_pci_quirk alc268_cfg_tbl[] = {
        SND_PCI_QUIRK(0x1025, 0x015b, "Acer Aspire One",
                                                ALC268_ACER_ASPIRE_ONE),
        SND_PCI_QUIRK(0x1028, 0x0253, "Dell OEM", ALC268_DELL),
-       SND_PCI_QUIRK(0x1028, 0x02b0, "Dell Inspiron Mini9", ALC268_DELL),
+       SND_PCI_QUIRK_MASK(0x1028, 0xfff0, 0x02b0,
+                       "Dell Inspiron Mini9/Vostro A90", ALC268_DELL),
        /* almost compatible with toshiba but with optional digital outs;
         * auto-probing seems working fine
         */
@@ -14254,9 +14251,7 @@ static int alc861_auto_fill_dac_nids(struct hda_codec *codec,
 static int alc861_create_out_sw(struct hda_codec *codec, const char *pfx,
                                hda_nid_t nid, unsigned int chs)
 {
-       char name[32];
-       snprintf(name, sizeof(name), "%s Playback Switch", pfx);
-       return add_control(codec->spec, ALC_CTL_WIDGET_MUTE, name,
+       return add_pb_sw_ctrl(codec->spec, ALC_CTL_WIDGET_MUTE, pfx,
                           HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
 }
 
@@ -15380,7 +15375,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)
 {
-       char name[32];
        static const char *chname[4] = {"Front", "Surround", "CLFE", "Side"};
        hda_nid_t nid_v, nid_s;
        int i, err;
@@ -15397,26 +15391,26 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec,
 
                if (i == 2) {
                        /* Center/LFE */
-                       err = add_control(spec, ALC_CTL_WIDGET_VOL,
-                                         "Center Playback Volume",
+                       err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
+                                             "Center",
                                          HDA_COMPOSE_AMP_VAL(nid_v, 1, 0,
                                                              HDA_OUTPUT));
                        if (err < 0)
                                return err;
-                       err = add_control(spec, ALC_CTL_WIDGET_VOL,
-                                         "LFE Playback Volume",
+                       err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
+                                             "LFE",
                                          HDA_COMPOSE_AMP_VAL(nid_v, 2, 0,
                                                              HDA_OUTPUT));
                        if (err < 0)
                                return err;
-                       err = add_control(spec, ALC_CTL_BIND_MUTE,
-                                         "Center Playback Switch",
+                       err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE,
+                                            "Center",
                                          HDA_COMPOSE_AMP_VAL(nid_s, 1, 2,
                                                              HDA_INPUT));
                        if (err < 0)
                                return err;
-                       err = add_control(spec, ALC_CTL_BIND_MUTE,
-                                         "LFE Playback Switch",
+                       err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE,
+                                            "LFE",
                                          HDA_COMPOSE_AMP_VAL(nid_s, 2, 2,
                                                              HDA_INPUT));
                        if (err < 0)
@@ -15431,8 +15425,7 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec,
                                        pfx = "PCM";
                        } else
                                pfx = chname[i];
-                       sprintf(name, "%s Playback Volume", pfx);
-                       err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
+                       err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx,
                                          HDA_COMPOSE_AMP_VAL(nid_v, 3, 0,
                                                              HDA_OUTPUT));
                        if (err < 0)
@@ -15440,8 +15433,7 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec,
                        if (cfg->line_outs == 1 &&
                            cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
                                pfx = "Speaker";
-                       sprintf(name, "%s Playback Switch", pfx);
-                       err = add_control(spec, ALC_CTL_BIND_MUTE, name,
+                       err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx,
                                          HDA_COMPOSE_AMP_VAL(nid_s, 3, 2,
                                                              HDA_INPUT));
                        if (err < 0)
@@ -15459,7 +15451,6 @@ static int alc861vd_auto_create_extra_out(struct alc_spec *spec,
 {
        hda_nid_t nid_v, nid_s;
        int err;
-       char name[32];
 
        if (!pin)
                return 0;
@@ -15477,21 +15468,18 @@ static int alc861vd_auto_create_extra_out(struct alc_spec *spec,
                nid_s = alc861vd_idx_to_mixer_switch(
                                alc880_fixed_pin_idx(pin));
 
-               sprintf(name, "%s Playback Volume", pfx);
-               err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
+               err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx,
                                  HDA_COMPOSE_AMP_VAL(nid_v, 3, 0, HDA_OUTPUT));
                if (err < 0)
                        return err;
-               sprintf(name, "%s Playback Switch", pfx);
-               err = add_control(spec, ALC_CTL_BIND_MUTE, name,
+               err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx,
                                  HDA_COMPOSE_AMP_VAL(nid_s, 3, 2, HDA_INPUT));
                if (err < 0)
                        return err;
        } else if (alc880_is_multi_pin(pin)) {
                /* set manual connection */
                /* we have only a switch on HP-out PIN */
-               sprintf(name, "%s Playback Switch", pfx);
-               err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+               err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx,
                                  HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
                if (err < 0)
                        return err;
@@ -17258,21 +17246,17 @@ static int alc662_auto_fill_dac_nids(struct hda_codec *codec,
        return 0;
 }
 
-static int alc662_add_vol_ctl(struct alc_spec *spec, const char *pfx,
+static inline int alc662_add_vol_ctl(struct alc_spec *spec, const char *pfx,
                              hda_nid_t nid, unsigned int chs)
 {
-       char name[32];
-       sprintf(name, "%s Playback Volume", pfx);
-       return add_control(spec, ALC_CTL_WIDGET_VOL, name,
+       return add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx,
                           HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
 }
 
-static int alc662_add_sw_ctl(struct alc_spec *spec, const char *pfx,
+static inline int alc662_add_sw_ctl(struct alc_spec *spec, const char *pfx,
                             hda_nid_t nid, unsigned int chs)
 {
-       char name[32];
-       sprintf(name, "%s Playback Switch", pfx);
-       return add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+       return add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx,
                           HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_INPUT));
 }
 
@@ -17350,13 +17334,11 @@ static int alc662_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
                return 0;
        nid = alc662_look_for_dac(codec, pin);
        if (!nid) {
-               char name[32];
                /* the corresponding DAC is already occupied */
                if (!(get_wcaps(codec, pin) & AC_WCAP_OUT_AMP))
                        return 0; /* no way */
                /* create a switch only */
-               sprintf(name, "%s Playback Switch", pfx);
-               return add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+               return add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx,
                                   HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
        }
 
@@ -17374,7 +17356,7 @@ static int alc662_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
 
 /* create playback/capture controls for input pins */
 #define alc662_auto_create_input_ctls \
-       alc880_auto_create_input_ctls
+       alc882_auto_create_input_ctls
 
 static void alc662_auto_set_output_and_unmute(struct hda_codec *codec,
                                              hda_nid_t nid, int pin_type,
index 426edfa476a2e87e72848e49aabbec6309cfbdc8..8d65d2b25234972cec830fb1a248cc86356260fa 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/pci.h>
+#include <linux/dmi.h>
 #include <sound/core.h>
 #include <sound/asoundef.h>
 #include <sound/jack.h>
@@ -1589,6 +1590,8 @@ static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = {
                                "Dell Studio 17", STAC_DELL_M6_DMIC),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02be,
                                "Dell Studio 1555", STAC_DELL_M6_DMIC),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02bd,
+                               "Dell Studio 1557", STAC_DELL_M6_DMIC),
        {} /* terminator */
 };
 
@@ -1693,6 +1696,8 @@ static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
                      "DFI LanParty", STAC_92HD71BXX_REF),
        SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30fb,
                      "HP dv4-1222nr", STAC_HP_DV4_1222NR),
+       SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x1720,
+                         "HP", STAC_HP_DV5),
        SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3080,
                      "HP", STAC_HP_DV5),
        SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x30f0,
@@ -4324,6 +4329,28 @@ static void stac92xx_free_kctls(struct hda_codec *codec)
        snd_array_free(&spec->kctls);
 }
 
+static void stac92xx_shutup(struct hda_codec *codec)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       int i;
+       hda_nid_t nid;
+
+       /* reset each pin before powering down DAC/ADC to avoid click noise */
+       nid = codec->start_nid;
+       for (i = 0; i < codec->num_nodes; i++, nid++) {
+               unsigned int wcaps = get_wcaps(codec, nid);
+               unsigned int wid_type = get_wcaps_type(wcaps);
+               if (wid_type == AC_WID_PIN)
+                       snd_hda_codec_read(codec, nid, 0,
+                               AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
+       }
+
+       if (spec->eapd_mask)
+               stac_gpio_set(codec, spec->gpio_mask,
+                               spec->gpio_dir, spec->gpio_data &
+                               ~spec->eapd_mask);
+}
+
 static void stac92xx_free(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec = codec->spec;
@@ -4331,6 +4358,7 @@ static void stac92xx_free(struct hda_codec *codec)
        if (! spec)
                return;
 
+       stac92xx_shutup(codec);
        stac92xx_free_jacks(codec);
        snd_array_free(&spec->events);
 
@@ -4665,6 +4693,26 @@ static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
        }
 }
 
+static int hp_bseries_system(u32 subsystem_id)
+{
+       switch (subsystem_id) {
+       case 0x103c307e:
+       case 0x103c307f:
+       case 0x103c3080:
+       case 0x103c3081:
+       case 0x103c1722:
+       case 0x103c1723:
+       case 0x103c1724:
+       case 0x103c1725:
+       case 0x103c1726:
+       case 0x103c1727:
+       case 0x103c1728:
+       case 0x103c1729:
+               return 1;
+       }
+       return 0;
+}
+
 #ifdef CONFIG_PROC_FS
 static void stac92hd_proc_hook(struct snd_info_buffer *buffer,
                               struct hda_codec *codec, hda_nid_t nid)
@@ -4754,6 +4802,11 @@ static int stac92xx_hp_check_power_status(struct hda_codec *codec,
                else
                        spec->gpio_data |= spec->gpio_led; /* white */
 
+               if (hp_bseries_system(codec->subsystem_id)) {
+                       /* LED state is inverted on these systems */
+                       spec->gpio_data ^= spec->gpio_led;
+               }
+
                stac_gpio_set(codec, spec->gpio_mask,
                              spec->gpio_dir,
                              spec->gpio_data);
@@ -4765,24 +4818,7 @@ static int stac92xx_hp_check_power_status(struct hda_codec *codec,
 
 static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state)
 {
-       struct sigmatel_spec *spec = codec->spec;
-       int i;
-       hda_nid_t nid;
-
-       /* reset each pin before powering down DAC/ADC to avoid click noise */
-       nid = codec->start_nid;
-       for (i = 0; i < codec->num_nodes; i++, nid++) {
-               unsigned int wcaps = get_wcaps(codec, nid);
-               unsigned int wid_type = get_wcaps_type(wcaps);
-               if (wid_type == AC_WID_PIN)
-                       snd_hda_codec_read(codec, nid, 0,
-                               AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
-       }
-
-       if (spec->eapd_mask)
-               stac_gpio_set(codec, spec->gpio_mask,
-                               spec->gpio_dir, spec->gpio_data &
-                               ~spec->eapd_mask);
+       stac92xx_shutup(codec);
        return 0;
 }
 #endif
@@ -4797,6 +4833,7 @@ static struct hda_codec_ops stac92xx_patch_ops = {
        .suspend = stac92xx_suspend,
        .resume = stac92xx_resume,
 #endif
+       .reboot_notify = stac92xx_shutup,
 };
 
 static int patch_stac9200(struct hda_codec *codec)
@@ -5243,6 +5280,7 @@ static int patch_stac92hd71bxx(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec;
        struct hda_verb *unmute_init = stac92hd71bxx_unmute_core_init;
+       unsigned int pin_cfg;
        int err = 0;
 
        spec  = kzalloc(sizeof(*spec), GFP_KERNEL);
@@ -5426,6 +5464,45 @@ again:
                break;
        }
 
+       if (hp_bseries_system(codec->subsystem_id)) {
+               pin_cfg = snd_hda_codec_get_pincfg(codec, 0x0f);
+               if (get_defcfg_device(pin_cfg) == AC_JACK_LINE_OUT ||
+                       get_defcfg_device(pin_cfg) == AC_JACK_SPEAKER  ||
+                       get_defcfg_device(pin_cfg) == AC_JACK_HP_OUT) {
+                       /* It was changed in the BIOS to just satisfy MS DTM.
+                        * Lets turn it back into slaved HP
+                        */
+                       pin_cfg = (pin_cfg & (~AC_DEFCFG_DEVICE))
+                                       | (AC_JACK_HP_OUT <<
+                                               AC_DEFCFG_DEVICE_SHIFT);
+                       pin_cfg = (pin_cfg & (~(AC_DEFCFG_DEF_ASSOC
+                                                       | AC_DEFCFG_SEQUENCE)))
+                                                               | 0x1f;
+                       snd_hda_codec_set_pincfg(codec, 0x0f, pin_cfg);
+               }
+       }
+
+       if ((codec->subsystem_id >> 16) == PCI_VENDOR_ID_HP) {
+               const struct dmi_device *dev = NULL;
+               while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING,
+                                             NULL, dev))) {
+                       if (strcmp(dev->name, "HP_Mute_LED_1")) {
+                               switch (codec->vendor_id) {
+                               case 0x111d7608:
+                                       spec->gpio_led = 0x01;
+                                       break;
+                               case 0x111d7600:
+                               case 0x111d7601:
+                               case 0x111d7602:
+                               case 0x111d7603:
+                                       spec->gpio_led = 0x08;
+                                       break;
+                               }
+                               break;
+                       }
+               }
+       }
+
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        if (spec->gpio_led) {
                spec->gpio_mask |= spec->gpio_led;
index ee89db90c9b62913758a480dd2adb228aa69444d..5a856009c916b8f466931d5029456ece0ed82f41 100644 (file)
@@ -1,10 +1,10 @@
 /*
  * Universal Interface for Intel High Definition Audio Codec
  *
- * HD audio interface patch for VIA VT1702/VT1708/VT1709 codec
+ * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
  *
- * Copyright (c) 2006-2008 Lydia Wang <lydiawang@viatech.com>
- *                        Takashi Iwai <tiwai@suse.de>
+ *  (C) 2006-2009 VIA Technology, Inc.
+ *  (C) 2006-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
  */
 
 /* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
-/*                                                                           */
+/*                                                                          */
 /* 2006-03-03  Lydia Wang  Create the basic patch to support VT1708 codec    */
-/* 2006-03-14  Lydia Wang  Modify hard code for some pin widget nid          */
-/* 2006-08-02  Lydia Wang  Add support to VT1709 codec                       */
+/* 2006-03-14  Lydia Wang  Modify hard code for some pin widget nid         */
+/* 2006-08-02  Lydia Wang  Add support to VT1709 codec                      */
 /* 2006-09-08  Lydia Wang  Fix internal loopback recording source select bug */
-/* 2007-09-12  Lydia Wang  Add EAPD enable during driver initialization      */
-/* 2007-09-17  Lydia Wang  Add VT1708B codec support                        */
+/* 2007-09-12  Lydia Wang  Add EAPD enable during driver initialization             */
+/* 2007-09-17  Lydia Wang  Add VT1708B codec support                       */
 /* 2007-11-14  Lydia Wang  Add VT1708A codec HP and CD pin connect config    */
 /* 2008-02-03  Lydia Wang  Fix Rear channels and Back channels inverse issue */
-/* 2008-03-06  Lydia Wang  Add VT1702 codec and VT1708S codec support        */
-/* 2008-04-09  Lydia Wang  Add mute front speaker when HP plugin             */
-/* 2008-04-09  Lydia Wang  Add Independent HP feature                        */
+/* 2008-03-06  Lydia Wang  Add VT1702 codec and VT1708S codec support       */
+/* 2008-04-09  Lydia Wang  Add mute front speaker when HP plugin            */
+/* 2008-04-09  Lydia Wang  Add Independent HP feature                       */
 /* 2008-05-28  Lydia Wang  Add second S/PDIF Out support for VT1702         */
-/* 2008-09-15  Logan Li    Add VT1708S Mic Boost workaround/backdoor        */
-/*                                                                           */
+/* 2008-09-15  Logan Li           Add VT1708S Mic Boost workaround/backdoor         */
+/* 2009-02-16  Logan Li           Add support for VT1718S                           */
+/* 2009-03-13  Logan Li           Add support for VT1716S                           */
+/* 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                          */
+/*                                                                          */
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
 
 #define VT1702_HP_NID          0x17
 #define VT1702_DIGOUT_NID      0x11
 
-#define IS_VT1708_VENDORID(x)          ((x) >= 0x11061708 && (x) <= 0x1106170b)
-#define IS_VT1709_10CH_VENDORID(x)     ((x) >= 0x1106e710 && (x) <= 0x1106e713)
-#define IS_VT1709_6CH_VENDORID(x)      ((x) >= 0x1106e714 && (x) <= 0x1106e717)
-#define IS_VT1708B_8CH_VENDORID(x)     ((x) >= 0x1106e720 && (x) <= 0x1106e723)
-#define IS_VT1708B_4CH_VENDORID(x)     ((x) >= 0x1106e724 && (x) <= 0x1106e727)
-#define IS_VT1708S_VENDORID(x)         ((x) >= 0x11060397 && (x) <= 0x11067397)
-#define IS_VT1702_VENDORID(x)          ((x) >= 0x11060398 && (x) <= 0x11067398)
-
 enum VIA_HDA_CODEC {
        UNKNOWN = -1,
        VT1708,
@@ -92,12 +90,76 @@ enum VIA_HDA_CODEC {
        VT1708B_8CH,
        VT1708B_4CH,
        VT1708S,
+       VT1708BCE,
        VT1702,
+       VT1718S,
+       VT1716S,
+       VT2002P,
+       VT1812,
        CODEC_TYPES,
 };
 
-static enum VIA_HDA_CODEC get_codec_type(u32 vendor_id)
+struct via_spec {
+       /* codec parameterization */
+       struct snd_kcontrol_new *mixers[6];
+       unsigned int num_mixers;
+
+       struct hda_verb *init_verbs[5];
+       unsigned int num_iverbs;
+
+       char *stream_name_analog;
+       struct hda_pcm_stream *stream_analog_playback;
+       struct hda_pcm_stream *stream_analog_capture;
+
+       char *stream_name_digital;
+       struct hda_pcm_stream *stream_digital_playback;
+       struct hda_pcm_stream *stream_digital_capture;
+
+       /* playback */
+       struct hda_multi_out multiout;
+       hda_nid_t slave_dig_outs[2];
+
+       /* capture */
+       unsigned int num_adc_nids;
+       hda_nid_t *adc_nids;
+       hda_nid_t mux_nids[3];
+       hda_nid_t dig_in_nid;
+       hda_nid_t dig_in_pin;
+
+       /* capture source */
+       const struct hda_input_mux *input_mux;
+       unsigned int cur_mux[3];
+
+       /* 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;
+       enum VIA_HDA_CODEC codec_type;
+
+       /* work to check hp jack state */
+       struct hda_codec *codec;
+       struct delayed_work vt1708_hp_work;
+       int vt1708_jack_detectect;
+       int vt1708_hp_present;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       struct hda_loopback_check loopback;
+#endif
+};
+
+static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
 {
+       u32 vendor_id = codec->vendor_id;
        u16 ven_id = vendor_id >> 16;
        u16 dev_id = vendor_id & 0xffff;
        enum VIA_HDA_CODEC codec_type;
@@ -111,9 +173,11 @@ static enum VIA_HDA_CODEC get_codec_type(u32 vendor_id)
                codec_type = VT1709_10CH;
        else if (dev_id >= 0xe714 && dev_id <= 0xe717)
                codec_type = VT1709_6CH;
-       else if (dev_id >= 0xe720 && dev_id <= 0xe723)
+       else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
                codec_type = VT1708B_8CH;
-       else if (dev_id >= 0xe724 && dev_id <= 0xe727)
+               if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
+                       codec_type = VT1708BCE;
+       } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
                codec_type = VT1708B_4CH;
        else if ((dev_id & 0xfff) == 0x397
                 && (dev_id >> 12) < 8)
@@ -121,6 +185,19 @@ static enum VIA_HDA_CODEC get_codec_type(u32 vendor_id)
        else if ((dev_id & 0xfff) == 0x398
                 && (dev_id >> 12) < 8)
                codec_type = VT1702;
+       else if ((dev_id & 0xfff) == 0x428
+                && (dev_id >> 12) < 8)
+               codec_type = VT1718S;
+       else if (dev_id == 0x0433 || dev_id == 0xa721)
+               codec_type = VT1716S;
+       else if (dev_id == 0x0441 || dev_id == 0x4441)
+               codec_type = VT1718S;
+       else if (dev_id == 0x0438 || dev_id == 0x4438)
+               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;
@@ -128,10 +205,16 @@ static enum VIA_HDA_CODEC get_codec_type(u32 vendor_id)
 
 #define VIA_HP_EVENT           0x01
 #define VIA_GPIO_EVENT         0x02
+#define VIA_JACK_EVENT         0x04
+#define VIA_MONO_EVENT         0x08
+#define VIA_SPEAKER_EVENT      0x10
+#define VIA_BIND_HP_EVENT      0x20
 
 enum {
        VIA_CTL_WIDGET_VOL,
        VIA_CTL_WIDGET_MUTE,
+       VIA_CTL_WIDGET_ANALOG_MUTE,
+       VIA_CTL_WIDGET_BIND_PIN_MUTE,
 };
 
 enum {
@@ -141,99 +224,162 @@ enum {
        AUTO_SEQ_SIDE
 };
 
-/* Some VT1708S based boards gets the micboost setting wrong, so we have
- * to apply some brute-force and re-write the TLV's by software. */
-static int mic_boost_tlv(struct snd_kcontrol *kcontrol, int op_flag,
-                        unsigned int size, unsigned int __user *_tlv)
+static void analog_low_current_mode(struct hda_codec *codec, int stream_idle);
+static void set_jack_power_state(struct hda_codec *codec);
+static int is_aa_path_mute(struct hda_codec *codec);
+
+static void vt1708_start_hp_work(struct via_spec *spec)
 {
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       hda_nid_t nid = get_amp_nid(kcontrol);
+       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);
+       if (!delayed_work_pending(&spec->vt1708_hp_work))
+               schedule_delayed_work(&spec->vt1708_hp_work,
+                                     msecs_to_jiffies(100));
+}
 
-       if (get_codec_type(codec->vendor_id) == VT1708S
-           && (nid == 0x1a || nid == 0x1e)) {
-               if (size < 4 * sizeof(unsigned int))
-                       return -ENOMEM;
-               if (put_user(1, _tlv))  /* SNDRV_CTL_TLVT_DB_SCALE */
-                       return -EFAULT;
-               if (put_user(2 * sizeof(unsigned int), _tlv + 1))
-                       return -EFAULT;
-               if (put_user(0, _tlv + 2)) /* offset = 0 */
-                       return -EFAULT;
-               if (put_user(1000, _tlv + 3)) /* step size = 10 dB */
-                       return -EFAULT;
-       }
-       return 0;
+static void vt1708_stop_hp_work(struct via_spec *spec)
+{
+       if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
+               return;
+       if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
+           && !is_aa_path_mute(spec->codec))
+               return;
+       snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
+                           !spec->vt1708_jack_detectect);
+       cancel_delayed_work(&spec->vt1708_hp_work);
+       flush_scheduled_work();
 }
 
-static int mic_boost_volume_info(struct snd_kcontrol *kcontrol,
-                                struct snd_ctl_elem_info *uinfo)
+
+static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
 {
+       int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       hda_nid_t nid = get_amp_nid(kcontrol);
 
-       if (get_codec_type(codec->vendor_id) == VT1708S
-           && (nid == 0x1a || nid == 0x1e)) {
-               uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-               uinfo->count = 2;
-               uinfo->value.integer.min = 0;
-               uinfo->value.integer.max = 3;
+       set_jack_power_state(codec);
+       analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1);
+       if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
+               if (is_aa_path_mute(codec))
+                       vt1708_start_hp_work(codec->spec);
+               else
+                       vt1708_stop_hp_work(codec->spec);
        }
-       return 0;
+       return change;
 }
 
-static struct snd_kcontrol_new vt1708_control_templates[] = {
-       HDA_CODEC_VOLUME(NULL, 0, 0, 0),
-       HDA_CODEC_MUTE(NULL, 0, 0, 0),
-};
-
-
-struct via_spec {
-       /* codec parameterization */
-       struct snd_kcontrol_new *mixers[3];
-       unsigned int num_mixers;
-
-       struct hda_verb *init_verbs[5];
-       unsigned int num_iverbs;
-
-       char *stream_name_analog;
-       struct hda_pcm_stream *stream_analog_playback;
-       struct hda_pcm_stream *stream_analog_capture;
-
-       char *stream_name_digital;
-       struct hda_pcm_stream *stream_digital_playback;
-       struct hda_pcm_stream *stream_digital_capture;
+/* modify .put = snd_hda_mixer_amp_switch_put */
+#define ANALOG_INPUT_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 = analog_input_switch_put,                 \
+                       .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
 
-       /* playback */
-       struct hda_multi_out multiout;
-       hda_nid_t slave_dig_outs[2];
+static void via_hp_bind_automute(struct hda_codec *codec);
 
-       /* capture */
-       unsigned int num_adc_nids;
-       hda_nid_t *adc_nids;
-       hda_nid_t mux_nids[3];
-       hda_nid_t dig_in_nid;
-       hda_nid_t dig_in_pin;
+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;
 
-       /* capture source */
-       const struct hda_input_mux *input_mux;
-       unsigned int cur_mux[3];
+       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);
+               }
+       }
 
-       /* PCM information */
-       struct hda_pcm pcm_rec[3];
+       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);
 
-       /* 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];
+       } 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;
+}
 
-       /* HP mode source */
-       const struct hda_input_mux *hp_mux;
-       unsigned int hp_independent_mode;
+#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) }
 
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-       struct hda_loopback_check loopback;
-#endif
+static 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 hda_nid_t vt1708_adc_nids[2] = {
@@ -261,6 +407,27 @@ static hda_nid_t vt1702_adc_nids[3] = {
        0x12, 0x20, 0x1F
 };
 
+static hda_nid_t vt1718S_adc_nids[2] = {
+       /* ADC1-2 */
+       0x10, 0x11
+};
+
+static hda_nid_t vt1716S_adc_nids[2] = {
+       /* ADC1-2 */
+       0x13, 0x14
+};
+
+static hda_nid_t vt2002P_adc_nids[2] = {
+       /* ADC1-2 */
+       0x10, 0x11
+};
+
+static hda_nid_t vt1812_adc_nids[2] = {
+       /* ADC1-2 */
+       0x10, 0x11
+};
+
+
 /* add dynamic controls */
 static int via_add_control(struct via_spec *spec, int type, const char *name,
                           unsigned long val)
@@ -271,7 +438,7 @@ static int via_add_control(struct via_spec *spec, int type, const char *name,
        knew = snd_array_new(&spec->kctls);
        if (!knew)
                return -ENOMEM;
-       *knew = vt1708_control_templates[type];
+       *knew = via_control_templates[type];
        knew->name = kstrdup(name, GFP_KERNEL);
        if (!knew->name)
                return -ENOMEM;
@@ -293,8 +460,8 @@ 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, hda_nid_t pin,
-                               const char *ctlname, int idx, int mix_nid)
+static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
+                               int idx, int mix_nid)
 {
        char name[32];
        int err;
@@ -305,7 +472,7 @@ static int via_new_analog_input(struct via_spec *spec, hda_nid_t pin,
        if (err < 0)
                return err;
        sprintf(name, "%s Playback Switch", ctlname);
-       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
+       err = via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name,
                              HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
        if (err < 0)
                return err;
@@ -322,7 +489,7 @@ static void via_auto_set_output_and_unmute(struct hda_codec *codec,
        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, 
+               snd_hda_codec_write(codec, nid, 0,
                                    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
 }
 
@@ -343,10 +510,13 @@ static void via_auto_init_hp_out(struct hda_codec *codec)
 {
        struct via_spec *spec = codec->spec;
        hda_nid_t pin;
+       int i;
 
-       pin = spec->autocfg.hp_pins[0];
-       if (pin) /* connect to front */
-               via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
+       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);
+       }
 }
 
 static void via_auto_init_analog_input(struct hda_codec *codec)
@@ -364,6 +534,510 @@ static void via_auto_init_analog_input(struct hda_codec *codec)
 
        }
 }
+
+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)
+{
+       unsigned parm;
+       unsigned def_conf = snd_hda_codec_get_pincfg(codec, 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;
+       struct via_spec *spec = codec->spec;
+       if ((spec->smart51_enabled && is_smart51_pins(spec, nid))
+           || ((no_presence || present)
+               && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
+               *affected_parm = AC_PWRST_D0; /* if it's connected */
+               parm = AC_PWRST_D0;
+       } else
+               parm = AC_PWRST_D3;
+
+       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
+}
+
+static void set_jack_power_state(struct hda_codec *codec)
+{
+       struct via_spec *spec = codec->spec;
+       int imux_is_smixer;
+       unsigned int parm;
+
+       if (spec->codec_type == VT1702) {
+               imux_is_smixer = snd_hda_codec_read(
+                       codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
+               /* 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 = 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, 0x16, &parm);
+               set_pin_power_state(codec, 0x17, &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);
+       } else if (spec->codec_type == VT1708B_8CH
+                  || spec->codec_type == VT1708B_4CH
+                  || spec->codec_type == VT1708S) {
+               /* SW0 (17h) = stereo mixer */
+               int is_8ch = spec->codec_type != VT1708B_4CH;
+               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);
+               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);
+                       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);
+               }
+
+               /* 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 (spec->codec_type == VT1718S) {
+               /* MUX6 (1eh) = stereo mixer */
+               imux_is_smixer = snd_hda_codec_read(
+                       codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
+               /* inputs */
+               /* PW 5/6/7 (29h/2ah/2bh) */
+               parm = AC_PWRST_D3;
+               set_pin_power_state(codec, 0x29, &parm);
+               set_pin_power_state(codec, 0x2a, &parm);
+               set_pin_power_state(codec, 0x2b, &parm);
+               if (imux_is_smixer)
+                       parm = AC_PWRST_D0;
+               /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
+               snd_hda_codec_write(codec, 0x1e, 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, 0x10, 0, AC_VERB_SET_POWER_STATE,
+                                   parm);
+               snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
+                                   parm);
+
+               /* outputs */
+               /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
+               parm = AC_PWRST_D3;
+               set_pin_power_state(codec, 0x27, &parm);
+               snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
+                                   parm);
+               snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE,
+                                   parm);
+
+               /* PW2 (26h), AOW2 (ah) */
+               parm = AC_PWRST_D3;
+               set_pin_power_state(codec, 0x26, &parm);
+               snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE,
+                                   parm);
+
+               /* PW0/1 (24h/25h) */
+               parm = AC_PWRST_D3;
+               set_pin_power_state(codec, 0x24, &parm);
+               set_pin_power_state(codec, 0x25, &parm);
+               if (!spec->hp_independent_mode) /* check for redirected HP */
+                       set_pin_power_state(codec, 0x28, &parm);
+               snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE,
+                                   parm);
+               snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE,
+                                   parm);
+               /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
+               snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
+                                   imux_is_smixer ? AC_PWRST_D0 : parm);
+               if (spec->hp_independent_mode) {
+                       /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
+                       parm = AC_PWRST_D3;
+                       set_pin_power_state(codec, 0x28, &parm);
+                       snd_hda_codec_write(codec, 0x1b, 0,
+                                           AC_VERB_SET_POWER_STATE, parm);
+                       snd_hda_codec_write(codec, 0x34, 0,
+                                           AC_VERB_SET_POWER_STATE, parm);
+                       snd_hda_codec_write(codec, 0xc, 0,
+                                           AC_VERB_SET_POWER_STATE, parm);
+               }
+       } else if (spec->codec_type == VT1716S) {
+               unsigned int mono_out, present;
+               /* SW0 (17h) = stereo mixer */
+               imux_is_smixer = snd_hda_codec_read(
+                       codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) ==  5;
+               /* 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), AIW0(13h) */
+               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);
+
+               parm = AC_PWRST_D3;
+               set_pin_power_state(codec, 0x1e, &parm);
+               /* PW11 (22h) */
+               if (spec->dmic_enabled)
+                       set_pin_power_state(codec, 0x22, &parm);
+               else
+                       snd_hda_codec_write(
+                               codec, 0x22, 0,
+                               AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+
+               /* SW2(26h), AIW1(14h) */
+               snd_hda_codec_write(codec, 0x26, 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);
+               /* Smart 5.1 PW2(1bh) */
+               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);
+
+               /* PW7 (23h), SW3 (27h), AOW3 (25h) */
+               parm = AC_PWRST_D3;
+               set_pin_power_state(codec, 0x23, &parm);
+               /* 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_codec_read(
+                       codec, 0x1c, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+               if (present)
+                       mono_out = 0;
+               else {
+                       present = snd_hda_codec_read(
+                               codec, 0x1d, 0, AC_VERB_GET_PIN_SENSE, 0)
+                               & 0x80000000;
+                       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);
+       } else if (spec->codec_type == VT2002P) {
+               unsigned int present;
+               /* MUX9 (1eh) = stereo mixer */
+               imux_is_smixer = snd_hda_codec_read(
+                       codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
+               /* inputs */
+               /* PW 5/6/7 (29h/2ah/2bh) */
+               parm = AC_PWRST_D3;
+               set_pin_power_state(codec, 0x29, &parm);
+               set_pin_power_state(codec, 0x2a, &parm);
+               set_pin_power_state(codec, 0x2b, &parm);
+               if (imux_is_smixer)
+                       parm = AC_PWRST_D0;
+               /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
+               snd_hda_codec_write(codec, 0x1e, 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, 0x10, 0,
+                                   AC_VERB_SET_POWER_STATE, parm);
+               snd_hda_codec_write(codec, 0x11, 0,
+                                   AC_VERB_SET_POWER_STATE, parm);
+
+               /* outputs */
+               /* AOW0 (8h)*/
+               snd_hda_codec_write(codec, 0x8, 0,
+                                   AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+
+               /* PW4 (26h), MW4 (1ch), MUX4(37h) */
+               parm = AC_PWRST_D3;
+               set_pin_power_state(codec, 0x26, &parm);
+               snd_hda_codec_write(codec, 0x1c, 0,
+                                   AC_VERB_SET_POWER_STATE, parm);
+               snd_hda_codec_write(codec, 0x37,
+                                   0, AC_VERB_SET_POWER_STATE, parm);
+
+               /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
+               parm = AC_PWRST_D3;
+               set_pin_power_state(codec, 0x25, &parm);
+               snd_hda_codec_write(codec, 0x19, 0,
+                                   AC_VERB_SET_POWER_STATE, parm);
+               snd_hda_codec_write(codec, 0x35, 0,
+                                   AC_VERB_SET_POWER_STATE, parm);
+               if (spec->hp_independent_mode)  {
+                       snd_hda_codec_write(codec, 0x9, 0,
+                                           AC_VERB_SET_POWER_STATE, parm);
+               }
+
+               /* Class-D */
+               /* PW0 (24h), MW0(18h), MUX0(34h) */
+               present = snd_hda_codec_read(
+                       codec, 0x25, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+               parm = AC_PWRST_D3;
+               set_pin_power_state(codec, 0x24, &parm);
+               if (present) {
+                       snd_hda_codec_write(
+                               codec, 0x18, 0,
+                               AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+                       snd_hda_codec_write(
+                               codec, 0x34, 0,
+                               AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+               } else {
+                       snd_hda_codec_write(
+                               codec, 0x18, 0,
+                               AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+                       snd_hda_codec_write(
+                               codec, 0x34, 0,
+                               AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+               }
+
+               /* Mono Out */
+               /* PW15 (31h), MW8(17h), MUX8(3bh) */
+               present = snd_hda_codec_read(
+                       codec, 0x26, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+               parm = AC_PWRST_D3;
+               set_pin_power_state(codec, 0x31, &parm);
+               if (present) {
+                       snd_hda_codec_write(
+                               codec, 0x17, 0,
+                               AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+                       snd_hda_codec_write(
+                               codec, 0x3b, 0,
+                               AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+               } else {
+                       snd_hda_codec_write(
+                               codec, 0x17, 0,
+                               AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+                       snd_hda_codec_write(
+                               codec, 0x3b, 0,
+                               AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+               }
+
+               /* MW9 (21h) */
+               if (imux_is_smixer || !is_aa_path_mute(codec))
+                       snd_hda_codec_write(
+                               codec, 0x21, 0,
+                               AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+               else
+                       snd_hda_codec_write(
+                               codec, 0x21, 0,
+                               AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+       } else if (spec->codec_type == VT1812) {
+               unsigned int present;
+               /* MUX10 (1eh) = stereo mixer */
+               imux_is_smixer = snd_hda_codec_read(
+                       codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
+               /* inputs */
+               /* PW 5/6/7 (29h/2ah/2bh) */
+               parm = AC_PWRST_D3;
+               set_pin_power_state(codec, 0x29, &parm);
+               set_pin_power_state(codec, 0x2a, &parm);
+               set_pin_power_state(codec, 0x2b, &parm);
+               if (imux_is_smixer)
+                       parm = AC_PWRST_D0;
+               /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
+               snd_hda_codec_write(codec, 0x1e, 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, 0x10, 0,
+                                   AC_VERB_SET_POWER_STATE, parm);
+               snd_hda_codec_write(codec, 0x11, 0,
+                                   AC_VERB_SET_POWER_STATE, parm);
+
+               /* outputs */
+               /* AOW0 (8h)*/
+               snd_hda_codec_write(codec, 0x8, 0,
+                                   AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+
+               /* PW4 (28h), MW4 (18h), MUX4(38h) */
+               parm = AC_PWRST_D3;
+               set_pin_power_state(codec, 0x28, &parm);
+               snd_hda_codec_write(codec, 0x18, 0,
+                                   AC_VERB_SET_POWER_STATE, parm);
+               snd_hda_codec_write(codec, 0x38, 0,
+                                   AC_VERB_SET_POWER_STATE, parm);
+
+               /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
+               parm = AC_PWRST_D3;
+               set_pin_power_state(codec, 0x25, &parm);
+               snd_hda_codec_write(codec, 0x15, 0,
+                                   AC_VERB_SET_POWER_STATE, parm);
+               snd_hda_codec_write(codec, 0x35, 0,
+                                   AC_VERB_SET_POWER_STATE, parm);
+               if (spec->hp_independent_mode)  {
+                       snd_hda_codec_write(codec, 0x9, 0,
+                                           AC_VERB_SET_POWER_STATE, parm);
+               }
+
+               /* Internal Speaker */
+               /* PW0 (24h), MW0(14h), MUX0(34h) */
+               present = snd_hda_codec_read(
+                       codec, 0x25, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+               parm = AC_PWRST_D3;
+               set_pin_power_state(codec, 0x24, &parm);
+               if (present) {
+                       snd_hda_codec_write(codec, 0x14, 0,
+                                           AC_VERB_SET_POWER_STATE,
+                                           AC_PWRST_D3);
+                       snd_hda_codec_write(codec, 0x34, 0,
+                                           AC_VERB_SET_POWER_STATE,
+                                           AC_PWRST_D3);
+               } else {
+                       snd_hda_codec_write(codec, 0x14, 0,
+                                           AC_VERB_SET_POWER_STATE,
+                                           AC_PWRST_D0);
+                       snd_hda_codec_write(codec, 0x34, 0,
+                                           AC_VERB_SET_POWER_STATE,
+                                           AC_PWRST_D0);
+               }
+               /* 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;
+               parm = AC_PWRST_D3;
+               set_pin_power_state(codec, 0x31, &parm);
+               if (present) {
+                       snd_hda_codec_write(codec, 0x1c, 0,
+                                           AC_VERB_SET_POWER_STATE,
+                                           AC_PWRST_D3);
+                       snd_hda_codec_write(codec, 0x3c, 0,
+                                           AC_VERB_SET_POWER_STATE,
+                                           AC_PWRST_D3);
+                       snd_hda_codec_write(codec, 0x3e, 0,
+                                           AC_VERB_SET_POWER_STATE,
+                                           AC_PWRST_D3);
+               } else {
+                       snd_hda_codec_write(codec, 0x1c, 0,
+                                           AC_VERB_SET_POWER_STATE,
+                                           AC_PWRST_D0);
+                       snd_hda_codec_write(codec, 0x3c, 0,
+                                           AC_VERB_SET_POWER_STATE,
+                                           AC_PWRST_D0);
+                       snd_hda_codec_write(codec, 0x3e, 0,
+                                           AC_VERB_SET_POWER_STATE,
+                                           AC_PWRST_D0);
+               }
+
+               /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
+               parm = AC_PWRST_D3;
+               set_pin_power_state(codec, 0x33, &parm);
+               snd_hda_codec_write(codec, 0x1d, 0,
+                                   AC_VERB_SET_POWER_STATE, parm);
+               snd_hda_codec_write(codec, 0x3d, 0,
+                                   AC_VERB_SET_POWER_STATE, parm);
+
+               /* MW9 (21h) */
+               if (imux_is_smixer || !is_aa_path_mute(codec))
+                       snd_hda_codec_write(
+                               codec, 0x21, 0,
+                               AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+               else
+                       snd_hda_codec_write(
+                               codec, 0x21, 0,
+                               AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+       }
+}
+
 /*
  * input MUX handling
  */
@@ -395,6 +1069,14 @@ static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
 
        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);
+       /* update jack power state */
+       set_jack_power_state(codec);
+
        return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
                                     spec->mux_nids[adc_idx],
                                     &spec->cur_mux[adc_idx]);
@@ -413,16 +1095,74 @@ static int via_independent_hp_get(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];
-       unsigned int pinsel = snd_hda_codec_read(codec, nid, 0,
-                                                AC_VERB_GET_CONNECT_SEL,
-                                                0x00);
-
+       hda_nid_t nid;
+       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,
+                                     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 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;
+       }
+
+       if (sw3)
+               snd_hda_codec_write(codec, sw3, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+                                   parm);
+       return 0;
+}
+
 static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
                                  struct snd_ctl_elem_value *ucontrol)
 {
@@ -430,47 +1170,46 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
        struct via_spec *spec = codec->spec;
        hda_nid_t nid = spec->autocfg.hp_pins[0];
        unsigned int pinsel = ucontrol->value.enumerated.item[0];
-       unsigned int con_nid = snd_hda_codec_read(codec, nid, 0,
-                                        AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
-
-       if (con_nid == spec->multiout.hp_nid) {
-               if (pinsel == 0) {
-                       if (!spec->hp_independent_mode) {
-                               if (spec->multiout.num_dacs > 1)
-                                       spec->multiout.num_dacs -= 1;
-                               spec->hp_independent_mode = 1;
-                       }
-               } else if (pinsel == 1) {
-                      if (spec->hp_independent_mode) {
-                               if (spec->multiout.num_dacs > 1)
-                                       spec->multiout.num_dacs += 1;
-                               spec->hp_independent_mode = 0;
-                      }
-               }
-       } else {
-               if (pinsel == 0) {
-                       if (spec->hp_independent_mode) {
-                               if (spec->multiout.num_dacs > 1)
-                                       spec->multiout.num_dacs += 1;
-                               spec->hp_independent_mode = 0;
-                       }
-               } else if (pinsel == 1) {
-                      if (!spec->hp_independent_mode) {
-                               if (spec->multiout.num_dacs > 1)
-                                       spec->multiout.num_dacs -= 1;
-                               spec->hp_independent_mode = 1;
-                      }
-               }
+       /* Get Independent Mode index of headphone pin widget */
+       spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel
+               ? 1 : 0;
+
+       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->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
+           || spec->codec_type == VT2002P
+           || spec->codec_type == VT1812) {
+               activate_ctl(codec, "Headphone Playback Volume",
+                            spec->hp_independent_mode);
+               activate_ctl(codec, "Headphone Playback Switch",
+                            spec->hp_independent_mode);
        }
-       snd_hda_codec_write(codec, nid, 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);
-
        return 0;
 }
 
@@ -486,6 +1225,175 @@ static struct snd_kcontrol_new via_hp_mixer[] = {
        { } /* end */
 };
 
+static void notify_aa_path_ctls(struct hda_codec *codec)
+{
+       int i;
+       struct snd_ctl_elem_id id;
+       const char *labels[] = {"Mic", "Front Mic", "Line"};
+
+       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]);
+               snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                              &id);
+       }
+}
+
+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 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;
+       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,
+                                        HDA_AMP_MUTE, val);
+       }
+}
+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;
+               }
+       }
+       return res;
+}
+
+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)
+{
+       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 };
+       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;
+               }
+       }
+       *ucontrol->value.integer.value = on;
+       return 0;
+}
+
+static int via_smart51_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 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)
+                       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(
+                                       codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE,
+                                       HDA_AMP_UNMUTE);
+               }
+               if (i == AUTO_PIN_FRONT_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_jack_power_state(codec);
+       return 1;
+}
+
+static struct snd_kcontrol_new via_smart51_mixer[] = {
+       {
+        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name = "Smart 5.1",
+        .count = 1,
+        .info = via_smart51_info,
+        .get = via_smart51_get,
+        .put = via_smart51_put,
+        },
+       {}                      /* end */
+};
+
 /* capture mixer elements */
 static struct snd_kcontrol_new vt1708_capture_mixer[] = {
        HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT),
@@ -506,15 +1414,121 @@ static struct snd_kcontrol_new vt1708_capture_mixer[] = {
        },
        { } /* end */
 };
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static 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)},
+
+/* check AA path's mute statue */
+static int 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:
+               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;
+                       }
+               }
+       }
+       return mute;
+}
+
+/* enter/exit analog low-current mode */
+static void analog_low_current_mode(struct hda_codec *codec, int stream_idle)
+{
+       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;
+
+       if (stream_idle == -1)  /* stream status did not change */
+               enable = enable && saved_stream_idle;
+       else {
+               enable = enable && stream_idle;
+               saved_stream_idle = stream_idle;
+       }
+
+       /* decide low current mode's verb & parameter */
+       switch (spec->codec_type) {
+       case VT1708B_8CH:
+       case VT1708B_4CH:
+               verb = 0xf70;
+               parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
+               break;
+       case VT1708S:
+       case VT1718S:
+       case VT1716S:
+               verb = 0xf73;
+               parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
+               break;
+       case VT1702:
+               verb = 0xf73;
+               parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
+               break;
+       case VT2002P:
+       case VT1812:
+               verb = 0xf93;
+               parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
+               break;
+       default:
+               return;         /* other codecs are not supported */
+       }
+       /* send verb */
+       snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
+}
+
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static 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
@@ -534,9 +1548,9 @@ static struct hda_verb vt1708_volume_init_verbs[] = {
        {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 to PW4 */
-       {0x20, AC_VERB_SET_CONNECT_SEL, 0x1},
+
+       /* Setup default input MW0 to PW4 */
+       {0x20, AC_VERB_SET_CONNECT_SEL, 0},
        /* PW9 Output enable */
        {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
        { }
@@ -547,30 +1561,13 @@ static int via_playback_pcm_open(struct hda_pcm_stream *hinfo,
                                 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);
 }
 
-static int via_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 via_spec *spec = codec->spec;
-       return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
-                                               stream_tag, format, substream);
-}
-
-static int via_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
-                                   struct hda_codec *codec,
-                                   struct snd_pcm_substream *substream)
-{
-       struct via_spec *spec = codec->spec;
-       return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
-}
-
-
 static void playback_multi_pcm_prep_0(struct hda_codec *codec,
                                      unsigned int stream_tag,
                                      unsigned int format,
@@ -615,8 +1612,8 @@ static void playback_multi_pcm_prep_0(struct hda_codec *codec,
        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)
+       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);
@@ -658,7 +1655,7 @@ static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
                        snd_hda_codec_setup_stream(codec, mout->hp_nid,
                                                   stream_tag, 0, format);
        }
-
+       vt1708_start_hp_work(spec);
        return 0;
 }
 
@@ -698,7 +1695,7 @@ static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
                        snd_hda_codec_setup_stream(codec, mout->hp_nid,
                                                   0, 0, 0);
        }
-
+       vt1708_stop_hp_work(spec);
        return 0;
 }
 
@@ -779,7 +1776,7 @@ static struct hda_pcm_stream vt1708_pcm_analog_playback = {
 };
 
 static struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
-       .substreams = 1,
+       .substreams = 2,
        .channels_min = 2,
        .channels_max = 8,
        .nid = 0x10, /* NID to query formats and rates */
@@ -790,8 +1787,8 @@ static struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
        .formats = SNDRV_PCM_FMTBIT_S16_LE,
        .ops = {
                .open = via_playback_pcm_open,
-               .prepare = via_playback_pcm_prepare,
-               .cleanup = via_playback_pcm_cleanup
+               .prepare = via_playback_multi_pcm_prepare,
+               .cleanup = via_playback_multi_pcm_cleanup
        },
 };
 
@@ -853,6 +1850,11 @@ static int via_build_controls(struct hda_codec *codec)
                if (err < 0)
                        return err;
        }
+
+       /* init power states */
+       set_jack_power_state(codec);
+       analog_low_current_mode(codec, 1);
+
        via_free_kctls(codec); /* no longer needed */
        return 0;
 }
@@ -866,8 +1868,10 @@ static int via_build_pcms(struct hda_codec *codec)
        codec->pcm_info = info;
 
        info->name = spec->stream_name_analog;
-       info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_analog_playback);
-       info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
+       info->stream[SNDRV_PCM_STREAM_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];
 
@@ -904,20 +1908,62 @@ static void via_free(struct hda_codec *codec)
                return;
 
        via_free_kctls(codec);
+       vt1708_stop_hp_work(spec);
        kfree(codec->spec);
 }
 
 /* mute internal speaker if HP is plugged */
 static void via_hp_automute(struct hda_codec *codec)
 {
-       unsigned int present;
+       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;
-       snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0],
-                                HDA_OUTPUT, 0, HDA_AMP_MUTE,
-                                present ? HDA_AMP_MUTE : 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);
+       }
+}
+
+/* mute mono out if HP or Line out is plugged */
+static void via_mono_automute(struct hda_codec *codec)
+{
+       unsigned int hp_present, lineout_present;
+       struct via_spec *spec = codec->spec;
+
+       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;
+
+       /* 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_codec_read(
+               codec, spec->autocfg.hp_pins[0], 0,
+               AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+
+       if (!spec->hp_independent_mode)
+               snd_hda_codec_amp_stereo(
+                       codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE,
+                       hp_present ? HDA_AMP_MUTE : 0);
 }
 
 static void via_gpio_control(struct hda_codec *codec)
@@ -968,15 +2014,86 @@ static void via_gpio_control(struct hda_codec *codec)
        }
 }
 
+/* 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 (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;
+
+       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_codec_read(codec, spec->autocfg.hp_pins[0], 0,
+                                       AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+
+       present = snd_hda_codec_read(codec, spec->autocfg.line_out_pins[0], 0,
+                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+
+       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;
+       }
+       /* 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)
 {
        res >>= 26;
-       if (res == VIA_HP_EVENT)
+       if (res & VIA_HP_EVENT)
                via_hp_automute(codec);
-       else if (res == VIA_GPIO_EVENT)
+       if (res & VIA_GPIO_EVENT)
                via_gpio_control(codec);
+       if (res & VIA_JACK_EVENT)
+               set_jack_power_state(codec);
+       if (res & VIA_MONO_EVENT)
+               via_mono_automute(codec);
+       if (res & VIA_SPEAKER_EVENT)
+               via_speaker_automute(codec);
+       if (res & VIA_BIND_HP_EVENT)
+               via_hp_bind_automute(codec);
 }
 
 static int via_init(struct hda_codec *codec)
@@ -986,6 +2103,10 @@ static int via_init(struct hda_codec *codec)
        for (i = 0; i < spec->num_iverbs; i++)
                snd_hda_sequence_write(codec, spec->init_verbs[i]);
 
+       spec->codec_type = get_codec_type(codec);
+       if (spec->codec_type == VT1708BCE)
+               spec->codec_type = VT1708S; /* VT1708BCE & VT1708S are almost
+                                              same */
        /* Lydia Add for EAPD enable */
        if (!spec->dig_in_nid) { /* No Digital In connection */
                if (spec->dig_in_pin) {
@@ -1003,8 +2124,17 @@ static int via_init(struct hda_codec *codec)
        if (spec->slave_dig_outs[0])
                codec->slave_dig_outs = spec->slave_dig_outs;
 
-       return 0;
+       return 0;
+}
+
+#ifdef SND_HDA_NEEDS_RESUME
+static int via_suspend(struct hda_codec *codec, pm_message_t state)
+{
+       struct via_spec *spec = codec->spec;
+       vt1708_stop_hp_work(spec);
+       return 0;
 }
+#endif
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
@@ -1021,6 +2151,9 @@ static struct hda_codec_ops via_patch_ops = {
        .build_pcms = via_build_pcms,
        .init = via_init,
        .free = via_free,
+#ifdef SND_HDA_NEEDS_RESUME
+       .suspend = via_suspend,
+#endif
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        .check_power_status = via_check_power_status,
 #endif
@@ -1036,8 +2169,8 @@ static int vt1708_auto_fill_dac_nids(struct via_spec *spec,
        spec->multiout.num_dacs = cfg->line_outs;
 
        spec->multiout.dac_nids = spec->private_dac_nids;
-       
-       for(i = 0; i < 4; i++) {
+
+       for (i = 0; i < 4; i++) {
                nid = cfg->line_out_pins[i];
                if (nid) {
                        /* config dac list */
@@ -1067,7 +2200,7 @@ static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec,
 {
        char name[32];
        static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
-       hda_nid_t nid, nid_vol = 0;
+       hda_nid_t nid, nid_vol, nid_vols[] = {0x17, 0x19, 0x1a, 0x1b};
        int i, err;
 
        for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
@@ -1075,9 +2208,8 @@ static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec,
 
                if (!nid)
                        continue;
-               
-               if (i != AUTO_SEQ_FRONT)
-                       nid_vol = 0x18 + i;
+
+               nid_vol = nid_vols[i];
 
                if (i == AUTO_SEQ_CENLFE) {
                        /* Center/LFE */
@@ -1105,21 +2237,21 @@ static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec,
                                                                  HDA_OUTPUT));
                        if (err < 0)
                                return err;
-               } else if (i == AUTO_SEQ_FRONT){
+               } 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(0x17, 3, 0,
+                                             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(0x17, 3, 0,
+                                             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,
@@ -1178,6 +2310,7 @@ static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
                return 0;
 
        spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */
+       spec->hp_independent_mode_index = 1;
 
        err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
                              "Headphone Playback Volume",
@@ -1218,7 +2351,7 @@ static int vt1708_auto_create_analog_input_ctls(struct via_spec *spec,
                case 0x1d: /* Mic */
                        idx = 2;
                        break;
-                               
+
                case 0x1e: /* Line In */
                        idx = 3;
                        break;
@@ -1231,8 +2364,7 @@ static int vt1708_auto_create_analog_input_ctls(struct via_spec *spec,
                        idx = 1;
                        break;
                }
-               err = via_new_analog_input(spec, cfg->input_pins[i], labels[i],
-                                          idx, 0x17);
+               err = via_new_analog_input(spec, labels[i], idx, 0x17);
                if (err < 0)
                        return err;
                imux->items[imux->num_items].label = labels[i];
@@ -1260,16 +2392,60 @@ static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
        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) {
-               if (seqassoc == 0xff) {
-                       def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
-                       snd_hda_codec_set_pincfg(codec, nid, 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_detectect_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_detectect =
+               !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
+       ucontrol->value.integer.value[0] = spec->vt1708_jack_detectect;
+       return 0;
+}
+
+static int vt1708_jack_detectect_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_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);
+       }
+       return change;
+}
+
+static 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 int vt1708_parse_auto_config(struct hda_codec *codec)
 {
        struct via_spec *spec = codec->spec;
@@ -1295,6 +2471,10 @@ static int vt1708_parse_auto_config(struct hda_codec *codec)
        if (err < 0)
                return err;
        err = vt1708_auto_create_analog_input_ctls(spec, &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;
 
@@ -1316,19 +2496,45 @@ static int vt1708_parse_auto_config(struct hda_codec *codec)
        if (spec->hp_mux)
                spec->mixers[spec->num_mixers++] = via_hp_mixer;
 
+       spec->mixers[spec->num_mixers++] = via_smart51_mixer;
        return 1;
 }
 
 /* init callback for auto-configuration model -- overriding the default init */
 static int via_auto_init(struct hda_codec *codec)
 {
+       struct via_spec *spec = codec->spec;
+
        via_init(codec);
        via_auto_init_multi_out(codec);
        via_auto_init_hp_out(codec);
        via_auto_init_analog_input(codec);
+       if (spec->codec_type == VT2002P || spec->codec_type == VT1812) {
+               via_hp_bind_automute(codec);
+       } else {
+               via_hp_automute(codec);
+               via_speaker_automute(codec);
+       }
+
        return 0;
 }
 
+static void vt1708_update_hp_jack_state(struct work_struct *work)
+{
+       struct via_spec *spec = container_of(work, struct via_spec,
+                                            vt1708_hp_work.work);
+       if (spec->codec_type != VT1708)
+               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)) {
+               spec->vt1708_hp_present ^= 1;
+               via_hp_automute(spec->codec);
+       }
+       vt1708_start_hp_work(spec);
+}
+
 static int get_mux_nids(struct hda_codec *codec)
 {
        struct via_spec *spec = codec->spec;
@@ -1378,7 +2584,7 @@ static int patch_vt1708(struct hda_codec *codec)
                       "from BIOS.  Using genenic mode...\n");
        }
 
-       
+
        spec->stream_name_analog = "VT1708 Analog";
        spec->stream_analog_playback = &vt1708_pcm_analog_playback;
        /* disable 32bit format on VT1708 */
@@ -1390,7 +2596,7 @@ static int patch_vt1708(struct hda_codec *codec)
        spec->stream_digital_playback = &vt1708_pcm_digital_playback;
        spec->stream_digital_capture = &vt1708_pcm_digital_capture;
 
-       
+
        if (!spec->adc_nids && spec->input_mux) {
                spec->adc_nids = vt1708_adc_nids;
                spec->num_adc_nids = ARRAY_SIZE(vt1708_adc_nids);
@@ -1405,7 +2611,8 @@ 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;
 }
 
@@ -1433,7 +2640,8 @@ static struct snd_kcontrol_new vt1709_capture_mixer[] = {
 };
 
 static struct hda_verb vt1709_uniwill_init_verbs[] = {
-       {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT},
+       {0x20, AC_VERB_SET_UNSOLICITED_ENABLE,
+        AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
        { }
 };
 
@@ -1473,8 +2681,8 @@ static struct hda_verb vt1709_10ch_volume_init_verbs[] = {
        {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 AOW4 */
-       {0x20, AC_VERB_SET_CONNECT_SEL, 0x1},
+       /* Set input of PW4 as MW0 */
+       {0x20, AC_VERB_SET_CONNECT_SEL, 0},
        /* PW9 Output enable */
        {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
        { }
@@ -1487,8 +2695,8 @@ static struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = {
        .nid = 0x10, /* NID to query formats and rates */
        .ops = {
                .open = via_playback_pcm_open,
-               .prepare = via_playback_pcm_prepare,
-               .cleanup = via_playback_pcm_cleanup
+               .prepare = via_playback_multi_pcm_prepare,
+               .cleanup = via_playback_multi_pcm_cleanup,
        },
 };
 
@@ -1499,8 +2707,8 @@ static struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = {
        .nid = 0x10, /* NID to query formats and rates */
        .ops = {
                .open = via_playback_pcm_open,
-               .prepare = via_playback_pcm_prepare,
-               .cleanup = via_playback_pcm_cleanup
+               .prepare = via_playback_multi_pcm_prepare,
+               .cleanup = via_playback_multi_pcm_cleanup,
        },
 };
 
@@ -1575,11 +2783,11 @@ static int vt1709_auto_fill_dac_nids(struct via_spec *spec,
                spec->multiout.dac_nids[cfg->line_outs] = 0x28; /* AOW4 */
 
        } else if (cfg->line_outs == 3) { /* 6 channels */
-               for(i = 0; i < cfg->line_outs; i++) {
+               for (i = 0; i < cfg->line_outs; i++) {
                        nid = cfg->line_out_pins[i];
                        if (nid) {
                                /* config dac list */
-                               switch(i) {
+                               switch (i) {
                                case AUTO_SEQ_FRONT:
                                        /* AOW0 */
                                        spec->multiout.dac_nids[i] = 0x10;
@@ -1608,56 +2816,58 @@ static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec,
 {
        char name[32];
        static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
-       hda_nid_t nid = 0;
+       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)       
+               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(0x1b, 1, 0,
+                                             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(0x1b, 2, 0,
+                                             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(0x1b, 1, 0,
+                                             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(0x1b, 2, 0,
+                                             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 */
+               } 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(0x18, 3, 0,
+                                             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(0x18, 3, 0,
+                                             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,
@@ -1674,26 +2884,26 @@ static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec,
                } 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(0x1a, 3, 0,
+                                             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(0x1a, 3, 0,
+                                             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(0x29, 3, 0,
+                                             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(0x29, 3, 0,
+                                             HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
                                                                  HDA_OUTPUT));
                        if (err < 0)
                                return err;
@@ -1714,6 +2924,7 @@ static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
                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",
@@ -1752,7 +2963,7 @@ static int vt1709_auto_create_analog_input_ctls(struct via_spec *spec,
                case 0x1d: /* Mic */
                        idx = 2;
                        break;
-                               
+
                case 0x1e: /* Line In */
                        idx = 3;
                        break;
@@ -1765,8 +2976,7 @@ static int vt1709_auto_create_analog_input_ctls(struct via_spec *spec,
                        idx = 1;
                        break;
                }
-               err = via_new_analog_input(spec, cfg->input_pins[i], labels[i],
-                                          idx, 0x18);
+               err = via_new_analog_input(spec, labels[i], idx, 0x18);
                if (err < 0)
                        return err;
                imux->items[imux->num_items].label = labels[i];
@@ -1816,6 +3026,7 @@ static int vt1709_parse_auto_config(struct hda_codec *codec)
        if (spec->hp_mux)
                spec->mixers[spec->num_mixers++] = via_hp_mixer;
 
+       spec->mixers[spec->num_mixers++] = via_smart51_mixer;
        return 1;
 }
 
@@ -1861,7 +3072,7 @@ static int patch_vt1709_10ch(struct hda_codec *codec)
        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);
@@ -1955,7 +3166,7 @@ static int patch_vt1709_6ch(struct hda_codec *codec)
        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);
@@ -2024,7 +3235,7 @@ static struct hda_verb vt1708B_8ch_volume_init_verbs[] = {
        {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
 
        /* Setup default input to PW4 */
-       {0x1d, AC_VERB_SET_CONNECT_SEL, 0x1},
+       {0x1d, AC_VERB_SET_CONNECT_SEL, 0},
        /* PW9 Output enable */
        {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
        /* PW10 Input enable */
@@ -2068,10 +3279,29 @@ static struct hda_verb vt1708B_4ch_volume_init_verbs[] = {
 };
 
 static struct hda_verb vt1708B_uniwill_init_verbs[] = {
-       {0x1D, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT},
+       {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 struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = {
        .substreams = 2,
        .channels_min = 2,
@@ -2080,7 +3310,8 @@ static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = {
        .ops = {
                .open = via_playback_pcm_open,
                .prepare = via_playback_multi_pcm_prepare,
-               .cleanup = via_playback_multi_pcm_cleanup
+               .cleanup = via_playback_multi_pcm_cleanup,
+               .close = via_pcm_open_close
        },
 };
 
@@ -2102,8 +3333,10 @@ static struct hda_pcm_stream vt1708B_pcm_analog_capture = {
        .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
+               .cleanup = via_capture_pcm_cleanup,
+               .close = via_pcm_open_close
        },
 };
 
@@ -2260,6 +3493,7 @@ static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t 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",
@@ -2313,8 +3547,7 @@ static int vt1708B_auto_create_analog_input_ctls(struct via_spec *spec,
                        idx = 1;
                        break;
                }
-               err = via_new_analog_input(spec, cfg->input_pins[i], labels[i],
-                                          idx, 0x16);
+               err = via_new_analog_input(spec, labels[i], idx, 0x16);
                if (err < 0)
                        return err;
                imux->items[imux->num_items].label = labels[i];
@@ -2364,6 +3597,7 @@ static int vt1708B_parse_auto_config(struct hda_codec *codec)
        if (spec->hp_mux)
                spec->mixers[spec->num_mixers++] = via_hp_mixer;
 
+       spec->mixers[spec->num_mixers++] = via_smart51_mixer;
        return 1;
 }
 
@@ -2376,12 +3610,14 @@ static struct hda_amp_list vt1708B_loopbacks[] = {
        { } /* end */
 };
 #endif
-
+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 = kzalloc(sizeof(*spec), GFP_KERNEL);
        if (spec == NULL)
@@ -2483,29 +3719,15 @@ static int patch_vt1708B_4ch(struct hda_codec *codec)
 
 /* Patch for VT1708S */
 
-/* VT1708S software backdoor based override for buggy hardware micboost
- * setting */
-#define MIC_BOOST_VOLUME(xname, nid) {                         \
-       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,            \
-       .name = xname,                                  \
-       .index = 0,                                     \
-       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |     \
-       SNDRV_CTL_ELEM_ACCESS_TLV_READ |                \
-       SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,             \
-       .info = mic_boost_volume_info,                  \
-       .get = snd_hda_mixer_amp_volume_get,            \
-       .put = snd_hda_mixer_amp_volume_put,            \
-       .tlv = { .c = mic_boost_tlv },                  \
-       .private_value = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT) }
-
 /* capture mixer elements */
 static 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),
-       MIC_BOOST_VOLUME("Mic Boost Capture Volume", 0x1A),
-       MIC_BOOST_VOLUME("Front Mic Boost Capture Volume", 0x1E),
+       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
@@ -2542,11 +3764,21 @@ static struct hda_verb vt1708S_volume_init_verbs[] = {
        {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
        /* Enable Mic Boost Volume backdoor */
        {0x1, 0xf98, 0x1},
+       /* don't bybass mixer */
+       {0x1, 0xf88, 0xc0},
        { }
 };
 
 static struct hda_verb vt1708S_uniwill_init_verbs[] = {
-       {0x1D, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT},
+       {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},
        { }
 };
 
@@ -2557,8 +3789,9 @@ static struct hda_pcm_stream vt1708S_pcm_analog_playback = {
        .nid = 0x10, /* NID to query formats and rates */
        .ops = {
                .open = via_playback_pcm_open,
-               .prepare = via_playback_pcm_prepare,
-               .cleanup = via_playback_pcm_cleanup
+               .prepare = via_playback_multi_pcm_prepare,
+               .cleanup = via_playback_multi_pcm_cleanup,
+               .close = via_pcm_open_close
        },
 };
 
@@ -2568,8 +3801,10 @@ static struct hda_pcm_stream vt1708S_pcm_analog_capture = {
        .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
+               .cleanup = via_capture_pcm_cleanup,
+               .close = via_pcm_open_close
        },
 };
 
@@ -2718,34 +3953,1698 @@ static int vt1708S_auto_create_multi_out_ctls(struct via_spec *spec,
        return 0;
 }
 
-static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
+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 via_spec *spec,
+                                               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;
+}
+
+/* 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(spec, &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(spec, &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)
+               spec->mixers[spec->num_mixers++] = via_hp_mixer;
+
+       spec->mixers[spec->num_mixers++] = via_smart51_mixer;
+       return 1;
+}
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static 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 = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (spec == NULL)
+               return -ENOMEM;
+
+       codec->spec = spec;
+
+       /* 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;
+       spec->init_verbs[spec->num_iverbs++] = vt1708S_uniwill_init_verbs;
+
+       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;
+
+       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) {
+               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";
+       }
+       return 0;
+}
+
+/* Patch for VT1702 */
+
+/* capture mixer elements */
+static 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 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 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 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 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 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->multiout.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 *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 */
+       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++;
+       }
+
+       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,
+                                               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 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(spec, &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)
+               spec->mixers[spec->num_mixers++] = via_hp_mixer;
+
+       return 1;
+}
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static 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 int patch_vt1702(struct hda_codec *codec)
+{
+       struct via_spec *spec;
+       int err;
+
+       /* create a codec specific record */
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (spec == NULL)
+               return -ENOMEM;
+
+       codec->spec = spec;
+
+       /* automatic parse from the BIOS config */
+       err = vt1702_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
+
+       return 0;
+}
+
+/* Patch for VT1718S */
+
+/* capture mixer elements */
+static 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 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)},
+
+
+       /* 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(5)},
+
+       /* Setup default input of Front HP to MW9 */
+       {0x28, AC_VERB_SET_CONNECT_SEL, 0x1},
+       /* 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_UNMUTE(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_UNMUTE(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},
+       /* Unmute MW4's index 0 */
+       {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       { }
+};
+
+
+static 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},
+       { }
+};
+
+static 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 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 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 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)
+{
+       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->multiout.dac_nids[i] = 0x8;
+                               break;
+                       case AUTO_SEQ_CENLFE:
+                               spec->multiout.dac_nids[i] = 0xa;
+                               break;
+                       case AUTO_SEQ_SURROUND:
+                               spec->multiout.dac_nids[i] = 0x9;
+                               break;
+                       case AUTO_SEQ_SIDE:
+                               spec->multiout.dac_nids[i] = 0xb;
+                               break;
+                       }
+               }
+       }
+
+       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)
+{
+       char name[32];
+       static const char *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;
+       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];
+               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) {
+                       /* 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 vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
+{
+       int err;
+
+       if (!pin)
+               return 0;
+
+       spec->multiout.hp_nid = 0xc; /* AOW4 */
+       spec->hp_independent_mode_index = 1;
+
+       err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+                             "Headphone Playback Volume",
+                             HDA_COMPOSE_AMP_VAL(0xc, 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 vt1718S_auto_create_analog_input_ctls(struct via_spec *spec,
+                                               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 int vt1718S_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 = 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(spec, &spec->autocfg);
+       if (err < 0)
+               return err;
+
+       spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+
+       fill_dig_outs(codec);
+
+       if (spec->autocfg.dig_in_pin && codec->vendor_id == 0x11060428)
+               spec->dig_in_nid = 0x13;
+
+       if (spec->kctls.list)
+               spec->mixers[spec->num_mixers++] = spec->kctls.list;
+
+       spec->input_mux = &spec->private_imux[0];
+
+       if (spec->hp_mux)
+               spec->mixers[spec->num_mixers++] = via_hp_mixer;
+
+       spec->mixers[spec->num_mixers++] = via_smart51_mixer;
+
+       return 1;
+}
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static 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 int patch_vt1718S(struct hda_codec *codec)
+{
+       struct via_spec *spec;
+       int err;
+
+       /* create a codec specific record */
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (spec == NULL)
+               return -ENOMEM;
+
+       codec->spec = spec;
+
+       /* automatic parse from the BIOS config */
+       err = vt1718S_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++;
+       }
+
+       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
+
+       return 0;
+}
+
+/* Patch for VT1716S */
+
+static int vt1716s_dmic_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 vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
+                          struct snd_ctl_elem_value *ucontrol)
+{
+       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_jack_power_state(codec);
+
+       return 1;
+}
+
+/* capture mixer elements */
+static 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 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",
+        .count = 1,
+        .info = vt1716s_dmic_info,
+        .get = vt1716s_dmic_get,
+        .put = vt1716s_dmic_put,
+        },
+       {}                      /* end */
+};
+
+
+/* mono-out mixer elements */
+static struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
+       HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
+       { } /* end */
+};
+
+static 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 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 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 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 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->multiout.dac_nids[i] = 0x10;
+                               break;
+                       case AUTO_SEQ_CENLFE:
+                               spec->multiout.dac_nids[i] = 0x25;
+                               break;
+                       case AUTO_SEQ_SURROUND:
+                               spec->multiout.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 *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;
+
+       create_hp_imux(spec);
+       return 0;
+}
+
+/* create playback/capture controls for input pins */
+static int vt1716S_auto_create_analog_input_ctls(struct via_spec *spec,
+                                               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 int vt1716S_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 = 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(spec, &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)
+               spec->mixers[spec->num_mixers++] = via_hp_mixer;
+
+       spec->mixers[spec->num_mixers++] = via_smart51_mixer;
+
+       return 1;
+}
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static struct hda_amp_list vt1716S_loopbacks[] = {
+       { 0x16, HDA_INPUT, 1 },
+       { 0x16, HDA_INPUT, 2 },
+       { 0x16, HDA_INPUT, 3 },
+       { 0x16, HDA_INPUT, 4 },
+       { } /* end */
+};
+#endif
+
+static int patch_vt1716S(struct hda_codec *codec)
+{
+       struct via_spec *spec;
+       int err;
+
+       /* create a codec specific record */
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (spec == NULL)
+               return -ENOMEM;
+
+       codec->spec = spec;
+
+       /* 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
+
+       return 0;
+}
+
+/* for vt2002P */
+
+/* capture mixer elements */
+static 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 struct hda_verb vt2002P_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) */
+       {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 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 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 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 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->multiout.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;
+
+       if (!cfg->line_out_pins[0])
+               return -1;
+
+
+       /* 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(0x26, 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 = VT1708S_HP_NID; /* AOW3 */
+       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(0x25, 3, 0, HDA_OUTPUT));
+                             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));
+                             HDA_COMPOSE_AMP_VAL(0x25, 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 via_spec *spec,
+static int vt2002P_auto_create_analog_input_ctls(struct via_spec *spec,
                                                const struct auto_pin_cfg *cfg)
 {
        static char *labels[] = {
@@ -2754,89 +5653,73 @@ static int vt1708S_auto_create_analog_input_ctls(struct via_spec *spec,
        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;
+               case 0x2b: /* Mic */
+                       idx = 0;
                        break;
 
-               case 0x1e: /* Front Mic */
-                       idx = 4;
+               case 0x2a: /* Line In */
+                       idx = 1;
                        break;
 
-               case 0x1f: /* CD */
-                       idx = 1;
+               case 0x29: /* Front Mic */
+                       idx = 2;
                        break;
                }
-               err = via_new_analog_input(spec, cfg->input_pins[i], labels[i],
-                                          idx, 0x16);
+               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-1;
+               imux->items[imux->num_items].index = idx;
                imux->num_items++;
        }
-       return 0;
-}
 
-/* 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;
+       /* build volume/mute control of loopback */
+       err = via_new_analog_input(spec, "Stereo Mixer", 3, 0x21);
+       if (err < 0)
+               return err;
 
-       for (i = 0; i < spec->autocfg.dig_outs; i++) {
-               hda_nid_t nid;
-               int conn;
+       /* for internal loopback recording select */
+       imux->items[imux->num_items].label = "Stereo Mixer";
+       imux->items[imux->num_items].index = 3;
+       imux->num_items++;
 
-               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 */
-               }
-       }
+       /* for digital mic select */
+       imux->items[imux->num_items].label = "Digital Mic";
+       imux->items[imux->num_items].index = 4;
+       imux->num_items++;
+
+       return 0;
 }
 
-static int vt1708S_parse_auto_config(struct hda_codec *codec)
+static int vt2002P_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);
+
+       err = vt2002P_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(spec, &spec->autocfg);
+       err = vt2002P_auto_create_multi_out_ctls(spec, &spec->autocfg);
        if (err < 0)
                return err;
-       err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
+       err = vt2002P_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 = vt2002P_auto_create_analog_input_ctls(spec, &spec->autocfg);
        if (err < 0)
                return err;
 
@@ -2856,16 +5739,17 @@ static int vt1708S_parse_auto_config(struct hda_codec *codec)
 }
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list vt1708S_loopbacks[] = {
-       { 0x16, HDA_INPUT, 1 },
-       { 0x16, HDA_INPUT, 2 },
-       { 0x16, HDA_INPUT, 3 },
-       { 0x16, HDA_INPUT, 4 },
+static struct hda_amp_list vt2002P_loopbacks[] = {
+       { 0x21, HDA_INPUT, 0 },
+       { 0x21, HDA_INPUT, 1 },
+       { 0x21, HDA_INPUT, 2 },
        { } /* end */
 };
 #endif
 
-static int patch_vt1708S(struct hda_codec *codec)
+
+/* patch for vt2002P */
+static int patch_vt2002P(struct hda_codec *codec)
 {
        struct via_spec *spec;
        int err;
@@ -2878,7 +5762,7 @@ static int patch_vt1708S(struct hda_codec *codec)
        codec->spec = spec;
 
        /* automatic parse from the BIOS config */
-       err = vt1708S_parse_auto_config(codec);
+       err = vt2002P_parse_auto_config(codec);
        if (err < 0) {
                via_free(codec);
                return err;
@@ -2887,21 +5771,23 @@ static int patch_vt1708S(struct hda_codec *codec)
                       "from BIOS.  Using genenic mode...\n");
        }
 
-       spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs;
-       spec->init_verbs[spec->num_iverbs++] = vt1708S_uniwill_init_verbs;
+       spec->init_verbs[spec->num_iverbs++]  = vt2002P_volume_init_verbs;
+       spec->init_verbs[spec->num_iverbs++] = vt2002P_uniwill_init_verbs;
 
-       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_analog = "VT2002P Analog";
+       spec->stream_analog_playback = &vt2002P_pcm_analog_playback;
+       spec->stream_analog_capture = &vt2002P_pcm_analog_capture;
 
-       spec->stream_name_digital = "VT1708S Digital";
-       spec->stream_digital_playback = &vt1708S_pcm_digital_playback;
+       spec->stream_name_digital = "VT2002P Digital";
+       spec->stream_digital_playback = &vt2002P_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);
+               spec->adc_nids = vt2002P_adc_nids;
+               spec->num_adc_nids = ARRAY_SIZE(vt2002P_adc_nids);
                get_mux_nids(codec);
-               spec->mixers[spec->num_mixers] = vt1708S_capture_mixer;
+               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++;
        }
 
@@ -2909,33 +5795,32 @@ static int patch_vt1708S(struct hda_codec *codec)
 
        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;
+       spec->loopback.amplist = vt2002P_loopbacks;
 #endif
 
        return 0;
 }
 
-/* Patch for VT1702 */
+/* for vt1812 */
 
 /* capture mixer elements */
-static 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),
+static 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 = "Capture Source", */
                .name = "Input Source",
-               .count = 1,
+               .count = 2,
                .info = via_mux_enum_info,
                .get = via_mux_enum_get,
                .put = via_mux_enum_put,
@@ -2943,64 +5828,99 @@ static struct snd_kcontrol_new vt1702_capture_mixer[] = {
        { } /* end */
 };
 
-static struct hda_verb vt1702_volume_init_verbs[] = {
+static struct hda_verb vt1812_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)},
+       {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 
 
-       /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+       /* Mute 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)},
+       /* 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)},
 
-       /* 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},
+       /* 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/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 struct hda_verb vt1702_uniwill_init_verbs[] = {
-       {0x01, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_GPIO_EVENT},
-       {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT},
+
+static 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 struct hda_pcm_stream vt1702_pcm_analog_playback = {
+static struct hda_pcm_stream vt1812_pcm_analog_playback = {
        .substreams = 2,
        .channels_min = 2,
        .channels_max = 2,
-       .nid = 0x10, /* NID to query formats and rates */
+       .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
+               .cleanup = via_playback_multi_pcm_cleanup,
+               .close = via_pcm_open_close,
        },
 };
 
-static struct hda_pcm_stream vt1702_pcm_analog_capture = {
-       .substreams = 3,
+static struct hda_pcm_stream vt1812_pcm_analog_capture = {
+       .substreams = 2,
        .channels_min = 2,
        .channels_max = 2,
-       .nid = 0x12, /* NID to query formats and rates */
+       .nid = 0x10, /* NID to query formats and rates */
        .ops = {
+               .open = via_pcm_open_close,
                .prepare = via_capture_pcm_prepare,
-               .cleanup = via_capture_pcm_cleanup
+               .cleanup = via_capture_pcm_cleanup,
+               .close = via_pcm_open_close,
        },
 };
 
-static struct hda_pcm_stream vt1702_pcm_digital_playback = {
-       .substreams = 2,
+static struct hda_pcm_stream vt1812_pcm_digital_playback = {
+       .substreams = 1,
        .channels_min = 2,
        .channels_max = 2,
        /* NID is set in via_build_pcms */
@@ -3011,24 +5931,20 @@ static struct hda_pcm_stream vt1702_pcm_digital_playback = {
                .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,
+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]) {
-               /* config dac list */
-               spec->multiout.dac_nids[0] = 0x10;
-       }
-
+       if (cfg->line_out_pins[0])
+               spec->multiout.dac_nids[0] = 0x8;
        return 0;
 }
 
+
 /* add playback controls from the parsed DAC table */
-static int vt1702_auto_create_line_out_ctls(struct via_spec *spec,
+static int vt1812_auto_create_multi_out_ctls(struct via_spec *spec,
                                             const struct auto_pin_cfg *cfg)
 {
        int err;
@@ -3036,45 +5952,36 @@ static int vt1702_auto_create_line_out_ctls(struct via_spec *spec,
        if (!cfg->line_out_pins[0])
                return -1;
 
-       /* add control to mixer index 0 */
+       /* Line-Out: PortE */
        err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
                              "Master Front Playback Volume",
-                             HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT));
+                             HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT));
        if (err < 0)
                return err;
-       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+       err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_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));
+                             HDA_COMPOSE_AMP_VAL(0x28, 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)
+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 = 0x1D;
+       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(0x1D, 3, 0, HDA_OUTPUT));
+                             HDA_COMPOSE_AMP_VAL(
+                                     spec->multiout.hp_nid, 3, 0, HDA_OUTPUT));
        if (err < 0)
                return err;
 
@@ -3085,12 +5992,11 @@ static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
                return err;
 
        create_hp_imux(spec);
-
        return 0;
 }
 
 /* create playback/capture controls for input pins */
-static int vt1702_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1812_auto_create_analog_input_ctls(struct via_spec *spec,
                                                const struct auto_pin_cfg *cfg)
 {
        static char *labels[] = {
@@ -3099,60 +6005,72 @@ static int vt1702_auto_create_analog_input_ctls(struct via_spec *spec,
        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;
+               case 0x2b: /* Mic */
+                       idx = 0;
                        break;
 
-               case 0x15: /* Line In */
-                       idx = 2;
+               case 0x2a: /* Line In */
+                       idx = 1;
                        break;
 
-               case 0x18: /* Front Mic */
-                       idx = 3;
+               case 0x29: /* Front Mic */
+                       idx = 2;
                        break;
                }
-               err = via_new_analog_input(spec, cfg->input_pins[i],
-                                          labels[i], idx, 0x1A);
+               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-1;
+               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);
+       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++;
+
        return 0;
 }
 
-static int vt1702_parse_auto_config(struct hda_codec *codec)
+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;
-       err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg);
+       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_pins[0])
+
+       if (!spec->autocfg.line_outs && !spec->autocfg.hp_outs)
                return 0; /* can't find valid BIOS pin config */
 
-       err = vt1702_auto_create_line_out_ctls(spec, &spec->autocfg);
+       err = vt1812_auto_create_multi_out_ctls(spec, &spec->autocfg);
        if (err < 0)
                return err;
-       err = vt1702_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
+       err = vt1812_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
        if (err < 0)
                return err;
-       err = vt1702_auto_create_analog_input_ctls(spec, &spec->autocfg);
+       err = vt1812_auto_create_analog_input_ctls(spec, &spec->autocfg);
        if (err < 0)
                return err;
 
@@ -3172,21 +6090,20 @@ static int vt1702_parse_auto_config(struct hda_codec *codec)
 }
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list vt1702_loopbacks[] = {
-       { 0x1A, HDA_INPUT, 1 },
-       { 0x1A, HDA_INPUT, 2 },
-       { 0x1A, HDA_INPUT, 3 },
-       { 0x1A, HDA_INPUT, 4 },
+static struct hda_amp_list vt1812_loopbacks[] = {
+       { 0x21, HDA_INPUT, 0 },
+       { 0x21, HDA_INPUT, 1 },
+       { 0x21, HDA_INPUT, 2 },
        { } /* end */
 };
 #endif
 
-static int patch_vt1702(struct hda_codec *codec)
+
+/* patch for vt1812 */
+static int patch_vt1812(struct hda_codec *codec)
 {
        struct via_spec *spec;
        int err;
-       unsigned int response;
-       unsigned char control;
 
        /* create a codec specific record */
        spec = kzalloc(sizeof(*spec), GFP_KERNEL);
@@ -3196,7 +6113,7 @@ static int patch_vt1702(struct hda_codec *codec)
        codec->spec = spec;
 
        /* automatic parse from the BIOS config */
-       err = vt1702_parse_auto_config(codec);
+       err = vt1812_parse_auto_config(codec);
        if (err < 0) {
                via_free(codec);
                return err;
@@ -3205,21 +6122,25 @@ static int patch_vt1702(struct hda_codec *codec)
                       "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->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;
 
-       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);
+               spec->adc_nids = vt1812_adc_nids;
+               spec->num_adc_nids = ARRAY_SIZE(vt1812_adc_nids);
                get_mux_nids(codec);
-               spec->mixers[spec->num_mixers] = vt1702_capture_mixer;
+               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++;
        }
 
@@ -3227,22 +6148,11 @@ static int patch_vt1702(struct hda_codec *codec)
 
        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;
+       spec->loopback.amplist = vt1812_loopbacks;
 #endif
 
-       /* Open backdoor */
-       response = snd_hda_codec_read(codec, codec->afg, 0, 0xF8C, 0);
-       control = (unsigned char)(response & 0xff);
-       control |= 0x3;
-       snd_hda_codec_write(codec,  codec->afg, 0, 0xF88, control);
-
-       /* Enable GPIO 0&1 for volume&mute control */
-       /* Enable GPIO 2 for DMIC-DATA */
-       response = snd_hda_codec_read(codec, codec->afg, 0, 0xF84, 0);
-       control = (unsigned char)((response >> 16) & 0x3f);
-       snd_hda_codec_write(codec,  codec->afg, 0, 0xF82, control);
-
        return 0;
 }
 
@@ -3318,6 +6228,23 @@ static struct hda_codec_preset snd_hda_preset_via[] = {
          .patch = patch_vt1702},
        { .id = 0x11067398, .name = "VT1702",
          .patch = patch_vt1702},
+       { .id = 0x11060428, .name = "VT1718S",
+         .patch = patch_vt1718S},
+       { .id = 0x11064428, .name = "VT1718S",
+         .patch = patch_vt1718S},
+       { .id = 0x11060441, .name = "VT2020",
+         .patch = patch_vt1718S},
+       { .id = 0x11064441, .name = "VT1828S",
+         .patch = patch_vt1718S},
+       { .id = 0x11060433, .name = "VT1716S",
+         .patch = patch_vt1716S},
+       { .id = 0x1106a721, .name = "VT1716S",
+         .patch = patch_vt1716S},
+       { .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 */
 };