]> git.ipfire.org Git - people/pmueller/ipfire-2.x.git/blobdiff - src/patches/suse-2.6.27.31/patches.drivers/alsa-hda-sigmatel-update
Reenabled linux-xen, added patches for Xen Kernel Version 2.6.27.31,
[people/pmueller/ipfire-2.x.git] / src / patches / suse-2.6.27.31 / patches.drivers / alsa-hda-sigmatel-update
diff --git a/src/patches/suse-2.6.27.31/patches.drivers/alsa-hda-sigmatel-update b/src/patches/suse-2.6.27.31/patches.drivers/alsa-hda-sigmatel-update
new file mode 100644 (file)
index 0000000..a41761a
--- /dev/null
@@ -0,0 +1,1772 @@
+From: Takashi Iwai <tiwai@suse.de>
+Subject: ALSA: hda - IDT/Sigmatel codec updates
+Patch-mainline: 2.6.28-rc1
+References: bnc#409140
+
+A pile of updates for IDT/Sigtmatel codecs.
+
+- digital beep support
+- 92HD83x codecs
+- HP M4 model support
+- ECS 202 model support
+- Multiple SPDIF support
+- More power-saving
+
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+
+---
+---
+ sound/pci/hda/hda_codec.c      |    9 
+ sound/pci/hda/hda_local.h      |    9 
+ sound/pci/hda/patch_sigmatel.c |  975 ++++++++++++++++++++++++++++++++++++-----
+ 3 files changed, 877 insertions(+), 116 deletions(-)
+
+--- a/sound/pci/hda/hda_codec.c
++++ b/sound/pci/hda/hda_codec.c
+@@ -959,15 +959,6 @@ void snd_hda_codec_resume_amp(struct hda
+ }
+ #endif /* SND_HDA_NEEDS_RESUME */
+-/*
+- * AMP control callbacks
+- */
+-/* retrieve parameters from private_value */
+-#define get_amp_nid(kc)               ((kc)->private_value & 0xffff)
+-#define get_amp_channels(kc)  (((kc)->private_value >> 16) & 0x3)
+-#define get_amp_direction(kc) (((kc)->private_value >> 18) & 0x1)
+-#define get_amp_index(kc)     (((kc)->private_value >> 19) & 0xf)
+-
+ /* volume */
+ int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_info *uinfo)
+--- a/sound/pci/hda/hda_local.h
++++ b/sound/pci/hda/hda_local.h
+@@ -421,4 +421,13 @@ int snd_hda_check_amp_list_power(struct 
+                                hda_nid_t nid);
+ #endif /* CONFIG_SND_HDA_POWER_SAVE */
++/*
++ * AMP control callbacks
++ */
++/* retrieve parameters from private_value */
++#define get_amp_nid(kc)               ((kc)->private_value & 0xffff)
++#define get_amp_channels(kc)  (((kc)->private_value >> 16) & 0x3)
++#define get_amp_direction(kc) (((kc)->private_value >> 18) & 0x1)
++#define get_amp_index(kc)     (((kc)->private_value >> 19) & 0xf)
++
+ #endif /* __SOUND_HDA_LOCAL_H */
+--- a/sound/pci/hda/patch_sigmatel.c
++++ b/sound/pci/hda/patch_sigmatel.c
+@@ -33,10 +33,12 @@
+ #include "hda_codec.h"
+ #include "hda_local.h"
+ #include "hda_patch.h"
++#include "hda_beep.h"
+ #define NUM_CONTROL_ALLOC     32
+ #define STAC_PWR_EVENT                0x20
+ #define STAC_HP_EVENT         0x30
++#define STAC_VREF_EVENT               0x40
+ enum {
+       STAC_REF,
+@@ -72,9 +74,15 @@ enum {
+ };
+ enum {
++      STAC_92HD83XXX_REF,
++      STAC_92HD83XXX_MODELS
++};
++
++enum {
+       STAC_92HD71BXX_REF,
+       STAC_DELL_M4_1,
+       STAC_DELL_M4_2,
++      STAC_HP_M4,
+       STAC_92HD71BXX_MODELS
+ };
+@@ -132,6 +140,7 @@ struct sigmatel_spec {
+       unsigned int mic_switch: 1;
+       unsigned int alt_switch: 1;
+       unsigned int hp_detect: 1;
++      unsigned int spdif_mute: 1;
+       /* gpio lines */
+       unsigned int eapd_mask;
+@@ -140,17 +149,22 @@ struct sigmatel_spec {
+       unsigned int gpio_data;
+       unsigned int gpio_mute;
++      /* stream */
++      unsigned int stream_delay;
++
+       /* analog loopback */
+       unsigned char aloopback_mask;
+       unsigned char aloopback_shift;
+       /* power management */
+       unsigned int num_pwrs;
++      unsigned int *pwr_mapping;
+       hda_nid_t *pwr_nids;
+       hda_nid_t *dac_list;
+       /* playback */
+       struct hda_input_mux *mono_mux;
++      struct hda_input_mux *amp_mux;
+       unsigned int cur_mmux;
+       struct hda_multi_out multiout;
+       hda_nid_t dac_nids[5];
+@@ -164,8 +178,14 @@ struct sigmatel_spec {
+       unsigned int num_dmics;
+       hda_nid_t *dmux_nids;
+       unsigned int num_dmuxes;
++      hda_nid_t *smux_nids;
++      unsigned int num_smuxes;
++      const char **spdif_labels;
++
+       hda_nid_t dig_in_nid;
+       hda_nid_t mono_nid;
++      hda_nid_t anabeep_nid;
++      hda_nid_t digbeep_nid;
+       /* pin widgets */
+       hda_nid_t *pin_nids;
+@@ -182,6 +202,12 @@ struct sigmatel_spec {
+       unsigned int cur_dmux[2];
+       struct hda_input_mux *input_mux;
+       unsigned int cur_mux[3];
++      struct hda_input_mux *sinput_mux;
++      unsigned int cur_smux[2];
++      unsigned int cur_amux;
++      hda_nid_t *amp_nids;
++      unsigned int num_amps;
++      unsigned int powerdown_adcs;
+       /* i/o switches */
+       unsigned int io_switch[2];
+@@ -197,6 +223,8 @@ struct sigmatel_spec {
+       struct snd_kcontrol_new *kctl_alloc;
+       struct hda_input_mux private_dimux;
+       struct hda_input_mux private_imux;
++      struct hda_input_mux private_smux;
++      struct hda_input_mux private_amp_mux;
+       struct hda_input_mux private_mono_mux;
+ };
+@@ -217,10 +245,19 @@ static hda_nid_t stac92hd73xx_pwr_nids[8
+       0x0f, 0x10, 0x11
+ };
++static hda_nid_t stac92hd73xx_slave_dig_outs[2] = {
++      0x26, 0,
++};
++
+ static hda_nid_t stac92hd73xx_adc_nids[2] = {
+       0x1a, 0x1b
+ };
++#define DELL_M6_AMP 2
++static hda_nid_t stac92hd73xx_amp_nids[3] = {
++      0x0b, 0x0c, 0x0e
++};
++
+ #define STAC92HD73XX_NUM_DMICS        2
+ static hda_nid_t stac92hd73xx_dmic_nids[STAC92HD73XX_NUM_DMICS + 1] = {
+       0x13, 0x14, 0
+@@ -239,6 +276,41 @@ static hda_nid_t stac92hd73xx_dmux_nids[
+       0x20, 0x21,
+ };
++static hda_nid_t stac92hd73xx_smux_nids[2] = {
++      0x22, 0x23,
++};
++
++#define STAC92HD83XXX_NUM_DMICS       2
++static hda_nid_t stac92hd83xxx_dmic_nids[STAC92HD83XXX_NUM_DMICS + 1] = {
++      0x11, 0x12, 0
++};
++
++#define STAC92HD81_DAC_COUNT 2
++#define STAC92HD83_DAC_COUNT 3
++static hda_nid_t stac92hd83xxx_dac_nids[STAC92HD73_DAC_COUNT] = {
++      0x13, 0x14, 0x22,
++};
++
++static hda_nid_t stac92hd83xxx_dmux_nids[2] = {
++      0x17, 0x18,
++};
++
++static hda_nid_t stac92hd83xxx_adc_nids[2] = {
++      0x15, 0x16,
++};
++
++static hda_nid_t stac92hd83xxx_pwr_nids[4] = {
++      0xa, 0xb, 0xd, 0xe,
++};
++
++static hda_nid_t stac92hd83xxx_slave_dig_outs[2] = {
++      0x1e, 0,
++};
++
++static unsigned int stac92hd83xxx_pwr_mapping[4] = {
++      0x03, 0x0c, 0x10, 0x40,
++};
++
+ static hda_nid_t stac92hd71bxx_pwr_nids[3] = {
+       0x0a, 0x0d, 0x0f
+ };
+@@ -251,8 +323,12 @@ static hda_nid_t stac92hd71bxx_mux_nids[
+       0x1a, 0x1b
+ };
+-static hda_nid_t stac92hd71bxx_dmux_nids[1] = {
+-      0x1c,
++static hda_nid_t stac92hd71bxx_dmux_nids[2] = {
++      0x1c, 0x1d,
++};
++
++static hda_nid_t stac92hd71bxx_smux_nids[2] = {
++      0x24, 0x25,
+ };
+ static hda_nid_t stac92hd71bxx_dac_nids[1] = {
+@@ -264,6 +340,10 @@ static hda_nid_t stac92hd71bxx_dmic_nids
+       0x18, 0x19, 0
+ };
++static hda_nid_t stac92hd71bxx_slave_dig_outs[2] = {
++      0x22, 0
++};
++
+ static hda_nid_t stac925x_adc_nids[1] = {
+         0x03,
+ };
+@@ -301,6 +381,10 @@ static hda_nid_t stac927x_mux_nids[3] = 
+         0x15, 0x16, 0x17
+ };
++static hda_nid_t stac927x_smux_nids[1] = {
++      0x21,
++};
++
+ static hda_nid_t stac927x_dac_nids[6] = {
+       0x02, 0x03, 0x04, 0x05, 0x06, 0
+ };
+@@ -314,6 +398,11 @@ static hda_nid_t stac927x_dmic_nids[STAC
+       0x13, 0x14, 0
+ };
++static const char *stac927x_spdif_labels[5] = {
++      "Digital Playback", "ADAT", "Analog Mux 1",
++      "Analog Mux 2", "Analog Mux 3"
++};
++
+ static hda_nid_t stac9205_adc_nids[2] = {
+         0x12, 0x13
+ };
+@@ -326,6 +415,10 @@ static hda_nid_t stac9205_dmux_nids[1] =
+       0x1d,
+ };
++static hda_nid_t stac9205_smux_nids[1] = {
++      0x21,
++};
++
+ #define STAC9205_NUM_DMICS    2
+ static hda_nid_t stac9205_dmic_nids[STAC9205_NUM_DMICS + 1] = {
+         0x17, 0x18, 0
+@@ -349,12 +442,18 @@ static hda_nid_t stac922x_pin_nids[10] =
+ static hda_nid_t stac92hd73xx_pin_nids[13] = {
+       0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+       0x0f, 0x10, 0x11, 0x12, 0x13,
+-      0x14, 0x1e, 0x22
++      0x14, 0x22, 0x23
+ };
+-static hda_nid_t stac92hd71bxx_pin_nids[10] = {
++static hda_nid_t stac92hd83xxx_pin_nids[14] = {
++      0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
++      0x0f, 0x10, 0x11, 0x12, 0x13,
++      0x1d, 0x1e, 0x1f, 0x20
++};
++static hda_nid_t stac92hd71bxx_pin_nids[11] = {
+       0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+       0x0f, 0x14, 0x18, 0x19, 0x1e,
++      0x1f,
+ };
+ static hda_nid_t stac927x_pin_nids[14] = {
+@@ -369,6 +468,34 @@ static hda_nid_t stac9205_pin_nids[12] =
+       0x21, 0x22,
+ };
++#define stac92xx_amp_volume_info snd_hda_mixer_amp_volume_info
++
++static int stac92xx_amp_volume_get(struct snd_kcontrol *kcontrol,
++                               struct snd_ctl_elem_value *ucontrol)
++{
++      struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
++      struct sigmatel_spec *spec = codec->spec;
++      hda_nid_t nid = spec->amp_nids[spec->cur_amux];
++
++      kcontrol->private_value ^= get_amp_nid(kcontrol);
++      kcontrol->private_value |= nid;
++
++      return snd_hda_mixer_amp_volume_get(kcontrol, ucontrol);
++}
++
++static int stac92xx_amp_volume_put(struct snd_kcontrol *kcontrol,
++                               struct snd_ctl_elem_value *ucontrol)
++{
++      struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
++      struct sigmatel_spec *spec = codec->spec;
++      hda_nid_t nid = spec->amp_nids[spec->cur_amux];
++
++      kcontrol->private_value ^= get_amp_nid(kcontrol);
++      kcontrol->private_value |= nid;
++
++      return snd_hda_mixer_amp_volume_put(kcontrol, ucontrol);
++}
++
+ static int stac92xx_dmux_enum_info(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_info *uinfo)
+ {
+@@ -399,6 +526,58 @@ static int stac92xx_dmux_enum_put(struct
+                       spec->dmux_nids[dmux_idx], &spec->cur_dmux[dmux_idx]);
+ }
++static int stac92xx_smux_enum_info(struct snd_kcontrol *kcontrol,
++                                 struct snd_ctl_elem_info *uinfo)
++{
++      struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
++      struct sigmatel_spec *spec = codec->spec;
++      return snd_hda_input_mux_info(spec->sinput_mux, uinfo);
++}
++
++static int stac92xx_smux_enum_get(struct snd_kcontrol *kcontrol,
++                                struct snd_ctl_elem_value *ucontrol)
++{
++      struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
++      struct sigmatel_spec *spec = codec->spec;
++      unsigned int smux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
++
++      ucontrol->value.enumerated.item[0] = spec->cur_smux[smux_idx];
++      return 0;
++}
++
++static int stac92xx_smux_enum_put(struct snd_kcontrol *kcontrol,
++                                struct snd_ctl_elem_value *ucontrol)
++{
++      struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
++      struct sigmatel_spec *spec = codec->spec;
++      struct hda_input_mux *smux = &spec->private_smux;
++      unsigned int smux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
++      int err, val;
++      hda_nid_t nid;
++
++      err = snd_hda_input_mux_put(codec, spec->sinput_mux, ucontrol,
++                      spec->smux_nids[smux_idx], &spec->cur_smux[smux_idx]);
++      if (err < 0)
++              return err;
++
++      if (spec->spdif_mute) {
++              if (smux_idx == 0)
++                      nid = spec->multiout.dig_out_nid;
++              else
++                      nid = codec->slave_dig_outs[smux_idx - 1];
++              if (spec->cur_smux[smux_idx] == smux->num_items - 1)
++                      val = AMP_OUT_MUTE;
++              if (smux_idx == 0)
++                      nid = spec->multiout.dig_out_nid;
++              else
++                      nid = codec->slave_dig_outs[smux_idx - 1];
++              /* un/mute SPDIF out */
++              snd_hda_codec_write_cache(codec, nid, 0,
++                      AC_VERB_SET_AMP_GAIN_MUTE, val);
++      }
++      return 0;
++}
++
+ static int stac92xx_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+ {
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+@@ -454,6 +633,41 @@ static int stac92xx_mono_mux_enum_put(st
+                                    spec->mono_nid, &spec->cur_mmux);
+ }
++static int stac92xx_amp_mux_enum_info(struct snd_kcontrol *kcontrol,
++      struct snd_ctl_elem_info *uinfo)
++{
++      struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
++      struct sigmatel_spec *spec = codec->spec;
++      return snd_hda_input_mux_info(spec->amp_mux, uinfo);
++}
++
++static int stac92xx_amp_mux_enum_get(struct snd_kcontrol *kcontrol,
++      struct snd_ctl_elem_value *ucontrol)
++{
++      struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
++      struct sigmatel_spec *spec = codec->spec;
++
++      ucontrol->value.enumerated.item[0] = spec->cur_amux;
++      return 0;
++}
++
++static int stac92xx_amp_mux_enum_put(struct snd_kcontrol *kcontrol,
++      struct snd_ctl_elem_value *ucontrol)
++{
++      struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
++      struct sigmatel_spec *spec = codec->spec;
++      struct snd_kcontrol *ctl =
++              snd_hda_find_mixer_ctl(codec, "Amp Capture Volume");
++      if (!ctl)
++              return -EINVAL;
++
++      snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE |
++              SNDRV_CTL_EVENT_MASK_INFO, &ctl->id);
++
++      return snd_hda_input_mux_put(codec, spec->amp_mux, ucontrol,
++                                   0, &spec->cur_amux);
++}
++
+ #define stac92xx_aloopback_info snd_ctl_boolean_mono_info
+ static int stac92xx_aloopback_get(struct snd_kcontrol *kcontrol,
+@@ -565,8 +779,8 @@ static struct hda_verb dell_m6_core_init
+       { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+       /* setup audio connections */
+       { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
+-      { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01},
+-      { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x02},
++      { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x02},
++      { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x01},
+       /* setup adcs to point to mixer */
+       { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
+       { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
+@@ -628,12 +842,25 @@ static struct hda_verb stac92hd73xx_10ch
+       {}
+ };
++static struct hda_verb stac92hd83xxx_core_init[] = {
++      /* start of config #1 */
++      { 0xe, AC_VERB_SET_CONNECT_SEL, 0x3},
++
++      /* start of config #2 */
++      { 0xa, AC_VERB_SET_CONNECT_SEL, 0x0},
++      { 0xb, AC_VERB_SET_CONNECT_SEL, 0x0},
++      { 0xd, AC_VERB_SET_CONNECT_SEL, 0x1},
++
++      /* power state controls amps */
++      { 0x01, AC_VERB_SET_EAPD, 1 << 2},
++      {}
++};
++
+ static struct hda_verb stac92hd71bxx_core_init[] = {
+       /* set master volume and direct control */
+       { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+       /* connect headphone jack to dac1 */
+       { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01},
+-      { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Speaker */
+       /* unmute right and left channels for nodes 0x0a, 0xd, 0x0f */
+       { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+@@ -641,13 +868,12 @@ static struct hda_verb stac92hd71bxx_cor
+       {}
+ };
+-#define HD_DISABLE_PORTF 3
++#define HD_DISABLE_PORTF 2
+ static struct hda_verb stac92hd71bxx_analog_core_init[] = {
+       /* start of config #1 */
+       /* connect port 0f to audio mixer */
+       { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2},
+-      { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Speaker */
+       /* unmute right and left channels for node 0x0f */
+       { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       /* start of config #2 */
+@@ -656,10 +882,6 @@ static struct hda_verb stac92hd71bxx_ana
+       { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+       /* connect headphone jack to dac1 */
+       { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01},
+-      /* connect port 0d to audio mixer */
+-      { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x2},
+-      /* unmute dac0 input in audio mixer */
+-      { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0x701f},
+       /* unmute right and left channels for nodes 0x0a, 0xd */
+       { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+@@ -691,12 +913,16 @@ static struct hda_verb d965_core_init[] 
+ static struct hda_verb stac927x_core_init[] = {
+       /* set master volume and direct control */      
+       { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
++      /* enable analog pc beep path */
++      { 0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5},
+       {}
+ };
+ static struct hda_verb stac9205_core_init[] = {
+       /* set master volume and direct control */      
+       { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
++      /* enable analog pc beep path */
++      { 0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5},
+       {}
+ };
+@@ -710,6 +936,31 @@ static struct hda_verb stac9205_core_ini
+               .put = stac92xx_mono_mux_enum_put, \
+       }
++#define STAC_AMP_MUX \
++      { \
++              .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
++              .name = "Amp Selector Capture Switch", \
++              .count = 1, \
++              .info = stac92xx_amp_mux_enum_info, \
++              .get = stac92xx_amp_mux_enum_get, \
++              .put = stac92xx_amp_mux_enum_put, \
++      }
++
++#define STAC_AMP_VOL(xname, nid, chs, idx, dir) \
++      { \
++              .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
++              .name = xname, \
++              .index = 0, \
++              .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
++                      SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
++                      SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \
++              .info = stac92xx_amp_volume_info, \
++              .get = stac92xx_amp_volume_get, \
++              .put = stac92xx_amp_volume_put, \
++              .tlv = { .c = snd_hda_mixer_amp_tlv }, \
++              .private_value = HDA_COMPOSE_AMP_VAL(nid, chs, idx, dir) \
++      }
++
+ #define STAC_INPUT_SOURCE(cnt) \
+       { \
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+@@ -737,33 +988,36 @@ static struct snd_kcontrol_new stac9200_
+       STAC_INPUT_SOURCE(1),
+       HDA_CODEC_VOLUME("Capture Volume", 0x0a, 0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Capture Switch", 0x0a, 0, HDA_OUTPUT),
+-      HDA_CODEC_VOLUME("Capture Mux Volume", 0x0c, 0, HDA_OUTPUT),
+       { } /* end */
+ };
++#define DELL_M6_MIXER 6
+ static struct snd_kcontrol_new stac92hd73xx_6ch_mixer[] = {
+-      STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3),
+-
+-      HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
+-      HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT),
+-
+-      HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT),
+-      HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT),
+-
++      /* start of config #1 */
+       HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT),
+       HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT),
+-      HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT),
+-      HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT),
+-
+       HDA_CODEC_VOLUME("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT),
+       HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT),
++      HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT),
++      HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT),
++
++      /* start of config #2 */
++      HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT),
++      HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT),
++
+       HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT),
+       HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT),
+-      HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT),
+-      HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT),
++      STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3),
++
++      HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
++      HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT),
++
++      HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT),
++      HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT),
++
+       { } /* end */
+ };
+@@ -819,22 +1073,59 @@ static struct snd_kcontrol_new stac92hd7
+       { } /* end */
+ };
++
++static struct snd_kcontrol_new stac92hd83xxx_mixer[] = {
++      HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x17, 0x0, HDA_OUTPUT),
++      HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x17, 0x0, HDA_OUTPUT),
++
++      HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x18, 0x0, HDA_OUTPUT),
++      HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x18, 0x0, HDA_OUTPUT),
++
++      HDA_CODEC_VOLUME("DAC0 Capture Volume", 0x1b, 0, HDA_INPUT),
++      HDA_CODEC_MUTE("DAC0 Capture Switch", 0x1b, 0, HDA_INPUT),
++
++      HDA_CODEC_VOLUME("DAC1 Capture Volume", 0x1b, 0x1, HDA_INPUT),
++      HDA_CODEC_MUTE("DAC1 Capture Switch", 0x1b, 0x1, HDA_INPUT),
++
++      HDA_CODEC_VOLUME("Front Mic Capture Volume", 0x1b, 0x2, HDA_INPUT),
++      HDA_CODEC_MUTE("Front Mic Capture Switch", 0x1b, 0x2, HDA_INPUT),
++
++      HDA_CODEC_VOLUME("Line In Capture Volume", 0x1b, 0x3, HDA_INPUT),
++      HDA_CODEC_MUTE("Line In Capture Switch", 0x1b, 0x3, HDA_INPUT),
++
++      /*
++      HDA_CODEC_VOLUME("Mic Capture Volume", 0x1b, 0x4, HDA_INPUT),
++      HDA_CODEC_MUTE("Mic Capture Switch", 0x1b 0x4, HDA_INPUT),
++      */
++      { } /* end */
++};
++
+ static struct snd_kcontrol_new stac92hd71bxx_analog_mixer[] = {
+       STAC_INPUT_SOURCE(2),
++      STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2),
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT),
+-      HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x0, 0x1a, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1d, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1d, 0x0, HDA_OUTPUT),
+-      HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x1, 0x1b, 0x0, HDA_OUTPUT),
+-
++      /* analog pc-beep replaced with digital beep support */
++      /*
+       HDA_CODEC_VOLUME("PC Beep Volume", 0x17, 0x2, HDA_INPUT),
+       HDA_CODEC_MUTE("PC Beep Switch", 0x17, 0x2, HDA_INPUT),
++      */
++
++      HDA_CODEC_MUTE("Import0 Mux Capture Switch", 0x17, 0x0, HDA_INPUT),
++      HDA_CODEC_VOLUME("Import0 Mux Capture Volume", 0x17, 0x0, HDA_INPUT),
++
++      HDA_CODEC_MUTE("Import1 Mux Capture Switch", 0x17, 0x1, HDA_INPUT),
++      HDA_CODEC_VOLUME("Import1 Mux Capture Volume", 0x17, 0x1, HDA_INPUT),
+-      HDA_CODEC_MUTE("Analog Loopback 1", 0x17, 0x3, HDA_INPUT),
+-      HDA_CODEC_MUTE("Analog Loopback 2", 0x17, 0x4, HDA_INPUT),
++      HDA_CODEC_MUTE("DAC0 Capture Switch", 0x17, 0x3, HDA_INPUT),
++      HDA_CODEC_VOLUME("DAC0 Capture Volume", 0x17, 0x3, HDA_INPUT),
++
++      HDA_CODEC_MUTE("DAC1 Capture Switch", 0x17, 0x4, HDA_INPUT),
++      HDA_CODEC_VOLUME("DAC1 Capture Volume", 0x17, 0x4, HDA_INPUT),
+       { } /* end */
+ };
+@@ -844,11 +1135,9 @@ static struct snd_kcontrol_new stac92hd7
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT),
+-      HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x0, 0x1a, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1d, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1d, 0x0, HDA_OUTPUT),
+-      HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x1, 0x1b, 0x0, HDA_OUTPUT),
+       { } /* end */
+ };
+@@ -856,7 +1145,6 @@ static struct snd_kcontrol_new stac925x_
+       STAC_INPUT_SOURCE(1),
+       HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Capture Switch", 0x14, 0, HDA_OUTPUT),
+-      HDA_CODEC_VOLUME("Capture Mux Volume", 0x0f, 0, HDA_OUTPUT),
+       { } /* end */
+ };
+@@ -866,12 +1154,9 @@ static struct snd_kcontrol_new stac9205_
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1b, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1d, 0x0, HDA_OUTPUT),
+-      HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x0, 0x19, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1c, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1e, 0x0, HDA_OUTPUT),
+-      HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x1, 0x1A, 0x0, HDA_OUTPUT),
+-
+       { } /* end */
+ };
+@@ -880,11 +1165,9 @@ static struct snd_kcontrol_new stac922x_
+       STAC_INPUT_SOURCE(2),
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x17, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x17, 0x0, HDA_INPUT),
+-      HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x0, 0x12, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x18, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x18, 0x0, HDA_INPUT),
+-      HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x1, 0x13, 0x0, HDA_OUTPUT),
+       { } /* end */
+ };
+@@ -895,15 +1178,12 @@ static struct snd_kcontrol_new stac927x_
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x18, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1b, 0x0, HDA_OUTPUT),
+-      HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x0, 0x15, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x19, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1c, 0x0, HDA_OUTPUT),
+-      HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x1, 0x16, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 0x2, 0x1A, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 0x2, 0x1d, 0x0, HDA_OUTPUT),
+-      HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x2, 0x17, 0x0, HDA_OUTPUT),
+       { } /* end */
+ };
+@@ -916,6 +1196,15 @@ static struct snd_kcontrol_new stac_dmux
+       .put = stac92xx_dmux_enum_put,
+ };
++static struct snd_kcontrol_new stac_smux_mixer = {
++      .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++      .name = "IEC958 Playback Source",
++      /* count set later */
++      .info = stac92xx_smux_enum_info,
++      .get = stac92xx_smux_enum_get,
++      .put = stac92xx_smux_enum_put,
++};
++
+ static const char *slave_vols[] = {
+       "Front Playback Volume",
+       "Surround Playback Volume",
+@@ -967,6 +1256,22 @@ static int stac92xx_build_controls(struc
+               if (err < 0)
+                       return err;
+       }
++      if (spec->num_smuxes > 0) {
++              int wcaps = get_wcaps(codec, spec->multiout.dig_out_nid);
++              struct hda_input_mux *smux = &spec->private_smux;
++              /* check for mute support on SPDIF out */
++              if (wcaps & AC_WCAP_OUT_AMP) {
++                      smux->items[smux->num_items].label = "Off";
++                      smux->items[smux->num_items].index = 0;
++                      smux->num_items++;
++                      spec->spdif_mute = 1;
++              }
++              stac_smux_mixer.count = spec->num_smuxes;
++              err = snd_ctl_add(codec->bus->card,
++                                snd_ctl_new1(&stac_smux_mixer, codec));
++              if (err < 0)
++                      return err;
++      }
+       if (spec->multiout.dig_out_nid) {
+               err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
+@@ -978,7 +1283,7 @@ static int stac92xx_build_controls(struc
+                       return err;
+               spec->multiout.share_spdif = 1;
+       }
+-      if (spec->dig_in_nid) {
++      if (spec->dig_in_nid && !(spec->gpio_dir & 0x01)) {
+               err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
+               if (err < 0)
+                       return err;
+@@ -1330,40 +1635,65 @@ static struct snd_pci_quirk stac92hd73xx
+       {} /* terminator */
+ };
+-static unsigned int ref92hd71bxx_pin_configs[10] = {
++static unsigned int ref92hd83xxx_pin_configs[14] = {
++      0x02214030, 0x02211010, 0x02a19020, 0x02170130,
++      0x01014050, 0x01819040, 0x01014020, 0x90a3014e,
++      0x40f000f0, 0x40f000f0, 0x40f000f0, 0x40f000f0,
++      0x01451160, 0x98560170,
++};
++
++static unsigned int *stac92hd83xxx_brd_tbl[STAC_92HD83XXX_MODELS] = {
++      [STAC_92HD83XXX_REF] = ref92hd83xxx_pin_configs,
++};
++
++static const char *stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = {
++      [STAC_92HD83XXX_REF] = "ref",
++};
++
++static struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = {
++      /* SigmaTel reference board */
++      SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
++                    "DFI LanParty", STAC_92HD71BXX_REF),
++};
++
++static unsigned int ref92hd71bxx_pin_configs[11] = {
+       0x02214030, 0x02a19040, 0x01a19020, 0x01014010,
+-      0x0181302e, 0x01114010, 0x01019020, 0x90a000f0,
+-      0x90a000f0, 0x01452050,
++      0x0181302e, 0x01014010, 0x01019020, 0x90a000f0,
++      0x90a000f0, 0x01452050, 0x01452050,
+ };
+-static unsigned int dell_m4_1_pin_configs[10] = {
++static unsigned int dell_m4_1_pin_configs[11] = {
+       0x0421101f, 0x04a11221, 0x40f000f0, 0x90170110,
+       0x23a1902e, 0x23014250, 0x40f000f0, 0x90a000f0,
+-      0x40f000f0, 0x4f0000f0,
++      0x40f000f0, 0x4f0000f0, 0x4f0000f0,
+ };
+-static unsigned int dell_m4_2_pin_configs[10] = {
++static unsigned int dell_m4_2_pin_configs[11] = {
+       0x0421101f, 0x04a11221, 0x90a70330, 0x90170110,
+       0x23a1902e, 0x23014250, 0x40f000f0, 0x40f000f0,
+-      0x40f000f0, 0x044413b0,
++      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_HP_M4]            = NULL,
+ };
+ 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_HP_M4] = "hp-m4",
+ };
+ 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, 0x361a,
++                              "unknown HP", STAC_HP_M4),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233,
+                               "unknown Dell", STAC_DELL_M4_1),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0234,
+@@ -1906,6 +2236,8 @@ static int stac92xx_playback_pcm_open(st
+                                     struct snd_pcm_substream *substream)
+ {
+       struct sigmatel_spec *spec = codec->spec;
++      if (spec->stream_delay)
++              msleep(spec->stream_delay);
+       return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
+                                            hinfo);
+ }
+@@ -1958,7 +2290,6 @@ static int stac92xx_dig_playback_pcm_pre
+                                            stream_tag, format, substream);
+ }
+-
+ /*
+  * Analog capture callbacks
+  */
+@@ -1969,9 +2300,14 @@ static int stac92xx_capture_pcm_prepare(
+                                       struct snd_pcm_substream *substream)
+ {
+       struct sigmatel_spec *spec = codec->spec;
++      hda_nid_t nid = spec->adc_nids[substream->number];
+-      snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
+-                                   stream_tag, 0, format);
++      if (spec->powerdown_adcs) {
++              msleep(40);
++              snd_hda_codec_write_cache(codec, nid, 0,
++                      AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
++      }
++      snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
+       return 0;
+ }
+@@ -1980,8 +2316,12 @@ static int stac92xx_capture_pcm_cleanup(
+                                       struct snd_pcm_substream *substream)
+ {
+       struct sigmatel_spec *spec = codec->spec;
++      hda_nid_t nid = spec->adc_nids[substream->number];
+-      snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
++      snd_hda_codec_cleanup_stream(codec, nid);
++      if (spec->powerdown_adcs)
++              snd_hda_codec_write_cache(codec, nid, 0,
++                      AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+       return 0;
+ }
+@@ -2234,6 +2574,8 @@ enum {
+       STAC_CTL_WIDGET_VOL,
+       STAC_CTL_WIDGET_MUTE,
+       STAC_CTL_WIDGET_MONO_MUX,
++      STAC_CTL_WIDGET_AMP_MUX,
++      STAC_CTL_WIDGET_AMP_VOL,
+       STAC_CTL_WIDGET_HP_SWITCH,
+       STAC_CTL_WIDGET_IO_SWITCH,
+       STAC_CTL_WIDGET_CLFE_SWITCH
+@@ -2243,13 +2585,16 @@ static struct snd_kcontrol_new stac92xx_
+       HDA_CODEC_VOLUME(NULL, 0, 0, 0),
+       HDA_CODEC_MUTE(NULL, 0, 0, 0),
+       STAC_MONO_MUX,
++      STAC_AMP_MUX,
++      STAC_AMP_VOL(NULL, 0, 0, 0, 0),
+       STAC_CODEC_HP_SWITCH(NULL),
+       STAC_CODEC_IO_SWITCH(NULL, 0),
+       STAC_CODEC_CLFE_SWITCH(NULL, 0),
+ };
+ /* add dynamic controls */
+-static int stac92xx_add_control(struct sigmatel_spec *spec, int type, const char *name, unsigned long val)
++static int stac92xx_add_control_idx(struct sigmatel_spec *spec, int type,
++              int idx, const char *name, unsigned long val)
+ {
+       struct snd_kcontrol_new *knew;
+@@ -2269,6 +2614,7 @@ static int stac92xx_add_control(struct s
+       knew = &spec->kctl_alloc[spec->num_kctl_used];
+       *knew = stac92xx_control_templates[type];
++      knew->index = idx;
+       knew->name = kstrdup(name, GFP_KERNEL);
+       if (! knew->name)
+               return -ENOMEM;
+@@ -2277,6 +2623,14 @@ static int stac92xx_add_control(struct s
+       return 0;
+ }
++
++/* add dynamic controls */
++static int stac92xx_add_control(struct sigmatel_spec *spec, int type,
++              const char *name, unsigned long val)
++{
++      return stac92xx_add_control_idx(spec, type, 0, name, val);
++}
++
+ /* flag inputs as additional dynamic lineouts */
+ static int stac92xx_add_dyn_out_pins(struct hda_codec *codec, struct auto_pin_cfg *cfg)
+ {
+@@ -2468,7 +2822,7 @@ static int stac92xx_auto_create_multi_ou
+       static const char *chname[4] = {
+               "Front", "Surround", NULL /*CLFE*/, "Side"
+       };
+-      hda_nid_t nid;
++      hda_nid_t nid = 0;
+       int i, err;
+       struct sigmatel_spec *spec = codec->spec;
+@@ -2508,6 +2862,10 @@ static int stac92xx_auto_create_multi_ou
+               }
+       }
++      if ((spec->multiout.num_dacs - cfg->line_outs) > 0 &&
++                      cfg->hp_outs && !spec->multiout.hp_nid)
++              spec->multiout.hp_nid = nid;
++
+       if (cfg->hp_outs > 1 && cfg->line_out_type == AUTO_PIN_LINE_OUT) {
+               err = stac92xx_add_control(spec,
+                       STAC_CTL_WIDGET_HP_SWITCH,
+@@ -2620,8 +2978,8 @@ static int stac92xx_auto_create_hp_ctls(
+ }
+ /* labels for mono mux outputs */
+-static const char *stac92xx_mono_labels[3] = {
+-      "DAC0", "DAC1", "Mixer"
++static const char *stac92xx_mono_labels[4] = {
++      "DAC0", "DAC1", "Mixer", "DAC2"
+ };
+ /* create mono mux for mono out on capable codecs */
+@@ -2650,6 +3008,116 @@ static int stac92xx_auto_create_mono_out
+                               "Mono Mux", spec->mono_nid);
+ }
++/* labels for amp mux outputs */
++static const char *stac92xx_amp_labels[3] = {
++      "Front Microphone", "Microphone", "Line In",
++};
++
++/* create amp out controls mux on capable codecs */
++static int stac92xx_auto_create_amp_output_ctls(struct hda_codec *codec)
++{
++      struct sigmatel_spec *spec = codec->spec;
++      struct hda_input_mux *amp_mux = &spec->private_amp_mux;
++      int i, err;
++
++      for (i = 0; i < spec->num_amps; i++) {
++              amp_mux->items[amp_mux->num_items].label =
++                                      stac92xx_amp_labels[i];
++              amp_mux->items[amp_mux->num_items].index = i;
++              amp_mux->num_items++;
++      }
++
++      if (spec->num_amps > 1) {
++              err = stac92xx_add_control(spec, STAC_CTL_WIDGET_AMP_MUX,
++                      "Amp Selector Capture Switch", 0);
++              if (err < 0)
++                      return err;
++      }
++      return stac92xx_add_control(spec, STAC_CTL_WIDGET_AMP_VOL,
++              "Amp Capture Volume",
++              HDA_COMPOSE_AMP_VAL(spec->amp_nids[0], 3, 0, HDA_INPUT));
++}
++
++
++/* create PC beep volume controls */
++static int stac92xx_auto_create_beep_ctls(struct hda_codec *codec,
++                                              hda_nid_t nid)
++{
++      struct sigmatel_spec *spec = codec->spec;
++      u32 caps = query_amp_caps(codec, nid, HDA_OUTPUT);
++      int err;
++
++      /* check for mute support for the the amp */
++      if ((caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT) {
++              err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE,
++                      "PC Beep Playback Switch",
++                      HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT));
++                      if (err < 0)
++                              return err;
++      }
++
++      /* check to see if there is volume support for the amp */
++      if ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) {
++              err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL,
++                      "PC Beep Playback Volume",
++                      HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT));
++                      if (err < 0)
++                              return err;
++      }
++      return 0;
++}
++
++static int stac92xx_auto_create_mux_input_ctls(struct hda_codec *codec)
++{
++      struct sigmatel_spec *spec = codec->spec;
++      int wcaps, nid, i, err = 0;
++
++      for (i = 0; i < spec->num_muxes; i++) {
++              nid = spec->mux_nids[i];
++              wcaps = get_wcaps(codec, nid);
++
++              if (wcaps & AC_WCAP_OUT_AMP) {
++                      err = stac92xx_add_control_idx(spec,
++                              STAC_CTL_WIDGET_VOL, i, "Mux Capture Volume",
++                              HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
++                      if (err < 0)
++                              return err;
++              }
++      }
++      return 0;
++};
++
++static const char *stac92xx_spdif_labels[3] = {
++      "Digital Playback", "Analog Mux 1", "Analog Mux 2",
++};
++
++static int stac92xx_auto_create_spdif_mux_ctls(struct hda_codec *codec)
++{
++      struct sigmatel_spec *spec = codec->spec;
++      struct hda_input_mux *spdif_mux = &spec->private_smux;
++      const char **labels = spec->spdif_labels;
++      int i, num_cons;
++      hda_nid_t con_lst[HDA_MAX_NUM_INPUTS];
++
++      num_cons = snd_hda_get_connections(codec,
++                              spec->smux_nids[0],
++                              con_lst,
++                              HDA_MAX_NUM_INPUTS);
++      if (!num_cons)
++              return -EINVAL;
++
++      if (!labels)
++              labels = stac92xx_spdif_labels;
++
++      for (i = 0; i < num_cons; i++) {
++              spdif_mux->items[spdif_mux->num_items].label = labels[i];
++              spdif_mux->items[spdif_mux->num_items].index = i;
++              spdif_mux->num_items++;
++      }
++
++      return 0;
++}
++
+ /* labels for dmic mux inputs */
+ static const char *stac92xx_dmic_labels[5] = {
+       "Analog Inputs", "Digital Mic 1", "Digital Mic 2",
+@@ -2697,16 +3165,19 @@ static int stac92xx_auto_create_dmic_inp
+                       }
+               continue;
+ found:
+-              wcaps = get_wcaps(codec, nid);
++              wcaps = get_wcaps(codec, nid) &
++                      (AC_WCAP_OUT_AMP | AC_WCAP_IN_AMP);
+-              if (wcaps & AC_WCAP_OUT_AMP) {
++              if (wcaps) {
+                       sprintf(name, "%s Capture Volume",
+                               stac92xx_dmic_labels[dimux->num_items]);
+                       err = stac92xx_add_control(spec,
+                               STAC_CTL_WIDGET_VOL,
+                               name,
+-                              HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
++                              HDA_COMPOSE_AMP_VAL(nid, 3, 0,
++                              (wcaps & AC_WCAP_OUT_AMP) ?
++                              HDA_OUTPUT : HDA_INPUT));
+                       if (err < 0)
+                               return err;
+               }
+@@ -2830,8 +3301,8 @@ static int stac92xx_parse_auto_config(st
+               hp_speaker_swap = 1;
+       }
+       if (spec->autocfg.mono_out_pin) {
+-              int dir = (get_wcaps(codec, spec->autocfg.mono_out_pin)
+-                              & AC_WCAP_OUT_AMP) ? HDA_OUTPUT : HDA_INPUT;
++              int dir = get_wcaps(codec, spec->autocfg.mono_out_pin) &
++                      (AC_WCAP_OUT_AMP | AC_WCAP_IN_AMP);
+               u32 caps = query_amp_caps(codec,
+                               spec->autocfg.mono_out_pin, dir);
+               hda_nid_t conn_list[1];
+@@ -2853,21 +3324,26 @@ static int stac92xx_parse_auto_config(st
+                                               !(wcaps & AC_WCAP_LR_SWAP))
+                                       spec->mono_nid = conn_list[0];
+               }
+-              /* all mono outs have a least a mute/unmute switch */
+-              err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE,
+-                      "Mono Playback Switch",
+-                      HDA_COMPOSE_AMP_VAL(spec->autocfg.mono_out_pin,
+-                                      1, 0, dir));
+-              if (err < 0)
+-                      return err;
+-              /* check to see if there is volume support for the amp */
+-              if ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) {
+-                      err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL,
+-                              "Mono Playback Volume",
+-                              HDA_COMPOSE_AMP_VAL(spec->autocfg.mono_out_pin,
+-                                      1, 0, dir));
++              if (dir) {
++                      hda_nid_t nid = spec->autocfg.mono_out_pin;
++
++                      /* most mono outs have a least a mute/unmute switch */
++                      dir = (dir & AC_WCAP_OUT_AMP) ? HDA_OUTPUT : HDA_INPUT;
++                      err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE,
++                              "Mono Playback Switch",
++                              HDA_COMPOSE_AMP_VAL(nid, 1, 0, dir));
+                       if (err < 0)
+                               return err;
++                      /* check for volume support for the amp */
++                      if ((caps & AC_AMPCAP_NUM_STEPS)
++                                      >> AC_AMPCAP_NUM_STEPS_SHIFT) {
++                              err = stac92xx_add_control(spec,
++                                      STAC_CTL_WIDGET_VOL,
++                                      "Mono Playback Volume",
++                              HDA_COMPOSE_AMP_VAL(nid, 1, 0, dir));
++                              if (err < 0)
++                                      return err;
++                      }
+               }
+               stac92xx_auto_set_pinctl(codec, spec->autocfg.mono_out_pin,
+@@ -2885,6 +3361,28 @@ static int stac92xx_parse_auto_config(st
+       if (err < 0)
+               return err;
++      /* setup analog beep controls */
++      if (spec->anabeep_nid > 0) {
++              err = stac92xx_auto_create_beep_ctls(codec,
++                      spec->anabeep_nid);
++              if (err < 0)
++                      return err;
++      }
++
++      /* setup digital beep controls and input device */
++#ifdef CONFIG_SND_HDA_INPUT_BEEP
++      if (spec->digbeep_nid > 0) {
++              hda_nid_t nid = spec->digbeep_nid;
++
++              err = stac92xx_auto_create_beep_ctls(codec, nid);
++              if (err < 0)
++                      return err;
++              err = snd_hda_attach_beep_device(codec, nid);
++              if (err < 0)
++                      return err;
++      }
++#endif
++
+       if (hp_speaker_swap == 1) {
+               /* Restore the hp_outs and line_outs */
+               memcpy(spec->autocfg.hp_pins, spec->autocfg.line_out_pins,
+@@ -2913,11 +3411,25 @@ static int stac92xx_parse_auto_config(st
+               if (err < 0)
+                       return err;
+       }
+-
+-      if (spec->num_dmics > 0)
++      if (spec->num_amps > 0) {
++              err = stac92xx_auto_create_amp_output_ctls(codec);
++              if (err < 0)
++                      return err;
++      }
++      if (spec->num_dmics > 0 && !spec->dinput_mux)
+               if ((err = stac92xx_auto_create_dmic_input_ctls(codec,
+                                               &spec->autocfg)) < 0)
+                       return err;
++      if (spec->num_muxes > 0) {
++              err = stac92xx_auto_create_mux_input_ctls(codec);
++              if (err < 0)
++                      return err;
++      }
++      if (spec->num_smuxes > 0) {
++              err = stac92xx_auto_create_spdif_mux_ctls(codec);
++              if (err < 0)
++                      return err;
++      }
+       spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+       if (spec->multiout.max_channels > 2)
+@@ -2925,17 +3437,17 @@ static int stac92xx_parse_auto_config(st
+       if (spec->autocfg.dig_out_pin)
+               spec->multiout.dig_out_nid = dig_out;
+-      if (spec->autocfg.dig_in_pin)
++      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;
+       spec->input_mux = &spec->private_imux;
+-      if (!spec->dinput_mux)
+-              spec->dinput_mux = &spec->private_dimux;
++      spec->dinput_mux = &spec->private_dimux;
++      spec->sinput_mux = &spec->private_smux;
+       spec->mono_mux = &spec->private_mono_mux;
+-
++      spec->amp_mux = &spec->private_amp_mux;
+       return 1;
+ }
+@@ -3115,6 +3627,12 @@ static int stac92xx_init(struct hda_code
+       snd_hda_sequence_write(codec, spec->init);
++      /* power down adcs initially */
++      if (spec->powerdown_adcs)
++              for (i = 0; i < spec->num_adcs; i++)
++                      snd_hda_codec_write_cache(codec,
++                              spec->adc_nids[i], 0,
++                              AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+       /* set up pins */
+       if (spec->hp_detect) {
+               /* Enable unsolicited responses on the HP widget */
+@@ -3136,7 +3654,12 @@ static int stac92xx_init(struct hda_code
+       for (i = 0; i < AUTO_PIN_LAST; i++) {
+               hda_nid_t nid = cfg->input_pins[i];
+               if (nid) {
+-                      unsigned int pinctl = AC_PINCTL_IN_EN;
++                      unsigned int pinctl = snd_hda_codec_read(codec, nid,
++                              0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
++                      /* if PINCTL already set then skip */
++                      if (pinctl & AC_PINCAP_IN)
++                              continue;
++                      pinctl = AC_PINCTL_IN_EN;
+                       if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC)
+                               pinctl |= stac92xx_get_vref(codec, nid);
+                       stac92xx_auto_set_pinctl(codec, nid, pinctl);
+@@ -3199,6 +3722,7 @@ static void stac92xx_free(struct hda_cod
+               kfree(spec->bios_pin_configs);
+       kfree(spec);
++      snd_hda_detach_beep_device(codec);
+ }
+ static void stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid,
+@@ -3320,7 +3844,12 @@ static void stac92xx_pin_sense(struct hd
+       val = snd_hda_codec_read(codec, codec->afg, 0, 0x0fec, 0x0)
+                                                       & 0x000000ff;
+       presence = get_hp_pin_presence(codec, nid);
+-      idx = 1 << idx;
++
++      /* several codecs have two power down bits */
++      if (spec->pwr_mapping)
++              idx = spec->pwr_mapping[idx];
++      else
++              idx = 1 << idx;
+       if (presence)
+               val &= ~idx;
+@@ -3336,13 +3865,22 @@ static void stac92xx_unsol_event(struct 
+       struct sigmatel_spec *spec = codec->spec;
+       int idx = res >> 26 & 0x0f;
+-      switch ((res >> 26) & 0x30) {
++      switch ((res >> 26) & 0x70) {
+       case STAC_HP_EVENT:
+               stac92xx_hp_detect(codec, res);
+               /* fallthru */
+       case STAC_PWR_EVENT:
+               if (spec->num_pwrs > 0)
+                       stac92xx_pin_sense(codec, idx);
++              break;
++      case STAC_VREF_EVENT: {
++              int 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)));
++              break;
++              }
+       }
+ }
+@@ -3519,9 +4057,9 @@ static struct hda_input_mux stac92hd73xx
+       .num_items = 4,
+       .items = {
+               { "Analog Inputs", 0x0b },
+-              { "CD", 0x08 },
+               { "Digital Mic 1", 0x09 },
+               { "Digital Mic 2", 0x0a },
++              { "CD", 0x08 },
+       }
+ };
+@@ -3536,6 +4074,7 @@ static int patch_stac92hd73xx(struct hda
+               return -ENOMEM;
+       codec->spec = spec;
++      codec->slave_dig_outs = stac92hd73xx_slave_dig_outs;
+       spec->num_pins = ARRAY_SIZE(stac92hd73xx_pin_nids);
+       spec->pin_nids = stac92hd73xx_pin_nids;
+       spec->board_config = snd_hda_check_board_config(codec,
+@@ -3568,17 +4107,14 @@ 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;
+       };
+@@ -3587,18 +4123,20 @@ again:
+       spec->aloopback_mask = 0x01;
+       spec->aloopback_shift = 8;
++      spec->digbeep_nid = 0x1c;
+       spec->mux_nids = stac92hd73xx_mux_nids;
+       spec->adc_nids = stac92hd73xx_adc_nids;
+       spec->dmic_nids = stac92hd73xx_dmic_nids;
+       spec->dmux_nids = stac92hd73xx_dmux_nids;
++      spec->smux_nids = stac92hd73xx_smux_nids;
++      spec->amp_nids = stac92hd73xx_amp_nids;
++      spec->num_amps = ARRAY_SIZE(stac92hd73xx_amp_nids);
+       spec->num_muxes = ARRAY_SIZE(stac92hd73xx_mux_nids);
+       spec->num_adcs = ARRAY_SIZE(stac92hd73xx_adc_nids);
+       spec->num_dmuxes = ARRAY_SIZE(stac92hd73xx_dmux_nids);
+-      spec->dinput_mux = &stac92hd73xx_dmux;
+-      /* GPIO0 High = Enable EAPD */
+-      spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1;
+-      spec->gpio_data = 0x01;
++      memcpy(&spec->private_dimux, &stac92hd73xx_dmux,
++                      sizeof(stac92hd73xx_dmux));
+       switch (spec->board_config) {
+       case STAC_DELL_EQ:
+@@ -3607,11 +4145,16 @@ again:
+       case STAC_DELL_M6:
+               if (!spec->init)
+                       spec->init = dell_m6_core_init;
++              spec->num_smuxes = 0;
++              spec->mixer = &stac92hd73xx_6ch_mixer[DELL_M6_MIXER];
++              spec->amp_nids = &stac92hd73xx_amp_nids[DELL_M6_AMP];
++              spec->num_amps = 1;
+               switch (codec->subsystem_id) {
+               case 0x1028025e: /* Analog Mics */
+               case 0x1028025f:
+                       stac92xx_set_config_reg(codec, 0x0b, 0x90A70170);
+                       spec->num_dmics = 0;
++                      spec->private_dimux.num_items = 1;
+                       break;
+               case 0x10280271: /* Digital Mics */
+               case 0x10280272:
+@@ -3619,23 +4162,32 @@ again:
+               case 0x10280255:
+                       stac92xx_set_config_reg(codec, 0x13, 0x90A60160);
+                       spec->num_dmics = 1;
++                      spec->private_dimux.num_items = 2;
+                       break;
+               case 0x10280256: /* Both */
+               case 0x10280057:
+                       stac92xx_set_config_reg(codec, 0x0b, 0x90A70170);
+                       stac92xx_set_config_reg(codec, 0x13, 0x90A60160);
+                       spec->num_dmics = 1;
++                      spec->private_dimux.num_items = 2;
+                       break;
+               }
+               break;
+       default:
+               spec->num_dmics = STAC92HD73XX_NUM_DMICS;
++              spec->num_smuxes = ARRAY_SIZE(stac92hd73xx_smux_nids);
+       }
++      if (spec->board_config > STAC_92HD73XX_REF) {
++              /* GPIO0 High = Enable EAPD */
++              spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1;
++              spec->gpio_data = 0x01;
++      }
++      spec->dinput_mux = &spec->private_dimux;
+       spec->num_pwrs = ARRAY_SIZE(stac92hd73xx_pwr_nids);
+       spec->pwr_nids = stac92hd73xx_pwr_nids;
+-      err = stac92xx_parse_auto_config(codec, 0x22, 0x24);
++      err = stac92xx_parse_auto_config(codec, 0x25, 0x27);
+       if (!err) {
+               if (spec->board_config < 0) {
+@@ -3657,6 +4209,146 @@ again:
+       return 0;
+ }
++static struct hda_input_mux stac92hd83xxx_dmux = {
++      .num_items = 3,
++      .items = {
++              { "Analog Inputs", 0x03 },
++              { "Digital Mic 1", 0x04 },
++              { "Digital Mic 2", 0x05 },
++      }
++};
++
++static int patch_stac92hd83xxx(struct hda_codec *codec)
++{
++      struct sigmatel_spec *spec;
++      int err;
++
++      spec  = kzalloc(sizeof(*spec), GFP_KERNEL);
++      if (spec == NULL)
++              return -ENOMEM;
++
++      codec->spec = spec;
++      codec->slave_dig_outs = stac92hd83xxx_slave_dig_outs;
++      spec->mono_nid = 0x19;
++      spec->digbeep_nid = 0x21;
++      spec->dmic_nids = stac92hd83xxx_dmic_nids;
++      spec->dmux_nids = stac92hd83xxx_dmux_nids;
++      spec->adc_nids = stac92hd83xxx_adc_nids;
++      spec->pwr_nids = stac92hd83xxx_pwr_nids;
++      spec->pwr_mapping = stac92hd83xxx_pwr_mapping;
++      spec->num_pwrs = ARRAY_SIZE(stac92hd83xxx_pwr_nids);
++      spec->multiout.dac_nids = stac92hd83xxx_dac_nids;
++
++      spec->init = stac92hd83xxx_core_init;
++      switch (codec->vendor_id) {
++      case 0x111d7605:
++              spec->multiout.num_dacs = STAC92HD81_DAC_COUNT;
++              break;
++      default:
++              spec->num_pwrs--;
++              spec->init++; /* switch to config #2 */
++              spec->multiout.num_dacs = STAC92HD83_DAC_COUNT;
++      }
++
++      spec->mixer = stac92hd83xxx_mixer;
++      spec->num_pins = ARRAY_SIZE(stac92hd83xxx_pin_nids);
++      spec->num_dmuxes = ARRAY_SIZE(stac92hd83xxx_dmux_nids);
++      spec->num_adcs = ARRAY_SIZE(stac92hd83xxx_adc_nids);
++      spec->num_dmics = STAC92HD83XXX_NUM_DMICS;
++      spec->dinput_mux = &stac92hd83xxx_dmux;
++      spec->pin_nids = stac92hd83xxx_pin_nids;
++      spec->board_config = snd_hda_check_board_config(codec,
++                                                      STAC_92HD83XXX_MODELS,
++                                                      stac92hd83xxx_models,
++                                                      stac92hd83xxx_cfg_tbl);
++again:
++      if (spec->board_config < 0) {
++              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);
++      }
++
++      err = stac92xx_parse_auto_config(codec, 0x1d, 0);
++      if (!err) {
++              if (spec->board_config < 0) {
++                      printk(KERN_WARNING "hda_codec: No auto-config is "
++                             "available, default to model=ref\n");
++                      spec->board_config = STAC_92HD83XXX_REF;
++                      goto again;
++              }
++              err = -EINVAL;
++      }
++
++      if (err < 0) {
++              stac92xx_free(codec);
++              return err;
++      }
++
++      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);
++}
++
++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,
++      .items = {
++              { "Analog Inputs", 0x00 },
++              { "Mixer", 0x01 },
++              { "Digital Mic 1", 0x02 },
++              { "Digital Mic 2", 0x03 },
++      }
++};
++
+ static int patch_stac92hd71bxx(struct hda_codec *codec)
+ {
+       struct sigmatel_spec *spec;
+@@ -3667,9 +4359,12 @@ static int patch_stac92hd71bxx(struct hd
+               return -ENOMEM;
+       codec->spec = spec;
++      codec->patch_ops = stac92xx_patch_ops;
+       spec->num_pins = ARRAY_SIZE(stac92hd71bxx_pin_nids);
+       spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids);
+       spec->pin_nids = stac92hd71bxx_pin_nids;
++      memcpy(&spec->private_dimux, &stac92hd71bxx_dmux,
++                      sizeof(stac92hd71bxx_dmux));
+       spec->board_config = snd_hda_check_board_config(codec,
+                                                       STAC_92HD71BXX_MODELS,
+                                                       stac92hd71bxx_models,
+@@ -3696,47 +4391,101 @@ again:
+       case 0x111d76b5:
+               spec->mixer = stac92hd71bxx_mixer;
+               spec->init = stac92hd71bxx_core_init;
++              codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
+               break;
+       case 0x111d7608: /* 5 Port with Analog Mixer */
++              switch (codec->subsystem_id) {
++              case 0x103c361a:
++                      /* Enable VREF power saving on GPIO1 detect */
++                      snd_hda_codec_write(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));
++                      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
++                      spec->stream_delay = 40; /* 40 milliseconds */
++              }
++
+               /* no output amps */
+               spec->num_pwrs = 0;
+               spec->mixer = stac92hd71bxx_analog_mixer;
++              spec->dinput_mux = &spec->private_dimux;
+               /* disable VSW */
+               spec->init = &stac92hd71bxx_analog_core_init[HD_DISABLE_PORTF];
+               stac92xx_set_config_reg(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
++                      spec->stream_delay = 40; /* 40 milliseconds */
++              }
++
+               /* no output amps */
+               spec->num_pwrs = 0;
+               /* fallthru */
+       default:
++              spec->dinput_mux = &spec->private_dimux;
+               spec->mixer = stac92hd71bxx_analog_mixer;
+               spec->init = stac92hd71bxx_analog_core_init;
++              codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
+       }
+-      spec->aloopback_mask = 0x20;
++      spec->aloopback_mask = 0x50;
+       spec->aloopback_shift = 0;
+-      /* GPIO0 High = EAPD */
+-      spec->gpio_mask = 0x01;
+-      spec->gpio_dir = 0x01;
+-      spec->gpio_data = 0x01;
++      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->adc_nids = stac92hd71bxx_adc_nids;
+       spec->dmic_nids = stac92hd71bxx_dmic_nids;
+       spec->dmux_nids = stac92hd71bxx_dmux_nids;
++      spec->smux_nids = stac92hd71bxx_smux_nids;
+       spec->pwr_nids = stac92hd71bxx_pwr_nids;
+       spec->num_muxes = ARRAY_SIZE(stac92hd71bxx_mux_nids);
+       spec->num_adcs = ARRAY_SIZE(stac92hd71bxx_adc_nids);
+-      spec->num_dmics = STAC92HD71BXX_NUM_DMICS;
+-      spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids);
++
++      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);
++              stac92xx_auto_set_pinctl(codec, 0x0e,
++                      AC_PINCTL_IN_EN | AC_PINCTL_VREF_80);
++              break;
++      default:
++              spec->num_dmics = STAC92HD71BXX_NUM_DMICS;
++              spec->num_smuxes = ARRAY_SIZE(stac92hd71bxx_smux_nids);
++              spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids);
++      };
+       spec->multiout.num_dacs = 1;
+       spec->multiout.hp_nid = 0x11;
+       spec->multiout.dac_nids = stac92hd71bxx_dac_nids;
++      if (spec->dinput_mux)
++              spec->private_dimux.num_items +=
++                      spec->num_dmics -
++                              (ARRAY_SIZE(stac92hd71bxx_dmic_nids) - 1);
+       err = stac92xx_parse_auto_config(codec, 0x21, 0x23);
+       if (!err) {
+@@ -3754,8 +4503,6 @@ again:
+               return err;
+       }
+-      codec->patch_ops = stac92xx_patch_ops;
+-
+       return 0;
+ };
+@@ -3897,10 +4644,14 @@ static int patch_stac927x(struct hda_cod
+               stac92xx_set_config_regs(codec);
+       }
++      spec->digbeep_nid = 0x23;
+       spec->adc_nids = stac927x_adc_nids;
+       spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids);
+       spec->mux_nids = stac927x_mux_nids;
+       spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids);
++      spec->smux_nids = stac927x_smux_nids;
++      spec->num_smuxes = ARRAY_SIZE(stac927x_smux_nids);
++      spec->spdif_labels = stac927x_spdif_labels;
+       spec->dac_list = stac927x_dac_nids;
+       spec->multiout.dac_nids = spec->dac_nids;
+@@ -3943,9 +4694,11 @@ static int patch_stac927x(struct hda_cod
+               spec->num_dmuxes = ARRAY_SIZE(stac927x_dmux_nids);
+               break;
+       default:
+-              /* GPIO0 High = Enable EAPD */
+-              spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1;
+-              spec->gpio_data = 0x01;
++              if (spec->board_config > STAC_D965_REF) {
++                      /* GPIO0 High = Enable EAPD */
++                      spec->eapd_mask = spec->gpio_mask = 0x01;
++                      spec->gpio_dir = spec->gpio_data = 0x01;
++              }
+               spec->num_dmics = 0;
+               spec->init = stac927x_core_init;
+@@ -4017,10 +4770,13 @@ static int patch_stac9205(struct hda_cod
+               stac92xx_set_config_regs(codec);
+       }
++      spec->digbeep_nid = 0x23;
+       spec->adc_nids = stac9205_adc_nids;
+       spec->num_adcs = ARRAY_SIZE(stac9205_adc_nids);
+       spec->mux_nids = stac9205_mux_nids;
+       spec->num_muxes = ARRAY_SIZE(stac9205_mux_nids);
++      spec->smux_nids = stac9205_smux_nids;
++      spec->num_smuxes = ARRAY_SIZE(stac9205_smux_nids);
+       spec->dmic_nids = stac9205_dmic_nids;
+       spec->num_dmics = STAC9205_NUM_DMICS;
+       spec->dmux_nids = stac9205_dmux_nids;
+@@ -4056,6 +4812,9 @@ static int patch_stac9205(struct hda_cod
+                */
+               spec->gpio_data = 0x01;
+               break;
++      case STAC_9205_REF:
++              /* SPDIF-In enabled */
++              break;
+       default:
+               /* GPIO0 High = EAPD */
+               spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1;
+@@ -4375,6 +5134,8 @@ struct hda_codec_preset snd_hda_preset_s
+       { .id = 0x838476a6, .name = "STAC9254", .patch = patch_stac9205 },
+       { .id = 0x838476a7, .name = "STAC9254D", .patch = patch_stac9205 },
+       { .id = 0x111d7603, .name = "92HD75B3X5", .patch = patch_stac92hd71bxx},
++      { .id = 0x111d7604, .name = "92HD83C1X5", .patch = patch_stac92hd83xxx},
++      { .id = 0x111d7605, .name = "92HD81B1X5", .patch = patch_stac92hd83xxx},
+       { .id = 0x111d7608, .name = "92HD75B2X5", .patch = patch_stac92hd71bxx},
+       { .id = 0x111d7674, .name = "92HD73D1X5", .patch = patch_stac92hd73xx },
+       { .id = 0x111d7675, .name = "92HD73C1X5", .patch = patch_stac92hd73xx },