ALSA: hda/idt - Fix power-map for speaker-pins with some HP laptops
[linux-flexiantxendom0-3.2.10.git] / sound / pci / hda / patch_sigmatel.c
index aaed004..2cb1e08 100644 (file)
@@ -37,6 +37,7 @@
 #include "hda_codec.h"
 #include "hda_local.h"
 #include "hda_beep.h"
+#include "hda_jack.h"
 
 enum {
        STAC_VREF_EVENT = 1,
@@ -98,6 +99,7 @@ enum {
        STAC_DELL_VOSTRO_3500,
        STAC_92HD83XXX_HP_cNB11_INTQUAD,
        STAC_HP_DV7_4000,
+       STAC_HP_ZEPHYR,
        STAC_92HD83XXX_MODELS
 };
 
@@ -175,13 +177,6 @@ enum {
        STAC_9872_MODELS
 };
 
-struct sigmatel_event {
-       hda_nid_t nid;
-       unsigned char type;
-       unsigned char tag;
-       int data;
-};
-
 struct sigmatel_mic_route {
        hda_nid_t pin;
        signed char mux_idx;
@@ -230,9 +225,6 @@ struct sigmatel_spec {
        const hda_nid_t *pwr_nids;
        const hda_nid_t *dac_list;
 
-       /* events */
-       struct snd_array events;
-
        /* playback */
        struct hda_input_mux *mono_mux;
        unsigned int cur_mmux;
@@ -318,6 +310,8 @@ struct sigmatel_spec {
        unsigned long auto_capvols[MAX_ADCS_NUM];
        unsigned auto_dmic_cnt;
        hda_nid_t auto_dmic_nids[MAX_DMICS_NUM];
+
+       struct hda_vmaster_mute_hook vmaster_mute;
 };
 
 static const hda_nid_t stac9200_adc_nids[1] = {
@@ -671,7 +665,6 @@ static int stac92xx_smux_enum_put(struct snd_kcontrol *kcontrol,
        return 0;
 }
 
-#ifdef CONFIG_SND_HDA_POWER_SAVE
 static int stac_vrefout_set(struct hda_codec *codec,
                                        hda_nid_t nid, unsigned int new_vref)
 {
@@ -695,7 +688,6 @@ static int stac_vrefout_set(struct hda_codec *codec,
 
        return 1;
 }
-#endif
 
 static unsigned int stac92xx_vref_set(struct hda_codec *codec,
                                        hda_nid_t nid, unsigned int new_vref)
@@ -903,6 +895,13 @@ static const struct hda_verb stac92hd83xxx_core_init[] = {
        {}
 };
 
+static const struct hda_verb stac92hd83xxx_hp_zephyr_init[] = {
+       { 0x22, 0x785, 0x43 },
+       { 0x22, 0x782, 0xe0 },
+       { 0x22, 0x795, 0x00 },
+       {}
+};
+
 static const struct hda_verb stac92hd71bxx_core_init[] = {
        /* set master volume and direct control */
        { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
@@ -1008,8 +1007,8 @@ static const struct hda_verb stac9205_core_init[] = {
        }
 
 static const struct snd_kcontrol_new stac9200_mixer[] = {
-       HDA_CODEC_VOLUME_MIN_MUTE("Master Playback Volume", 0xb, 0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Master Playback Switch", 0xb, 0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_MIN_MUTE("PCM Playback Volume", 0xb, 0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("PCM Playback Switch", 0xb, 0, HDA_OUTPUT),
        HDA_CODEC_VOLUME("Capture Volume", 0x0a, 0, HDA_OUTPUT),
        HDA_CODEC_MUTE("Capture Switch", 0x0a, 0, HDA_OUTPUT),
        { } /* end */
@@ -1036,8 +1035,8 @@ static const struct snd_kcontrol_new stac92hd71bxx_loopback[] = {
 };
 
 static const struct snd_kcontrol_new stac925x_mixer[] = {
-       HDA_CODEC_VOLUME_MIN_MUTE("Master Playback Volume", 0xe, 0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Master Playback Switch", 0x0e, 0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_MIN_MUTE("PCM Playback Volume", 0xe, 0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("PCM Playback Switch", 0x0e, 0, HDA_OUTPUT),
        { } /* end */
 };
 
@@ -1069,37 +1068,25 @@ static struct snd_kcontrol_new stac_smux_mixer = {
        .put = stac92xx_smux_enum_put,
 };
 
-static const char * const slave_vols[] = {
-       "Front Playback Volume",
-       "Surround Playback Volume",
-       "Center Playback Volume",
-       "LFE Playback Volume",
-       "Side Playback Volume",
-       "Headphone Playback Volume",
-       "Speaker Playback Volume",
+static const char * const slave_pfxs[] = {
+       "Front", "Surround", "Center", "LFE", "Side",
+       "Headphone", "Speaker", "IEC958",
        NULL
 };
 
-static const char * const slave_sws[] = {
-       "Front Playback Switch",
-       "Surround Playback Switch",
-       "Center Playback Switch",
-       "LFE Playback Switch",
-       "Side Playback Switch",
-       "Headphone Playback Switch",
-       "Speaker Playback Switch",
-       "IEC958 Playback Switch",
-       NULL
-};
+static void stac92xx_update_led_status(struct hda_codec *codec, int enabled);
+
+static void stac92xx_vmaster_hook(void *private_data, int val)
+{
+       stac92xx_update_led_status(private_data, val);
+}
 
 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;
+       unsigned int vmaster_tlv[4];
        int err;
        int i;
 
@@ -1156,22 +1143,28 @@ static int stac92xx_build_controls(struct hda_codec *codec)
        }
 
        /* if we have no master control, let's create it */
-       if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
-               unsigned int vmaster_tlv[4];
-               snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
-                                       HDA_OUTPUT, vmaster_tlv);
-               /* correct volume offset */
-               vmaster_tlv[2] += vmaster_tlv[3] * spec->volume_offset;
-               /* minimum value is actually mute */
-               vmaster_tlv[3] |= TLV_DB_SCALE_MUTE;
-               err = snd_hda_add_vmaster(codec, "Master Playback Volume",
-                                         vmaster_tlv, slave_vols);
-               if (err < 0)
-                       return err;
-       }
-       if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
-               err = snd_hda_add_vmaster(codec, "Master Playback Switch",
-                                         NULL, slave_sws);
+       snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
+                               HDA_OUTPUT, vmaster_tlv);
+       /* correct volume offset */
+       vmaster_tlv[2] += vmaster_tlv[3] * spec->volume_offset;
+       /* minimum value is actually mute */
+       vmaster_tlv[3] |= TLV_DB_SCALE_MUTE;
+       err = snd_hda_add_vmaster(codec, "Master Playback Volume",
+                                 vmaster_tlv, slave_pfxs,
+                                 "Playback Volume");
+       if (err < 0)
+               return err;
+
+       err = __snd_hda_add_vmaster(codec, "Master Playback Switch",
+                                   NULL, slave_pfxs,
+                                   "Playback Switch", true,
+                                   &spec->vmaster_mute.sw_kctl);
+       if (err < 0)
+               return err;
+
+       if (spec->gpio_led) {
+               spec->vmaster_mute.hook = stac92xx_vmaster_hook;
+               err = snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, true);
                if (err < 0)
                        return err;
        }
@@ -1185,31 +1178,9 @@ static int stac92xx_build_controls(struct hda_codec *codec)
 
        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 < cfg->num_inputs; i++) {
-               nid = cfg->inputs[i].pin;
-               err = stac92xx_add_jack(codec, nid, SND_JACK_MICROPHONE);
-               if (err < 0)
-                       return err;
-       }
+       err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
+       if (err < 0)
+               return err;
 
        return 0;       
 }
@@ -1630,7 +1601,7 @@ static const struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = {
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02bd,
                                "Dell Studio 1557", STAC_DELL_M6_DMIC),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02fe,
-                               "Dell Studio XPS 1645", STAC_DELL_M6_BOTH),
+                               "Dell Studio XPS 1645", STAC_DELL_M6_DMIC),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0413,
                                "Dell Studio 1558", STAC_DELL_M6_DMIC),
        {} /* terminator */
@@ -1642,7 +1613,7 @@ static const struct snd_pci_quirk stac92hd73xx_codec_id_cfg_tbl[] = {
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x043a,
                      "Alienware M17x", STAC_ALIENWARE_M17X),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0490,
-                     "Alienware M17x", STAC_ALIENWARE_M17X),
+                     "Alienware M17x R3", STAC_DELL_EQ),
        {} /* terminator */
 };
 
@@ -1670,6 +1641,12 @@ static const unsigned int hp_dv7_4000_pin_configs[10] = {
        0x40f000f0, 0x40f000f0,
 };
 
+static const unsigned int hp_zephyr_pin_configs[10] = {
+       0x01813050, 0x0421201f, 0x04a1205e, 0x96130310,
+       0x96130310, 0x0101401f, 0x1111611f, 0xd5a30130,
+       0, 0,
+};
+
 static const unsigned int hp_cNB11_intquad_pin_configs[10] = {
        0x40f000f0, 0x0221101f, 0x02a11020, 0x92170110,
        0x40f000f0, 0x92170110, 0x40f000f0, 0xd5a30130,
@@ -1683,6 +1660,7 @@ static const unsigned int *stac92hd83xxx_brd_tbl[STAC_92HD83XXX_MODELS] = {
        [STAC_DELL_VOSTRO_3500] = dell_vostro_3500_pin_configs,
        [STAC_92HD83XXX_HP_cNB11_INTQUAD] = hp_cNB11_intquad_pin_configs,
        [STAC_HP_DV7_4000] = hp_dv7_4000_pin_configs,
+       [STAC_HP_ZEPHYR] = hp_zephyr_pin_configs,
 };
 
 static const char * const stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = {
@@ -1693,6 +1671,7 @@ static const char * const stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = {
        [STAC_DELL_VOSTRO_3500] = "dell-vostro-3500",
        [STAC_92HD83XXX_HP_cNB11_INTQUAD] = "hp_cNB11_intquad",
        [STAC_HP_DV7_4000] = "hp-dv7-4000",
+       [STAC_HP_ZEPHYR] = "hp-zephyr",
 };
 
 static const struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = {
@@ -1745,6 +1724,14 @@ static const struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = {
                          "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
        SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3593,
                          "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3561,
+                         "HP", STAC_HP_ZEPHYR),
+       {} /* terminator */
+};
+
+static const struct snd_pci_quirk stac92hd83xxx_codec_id_cfg_tbl[] = {
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3561,
+                         "HP", STAC_HP_ZEPHYR),
        {} /* terminator */
 };
 
@@ -2871,7 +2858,8 @@ static inline int stac92xx_add_jack_mode_control(struct hda_codec *codec,
        }
 
        if (control) {
-               strcpy(name, hda_get_input_pin_label(codec, nid, 1));
+               snd_hda_get_pin_label(codec, nid, &spec->autocfg,
+                                     name, sizeof(name), NULL);
                return stac92xx_add_control(codec->spec, control,
                                        strcat(name, " Jack Mode"), nid);
        }
@@ -3549,7 +3537,7 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
        for (i = 0; i < spec->num_dmics; i++) {
                hda_nid_t nid;
                int index, type_idx;
-               const char *label;
+               char label[32];
 
                nid = spec->dmic_nids[i];
                if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
@@ -3562,7 +3550,8 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
                if (index < 0)
                        continue;
 
-               label = hda_get_input_pin_label(codec, nid, 1);
+               snd_hda_get_pin_label(codec, nid, &spec->autocfg,
+                                     label, sizeof(label), NULL);
                snd_hda_add_imux_item(dimux, label, index, &type_idx);
                if (snd_hda_get_bool_hint(codec, "separate_dmux") != 1)
                        snd_hda_add_imux_item(imux, label, index, &type_idx);
@@ -4160,65 +4149,18 @@ 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_HDA_INPUT_JACK
-       int def_conf = snd_hda_codec_get_pincfg(codec, nid);
-       int connectivity = get_defcfg_connect(def_conf);
-
-       if (connectivity && connectivity != AC_JACK_PORT_FIXED)
-               return 0;
-
-       return snd_hda_input_jack_add(codec, nid, type, NULL);
-#else
-       return 0;
-#endif /* CONFIG_SND_HDA_INPUT_JACK */
-}
-
-static int stac_add_event(struct sigmatel_spec *spec, hda_nid_t nid,
+static int stac_add_event(struct hda_codec *codec, hda_nid_t nid,
                          unsigned char type, int data)
 {
-       struct sigmatel_event *event;
+       struct hda_jack_tbl *event;
 
-       snd_array_init(&spec->events, sizeof(*event), 32);
-       event = snd_array_new(&spec->events);
+       event = snd_hda_jack_tbl_new(codec, nid);
        if (!event)
                return -ENOMEM;
-       event->nid = nid;
-       event->type = type;
-       event->tag = spec->events.used;
-       event->data = data;
-
-       return event->tag;
-}
+       event->action = type;
+       event->private_data = data;
 
-static struct sigmatel_event *stac_get_event(struct hda_codec *codec,
-                                            hda_nid_t nid)
-{
-       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)
-                       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;
+       return 0;
 }
 
 /* check if given nid is a valid pin and no other events are assigned
@@ -4228,34 +4170,29 @@ static struct sigmatel_event *stac_get_event_from_tag(struct hda_codec *codec,
 static int enable_pin_detect(struct hda_codec *codec, hda_nid_t nid,
                             unsigned int type)
 {
-       struct sigmatel_event *event;
-       int tag;
+       struct hda_jack_tbl *event;
 
        if (!is_jack_detectable(codec, nid))
                return 0;
-       event = stac_get_event(codec, nid);
-       if (event) {
-               if (event->type != type)
-                       return 0;
-               tag = event->tag;
-       } else {
-               tag = stac_add_event(codec->spec, nid, type, 0);
-               if (tag < 0)
-                       return 0;
-       }
-       snd_hda_codec_write_cache(codec, nid, 0,
-                                 AC_VERB_SET_UNSOLICITED_ENABLE,
-                                 AC_USRSP_EN | tag);
+       event = snd_hda_jack_tbl_new(codec, nid);
+       if (!event)
+               return -ENOMEM;
+       if (event->action && event->action != type)
+               return 0;
+       event->action = type;
+       snd_hda_jack_detect_enable(codec, nid, 0);
        return 1;
 }
 
-static int is_nid_hp_pin(struct auto_pin_cfg *cfg, hda_nid_t nid)
+static int is_nid_out_jack_pin(struct auto_pin_cfg *cfg, hda_nid_t nid)
 {
        int i;
        for (i = 0; i < cfg->hp_outs; i++)
                if (cfg->hp_pins[i] == nid)
                        return 1; /* nid is a HP-Out */
-
+       for (i = 0; i < cfg->line_outs; i++)
+               if (cfg->line_out_pins[i] == nid)
+                       return 1; /* nid is a line-Out */
        return 0; /* nid is not a HP-Out */
 };
 
@@ -4322,6 +4259,27 @@ static void stac_store_hints(struct hda_codec *codec)
        }
 }
 
+static void stac_issue_unsol_events(struct hda_codec *codec, int num_pins,
+                                   const hda_nid_t *pins)
+{
+       while (num_pins--)
+               stac_issue_unsol_event(codec, *pins++);
+}
+
+/* fake event to set up pins */
+static void stac_fake_hp_events(struct hda_codec *codec)
+{
+       struct sigmatel_spec *spec = codec->spec;
+
+       if (spec->autocfg.hp_outs)
+               stac_issue_unsol_events(codec, spec->autocfg.hp_outs,
+                                       spec->autocfg.hp_pins);
+       if (spec->autocfg.line_outs &&
+           spec->autocfg.line_out_pins[0] != spec->autocfg.hp_pins[0])
+               stac_issue_unsol_events(codec, spec->autocfg.line_outs,
+                                       spec->autocfg.line_out_pins);
+}
+
 static int stac92xx_init(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec = codec->spec;
@@ -4372,10 +4330,7 @@ static int stac92xx_init(struct hda_codec *codec)
                stac92xx_auto_set_pinctl(codec, spec->autocfg.line_out_pins[0],
                                AC_PINCTL_OUT_EN);
                /* fake event to set up pins */
-               if (cfg->hp_pins[0])
-                       stac_issue_unsol_event(codec, cfg->hp_pins[0]);
-               else if (cfg->line_out_pins[0])
-                       stac_issue_unsol_event(codec, cfg->line_out_pins[0]);
+               stac_fake_hp_events(codec);
        } else {
                stac92xx_auto_init_multi_out(codec);
                stac92xx_auto_init_hp_out(codec);
@@ -4443,7 +4398,7 @@ static int stac92xx_init(struct hda_codec *codec)
                        continue;
                }
 
-               if (is_nid_hp_pin(cfg, nid))
+               if (is_nid_out_jack_pin(cfg, nid))
                        continue; /* already has an unsol event */
 
                pinctl = snd_hda_codec_read(codec, nid, 0,
@@ -4460,9 +4415,9 @@ static int stac92xx_init(struct hda_codec *codec)
                def_conf = get_defcfg_connect(def_conf);
                /* skip any ports that don't have jacks since presence
                 * detection is useless */
-               if (def_conf != AC_JACK_PORT_COMPLEX) {
-                       if (def_conf != AC_JACK_PORT_NONE)
-                               stac_toggle_power_map(codec, nid, 1);
+               if (def_conf != AC_JACK_PORT_NONE &&
+                   !is_jack_detectable(codec, nid)) {
+                       stac_toggle_power_map(codec, nid, 1);
                        continue;
                }
                if (enable_pin_detect(codec, nid, STAC_PWR_EVENT)) {
@@ -4473,9 +4428,10 @@ static int stac92xx_init(struct hda_codec *codec)
                stac_toggle_power_map(codec, nid, 0);
        }
 
+       snd_hda_jack_report_sync(codec);
+
        /* sync mute LED */
-       if (spec->gpio_led)
-               hda_call_check_power_status(codec, 0x01);
+       snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
        if (spec->dac_list)
                stac92xx_power_down(codec);
        return 0;
@@ -4529,8 +4485,6 @@ static void stac92xx_free(struct hda_codec *codec)
                return;
 
        stac92xx_shutup(codec);
-       snd_hda_input_jack_free(codec);
-       snd_array_free(&spec->events);
 
        kfree(spec);
        snd_hda_detach_beep_device(codec);
@@ -4695,7 +4649,7 @@ static void stac92xx_hp_detect(struct hda_codec *codec)
                unsigned int val = AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN;
                if (no_hp_sensing(spec, i))
                        continue;
-               if (presence)
+               if (1 /*presence*/)
                        stac92xx_set_pinctl(codec, cfg->hp_pins[i], val);
 #if 0 /* FIXME */
 /* Resetting the pinctl like below may lead to (a sort of) regressions
@@ -4794,26 +4748,13 @@ static void stac92xx_mic_detect(struct hda_codec *codec)
                                          mic->mux_idx);
 }
 
-static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid)
-{
-       struct sigmatel_event *event = stac_get_event(codec, nid);
-       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)
+static void handle_unsol_event(struct hda_codec *codec,
+                              struct hda_jack_tbl *event)
 {
        struct sigmatel_spec *spec = codec->spec;
-       struct sigmatel_event *event;
-       int tag, data;
-
-       tag = (res >> 26) & 0x7f;
-       event = stac_get_event_from_tag(codec, tag);
-       if (!event)
-               return;
+       int data;
 
-       switch (event->type) {
+       switch (event->action) {
        case STAC_HP_EVENT:
        case STAC_LO_EVENT:
                stac92xx_hp_detect(codec);
@@ -4823,7 +4764,7 @@ static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
                break;
        }
 
-       switch (event->type) {
+       switch (event->action) {
        case STAC_HP_EVENT:
        case STAC_LO_EVENT:
        case STAC_MIC_EVENT:
@@ -4831,7 +4772,6 @@ static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
        case STAC_PWR_EVENT:
                if (spec->num_pwrs > 0)
                        stac92xx_pin_sense(codec, event->nid);
-               snd_hda_input_jack_report(codec, event->nid);
 
                switch (codec->subsystem_id) {
                case 0x103c308f:
@@ -4856,11 +4796,33 @@ static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
                                          AC_VERB_GET_GPIO_DATA, 0);
                /* toggle VREF state based on GPIOx status */
                snd_hda_codec_write(codec, codec->afg, 0, 0x7e0,
-                                   !!(data & (1 << event->data)));
+                                   !!(data & (1 << event->private_data)));
                break;
        }
 }
 
+static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid)
+{
+       struct hda_jack_tbl *event = snd_hda_jack_tbl_get(codec, nid);
+       if (!event)
+               return;
+       handle_unsol_event(codec, event);
+}
+
+static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+       struct hda_jack_tbl *event;
+       int tag;
+
+       tag = (res >> 26) & 0x7f;
+       event = snd_hda_jack_tbl_get_from_tag(codec, tag);
+       if (!event)
+               return;
+       event->jack_dirty = 1;
+       handle_unsol_event(codec, event);
+       snd_hda_jack_report_sync(codec);
+}
+
 static int hp_blike_system(u32 subsystem_id);
 
 static void set_hp_led_gpio(struct hda_codec *codec)
@@ -4928,7 +4890,14 @@ static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity)
                        /* BIOS bug: unfilled OEM string */
                        if (strstr(dev->name, "HP_Mute_LED_P_G")) {
                                set_hp_led_gpio(codec);
-                               spec->gpio_led_polarity = 1;
+                               switch (codec->subsystem_id) {
+                               case 0x103c148a:
+                                       spec->gpio_led_polarity = 0;
+                                       break;
+                               default:
+                                       spec->gpio_led_polarity = 1;
+                                       break;
+                               }
                                return 1;
                        }
                }
@@ -5026,19 +4995,11 @@ static void stac927x_proc_hook(struct snd_info_buffer *buffer,
 #ifdef CONFIG_PM
 static int stac92xx_resume(struct hda_codec *codec)
 {
-       struct sigmatel_spec *spec = codec->spec;
-
        stac92xx_init(codec);
        snd_hda_codec_resume_amp(codec);
        snd_hda_codec_resume_cache(codec);
        /* fake event to set up pins again to override cached values */
-       if (spec->hp_detect) {
-               if (spec->autocfg.hp_pins[0])
-                       stac_issue_unsol_event(codec, spec->autocfg.hp_pins[0]);
-               else if (spec->autocfg.line_out_pins[0])
-                       stac_issue_unsol_event(codec,
-                                              spec->autocfg.line_out_pins[0]);
-       }
+       stac_fake_hp_events(codec);
        return 0;
 }
 
@@ -5048,7 +5009,6 @@ static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state)
        return 0;
 }
 
-#ifdef CONFIG_SND_HDA_POWER_SAVE
 static int stac92xx_pre_resume(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec = codec->spec;
@@ -5083,82 +5043,39 @@ static void stac92xx_set_power_state(struct hda_codec *codec, hda_nid_t fg,
                        afg_power_state);
        snd_hda_codec_set_power_to_all(codec, fg, power_state, true);
 }
+#else
+#define stac92xx_suspend       NULL
+#define stac92xx_resume                NULL
+#define stac92xx_pre_resume    NULL
+#define stac92xx_set_power_state NULL
+#endif /* CONFIG_PM */
 
-/*
- * For this feature CONFIG_SND_HDA_POWER_SAVE is needed
- * as mute LED state is updated in check_power_status hook
- */
-static int stac92xx_update_led_status(struct hda_codec *codec)
+/* update mute-LED accoring to the master switch */
+static void stac92xx_update_led_status(struct hda_codec *codec, int enabled)
 {
        struct sigmatel_spec *spec = codec->spec;
-       int i, num_ext_dacs, muted = 1;
-       unsigned int muted_lvl, notmtd_lvl;
-       hda_nid_t nid;
+       int muted = !enabled;
 
        if (!spec->gpio_led)
-               return 0;
+               return;
+
+       /* LED state is inverted on these systems */
+       if (spec->gpio_led_polarity)
+               muted = !muted;
 
-       for (i = 0; i < spec->multiout.num_dacs; i++) {
-               nid = spec->multiout.dac_nids[i];
-               if (!(snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) &
-                     HDA_AMP_MUTE)) {
-                       muted = 0; /* something heard */
-                       break;
-               }
-       }
-       if (muted && spec->multiout.hp_nid)
-               if (!(snd_hda_codec_amp_read(codec,
-                               spec->multiout.hp_nid, 0, HDA_OUTPUT, 0) &
-                                       HDA_AMP_MUTE)) {
-                       muted = 0; /* HP is not muted */
-               }
-       num_ext_dacs = ARRAY_SIZE(spec->multiout.extra_out_nid);
-       for (i = 0; muted && i < num_ext_dacs; i++) {
-               nid = spec->multiout.extra_out_nid[i];
-               if (nid == 0)
-                       break;
-               if (!(snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) &
-                     HDA_AMP_MUTE)) {
-                       muted = 0; /* extra output is not muted */
-               }
-       }
-       /*polarity defines *not* muted state level*/
        if (!spec->vref_mute_led_nid) {
                if (muted)
-                       spec->gpio_data &= ~spec->gpio_led; /* orange */
+                       spec->gpio_data |= spec->gpio_led;
                else
-                       spec->gpio_data |= spec->gpio_led; /* white */
-
-               if (!spec->gpio_led_polarity) {
-                       /* LED state is inverted on these systems */
-                       spec->gpio_data ^= spec->gpio_led;
-               }
+                       spec->gpio_data &= ~spec->gpio_led;
                stac_gpio_set(codec, spec->gpio_mask,
                                spec->gpio_dir, spec->gpio_data);
        } else {
-               notmtd_lvl = spec->gpio_led_polarity ?
-                               AC_PINCTL_VREF_HIZ : AC_PINCTL_VREF_GRD;
-               muted_lvl = spec->gpio_led_polarity ?
-                               AC_PINCTL_VREF_GRD : AC_PINCTL_VREF_HIZ;
-               spec->vref_led = muted ? muted_lvl : notmtd_lvl;
+               spec->vref_led = muted ? AC_PINCTL_VREF_50 : AC_PINCTL_VREF_GRD;
                stac_vrefout_set(codec, spec->vref_mute_led_nid,
                                 spec->vref_led);
        }
-       return 0;
-}
-
-/*
- * use power check for controlling mute led of HP notebooks
- */
-static int stac92xx_check_power_status(struct hda_codec *codec,
-                                             hda_nid_t nid)
-{
-       stac92xx_update_led_status(codec);
-
-       return 0;
 }
-#endif /* CONFIG_SND_HDA_POWER_SAVE */
-#endif /* CONFIG_PM */
 
 static const struct hda_codec_ops stac92xx_patch_ops = {
        .build_controls = stac92xx_build_controls,
@@ -5639,6 +5556,12 @@ static int patch_stac92hd83xxx(struct hda_codec *codec)
                                                        STAC_92HD83XXX_MODELS,
                                                        stac92hd83xxx_models,
                                                        stac92hd83xxx_cfg_tbl);
+       /* check codec subsystem id if not found */
+       if (spec->board_config < 0)
+               spec->board_config =
+                       snd_hda_check_board_codec_sid_config(codec,
+                               STAC_92HD83XXX_MODELS, stac92hd83xxx_models,
+                               stac92hd83xxx_codec_id_cfg_tbl);
 again:
        if (spec->board_config < 0)
                snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
@@ -5649,12 +5572,17 @@ again:
 
        codec->patch_ops = stac92xx_patch_ops;
 
+       switch (spec->board_config) {
+       case STAC_HP_ZEPHYR:
+               spec->init = stac92hd83xxx_hp_zephyr_init;
+               break;
+       }
+
        if (find_mute_led_cfg(codec, -1/*no default cfg*/))
                snd_printd("mute LED gpio %d polarity %d\n",
                                spec->gpio_led,
                                spec->gpio_led_polarity);
 
-#ifdef CONFIG_SND_HDA_POWER_SAVE
        if (spec->gpio_led) {
                if (!spec->vref_mute_led_nid) {
                        spec->gpio_mask |= spec->gpio_led;
@@ -5664,11 +5592,10 @@ again:
                        codec->patch_ops.set_power_state =
                                        stac92xx_set_power_state;
                }
+#ifdef CONFIG_PM
                codec->patch_ops.pre_resume = stac92xx_pre_resume;
-               codec->patch_ops.check_power_status =
-                       stac92xx_check_power_status;
+#endif
        }
-#endif 
 
        err = stac92xx_parse_auto_config(codec);
        if (!err) {
@@ -5837,15 +5764,13 @@ again:
                switch (spec->board_config) {
                case STAC_HP_M4:
                        /* Enable VREF power saving on GPIO1 detect */
-                       err = stac_add_event(spec, codec->afg,
+                       err = stac_add_event(codec, 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 | err);
+                       snd_hda_jack_detect_enable(codec, codec->afg, 0);
                        spec->gpio_mask |= 0x02;
                        break;
                }
@@ -5967,7 +5892,6 @@ again:
                                spec->gpio_led,
                                spec->gpio_led_polarity);
 
-#ifdef CONFIG_SND_HDA_POWER_SAVE
        if (spec->gpio_led) {
                if (!spec->vref_mute_led_nid) {
                        spec->gpio_mask |= spec->gpio_led;
@@ -5977,11 +5901,10 @@ again:
                        codec->patch_ops.set_power_state =
                                        stac92xx_set_power_state;
                }
+#ifdef CONFIG_PM
                codec->patch_ops.pre_resume = stac92xx_pre_resume;
-               codec->patch_ops.check_power_status =
-                       stac92xx_check_power_status;
+#endif
        }
-#endif 
 
        spec->multiout.dac_nids = spec->dac_nids;
 
@@ -6316,14 +6239,12 @@ static int patch_stac9205(struct hda_codec *codec)
                snd_hda_codec_set_pincfg(codec, 0x20, 0x1c410030);
 
                /* Enable unsol response for GPIO4/Dock HP connection */
-               err = stac_add_event(spec, codec->afg, STAC_VREF_EVENT, 0x01);
+               err = stac_add_event(codec, 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 | err);
+               snd_hda_jack_detect_enable(codec, codec->afg, 0);
 
                spec->gpio_dir = 0x0b;
                spec->eapd_mask = 0x01;