Merge branch 'topic/fix/hda' into topic/hda
authorTakashi Iwai <tiwai@suse.de>
Tue, 18 Nov 2008 09:57:07 +0000 (10:57 +0100)
committerTakashi Iwai <tiwai@suse.de>
Tue, 18 Nov 2008 09:57:07 +0000 (10:57 +0100)
1  2 
sound/pci/hda/patch_sigmatel.c

  #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
@@@ -131,23 -132,11 +131,23 @@@ enum 
        STAC_927X_MODELS
  };
  
 +struct sigmatel_event {
 +      hda_nid_t nid;
 +      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;
        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;
        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;
  
        /* 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;
@@@ -1247,14 -1232,9 +1247,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;
  
        }
        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;
                        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;       
  }
  
@@@ -1739,6 -1688,10 +1739,10 @@@ static struct snd_pci_quirk stac92hd71b
        /* 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,
@@@ -2218,11 -2171,12 +2222,11 @@@ static int stac92xx_save_bios_config_re
        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];
                        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;
@@@ -2274,39 -2228,6 +2278,39 @@@ static void stac92xx_set_config_regs(st
                                        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
   */
@@@ -2544,7 -2465,7 +2548,7 @@@ static int stac92xx_hp_switch_put(struc
        /* check to be sure that the ports are upto date with
         * switch changes
         */
 -      codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
 +      codec->patch_ops.unsol_event(codec, (STAC_HP_EVENT | nid) << 26);
  
        return 1;
  }
@@@ -2584,8 -2505,7 +2588,8 @@@ static int stac92xx_io_switch_put(struc
         * appropriately according to the pin direction
         */
        if (spec->hp_detect)
 -              codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
 +              codec->patch_ops.unsol_event(codec,
 +                      (STAC_HP_EVENT | nid) << 26);
  
          return 1;
  }
@@@ -2680,16 -2600,28 +2684,16 @@@ static int stac92xx_add_control_temp(st
  {
        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;
  }
  
@@@ -3566,8 -3498,8 +3570,8 @@@ static int stac92xx_parse_auto_config(s
        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;
@@@ -3674,8 -3606,8 +3678,8 @@@ static int stac9200_parse_auto_config(s
        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;
@@@ -3719,74 -3651,13 +3723,74 @@@ static void stac_gpio_set(struct hda_co
                           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 stac92xx_add_event(struct sigmatel_spec *spec, hda_nid_t nid,
 +                           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->data = data;
 +
 +      return 0;
 +}
 +
 +static int stac92xx_event_data(struct hda_codec *codec, hda_nid_t nid)
 +{
 +      struct sigmatel_spec *spec = codec->spec;
 +      struct sigmatel_event *events = spec->events.list;
 +      if (events) {
 +              int i;
 +              for (i = 0; i < spec->events.used; i++)
 +                      if (events[i].nid == nid)
 +                              return events[i].data;
 +      }
 +      return 0;
 +}
 +
  static void enable_pin_detect(struct hda_codec *codec, hda_nid_t nid,
                              unsigned int event)
  {
 -      if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP)
 +      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));
 +                                        (AC_USRSP_EN | event | nid));
 +      }
  }
  
  static int is_nid_hp_pin(struct auto_pin_cfg *cfg, hda_nid_t nid)
@@@ -3829,18 -3700,17 +3833,18 @@@ static int stac92xx_init(struct hda_cod
        /* 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 | nid);
 +              }
                /* 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);
 +              codec->patch_ops.unsol_event(codec,
 +                      (STAC_HP_EVENT | spec->autocfg.hp_pins[0]) << 26);
        } else {
                stac92xx_auto_init_multi_out(codec);
                stac92xx_auto_init_hp_out(codec);
                        }
                        pinctl |= AC_PINCTL_IN_EN;
                        stac92xx_auto_set_pinctl(codec, nid, pinctl);
 +                      enable_pin_detect(codec, nid, STAC_INSERT_EVENT | nid);
                }
        }
        for (i = 0; i < spec->num_dmics; i++)
        return 0;
  }
  
 +static void stac92xx_free_jacks(struct hda_codec *codec)
 +{
 +#ifdef CONFIG_SND_JACK
 +      struct sigmatel_spec *spec = codec->spec;
 +      if (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
 +}
 +
 +static void stac92xx_free_kctls(struct hda_codec *codec)
 +{
 +      struct sigmatel_spec *spec = codec->spec;
 +
 +      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);
@@@ -4053,7 -3901,7 +4057,7 @@@ static void stac92xx_hp_detect(struct h
                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);
                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);
@@@ -4107,57 -3955,24 +4111,57 @@@ static void stac92xx_pin_sense(struct h
  
        /* power down unused output ports */
        snd_hda_codec_write(codec, codec->afg, 0, 0x7ec, val);
 -};
 +}
 +
 +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_hp_pin_presence(codec, nid)
 +                                      ? type : 0);
 +                      }
 +                      jacks++;
 +              }
 +      }
 +}
  
  static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
  {
        struct sigmatel_spec *spec = codec->spec;
 -      int idx = res >> 26 & 0x0f;
 +      int event = (res >> 26) & 0x70;
 +      int nid = res >> 26 & 0x0f;
  
 -      switch ((res >> 26) & 0x70) {
 +      switch (event) {
        case STAC_HP_EVENT:
                stac92xx_hp_detect(codec, res);
                /* fallthru */
 +      case STAC_INSERT_EVENT:
        case STAC_PWR_EVENT:
 -              if (spec->num_pwrs > 0)
 -                      stac92xx_pin_sense(codec, idx);
 +              if (nid) {
 +                      if (spec->num_pwrs > 0)
 +                              stac92xx_pin_sense(codec, nid);
 +                      stac92xx_report_jack(codec, nid);
 +              }
                break;
        case STAC_VREF_EVENT: {
                int data = snd_hda_codec_read(codec, codec->afg, 0,
                        AC_VERB_GET_GPIO_DATA, 0);
 +              int idx = stac92xx_event_data(codec, nid);
                /* toggle VREF state based on GPIOx status */
                snd_hda_codec_write(codec, codec->afg, 0, 0x7e0,
                        !!(data & (1 << idx)));
@@@ -4172,13 -3987,17 +4176,13 @@@ static int stac92xx_resume(struct hda_c
        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);
 +              codec->patch_ops.unsol_event(codec,
 +                      (STAC_HP_EVENT | spec->autocfg.hp_pins[0]) << 26);
        return 0;
  }
  #endif
@@@ -4212,12 -4031,14 +4216,12 @@@ static int patch_stac9200(struct hda_co
        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;
@@@ -4273,12 -4094,14 +4277,12 @@@ static int patch_stac925x(struct hda_co
                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;
@@@ -4360,12 -4183,14 +4364,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,
                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)
        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 */
@@@ -4541,12 -4364,14 +4545,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);
@@@ -4594,13 -4419,7 +4598,13 @@@ static int stac92hd71xx_resume(struct h
  
  static int stac92hd71xx_suspend(struct hda_codec *codec, pm_message_t state)
  {
 +      struct sigmatel_spec *spec = codec->spec;
 +
        stac92hd71xx_set_power_state(codec, AC_PWRST_D3);
 +      if (spec->eapd_mask)
 +              stac_gpio_set(codec, spec->gpio_mask,
 +                              spec->gpio_dir, spec->gpio_data &
 +                              ~spec->eapd_mask);
        return 0;
  };
  
@@@ -4653,14 -4472,23 +4657,21 @@@ 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) {
        case 0x111d76b6: /* 4 Port without Analog Mixer */
        case 0x111d76b7:
                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,
+                       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 | STAC_VREF_EVENT | codec->afg));
 +                      err = stac92xx_add_event(spec, codec->afg, 0x02);
 +                      if (err < 0)
 +                              return err;
                        spec->gpio_mask |= 0x02;
                        break;
                }
  
                /* 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) {
        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;
                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);
                break;
@@@ -4844,12 -4662,14 +4848,12 @@@ static int patch_stac922x(struct hda_co
                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;
@@@ -4912,12 -4732,14 +4916,12 @@@ static int patch_stac927x(struct hda_co
                        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;
                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 */
        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) {
@@@ -5037,12 -4858,14 +5041,12 @@@ static int patch_stac9205(struct hda_co
        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;
  
        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,
+               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_VERB_SET_UNSOLICITED_ENABLE,
 +                      (AC_USRSP_EN | STAC_VREF_EVENT | codec->afg));
 +              err = stac92xx_add_event(spec, codec->afg, 0x01);
 +              if (err < 0)
 +                      return err;
  
                spec->gpio_dir = 0x0b;
                spec->eapd_mask = 0x01;
@@@ -5174,11 -4993,29 +5178,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),
  };
  
  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),