]> git.ipfire.org Git - ipfire-2.x.git/blobdiff - src/patches/suse-2.6.27.39/patches.drivers/alsa-hda-via-update
Imported linux-2.6.27.39 suse/xen patches.
[ipfire-2.x.git] / src / patches / suse-2.6.27.39 / patches.drivers / alsa-hda-via-update
diff --git a/src/patches/suse-2.6.27.39/patches.drivers/alsa-hda-via-update b/src/patches/suse-2.6.27.39/patches.drivers/alsa-hda-via-update
new file mode 100644 (file)
index 0000000..15a9407
--- /dev/null
@@ -0,0 +1,1858 @@
+From: Takashi Iwai <tiwai@suse.de>
+Subject: ALSA: hda - VIA codecs updates
+Patch-mainline: 2.6.28-rc1
+References: 
+
+A pile of updates for VIA HD-audio codecs.
+
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+
+---
+diff -ruN a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
+--- a/sound/pci/hda/patch_via.c
++++ b/sound/pci/hda/patch_via.c
+@@ -1,10 +1,10 @@
+ /*
+  * Universal Interface for Intel High Definition Audio Codec
+  *
+- * HD audio interface patch for VIA VT1708 codec
++ * HD audio interface patch for VIA VT1702/VT1708/VT1709 codec
+  *
+- * Copyright (c) 2006 Lydia Wang <lydiawang@viatech.com>
+- *                    Takashi Iwai <tiwai@suse.de>
++ * Copyright (c) 2006-2008 Lydia Wang <lydiawang@viatech.com>
++ *                       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
+@@ -29,6 +29,13 @@
+ /* 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-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-05-28  Lydia Wang  Add second S/PDIF Out support for VT1702        */
++/* 2008-09-15  Logan Li    Add VT1708S Mic Boost workaround/backdoor       */
+ /*                                                                           */
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+@@ -37,6 +44,7 @@
+ #include <linux/delay.h>
+ #include <linux/slab.h>
+ #include <sound/core.h>
++#include <sound/asoundef.h>
+ #include "hda_codec.h"
+ #include "hda_local.h"
+ #include "hda_patch.h"
+@@ -53,6 +61,8 @@
+ #define VT1708_DIGOUT_NID     0x14
+ #define VT1708_DIGIN_NID      0x16
+ #define VT1708_DIGIN_PIN      0x26
++#define VT1708_HP_PIN_NID     0x20
++#define VT1708_CD_PIN_NID     0x24
+ #define VT1709_HP_DAC_NID     0x28
+ #define VT1709_DIGOUT_NID     0x13
+@@ -64,12 +74,64 @@
+ #define VT1708B_DIGIN_NID     0x15
+ #define VT1708B_DIGIN_PIN     0x21
++#define VT1708S_HP_NID                0x25
++#define VT1708S_DIGOUT_NID    0x12
++
++#define VT1702_HP_NID         0x17
++#define VT1702_DIGOUT_NID     0x11
++
+ #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,
++      VT1709_10CH,
++      VT1709_6CH,
++      VT1708B_8CH,
++      VT1708B_4CH,
++      VT1708S,
++      VT1702,
++      CODEC_TYPES,
++};
++
++static enum VIA_HDA_CODEC get_codec_type(u32 vendor_id)
++{
++      u16 ven_id = vendor_id >> 16;
++      u16 dev_id = vendor_id & 0xffff;
++      enum VIA_HDA_CODEC codec_type;
++
++      /* get codec type */
++      if (ven_id != 0x1106)
++              codec_type = UNKNOWN;
++      else if (dev_id >= 0x1708 && dev_id <= 0x170b)
++              codec_type = VT1708;
++      else if (dev_id >= 0xe710 && dev_id <= 0xe713)
++              codec_type = VT1709_10CH;
++      else if (dev_id >= 0xe714 && dev_id <= 0xe717)
++              codec_type = VT1709_6CH;
++      else if (dev_id >= 0xe720 && dev_id <= 0xe723)
++              codec_type = VT1708B_8CH;
++      else if (dev_id >= 0xe724 && dev_id <= 0xe727)
++              codec_type = VT1708B_4CH;
++      else if ((dev_id & 0xfff) == 0x397
++               && (dev_id >> 12) < 8)
++              codec_type = VT1708S;
++      else if ((dev_id & 0xfff) == 0x398
++               && (dev_id >> 12) < 8)
++              codec_type = VT1702;
++      else
++              codec_type = UNKNOWN;
++      return codec_type;
++};
++#define VIA_HP_EVENT          0x01
++#define VIA_GPIO_EVENT                0x02
+ enum {
+       VIA_CTL_WIDGET_VOL,
+@@ -77,12 +139,54 @@
+ };
+ enum {
+-      AUTO_SEQ_FRONT,
++      AUTO_SEQ_FRONT = 0,
+       AUTO_SEQ_SURROUND,
+       AUTO_SEQ_CENLFE,
+       AUTO_SEQ_SIDE
+ };
++#define get_amp_nid(kc)       ((kc)->private_value & 0xffff)
++
++/* 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)
++{
++      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)) {
++              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 int mic_boost_volume_info(struct snd_kcontrol *kcontrol,
++                               struct snd_ctl_elem_info *uinfo)
++{
++      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;
++      }
++      return 0;
++}
++
+ static struct snd_kcontrol_new vt1708_control_templates[] = {
+       HDA_CODEC_VOLUME(NULL, 0, 0, 0),
+       HDA_CODEC_MUTE(NULL, 0, 0, 0),
+@@ -94,7 +198,8 @@
+       struct snd_kcontrol_new *mixers[3];
+       unsigned int num_mixers;
+-      struct hda_verb *init_verbs;
++      struct hda_verb *init_verbs[5];
++      unsigned int num_iverbs;
+       char *stream_name_analog;
+       struct hda_pcm_stream *stream_analog_playback;
+@@ -106,6 +211,7 @@
+       /* playback */
+       struct hda_multi_out multiout;
++      hda_nid_t extra_dig_out_nid;
+       /* capture */
+       unsigned int num_adc_nids;
+@@ -117,15 +223,19 @@
+       unsigned int cur_mux[3];
+       /* PCM information */
+-      struct hda_pcm pcm_rec[2];
++      struct hda_pcm pcm_rec[3];
+       /* dynamic controls, init_verbs and input_mux */
+       struct auto_pin_cfg autocfg;
+       unsigned int num_kctl_alloc, num_kctl_used;
+       struct snd_kcontrol_new *kctl_alloc;
+-      struct hda_input_mux private_imux;
++      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;
++
+ #ifdef CONFIG_SND_HDA_POWER_SAVE
+       struct hda_loopback_check loopback;
+ #endif
+@@ -146,6 +256,16 @@
+       0x13, 0x14
+ };
++static hda_nid_t vt1708S_adc_nids[2] = {
++      /* ADC1-2 */
++      0x13, 0x14
++};
++
++static hda_nid_t vt1702_adc_nids[3] = {
++      /* ADC1-2 */
++      0x12, 0x20, 0x1F
++};
++
+ /* add dynamic controls */
+ static int via_add_control(struct via_spec *spec, int type, const char *name,
+                          unsigned long val)
+@@ -283,19 +403,108 @@
+               return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
+                                            0x18, &spec->cur_mux[adc_idx]);
+       else if ((IS_VT1709_10CH_VENDORID(vendor_id) ||
+-                IS_VT1709_6CH_VENDORID(vendor_id)) && adc_idx == 0)
++                IS_VT1709_6CH_VENDORID(vendor_id)) && (adc_idx == 0))
+               return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
+                                            0x19, &spec->cur_mux[adc_idx]);
+       else if ((IS_VT1708B_8CH_VENDORID(vendor_id) ||
+-                IS_VT1708B_4CH_VENDORID(vendor_id)) && adc_idx == 0)
++                IS_VT1708B_4CH_VENDORID(vendor_id)) && (adc_idx == 0))
+               return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
+                                            0x17, &spec->cur_mux[adc_idx]);
++      else if (IS_VT1702_VENDORID(vendor_id) && (adc_idx == 0))
++              return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
++                                           0x13, &spec->cur_mux[adc_idx]);
+       else
+               return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
+                                            spec->adc_nids[adc_idx],
+                                            &spec->cur_mux[adc_idx]);
+ }
++static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
++                                 struct snd_ctl_elem_info *uinfo)
++{
++      struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
++      struct via_spec *spec = codec->spec;
++      return snd_hda_input_mux_info(spec->hp_mux, uinfo);
++}
++
++static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
++                                struct snd_ctl_elem_value *ucontrol)
++{
++      struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
++      struct via_spec *spec = codec->spec;
++      hda_nid_t nid = spec->autocfg.hp_pins[0];
++      unsigned int pinsel = snd_hda_codec_read(codec, nid, 0,
++                                               AC_VERB_GET_CONNECT_SEL,
++                                               0x00);
++
++      ucontrol->value.enumerated.item[0] = pinsel;
++
++      return 0;
++}
++
++static int via_independent_hp_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;
++      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;
++                     }
++              }
++      }
++      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;
++}
++
++static struct snd_kcontrol_new via_hp_mixer[] = {
++      {
++              .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++              .name = "Independent HP",
++              .count = 1,
++              .info = via_independent_hp_info,
++              .get = via_independent_hp_get,
++              .put = via_independent_hp_put,
++      },
++      { } /* end */
++};
++
+ /* capture mixer elements */
+ static struct snd_kcontrol_new vt1708_capture_mixer[] = {
+       HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT),
+@@ -380,6 +589,138 @@
+       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,
++                                    struct snd_pcm_substream *substream)
++{
++      struct via_spec *spec = codec->spec;
++      struct hda_multi_out *mout = &spec->multiout;
++      hda_nid_t *nids = mout->dac_nids;
++      int chs = substream->runtime->channels;
++      int i;
++
++      mutex_lock(&codec->spdif_mutex);
++      if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
++              if (chs == 2 &&
++                  snd_hda_is_supported_format(codec, mout->dig_out_nid,
++                                              format) &&
++                  !(codec->spdif_status & IEC958_AES0_NONAUDIO)) {
++                      mout->dig_out_used = HDA_DIG_ANALOG_DUP;
++                      /* turn off SPDIF once; otherwise the IEC958 bits won't
++                       * be updated */
++                      if (codec->spdif_ctls & AC_DIG1_ENABLE)
++                              snd_hda_codec_write(codec, mout->dig_out_nid, 0,
++                                                  AC_VERB_SET_DIGI_CONVERT_1,
++                                                  codec->spdif_ctls &
++                                                      ~AC_DIG1_ENABLE & 0xff);
++                      snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
++                                                 stream_tag, 0, format);
++                      /* turn on again (if needed) */
++                      if (codec->spdif_ctls & AC_DIG1_ENABLE)
++                              snd_hda_codec_write(codec, mout->dig_out_nid, 0,
++                                                  AC_VERB_SET_DIGI_CONVERT_1,
++                                                  codec->spdif_ctls & 0xff);
++              } else {
++                      mout->dig_out_used = 0;
++                      snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
++                                                 0, 0, 0);
++              }
++      }
++      mutex_unlock(&codec->spdif_mutex);
++
++      /* front */
++      snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag,
++                                 0, format);
++
++      if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
++          !spec->hp_independent_mode)
++              /* headphone out will just decode front left/right (stereo) */
++              snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag,
++                                         0, format);
++
++      /* extra outputs copied from front */
++      for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
++              if (mout->extra_out_nid[i])
++                      snd_hda_codec_setup_stream(codec,
++                                                 mout->extra_out_nid[i],
++                                                 stream_tag, 0, format);
++
++      /* surrounds */
++      for (i = 1; i < mout->num_dacs; i++) {
++              if (chs >= (i + 1) * 2) /* independent out */
++                      snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
++                                                 i * 2, format);
++              else /* copy front */
++                      snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
++                                                 0, format);
++      }
++}
++
++static int via_playback_multi_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;
++      struct hda_multi_out *mout = &spec->multiout;
++      hda_nid_t *nids = mout->dac_nids;
++
++      if (substream->number == 0)
++              playback_multi_pcm_prep_0(codec, stream_tag, format,
++                                        substream);
++      else {
++              if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
++                  spec->hp_independent_mode)
++                      snd_hda_codec_setup_stream(codec, mout->hp_nid,
++                                                 stream_tag, 0, format);
++      }
++
++      return 0;
++}
++
++static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
++                                  struct hda_codec *codec,
++                                  struct snd_pcm_substream *substream)
++{
++      struct via_spec *spec = codec->spec;
++      struct hda_multi_out *mout = &spec->multiout;
++      hda_nid_t *nids = mout->dac_nids;
++      int i;
++
++      if (substream->number == 0) {
++              for (i = 0; i < mout->num_dacs; i++)
++                      snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0);
++
++              if (mout->hp_nid && !spec->hp_independent_mode)
++                      snd_hda_codec_setup_stream(codec, mout->hp_nid,
++                                                 0, 0, 0);
++
++              for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
++                      if (mout->extra_out_nid[i])
++                              snd_hda_codec_setup_stream(codec,
++                                                      mout->extra_out_nid[i],
++                                                      0, 0, 0);
++              mutex_lock(&codec->spdif_mutex);
++              if (mout->dig_out_nid &&
++                  mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
++                      snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
++                                                 0, 0, 0);
++                      mout->dig_out_used = 0;
++              }
++              mutex_unlock(&codec->spdif_mutex);
++      } else {
++              if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
++                  spec->hp_independent_mode)
++                      snd_hda_codec_setup_stream(codec, mout->hp_nid,
++                                                 0, 0, 0);
++      }
++
++      return 0;
++}
++
+ /*
+  * Digital out
+  */
+@@ -399,6 +740,21 @@
+       return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+ }
++/* setup SPDIF output stream */
++static void setup_dig_playback_stream(struct hda_codec *codec, hda_nid_t nid,
++                               unsigned int stream_tag, unsigned int format)
++{
++      /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
++      if (codec->spdif_ctls & AC_DIG1_ENABLE)
++              snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
++                                  codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
++      snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
++      /* turn on again (if needed) */
++      if (codec->spdif_ctls & AC_DIG1_ENABLE)
++              snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
++                                  codec->spdif_ctls & 0xff);
++}
++
+ static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+                                       struct hda_codec *codec,
+                                       unsigned int stream_tag,
+@@ -406,8 +762,20 @@
+                                       struct snd_pcm_substream *substream)
+ {
+       struct via_spec *spec = codec->spec;
+-      return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
+-                                           stream_tag, format, substream);
++      hda_nid_t nid;
++
++      /* 1st or 2nd S/PDIF */
++      if (substream->number == 0)
++              nid = spec->multiout.dig_out_nid;
++      else if (substream->number == 1)
++              nid = spec->extra_dig_out_nid;
++      else
++              return -1;
++
++      mutex_lock(&codec->spdif_mutex);
++      setup_dig_playback_stream(codec, nid, stream_tag, format);
++      mutex_unlock(&codec->spdif_mutex);
++      return 0;
+ }
+ /*
+@@ -436,14 +804,14 @@
+ }
+ static struct hda_pcm_stream vt1708_pcm_analog_playback = {
+-      .substreams = 1,
++      .substreams = 2,
+       .channels_min = 2,
+       .channels_max = 8,
+       .nid = 0x10, /* NID to query formats and rates */
+       .ops = {
+               .open = via_playback_pcm_open,
+-              .prepare = via_playback_pcm_prepare,
+-              .cleanup = via_playback_pcm_cleanup
++              .prepare = via_playback_multi_pcm_prepare,
++              .cleanup = via_playback_multi_pcm_cleanup
+       },
+ };
+@@ -515,6 +883,13 @@
+               if (err < 0)
+                       return err;
+               spec->multiout.share_spdif = 1;
++
++              if (spec->extra_dig_out_nid) {
++                      err = snd_hda_create_spdif_out_ctls(codec,
++                                                  spec->extra_dig_out_nid);
++                      if (err < 0)
++                              return err;
++              }
+       }
+       if (spec->dig_in_nid) {
+               err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
+@@ -580,10 +955,89 @@
+       kfree(codec->spec);
+ }
++/* mute internal speaker if HP is plugged */
++static void via_hp_automute(struct hda_codec *codec)
++{
++      unsigned int present;
++      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);
++}
++
++static void via_gpio_control(struct hda_codec *codec)
++{
++      unsigned int gpio_data;
++      unsigned int vol_counter;
++      unsigned int vol;
++      unsigned int master_vol;
++
++      struct via_spec *spec = codec->spec;
++
++      gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
++                                     AC_VERB_GET_GPIO_DATA, 0) & 0x03;
++
++      vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
++                                        0xF84, 0) & 0x3F0000) >> 16;
++
++      vol = vol_counter & 0x1F;
++      master_vol = snd_hda_codec_read(codec, 0x1A, 0,
++                                      AC_VERB_GET_AMP_GAIN_MUTE,
++                                      AC_AMP_GET_INPUT);
++
++      if (gpio_data == 0x02) {
++              /* unmute line out */
++              snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0],
++                                       HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
++
++              if (vol_counter & 0x20) {
++                      /* decrease volume */
++                      if (vol > master_vol)
++                              vol = master_vol;
++                      snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
++                                               0, HDA_AMP_VOLMASK,
++                                               master_vol-vol);
++              } else {
++                      /* increase volume */
++                      snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
++                                       HDA_AMP_VOLMASK,
++                                       ((master_vol+vol) > 0x2A) ? 0x2A :
++                                        (master_vol+vol));
++              }
++      } else if (!(gpio_data & 0x02)) {
++              /* mute line out */
++              snd_hda_codec_amp_stereo(codec,
++                                       spec->autocfg.line_out_pins[0],
++                                       HDA_OUTPUT, 0, HDA_AMP_MUTE,
++                                       HDA_AMP_MUTE);
++      }
++}
++
++/* unsolicited event for jack sensing */
++static void via_unsol_event(struct hda_codec *codec,
++                                unsigned int res)
++{
++      res >>= 26;
++      if (res == VIA_HP_EVENT)
++              via_hp_automute(codec);
++      else if (res == VIA_GPIO_EVENT)
++              via_gpio_control(codec);
++}
++
++static hda_nid_t slave_dig_outs[] = {
++      0,
++};
++
+ static int via_init(struct hda_codec *codec)
+ {
+       struct via_spec *spec = codec->spec;
+-      snd_hda_sequence_write(codec, spec->init_verbs);
++      int i;
++      for (i = 0; i < spec->num_iverbs; i++)
++              snd_hda_sequence_write(codec, spec->init_verbs[i]);
++
+       /* Lydia Add for EAPD enable */
+       if (!spec->dig_in_nid) { /* No Digital In connection */
+               if (IS_VT1708_VENDORID(codec->vendor_id)) {
+@@ -611,6 +1065,9 @@
+               snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
+                                   AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
++      /* no slave outs */
++      codec->slave_dig_outs = slave_dig_outs;
++
+       return 0;
+ }
+@@ -657,10 +1114,10 @@
+                               spec->multiout.dac_nids[i] = 0x12;
+                               break;
+                       case AUTO_SEQ_SURROUND:
+-                              spec->multiout.dac_nids[i] = 0x13;
++                              spec->multiout.dac_nids[i] = 0x11;
+                               break;
+                       case AUTO_SEQ_SIDE:
+-                              spec->multiout.dac_nids[i] = 0x11;
++                              spec->multiout.dac_nids[i] = 0x13;
+                               break;
+                       }
+               }
+@@ -685,7 +1142,7 @@
+                       continue;
+               
+               if (i != AUTO_SEQ_FRONT)
+-                      nid_vol = 0x1b - i + 1;
++                      nid_vol = 0x18 + i;
+               if (i == AUTO_SEQ_CENLFE) {
+                       /* Center/LFE */
+@@ -760,6 +1217,24 @@
+       return 0;
+ }
++static void create_hp_imux(struct via_spec *spec)
++{
++      int i;
++      struct hda_input_mux *imux = &spec->private_imux[1];
++      static const char *texts[] = { "OFF", "ON", NULL};
++
++      /* for hp mode select */
++      i = 0;
++      while (texts[i] != NULL) {
++              imux->items[imux->num_items].label =  texts[i];
++              imux->items[imux->num_items].index = i;
++              imux->num_items++;
++              i++;
++      }
++
++      spec->hp_mux = &spec->private_imux[1];
++}
++
+ static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
+ {
+       int err;
+@@ -780,6 +1255,8 @@
+       if (err < 0)
+               return err;
++      create_hp_imux(spec);
++
+       return 0;
+ }
+@@ -790,7 +1267,7 @@
+       static char *labels[] = {
+               "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
+       };
+-      struct hda_input_mux *imux = &spec->private_imux;
++      struct hda_input_mux *imux = &spec->private_imux[0];
+       int i, err, idx = 0;
+       /* for internal loopback recording select */
+@@ -840,11 +1317,36 @@
+ };
+ #endif
++static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
++{
++      unsigned int def_conf;
++      unsigned char seqassoc;
++
++      def_conf = snd_hda_codec_read(codec, nid, 0,
++                                    AC_VERB_GET_CONFIG_DEFAULT, 0);
++      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_write(codec, nid, 0,
++                                          AC_VERB_SET_CONFIG_DEFAULT_BYTES_3,
++                                          def_conf >> 24);
++              }
++      }
++
++      return;
++}
++
+ static int vt1708_parse_auto_config(struct hda_codec *codec)
+ {
+       struct via_spec *spec = codec->spec;
+       int err;
++      /* Add HP and CD pin config connect bit re-config action */
++      vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
++      vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
++
+       err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
+       if (err < 0)
+               return err;
+@@ -874,9 +1376,12 @@
+       if (spec->kctl_alloc)
+               spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+-      spec->init_verbs = vt1708_volume_init_verbs;    
++      spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs;
++
++      spec->input_mux = &spec->private_imux[0];
+-      spec->input_mux = &spec->private_imux;
++      if (spec->hp_mux)
++              spec->mixers[spec->num_mixers++] = via_hp_mixer;
+       return 1;
+ }
+@@ -897,7 +1402,7 @@
+       int err;
+       /* create a codec specific record */
+-      spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
++      spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (spec == NULL)
+               return -ENOMEM;
+@@ -966,6 +1471,11 @@
+       { } /* end */
+ };
++static struct hda_verb vt1709_uniwill_init_verbs[] = {
++      {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT},
++      { }
++};
++
+ /*
+  * generic initialization of ADC, input mixers and output mixers
+  */
+@@ -1090,11 +1600,11 @@
+                                       break;
+                               case AUTO_SEQ_SURROUND:
+                                       /* AOW3 */
+-                                      spec->multiout.dac_nids[i] = 0x27;
++                                      spec->multiout.dac_nids[i] = 0x11;
+                                       break;
+                               case AUTO_SEQ_SIDE:
+                                       /* AOW1 */
+-                                      spec->multiout.dac_nids[i] = 0x11;
++                                      spec->multiout.dac_nids[i] = 0x27;
+                                       break;
+                               default:
+                                       break;
+@@ -1203,26 +1713,26 @@
+               } 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(0x29, 3, 0,
++                                            HDA_COMPOSE_AMP_VAL(0x1a, 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(0x1a, 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(0x1a, 3, 0,
++                                            HDA_COMPOSE_AMP_VAL(0x29, 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(0x29, 3, 0,
+                                                                 HDA_OUTPUT));
+                       if (err < 0)
+                               return err;
+@@ -1265,7 +1775,7 @@
+       static char *labels[] = {
+               "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
+       };
+-      struct hda_input_mux *imux = &spec->private_imux;
++      struct hda_input_mux *imux = &spec->private_imux[0];
+       int i, err, idx = 0;
+       /* for internal loopback recording select */
+@@ -1339,7 +1849,10 @@
+       if (spec->kctl_alloc)
+               spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+-      spec->input_mux = &spec->private_imux;
++      spec->input_mux = &spec->private_imux[0];
++
++      if (spec->hp_mux)
++              spec->mixers[spec->num_mixers++] = via_hp_mixer;
+       return 1;
+ }
+@@ -1360,7 +1873,7 @@
+       int err;
+       /* create a codec specific record */
+-      spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
++      spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (spec == NULL)
+               return -ENOMEM;
+@@ -1375,7 +1888,8 @@
+                      "Using genenic mode...\n");
+       }
+-      spec->init_verbs = vt1709_10ch_volume_init_verbs;       
++      spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs;
++      spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
+       spec->stream_name_analog = "VT1709 Analog";
+       spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback;
+@@ -1396,6 +1910,7 @@
+       codec->patch_ops = via_patch_ops;
+       codec->patch_ops.init = via_auto_init;
++      codec->patch_ops.unsol_event = via_unsol_event;
+ #ifdef CONFIG_SND_HDA_POWER_SAVE
+       spec->loopback.amplist = vt1709_loopbacks;
+ #endif
+@@ -1451,7 +1966,7 @@
+       int err;
+       /* create a codec specific record */
+-      spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
++      spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (spec == NULL)
+               return -ENOMEM;
+@@ -1466,7 +1981,8 @@
+                      "Using genenic mode...\n");
+       }
+-      spec->init_verbs = vt1709_6ch_volume_init_verbs;        
++      spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs;
++      spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
+       spec->stream_name_analog = "VT1709 Analog";
+       spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback;
+@@ -1487,6 +2003,7 @@
+       codec->patch_ops = via_patch_ops;
+       codec->patch_ops.init = via_auto_init;
++      codec->patch_ops.unsol_event = via_unsol_event;
+ #ifdef CONFIG_SND_HDA_POWER_SAVE
+       spec->loopback.amplist = vt1709_loopbacks;
+ #endif
+@@ -1586,27 +2103,32 @@
+       { }
+ };
++static struct hda_verb vt1708B_uniwill_init_verbs[] = {
++      {0x1D, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT},
++      { }
++};
++
+ static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = {
+-      .substreams = 1,
++      .substreams = 2,
+       .channels_min = 2,
+       .channels_max = 8,
+       .nid = 0x10, /* NID to query formats and rates */
+       .ops = {
+               .open = via_playback_pcm_open,
+-              .prepare = via_playback_pcm_prepare,
+-              .cleanup = via_playback_pcm_cleanup
++              .prepare = via_playback_multi_pcm_prepare,
++              .cleanup = via_playback_multi_pcm_cleanup
+       },
+ };
+ static struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = {
+-      .substreams = 1,
++      .substreams = 2,
+       .channels_min = 2,
+       .channels_max = 4,
+       .nid = 0x10, /* NID to query formats and rates */
+       .ops = {
+               .open = via_playback_pcm_open,
+-              .prepare = via_playback_pcm_prepare,
+-              .cleanup = via_playback_pcm_cleanup
++              .prepare = via_playback_multi_pcm_prepare,
++              .cleanup = via_playback_multi_pcm_cleanup
+       },
+ };
+@@ -1662,10 +2184,10 @@
+                               spec->multiout.dac_nids[i] = 0x24;
+                               break;
+                       case AUTO_SEQ_SURROUND:
+-                              spec->multiout.dac_nids[i] = 0x25;
++                              spec->multiout.dac_nids[i] = 0x11;
+                               break;
+                       case AUTO_SEQ_SIDE:
+-                              spec->multiout.dac_nids[i] = 0x11;
++                              spec->multiout.dac_nids[i] = 0x25;
+                               break;
+                       }
+               }
+@@ -1680,7 +2202,7 @@
+ {
+       char name[32];
+       static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
+-      hda_nid_t nid_vols[] = {0x16, 0x27, 0x26, 0x18};
++      hda_nid_t nid_vols[] = {0x16, 0x18, 0x26, 0x27};
+       hda_nid_t nid, nid_vol = 0;
+       int i, err;
+@@ -1785,6 +2307,8 @@
+       if (err < 0)
+               return err;
++      create_hp_imux(spec);
++
+       return 0;
+ }
+@@ -1795,7 +2319,7 @@
+       static char *labels[] = {
+               "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
+       };
+-      struct hda_input_mux *imux = &spec->private_imux;
++      struct hda_input_mux *imux = &spec->private_imux[0];
+       int i, err, idx = 0;
+       /* for internal loopback recording select */
+@@ -1869,7 +2393,10 @@
+       if (spec->kctl_alloc)
+               spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+-      spec->input_mux = &spec->private_imux;
++      spec->input_mux = &spec->private_imux[0];
++
++      if (spec->hp_mux)
++              spec->mixers[spec->num_mixers++] = via_hp_mixer;
+       return 1;
+ }
+@@ -1890,7 +2417,7 @@
+       int err;
+       /* create a codec specific record */
+-      spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
++      spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (spec == NULL)
+               return -ENOMEM;
+@@ -1906,7 +2433,8 @@
+                      "from BIOS.  Using genenic mode...\n");
+       }
+-      spec->init_verbs = vt1708B_8ch_volume_init_verbs;
++      spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs;
++      spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
+       spec->stream_name_analog = "VT1708B Analog";
+       spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback;
+@@ -1926,6 +2454,7 @@
+       codec->patch_ops = via_patch_ops;
+       codec->patch_ops.init = via_auto_init;
++      codec->patch_ops.unsol_event = via_unsol_event;
+ #ifdef CONFIG_SND_HDA_POWER_SAVE
+       spec->loopback.amplist = vt1708B_loopbacks;
+ #endif
+@@ -1939,7 +2468,7 @@
+       int err;
+       /* create a codec specific record */
+-      spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
++      spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (spec == NULL)
+               return -ENOMEM;
+@@ -1955,7 +2484,8 @@
+                      "from BIOS.  Using genenic mode...\n");
+       }
+-      spec->init_verbs = vt1708B_4ch_volume_init_verbs;
++      spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs;
++      spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
+       spec->stream_name_analog = "VT1708B Analog";
+       spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback;
+@@ -1975,6 +2505,7 @@
+       codec->patch_ops = via_patch_ops;
+       codec->patch_ops.init = via_auto_init;
++      codec->patch_ops.unsol_event = via_unsol_event;
+ #ifdef CONFIG_SND_HDA_POWER_SAVE
+       spec->loopback.amplist = vt1708B_loopbacks;
+ #endif
+@@ -1982,6 +2513,752 @@
+       return 0;
+ }
++/* 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),
++      {
++              .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 vt1708S_volume_init_verbs[] = {
++      /* Unmute ADC0-1 and set the default input to mic-in */
++      {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++      {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++
++      /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the
++       * analog-loopback mixer widget */
++      /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
++      {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++      {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
++      {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
++      {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
++      {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
++
++      /* Setup default input of PW4 to MW0 */
++      {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
++      /* PW9, PW10  Output enable */
++      {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
++      {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
++      /* Enable Mic Boost Volume backdoor */
++      {0x1, 0xf98, 0x1},
++      { }
++};
++
++static struct hda_verb vt1708S_uniwill_init_verbs[] = {
++      {0x1D, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT},
++      { }
++};
++
++static struct hda_pcm_stream vt1708S_pcm_analog_playback = {
++      .substreams = 2,
++      .channels_min = 2,
++      .channels_max = 8,
++      .nid = 0x10, /* NID to query formats and rates */
++      .ops = {
++              .open = via_playback_pcm_open,
++              .prepare = via_playback_pcm_prepare,
++              .cleanup = via_playback_pcm_cleanup
++      },
++};
++
++static struct hda_pcm_stream vt1708S_pcm_analog_capture = {
++      .substreams = 2,
++      .channels_min = 2,
++      .channels_max = 2,
++      .nid = 0x13, /* NID to query formats and rates */
++      .ops = {
++              .prepare = via_capture_pcm_prepare,
++              .cleanup = via_capture_pcm_cleanup
++      },
++};
++
++static struct hda_pcm_stream vt1708S_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
++      },
++};
++
++/* fill in the dac_nids table from the parsed pin configuration */
++static int vt1708S_auto_fill_dac_nids(struct via_spec *spec,
++                                   const struct auto_pin_cfg *cfg)
++{
++      int i;
++      hda_nid_t nid;
++
++      spec->multiout.num_dacs = cfg->line_outs;
++
++      spec->multiout.dac_nids = spec->private_dac_nids;
++
++      for (i = 0; i < 4; i++) {
++              nid = cfg->line_out_pins[i];
++              if (nid) {
++                      /* config dac list */
++                      switch (i) {
++                      case AUTO_SEQ_FRONT:
++                              spec->multiout.dac_nids[i] = 0x10;
++                              break;
++                      case AUTO_SEQ_CENLFE:
++                              spec->multiout.dac_nids[i] = 0x24;
++                              break;
++                      case AUTO_SEQ_SURROUND:
++                              spec->multiout.dac_nids[i] = 0x11;
++                              break;
++                      case AUTO_SEQ_SIDE:
++                              spec->multiout.dac_nids[i] = 0x25;
++                              break;
++                      }
++              }
++      }
++
++      return 0;
++}
++
++/* add playback controls from the parsed DAC table */
++static int vt1708S_auto_create_multi_out_ctls(struct via_spec *spec,
++                                           const struct auto_pin_cfg *cfg)
++{
++      char name[32];
++      static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
++      hda_nid_t nid_vols[] = {0x10, 0x11, 0x24, 0x25};
++      hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x26, 0x27};
++      hda_nid_t nid, nid_vol, nid_mute;
++      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) {
++                      /* add control to mixer index 0 */
++                      err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
++                                            "Master Front Playback Volume",
++                                            HDA_COMPOSE_AMP_VAL(0x16, 3, 0,
++                                                                HDA_INPUT));
++                      if (err < 0)
++                              return err;
++                      err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
++                                            "Master Front Playback Switch",
++                                            HDA_COMPOSE_AMP_VAL(0x16, 3, 0,
++                                                                HDA_INPUT));
++                      if (err < 0)
++                              return err;
++
++                      /* Front */
++                      sprintf(name, "%s Playback Volume", chname[i]);
++                      err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
++                                            HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
++                                                                HDA_OUTPUT));
++                      if (err < 0)
++                              return err;
++                      sprintf(name, "%s Playback Switch", chname[i]);
++                      err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
++                                            HDA_COMPOSE_AMP_VAL(nid_mute,
++                                                                3, 0,
++                                                                HDA_OUTPUT));
++                      if (err < 0)
++                              return err;
++              } else {
++                      sprintf(name, "%s Playback Volume", chname[i]);
++                      err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
++                                            HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
++                                                                HDA_OUTPUT));
++                      if (err < 0)
++                              return err;
++                      sprintf(name, "%s Playback Switch", chname[i]);
++                      err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
++                                            HDA_COMPOSE_AMP_VAL(nid_mute,
++                                                                3, 0,
++                                                                HDA_OUTPUT));
++                      if (err < 0)
++                              return err;
++              }
++      }
++
++      return 0;
++}
++
++static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
++{
++      int err;
++
++      if (!pin)
++              return 0;
++
++      spec->multiout.hp_nid = VT1708S_HP_NID; /* AOW3 */
++
++      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, cfg->input_pins[i], 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 vt1708S_parse_auto_config(struct hda_codec *codec)
++{
++      struct via_spec *spec = codec->spec;
++      int err;
++      static hda_nid_t vt1708s_ignore[] = {0x21, 0};
++
++      err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
++                                         vt1708s_ignore);
++      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;
++
++      if (spec->autocfg.dig_out_pin)
++              spec->multiout.dig_out_nid = VT1708S_DIGOUT_NID;
++
++      spec->extra_dig_out_nid = 0x15;
++
++      if (spec->kctl_alloc)
++              spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
++
++      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 vt1708S_loopbacks[] = {
++      { 0x16, HDA_INPUT, 1 },
++      { 0x16, HDA_INPUT, 2 },
++      { 0x16, HDA_INPUT, 3 },
++      { 0x16, HDA_INPUT, 4 },
++      { } /* end */
++};
++#endif
++
++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;
++
++      spec->stream_name_analog = "VT1708S Analog";
++      spec->stream_analog_playback = &vt1708S_pcm_analog_playback;
++      spec->stream_analog_capture = &vt1708S_pcm_analog_capture;
++
++      spec->stream_name_digital = "VT1708S Digital";
++      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);
++              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
++
++      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},
++      { }
++};
++
++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_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
++      },
++};
++
++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 = {
++              .prepare = via_capture_pcm_prepare,
++              .cleanup = via_capture_pcm_cleanup
++      },
++};
++
++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
++      },
++};
++
++/* 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;
++
++      if (!pin)
++              return 0;
++
++      spec->multiout.hp_nid = 0x1D;
++
++      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;
++
++      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,
++                                              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, cfg->input_pins[i],
++                                         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;
++      static hda_nid_t vt1702_ignore[] = {0x1C, 0};
++
++      err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
++                                         vt1702_ignore);
++      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;
++      err = vt1702_auto_create_analog_input_ctls(spec, &spec->autocfg);
++      if (err < 0)
++              return err;
++
++      spec->multiout.max_channels = spec->multiout.num_dacs * 2;
++
++      if (spec->autocfg.dig_out_pin)
++              spec->multiout.dig_out_nid = VT1702_DIGOUT_NID;
++
++      spec->extra_dig_out_nid = 0x1B;
++
++      if (spec->kctl_alloc)
++              spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
++
++      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;
++      unsigned int response;
++      unsigned char control;
++
++      /* 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);
++              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
++
++      /* 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;
++}
++
+ /*
+  * patch entries
+  */
+@@ -2022,5 +3299,37 @@
+         .patch = patch_vt1708B_4ch},
+       { .id = 0x1106E727, .name = "VIA VT1708B 4-Ch",
+         .patch = patch_vt1708B_4ch},
++      { .id = 0x11060397, .name = "VIA VT1708S",
++        .patch = patch_vt1708S},
++      { .id = 0x11061397, .name = "VIA VT1708S",
++        .patch = patch_vt1708S},
++      { .id = 0x11062397, .name = "VIA VT1708S",
++        .patch = patch_vt1708S},
++      { .id = 0x11063397, .name = "VIA VT1708S",
++        .patch = patch_vt1708S},
++      { .id = 0x11064397, .name = "VIA VT1708S",
++        .patch = patch_vt1708S},
++      { .id = 0x11065397, .name = "VIA VT1708S",
++        .patch = patch_vt1708S},
++      { .id = 0x11066397, .name = "VIA VT1708S",
++        .patch = patch_vt1708S},
++      { .id = 0x11067397, .name = "VIA VT1708S",
++        .patch = patch_vt1708S},
++      { .id = 0x11060398, .name = "VIA VT1702",
++        .patch = patch_vt1702},
++      { .id = 0x11061398, .name = "VIA VT1702",
++        .patch = patch_vt1702},
++      { .id = 0x11062398, .name = "VIA VT1702",
++        .patch = patch_vt1702},
++      { .id = 0x11063398, .name = "VIA VT1702",
++        .patch = patch_vt1702},
++      { .id = 0x11064398, .name = "VIA VT1702",
++        .patch = patch_vt1702},
++      { .id = 0x11065398, .name = "VIA VT1702",
++        .patch = patch_vt1702},
++      { .id = 0x11066398, .name = "VIA VT1702",
++        .patch = patch_vt1702},
++      { .id = 0x11067398, .name = "VIA VT1702",
++        .patch = patch_vt1702},
+       {} /* terminator */
+ };