]> err.no Git - linux-2.6/blobdiff - sound/pci/hda/patch_analog.c
[ALSA] hda-codec - Add internal mic item for ALC268 acer model
[linux-2.6] / sound / pci / hda / patch_analog.c
index 4659fdeec3dc2657432810155ae61f7bf9fe92e1..87db3c410a10a526d300dc2a722cc7ae0dd1f540 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * HD audio interface patch for AD1884, AD1981HD, AD1983, AD1984, AD1986A,
- *   AD1988
+ * HD audio interface patch for AD1882, AD1884, AD1981HD, AD1983, AD1984,
+ *   AD1986A, AD1988
  *
  * Copyright (c) 2005-2007 Takashi Iwai <tiwai@suse.de>
  *
@@ -19,7 +19,6 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
@@ -29,6 +28,7 @@
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_local.h"
+#include "hda_patch.h"
 
 struct ad198x_spec {
        struct snd_kcontrol_new *mixers[5];
@@ -72,7 +72,17 @@ struct ad198x_spec {
        unsigned int num_kctl_alloc, num_kctl_used;
        struct snd_kcontrol_new *kctl_alloc;
        struct hda_input_mux private_imux;
-       hda_nid_t private_dac_nids[4];
+       hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
+
+       unsigned int jack_present :1;
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       struct hda_loopback_check loopback;
+#endif
+       /* for virtual master */
+       hda_nid_t vmaster_nid;
+       const char **slave_vols;
+       const char **slave_sws;
 };
 
 /*
@@ -120,6 +130,32 @@ static int ad198x_init(struct hda_codec *codec)
        return 0;
 }
 
+static const char *ad_slave_vols[] = {
+       "Front Playback Volume",
+       "Surround Playback Volume",
+       "Center Playback Volume",
+       "LFE Playback Volume",
+       "Side Playback Volume",
+       "Headphone Playback Volume",
+       "Mono Playback Volume",
+       "Speaker Playback Volume",
+       "IEC958 Playback Volume",
+       NULL
+};
+
+static const char *ad_slave_sws[] = {
+       "Front Playback Switch",
+       "Surround Playback Switch",
+       "Center Playback Switch",
+       "LFE Playback Switch",
+       "Side Playback Switch",
+       "Headphone Playback Switch",
+       "Mono Playback Switch",
+       "Speaker Playback Switch",
+       "IEC958 Playback Switch",
+       NULL
+};
+
 static int ad198x_build_controls(struct hda_codec *codec)
 {
        struct ad198x_spec *spec = codec->spec;
@@ -135,15 +171,50 @@ static int ad198x_build_controls(struct hda_codec *codec)
                err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
                if (err < 0)
                        return err;
+               err = snd_hda_create_spdif_share_sw(codec,
+                                                   &spec->multiout);
+               if (err < 0)
+                       return err;
+               spec->multiout.share_spdif = 1;
        } 
        if (spec->dig_in_nid) {
                err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
                if (err < 0)
                        return err;
        }
+
+       /* if we have no master control, let's create it */
+       if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
+               unsigned int vmaster_tlv[4];
+               snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
+                                       HDA_OUTPUT, vmaster_tlv);
+               err = snd_hda_add_vmaster(codec, "Master Playback Volume",
+                                         vmaster_tlv,
+                                         (spec->slave_vols ?
+                                          spec->slave_vols : ad_slave_vols));
+               if (err < 0)
+                       return err;
+       }
+       if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
+               err = snd_hda_add_vmaster(codec, "Master Playback Switch",
+                                         NULL,
+                                         (spec->slave_sws ?
+                                          spec->slave_sws : ad_slave_sws));
+               if (err < 0)
+                       return err;
+       }
+
        return 0;
 }
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid)
+{
+       struct ad198x_spec *spec = codec->spec;
+       return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
+}
+#endif
+
 /*
  * Analog playback callbacks
  */
@@ -152,7 +223,8 @@ static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo,
                                    struct snd_pcm_substream *substream)
 {
        struct ad198x_spec *spec = codec->spec;
-       return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
+       return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
+                                            hinfo);
 }
 
 static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
@@ -294,6 +366,7 @@ static int ad198x_build_pcms(struct hda_codec *codec)
                info++;
                codec->num_pcms++;
                info->name = "AD198x Digital";
+               info->pcm_type = HDA_PCM_TYPE_SPDIF;
                info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_digital_playback;
                info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
                if (spec->dig_in_nid) {
@@ -318,30 +391,13 @@ static void ad198x_free(struct hda_codec *codec)
        kfree(codec->spec);
 }
 
-#ifdef CONFIG_PM
-static int ad198x_resume(struct hda_codec *codec)
-{
-       struct ad198x_spec *spec = codec->spec;
-       int i;
-
-       codec->patch_ops.init(codec);
-       for (i = 0; i < spec->num_mixers; i++)
-               snd_hda_resume_ctls(codec, spec->mixers[i]);
-       if (spec->multiout.dig_out_nid)
-               snd_hda_resume_spdif_out(codec);
-       if (spec->dig_in_nid)
-               snd_hda_resume_spdif_in(codec);
-       return 0;
-}
-#endif
-
 static struct hda_codec_ops ad198x_patch_ops = {
        .build_controls = ad198x_build_controls,
        .build_pcms = ad198x_build_pcms,
        .init = ad198x_init,
        .free = ad198x_free,
-#ifdef CONFIG_PM
-       .resume = ad198x_resume,
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       .check_power_status = ad198x_check_power_status,
 #endif
 };
 
@@ -350,15 +406,7 @@ static struct hda_codec_ops ad198x_patch_ops = {
  * EAPD control
  * the private value = nid | (invert << 8)
  */
-static int ad198x_eapd_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;
-}
+#define ad198x_eapd_info       snd_ctl_boolean_mono_info
 
 static int ad198x_eapd_get(struct snd_kcontrol *kcontrol,
                           struct snd_ctl_elem_value *ucontrol)
@@ -381,15 +429,15 @@ static int ad198x_eapd_put(struct snd_kcontrol *kcontrol,
        int invert = (kcontrol->private_value >> 8) & 1;
        hda_nid_t nid = kcontrol->private_value & 0xff;
        unsigned int eapd;
-       eapd = ucontrol->value.integer.value[0];
+       eapd = !!ucontrol->value.integer.value[0];
        if (invert)
                eapd = !eapd;
-       if (eapd == spec->cur_eapd && ! codec->in_resume)
+       if (eapd == spec->cur_eapd)
                return 0;
        spec->cur_eapd = eapd;
-       snd_hda_codec_write(codec, nid,
-                           0, AC_VERB_SET_EAPD_BTLENABLE,
-                           eapd ? 0x02 : 0x00);
+       snd_hda_codec_write_cache(codec, nid,
+                                 0, AC_VERB_SET_EAPD_BTLENABLE,
+                                 eapd ? 0x02 : 0x00);
        return 1;
 }
 
@@ -430,94 +478,36 @@ static struct hda_input_mux ad1986a_capture_source = {
        },
 };
 
-/*
- * PCM control
- *
- * bind volumes/mutes of 3 DACs as a single PCM control for simplicity
- */
-
-#define ad1986a_pcm_amp_vol_info       snd_hda_mixer_amp_volume_info
-
-static int ad1986a_pcm_amp_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       struct ad198x_spec *ad = codec->spec;
-
-       mutex_lock(&ad->amp_mutex);
-       snd_hda_mixer_amp_volume_get(kcontrol, ucontrol);
-       mutex_unlock(&ad->amp_mutex);
-       return 0;
-}
-
-static int ad1986a_pcm_amp_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       struct ad198x_spec *ad = codec->spec;
-       int i, change = 0;
-
-       mutex_lock(&ad->amp_mutex);
-       for (i = 0; i < ARRAY_SIZE(ad1986a_dac_nids); i++) {
-               kcontrol->private_value = HDA_COMPOSE_AMP_VAL(ad1986a_dac_nids[i], 3, 0, HDA_OUTPUT);
-               change |= snd_hda_mixer_amp_volume_put(kcontrol, ucontrol);
-       }
-       kcontrol->private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT);
-       mutex_unlock(&ad->amp_mutex);
-       return change;
-}
-
-#define ad1986a_pcm_amp_sw_info                snd_hda_mixer_amp_switch_info
-
-static int ad1986a_pcm_amp_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       struct ad198x_spec *ad = codec->spec;
-
-       mutex_lock(&ad->amp_mutex);
-       snd_hda_mixer_amp_switch_get(kcontrol, ucontrol);
-       mutex_unlock(&ad->amp_mutex);
-       return 0;
-}
 
-static int ad1986a_pcm_amp_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       struct ad198x_spec *ad = codec->spec;
-       int i, change = 0;
+static struct hda_bind_ctls ad1986a_bind_pcm_vol = {
+       .ops = &snd_hda_bind_vol,
+       .values = {
+               HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
+               HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
+               HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
+               0
+       },
+};
 
-       mutex_lock(&ad->amp_mutex);
-       for (i = 0; i < ARRAY_SIZE(ad1986a_dac_nids); i++) {
-               kcontrol->private_value = HDA_COMPOSE_AMP_VAL(ad1986a_dac_nids[i], 3, 0, HDA_OUTPUT);
-               change |= snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
-       }
-       kcontrol->private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT);
-       mutex_unlock(&ad->amp_mutex);
-       return change;
-}
+static struct hda_bind_ctls ad1986a_bind_pcm_sw = {
+       .ops = &snd_hda_bind_sw,
+       .values = {
+               HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
+               HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
+               HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
+               0
+       },
+};
 
 /*
  * mixers
  */
 static struct snd_kcontrol_new ad1986a_mixers[] = {
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "PCM Playback Volume",
-               .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
-                         SNDRV_CTL_ELEM_ACCESS_TLV_READ |
-                         SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,
-               .info = ad1986a_pcm_amp_vol_info,
-               .get = ad1986a_pcm_amp_vol_get,
-               .put = ad1986a_pcm_amp_vol_put,
-               .tlv = { .c = snd_hda_mixer_amp_tlv },
-               .private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT)
-       },
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "PCM Playback Switch",
-               .info = ad1986a_pcm_amp_sw_info,
-               .get = ad1986a_pcm_amp_sw_get,
-               .put = ad1986a_pcm_amp_sw_put,
-               .private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT)
-       },
+       /*
+        * bind volumes/mutes of 3 DACs as a single PCM control for simplicity
+        */
+       HDA_BIND_VOL("PCM Playback Volume", &ad1986a_bind_pcm_vol),
+       HDA_BIND_SW("PCM Playback Switch", &ad1986a_bind_pcm_sw),
        HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
        HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
        HDA_CODEC_VOLUME("Surround Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
@@ -569,13 +559,30 @@ static struct snd_kcontrol_new ad1986a_3st_mixers[] = {
 /* laptop model - 2ch only */
 static hda_nid_t ad1986a_laptop_dac_nids[1] = { AD1986A_FRONT_DAC };
 
+/* master controls both pins 0x1a and 0x1b */
+static struct hda_bind_ctls ad1986a_laptop_master_vol = {
+       .ops = &snd_hda_bind_vol,
+       .values = {
+               HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
+               HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
+               0,
+       },
+};
+
+static struct hda_bind_ctls ad1986a_laptop_master_sw = {
+       .ops = &snd_hda_bind_sw,
+       .values = {
+               HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
+               HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
+               0,
+       },
+};
+
 static struct snd_kcontrol_new ad1986a_laptop_mixers[] = {
        HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
        HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Master Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Master Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
-       /* HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT),
-          HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT), */
+       HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
+       HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
        HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
        HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
        HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
@@ -603,68 +610,148 @@ static struct snd_kcontrol_new ad1986a_laptop_mixers[] = {
 
 /* laptop-eapd model - 2ch only */
 
-/* master controls both pins 0x1a and 0x1b */
-static int ad1986a_laptop_master_vol_put(struct snd_kcontrol *kcontrol,
-                                        struct snd_ctl_elem_value *ucontrol)
+static struct hda_input_mux ad1986a_laptop_eapd_capture_source = {
+       .num_items = 3,
+       .items = {
+               { "Mic", 0x0 },
+               { "Internal Mic", 0x4 },
+               { "Mix", 0x5 },
+       },
+};
+
+static struct hda_input_mux ad1986a_automic_capture_source = {
+       .num_items = 2,
+       .items = {
+               { "Mic", 0x0 },
+               { "Mix", 0x5 },
+       },
+};
+
+static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
+       HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
+       HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
+       HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Capture Source",
+               .info = ad198x_mux_enum_info,
+               .get = ad198x_mux_enum_get,
+               .put = ad198x_mux_enum_put,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "External Amplifier",
+               .info = ad198x_eapd_info,
+               .get = ad198x_eapd_get,
+               .put = ad198x_eapd_put,
+               .private_value = 0x1b | (1 << 8), /* port-D, inversed */
+       },
+       { } /* end */
+};
+
+/* re-connect the mic boost input according to the jack sensing */
+static void ad1986a_automic(struct hda_codec *codec)
 {
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       long *valp = ucontrol->value.integer.value;
-       int change;
+       unsigned int present;
+       present = snd_hda_codec_read(codec, 0x1f, 0, AC_VERB_GET_PIN_SENSE, 0);
+       /* 0 = 0x1f, 2 = 0x1d, 4 = mixed */
+       snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL,
+                           (present & AC_PINSENSE_PRESENCE) ? 0 : 2);
+}
 
-       change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0,
-                                         0x7f, valp[0] & 0x7f);
-       change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0,
-                                          0x7f, valp[1] & 0x7f);
-       snd_hda_codec_amp_update(codec, 0x1b, 0, HDA_OUTPUT, 0,
-                                0x7f, valp[0] & 0x7f);
-       snd_hda_codec_amp_update(codec, 0x1b, 1, HDA_OUTPUT, 0,
-                                0x7f, valp[1] & 0x7f);
-       return change;
+#define AD1986A_MIC_EVENT              0x36
+
+static void ad1986a_automic_unsol_event(struct hda_codec *codec,
+                                           unsigned int res)
+{
+       if ((res >> 26) != AD1986A_MIC_EVENT)
+               return;
+       ad1986a_automic(codec);
+}
+
+static int ad1986a_automic_init(struct hda_codec *codec)
+{
+       ad198x_init(codec);
+       ad1986a_automic(codec);
+       return 0;
+}
+
+/* laptop-automute - 2ch only */
+
+static void ad1986a_update_hp(struct hda_codec *codec)
+{
+       struct ad198x_spec *spec = codec->spec;
+       unsigned int mute;
+
+       if (spec->jack_present)
+               mute = HDA_AMP_MUTE; /* mute internal speaker */
+       else
+               /* unmute internal speaker if necessary */
+               mute = snd_hda_codec_amp_read(codec, 0x1a, 0, HDA_OUTPUT, 0);
+       snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
+                                HDA_AMP_MUTE, mute);
+}
+
+static void ad1986a_hp_automute(struct hda_codec *codec)
+{
+       struct ad198x_spec *spec = codec->spec;
+       unsigned int present;
+
+       present = snd_hda_codec_read(codec, 0x1a, 0, AC_VERB_GET_PIN_SENSE, 0);
+       /* Lenovo N100 seems to report the reversed bit for HP jack-sensing */
+       spec->jack_present = !(present & 0x80000000);
+       ad1986a_update_hp(codec);
+}
+
+#define AD1986A_HP_EVENT               0x37
+
+static void ad1986a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+       if ((res >> 26) != AD1986A_HP_EVENT)
+               return;
+       ad1986a_hp_automute(codec);
+}
+
+static int ad1986a_hp_init(struct hda_codec *codec)
+{
+       ad198x_init(codec);
+       ad1986a_hp_automute(codec);
+       return 0;
 }
 
-static int ad1986a_laptop_master_sw_put(struct snd_kcontrol *kcontrol,
-                                       struct snd_ctl_elem_value *ucontrol)
+/* bind hp and internal speaker mute (with plug check) */
+static int ad1986a_hp_master_sw_put(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_value *ucontrol)
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        long *valp = ucontrol->value.integer.value;
        int change;
 
        change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0,
-                                         0x80, valp[0] ? 0 : 0x80);
+                                         HDA_AMP_MUTE,
+                                         valp[0] ? 0 : HDA_AMP_MUTE);
        change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0,
-                                          0x80, valp[1] ? 0 : 0x80);
-       snd_hda_codec_amp_update(codec, 0x1b, 0, HDA_OUTPUT, 0,
-                                0x80, valp[0] ? 0 : 0x80);
-       snd_hda_codec_amp_update(codec, 0x1b, 1, HDA_OUTPUT, 0,
-                                0x80, valp[1] ? 0 : 0x80);
+                                          HDA_AMP_MUTE,
+                                          valp[1] ? 0 : HDA_AMP_MUTE);
+       if (change)
+               ad1986a_update_hp(codec);
        return change;
 }
 
-static struct hda_input_mux ad1986a_laptop_eapd_capture_source = {
-       .num_items = 3,
-       .items = {
-               { "Mic", 0x0 },
-               { "Internal Mic", 0x4 },
-               { "Mix", 0x5 },
-       },
-};
-
-static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Master Playback Volume",
-               .info = snd_hda_mixer_amp_volume_info,
-               .get = snd_hda_mixer_amp_volume_get,
-               .put = ad1986a_laptop_master_vol_put,
-               .tlv = { .c = snd_hda_mixer_amp_tlv },
-               .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
-       },
+static struct snd_kcontrol_new ad1986a_laptop_automute_mixers[] = {
+       HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Master Playback Switch",
                .info = snd_hda_mixer_amp_switch_info,
                .get = snd_hda_mixer_amp_switch_get,
-               .put = ad1986a_laptop_master_sw_put,
+               .put = ad1986a_hp_master_sw_put,
                .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
        },
        HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
@@ -674,6 +761,8 @@ static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
        HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
        HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
        HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Beep Playback Volume", 0x18, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Beep Playback Switch", 0x18, 0x0, HDA_OUTPUT),
        HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
        HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
        {
@@ -796,6 +885,15 @@ static struct hda_verb ad1986a_eapd_init_verbs[] = {
        {}
 };
 
+static struct hda_verb ad1986a_automic_verbs[] = {
+       {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       /*{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},*/
+       {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
+       {0x1f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_MIC_EVENT},
+       {}
+};
+
 /* Ultra initialization */
 static struct hda_verb ad1986a_ultra_init[] = {
        /* eapd initialization */
@@ -807,12 +905,20 @@ static struct hda_verb ad1986a_ultra_init[] = {
        { } /* end */
 };
 
+/* pin sensing on HP jack */
+static struct hda_verb ad1986a_hp_init_verbs[] = {
+       {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_HP_EVENT},
+       {}
+};
+
+
 /* models */
 enum {
        AD1986A_6STACK,
        AD1986A_3STACK,
        AD1986A_LAPTOP,
        AD1986A_LAPTOP_EAPD,
+       AD1986A_LAPTOP_AUTOMUTE,
        AD1986A_ULTRA,
        AD1986A_MODELS
 };
@@ -822,39 +928,60 @@ static const char *ad1986a_models[AD1986A_MODELS] = {
        [AD1986A_3STACK]        = "3stack",
        [AD1986A_LAPTOP]        = "laptop",
        [AD1986A_LAPTOP_EAPD]   = "laptop-eapd",
+       [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute",
        [AD1986A_ULTRA]         = "ultra",
 };
 
 static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
        SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD),
-       SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
        SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD),
-       SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
        SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD),
+       SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
        SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD),
        SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD),
        SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD),
        SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD),
+       SND_PCI_QUIRK(0x1043, 0x1443, "ASUS VX1", AD1986A_LAPTOP),
        SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8J", AD1986A_3STACK),
        SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK),
        SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP),
        SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK),
        SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK),
        SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK),
+       SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
+       SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba", AD1986A_LAPTOP_EAPD),
        SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
        SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
        SND_PCI_QUIRK(0x144d, 0xc023, "Samsung X60", AD1986A_LAPTOP_EAPD),
        SND_PCI_QUIRK(0x144d, 0xc024, "Samsung R65", AD1986A_LAPTOP_EAPD),
        SND_PCI_QUIRK(0x144d, 0xc026, "Samsung X11", AD1986A_LAPTOP_EAPD),
-       SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
        SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
+       SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
        SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
        SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),
-       SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_EAPD),
+       SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE),
        SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP),
        {}
 };
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static struct hda_amp_list ad1986a_loopbacks[] = {
+       { 0x13, HDA_OUTPUT, 0 }, /* Mic */
+       { 0x14, HDA_OUTPUT, 0 }, /* Phone */
+       { 0x15, HDA_OUTPUT, 0 }, /* CD */
+       { 0x16, HDA_OUTPUT, 0 }, /* Aux */
+       { 0x17, HDA_OUTPUT, 0 }, /* Line */
+       { } /* end */
+};
+#endif
+
+static int is_jack_available(struct hda_codec *codec, hda_nid_t nid)
+{
+       unsigned int conf = snd_hda_codec_read(codec, nid, 0,
+                                              AC_VERB_GET_CONFIG_DEFAULT, 0);
+       return get_defcfg_connect(conf) != AC_JACK_PORT_NONE;
+}
+
 static int patch_ad1986a(struct hda_codec *codec)
 {
        struct ad198x_spec *spec;
@@ -864,7 +991,6 @@ static int patch_ad1986a(struct hda_codec *codec)
        if (spec == NULL)
                return -ENOMEM;
 
-       mutex_init(&spec->amp_mutex);
        codec->spec = spec;
 
        spec->multiout.max_channels = 6;
@@ -879,6 +1005,10 @@ static int patch_ad1986a(struct hda_codec *codec)
        spec->mixers[0] = ad1986a_mixers;
        spec->num_init_verbs = 1;
        spec->init_verbs[0] = ad1986a_init_verbs;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       spec->loopback.amplist = ad1986a_loopbacks;
+#endif
+       spec->vmaster_nid = 0x1b;
 
        codec->patch_ops = ad198x_patch_ops;
 
@@ -906,13 +1036,31 @@ static int patch_ad1986a(struct hda_codec *codec)
                break;
        case AD1986A_LAPTOP_EAPD:
                spec->mixers[0] = ad1986a_laptop_eapd_mixers;
-               spec->num_init_verbs = 2;
+               spec->num_init_verbs = 3;
                spec->init_verbs[1] = ad1986a_eapd_init_verbs;
+               spec->init_verbs[2] = ad1986a_automic_verbs;
                spec->multiout.max_channels = 2;
                spec->multiout.num_dacs = 1;
                spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
-               spec->multiout.dig_out_nid = 0;
+               if (!is_jack_available(codec, 0x25))
+                       spec->multiout.dig_out_nid = 0;
+               spec->input_mux = &ad1986a_automic_capture_source;
+               codec->patch_ops.unsol_event = ad1986a_automic_unsol_event;
+               codec->patch_ops.init = ad1986a_automic_init;
+               break;
+       case AD1986A_LAPTOP_AUTOMUTE:
+               spec->mixers[0] = ad1986a_laptop_automute_mixers;
+               spec->num_init_verbs = 3;
+               spec->init_verbs[1] = ad1986a_eapd_init_verbs;
+               spec->init_verbs[2] = ad1986a_hp_init_verbs;
+               spec->multiout.max_channels = 2;
+               spec->multiout.num_dacs = 1;
+               spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
+               if (!is_jack_available(codec, 0x25))
+                       spec->multiout.dig_out_nid = 0;
                spec->input_mux = &ad1986a_laptop_eapd_capture_source;
+               codec->patch_ops.unsol_event = ad1986a_hp_unsol_event;
+               codec->patch_ops.init = ad1986a_hp_init;
                break;
        case AD1986A_ULTRA:
                spec->mixers[0] = ad1986a_laptop_eapd_mixers;
@@ -925,6 +1073,14 @@ static int patch_ad1986a(struct hda_codec *codec)
                break;
        }
 
+       /* AD1986A has a hardware problem that it can't share a stream
+        * with multiple output pins.  The copy of front to surrounds
+        * causes noisy or silent outputs at a certain timing, e.g.
+        * changing the volume.
+        * So, let's disable the shared stream.
+        */
+       spec->multiout.no_share_stream = 1;
+
        return 0;
 }
 
@@ -980,10 +1136,13 @@ static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct ad198x_spec *spec = codec->spec;
 
+       if (ucontrol->value.enumerated.item[0] > 1)
+               return -EINVAL;
        if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
                spec->spdif_route = ucontrol->value.enumerated.item[0];
-               snd_hda_codec_write(codec, spec->multiout.dig_out_nid, 0,
-                                   AC_VERB_SET_CONNECT_SEL, spec->spdif_route);
+               snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0,
+                                         AC_VERB_SET_CONNECT_SEL,
+                                         spec->spdif_route);
                return 1;
        }
        return 0;
@@ -1063,6 +1222,13 @@ static struct hda_verb ad1983_init_verbs[] = {
        { } /* end */
 };
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static struct hda_amp_list ad1983_loopbacks[] = {
+       { 0x12, HDA_OUTPUT, 0 }, /* Mic */
+       { 0x13, HDA_OUTPUT, 0 }, /* Line */
+       { } /* end */
+};
+#endif
 
 static int patch_ad1983(struct hda_codec *codec)
 {
@@ -1072,7 +1238,6 @@ static int patch_ad1983(struct hda_codec *codec)
        if (spec == NULL)
                return -ENOMEM;
 
-       mutex_init(&spec->amp_mutex);
        codec->spec = spec;
 
        spec->multiout.max_channels = 2;
@@ -1088,6 +1253,10 @@ static int patch_ad1983(struct hda_codec *codec)
        spec->num_init_verbs = 1;
        spec->init_verbs[0] = ad1983_init_verbs;
        spec->spdif_route = 0;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       spec->loopback.amplist = ad1983_loopbacks;
+#endif
+       spec->vmaster_nid = 0x05;
 
        codec->patch_ops = ad198x_patch_ops;
 
@@ -1211,6 +1380,17 @@ static struct hda_verb ad1981_init_verbs[] = {
        { } /* end */
 };
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static struct hda_amp_list ad1981_loopbacks[] = {
+       { 0x12, HDA_OUTPUT, 0 }, /* Front Mic */
+       { 0x13, HDA_OUTPUT, 0 }, /* Line */
+       { 0x1b, HDA_OUTPUT, 0 }, /* Aux */
+       { 0x1c, HDA_OUTPUT, 0 }, /* Mic */
+       { 0x1d, HDA_OUTPUT, 0 }, /* CD */
+       { } /* end */
+};
+#endif
+
 /*
  * Patch for HP nx6320
  *
@@ -1238,33 +1418,26 @@ static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol,
 
        if (! ad198x_eapd_put(kcontrol, ucontrol))
                return 0;
-
+       /* change speaker pin appropriately */
+       snd_hda_codec_write(codec, 0x05, 0,
+                           AC_VERB_SET_PIN_WIDGET_CONTROL,
+                           spec->cur_eapd ? PIN_OUT : 0);
        /* toggle HP mute appropriately */
-       snd_hda_codec_amp_update(codec, 0x06, 0, HDA_OUTPUT, 0,
-                                0x80, spec->cur_eapd ? 0 : 0x80);
-       snd_hda_codec_amp_update(codec, 0x06, 1, HDA_OUTPUT, 0,
-                                0x80, spec->cur_eapd ? 0 : 0x80);
+       snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0,
+                                HDA_AMP_MUTE,
+                                spec->cur_eapd ? 0 : HDA_AMP_MUTE);
        return 1;
 }
 
 /* bind volumes of both NID 0x05 and 0x06 */
-static int ad1981_hp_master_vol_put(struct snd_kcontrol *kcontrol,
-                                   struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       long *valp = ucontrol->value.integer.value;
-       int change;
-
-       change = snd_hda_codec_amp_update(codec, 0x05, 0, HDA_OUTPUT, 0,
-                                         0x7f, valp[0] & 0x7f);
-       change |= snd_hda_codec_amp_update(codec, 0x05, 1, HDA_OUTPUT, 0,
-                                          0x7f, valp[1] & 0x7f);
-       snd_hda_codec_amp_update(codec, 0x06, 0, HDA_OUTPUT, 0,
-                                0x7f, valp[0] & 0x7f);
-       snd_hda_codec_amp_update(codec, 0x06, 1, HDA_OUTPUT, 0,
-                                0x7f, valp[1] & 0x7f);
-       return change;
-}
+static struct hda_bind_ctls ad1981_hp_bind_master_vol = {
+       .ops = &snd_hda_bind_vol,
+       .values = {
+               HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
+               HDA_COMPOSE_AMP_VAL(0x06, 3, 0, HDA_OUTPUT),
+               0
+       },
+};
 
 /* mute internal speaker if HP is plugged */
 static void ad1981_hp_automute(struct hda_codec *codec)
@@ -1273,10 +1446,8 @@ static void ad1981_hp_automute(struct hda_codec *codec)
 
        present = snd_hda_codec_read(codec, 0x06, 0,
                                     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-       snd_hda_codec_amp_update(codec, 0x05, 0, HDA_OUTPUT, 0,
-                                0x80, present ? 0x80 : 0);
-       snd_hda_codec_amp_update(codec, 0x05, 1, HDA_OUTPUT, 0,
-                                0x80, present ? 0x80 : 0);
+       snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0,
+                                HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
 }
 
 /* toggle input of built-in and mic jack appropriately */
@@ -1327,14 +1498,7 @@ static struct hda_input_mux ad1981_hp_capture_source = {
 };
 
 static struct snd_kcontrol_new ad1981_hp_mixers[] = {
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Master Playback Volume",
-               .info = snd_hda_mixer_amp_volume_info,
-               .get = snd_hda_mixer_amp_volume_get,
-               .put = ad1981_hp_master_vol_put,
-               .private_value = HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
-       },
+       HDA_BIND_VOL("Master Playback Volume", &ad1981_hp_bind_master_vol),
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Master Playback Switch",
@@ -1454,14 +1618,14 @@ static const char *ad1981_models[AD1981_MODELS] = {
 };
 
 static struct snd_pci_quirk ad1981_cfg_tbl[] = {
+       SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
        /* All HP models */
        SND_PCI_QUIRK(0x103c, 0, "HP nx", AD1981_HP),
-       /* HP nx6320 (reversed SSID, H/W bug) */
-       SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
+       SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
        /* Lenovo Thinkpad T60/X60/Z6xx */
        SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1981_THINKPAD),
-       SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
-       SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
+       /* HP nx6320 (reversed SSID, H/W bug) */
+       SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
        {}
 };
 
@@ -1474,7 +1638,6 @@ static int patch_ad1981(struct hda_codec *codec)
        if (spec == NULL)
                return -ENOMEM;
 
-       mutex_init(&spec->amp_mutex);
        codec->spec = spec;
 
        spec->multiout.max_channels = 2;
@@ -1490,6 +1653,10 @@ static int patch_ad1981(struct hda_codec *codec)
        spec->num_init_verbs = 1;
        spec->init_verbs[0] = ad1981_init_verbs;
        spec->spdif_route = 0;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       spec->loopback.amplist = ad1981_loopbacks;
+#endif
+       spec->vmaster_nid = 0x05;
 
        codec->patch_ops = ad198x_patch_ops;
 
@@ -1667,9 +1834,9 @@ static hda_nid_t ad1988_capsrc_nids[3] = {
 static struct hda_input_mux ad1988_6stack_capture_source = {
        .num_items = 5,
        .items = {
-               { "Front Mic", 0x0 },
-               { "Line", 0x1 },
-               { "Mic", 0x4 },
+               { "Front Mic", 0x1 },   /* port-B */
+               { "Line", 0x2 },        /* port-C */
+               { "Mic", 0x4 },         /* port-E */
                { "CD", 0x5 },
                { "Mix", 0x9 },
        },
@@ -1678,7 +1845,7 @@ static struct hda_input_mux ad1988_6stack_capture_source = {
 static struct hda_input_mux ad1988_laptop_capture_source = {
        .num_items = 3,
        .items = {
-               { "Mic/Line", 0x0 },
+               { "Mic/Line", 0x1 },    /* port-B */
                { "CD", 0x5 },
                { "Mix", 0x9 },
        },
@@ -1864,7 +2031,6 @@ static struct snd_kcontrol_new ad1988_capture_mixers[] = {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                /* The multiple "Capture Source" controls confuse alsamixer
                 * So call somewhat different..
-                * FIXME: the controls appear in the "playback" view!
                 */
                /* .name = "Capture Source", */
                .name = "Input Source",
@@ -1897,16 +2063,19 @@ static int ad1988_spdif_playback_source_get(struct snd_kcontrol *kcontrol,
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        unsigned int sel;
 
-       sel = snd_hda_codec_read(codec, 0x02, 0, AC_VERB_GET_CONNECT_SEL, 0);
-       if (sel > 0) {
+       sel = snd_hda_codec_read(codec, 0x1d, 0, AC_VERB_GET_AMP_GAIN_MUTE,
+                                AC_AMP_GET_INPUT);
+       if (!(sel & 0x80))
+               ucontrol->value.enumerated.item[0] = 0;
+       else {
                sel = snd_hda_codec_read(codec, 0x0b, 0,
                                         AC_VERB_GET_CONNECT_SEL, 0);
                if (sel < 3)
                        sel++;
                else
                        sel = 0;
+               ucontrol->value.enumerated.item[0] = sel;
        }
-       ucontrol->value.enumerated.item[0] = sel;
        return 0;
 }
 
@@ -1918,23 +2087,41 @@ static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol,
        int change;
 
        val = ucontrol->value.enumerated.item[0];
-       sel = snd_hda_codec_read(codec, 0x02, 0, AC_VERB_GET_CONNECT_SEL, 0);
+       if (val > 3)
+               return -EINVAL;
        if (!val) {
-               change = sel != 0;
-               if (change || codec->in_resume)
-                       snd_hda_codec_write(codec, 0x02, 0,
-                                           AC_VERB_SET_CONNECT_SEL, 0);
+               sel = snd_hda_codec_read(codec, 0x1d, 0,
+                                        AC_VERB_GET_AMP_GAIN_MUTE,
+                                        AC_AMP_GET_INPUT);
+               change = sel & 0x80;
+               if (change) {
+                       snd_hda_codec_write_cache(codec, 0x1d, 0,
+                                                 AC_VERB_SET_AMP_GAIN_MUTE,
+                                                 AMP_IN_UNMUTE(0));
+                       snd_hda_codec_write_cache(codec, 0x1d, 0,
+                                                 AC_VERB_SET_AMP_GAIN_MUTE,
+                                                 AMP_IN_MUTE(1));
+               }
        } else {
-               change = sel == 0;
-               if (change || codec->in_resume)
-                       snd_hda_codec_write(codec, 0x02, 0,
-                                           AC_VERB_SET_CONNECT_SEL, 1);
+               sel = snd_hda_codec_read(codec, 0x1d, 0,
+                                        AC_VERB_GET_AMP_GAIN_MUTE,
+                                        AC_AMP_GET_INPUT | 0x01);
+               change = sel & 0x80;
+               if (change) {
+                       snd_hda_codec_write_cache(codec, 0x1d, 0,
+                                                 AC_VERB_SET_AMP_GAIN_MUTE,
+                                                 AMP_IN_MUTE(0));
+                       snd_hda_codec_write_cache(codec, 0x1d, 0,
+                                                 AC_VERB_SET_AMP_GAIN_MUTE,
+                                                 AMP_IN_UNMUTE(1));
+               }
                sel = snd_hda_codec_read(codec, 0x0b, 0,
                                         AC_VERB_GET_CONNECT_SEL, 0) + 1;
                change |= sel != val;
-               if (change || codec->in_resume)
-                       snd_hda_codec_write(codec, 0x0b, 0,
-                                           AC_VERB_SET_CONNECT_SEL, val - 1);
+               if (change)
+                       snd_hda_codec_write_cache(codec, 0x0b, 0,
+                                                 AC_VERB_SET_CONNECT_SEL,
+                                                 val - 1);
        }
        return change;
 }
@@ -2016,6 +2203,8 @@ static struct hda_verb ad1988_6stack_init_verbs[] = {
        {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
        {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
        {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
+       /* Analog CD Input */
+       {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
 
        { }
 };
@@ -2047,10 +2236,9 @@ static struct hda_verb ad1988_spdif_init_verbs[] = {
        {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
        {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, /* ADC1 */
        {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+       {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
        /* SPDIF out pin */
        {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
-       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x17}, /* 0dB */
 
        { }
 };
@@ -2225,6 +2413,15 @@ static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
                snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
 } 
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static struct hda_amp_list ad1988_loopbacks[] = {
+       { 0x20, HDA_INPUT, 0 }, /* Front Mic */
+       { 0x20, HDA_INPUT, 1 }, /* Line */
+       { 0x20, HDA_INPUT, 4 }, /* Mic */
+       { 0x20, HDA_INPUT, 6 }, /* CD */
+       { } /* end */
+};
+#endif
 
 /*
  * Automatic parse of I/O pins from the BIOS configuration
@@ -2649,8 +2846,8 @@ static const char *ad1988_models[AD1988_MODEL_LAST] = {
 };
 
 static struct snd_pci_quirk ad1988_cfg_tbl[] = {
-       SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
        SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),
+       SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
        {}
 };
 
@@ -2663,7 +2860,6 @@ static int patch_ad1988(struct hda_codec *codec)
        if (spec == NULL)
                return -ENOMEM;
 
-       mutex_init(&spec->amp_mutex);
        codec->spec = spec;
 
        if (is_rev2(codec))
@@ -2770,6 +2966,10 @@ static int patch_ad1988(struct hda_codec *codec)
                codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;
                break;
        }
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       spec->loopback.amplist = ad1988_loopbacks;
+#endif
+       spec->vmaster_nid = 0x04;
 
        return 0;
 }
@@ -2846,7 +3046,6 @@ static struct snd_kcontrol_new ad1884_base_mixers[] = {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                /* The multiple "Capture Source" controls confuse alsamixer
                 * So call somewhat different..
-                * FIXME: the controls appear in the "playback" view!
                 */
                /* .name = "Capture Source", */
                .name = "Input Source",
@@ -2872,9 +3071,9 @@ static struct snd_kcontrol_new ad1984_dmic_mixers[] = {
        HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT),
        HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT),
        HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0,
-                            HDA_OUTPUT),
+                            HDA_INPUT),
        HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0,
-                          HDA_OUTPUT),
+                          HDA_INPUT),
        { } /* end */
 };
 
@@ -2883,8 +3082,8 @@ static struct snd_kcontrol_new ad1984_dmic_mixers[] = {
  */
 static struct hda_verb ad1884_init_verbs[] = {
        /* DACs; mute as default */
-       {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+       {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
        /* Port-A (HP) mixer */
        {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
        {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
@@ -2926,6 +3125,30 @@ static struct hda_verb ad1884_init_verbs[] = {
        { } /* end */
 };
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static struct hda_amp_list ad1884_loopbacks[] = {
+       { 0x20, HDA_INPUT, 0 }, /* Front Mic */
+       { 0x20, HDA_INPUT, 1 }, /* Mic */
+       { 0x20, HDA_INPUT, 2 }, /* CD */
+       { 0x20, HDA_INPUT, 4 }, /* Docking */
+       { } /* end */
+};
+#endif
+
+static const char *ad1884_slave_vols[] = {
+       "PCM Playback Volume",
+       "Mic Playback Volume",
+       "Mono Playback Volume",
+       "Front Mic Playback Volume",
+       "Mic Playback Volume",
+       "CD Playback Volume",
+       "Internal Mic Playback Volume",
+       "Docking Mic Playback Volume"
+       "Beep Playback Volume",
+       "IEC958 Playback Volume",
+       NULL
+};
+
 static int patch_ad1884(struct hda_codec *codec)
 {
        struct ad198x_spec *spec;
@@ -2950,6 +3173,12 @@ static int patch_ad1884(struct hda_codec *codec)
        spec->num_init_verbs = 1;
        spec->init_verbs[0] = ad1884_init_verbs;
        spec->spdif_route = 0;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       spec->loopback.amplist = ad1884_loopbacks;
+#endif
+       spec->vmaster_nid = 0x04;
+       /* we need to cover all playback volumes */
+       spec->slave_vols = ad1884_slave_vols;
 
        codec->patch_ops = ad198x_patch_ops;
 
@@ -2965,9 +3194,24 @@ static struct hda_input_mux ad1984_thinkpad_capture_source = {
                { "Mic", 0x0 },
                { "Internal Mic", 0x1 },
                { "Mix", 0x3 },
+               { "Docking-Station", 0x4 },
+       },
+};
+
+
+/*
+ * Dell Precision T3400
+ */
+static struct hda_input_mux ad1984_dell_desktop_capture_source = {
+       .num_items = 3,
+       .items = {
+               { "Front Mic", 0x0 },
+               { "Line-In", 0x1 },
+               { "Mix", 0x3 },
        },
 };
 
+
 static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
        HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
        /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
@@ -2982,6 +3226,8 @@ static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
        HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
        HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
        HDA_CODEC_VOLUME("Docking Mic Boost", 0x25, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
+       HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
        HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
        HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
        HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
@@ -2990,7 +3236,6 @@ static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                /* The multiple "Capture Source" controls confuse alsamixer
                 * So call somewhat different..
-                * FIXME: the controls appear in the "playback" view!
                 */
                /* .name = "Capture Source", */
                .name = "Input Source",
@@ -2999,6 +3244,16 @@ static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
                .get = ad198x_mux_enum_get,
                .put = ad198x_mux_enum_put,
        },
+       /* SPDIF controls */
+       HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
+               /* identical with ad1983 */
+               .info = ad1983_spdif_route_info,
+               .get = ad1983_spdif_route_get,
+               .put = ad1983_spdif_route_put,
+       },
        { } /* end */
 };
 
@@ -3011,6 +3266,46 @@ static struct hda_verb ad1984_thinkpad_init_verbs[] = {
        {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
        /* Analog mixer - docking mic; mute as default */
        {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+       /* enable EAPD bit */
+       {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
+       { } /* end */
+};
+
+/*
+ * Dell Precision T3400
+ */
+static struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = {
+       HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
+       HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
+       HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
+       HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
+       /*
+       HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x20, 0x03, HDA_INPUT),
+       HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x20, 0x03, HDA_INPUT),
+       */
+       HDA_CODEC_VOLUME("Line-In Boost", 0x15, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
+       {
+               .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 = ad198x_mux_enum_info,
+               .get = ad198x_mux_enum_get,
+               .put = ad198x_mux_enum_put,
+       },
        { } /* end */
 };
 
@@ -3067,17 +3362,20 @@ static int ad1984_build_pcms(struct hda_codec *codec)
 enum {
        AD1984_BASIC,
        AD1984_THINKPAD,
+       AD1984_DELL_DESKTOP,
        AD1984_MODELS
 };
 
 static const char *ad1984_models[AD1984_MODELS] = {
        [AD1984_BASIC]          = "basic",
        [AD1984_THINKPAD]       = "thinkpad",
+       [AD1984_DELL_DESKTOP]   = "dell_desktop",
 };
 
 static struct snd_pci_quirk ad1984_cfg_tbl[] = {
        /* Lenovo Thinkpad T61/X61 */
        SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1984_THINKPAD),
+       SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
        {}
 };
 
@@ -3099,11 +3397,680 @@ static int patch_ad1984(struct hda_codec *codec)
                codec->patch_ops.build_pcms = ad1984_build_pcms;
                break;
        case AD1984_THINKPAD:
-               spec->multiout.dig_out_nid = 0;
+               spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
                spec->input_mux = &ad1984_thinkpad_capture_source;
                spec->mixers[0] = ad1984_thinkpad_mixers;
                spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
                break;
+       case AD1984_DELL_DESKTOP:
+               spec->multiout.dig_out_nid = 0;
+               spec->input_mux = &ad1984_dell_desktop_capture_source;
+               spec->mixers[0] = ad1984_dell_desktop_mixers;
+               break;
+       }
+       return 0;
+}
+
+
+/*
+ * AD1883 / AD1884A / AD1984A / AD1984B
+ *
+ * port-B (0x14) - front mic-in
+ * port-E (0x1c) - rear mic-in
+ * port-F (0x16) - CD / ext out
+ * port-C (0x15) - rear line-in
+ * port-D (0x12) - rear line-out
+ * port-A (0x11) - front hp-out
+ *
+ * AD1984A = AD1884A + digital-mic
+ * AD1883 = equivalent with AD1984A
+ * AD1984B = AD1984A + extra SPDIF-out
+ *
+ * FIXME:
+ * We share the single DAC for both HP and line-outs (see AD1884/1984).
+ */
+
+static hda_nid_t ad1884a_dac_nids[1] = {
+       0x03,
+};
+
+#define ad1884a_adc_nids       ad1884_adc_nids
+#define ad1884a_capsrc_nids    ad1884_capsrc_nids
+
+#define AD1884A_SPDIF_OUT      0x02
+
+static struct hda_input_mux ad1884a_capture_source = {
+       .num_items = 5,
+       .items = {
+               { "Front Mic", 0x0 },
+               { "Mic", 0x4 },
+               { "Line", 0x1 },
+               { "CD", 0x2 },
+               { "Mix", 0x3 },
+       },
+};
+
+static struct snd_kcontrol_new ad1884a_base_mixers[] = {
+       HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
+       HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
+       HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
+       HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
+       HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
+       HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
+       HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
+       HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
+       HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
+       HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
+       HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
+       HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Line Boost", 0x15, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
+       {
+               .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 = ad198x_mux_enum_info,
+               .get = ad198x_mux_enum_get,
+               .put = ad198x_mux_enum_put,
+       },
+       /* SPDIF controls */
+       HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
+               /* identical with ad1983 */
+               .info = ad1983_spdif_route_info,
+               .get = ad1983_spdif_route_get,
+               .put = ad1983_spdif_route_put,
+       },
+       { } /* end */
+};
+
+/*
+ * initialization verbs
+ */
+static struct hda_verb ad1884a_init_verbs[] = {
+       /* DACs; unmute as default */
+       {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
+       {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
+       /* Port-A (HP) mixer - route only from analog mixer */
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+       /* Port-A pin */
+       {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       /* Port-D (Line-out) mixer - route only from analog mixer */
+       {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+       /* Port-D pin */
+       {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       /* Mono-out mixer - route only from analog mixer */
+       {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+       /* Mono-out pin */
+       {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       /* Port-B (front mic) pin */
+       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       /* Port-C (rear line-in) pin */
+       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       /* Port-E (rear mic) pin */
+       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* no boost */
+       /* Port-F (CD) pin */
+       {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       /* Analog mixer; mute as default */
+       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* aux */
+       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
+       /* Analog Mix output amp */
+       {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       /* capture sources */
+       {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
+       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
+       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       /* SPDIF output amp */
+       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
+       { } /* end */
+};
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static struct hda_amp_list ad1884a_loopbacks[] = {
+       { 0x20, HDA_INPUT, 0 }, /* Front Mic */
+       { 0x20, HDA_INPUT, 1 }, /* Mic */
+       { 0x20, HDA_INPUT, 2 }, /* CD */
+       { 0x20, HDA_INPUT, 4 }, /* Docking */
+       { } /* end */
+};
+#endif
+
+/*
+ * Laptop model
+ *
+ * Port A: Headphone jack
+ * Port B: MIC jack
+ * Port C: Internal MIC
+ * Port D: Dock Line Out (if enabled)
+ * Port E: Dock Line In (if enabled)
+ * Port F: Internal speakers
+ */
+
+static struct hda_input_mux ad1884a_laptop_capture_source = {
+       .num_items = 4,
+       .items = {
+               { "Mic", 0x0 },         /* port-B */
+               { "Internal Mic", 0x1 }, /* port-C */
+               { "Dock Mic", 0x4 },    /* port-E */
+               { "Mix", 0x3 },
+       },
+};
+
+static struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
+       HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
+       HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
+       HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
+       HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
+       HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
+       HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
+       HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
+       HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
+       HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Dock Mic Boost", 0x25, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
+       {
+               .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 = ad198x_mux_enum_info,
+               .get = ad198x_mux_enum_get,
+               .put = ad198x_mux_enum_put,
+       },
+       { } /* end */
+};
+
+static struct hda_input_mux ad1884a_mobile_capture_source = {
+       .num_items = 2,
+       .items = {
+               { "Mic", 0x1 }, /* port-C */
+               { "Mix", 0x3 },
+       },
+};
+
+static struct snd_kcontrol_new ad1884a_mobile_mixers[] = {
+       HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
+       HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
+       HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
+       HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
+       HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Capture Source",
+               .info = ad198x_mux_enum_info,
+               .get = ad198x_mux_enum_get,
+               .put = ad198x_mux_enum_put,
+       },
+       { } /* end */
+};
+
+/* mute internal speaker if HP is plugged */
+static void ad1884a_hp_automute(struct hda_codec *codec)
+{
+       unsigned int present;
+
+       present = snd_hda_codec_read(codec, 0x11, 0,
+                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
+                                HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+       snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
+                           present ? 0x00 : 0x02);
+}
+
+#define AD1884A_HP_EVENT               0x37
+
+/* unsolicited event for HP jack sensing */
+static void ad1884a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+       if ((res >> 26) != AD1884A_HP_EVENT)
+               return;
+       ad1884a_hp_automute(codec);
+}
+
+/* initialize jack-sensing, too */
+static int ad1884a_hp_init(struct hda_codec *codec)
+{
+       ad198x_init(codec);
+       ad1884a_hp_automute(codec);
+       return 0;
+}
+
+/* additional verbs for laptop model */
+static struct hda_verb ad1884a_laptop_verbs[] = {
+       /* Port-A (HP) pin - always unmuted */
+       {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       /* Port-F (int speaker) mixer - route only from analog mixer */
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+       /* Port-F pin */
+       {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       /* analog mix */
+       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+       /* unsolicited event for pin-sense */
+       {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
+       { } /* end */
+};
+
+/*
+ */
+
+enum {
+       AD1884A_DESKTOP,
+       AD1884A_LAPTOP,
+       AD1884A_MOBILE,
+       AD1884A_MODELS
+};
+
+static const char *ad1884a_models[AD1884A_MODELS] = {
+       [AD1884A_DESKTOP]       = "desktop",
+       [AD1884A_LAPTOP]        = "laptop",
+       [AD1884A_MOBILE]        = "mobile",
+};
+
+static struct snd_pci_quirk ad1884a_cfg_tbl[] = {
+       SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
+       {}
+};
+
+static int patch_ad1884a(struct hda_codec *codec)
+{
+       struct ad198x_spec *spec;
+       int board_config;
+
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (spec == NULL)
+               return -ENOMEM;
+
+       mutex_init(&spec->amp_mutex);
+       codec->spec = spec;
+
+       spec->multiout.max_channels = 2;
+       spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids);
+       spec->multiout.dac_nids = ad1884a_dac_nids;
+       spec->multiout.dig_out_nid = AD1884A_SPDIF_OUT;
+       spec->num_adc_nids = ARRAY_SIZE(ad1884a_adc_nids);
+       spec->adc_nids = ad1884a_adc_nids;
+       spec->capsrc_nids = ad1884a_capsrc_nids;
+       spec->input_mux = &ad1884a_capture_source;
+       spec->num_mixers = 1;
+       spec->mixers[0] = ad1884a_base_mixers;
+       spec->num_init_verbs = 1;
+       spec->init_verbs[0] = ad1884a_init_verbs;
+       spec->spdif_route = 0;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       spec->loopback.amplist = ad1884a_loopbacks;
+#endif
+       codec->patch_ops = ad198x_patch_ops;
+
+       /* override some parameters */
+       board_config = snd_hda_check_board_config(codec, AD1884A_MODELS,
+                                                 ad1884a_models,
+                                                 ad1884a_cfg_tbl);
+       switch (board_config) {
+       case AD1884A_LAPTOP:
+               spec->mixers[0] = ad1884a_laptop_mixers;
+               spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
+               spec->multiout.dig_out_nid = 0;
+               spec->input_mux = &ad1884a_laptop_capture_source;
+               codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
+               codec->patch_ops.init = ad1884a_hp_init;
+               break;
+       case AD1884A_MOBILE:
+               spec->mixers[0] = ad1884a_mobile_mixers;
+               spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
+               spec->multiout.dig_out_nid = 0;
+               spec->input_mux = &ad1884a_mobile_capture_source;
+               codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
+               codec->patch_ops.init = ad1884a_hp_init;
+               break;
+       }
+
+       return 0;
+}
+
+
+/*
+ * AD1882
+ *
+ * port-A - front hp-out
+ * port-B - front mic-in
+ * port-C - rear line-in, shared surr-out (3stack)
+ * port-D - rear line-out
+ * port-E - rear mic-in, shared clfe-out (3stack)
+ * port-F - rear surr-out (6stack)
+ * port-G - rear clfe-out (6stack)
+ */
+
+static hda_nid_t ad1882_dac_nids[3] = {
+       0x04, 0x03, 0x05
+};
+
+static hda_nid_t ad1882_adc_nids[2] = {
+       0x08, 0x09,
+};
+
+static hda_nid_t ad1882_capsrc_nids[2] = {
+       0x0c, 0x0d,
+};
+
+#define AD1882_SPDIF_OUT       0x02
+
+/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */
+static struct hda_input_mux ad1882_capture_source = {
+       .num_items = 5,
+       .items = {
+               { "Front Mic", 0x1 },
+               { "Mic", 0x4 },
+               { "Line", 0x2 },
+               { "CD", 0x3 },
+               { "Mix", 0x7 },
+       },
+};
+
+static struct snd_kcontrol_new ad1882_base_mixers[] = {
+       HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
+       HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
+       HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
+       HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT),
+       HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
+       HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
+       HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
+       HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x07, HDA_INPUT),
+       HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x07, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Line-In Boost", 0x3a, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
+       {
+               .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 = ad198x_mux_enum_info,
+               .get = ad198x_mux_enum_get,
+               .put = ad198x_mux_enum_put,
+       },
+       /* SPDIF controls */
+       HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
+               /* identical with ad1983 */
+               .info = ad1983_spdif_route_info,
+               .get = ad1983_spdif_route_get,
+               .put = ad1983_spdif_route_put,
+       },
+       { } /* end */
+};
+
+static struct snd_kcontrol_new ad1882_3stack_mixers[] = {
+       HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Channel Mode",
+               .info = ad198x_ch_mode_info,
+               .get = ad198x_ch_mode_get,
+               .put = ad198x_ch_mode_put,
+       },
+       { } /* end */
+};
+
+static struct snd_kcontrol_new ad1882_6stack_mixers[] = {
+       HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT),
+       { } /* end */
+};
+
+static struct hda_verb ad1882_ch2_init[] = {
+       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+       {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       { } /* end */
+};
+
+static struct hda_verb ad1882_ch4_init[] = {
+       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+       {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       { } /* end */
+};
+
+static struct hda_verb ad1882_ch6_init[] = {
+       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+       {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+       { } /* end */
+};
+
+static struct hda_channel_mode ad1882_modes[3] = {
+       { 2, ad1882_ch2_init },
+       { 4, ad1882_ch4_init },
+       { 6, ad1882_ch6_init },
+};
+
+/*
+ * initialization verbs
+ */
+static struct hda_verb ad1882_init_verbs[] = {
+       /* DACs; mute as default */
+       {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+       {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+       {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+       /* Port-A (HP) mixer */
+       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+       /* Port-A pin */
+       {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       /* HP selector - select DAC2 */
+       {0x37, AC_VERB_SET_CONNECT_SEL, 0x1},
+       /* Port-D (Line-out) mixer */
+       {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+       /* Port-D pin */
+       {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       /* Mono-out mixer */
+       {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+       /* Mono-out pin */
+       {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       /* Port-B (front mic) pin */
+       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
+       /* Port-C (line-in) pin */
+       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
+       /* Port-C mixer - mute as input */
+       {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       /* Port-E (mic-in) pin */
+       {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
+       /* Port-E mixer - mute as input */
+       {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       /* Port-F (surround) */
+       {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       /* Port-G (CLFE) */
+       {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       /* Analog mixer; mute as default */
+       /* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */
+       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
+       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
+       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
+       /* Analog Mix output amp */
+       {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
+       /* SPDIF output selector */
+       {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
+       {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
+       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
+       { } /* end */
+};
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static struct hda_amp_list ad1882_loopbacks[] = {
+       { 0x20, HDA_INPUT, 0 }, /* Front Mic */
+       { 0x20, HDA_INPUT, 1 }, /* Mic */
+       { 0x20, HDA_INPUT, 4 }, /* Line */
+       { 0x20, HDA_INPUT, 6 }, /* CD */
+       { } /* end */
+};
+#endif
+
+/* models */
+enum {
+       AD1882_3STACK,
+       AD1882_6STACK,
+       AD1882_MODELS
+};
+
+static const char *ad1882_models[AD1986A_MODELS] = {
+       [AD1882_3STACK]         = "3stack",
+       [AD1882_6STACK]         = "6stack",
+};
+
+
+static int patch_ad1882(struct hda_codec *codec)
+{
+       struct ad198x_spec *spec;
+       int board_config;
+
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (spec == NULL)
+               return -ENOMEM;
+
+       mutex_init(&spec->amp_mutex);
+       codec->spec = spec;
+
+       spec->multiout.max_channels = 6;
+       spec->multiout.num_dacs = 3;
+       spec->multiout.dac_nids = ad1882_dac_nids;
+       spec->multiout.dig_out_nid = AD1882_SPDIF_OUT;
+       spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);
+       spec->adc_nids = ad1882_adc_nids;
+       spec->capsrc_nids = ad1882_capsrc_nids;
+       spec->input_mux = &ad1882_capture_source;
+       spec->num_mixers = 1;
+       spec->mixers[0] = ad1882_base_mixers;
+       spec->num_init_verbs = 1;
+       spec->init_verbs[0] = ad1882_init_verbs;
+       spec->spdif_route = 0;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       spec->loopback.amplist = ad1882_loopbacks;
+#endif
+       spec->vmaster_nid = 0x04;
+
+       codec->patch_ops = ad198x_patch_ops;
+
+       /* override some parameters */
+       board_config = snd_hda_check_board_config(codec, AD1882_MODELS,
+                                                 ad1882_models, NULL);
+       switch (board_config) {
+       default:
+       case AD1882_3STACK:
+               spec->num_mixers = 2;
+               spec->mixers[1] = ad1882_3stack_mixers;
+               spec->channel_mode = ad1882_modes;
+               spec->num_channel_mode = ARRAY_SIZE(ad1882_modes);
+               spec->need_dac_fix = 1;
+               spec->multiout.max_channels = 2;
+               spec->multiout.num_dacs = 1;
+               break;
+       case AD1882_6STACK:
+               spec->num_mixers = 2;
+               spec->mixers[1] = ad1882_6stack_mixers;
+               break;
        }
        return 0;
 }
@@ -3113,7 +4080,12 @@ static int patch_ad1984(struct hda_codec *codec)
  * patch entries
  */
 struct hda_codec_preset snd_hda_preset_analog[] = {
+       { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a },
+       { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
+       { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a },
        { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
+       { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884a },
+       { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884a },
        { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
        { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
        { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },