]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - sound/pci/hda/patch_sigmatel.c
Merge branch 'fix/hda' into topic/hda
[linux-2.6.git] / sound / pci / hda / patch_sigmatel.c
index f205570def1c3a194682cb9b7f1344b0fec95615..71c3ccfcde167338047c049b857024bac1dc1a59 100644 (file)
 #include <linux/pci.h>
 #include <sound/core.h>
 #include <sound/asoundef.h>
+#include <sound/jack.h>
 #include "hda_codec.h"
 #include "hda_local.h"
-#include "hda_patch.h"
 #include "hda_beep.h"
 
-#define NUM_CONTROL_ALLOC      32
-
-#define STAC_VREF_EVENT                0x00
-#define STAC_INSERT_EVENT      0x10
-#define STAC_PWR_EVENT         0x20
-#define STAC_HP_EVENT          0x30
+enum {
+       STAC_VREF_EVENT = 1,
+       STAC_INSERT_EVENT,
+       STAC_PWR_EVENT,
+       STAC_HP_EVENT,
+};
 
 enum {
        STAC_REF,
@@ -69,8 +69,11 @@ enum {
 };
 
 enum {
+       STAC_92HD73XX_NO_JD, /* no jack-detection */
        STAC_92HD73XX_REF,
-       STAC_DELL_M6,
+       STAC_DELL_M6_AMIC,
+       STAC_DELL_M6_DMIC,
+       STAC_DELL_M6_BOTH,
        STAC_DELL_EQ,
        STAC_92HD73XX_MODELS
 };
@@ -84,6 +87,7 @@ enum {
        STAC_92HD71BXX_REF,
        STAC_DELL_M4_1,
        STAC_DELL_M4_2,
+       STAC_DELL_M4_3,
        STAC_HP_M4,
        STAC_92HD71BXX_MODELS
 };
@@ -124,6 +128,7 @@ enum {
 };
 
 enum {
+       STAC_D965_REF_NO_JD, /* no jack-detection */
        STAC_D965_REF,
        STAC_D965_3ST,
        STAC_D965_5ST,
@@ -132,11 +137,25 @@ enum {
        STAC_927X_MODELS
 };
 
+struct sigmatel_event {
+       hda_nid_t nid;
+       unsigned char type;
+       unsigned char tag;
+       int data;
+};
+
+struct sigmatel_jack {
+       hda_nid_t nid;
+       int type;
+       struct snd_jack *jack;
+};
+
 struct sigmatel_spec {
        struct snd_kcontrol_new *mixers[4];
        unsigned int num_mixers;
 
        int board_config;
+       unsigned int eapd_switch: 1;
        unsigned int surr_switch: 1;
        unsigned int line_switch: 1;
        unsigned int mic_switch: 1;
@@ -164,6 +183,12 @@ struct sigmatel_spec {
        hda_nid_t *pwr_nids;
        hda_nid_t *dac_list;
 
+       /* jack detection */
+       struct snd_array jacks;
+
+       /* events */
+       struct snd_array events;
+
        /* playback */
        struct hda_input_mux *mono_mux;
        struct hda_input_mux *amp_mux;
@@ -193,7 +218,6 @@ struct sigmatel_spec {
        hda_nid_t *pin_nids;
        unsigned int num_pins;
        unsigned int *pin_configs;
-       unsigned int *bios_pin_configs;
 
        /* codec specific stuff */
        struct hda_verb *init;
@@ -221,8 +245,7 @@ struct sigmatel_spec {
 
        /* dynamic controls and input_mux */
        struct auto_pin_cfg autocfg;
-       unsigned int num_kctl_alloc, num_kctl_used;
-       struct snd_kcontrol_new *kctl_alloc;
+       struct snd_array kctls;
        struct hda_input_mux private_dimux;
        struct hda_input_mux private_imux;
        struct hda_input_mux private_smux;
@@ -568,12 +591,12 @@ static int stac92xx_smux_enum_put(struct snd_kcontrol *kcontrol,
                else
                        nid = codec->slave_dig_outs[smux_idx - 1];
                if (spec->cur_smux[smux_idx] == smux->num_items - 1)
-                       val = AMP_OUT_MUTE;
+                       val = HDA_AMP_MUTE;
                else
-                       val = AMP_OUT_UNMUTE;
+                       val = 0;
                /* un/mute SPDIF out */
-               snd_hda_codec_write_cache(codec, nid, 0,
-                       AC_VERB_SET_AMP_GAIN_MUTE, val);
+               snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
+                                        HDA_AMP_MUTE, val);
        }
        return 0;
 }
@@ -1232,9 +1255,14 @@ static const char *slave_sws[] = {
        NULL
 };
 
+static void stac92xx_free_kctls(struct hda_codec *codec);
+static int stac92xx_add_jack(struct hda_codec *codec, hda_nid_t nid, int type);
+
 static int stac92xx_build_controls(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       hda_nid_t nid;
        int err;
        int i;
 
@@ -1249,7 +1277,7 @@ static int stac92xx_build_controls(struct hda_codec *codec)
        }
        if (spec->num_dmuxes > 0) {
                stac_dmux_mixer.count = spec->num_dmuxes;
-               err = snd_ctl_add(codec->bus->card,
+               err = snd_hda_ctl_add(codec,
                                  snd_ctl_new1(&stac_dmux_mixer, codec));
                if (err < 0)
                        return err;
@@ -1304,6 +1332,37 @@ static int stac92xx_build_controls(struct hda_codec *codec)
                        return err;
        }
 
+       stac92xx_free_kctls(codec); /* no longer needed */
+
+       /* create jack input elements */
+       if (spec->hp_detect) {
+               for (i = 0; i < cfg->hp_outs; i++) {
+                       int type = SND_JACK_HEADPHONE;
+                       nid = cfg->hp_pins[i];
+                       /* jack detection */
+                       if (cfg->hp_outs == i)
+                               type |= SND_JACK_LINEOUT;
+                       err = stac92xx_add_jack(codec, nid, type);
+                       if (err < 0)
+                               return err;
+               }
+       }
+       for (i = 0; i < cfg->line_outs; i++) {
+               err = stac92xx_add_jack(codec, cfg->line_out_pins[i],
+                                       SND_JACK_LINEOUT);
+               if (err < 0)
+                       return err;
+       }
+       for (i = 0; i < AUTO_PIN_LAST; i++) {
+               nid = cfg->input_pins[i];
+               if (nid) {
+                       err = stac92xx_add_jack(codec, nid,
+                                               SND_JACK_MICROPHONE);
+                       if (err < 0)
+                               return err;
+               }
+       }
+
        return 0;       
 }
 
@@ -1600,13 +1659,18 @@ static unsigned int dell_m6_pin_configs[13] = {
 
 static unsigned int *stac92hd73xx_brd_tbl[STAC_92HD73XX_MODELS] = {
        [STAC_92HD73XX_REF]     = ref92hd73xx_pin_configs,
-       [STAC_DELL_M6]  = dell_m6_pin_configs,
+       [STAC_DELL_M6_AMIC]     = dell_m6_pin_configs,
+       [STAC_DELL_M6_DMIC]     = dell_m6_pin_configs,
+       [STAC_DELL_M6_BOTH]     = dell_m6_pin_configs,
        [STAC_DELL_EQ]  = dell_m6_pin_configs,
 };
 
 static const char *stac92hd73xx_models[STAC_92HD73XX_MODELS] = {
+       [STAC_92HD73XX_NO_JD] = "no-jd",
        [STAC_92HD73XX_REF] = "ref",
-       [STAC_DELL_M6] = "dell-m6",
+       [STAC_DELL_M6_AMIC] = "dell-m6-amic",
+       [STAC_DELL_M6_DMIC] = "dell-m6-dmic",
+       [STAC_DELL_M6_BOTH] = "dell-m6",
        [STAC_DELL_EQ] = "dell-eq",
 };
 
@@ -1615,19 +1679,25 @@ static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = {
        SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
                                "DFI LanParty", STAC_92HD73XX_REF),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0254,
-                               "unknown Dell", STAC_DELL_M6),
+                               "Dell Studio 1535", STAC_DELL_M6_DMIC),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0255,
-                               "unknown Dell", STAC_DELL_M6),
+                               "unknown Dell", STAC_DELL_M6_DMIC),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0256,
-                               "unknown Dell", STAC_DELL_M6),
+                               "unknown Dell", STAC_DELL_M6_BOTH),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0257,
-                               "unknown Dell", STAC_DELL_M6),
+                               "unknown Dell", STAC_DELL_M6_BOTH),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x025e,
-                               "unknown Dell", STAC_DELL_M6),
+                               "unknown Dell", STAC_DELL_M6_AMIC),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x025f,
-                               "unknown Dell", STAC_DELL_M6),
+                               "unknown Dell", STAC_DELL_M6_AMIC),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0271,
-                               "unknown Dell", STAC_DELL_M6),
+                               "unknown Dell", STAC_DELL_M6_DMIC),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0272,
+                               "unknown Dell", STAC_DELL_M6_DMIC),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x029f,
+                               "Dell Studio 1537", STAC_DELL_M6_DMIC),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02a0,
+                               "Dell Studio 17", STAC_DELL_M6_DMIC),
        {} /* terminator */
 };
 
@@ -1670,10 +1740,17 @@ static unsigned int dell_m4_2_pin_configs[11] = {
        0x40f000f0, 0x044413b0, 0x044413b0,
 };
 
+static unsigned int dell_m4_3_pin_configs[11] = {
+       0x0421101f, 0x04a11221, 0x90a70330, 0x90170110,
+       0x40f000f0, 0x40f000f0, 0x40f000f0, 0x90a000f0,
+       0x40f000f0, 0x044413b0, 0x044413b0,
+};
+
 static unsigned int *stac92hd71bxx_brd_tbl[STAC_92HD71BXX_MODELS] = {
        [STAC_92HD71BXX_REF] = ref92hd71bxx_pin_configs,
        [STAC_DELL_M4_1]        = dell_m4_1_pin_configs,
        [STAC_DELL_M4_2]        = dell_m4_2_pin_configs,
+       [STAC_DELL_M4_3]        = dell_m4_3_pin_configs,
        [STAC_HP_M4]            = NULL,
 };
 
@@ -1681,6 +1758,7 @@ static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = {
        [STAC_92HD71BXX_REF] = "ref",
        [STAC_DELL_M4_1] = "dell-m4-1",
        [STAC_DELL_M4_2] = "dell-m4-2",
+       [STAC_DELL_M4_3] = "dell-m4-3",
        [STAC_HP_M4] = "hp-m4",
 };
 
@@ -1688,6 +1766,10 @@ static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
        /* SigmaTel reference board */
        SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
                      "DFI LanParty", STAC_92HD71BXX_REF),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30f2,
+                     "HP dv5", STAC_HP_M4),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30f4,
+                     "HP dv7", STAC_HP_M4),
        SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361a,
                                "unknown HP", STAC_HP_M4),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233,
@@ -1712,6 +1794,8 @@ static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
                                "unknown Dell", STAC_DELL_M4_2),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0264,
                                "unknown Dell", STAC_DELL_M4_2),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02aa,
+                               "unknown Dell", STAC_DELL_M4_3),
        {} /* terminator */
 };
 
@@ -2001,6 +2085,7 @@ static unsigned int dell_3st_pin_configs[14] = {
 };
 
 static unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = {
+       [STAC_D965_REF_NO_JD] = ref927x_pin_configs,
        [STAC_D965_REF]  = ref927x_pin_configs,
        [STAC_D965_3ST]  = d965_3st_pin_configs,
        [STAC_D965_5ST]  = d965_5st_pin_configs,
@@ -2009,6 +2094,7 @@ static unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = {
 };
 
 static const char *stac927x_models[STAC_927X_MODELS] = {
+       [STAC_D965_REF_NO_JD]   = "ref-no-jd",
        [STAC_D965_REF]         = "ref",
        [STAC_D965_3ST]         = "3stack",
        [STAC_D965_5ST]         = "5stack",
@@ -2167,12 +2253,11 @@ static int stac92xx_save_bios_config_regs(struct hda_codec *codec)
        int i;
        struct sigmatel_spec *spec = codec->spec;
        
-       if (! spec->bios_pin_configs) {
-               spec->bios_pin_configs = kcalloc(spec->num_pins,
-                                                sizeof(*spec->bios_pin_configs), GFP_KERNEL);
-               if (! spec->bios_pin_configs)
-                       return -ENOMEM;
-       }
+       kfree(spec->pin_configs);
+       spec->pin_configs = kcalloc(spec->num_pins, sizeof(*spec->pin_configs),
+                                   GFP_KERNEL);
+       if (!spec->pin_configs)
+               return -ENOMEM;
        
        for (i = 0; i < spec->num_pins; i++) {
                hda_nid_t nid = spec->pin_nids[i];
@@ -2182,7 +2267,7 @@ static int stac92xx_save_bios_config_regs(struct hda_codec *codec)
                        AC_VERB_GET_CONFIG_DEFAULT, 0x00);      
                snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x bios pin config %8.8x\n",
                                        nid, pin_cfg);
-               spec->bios_pin_configs[i] = pin_cfg;
+               spec->pin_configs[i] = pin_cfg;
        }
        
        return 0;
@@ -2224,6 +2309,39 @@ static void stac92xx_set_config_regs(struct hda_codec *codec)
                                        spec->pin_configs[i]);
 }
 
+static int stac_save_pin_cfgs(struct hda_codec *codec, unsigned int *pins)
+{
+       struct sigmatel_spec *spec = codec->spec;
+
+       if (!pins)
+               return stac92xx_save_bios_config_regs(codec);
+
+       kfree(spec->pin_configs);
+       spec->pin_configs = kmemdup(pins,
+                                   spec->num_pins * sizeof(*pins),
+                                   GFP_KERNEL);
+       if (!spec->pin_configs)
+               return -ENOMEM;
+
+       stac92xx_set_config_regs(codec);
+       return 0;
+}
+
+static void stac_change_pin_config(struct hda_codec *codec, hda_nid_t nid,
+                                  unsigned int cfg)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       int i;
+
+       for (i = 0; i < spec->num_pins; i++) {
+               if (spec->pin_nids[i] == nid) {
+                       spec->pin_configs[i] = cfg;
+                       stac92xx_set_config_reg(codec, nid, cfg);
+                       break;
+               }
+       }
+}
+
 /*
  * Analog playback callbacks
  */
@@ -2301,7 +2419,7 @@ static int stac92xx_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
 
        if (spec->powerdown_adcs) {
                msleep(40);
-               snd_hda_codec_write_cache(codec, nid, 0,
+               snd_hda_codec_write(codec, nid, 0,
                        AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
        }
        snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
@@ -2317,7 +2435,7 @@ static int stac92xx_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
 
        snd_hda_codec_cleanup_stream(codec, nid);
        if (spec->powerdown_adcs)
-               snd_hda_codec_write_cache(codec, nid, 0,
+               snd_hda_codec_write(codec, nid, 0,
                        AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
        return 0;
 }
@@ -2449,6 +2567,9 @@ static int stac92xx_hp_switch_get(struct snd_kcontrol *kcontrol,
        return 0;
 }
 
+static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid,
+                                  unsigned char type);
+
 static int stac92xx_hp_switch_put(struct snd_kcontrol *kcontrol,
                        struct snd_ctl_elem_value *ucontrol)
 {
@@ -2461,7 +2582,7 @@ static int stac92xx_hp_switch_put(struct snd_kcontrol *kcontrol,
        /* check to be sure that the ports are upto date with
         * switch changes
         */
-       codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
+       stac_issue_unsol_event(codec, nid, STAC_HP_EVENT);
 
        return 1;
 }
@@ -2501,7 +2622,7 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
         * appropriately according to the pin direction
         */
        if (spec->hp_detect)
-               codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
+               stac_issue_unsol_event(codec, nid, STAC_HP_EVENT);
 
         return 1;
 }
@@ -2596,28 +2717,16 @@ static int stac92xx_add_control_temp(struct sigmatel_spec *spec,
 {
        struct snd_kcontrol_new *knew;
 
-       if (spec->num_kctl_used >= spec->num_kctl_alloc) {
-               int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
-
-               knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); /* array + terminator */
-               if (! knew)
-                       return -ENOMEM;
-               if (spec->kctl_alloc) {
-                       memcpy(knew, spec->kctl_alloc, sizeof(*knew) * spec->num_kctl_alloc);
-                       kfree(spec->kctl_alloc);
-               }
-               spec->kctl_alloc = knew;
-               spec->num_kctl_alloc = num;
-       }
-
-       knew = &spec->kctl_alloc[spec->num_kctl_used];
+       snd_array_init(&spec->kctls, sizeof(*knew), 32);
+       knew = snd_array_new(&spec->kctls);
+       if (!knew)
+               return -ENOMEM;
        *knew = *ktemp;
        knew->index = idx;
        knew->name = kstrdup(name, GFP_KERNEL);
        if (!knew->name)
                return -ENOMEM;
        knew->private_value = val;
-       spec->num_kctl_used++;
        return 0;
 }
 
@@ -2873,7 +2982,7 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
                        cfg->hp_outs && !spec->multiout.hp_nid)
                spec->multiout.hp_nid = nid;
 
-       if (cfg->hp_outs > 1) {
+       if (cfg->hp_outs > 1 && cfg->line_out_type == AUTO_PIN_LINE_OUT) {
                err = stac92xx_add_control(spec,
                        STAC_CTL_WIDGET_HP_SWITCH,
                        "Headphone as Line Out Switch",
@@ -3494,8 +3603,8 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
        if (dig_in && spec->autocfg.dig_in_pin)
                spec->dig_in_nid = dig_in;
 
-       if (spec->kctl_alloc)
-               spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+       if (spec->kctls.list)
+               spec->mixers[spec->num_mixers++] = spec->kctls.list;
 
        spec->input_mux = &spec->private_imux;
        spec->dinput_mux = &spec->private_dimux;
@@ -3602,8 +3711,8 @@ static int stac9200_parse_auto_config(struct hda_codec *codec)
        if (spec->autocfg.dig_in_pin)
                spec->dig_in_nid = 0x04;
 
-       if (spec->kctl_alloc)
-               spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+       if (spec->kctls.list)
+               spec->mixers[spec->num_mixers++] = spec->kctls.list;
 
        spec->input_mux = &spec->private_imux;
        spec->dinput_mux = &spec->private_dimux;
@@ -3647,13 +3756,101 @@ static void stac_gpio_set(struct hda_codec *codec, unsigned int mask,
                           AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */
 }
 
+static int stac92xx_add_jack(struct hda_codec *codec,
+               hda_nid_t nid, int type)
+{
+#ifdef CONFIG_SND_JACK
+       struct sigmatel_spec *spec = codec->spec;
+       struct sigmatel_jack *jack;
+       int def_conf = snd_hda_codec_read(codec, nid,
+                       0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+       int connectivity = get_defcfg_connect(def_conf);
+       char name[32];
+
+       if (connectivity && connectivity != AC_JACK_PORT_FIXED)
+               return 0;
+
+       snd_array_init(&spec->jacks, sizeof(*jack), 32);
+       jack = snd_array_new(&spec->jacks);
+       if (!jack)
+               return -ENOMEM;
+       jack->nid = nid;
+       jack->type = type;
+
+       sprintf(name, "%s at %s %s Jack",
+               snd_hda_get_jack_type(def_conf),
+               snd_hda_get_jack_connectivity(def_conf),
+               snd_hda_get_jack_location(def_conf));
+
+       return snd_jack_new(codec->bus->card, name, type, &jack->jack);
+#else
+       return 0;
+#endif
+}
+
+static int stac_add_event(struct sigmatel_spec *spec, hda_nid_t nid,
+                         unsigned char type, int data)
+{
+       struct sigmatel_event *event;
+
+       snd_array_init(&spec->events, sizeof(*event), 32);
+       event = snd_array_new(&spec->events);
+       if (!event)
+               return -ENOMEM;
+       event->nid = nid;
+       event->type = type;
+       event->tag = spec->events.used;
+       event->data = data;
+
+       return event->tag;
+}
+
+static struct sigmatel_event *stac_get_event(struct hda_codec *codec,
+                                            hda_nid_t nid, unsigned char type)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       struct sigmatel_event *event = spec->events.list;
+       int i;
+
+       for (i = 0; i < spec->events.used; i++, event++) {
+               if (event->nid == nid && event->type == type)
+                       return event;
+       }
+       return NULL;
+}
+
+static struct sigmatel_event *stac_get_event_from_tag(struct hda_codec *codec,
+                                                     unsigned char tag)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       struct sigmatel_event *event = spec->events.list;
+       int i;
+
+       for (i = 0; i < spec->events.used; i++, event++) {
+               if (event->tag == tag)
+                       return event;
+       }
+       return NULL;
+}
+
 static void enable_pin_detect(struct hda_codec *codec, hda_nid_t nid,
-                             unsigned int event)
+                             unsigned int type)
 {
-       if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP)
-               snd_hda_codec_write_cache(codec, nid, 0,
-                                         AC_VERB_SET_UNSOLICITED_ENABLE,
-                                         (AC_USRSP_EN | event));
+       struct sigmatel_event *event;
+       int tag;
+
+       if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP))
+               return;
+       event = stac_get_event(codec, nid, type);
+       if (event)
+               tag = event->tag;
+       else
+               tag = stac_add_event(codec->spec, nid, type, 0);
+       if (tag < 0)
+               return;
+       snd_hda_codec_write_cache(codec, nid, 0,
+                                 AC_VERB_SET_UNSOLICITED_ENABLE,
+                                 AC_USRSP_EN | tag);
 }
 
 static int is_nid_hp_pin(struct auto_pin_cfg *cfg, hda_nid_t nid)
@@ -3675,14 +3872,18 @@ static void stac92xx_power_down(struct hda_codec *codec)
        for (dac = spec->dac_list; *dac; dac++)
                if (!is_in_dac_nids(spec, *dac) &&
                        spec->multiout.hp_nid != *dac)
-                       snd_hda_codec_write_cache(codec, *dac, 0,
+                       snd_hda_codec_write(codec, *dac, 0,
                                        AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
 }
 
+static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid,
+                                 int enable);
+
 static int stac92xx_init(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec = codec->spec;
        struct auto_pin_cfg *cfg = &spec->autocfg;
+       unsigned int gpio;
        int i;
 
        snd_hda_sequence_write(codec, spec->init);
@@ -3690,100 +3891,151 @@ static int stac92xx_init(struct hda_codec *codec)
        /* power down adcs initially */
        if (spec->powerdown_adcs)
                for (i = 0; i < spec->num_adcs; i++)
-                       snd_hda_codec_write_cache(codec,
+                       snd_hda_codec_write(codec,
                                spec->adc_nids[i], 0,
                                AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+
+       /* set up GPIO */
+       gpio = spec->gpio_data;
+       /* turn on EAPD statically when spec->eapd_switch isn't set.
+        * otherwise, unsol event will turn it on/off dynamically
+        */
+       if (!spec->eapd_switch)
+               gpio |= spec->eapd_mask;
+       stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, gpio);
+
        /* set up pins */
        if (spec->hp_detect) {
                /* Enable unsolicited responses on the HP widget */
-               for (i = 0; i < cfg->hp_outs; i++)
-                       enable_pin_detect(codec, cfg->hp_pins[i],
-                                         STAC_HP_EVENT);
+               for (i = 0; i < cfg->hp_outs; i++) {
+                       hda_nid_t nid = cfg->hp_pins[i];
+                       enable_pin_detect(codec, nid, STAC_HP_EVENT);
+               }
                /* force to enable the first line-out; the others are set up
                 * in unsol_event
                 */
                stac92xx_auto_set_pinctl(codec, spec->autocfg.line_out_pins[0],
-                                        AC_PINCTL_OUT_EN);
-               stac92xx_auto_init_hp_out(codec);
+                               AC_PINCTL_OUT_EN);
                /* fake event to set up pins */
-               codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
+               stac_issue_unsol_event(codec, spec->autocfg.hp_pins[0],
+                                      STAC_HP_EVENT);
        } else {
                stac92xx_auto_init_multi_out(codec);
                stac92xx_auto_init_hp_out(codec);
+               for (i = 0; i < cfg->hp_outs; i++)
+                       stac_toggle_power_map(codec, cfg->hp_pins[i], 1);
        }
        for (i = 0; i < AUTO_PIN_LAST; i++) {
                hda_nid_t nid = cfg->input_pins[i];
                if (nid) {
-                       unsigned int pinctl;
+                       unsigned int pinctl, conf;
                        if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC) {
                                /* for mic pins, force to initialize */
                                pinctl = stac92xx_get_vref(codec, nid);
+                               pinctl |= AC_PINCTL_IN_EN;
+                               stac92xx_auto_set_pinctl(codec, nid, pinctl);
                        } else {
                                pinctl = snd_hda_codec_read(codec, nid, 0,
                                        AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
                                /* if PINCTL already set then skip */
-                               if (pinctl & AC_PINCTL_IN_EN)
-                                       continue;
+                               if (!(pinctl & AC_PINCTL_IN_EN)) {
+                                       pinctl |= AC_PINCTL_IN_EN;
+                                       stac92xx_auto_set_pinctl(codec, nid,
+                                                                pinctl);
+                               }
+                       }
+                       conf = snd_hda_codec_read(codec, nid, 0,
+                                             AC_VERB_GET_CONFIG_DEFAULT, 0);
+                       if (get_defcfg_connect(conf) != AC_JACK_PORT_FIXED) {
+                               enable_pin_detect(codec, nid,
+                                                 STAC_INSERT_EVENT);
+                               stac_issue_unsol_event(codec, nid,
+                                                      STAC_INSERT_EVENT);
                        }
-                       pinctl |= AC_PINCTL_IN_EN;
-                       stac92xx_auto_set_pinctl(codec, nid, pinctl);
                }
        }
        for (i = 0; i < spec->num_dmics; i++)
                stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i],
                                        AC_PINCTL_IN_EN);
+       if (cfg->dig_out_pin)
+               stac92xx_auto_set_pinctl(codec, cfg->dig_out_pin,
+                                        AC_PINCTL_OUT_EN);
+       if (cfg->dig_in_pin)
+               stac92xx_auto_set_pinctl(codec, cfg->dig_in_pin,
+                                        AC_PINCTL_IN_EN);
        for (i = 0; i < spec->num_pwrs; i++)  {
-               int event = is_nid_hp_pin(cfg, spec->pwr_nids[i])
-                                       ? STAC_HP_EVENT : STAC_PWR_EVENT;
-               int pinctl = snd_hda_codec_read(codec, spec->pwr_nids[i],
-                                       0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
-               int def_conf = snd_hda_codec_read(codec, spec->pwr_nids[i],
-                                       0, AC_VERB_GET_CONFIG_DEFAULT, 0);
-               def_conf = get_defcfg_connect(def_conf);
+               hda_nid_t nid = spec->pwr_nids[i];
+               int pinctl, def_conf;
+
+               if (is_nid_hp_pin(cfg, nid) && spec->hp_detect)
+                       continue; /* already has an unsol event */
+
+               pinctl = snd_hda_codec_read(codec, nid, 0,
+                                           AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
                /* outputs are only ports capable of power management
                 * any attempts on powering down a input port cause the
                 * referenced VREF to act quirky.
                 */
                if (pinctl & AC_PINCTL_IN_EN)
                        continue;
+               def_conf = snd_hda_codec_read(codec, nid, 0,
+                                             AC_VERB_GET_CONFIG_DEFAULT, 0);
+               def_conf = get_defcfg_connect(def_conf);
                /* skip any ports that don't have jacks since presence
                 * detection is useless */
-               if (def_conf && def_conf != AC_JACK_PORT_FIXED)
+               if (def_conf != AC_JACK_PORT_COMPLEX) {
+                       if (def_conf != AC_JACK_PORT_NONE)
+                               stac_toggle_power_map(codec, nid, 1);
                        continue;
-               enable_pin_detect(codec, spec->pwr_nids[i], event | i);
-               codec->patch_ops.unsol_event(codec, (event | i) << 26);
+               }
+               if (!stac_get_event(codec, nid, STAC_INSERT_EVENT)) {
+                       enable_pin_detect(codec, nid, STAC_PWR_EVENT);
+                       stac_issue_unsol_event(codec, nid, STAC_PWR_EVENT);
+               }
        }
        if (spec->dac_list)
                stac92xx_power_down(codec);
-       if (cfg->dig_out_pin)
-               stac92xx_auto_set_pinctl(codec, cfg->dig_out_pin,
-                                        AC_PINCTL_OUT_EN);
-       if (cfg->dig_in_pin)
-               stac92xx_auto_set_pinctl(codec, cfg->dig_in_pin,
-                                        AC_PINCTL_IN_EN);
+       return 0;
+}
+
+static void stac92xx_free_jacks(struct hda_codec *codec)
+{
+#ifdef CONFIG_SND_JACK
+       /* free jack instances manually when clearing/reconfiguring */
+       struct sigmatel_spec *spec = codec->spec;
+       if (!codec->bus->shutdown && spec->jacks.list) {
+               struct sigmatel_jack *jacks = spec->jacks.list;
+               int i;
+               for (i = 0; i < spec->jacks.used; i++)
+                       snd_device_free(codec->bus->card, &jacks[i].jack);
+       }
+       snd_array_free(&spec->jacks);
+#endif
+}
 
-       stac_gpio_set(codec, spec->gpio_mask,
-                                       spec->gpio_dir, spec->gpio_data);
+static void stac92xx_free_kctls(struct hda_codec *codec)
+{
+       struct sigmatel_spec *spec = codec->spec;
 
-       return 0;
+       if (spec->kctls.list) {
+               struct snd_kcontrol_new *kctl = spec->kctls.list;
+               int i;
+               for (i = 0; i < spec->kctls.used; i++)
+                       kfree(kctl[i].name);
+       }
+       snd_array_free(&spec->kctls);
 }
 
 static void stac92xx_free(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec = codec->spec;
-       int i;
 
        if (! spec)
                return;
 
-       if (spec->kctl_alloc) {
-               for (i = 0; i < spec->num_kctl_used; i++)
-                       kfree(spec->kctl_alloc[i].name);
-               kfree(spec->kctl_alloc);
-       }
-
-       if (spec->bios_pin_configs)
-               kfree(spec->bios_pin_configs);
+       kfree(spec->pin_configs);
+       stac92xx_free_jacks(codec);
+       snd_array_free(&spec->events);
 
        kfree(spec);
        snd_hda_detach_beep_device(codec);
@@ -3830,20 +4082,13 @@ static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid,
                        pin_ctl & ~flag);
 }
 
-static int get_hp_pin_presence(struct hda_codec *codec, hda_nid_t nid)
+static int get_pin_presence(struct hda_codec *codec, hda_nid_t nid)
 {
        if (!nid)
                return 0;
        if (snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0x00)
-           & (1 << 31)) {
-               unsigned int pinctl;
-               pinctl = snd_hda_codec_read(codec, nid, 0,
-                                           AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
-               if (pinctl & AC_PINCTL_IN_EN)
-                       return 0; /* mic- or line-input */
-               else
-                       return 1; /* HP-output */
-       }
+           & (1 << 31))
+               return 1;
        return 0;
 }
 
@@ -3867,7 +4112,7 @@ static int no_hp_sensing(struct sigmatel_spec *spec, int i)
        return 0;
 }
 
-static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res)
+static void stac92xx_hp_detect(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec = codec->spec;
        struct auto_pin_cfg *cfg = &spec->autocfg;
@@ -3883,7 +4128,14 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res)
                        break;
                if (no_hp_sensing(spec, i))
                        continue;
-               presence = get_hp_pin_presence(codec, cfg->hp_pins[i]);
+               presence = get_pin_presence(codec, cfg->hp_pins[i]);
+               if (presence) {
+                       unsigned int pinctl;
+                       pinctl = snd_hda_codec_read(codec, cfg->hp_pins[i], 0,
+                                           AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+                       if (pinctl & AC_PINCTL_IN_EN)
+                               presence = 0; /* mic- or line-input */
+               }
        }
 
        if (presence) {
@@ -3897,7 +4149,7 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res)
                for (i = 0; i < cfg->speaker_outs; i++)
                        stac92xx_reset_pinctl(codec, cfg->speaker_pins[i],
                                                AC_PINCTL_OUT_EN);
-               if (spec->eapd_mask)
+               if (spec->eapd_mask && spec->eapd_switch)
                        stac_gpio_set(codec, spec->gpio_mask,
                                spec->gpio_dir, spec->gpio_data &
                                ~spec->eapd_mask);
@@ -3912,7 +4164,7 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res)
                for (i = 0; i < cfg->speaker_outs; i++)
                        stac92xx_set_pinctl(codec, cfg->speaker_pins[i],
                                                AC_PINCTL_OUT_EN);
-               if (spec->eapd_mask)
+               if (spec->eapd_mask && spec->eapd_switch)
                        stac_gpio_set(codec, spec->gpio_mask,
                                spec->gpio_dir, spec->gpio_data |
                                spec->eapd_mask);
@@ -3929,14 +4181,18 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res)
        }
 } 
 
-static void stac92xx_pin_sense(struct hda_codec *codec, int idx)
+static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid,
+                                 int enable)
 {
        struct sigmatel_spec *spec = codec->spec;
-       hda_nid_t nid = spec->pwr_nids[idx];
-       int presence, val;
-       val = snd_hda_codec_read(codec, codec->afg, 0, 0x0fec, 0x0)
-                                                       & 0x000000ff;
-       presence = get_hp_pin_presence(codec, nid);
+       unsigned int idx, val;
+
+       for (idx = 0; idx < spec->num_pwrs; idx++) {
+               if (spec->pwr_nids[idx] == nid)
+                       break;
+       }
+       if (idx >= spec->num_pwrs)
+               return;
 
        /* several codecs have two power down bits */
        if (spec->pwr_mapping)
@@ -3944,56 +4200,157 @@ static void stac92xx_pin_sense(struct hda_codec *codec, int idx)
        else
                idx = 1 << idx;
 
-       if (presence)
+       val = snd_hda_codec_read(codec, codec->afg, 0, 0x0fec, 0x0) & 0xff;
+       if (enable)
                val &= ~idx;
        else
                val |= idx;
 
        /* power down unused output ports */
        snd_hda_codec_write(codec, codec->afg, 0, 0x7ec, val);
-};
+}
+
+static void stac92xx_pin_sense(struct hda_codec *codec, hda_nid_t nid)
+{
+       stac_toggle_power_map(codec, nid, get_pin_presence(codec, nid));
+}
+
+static void stac92xx_report_jack(struct hda_codec *codec, hda_nid_t nid)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       struct sigmatel_jack *jacks = spec->jacks.list;
+
+       if (jacks) {
+               int i;
+               for (i = 0; i < spec->jacks.used; i++) {
+                       if (jacks->nid == nid) {
+                               unsigned int pin_ctl =
+                                       snd_hda_codec_read(codec, nid,
+                                       0, AC_VERB_GET_PIN_WIDGET_CONTROL,
+                                        0x00);
+                               int type = jacks->type;
+                               if (type == (SND_JACK_LINEOUT
+                                               | SND_JACK_HEADPHONE))
+                                       type = (pin_ctl & AC_PINCTL_HP_EN)
+                                       ? SND_JACK_HEADPHONE : SND_JACK_LINEOUT;
+                               snd_jack_report(jacks->jack,
+                                       get_pin_presence(codec, nid)
+                                       ? type : 0);
+                       }
+                       jacks++;
+               }
+       }
+}
+
+static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid,
+                                  unsigned char type)
+{
+       struct sigmatel_event *event = stac_get_event(codec, nid, type);
+       if (!event)
+               return;
+       codec->patch_ops.unsol_event(codec, (unsigned)event->tag << 26);
+}
 
 static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
 {
        struct sigmatel_spec *spec = codec->spec;
-       int idx = res >> 26 & 0x0f;
+       struct sigmatel_event *event;
+       int tag, data;
+
+       tag = (res >> 26) & 0x7f;
+       event = stac_get_event_from_tag(codec, tag);
+       if (!event)
+               return;
 
-       switch ((res >> 26) & 0x70) {
+       switch (event->type) {
        case STAC_HP_EVENT:
-               stac92xx_hp_detect(codec, res);
+               stac92xx_hp_detect(codec);
                /* fallthru */
+       case STAC_INSERT_EVENT:
        case STAC_PWR_EVENT:
                if (spec->num_pwrs > 0)
-                       stac92xx_pin_sense(codec, idx);
+                       stac92xx_pin_sense(codec, event->nid);
+               stac92xx_report_jack(codec, event->nid);
                break;
-       case STAC_VREF_EVENT: {
-               int data = snd_hda_codec_read(codec, codec->afg, 0,
-                       AC_VERB_GET_GPIO_DATA, 0);
+       case STAC_VREF_EVENT:
+               data = snd_hda_codec_read(codec, codec->afg, 0,
+                                         AC_VERB_GET_GPIO_DATA, 0);
                /* toggle VREF state based on GPIOx status */
                snd_hda_codec_write(codec, codec->afg, 0, 0x7e0,
-                       !!(data & (1 << idx)));
+                                   !!(data & (1 << event->data)));
                break;
-               }
        }
 }
 
+#ifdef CONFIG_PROC_FS
+static void stac92hd_proc_hook(struct snd_info_buffer *buffer,
+                              struct hda_codec *codec, hda_nid_t nid)
+{
+       if (nid == codec->afg)
+               snd_iprintf(buffer, "Power-Map: 0x%02x\n", 
+                           snd_hda_codec_read(codec, nid, 0, 0x0fec, 0x0));
+}
+
+static void analog_loop_proc_hook(struct snd_info_buffer *buffer,
+                                 struct hda_codec *codec,
+                                 unsigned int verb)
+{
+       snd_iprintf(buffer, "Analog Loopback: 0x%02x\n",
+                   snd_hda_codec_read(codec, codec->afg, 0, verb, 0));
+}
+
+/* stac92hd71bxx, stac92hd73xx */
+static void stac92hd7x_proc_hook(struct snd_info_buffer *buffer,
+                                struct hda_codec *codec, hda_nid_t nid)
+{
+       stac92hd_proc_hook(buffer, codec, nid);
+       if (nid == codec->afg)
+               analog_loop_proc_hook(buffer, codec, 0xfa0);
+}
+
+static void stac9205_proc_hook(struct snd_info_buffer *buffer,
+                              struct hda_codec *codec, hda_nid_t nid)
+{
+       if (nid == codec->afg)
+               analog_loop_proc_hook(buffer, codec, 0xfe0);
+}
+
+static void stac927x_proc_hook(struct snd_info_buffer *buffer,
+                              struct hda_codec *codec, hda_nid_t nid)
+{
+       if (nid == codec->afg)
+               analog_loop_proc_hook(buffer, codec, 0xfeb);
+}
+#else
+#define stac92hd_proc_hook     NULL
+#define stac92hd7x_proc_hook   NULL
+#define stac9205_proc_hook     NULL
+#define stac927x_proc_hook     NULL
+#endif
+
 #ifdef SND_HDA_NEEDS_RESUME
 static int stac92xx_resume(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec = codec->spec;
 
        stac92xx_set_config_regs(codec);
-       snd_hda_sequence_write(codec, spec->init);
-       stac_gpio_set(codec, spec->gpio_mask,
-               spec->gpio_dir, spec->gpio_data);
+       stac92xx_init(codec);
        snd_hda_codec_resume_amp(codec);
        snd_hda_codec_resume_cache(codec);
-       /* power down inactive DACs */
-       if (spec->dac_list)
-               stac92xx_power_down(codec);
-       /* invoke unsolicited event to reset the HP state */
+       /* fake event to set up pins again to override cached values */
        if (spec->hp_detect)
-               codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
+               stac_issue_unsol_event(codec, spec->autocfg.hp_pins[0],
+                                      STAC_HP_EVENT);
+       return 0;
+}
+
+static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       if (spec->eapd_mask)
+               stac_gpio_set(codec, spec->gpio_mask,
+                               spec->gpio_dir, spec->gpio_data &
+                               ~spec->eapd_mask);
        return 0;
 }
 #endif
@@ -4005,6 +4362,7 @@ static struct hda_codec_ops stac92xx_patch_ops = {
        .free = stac92xx_free,
        .unsol_event = stac92xx_unsol_event,
 #ifdef SND_HDA_NEEDS_RESUME
+       .suspend = stac92xx_suspend,
        .resume = stac92xx_resume,
 #endif
 };
@@ -4027,14 +4385,12 @@ static int patch_stac9200(struct hda_codec *codec)
        if (spec->board_config < 0) {
                snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9200, using BIOS defaults\n");
                err = stac92xx_save_bios_config_regs(codec);
-               if (err < 0) {
-                       stac92xx_free(codec);
-                       return err;
-               }
-               spec->pin_configs = spec->bios_pin_configs;
-       } else {
-               spec->pin_configs = stac9200_brd_tbl[spec->board_config];
-               stac92xx_set_config_regs(codec);
+       } else
+               err = stac_save_pin_cfgs(codec,
+                                        stac9200_brd_tbl[spec->board_config]);
+       if (err < 0) {
+               stac92xx_free(codec);
+               return err;
        }
 
        spec->multiout.max_channels = 2;
@@ -4090,14 +4446,12 @@ static int patch_stac925x(struct hda_codec *codec)
                snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC925x," 
                                      "using BIOS defaults\n");
                err = stac92xx_save_bios_config_regs(codec);
-               if (err < 0) {
-                       stac92xx_free(codec);
-                       return err;
-               }
-               spec->pin_configs = spec->bios_pin_configs;
-       } else if (stac925x_brd_tbl[spec->board_config] != NULL){
-               spec->pin_configs = stac925x_brd_tbl[spec->board_config];
-               stac92xx_set_config_regs(codec);
+       } else
+               err = stac_save_pin_cfgs(codec,
+                                        stac925x_brd_tbl[spec->board_config]);
+       if (err < 0) {
+               stac92xx_free(codec);
+               return err;
        }
 
        spec->multiout.max_channels = 2;
@@ -4179,14 +4533,12 @@ again:
                snd_printdd(KERN_INFO "hda_codec: Unknown model for"
                        " STAC92HD73XX, using BIOS defaults\n");
                err = stac92xx_save_bios_config_regs(codec);
-               if (err < 0) {
-                       stac92xx_free(codec);
-                       return err;
-               }
-               spec->pin_configs = spec->bios_pin_configs;
-       } else {
-               spec->pin_configs = stac92hd73xx_brd_tbl[spec->board_config];
-               stac92xx_set_config_regs(codec);
+       } else
+               err = stac_save_pin_cfgs(codec,
+                               stac92hd73xx_brd_tbl[spec->board_config]);
+       if (err < 0) {
+               stac92xx_free(codec);
+               return err;
        }
 
        spec->multiout.num_dacs = snd_hda_get_connections(codec, 0x0a,
@@ -4200,14 +4552,17 @@ again:
 
        switch (spec->multiout.num_dacs) {
        case 0x3: /* 6 Channel */
+               spec->multiout.hp_nid = 0x17;
                spec->mixer = stac92hd73xx_6ch_mixer;
                spec->init = stac92hd73xx_6ch_core_init;
                break;
        case 0x4: /* 8 Channel */
+               spec->multiout.hp_nid = 0x18;
                spec->mixer = stac92hd73xx_8ch_mixer;
                spec->init = stac92hd73xx_8ch_core_init;
                break;
        case 0x5: /* 10 Channel */
+               spec->multiout.hp_nid = 0x19;
                spec->mixer = stac92hd73xx_10ch_mixer;
                spec->init = stac92hd73xx_10ch_core_init;
        };
@@ -4235,31 +4590,29 @@ again:
        case STAC_DELL_EQ:
                spec->init = dell_eq_core_init;
                /* fallthru */
-       case STAC_DELL_M6:
+       case STAC_DELL_M6_AMIC:
+       case STAC_DELL_M6_DMIC:
+       case STAC_DELL_M6_BOTH:
                spec->num_smuxes = 0;
                spec->mixer = &stac92hd73xx_6ch_mixer[DELL_M6_MIXER];
                spec->amp_nids = &stac92hd73xx_amp_nids[DELL_M6_AMP];
+               spec->eapd_switch = 0;
                spec->num_amps = 1;
 
                if (!spec->init)
                        spec->init = dell_m6_core_init;
-               switch (codec->subsystem_id) {
-               case 0x1028025e: /* Analog Mics */
-               case 0x1028025f:
+               switch (spec->board_config) {
+               case STAC_DELL_M6_AMIC: /* Analog Mics */
                        stac92xx_set_config_reg(codec, 0x0b, 0x90A70170);
                        spec->num_dmics = 0;
                        spec->private_dimux.num_items = 1;
                        break;
-               case 0x10280271: /* Digital Mics */
-               case 0x10280272:
-               case 0x10280254:
-               case 0x10280255:
+               case STAC_DELL_M6_DMIC: /* Digital Mics */
                        stac92xx_set_config_reg(codec, 0x13, 0x90A60160);
                        spec->num_dmics = 1;
                        spec->private_dimux.num_items = 2;
                        break;
-               case 0x10280256: /* Both */
-               case 0x10280057:
+               case STAC_DELL_M6_BOTH: /* Both */
                        stac92xx_set_config_reg(codec, 0x0b, 0x90A70170);
                        stac92xx_set_config_reg(codec, 0x13, 0x90A60160);
                        spec->num_dmics = 1;
@@ -4270,6 +4623,7 @@ again:
        default:
                spec->num_dmics = STAC92HD73XX_NUM_DMICS;
                spec->num_smuxes = ARRAY_SIZE(stac92hd73xx_smux_nids);
+               spec->eapd_switch = 1;
        }
        if (spec->board_config > STAC_92HD73XX_REF) {
                /* GPIO0 High = Enable EAPD */
@@ -4298,8 +4652,13 @@ again:
                return err;
        }
 
+       if (spec->board_config == STAC_92HD73XX_NO_JD)
+               spec->hp_detect = 0;
+
        codec->patch_ops = stac92xx_patch_ops;
 
+       codec->proc_widget_hook = stac92hd7x_proc_hook;
+
        return 0;
 }
 
@@ -4360,14 +4719,12 @@ again:
                snd_printdd(KERN_INFO "hda_codec: Unknown model for"
                        " STAC92HD83XXX, using BIOS defaults\n");
                err = stac92xx_save_bios_config_regs(codec);
-               if (err < 0) {
-                       stac92xx_free(codec);
-                       return err;
-               }
-               spec->pin_configs = spec->bios_pin_configs;
-       } else {
-               spec->pin_configs = stac92hd83xxx_brd_tbl[spec->board_config];
-               stac92xx_set_config_regs(codec);
+       } else
+               err = stac_save_pin_cfgs(codec,
+                               stac92hd83xxx_brd_tbl[spec->board_config]);
+       if (err < 0) {
+               stac92xx_free(codec);
+               return err;
        }
 
        err = stac92xx_parse_auto_config(codec, 0x1d, 0);
@@ -4388,50 +4745,10 @@ again:
 
        codec->patch_ops = stac92xx_patch_ops;
 
-       return 0;
-}
-
-#ifdef SND_HDA_NEEDS_RESUME
-static void stac92hd71xx_set_power_state(struct hda_codec *codec, int pwr)
-{
-       struct sigmatel_spec *spec = codec->spec;
-       int i;
-       snd_hda_codec_write_cache(codec, codec->afg, 0,
-               AC_VERB_SET_POWER_STATE, pwr);
-
-       msleep(1);
-       for (i = 0; i < spec->num_adcs; i++) {
-               snd_hda_codec_write_cache(codec,
-                       spec->adc_nids[i], 0,
-                       AC_VERB_SET_POWER_STATE, pwr);
-       }
-};
-
-static int stac92hd71xx_resume(struct hda_codec *codec)
-{
-       stac92hd71xx_set_power_state(codec, AC_PWRST_D0);
-       return stac92xx_resume(codec);
-}
+       codec->proc_widget_hook = stac92hd_proc_hook;
 
-static int stac92hd71xx_suspend(struct hda_codec *codec, pm_message_t state)
-{
-       stac92hd71xx_set_power_state(codec, AC_PWRST_D3);
        return 0;
-};
-
-#endif
-
-static struct hda_codec_ops stac92hd71bxx_patch_ops = {
-       .build_controls = stac92xx_build_controls,
-       .build_pcms = stac92xx_build_pcms,
-       .init = stac92xx_init,
-       .free = stac92xx_free,
-       .unsol_event = stac92xx_unsol_event,
-#ifdef SND_HDA_NEEDS_RESUME
-       .resume = stac92hd71xx_resume,
-       .suspend = stac92hd71xx_suspend,
-#endif
-};
+}
 
 static struct hda_input_mux stac92hd71bxx_dmux = {
        .num_items = 4,
@@ -4468,14 +4785,19 @@ again:
                snd_printdd(KERN_INFO "hda_codec: Unknown model for"
                        " STAC92HD71BXX, using BIOS defaults\n");
                err = stac92xx_save_bios_config_regs(codec);
-               if (err < 0) {
-                       stac92xx_free(codec);
-                       return err;
-               }
-               spec->pin_configs = spec->bios_pin_configs;
-       } else {
-               spec->pin_configs = stac92hd71bxx_brd_tbl[spec->board_config];
-               stac92xx_set_config_regs(codec);
+       } else
+               err = stac_save_pin_cfgs(codec,
+                               stac92hd71bxx_brd_tbl[spec->board_config]);
+       if (err < 0) {
+               stac92xx_free(codec);
+               return err;
+       }
+
+       if (spec->board_config > STAC_92HD71BXX_REF) {
+               /* GPIO0 = EAPD */
+               spec->gpio_mask = 0x01;
+               spec->gpio_dir = 0x01;
+               spec->gpio_data = 0x01;
        }
 
        switch (codec->vendor_id) {
@@ -4488,24 +4810,24 @@ again:
                codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
                break;
        case 0x111d7608: /* 5 Port with Analog Mixer */
-               switch (codec->subsystem_id) {
-               case 0x103c361a:
+               switch (spec->board_config) {
+               case STAC_HP_M4:
                        /* Enable VREF power saving on GPIO1 detect */
-                       snd_hda_codec_write(codec, codec->afg, 0,
+                       err = stac_add_event(spec, codec->afg,
+                                            STAC_VREF_EVENT, 0x02);
+                       if (err < 0)
+                               return err;
+                       snd_hda_codec_write_cache(codec, codec->afg, 0,
                                AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x02);
                        snd_hda_codec_write_cache(codec, codec->afg, 0,
-                                       AC_VERB_SET_UNSOLICITED_ENABLE,
-                                       (AC_USRSP_EN | STAC_VREF_EVENT | 0x01));
+                               AC_VERB_SET_UNSOLICITED_ENABLE,
+                               AC_USRSP_EN | err);
                        spec->gpio_mask |= 0x02;
                        break;
                }
                if ((codec->revision_id & 0xf) == 0 ||
-                               (codec->revision_id & 0xf) == 1) {
-#ifdef SND_HDA_NEEDS_RESUME
-                       codec->patch_ops = stac92hd71bxx_patch_ops;
-#endif
+                   (codec->revision_id & 0xf) == 1)
                        spec->stream_delay = 40; /* 40 milliseconds */
-               }
 
                /* no output amps */
                spec->num_pwrs = 0;
@@ -4514,15 +4836,11 @@ again:
 
                /* disable VSW */
                spec->init = &stac92hd71bxx_analog_core_init[HD_DISABLE_PORTF];
-               stac92xx_set_config_reg(codec, 0xf, 0x40f000f0);
+               stac_change_pin_config(codec, 0xf, 0x40f000f0);
                break;
        case 0x111d7603: /* 6 Port with Analog Mixer */
-               if ((codec->revision_id & 0xf) == 1) {
-#ifdef SND_HDA_NEEDS_RESUME
-                       codec->patch_ops = stac92hd71bxx_patch_ops;
-#endif
+               if ((codec->revision_id & 0xf) == 1)
                        spec->stream_delay = 40; /* 40 milliseconds */
-               }
 
                /* no output amps */
                spec->num_pwrs = 0;
@@ -4537,13 +4855,6 @@ again:
        spec->aloopback_mask = 0x50;
        spec->aloopback_shift = 0;
 
-       if (spec->board_config > STAC_92HD71BXX_REF) {
-               /* GPIO0 = EAPD */
-               spec->gpio_mask = 0x01;
-               spec->gpio_dir = 0x01;
-               spec->gpio_data = 0x01;
-       }
-
        spec->powerdown_adcs = 1;
        spec->digbeep_nid = 0x26;
        spec->mux_nids = stac92hd71bxx_mux_nids;
@@ -4558,14 +4869,21 @@ again:
 
        switch (spec->board_config) {
        case STAC_HP_M4:
-               spec->num_dmics = 0;
-               spec->num_smuxes = 0;
-               spec->num_dmuxes = 0;
-
                /* enable internal microphone */
-               stac92xx_set_config_reg(codec, 0x0e, 0x01813040);
+               stac_change_pin_config(codec, 0x0e, 0x01813040);
                stac92xx_auto_set_pinctl(codec, 0x0e,
                        AC_PINCTL_IN_EN | AC_PINCTL_VREF_80);
+               /* fallthru */
+       case STAC_DELL_M4_2:
+               spec->num_dmics = 0;
+               spec->num_smuxes = 0;
+               spec->num_dmuxes = 0;
+               break;
+       case STAC_DELL_M4_1:
+       case STAC_DELL_M4_3:
+               spec->num_dmics = 1;
+               spec->num_smuxes = 0;
+               spec->num_dmuxes = 0;
                break;
        default:
                spec->num_dmics = STAC92HD71BXX_NUM_DMICS;
@@ -4597,6 +4915,8 @@ again:
                return err;
        }
 
+       codec->proc_widget_hook = stac92hd7x_proc_hook;
+
        return 0;
 };
 
@@ -4658,14 +4978,12 @@ static int patch_stac922x(struct hda_codec *codec)
                snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC922x, "
                        "using BIOS defaults\n");
                err = stac92xx_save_bios_config_regs(codec);
-               if (err < 0) {
-                       stac92xx_free(codec);
-                       return err;
-               }
-               spec->pin_configs = spec->bios_pin_configs;
-       } else if (stac922x_brd_tbl[spec->board_config] != NULL) {
-               spec->pin_configs = stac922x_brd_tbl[spec->board_config];
-               stac92xx_set_config_regs(codec);
+       } else
+               err = stac_save_pin_cfgs(codec,
+                               stac922x_brd_tbl[spec->board_config]);
+       if (err < 0) {
+               stac92xx_free(codec);
+               return err;
        }
 
        spec->adc_nids = stac922x_adc_nids;
@@ -4728,14 +5046,12 @@ static int patch_stac927x(struct hda_codec *codec)
                        snd_printdd(KERN_INFO "hda_codec: Unknown model for"
                                    "STAC927x, using BIOS defaults\n");
                err = stac92xx_save_bios_config_regs(codec);
-               if (err < 0) {
-                       stac92xx_free(codec);
-                       return err;
-               }
-               spec->pin_configs = spec->bios_pin_configs;
-       } else {
-               spec->pin_configs = stac927x_brd_tbl[spec->board_config];
-               stac92xx_set_config_regs(codec);
+       } else
+               err = stac_save_pin_cfgs(codec,
+                               stac927x_brd_tbl[spec->board_config]);
+       if (err < 0) {
+               stac92xx_free(codec);
+               return err;
        }
 
        spec->digbeep_nid = 0x23;
@@ -4765,15 +5081,15 @@ static int patch_stac927x(struct hda_codec *codec)
                case 0x10280209:
                case 0x1028022e:
                        /* correct the device field to SPDIF out */
-                       stac92xx_set_config_reg(codec, 0x21, 0x01442070);
+                       stac_change_pin_config(codec, 0x21, 0x01442070);
                        break;
                };
                /* configure the analog microphone on some laptops */
-               stac92xx_set_config_reg(codec, 0x0c, 0x90a79130);
+               stac_change_pin_config(codec, 0x0c, 0x90a79130);
                /* correct the front output jack as a hp out */
-               stac92xx_set_config_reg(codec, 0x0f, 0x0227011f);
+               stac_change_pin_config(codec, 0x0f, 0x0227011f);
                /* correct the front input jack as a mic */
-               stac92xx_set_config_reg(codec, 0x0e, 0x02a79130);
+               stac_change_pin_config(codec, 0x0e, 0x02a79130);
                /* fallthru */
        case STAC_DELL_3ST:
                /* GPIO2 High = Enable EAPD */
@@ -4802,6 +5118,7 @@ static int patch_stac927x(struct hda_codec *codec)
        spec->num_pwrs = 0;
        spec->aloopback_mask = 0x40;
        spec->aloopback_shift = 0;
+       spec->eapd_switch = 1;
 
        err = stac92xx_parse_auto_config(codec, 0x1e, 0x20);
        if (!err) {
@@ -4820,6 +5137,8 @@ static int patch_stac927x(struct hda_codec *codec)
 
        codec->patch_ops = stac92xx_patch_ops;
 
+       codec->proc_widget_hook = stac927x_proc_hook;
+
        /*
         * !!FIXME!!
         * The STAC927x seem to require fairly long delays for certain
@@ -4832,6 +5151,10 @@ static int patch_stac927x(struct hda_codec *codec)
         */
        codec->bus->needs_damn_long_delay = 1;
 
+       /* no jack detecion for ref-no-jd model */
+       if (spec->board_config == STAC_D965_REF_NO_JD)
+               spec->hp_detect = 0;
+
        return 0;
 }
 
@@ -4854,14 +5177,12 @@ static int patch_stac9205(struct hda_codec *codec)
        if (spec->board_config < 0) {
                snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9205, using BIOS defaults\n");
                err = stac92xx_save_bios_config_regs(codec);
-               if (err < 0) {
-                       stac92xx_free(codec);
-                       return err;
-               }
-               spec->pin_configs = spec->bios_pin_configs;
-       } else {
-               spec->pin_configs = stac9205_brd_tbl[spec->board_config];
-               stac92xx_set_config_regs(codec);
+       } else
+               err = stac_save_pin_cfgs(codec,
+                                        stac9205_brd_tbl[spec->board_config]);
+       if (err < 0) {
+               stac92xx_free(codec);
+               return err;
        }
 
        spec->digbeep_nid = 0x23;
@@ -4882,20 +5203,24 @@ static int patch_stac9205(struct hda_codec *codec)
 
        spec->aloopback_mask = 0x40;
        spec->aloopback_shift = 0;
+       spec->eapd_switch = 1;
        spec->multiout.dac_nids = spec->dac_nids;
        
        switch (spec->board_config){
        case STAC_9205_DELL_M43:
                /* Enable SPDIF in/out */
-               stac92xx_set_config_reg(codec, 0x1f, 0x01441030);
-               stac92xx_set_config_reg(codec, 0x20, 0x1c410030);
+               stac_change_pin_config(codec, 0x1f, 0x01441030);
+               stac_change_pin_config(codec, 0x20, 0x1c410030);
 
                /* Enable unsol response for GPIO4/Dock HP connection */
-               snd_hda_codec_write(codec, codec->afg, 0,
+               err = stac_add_event(spec, codec->afg, STAC_VREF_EVENT, 0x01);
+               if (err < 0)
+                       return err;
+               snd_hda_codec_write_cache(codec, codec->afg, 0,
                        AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10);
                snd_hda_codec_write_cache(codec, codec->afg, 0,
                                          AC_VERB_SET_UNSOLICITED_ENABLE,
-                                         (AC_USRSP_EN | STAC_HP_EVENT));
+                                         AC_USRSP_EN | err);
 
                spec->gpio_dir = 0x0b;
                spec->eapd_mask = 0x01;
@@ -4933,6 +5258,8 @@ static int patch_stac9205(struct hda_codec *codec)
 
        codec->patch_ops = stac92xx_patch_ops;
 
+       codec->proc_widget_hook = stac9205_proc_hook;
+
        return 0;
 }
 
@@ -4989,29 +5316,11 @@ static struct hda_verb vaio_ar_init[] = {
        {}
 };
 
-/* bind volumes of both NID 0x02 and 0x05 */
-static struct hda_bind_ctls vaio_bind_master_vol = {
-       .ops = &snd_hda_bind_vol,
-       .values = {
-               HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
-               HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
-               0
-       },
-};
-
-/* bind volumes of both NID 0x02 and 0x05 */
-static struct hda_bind_ctls vaio_bind_master_sw = {
-       .ops = &snd_hda_bind_sw,
-       .values = {
-               HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
-               HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
-               0,
-       },
-};
-
 static struct snd_kcontrol_new vaio_mixer[] = {
-       HDA_BIND_VOL("Master Playback Volume", &vaio_bind_master_vol),
-       HDA_BIND_SW("Master Playback Switch", &vaio_bind_master_sw),
+       HDA_CODEC_VOLUME("Headphone Playback Volume", 0x02, 0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Headphone Playback Switch", 0x02, 0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Speaker Playback Volume", 0x05, 0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Speaker Playback Switch", 0x05, 0, HDA_OUTPUT),
        /* HDA_CODEC_VOLUME("CD Capture Volume", 0x07, 0, HDA_INPUT), */
        HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_INPUT),
        HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_INPUT),
@@ -5027,8 +5336,10 @@ static struct snd_kcontrol_new vaio_mixer[] = {
 };
 
 static struct snd_kcontrol_new vaio_ar_mixer[] = {
-       HDA_BIND_VOL("Master Playback Volume", &vaio_bind_master_vol),
-       HDA_BIND_SW("Master Playback Switch", &vaio_bind_master_sw),
+       HDA_CODEC_VOLUME("Headphone Playback Volume", 0x02, 0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Headphone Playback Switch", 0x02, 0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Speaker Playback Volume", 0x05, 0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Speaker Playback Switch", 0x05, 0, HDA_OUTPUT),
        /* HDA_CODEC_VOLUME("CD Capture Volume", 0x07, 0, HDA_INPUT), */
        HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_INPUT),
        HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_INPUT),
@@ -5069,7 +5380,7 @@ static int stac9872_vaio_init(struct hda_codec *codec)
 
 static void stac9872_vaio_hp_detect(struct hda_codec *codec, unsigned int res)
 {
-       if (get_hp_pin_presence(codec, 0x0a)) {
+       if (get_pin_presence(codec, 0x0a)) {
                stac92xx_reset_pinctl(codec, 0x0f, AC_PINCTL_OUT_EN);
                stac92xx_set_pinctl(codec, 0x0a, AC_PINCTL_OUT_EN);
        } else {
@@ -5180,7 +5491,7 @@ static int patch_stac9872(struct hda_codec *codec)
 /*
  * patch entries
  */
-struct hda_codec_preset snd_hda_preset_sigmatel[] = {
+static struct hda_codec_preset snd_hda_preset_sigmatel[] = {
        { .id = 0x83847690, .name = "STAC9200", .patch = patch_stac9200 },
        { .id = 0x83847882, .name = "STAC9220 A1", .patch = patch_stac922x },
        { .id = 0x83847680, .name = "STAC9221 A1", .patch = patch_stac922x },
@@ -5244,3 +5555,27 @@ struct hda_codec_preset snd_hda_preset_sigmatel[] = {
        { .id = 0x111d76b7, .name = "92HD71B5X", .patch = patch_stac92hd71bxx },
        {} /* terminator */
 };
+
+MODULE_ALIAS("snd-hda-codec-id:8384*");
+MODULE_ALIAS("snd-hda-codec-id:111d*");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("IDT/Sigmatel HD-audio codec");
+
+static struct hda_codec_preset_list sigmatel_list = {
+       .preset = snd_hda_preset_sigmatel,
+       .owner = THIS_MODULE,
+};
+
+static int __init patch_sigmatel_init(void)
+{
+       return snd_hda_add_codec_preset(&sigmatel_list);
+}
+
+static void __exit patch_sigmatel_exit(void)
+{
+       snd_hda_delete_codec_preset(&sigmatel_list);
+}
+
+module_init(patch_sigmatel_init)
+module_exit(patch_sigmatel_exit)