ALSA: hda: Fix max PCM level to 0 dB for AD1981_HP
[safe/jmp/linux-2.6] / sound / pci / hda / patch_analog.c
index d0d66f3..1a36137 100644 (file)
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_local.h"
-#include "hda_patch.h"
+#include "hda_beep.h"
 
 struct ad198x_spec {
        struct snd_kcontrol_new *mixers[5];
        int num_mixers;
-
+       unsigned int beep_amp;  /* beep amp value, set via set_beep_amp() */
        const struct hda_verb *init_verbs[5];   /* initialization verbs
                                                 * don't forget NULL termination!
                                                 */
@@ -72,6 +72,8 @@ struct ad198x_spec {
        hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
 
        unsigned int jack_present :1;
+       unsigned int inv_jack_detect:1; /* inverted jack-detection */
+       unsigned int inv_eapd:1;        /* inverted EAPD implementation */
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        struct hda_loopback_check loopback;
@@ -155,6 +157,20 @@ static const char *ad_slave_sws[] = {
 
 static void ad198x_free_kctls(struct hda_codec *codec);
 
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+/* additional beep mixers; the actual parameters are overwritten at build */
+static struct snd_kcontrol_new ad_beep_mixer[] = {
+       HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_OUTPUT),
+       { } /* end */
+};
+
+#define set_beep_amp(spec, nid, idx, dir) \
+       ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */
+#else
+#define set_beep_amp(spec, nid, idx, dir) /* NOP */
+#endif
+
 static int ad198x_build_controls(struct hda_codec *codec)
 {
        struct ad198x_spec *spec = codec->spec;
@@ -182,6 +198,25 @@ static int ad198x_build_controls(struct hda_codec *codec)
                        return err;
        }
 
+       /* create beep controls if needed */
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+       if (spec->beep_amp) {
+               struct snd_kcontrol_new *knew;
+               for (knew = ad_beep_mixer; knew->name; knew++) {
+                       struct snd_kcontrol *kctl;
+                       kctl = snd_ctl_new1(knew, codec);
+                       if (!kctl)
+                               return -ENOMEM;
+                       kctl->private_value = spec->beep_amp;
+                       err = snd_hda_ctl_add(codec,
+                                               get_amp_nid_(spec->beep_amp),
+                                               kctl);
+                       if (err < 0)
+                               return err;
+               }
+       }
+#endif
+
        /* 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];
@@ -276,6 +311,14 @@ static int ad198x_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
                                             format, substream);
 }
 
+static int ad198x_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                                          struct hda_codec *codec,
+                                          struct snd_pcm_substream *substream)
+{
+       struct ad198x_spec *spec = codec->spec;
+       return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
+}
+
 /*
  * Analog capture
  */
@@ -334,7 +377,8 @@ static struct hda_pcm_stream ad198x_pcm_digital_playback = {
        .ops = {
                .open = ad198x_dig_playback_pcm_open,
                .close = ad198x_dig_playback_pcm_close,
-               .prepare = ad198x_dig_playback_pcm_prepare
+               .prepare = ad198x_dig_playback_pcm_prepare,
+               .cleanup = ad198x_dig_playback_pcm_cleanup
        },
 };
 
@@ -398,7 +442,8 @@ static void ad198x_free(struct hda_codec *codec)
                return;
 
        ad198x_free_kctls(codec);
-       kfree(codec->spec);
+       kfree(spec);
+       snd_hda_detach_beep_device(codec);
 }
 
 static struct hda_codec_ops ad198x_patch_ops = {
@@ -414,7 +459,7 @@ static struct hda_codec_ops ad198x_patch_ops = {
 
 /*
  * EAPD control
- * the private value = nid | (invert << 8)
+ * the private value = nid
  */
 #define ad198x_eapd_info       snd_ctl_boolean_mono_info
 
@@ -423,8 +468,7 @@ static int ad198x_eapd_get(struct snd_kcontrol *kcontrol,
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct ad198x_spec *spec = codec->spec;
-       int invert = (kcontrol->private_value >> 8) & 1;
-       if (invert)
+       if (spec->inv_eapd)
                ucontrol->value.integer.value[0] = ! spec->cur_eapd;
        else
                ucontrol->value.integer.value[0] = spec->cur_eapd;
@@ -436,11 +480,10 @@ static int ad198x_eapd_put(struct snd_kcontrol *kcontrol,
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct ad198x_spec *spec = codec->spec;
-       int invert = (kcontrol->private_value >> 8) & 1;
        hda_nid_t nid = kcontrol->private_value & 0xff;
        unsigned int eapd;
        eapd = !!ucontrol->value.integer.value[0];
-       if (invert)
+       if (spec->inv_eapd)
                eapd = !eapd;
        if (eapd == spec->cur_eapd)
                return 0;
@@ -537,8 +580,6 @@ static struct snd_kcontrol_new ad1986a_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("PC Speaker Playback Volume", 0x18, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x18, 0x0, HDA_OUTPUT),
        HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
        HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),
        HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
@@ -602,8 +643,7 @@ static struct snd_kcontrol_new ad1986a_laptop_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("PC Speaker Playback Volume", 0x18, 0x0, HDA_OUTPUT),
-          HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x18, 0x0, HDA_OUTPUT),
+       /* 
           HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
           HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */
        HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
@@ -637,9 +677,13 @@ static struct hda_input_mux ad1986a_automic_capture_source = {
        },
 };
 
-static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
+static struct snd_kcontrol_new ad1986a_laptop_master_mixers[] = {
        HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
        HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
+       { } /* end */
+};
+
+static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
        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),
@@ -660,19 +704,25 @@ static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
                .info = ad198x_eapd_info,
                .get = ad198x_eapd_get,
                .put = ad198x_eapd_put,
-               .private_value = 0x1b | (1 << 8), /* port-D, inversed */
+               .private_value = 0x1b, /* port-D */
        },
        { } /* end */
 };
 
+static struct snd_kcontrol_new ad1986a_laptop_intmic_mixers[] = {
+       HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0, HDA_OUTPUT),
+       { } /* end */
+};
+
 /* re-connect the mic boost input according to the jack sensing */
 static void ad1986a_automic(struct hda_codec *codec)
 {
        unsigned int present;
-       present = snd_hda_codec_read(codec, 0x1f, 0, AC_VERB_GET_PIN_SENSE, 0);
+       present = snd_hda_jack_detect(codec, 0x1f);
        /* 0 = 0x1f, 2 = 0x1d, 4 = mixed */
        snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL,
-                           (present & AC_PINSENSE_PRESENCE) ? 0 : 2);
+                           present ? 0 : 2);
 }
 
 #define AD1986A_MIC_EVENT              0x36
@@ -711,11 +761,10 @@ static void ad1986a_update_hp(struct hda_codec *codec)
 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);
+       spec->jack_present = snd_hda_jack_detect(codec, 0x1a);
+       if (spec->inv_jack_detect)
+               spec->jack_present = !spec->jack_present;
        ad1986a_update_hp(codec);
 }
 
@@ -754,7 +803,7 @@ static int ad1986a_hp_master_sw_put(struct snd_kcontrol *kcontrol,
        return change;
 }
 
-static struct snd_kcontrol_new ad1986a_laptop_automute_mixers[] = {
+static struct snd_kcontrol_new ad1986a_automute_master_mixers[] = {
        HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -764,35 +813,10 @@ static struct snd_kcontrol_new ad1986a_laptop_automute_mixers[] = {
                .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),
-       HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 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("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),
-       {
-               .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 */
 };
 
+
 /*
  * initialization verbs
  */
@@ -921,6 +945,27 @@ static struct hda_verb ad1986a_hp_init_verbs[] = {
        {}
 };
 
+static void ad1986a_samsung_p50_unsol_event(struct hda_codec *codec,
+                                           unsigned int res)
+{
+       switch (res >> 26) {
+       case AD1986A_HP_EVENT:
+               ad1986a_hp_automute(codec);
+               break;
+       case AD1986A_MIC_EVENT:
+               ad1986a_automic(codec);
+               break;
+       }
+}
+
+static int ad1986a_samsung_p50_init(struct hda_codec *codec)
+{
+       ad198x_init(codec);
+       ad1986a_hp_automute(codec);
+       ad1986a_automic(codec);
+       return 0;
+}
+
 
 /* models */
 enum {
@@ -930,6 +975,8 @@ enum {
        AD1986A_LAPTOP_EAPD,
        AD1986A_LAPTOP_AUTOMUTE,
        AD1986A_ULTRA,
+       AD1986A_SAMSUNG,
+       AD1986A_SAMSUNG_P50,
        AD1986A_MODELS
 };
 
@@ -940,6 +987,8 @@ static const char *ad1986a_models[AD1986A_MODELS] = {
        [AD1986A_LAPTOP_EAPD]   = "laptop-eapd",
        [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute",
        [AD1986A_ULTRA]         = "ultra",
+       [AD1986A_SAMSUNG]       = "samsung",
+       [AD1986A_SAMSUNG_P50]   = "samsung-p50",
 };
 
 static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
@@ -962,10 +1011,9 @@ static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
        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, 0xc024, "Samsung P50", AD1986A_SAMSUNG_P50),
        SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
+       SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_SAMSUNG),
        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),
@@ -987,15 +1035,14 @@ static struct hda_amp_list ad1986a_loopbacks[] = {
 
 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);
+       unsigned int conf = snd_hda_codec_get_pincfg(codec, nid);
        return get_defcfg_connect(conf) != AC_JACK_PORT_NONE;
 }
 
 static int patch_ad1986a(struct hda_codec *codec)
 {
        struct ad198x_spec *spec;
-       int board_config;
+       int err, board_config;
 
        spec = kzalloc(sizeof(*spec), GFP_KERNEL);
        if (spec == NULL)
@@ -1003,6 +1050,13 @@ static int patch_ad1986a(struct hda_codec *codec)
 
        codec->spec = spec;
 
+       err = snd_hda_attach_beep_device(codec, 0x19);
+       if (err < 0) {
+               ad198x_free(codec);
+               return err;
+       }
+       set_beep_amp(spec, 0x18, 0, HDA_OUTPUT);
+
        spec->multiout.max_channels = 6;
        spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
        spec->multiout.dac_nids = ad1986a_dac_nids;
@@ -1019,6 +1073,7 @@ static int patch_ad1986a(struct hda_codec *codec)
        spec->loopback.amplist = ad1986a_loopbacks;
 #endif
        spec->vmaster_nid = 0x1b;
+       spec->inv_eapd = 1; /* AD1986A has the inverted EAPD implementation */
 
        codec->patch_ops = ad198x_patch_ops;
 
@@ -1045,7 +1100,23 @@ static int patch_ad1986a(struct hda_codec *codec)
                spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
                break;
        case AD1986A_LAPTOP_EAPD:
-               spec->mixers[0] = ad1986a_laptop_eapd_mixers;
+               spec->num_mixers = 3;
+               spec->mixers[0] = ad1986a_laptop_master_mixers;
+               spec->mixers[1] = ad1986a_laptop_eapd_mixers;
+               spec->mixers[2] = ad1986a_laptop_intmic_mixers;
+               spec->num_init_verbs = 2;
+               spec->init_verbs[1] = ad1986a_eapd_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;
+               break;
+       case AD1986A_SAMSUNG:
+               spec->num_mixers = 2;
+               spec->mixers[0] = ad1986a_laptop_master_mixers;
+               spec->mixers[1] = ad1986a_laptop_eapd_mixers;
                spec->num_init_verbs = 3;
                spec->init_verbs[1] = ad1986a_eapd_init_verbs;
                spec->init_verbs[2] = ad1986a_automic_verbs;
@@ -1058,8 +1129,28 @@ static int patch_ad1986a(struct hda_codec *codec)
                codec->patch_ops.unsol_event = ad1986a_automic_unsol_event;
                codec->patch_ops.init = ad1986a_automic_init;
                break;
+       case AD1986A_SAMSUNG_P50:
+               spec->num_mixers = 2;
+               spec->mixers[0] = ad1986a_automute_master_mixers;
+               spec->mixers[1] = ad1986a_laptop_eapd_mixers;
+               spec->num_init_verbs = 4;
+               spec->init_verbs[1] = ad1986a_eapd_init_verbs;
+               spec->init_verbs[2] = ad1986a_automic_verbs;
+               spec->init_verbs[3] = 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_automic_capture_source;
+               codec->patch_ops.unsol_event = ad1986a_samsung_p50_unsol_event;
+               codec->patch_ops.init = ad1986a_samsung_p50_init;
+               break;
        case AD1986A_LAPTOP_AUTOMUTE:
-               spec->mixers[0] = ad1986a_laptop_automute_mixers;
+               spec->num_mixers = 3;
+               spec->mixers[0] = ad1986a_automute_master_mixers;
+               spec->mixers[1] = ad1986a_laptop_eapd_mixers;
+               spec->mixers[2] = ad1986a_laptop_intmic_mixers;
                spec->num_init_verbs = 3;
                spec->init_verbs[1] = ad1986a_eapd_init_verbs;
                spec->init_verbs[2] = ad1986a_hp_init_verbs;
@@ -1071,6 +1162,10 @@ static int patch_ad1986a(struct hda_codec *codec)
                spec->input_mux = &ad1986a_laptop_eapd_capture_source;
                codec->patch_ops.unsol_event = ad1986a_hp_unsol_event;
                codec->patch_ops.init = ad1986a_hp_init;
+               /* Lenovo N100 seems to report the reversed bit
+                * for HP jack-sensing
+                */
+               spec->inv_jack_detect = 1;
                break;
        case AD1986A_ULTRA:
                spec->mixers[0] = ad1986a_laptop_eapd_mixers;
@@ -1171,8 +1266,6 @@ static struct snd_kcontrol_new ad1983_mixers[] = {
        HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
        HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
        HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME_MONO("PC Speaker Playback Volume", 0x10, 1, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE_MONO("PC Speaker Playback Switch", 0x10, 1, 0x0, HDA_OUTPUT),
        HDA_CODEC_VOLUME("Mic Boost", 0x0c, 0x0, HDA_OUTPUT),
        HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
        HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
@@ -1243,6 +1336,7 @@ static struct hda_amp_list ad1983_loopbacks[] = {
 static int patch_ad1983(struct hda_codec *codec)
 {
        struct ad198x_spec *spec;
+       int err;
 
        spec = kzalloc(sizeof(*spec), GFP_KERNEL);
        if (spec == NULL)
@@ -1250,6 +1344,13 @@ static int patch_ad1983(struct hda_codec *codec)
 
        codec->spec = spec;
 
+       err = snd_hda_attach_beep_device(codec, 0x10);
+       if (err < 0) {
+               ad198x_free(codec);
+               return err;
+       }
+       set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
+
        spec->multiout.max_channels = 2;
        spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids);
        spec->multiout.dac_nids = ad1983_dac_nids;
@@ -1319,8 +1420,6 @@ static struct snd_kcontrol_new ad1981_mixers[] = {
        HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
        HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
        HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME_MONO("PC Speaker Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE_MONO("PC Speaker Playback Switch", 0x0d, 1, 0x0, HDA_OUTPUT),
        HDA_CODEC_VOLUME("Front Mic Boost", 0x08, 0x0, HDA_INPUT),
        HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x0, HDA_INPUT),
        HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
@@ -1365,8 +1464,8 @@ static struct hda_verb ad1981_init_verbs[] = {
        {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
        {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
        /* Mic boost: 0dB */
-       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
-       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
        /* Record selector: Front mic */
        {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
        {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
@@ -1454,8 +1553,7 @@ static void ad1981_hp_automute(struct hda_codec *codec)
 {
        unsigned int present;
 
-       present = snd_hda_codec_read(codec, 0x06, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       present = snd_hda_jack_detect(codec, 0x06);
        snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0,
                                 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
 }
@@ -1475,8 +1573,7 @@ static void ad1981_hp_automic(struct hda_codec *codec)
        };
        unsigned int present;
 
-       present = snd_hda_codec_read(codec, 0x08, 0,
-                                AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       present = snd_hda_jack_detect(codec, 0x08);
        if (present)
                snd_hda_sequence_write(codec, mic_jack_on);
        else
@@ -1631,10 +1728,10 @@ static struct snd_pci_quirk ad1981_cfg_tbl[] = {
        SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
        SND_PCI_QUIRK(0x1014, 0x05b7, "Lenovo Z60m", AD1981_THINKPAD),
        /* All HP models */
-       SND_PCI_QUIRK(0x103c, 0, "HP nx", AD1981_HP),
+       SND_PCI_QUIRK_VENDOR(0x103c, "HP nx", 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_VENDOR(0x17aa, "Lenovo Thinkpad", AD1981_THINKPAD),
        /* HP nx6320 (reversed SSID, H/W bug) */
        SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
        {}
@@ -1643,7 +1740,7 @@ static struct snd_pci_quirk ad1981_cfg_tbl[] = {
 static int patch_ad1981(struct hda_codec *codec)
 {
        struct ad198x_spec *spec;
-       int board_config;
+       int err, board_config;
 
        spec = kzalloc(sizeof(*spec), GFP_KERNEL);
        if (spec == NULL)
@@ -1651,6 +1748,13 @@ static int patch_ad1981(struct hda_codec *codec)
 
        codec->spec = spec;
 
+       err = snd_hda_attach_beep_device(codec, 0x10);
+       if (err < 0) {
+               ad198x_free(codec);
+               return err;
+       }
+       set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
+
        spec->multiout.max_channels = 2;
        spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids);
        spec->multiout.dac_nids = ad1981_dac_nids;
@@ -1685,6 +1789,14 @@ static int patch_ad1981(struct hda_codec *codec)
 
                codec->patch_ops.init = ad1981_hp_init;
                codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
+               /* set the upper-limit for mixer amp to 0dB for avoiding the
+                * possible damage by overloading
+                */
+               snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
+                                         (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
+                                         (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+                                         (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+                                         (1 << AC_AMPCAP_MUTE_SHIFT));
                break;
        case AD1981_THINKPAD:
                spec->mixers[0] = ad1981_thinkpad_mixers;
@@ -1843,8 +1955,8 @@ static hda_nid_t ad1988_capsrc_nids[3] = {
 #define AD1988_SPDIF_OUT_HDMI  0x0b
 #define AD1988_SPDIF_IN                0x07
 
-static hda_nid_t ad1989b_slave_dig_outs[2] = {
-       AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI
+static hda_nid_t ad1989b_slave_dig_outs[] = {
+       AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI, 0
 };
 
 static struct hda_input_mux ad1988_6stack_capture_source = {
@@ -1937,9 +2049,6 @@ static struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
        HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
        HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
 
-       HDA_CODEC_VOLUME("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
-
        HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
        HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
 
@@ -1983,9 +2092,6 @@ static struct snd_kcontrol_new ad1988_3stack_mixers2[] = {
        HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
        HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
 
-       HDA_CODEC_VOLUME("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
-
        HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
        HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
 
@@ -2015,9 +2121,6 @@ static struct snd_kcontrol_new ad1988_laptop_mixers[] = {
        HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
        HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
 
-       HDA_CODEC_VOLUME("Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
-
        HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
        HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
 
@@ -2029,7 +2132,7 @@ static struct snd_kcontrol_new ad1988_laptop_mixers[] = {
                .info = ad198x_eapd_info,
                .get = ad198x_eapd_get,
                .put = ad198x_eapd_put,
-               .private_value = 0x12 | (1 << 8), /* port-D, inversed */
+               .private_value = 0x12, /* port-D */
        },
 
        { } /* end */
@@ -2246,10 +2349,6 @@ static struct hda_verb ad1988_capture_init_verbs[] = {
        {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
        {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
        {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
-       /* ADCs; muted */
-       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
 
        { }
 };
@@ -2357,10 +2456,6 @@ static struct hda_verb ad1988_3stack_init_verbs[] = {
        {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
        {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
        {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
-       /* ADCs; muted */
-       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
        /* Analog Mix output amp */
        {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
        { }
@@ -2432,10 +2527,6 @@ static struct hda_verb ad1988_laptop_init_verbs[] = {
        {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
        {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
        {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
-       /* ADCs; muted */
-       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
        /* Analog Mix output amp */
        {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
        { }
@@ -2445,7 +2536,7 @@ static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
 {
        if ((res >> 26) != AD1988_HP_EVENT)
                return;
-       if (snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0) & (1 << 31))
+       if (snd_hda_jack_detect(codec, 0x11))
                snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
        else
                snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
@@ -2490,6 +2581,8 @@ static int add_control(struct ad198x_spec *spec, int type, const char *name,
        knew->name = kstrdup(name, GFP_KERNEL);
        if (! knew->name)
                return -ENOMEM;
+       if (get_amp_nid_(val))
+               knew->subdevice = HDA_SUBDEV_NID_FLAG | get_amp_nid_(val);
        knew->private_value = val;
        return 0;
 }
@@ -2839,7 +2932,7 @@ static int ad1988_parse_auto_config(struct hda_codec *codec)
 
        spec->multiout.max_channels = spec->multiout.num_dacs * 2;
 
-       if (spec->autocfg.dig_out_pin)
+       if (spec->autocfg.dig_outs)
                spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
        if (spec->autocfg.dig_in_pin)
                spec->dig_in_nid = AD1988_SPDIF_IN;
@@ -2889,7 +2982,7 @@ static struct snd_pci_quirk ad1988_cfg_tbl[] = {
 static int patch_ad1988(struct hda_codec *codec)
 {
        struct ad198x_spec *spec;
-       int board_config;
+       int err, board_config;
 
        spec = kzalloc(sizeof(*spec), GFP_KERNEL);
        if (spec == NULL)
@@ -2903,13 +2996,14 @@ static int patch_ad1988(struct hda_codec *codec)
        board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
                                                  ad1988_models, ad1988_cfg_tbl);
        if (board_config < 0) {
-               printk(KERN_INFO "hda_codec: Unknown model for AD1988, trying auto-probe from BIOS...\n");
+               printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+                      codec->chip_name);
                board_config = AD1988_AUTO;
        }
 
        if (board_config == AD1988_AUTO) {
                /* automatic parse from the BIOS config */
-               int err = ad1988_parse_auto_config(codec);
+               err = ad1988_parse_auto_config(codec);
                if (err < 0) {
                        ad198x_free(codec);
                        return err;
@@ -2919,6 +3013,13 @@ static int patch_ad1988(struct hda_codec *codec)
                }
        }
 
+       err = snd_hda_attach_beep_device(codec, 0x10);
+       if (err < 0) {
+               ad198x_free(codec);
+               return err;
+       }
+       set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
+
        switch (board_config) {
        case AD1988_6STACK:
        case AD1988_6STACK_DIG:
@@ -2972,6 +3073,7 @@ static int patch_ad1988(struct hda_codec *codec)
                spec->input_mux = &ad1988_laptop_capture_source;
                spec->num_mixers = 1;
                spec->mixers[0] = ad1988_laptop_mixers;
+               spec->inv_eapd = 1; /* inverted EAPD */
                spec->num_init_verbs = 1;
                spec->init_verbs[0] = ad1988_laptop_init_verbs;
                if (board_config == AD1988_LAPTOP_DIG)
@@ -3075,12 +3177,6 @@ static struct snd_kcontrol_new ad1884_base_mixers[] = {
        HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, 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("PC Speaker Playback Volume", 0x20, 0x03, HDA_INPUT),
-       HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x20, 0x03, HDA_INPUT),
-       HDA_CODEC_VOLUME("Digital Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Digital Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
-       */
        HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT),
        HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
        HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
@@ -3153,10 +3249,10 @@ static struct hda_verb ad1884_init_verbs[] = {
        {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
        /* Port-B (front mic) pin */
        {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
        /* Port-C (rear mic) pin */
        {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
        /* 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)},
@@ -3188,8 +3284,8 @@ static const char *ad1884_slave_vols[] = {
        "Mic Playback Volume",
        "CD Playback Volume",
        "Internal Mic Playback Volume",
-       "Docking Mic Playback Volume"
-       "Beep Playback Volume",
+       "Docking Mic Playback Volume",
+       /* "Beep Playback Volume", */
        "IEC958 Playback Volume",
        NULL
 };
@@ -3197,6 +3293,7 @@ static const char *ad1884_slave_vols[] = {
 static int patch_ad1884(struct hda_codec *codec)
 {
        struct ad198x_spec *spec;
+       int err;
 
        spec = kzalloc(sizeof(*spec), GFP_KERNEL);
        if (spec == NULL)
@@ -3204,6 +3301,13 @@ static int patch_ad1884(struct hda_codec *codec)
 
        codec->spec = spec;
 
+       err = snd_hda_attach_beep_device(codec, 0x10);
+       if (err < 0) {
+               ad198x_free(codec);
+               return err;
+       }
+       set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
+
        spec->multiout.max_channels = 2;
        spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
        spec->multiout.dac_nids = ad1884_dac_nids;
@@ -3270,8 +3374,6 @@ 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),
@@ -3307,7 +3409,7 @@ static struct hda_verb ad1984_thinkpad_init_verbs[] = {
        {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
        {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
        /* docking mic boost */
-       {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
        /* Analog mixer - docking mic; mute as default */
        {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
        /* enable EAPD bit */
@@ -3328,10 +3430,6 @@ static struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = {
        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),
@@ -3417,7 +3515,7 @@ static const char *ad1984_models[AD1984_MODELS] = {
 
 static struct snd_pci_quirk ad1984_cfg_tbl[] = {
        /* Lenovo Thinkpad T61/X61 */
-       SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1984_THINKPAD),
+       SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1984_THINKPAD),
        SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
        {}
 };
@@ -3510,8 +3608,6 @@ static struct snd_kcontrol_new ad1884a_base_mixers[] = {
        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),
@@ -3571,10 +3667,10 @@ static struct hda_verb ad1884a_init_verbs[] = {
        {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},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
        /* 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},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
        /* Port-E (rear mic) pin */
        {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
        {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
@@ -3622,19 +3718,29 @@ static struct hda_amp_list ad1884a_loopbacks[] = {
  * 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 int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol,
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+       int mute = (!ucontrol->value.integer.value[0] &&
+                   !ucontrol->value.integer.value[1]);
+       /* toggle GPIO1 according to the mute state */
+       snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
+                           mute ? 0x02 : 0x0);
+       return ret;
+}
 
 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),
+       {
+               .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 = ad1884a_mobile_master_sw_put,
+               .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, 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),
@@ -3644,37 +3750,27 @@ static struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
        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 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_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
+       {
+               .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 = ad1884a_mobile_master_sw_put,
+               .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, 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("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
-       HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
        HDA_CODEC_VOLUME("Mic Capture Volume", 0x14, 0x0, HDA_INPUT),
        HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x15, 0x0, HDA_INPUT),
        HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
@@ -3687,8 +3783,7 @@ 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;
+       present = snd_hda_jack_detect(codec, 0x11);
        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,
@@ -3700,8 +3795,7 @@ static void ad1884a_hp_automic(struct hda_codec *codec)
 {
        unsigned int present;
 
-       present = snd_hda_codec_read(codec, 0x14, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       present = snd_hda_jack_detect(codec, 0x14);
        snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL,
                            present ? 0 : 1);
 }
@@ -3731,6 +3825,57 @@ static int ad1884a_hp_init(struct hda_codec *codec)
        return 0;
 }
 
+/* mute internal speaker if HP or docking HP is plugged */
+static void ad1884a_laptop_automute(struct hda_codec *codec)
+{
+       unsigned int present;
+
+       present = snd_hda_jack_detect(codec, 0x11);
+       if (!present)
+               present = snd_hda_jack_detect(codec, 0x12);
+       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);
+}
+
+/* switch to external mic if plugged */
+static void ad1884a_laptop_automic(struct hda_codec *codec)
+{
+       unsigned int idx;
+
+       if (snd_hda_jack_detect(codec, 0x14))
+               idx = 0;
+       else if (snd_hda_jack_detect(codec, 0x1c))
+               idx = 4;
+       else
+               idx = 1;
+       snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, idx);
+}
+
+/* unsolicited event for HP jack sensing */
+static void ad1884a_laptop_unsol_event(struct hda_codec *codec,
+                                      unsigned int res)
+{
+       switch (res >> 26) {
+       case AD1884A_HP_EVENT:
+               ad1884a_laptop_automute(codec);
+               break;
+       case AD1884A_MIC_EVENT:
+               ad1884a_laptop_automic(codec);
+               break;
+       }
+}
+
+/* initialize jack-sensing, too */
+static int ad1884a_laptop_init(struct hda_codec *codec)
+{
+       ad198x_init(codec);
+       ad1884a_laptop_automute(codec);
+       ad1884a_laptop_automic(codec);
+       return 0;
+}
+
 /* additional verbs for laptop model */
 static struct hda_verb ad1884a_laptop_verbs[] = {
        /* Port-A (HP) pin - always unmuted */
@@ -3738,18 +3883,75 @@ static struct hda_verb ad1884a_laptop_verbs[] = {
        /* 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},
+       /* Port-F (int speaker) pin */
+       {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
        {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       /* required for compaq 6530s/6531s speaker output */
+       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
        /* Port-C pin - internal mic-in */
        {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
        {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
        {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
+       /* Port-D (docking line-out) pin - default unmuted */
+       {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
        /* 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},
+       {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
+       {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
+       {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
+       /* allow to touch GPIO1 (for mute control) */
+       {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
+       {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
+       {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
+       { } /* end */
+};
+
+static struct hda_verb ad1884a_mobile_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},
+       /* Port-A (HP) pin - always unmuted */
+       {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       /* Port-B (mic jack) pin */
+       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
+       /* Port-C (int mic) pin */
+       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
+       /* 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 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_MUTE(4)},
+       {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}, */ /* set via unsol */
+       {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},
+       /* unsolicited event for pin-sense */
+       {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
        {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
+       /* allow to touch GPIO1 (for mute control) */
+       {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
+       {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
+       {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
        { } /* end */
 };
 
@@ -3785,8 +3987,6 @@ static struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = {
        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("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", 0x17, 0x0, HDA_INPUT),
        HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
@@ -3815,8 +4015,7 @@ static void ad1984a_thinkpad_automute(struct hda_codec *codec)
 {
        unsigned int present;
 
-       present = snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0)
-               & AC_PINSENSE_PRESENCE;
+       present = snd_hda_jack_detect(codec, 0x11);
        snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0,
                                 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
 }
@@ -3839,6 +4038,125 @@ static int ad1984a_thinkpad_init(struct hda_codec *codec)
 }
 
 /*
+ * HP Touchsmart
+ * port-A (0x11)      - front hp-out
+ * port-B (0x14)      - unused
+ * port-C (0x15)      - unused
+ * port-D (0x12)      - rear line out
+ * port-E (0x1c)      - front mic-in
+ * port-F (0x16)      - Internal speakers
+ * digital-mic (0x17) - Internal mic
+ */
+
+static struct hda_verb ad1984a_touchsmart_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},
+       /* Port-A (HP) pin - always unmuted */
+       {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       /* Port-E (int speaker) mixer - route only from analog mixer */
+       {0x25, AC_VERB_SET_AMP_GAIN_MUTE, 0x03},
+       /* Port-E pin */
+       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+       {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       /* 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 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_MUTE(4)},
+       {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}, */ /* set via unsol */
+       {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},
+       /* unsolicited event for pin-sense */
+       {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
+       {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
+       /* allow to touch GPIO1 (for mute control) */
+       {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
+       {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
+       {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
+       /* internal mic - dmic */
+       {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+       /* set magic COEFs for dmic */
+       {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
+       {0x01, AC_VERB_SET_PROC_COEF, 0x08},
+       { } /* end */
+};
+
+static struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = {
+       HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
+/*     HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
+       {
+               .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 = ad1884a_mobile_master_sw_put,
+               .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, 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("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT),
+       { } /* end */
+};
+
+/* switch to external mic if plugged */
+static void ad1984a_touchsmart_automic(struct hda_codec *codec)
+{
+       if (snd_hda_jack_detect(codec, 0x1c))
+               snd_hda_codec_write(codec, 0x0c, 0,
+                                    AC_VERB_SET_CONNECT_SEL, 0x4);
+       else
+               snd_hda_codec_write(codec, 0x0c, 0,
+                                    AC_VERB_SET_CONNECT_SEL, 0x5);
+}
+
+
+/* unsolicited event for HP jack sensing */
+static void ad1984a_touchsmart_unsol_event(struct hda_codec *codec,
+       unsigned int res)
+{
+       switch (res >> 26) {
+       case AD1884A_HP_EVENT:
+               ad1884a_hp_automute(codec);
+               break;
+       case AD1884A_MIC_EVENT:
+               ad1984a_touchsmart_automic(codec);
+               break;
+       }
+}
+
+/* initialize jack-sensing, too */
+static int ad1984a_touchsmart_init(struct hda_codec *codec)
+{
+       ad198x_init(codec);
+       ad1884a_hp_automute(codec);
+       ad1984a_touchsmart_automic(codec);
+       return 0;
+}
+
+
+/*
  */
 
 enum {
@@ -3846,6 +4164,7 @@ enum {
        AD1884A_LAPTOP,
        AD1884A_MOBILE,
        AD1884A_THINKPAD,
+       AD1984A_TOUCHSMART,
        AD1884A_MODELS
 };
 
@@ -3854,19 +4173,27 @@ static const char *ad1884a_models[AD1884A_MODELS] = {
        [AD1884A_LAPTOP]        = "laptop",
        [AD1884A_MOBILE]        = "mobile",
        [AD1884A_THINKPAD]      = "thinkpad",
+       [AD1984A_TOUCHSMART]    = "touchsmart",
 };
 
 static struct snd_pci_quirk ad1884a_cfg_tbl[] = {
        SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
+       SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP),
        SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE),
+       SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x3070, "HP", AD1884A_MOBILE),
+       SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30d0, "HP laptop", AD1884A_LAPTOP),
+       SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP),
+       SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP),
+       SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x7010, "HP laptop", AD1884A_MOBILE),
        SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
+       SND_PCI_QUIRK(0x103c, 0x2a82, "Touchsmart", AD1984A_TOUCHSMART),
        {}
 };
 
 static int patch_ad1884a(struct hda_codec *codec)
 {
        struct ad198x_spec *spec;
-       int board_config;
+       int err, board_config;
 
        spec = kzalloc(sizeof(*spec), GFP_KERNEL);
        if (spec == NULL)
@@ -3874,6 +4201,13 @@ static int patch_ad1884a(struct hda_codec *codec)
 
        codec->spec = spec;
 
+       err = snd_hda_attach_beep_device(codec, 0x10);
+       if (err < 0) {
+               ad198x_free(codec);
+               return err;
+       }
+       set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
+
        spec->multiout.max_channels = 2;
        spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids);
        spec->multiout.dac_nids = ad1884a_dac_nids;
@@ -3901,16 +4235,31 @@ static int patch_ad1884a(struct hda_codec *codec)
                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;
+               codec->patch_ops.unsol_event = ad1884a_laptop_unsol_event;
+               codec->patch_ops.init = ad1884a_laptop_init;
+               /* set the upper-limit for mixer amp to 0dB for avoiding the
+                * possible damage by overloading
+                */
+               snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
+                                         (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
+                                         (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+                                         (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+                                         (1 << AC_AMPCAP_MUTE_SHIFT));
                break;
        case AD1884A_MOBILE:
                spec->mixers[0] = ad1884a_mobile_mixers;
-               spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
+               spec->init_verbs[0] = ad1884a_mobile_verbs;
                spec->multiout.dig_out_nid = 0;
                codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
                codec->patch_ops.init = ad1884a_hp_init;
+               /* set the upper-limit for mixer amp to 0dB for avoiding the
+                * possible damage by overloading
+                */
+               snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
+                                         (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
+                                         (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+                                         (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+                                         (1 << AC_AMPCAP_MUTE_SHIFT));
                break;
        case AD1884A_THINKPAD:
                spec->mixers[0] = ad1984a_thinkpad_mixers;
@@ -3921,6 +4270,21 @@ static int patch_ad1884a(struct hda_codec *codec)
                codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event;
                codec->patch_ops.init = ad1984a_thinkpad_init;
                break;
+       case AD1984A_TOUCHSMART:
+               spec->mixers[0] = ad1984a_touchsmart_mixers;
+               spec->init_verbs[0] = ad1984a_touchsmart_verbs;
+               spec->multiout.dig_out_nid = 0;
+               codec->patch_ops.unsol_event = ad1984a_touchsmart_unsol_event;
+               codec->patch_ops.init = ad1984a_touchsmart_init;
+               /* set the upper-limit for mixer amp to 0dB for avoiding the
+                * possible damage by overloading
+                */
+               snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
+                                         (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
+                                         (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+                                         (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+                                         (1 << AC_AMPCAP_MUTE_SHIFT));
+               break;
        }
 
        return 0;
@@ -4028,8 +4392,6 @@ static struct snd_kcontrol_new ad1882_loopback_mixers[] = {
        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),
        { } /* end */
 };
 
@@ -4042,8 +4404,6 @@ static struct snd_kcontrol_new ad1882a_loopback_mixers[] = {
        HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, 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("Digital Mic Boost", 0x1f, 0x0, HDA_INPUT),
        { } /* end */
 };
@@ -4202,7 +4562,7 @@ static const char *ad1882_models[AD1986A_MODELS] = {
 static int patch_ad1882(struct hda_codec *codec)
 {
        struct ad198x_spec *spec;
-       int board_config;
+       int err, board_config;
 
        spec = kzalloc(sizeof(*spec), GFP_KERNEL);
        if (spec == NULL)
@@ -4210,6 +4570,13 @@ static int patch_ad1882(struct hda_codec *codec)
 
        codec->spec = spec;
 
+       err = snd_hda_attach_beep_device(codec, 0x10);
+       if (err < 0) {
+               ad198x_free(codec);
+               return err;
+       }
+       set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
+
        spec->multiout.max_channels = 6;
        spec->multiout.num_dacs = 3;
        spec->multiout.dac_nids = ad1882_dac_nids;
@@ -4217,13 +4584,13 @@ static int patch_ad1882(struct hda_codec *codec)
        spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);
        spec->adc_nids = ad1882_adc_nids;
        spec->capsrc_nids = ad1882_capsrc_nids;
-       if (codec->vendor_id == 0x11d1882)
+       if (codec->vendor_id == 0x11d41882)
                spec->input_mux = &ad1882_capture_source;
        else
                spec->input_mux = &ad1882a_capture_source;
        spec->num_mixers = 2;
        spec->mixers[0] = ad1882_base_mixers;
-       if (codec->vendor_id == 0x11d1882)
+       if (codec->vendor_id == 0x11d41882)
                spec->mixers[1] = ad1882_loopback_mixers;
        else
                spec->mixers[1] = ad1882a_loopback_mixers;
@@ -4263,7 +4630,7 @@ static int patch_ad1882(struct hda_codec *codec)
 /*
  * patch entries
  */
-struct hda_codec_preset snd_hda_preset_analog[] = {
+static 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 },
@@ -4281,3 +4648,26 @@ struct hda_codec_preset snd_hda_preset_analog[] = {
        { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
        {} /* terminator */
 };
+
+MODULE_ALIAS("snd-hda-codec-id:11d4*");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Analog Devices HD-audio codec");
+
+static struct hda_codec_preset_list analog_list = {
+       .preset = snd_hda_preset_analog,
+       .owner = THIS_MODULE,
+};
+
+static int __init patch_analog_init(void)
+{
+       return snd_hda_add_codec_preset(&analog_list);
+}
+
+static void __exit patch_analog_exit(void)
+{
+       snd_hda_delete_codec_preset(&analog_list);
+}
+
+module_init(patch_analog_init)
+module_exit(patch_analog_exit)