ALSA: hda - Fix and clean up hippo-compat HP auto-muting
[safe/jmp/linux-2.6] / sound / pci / hda / patch_sigmatel.c
index 14db35a..76487de 100644 (file)
@@ -33,7 +33,6 @@
 #include <sound/jack.h>
 #include "hda_codec.h"
 #include "hda_local.h"
-#include "hda_patch.h"
 #include "hda_beep.h"
 
 enum {
@@ -44,6 +43,7 @@ enum {
 };
 
 enum {
+       STAC_AUTO,
        STAC_REF,
        STAC_9200_OQO,
        STAC_9200_DELL_D21,
@@ -56,20 +56,25 @@ enum {
        STAC_9200_DELL_M25,
        STAC_9200_DELL_M26,
        STAC_9200_DELL_M27,
-       STAC_9200_GATEWAY,
+       STAC_9200_M4,
+       STAC_9200_M4_2,
        STAC_9200_PANASONIC,
        STAC_9200_MODELS
 };
 
 enum {
+       STAC_9205_AUTO,
        STAC_9205_REF,
        STAC_9205_DELL_M42,
        STAC_9205_DELL_M43,
        STAC_9205_DELL_M44,
+       STAC_9205_EAPD,
        STAC_9205_MODELS
 };
 
 enum {
+       STAC_92HD73XX_AUTO,
+       STAC_92HD73XX_NO_JD, /* no jack-detection */
        STAC_92HD73XX_REF,
        STAC_DELL_M6_AMIC,
        STAC_DELL_M6_DMIC,
@@ -79,28 +84,41 @@ enum {
 };
 
 enum {
+       STAC_92HD83XXX_AUTO,
        STAC_92HD83XXX_REF,
+       STAC_92HD83XXX_PWR_REF,
+       STAC_DELL_S14,
        STAC_92HD83XXX_MODELS
 };
 
 enum {
+       STAC_92HD71BXX_AUTO,
        STAC_92HD71BXX_REF,
        STAC_DELL_M4_1,
        STAC_DELL_M4_2,
        STAC_DELL_M4_3,
        STAC_HP_M4,
+       STAC_HP_DV5,
+       STAC_HP_HDX,
+       STAC_HP_DV4_1222NR,
        STAC_92HD71BXX_MODELS
 };
 
 enum {
+       STAC_925x_AUTO,
        STAC_925x_REF,
+       STAC_M1,
+       STAC_M1_2,
+       STAC_M2,
        STAC_M2_2,
-       STAC_MA6,
-       STAC_PA6,
+       STAC_M3,
+       STAC_M5,
+       STAC_M6,
        STAC_925x_MODELS
 };
 
 enum {
+       STAC_922X_AUTO,
        STAC_D945_REF,
        STAC_D945GTP3,
        STAC_D945GTP5,
@@ -128,6 +146,8 @@ enum {
 };
 
 enum {
+       STAC_927X_AUTO,
+       STAC_D965_REF_NO_JD, /* no jack-detection */
        STAC_D965_REF,
        STAC_D965_3ST,
        STAC_D965_5ST,
@@ -136,6 +156,12 @@ enum {
        STAC_927X_MODELS
 };
 
+enum {
+       STAC_9872_AUTO,
+       STAC_9872_VAIO,
+       STAC_9872_MODELS
+};
+
 struct sigmatel_event {
        hda_nid_t nid;
        unsigned char type;
@@ -156,11 +182,10 @@ struct sigmatel_spec {
        int board_config;
        unsigned int eapd_switch: 1;
        unsigned int surr_switch: 1;
-       unsigned int line_switch: 1;
-       unsigned int mic_switch: 1;
        unsigned int alt_switch: 1;
        unsigned int hp_detect: 1;
        unsigned int spdif_mute: 1;
+       unsigned int check_volume_offset:1;
 
        /* gpio lines */
        unsigned int eapd_mask;
@@ -173,6 +198,7 @@ struct sigmatel_spec {
        unsigned int stream_delay;
 
        /* analog loopback */
+       struct snd_kcontrol_new *aloopback_ctl;
        unsigned char aloopback_mask;
        unsigned char aloopback_shift;
 
@@ -194,6 +220,10 @@ struct sigmatel_spec {
        unsigned int cur_mmux;
        struct hda_multi_out multiout;
        hda_nid_t dac_nids[5];
+       hda_nid_t hp_dacs[5];
+       hda_nid_t speaker_dacs[5];
+
+       int volume_offset;
 
        /* capture */
        hda_nid_t *adc_nids;
@@ -216,7 +246,6 @@ struct sigmatel_spec {
        /* pin widgets */
        hda_nid_t *pin_nids;
        unsigned int num_pins;
-       unsigned int *pin_configs;
 
        /* codec specific stuff */
        struct hda_verb *init;
@@ -237,7 +266,9 @@ struct sigmatel_spec {
        /* i/o switches */
        unsigned int io_switch[2];
        unsigned int clfe_swap;
-       unsigned int hp_switch; /* NID of HP as line-out */
+       hda_nid_t line_switch;  /* shared line-in for input and output */
+       hda_nid_t mic_switch;   /* shared mic-in for input and output */
+       hda_nid_t hp_switch; /* NID of HP as line-out */
        unsigned int aloopback;
 
        struct hda_pcm pcm_rec[2];      /* PCM information */
@@ -288,9 +319,6 @@ static hda_nid_t stac92hd73xx_dmic_nids[STAC92HD73XX_NUM_DMICS + 1] = {
 };
 
 #define STAC92HD73_DAC_COUNT 5
-static hda_nid_t stac92hd73xx_dac_nids[STAC92HD73_DAC_COUNT] = {
-       0x15, 0x16, 0x17, 0x18, 0x19,
-};
 
 static hda_nid_t stac92hd73xx_mux_nids[4] = {
        0x28, 0x29, 0x2a, 0x2b,
@@ -309,11 +337,7 @@ 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,
@@ -332,7 +356,11 @@ static hda_nid_t stac92hd83xxx_slave_dig_outs[2] = {
 };
 
 static unsigned int stac92hd83xxx_pwr_mapping[4] = {
-       0x03, 0x0c, 0x10, 0x40,
+       0x03, 0x0c, 0x20, 0x40,
+};
+
+static hda_nid_t stac92hd83xxx_amp_nids[1] = {
+       0xc,
 };
 
 static hda_nid_t stac92hd71bxx_pwr_nids[3] = {
@@ -355,10 +383,6 @@ static hda_nid_t stac92hd71bxx_smux_nids[2] = {
        0x24, 0x25,
 };
 
-static hda_nid_t stac92hd71bxx_dac_nids[1] = {
-       0x10, /*0x11, */
-};
-
 #define STAC92HD71BXX_NUM_DMICS        2
 static hda_nid_t stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS + 1] = {
        0x18, 0x19, 0
@@ -397,6 +421,10 @@ static hda_nid_t stac922x_mux_nids[2] = {
         0x12, 0x13,
 };
 
+static hda_nid_t stac927x_slave_dig_outs[2] = {
+       0x1f, 0,
+};
+
 static hda_nid_t stac927x_adc_nids[3] = {
         0x07, 0x08, 0x09
 };
@@ -469,15 +497,21 @@ static hda_nid_t stac92hd73xx_pin_nids[13] = {
        0x14, 0x22, 0x23
 };
 
-static hda_nid_t stac92hd83xxx_pin_nids[14] = {
+static hda_nid_t stac92hd83xxx_pin_nids[10] = {
        0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
-       0x0f, 0x10, 0x11, 0x12, 0x13,
-       0x1d, 0x1e, 0x1f, 0x20
+       0x0f, 0x10, 0x11, 0x1f, 0x20,
+};
+
+#define STAC92HD71BXX_NUM_PINS 13
+static hda_nid_t stac92hd71bxx_pin_nids_4port[STAC92HD71BXX_NUM_PINS] = {
+       0x0a, 0x0b, 0x0c, 0x0d, 0x00,
+       0x00, 0x14, 0x18, 0x19, 0x1e,
+       0x1f, 0x20, 0x27
 };
-static hda_nid_t stac92hd71bxx_pin_nids[11] = {
+static hda_nid_t stac92hd71bxx_pin_nids_6port[STAC92HD71BXX_NUM_PINS] = {
        0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
        0x0f, 0x14, 0x18, 0x19, 0x1e,
-       0x1f,
+       0x1f, 0x20, 0x27
 };
 
 static hda_nid_t stac927x_pin_nids[14] = {
@@ -760,10 +794,6 @@ static struct hda_verb stac9200_eapd_init[] = {
 static struct hda_verb stac92hd73xx_6ch_core_init[] = {
        /* set master volume and direct control */
        { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
-       /* setup audio connections */
-       { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00},
-       { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01},
-       { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02},
        /* setup adcs to point to mixer */
        { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
        { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
@@ -782,10 +812,6 @@ static struct hda_verb dell_eq_core_init[] = {
        /* set master volume to max value without distortion
         * and direct control */
        { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xec},
-       /* setup audio connections */
-       { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
-       { 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},
@@ -799,10 +825,6 @@ static struct hda_verb dell_eq_core_init[] = {
 
 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},
        /* setup adcs to point to mixer */
        { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
        { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
@@ -817,13 +839,6 @@ static struct hda_verb dell_m6_core_init[] = {
 static struct hda_verb stac92hd73xx_8ch_core_init[] = {
        /* set master volume and direct control */
        { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
-       /* setup audio connections */
-       { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00},
-       { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01},
-       { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02},
-       /* connect hp ports to dac3 */
-       { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x03},
-       { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x03},
        /* setup adcs to point to mixer */
        { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
        { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
@@ -841,15 +856,8 @@ static struct hda_verb stac92hd73xx_8ch_core_init[] = {
 static struct hda_verb stac92hd73xx_10ch_core_init[] = {
        /* set master volume and direct control */
        { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
-       /* setup audio connections */
-       { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
-       { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01 },
-       { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02 },
        /* dac3 is connected to import3 mux */
        { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb07f},
-       /* connect hp ports to dac4 */
-       { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x04},
-       { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x04},
        /* setup adcs to point to mixer */
        { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
        { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
@@ -865,44 +873,37 @@ static struct hda_verb stac92hd73xx_10ch_core_init[] = {
 };
 
 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},
+       { 0xa, AC_VERB_SET_CONNECT_SEL, 0x1},
+       { 0xb, AC_VERB_SET_CONNECT_SEL, 0x1},
+       { 0xd, AC_VERB_SET_CONNECT_SEL, 0x0},
 
        /* 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},
-       /* 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)},
-       { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       {}
 };
 
-#define HD_DISABLE_PORTF 2
+#define HD_DISABLE_PORTF 1
 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},
-       /* unmute right and left channels for node 0x0f */
-       { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
        /* start of config #2 */
 
        /* 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},
-       /* unmute right and left channels for nodes 0x0a, 0xd */
+       {}
+};
+
+static struct hda_verb stac92hd71bxx_unmute_core_init[] = {
+       /* unmute right and left channels for nodes 0x0f, 0xa, 0x0d */
+       { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
        { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
        { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
        {}
@@ -911,6 +912,8 @@ static struct hda_verb stac92hd71bxx_analog_core_init[] = {
 static struct hda_verb stac925x_core_init[] = {
        /* set dac0mux for dac converter */
        { 0x06, AC_VERB_SET_CONNECT_SEL, 0x00},
+       /* mute the master volume */
+       { 0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
        {}
 };
 
@@ -981,16 +984,6 @@ static struct hda_verb stac9205_core_init[] = {
                .private_value = HDA_COMPOSE_AMP_VAL(nid, chs, idx, dir) \
        }
 
-#define STAC_INPUT_SOURCE(cnt) \
-       { \
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
-               .name = "Input Source", \
-               .count = cnt, \
-               .info = stac92xx_mux_enum_info, \
-               .get = stac92xx_mux_enum_get, \
-               .put = stac92xx_mux_enum_put, \
-       }
-
 #define STAC_ANALOG_LOOPBACK(verb_read, verb_write, cnt) \
        { \
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
@@ -1005,7 +998,6 @@ static struct hda_verb stac9205_core_init[] = {
 static struct snd_kcontrol_new stac9200_mixer[] = {
        HDA_CODEC_VOLUME("Master Playback Volume", 0xb, 0, HDA_OUTPUT),
        HDA_CODEC_MUTE("Master Playback Switch", 0xb, 0, HDA_OUTPUT),
-       STAC_INPUT_SOURCE(1),
        HDA_CODEC_VOLUME("Capture Volume", 0x0a, 0, HDA_OUTPUT),
        HDA_CODEC_MUTE("Capture Switch", 0x0a, 0, HDA_OUTPUT),
        { } /* end */
@@ -1030,8 +1022,6 @@ static struct snd_kcontrol_new stac92hd73xx_6ch_mixer[] = {
        HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT),
        HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, 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),
 
@@ -1041,9 +1031,22 @@ static struct snd_kcontrol_new stac92hd73xx_6ch_mixer[] = {
        { } /* end */
 };
 
-static struct snd_kcontrol_new stac92hd73xx_8ch_mixer[] = {
+static struct snd_kcontrol_new stac92hd73xx_6ch_loopback[] = {
+       STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3),
+       {}
+};
+
+static struct snd_kcontrol_new stac92hd73xx_8ch_loopback[] = {
        STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 4),
+       {}
+};
+
+static struct snd_kcontrol_new stac92hd73xx_10ch_loopback[] = {
+       STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 5),
+       {}
+};
 
+static struct snd_kcontrol_new stac92hd73xx_8ch_mixer[] = {
        HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
        HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT),
 
@@ -1068,8 +1071,6 @@ static struct snd_kcontrol_new stac92hd73xx_8ch_mixer[] = {
 };
 
 static struct snd_kcontrol_new stac92hd73xx_10ch_mixer[] = {
-       STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 5),
-
        HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
        HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT),
 
@@ -1101,29 +1102,26 @@ static struct snd_kcontrol_new stac92hd83xxx_mixer[] = {
        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("DAC0 Capture Volume", 0x1b, 0x3, HDA_INPUT),
+       HDA_CODEC_MUTE("DAC0 Capture Switch", 0x1b, 0x3, 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("DAC1 Capture Volume", 0x1b, 0x4, HDA_INPUT),
+       HDA_CODEC_MUTE("DAC1 Capture Switch", 0x1b, 0x4, 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("Front Mic Capture Volume", 0x1b, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("Front Mic Capture Switch", 0x1b, 0x0, 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("Line In Capture Volume", 0x1b, 0x2, HDA_INPUT),
+       HDA_CODEC_MUTE("Line In Capture Switch", 0x1b, 0x2, HDA_INPUT),
 
        /*
-       HDA_CODEC_VOLUME("Mic Capture Volume", 0x1b, 0x4, HDA_INPUT),
-       HDA_CODEC_MUTE("Mic Capture Switch", 0x1b 0x4, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Capture Volume", 0x1b, 0x1, HDA_INPUT),
+       HDA_CODEC_MUTE("Mic Capture Switch", 0x1b 0x1, 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),
 
@@ -1149,10 +1147,11 @@ static struct snd_kcontrol_new stac92hd71bxx_analog_mixer[] = {
        { } /* end */
 };
 
-static struct snd_kcontrol_new stac92hd71bxx_mixer[] = {
-       STAC_INPUT_SOURCE(2),
-       STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2),
+static struct snd_kcontrol_new stac92hd71bxx_loopback[] = {
+       STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2)
+};
 
+static struct snd_kcontrol_new stac92hd71bxx_mixer[] = {
        HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT),
        HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT),
 
@@ -1162,16 +1161,14 @@ static struct snd_kcontrol_new stac92hd71bxx_mixer[] = {
 };
 
 static struct snd_kcontrol_new stac925x_mixer[] = {
-       STAC_INPUT_SOURCE(1),
+       HDA_CODEC_VOLUME("Master Playback Volume", 0x0e, 0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Master Playback Switch", 0x0e, 0, HDA_OUTPUT),
        HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_OUTPUT),
        HDA_CODEC_MUTE("Capture Switch", 0x14, 0, HDA_OUTPUT),
        { } /* end */
 };
 
 static struct snd_kcontrol_new stac9205_mixer[] = {
-       STAC_INPUT_SOURCE(2),
-       STAC_ANALOG_LOOPBACK(0xFE0, 0x7E0, 1),
-
        HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1b, 0x0, HDA_INPUT),
        HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1d, 0x0, HDA_OUTPUT),
 
@@ -1180,9 +1177,13 @@ static struct snd_kcontrol_new stac9205_mixer[] = {
        { } /* end */
 };
 
+static struct snd_kcontrol_new stac9205_loopback[] = {
+       STAC_ANALOG_LOOPBACK(0xFE0, 0x7E0, 1),
+       {}
+};
+
 /* This needs to be generated dynamically based on sequence */
 static struct snd_kcontrol_new stac922x_mixer[] = {
-       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),
 
@@ -1193,9 +1194,6 @@ static struct snd_kcontrol_new stac922x_mixer[] = {
 
 
 static struct snd_kcontrol_new stac927x_mixer[] = {
-       STAC_INPUT_SOURCE(3),
-       STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB, 1),
-
        HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x18, 0x0, HDA_INPUT),
        HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1b, 0x0, HDA_OUTPUT),
 
@@ -1207,6 +1205,11 @@ static struct snd_kcontrol_new stac927x_mixer[] = {
        { } /* end */
 };
 
+static struct snd_kcontrol_new stac927x_loopback[] = {
+       STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB, 1),
+       {}
+};
+
 static struct snd_kcontrol_new stac_dmux_mixer = {
        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
        .name = "Digital Input Source",
@@ -1232,10 +1235,7 @@ static const char *slave_vols[] = {
        "LFE Playback Volume",
        "Side Playback Volume",
        "Headphone Playback Volume",
-       "Headphone Playback Volume",
        "Speaker Playback Volume",
-       "External Speaker Playback Volume",
-       "Speaker2 Playback Volume",
        NULL
 };
 
@@ -1246,10 +1246,7 @@ static const char *slave_sws[] = {
        "LFE Playback Switch",
        "Side Playback Switch",
        "Headphone Playback Switch",
-       "Headphone Playback Switch",
        "Speaker Playback Switch",
-       "External Speaker Playback Switch",
-       "Speaker2 Playback Switch",
        "IEC958 Playback Switch",
        NULL
 };
@@ -1292,7 +1289,7 @@ static int stac92xx_build_controls(struct hda_codec *codec)
                        spec->spdif_mute = 1;
                }
                stac_smux_mixer.count = spec->num_smuxes;
-               err = snd_ctl_add(codec->bus->card,
+               err = snd_hda_ctl_add(codec,
                                  snd_ctl_new1(&stac_smux_mixer, codec));
                if (err < 0)
                        return err;
@@ -1319,6 +1316,8 @@ static int stac92xx_build_controls(struct hda_codec *codec)
                unsigned int vmaster_tlv[4];
                snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
                                        HDA_OUTPUT, vmaster_tlv);
+               /* correct volume offset */
+               vmaster_tlv[2] += vmaster_tlv[3] * spec->volume_offset;
                err = snd_hda_add_vmaster(codec, "Master Playback Volume",
                                          vmaster_tlv, slave_vols);
                if (err < 0)
@@ -1331,6 +1330,13 @@ static int stac92xx_build_controls(struct hda_codec *codec)
                        return err;
        }
 
+       if (spec->aloopback_ctl &&
+           snd_hda_get_bool_hint(codec, "loopback") == 1) {
+               err = snd_hda_add_new_ctls(codec, spec->aloopback_ctl);
+               if (err < 0)
+                       return err;
+       }
+
        stac92xx_free_kctls(codec); /* no longer needed */
 
        /* create jack input elements */
@@ -1370,7 +1376,16 @@ static unsigned int ref9200_pin_configs[8] = {
        0x02a19020, 0x01a19021, 0x90100140, 0x01813122,
 };
 
-/* 
+static unsigned int gateway9200_m4_pin_configs[8] = {
+       0x400000fe, 0x404500f4, 0x400100f0, 0x90110010,
+       0x400100f1, 0x02a1902e, 0x500000f2, 0x500000f3,
+};
+static unsigned int gateway9200_m4_2_pin_configs[8] = {
+       0x400000fe, 0x404500f4, 0x400100f0, 0x90110010,
+       0x400100f1, 0x02a1902e, 0x500000f2, 0x500000f3,
+};
+
+/*
     STAC 9200 pin configs for
     102801A8
     102801DE
@@ -1500,10 +1515,13 @@ static unsigned int *stac9200_brd_tbl[STAC_9200_MODELS] = {
        [STAC_9200_DELL_M25] = dell9200_m25_pin_configs,
        [STAC_9200_DELL_M26] = dell9200_m26_pin_configs,
        [STAC_9200_DELL_M27] = dell9200_m27_pin_configs,
+       [STAC_9200_M4] = gateway9200_m4_pin_configs,
+       [STAC_9200_M4_2] = gateway9200_m4_2_pin_configs,
        [STAC_9200_PANASONIC] = ref9200_pin_configs,
 };
 
 static const char *stac9200_models[STAC_9200_MODELS] = {
+       [STAC_AUTO] = "auto",
        [STAC_REF] = "ref",
        [STAC_9200_OQO] = "oqo",
        [STAC_9200_DELL_D21] = "dell-d21",
@@ -1516,7 +1534,8 @@ static const char *stac9200_models[STAC_9200_MODELS] = {
        [STAC_9200_DELL_M25] = "dell-m25",
        [STAC_9200_DELL_M26] = "dell-m26",
        [STAC_9200_DELL_M27] = "dell-m27",
-       [STAC_9200_GATEWAY] = "gateway",
+       [STAC_9200_M4] = "gateway-m4",
+       [STAC_9200_M4_2] = "gateway-m4-2",
        [STAC_9200_PANASONIC] = "panasonic",
 };
 
@@ -1524,6 +1543,8 @@ static struct snd_pci_quirk stac9200_cfg_tbl[] = {
        /* SigmaTel reference board */
        SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
                      "DFI LanParty", STAC_REF),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
+                     "DFI LanParty", STAC_REF),
        /* Dell laptops have BIOS problem */
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01a8,
                      "unknown Dell", STAC_9200_DELL_D21),
@@ -1586,11 +1607,9 @@ static struct snd_pci_quirk stac9200_cfg_tbl[] = {
        /* Panasonic */
        SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-74", STAC_9200_PANASONIC),
        /* Gateway machines needs EAPD to be set on resume */
-       SND_PCI_QUIRK(0x107b, 0x0205, "Gateway S-7110M", STAC_9200_GATEWAY),
-       SND_PCI_QUIRK(0x107b, 0x0317, "Gateway MT3423, MX341*",
-                     STAC_9200_GATEWAY),
-       SND_PCI_QUIRK(0x107b, 0x0318, "Gateway ML3019, MT3707",
-                     STAC_9200_GATEWAY),
+       SND_PCI_QUIRK(0x107b, 0x0205, "Gateway S-7110M", STAC_9200_M4),
+       SND_PCI_QUIRK(0x107b, 0x0317, "Gateway MT3423, MX341*", STAC_9200_M4_2),
+       SND_PCI_QUIRK(0x107b, 0x0318, "Gateway ML3019, MT3707", STAC_9200_M4_2),
        /* OQO Mobile */
        SND_PCI_QUIRK(0x1106, 0x3288, "OQO Model 2", STAC_9200_OQO),
        {} /* terminator */
@@ -1601,44 +1620,87 @@ static unsigned int ref925x_pin_configs[8] = {
        0x90a70320, 0x02214210, 0x01019020, 0x9033032e,
 };
 
-static unsigned int stac925x_MA6_pin_configs[8] = {
-       0x40c003f0, 0x424503f2, 0x01813022, 0x02a19021,
-       0x90a70320, 0x90100211, 0x400003f1, 0x9033032e,
+static unsigned int stac925xM1_pin_configs[8] = {
+       0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020,
+       0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e,
 };
 
-static unsigned int stac925x_PA6_pin_configs[8] = {
-       0x40c003f0, 0x424503f2, 0x01813022, 0x02a19021,
-       0x50a103f0, 0x90100211, 0x400003f1, 0x9033032e,
+static unsigned int stac925xM1_2_pin_configs[8] = {
+       0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020,
+       0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e,
+};
+
+static unsigned int stac925xM2_pin_configs[8] = {
+       0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020,
+       0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e,
 };
 
 static unsigned int stac925xM2_2_pin_configs[8] = {
-       0x40c003f3, 0x424503f2, 0x04180011, 0x02a19020,
-       0x50a103f0, 0x90100212, 0x400003f1, 0x9033032e,
+       0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020,
+       0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e,
+};
+
+static unsigned int stac925xM3_pin_configs[8] = {
+       0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020,
+       0x40a000f0, 0x90100210, 0x400003f1, 0x503303f3,
+};
+
+static unsigned int stac925xM5_pin_configs[8] = {
+       0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020,
+       0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e,
+};
+
+static unsigned int stac925xM6_pin_configs[8] = {
+       0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020,
+       0x40a000f0, 0x90100210, 0x400003f1, 0x90330320,
 };
 
 static unsigned int *stac925x_brd_tbl[STAC_925x_MODELS] = {
        [STAC_REF] = ref925x_pin_configs,
+       [STAC_M1] = stac925xM1_pin_configs,
+       [STAC_M1_2] = stac925xM1_2_pin_configs,
+       [STAC_M2] = stac925xM2_pin_configs,
        [STAC_M2_2] = stac925xM2_2_pin_configs,
-       [STAC_MA6] = stac925x_MA6_pin_configs,
-       [STAC_PA6] = stac925x_PA6_pin_configs,
+       [STAC_M3] = stac925xM3_pin_configs,
+       [STAC_M5] = stac925xM5_pin_configs,
+       [STAC_M6] = stac925xM6_pin_configs,
 };
 
 static const char *stac925x_models[STAC_925x_MODELS] = {
+       [STAC_925x_AUTO] = "auto",
        [STAC_REF] = "ref",
+       [STAC_M1] = "m1",
+       [STAC_M1_2] = "m1-2",
+       [STAC_M2] = "m2",
        [STAC_M2_2] = "m2-2",
-       [STAC_MA6] = "m6",
-       [STAC_PA6] = "pa6",
+       [STAC_M3] = "m3",
+       [STAC_M5] = "m5",
+       [STAC_M6] = "m6",
+};
+
+static struct snd_pci_quirk stac925x_codec_id_cfg_tbl[] = {
+       SND_PCI_QUIRK(0x107b, 0x0316, "Gateway M255", STAC_M2),
+       SND_PCI_QUIRK(0x107b, 0x0366, "Gateway MP6954", STAC_M5),
+       SND_PCI_QUIRK(0x107b, 0x0461, "Gateway NX560XL", STAC_M1),
+       SND_PCI_QUIRK(0x107b, 0x0681, "Gateway NX860", STAC_M2),
+       SND_PCI_QUIRK(0x107b, 0x0367, "Gateway MX6453", STAC_M1_2),
+       /* Not sure about the brand name for those */
+       SND_PCI_QUIRK(0x107b, 0x0281, "Gateway mobile", STAC_M1),
+       SND_PCI_QUIRK(0x107b, 0x0507, "Gateway mobile", STAC_M3),
+       SND_PCI_QUIRK(0x107b, 0x0281, "Gateway mobile", STAC_M6),
+       SND_PCI_QUIRK(0x107b, 0x0685, "Gateway mobile", STAC_M2_2),
+       {} /* terminator */
 };
 
 static struct snd_pci_quirk stac925x_cfg_tbl[] = {
        /* SigmaTel reference board */
        SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_REF),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, "DFI LanParty", STAC_REF),
        SND_PCI_QUIRK(0x8384, 0x7632, "Stac9202 Reference Board", STAC_REF),
-       SND_PCI_QUIRK(0x107b, 0x0316, "Gateway M255", STAC_REF),
-       SND_PCI_QUIRK(0x107b, 0x0366, "Gateway MP6954", STAC_REF),
-       SND_PCI_QUIRK(0x107b, 0x0461, "Gateway NX560XL", STAC_MA6),
-       SND_PCI_QUIRK(0x107b, 0x0681, "Gateway NX860", STAC_PA6),
-       SND_PCI_QUIRK(0x1002, 0x437b, "Gateway MX6453", STAC_M2_2),
+
+       /* Default table for unknown ID */
+       SND_PCI_QUIRK(0x1002, 0x437b, "Gateway mobile", STAC_M2_2),
+
        {} /* terminator */
 };
 
@@ -1665,6 +1727,8 @@ static unsigned int *stac92hd73xx_brd_tbl[STAC_92HD73XX_MODELS] = {
 };
 
 static const char *stac92hd73xx_models[STAC_92HD73XX_MODELS] = {
+       [STAC_92HD73XX_AUTO] = "auto",
+       [STAC_92HD73XX_NO_JD] = "no-jd",
        [STAC_92HD73XX_REF] = "ref",
        [STAC_DELL_M6_AMIC] = "dell-m6-amic",
        [STAC_DELL_M6_DMIC] = "dell-m6-dmic",
@@ -1676,6 +1740,8 @@ static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = {
        /* SigmaTel reference board */
        SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
                                "DFI LanParty", STAC_92HD73XX_REF),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
+                               "DFI LanParty", STAC_92HD73XX_REF),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0254,
                                "Dell Studio 1535", STAC_DELL_M6_DMIC),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0255,
@@ -1694,52 +1760,73 @@ static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = {
                                "unknown Dell", STAC_DELL_M6_DMIC),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x029f,
                                "Dell Studio 1537", STAC_DELL_M6_DMIC),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02a0,
+                               "Dell Studio 17", STAC_DELL_M6_DMIC),
        {} /* terminator */
 };
 
-static unsigned int ref92hd83xxx_pin_configs[14] = {
+static unsigned int ref92hd83xxx_pin_configs[10] = {
        0x02214030, 0x02211010, 0x02a19020, 0x02170130,
        0x01014050, 0x01819040, 0x01014020, 0x90a3014e,
-       0x40f000f0, 0x40f000f0, 0x40f000f0, 0x40f000f0,
        0x01451160, 0x98560170,
 };
 
+static unsigned int dell_s14_pin_configs[10] = {
+       0x02214030, 0x02211010, 0x02a19020, 0x01014050,
+       0x40f000f0, 0x01819040, 0x40f000f0, 0x90a60160,
+       0x40f000f0, 0x40f000f0,
+};
+
 static unsigned int *stac92hd83xxx_brd_tbl[STAC_92HD83XXX_MODELS] = {
        [STAC_92HD83XXX_REF] = ref92hd83xxx_pin_configs,
+       [STAC_92HD83XXX_PWR_REF] = ref92hd83xxx_pin_configs,
+       [STAC_DELL_S14] = dell_s14_pin_configs,
 };
 
 static const char *stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = {
+       [STAC_92HD83XXX_AUTO] = "auto",
        [STAC_92HD83XXX_REF] = "ref",
+       [STAC_92HD83XXX_PWR_REF] = "mic-ref",
+       [STAC_DELL_S14] = "dell-s14",
 };
 
 static struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = {
        /* SigmaTel reference board */
        SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
-                     "DFI LanParty", STAC_92HD71BXX_REF),
+                     "DFI LanParty", STAC_92HD83XXX_REF),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
+                     "DFI LanParty", STAC_92HD83XXX_REF),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ba,
+                     "unknown Dell", STAC_DELL_S14),
+       {} /* terminator */
 };
 
-static unsigned int ref92hd71bxx_pin_configs[11] = {
+static unsigned int ref92hd71bxx_pin_configs[STAC92HD71BXX_NUM_PINS] = {
        0x02214030, 0x02a19040, 0x01a19020, 0x01014010,
        0x0181302e, 0x01014010, 0x01019020, 0x90a000f0,
-       0x90a000f0, 0x01452050, 0x01452050,
+       0x90a000f0, 0x01452050, 0x01452050, 0x00000000,
+       0x00000000
 };
 
-static unsigned int dell_m4_1_pin_configs[11] = {
+static unsigned int dell_m4_1_pin_configs[STAC92HD71BXX_NUM_PINS] = {
        0x0421101f, 0x04a11221, 0x40f000f0, 0x90170110,
        0x23a1902e, 0x23014250, 0x40f000f0, 0x90a000f0,
-       0x40f000f0, 0x4f0000f0, 0x4f0000f0,
+       0x40f000f0, 0x4f0000f0, 0x4f0000f0, 0x00000000,
+       0x00000000
 };
 
-static unsigned int dell_m4_2_pin_configs[11] = {
+static unsigned int dell_m4_2_pin_configs[STAC92HD71BXX_NUM_PINS] = {
        0x0421101f, 0x04a11221, 0x90a70330, 0x90170110,
        0x23a1902e, 0x23014250, 0x40f000f0, 0x40f000f0,
-       0x40f000f0, 0x044413b0, 0x044413b0,
+       0x40f000f0, 0x044413b0, 0x044413b0, 0x00000000,
+       0x00000000
 };
 
-static unsigned int dell_m4_3_pin_configs[11] = {
+static unsigned int dell_m4_3_pin_configs[STAC92HD71BXX_NUM_PINS] = {
        0x0421101f, 0x04a11221, 0x90a70330, 0x90170110,
        0x40f000f0, 0x40f000f0, 0x40f000f0, 0x90a000f0,
-       0x40f000f0, 0x044413b0, 0x044413b0,
+       0x40f000f0, 0x044413b0, 0x044413b0, 0x00000000,
+       0x00000000
 };
 
 static unsigned int *stac92hd71bxx_brd_tbl[STAC_92HD71BXX_MODELS] = {
@@ -1748,26 +1835,43 @@ static unsigned int *stac92hd71bxx_brd_tbl[STAC_92HD71BXX_MODELS] = {
        [STAC_DELL_M4_2]        = dell_m4_2_pin_configs,
        [STAC_DELL_M4_3]        = dell_m4_3_pin_configs,
        [STAC_HP_M4]            = NULL,
+       [STAC_HP_DV5]           = NULL,
+       [STAC_HP_HDX]           = NULL,
+       [STAC_HP_DV4_1222NR]    = NULL,
 };
 
 static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = {
+       [STAC_92HD71BXX_AUTO] = "auto",
        [STAC_92HD71BXX_REF] = "ref",
        [STAC_DELL_M4_1] = "dell-m4-1",
        [STAC_DELL_M4_2] = "dell-m4-2",
        [STAC_DELL_M4_3] = "dell-m4-3",
        [STAC_HP_M4] = "hp-m4",
+       [STAC_HP_DV5] = "hp-dv5",
+       [STAC_HP_HDX] = "hp-hdx",
+       [STAC_HP_DV4_1222NR] = "hp-dv4-1222nr",
 };
 
 static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
        /* SigmaTel reference board */
        SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
                      "DFI LanParty", STAC_92HD71BXX_REF),
-       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30f2,
-                     "HP dv5", STAC_HP_M4),
-       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30f4,
-                     "HP dv7", STAC_HP_M4),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
+                     "DFI LanParty", STAC_92HD71BXX_REF),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30fb,
+                     "HP dv4-1222nr", STAC_HP_DV4_1222NR),
+       SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3080,
+                     "HP", STAC_HP_DV5),
+       SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x30f0,
+                     "HP dv4-7", STAC_HP_DV5),
+       SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3600,
+                     "HP dv4-7", STAC_HP_DV5),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3610,
+                     "HP HDX", STAC_HP_HDX),  /* HDX18 */
        SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361a,
-                               "unknown HP", STAC_HP_M4),
+                     "HP mini 1000", STAC_HP_M4),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361b,
+                     "HP HDX", STAC_HP_HDX),  /* HDX16 */
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233,
                                "unknown Dell", STAC_DELL_M4_1),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0234,
@@ -1919,6 +2023,7 @@ static unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = {
 };
 
 static const char *stac922x_models[STAC_922X_MODELS] = {
+       [STAC_922X_AUTO] = "auto",
        [STAC_D945_REF] = "ref",
        [STAC_D945GTP5] = "5stack",
        [STAC_D945GTP3] = "3stack",
@@ -1946,6 +2051,8 @@ static struct snd_pci_quirk stac922x_cfg_tbl[] = {
        /* SigmaTel reference board */
        SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
                      "DFI LanParty", STAC_D945_REF),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
+                     "DFI LanParty", STAC_D945_REF),
        /* Intel 945G based systems */
        SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0101,
                      "Intel D945G", STAC_D945GTP3),
@@ -1999,6 +2106,9 @@ static struct snd_pci_quirk stac922x_cfg_tbl[] = {
                      "Intel D945P", STAC_D945GTP3),
        SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0707,
                      "Intel D945P", STAC_D945GTP5),
+       /* other intel */
+       SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0204,
+                     "Intel D945", STAC_D945_REF),
        /* other systems  */
        /* Apple Intel Mac (Mac Mini, MacBook, MacBook Pro...) */
        SND_PCI_QUIRK(0x8384, 0x7680,
@@ -2023,31 +2133,7 @@ static struct snd_pci_quirk stac922x_cfg_tbl[] = {
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d7,
                      "Dell XPS M1210", STAC_922X_DELL_M82),
        /* ECS/PC Chips boards */
-       SND_PCI_QUIRK(0x1019, 0x2144,
-                     "ECS/PC chips", STAC_ECS_202),
-       SND_PCI_QUIRK(0x1019, 0x2608,
-                     "ECS/PC chips", STAC_ECS_202),
-       SND_PCI_QUIRK(0x1019, 0x2633,
-                     "ECS/PC chips P17G/1333", STAC_ECS_202),
-       SND_PCI_QUIRK(0x1019, 0x2811,
-                     "ECS/PC chips", STAC_ECS_202),
-       SND_PCI_QUIRK(0x1019, 0x2812,
-                     "ECS/PC chips", STAC_ECS_202),
-       SND_PCI_QUIRK(0x1019, 0x2813,
-                     "ECS/PC chips", STAC_ECS_202),
-       SND_PCI_QUIRK(0x1019, 0x2814,
-                     "ECS/PC chips", STAC_ECS_202),
-       SND_PCI_QUIRK(0x1019, 0x2815,
-                     "ECS/PC chips", STAC_ECS_202),
-       SND_PCI_QUIRK(0x1019, 0x2816,
-                     "ECS/PC chips", STAC_ECS_202),
-       SND_PCI_QUIRK(0x1019, 0x2817,
-                     "ECS/PC chips", STAC_ECS_202),
-       SND_PCI_QUIRK(0x1019, 0x2818,
-                     "ECS/PC chips", STAC_ECS_202),
-       SND_PCI_QUIRK(0x1019, 0x2819,
-                     "ECS/PC chips", STAC_ECS_202),
-       SND_PCI_QUIRK(0x1019, 0x2820,
+       SND_PCI_QUIRK_MASK(0x1019, 0xf000, 0x2000,
                      "ECS/PC chips", STAC_ECS_202),
        {} /* terminator */
 };
@@ -2081,6 +2167,7 @@ static unsigned int dell_3st_pin_configs[14] = {
 };
 
 static unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = {
+       [STAC_D965_REF_NO_JD] = ref927x_pin_configs,
        [STAC_D965_REF]  = ref927x_pin_configs,
        [STAC_D965_3ST]  = d965_3st_pin_configs,
        [STAC_D965_5ST]  = d965_5st_pin_configs,
@@ -2089,6 +2176,8 @@ static unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = {
 };
 
 static const char *stac927x_models[STAC_927X_MODELS] = {
+       [STAC_927X_AUTO]        = "auto",
+       [STAC_D965_REF_NO_JD]   = "ref-no-jd",
        [STAC_D965_REF]         = "ref",
        [STAC_D965_3ST]         = "3stack",
        [STAC_D965_5ST]         = "5stack",
@@ -2100,26 +2189,16 @@ static struct snd_pci_quirk stac927x_cfg_tbl[] = {
        /* SigmaTel reference board */
        SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
                      "DFI LanParty", STAC_D965_REF),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
+                     "DFI LanParty", STAC_D965_REF),
         /* Intel 946 based systems */
        SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x3d01, "Intel D946", STAC_D965_3ST),
        SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0xa301, "Intel D946", STAC_D965_3ST),
        /* 965 based 3 stack systems */
-       SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2116, "Intel D965", STAC_D965_3ST),
-       SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2115, "Intel D965", STAC_D965_3ST),
-       SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2114, "Intel D965", STAC_D965_3ST),
-       SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2113, "Intel D965", STAC_D965_3ST),
-       SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2112, "Intel D965", STAC_D965_3ST),
-       SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2111, "Intel D965", STAC_D965_3ST),
-       SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2110, "Intel D965", STAC_D965_3ST),
-       SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2009, "Intel D965", STAC_D965_3ST),
-       SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2008, "Intel D965", STAC_D965_3ST),
-       SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2007, "Intel D965", STAC_D965_3ST),
-       SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2006, "Intel D965", STAC_D965_3ST),
-       SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2005, "Intel D965", STAC_D965_3ST),
-       SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2004, "Intel D965", STAC_D965_3ST),
-       SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2003, "Intel D965", STAC_D965_3ST),
-       SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2002, "Intel D965", STAC_D965_3ST),
-       SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2001, "Intel D965", STAC_D965_3ST),
+       SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2100,
+                          "Intel D965", STAC_D965_3ST),
+       SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2000,
+                          "Intel D965", STAC_D965_3ST),
        /* Dell 3 stack systems */
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x01f7, "Dell XPS M1730", STAC_DELL_3ST),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x01dd, "Dell Dimension E520", STAC_DELL_3ST),
@@ -2135,15 +2214,10 @@ static struct snd_pci_quirk stac927x_cfg_tbl[] = {
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x02ff, "Dell     ", STAC_DELL_BIOS),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x0209, "Dell XPS 1330", STAC_DELL_BIOS),
        /* 965 based 5 stack systems */
-       SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2301, "Intel D965", STAC_D965_5ST),
-       SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2302, "Intel D965", STAC_D965_5ST),
-       SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2303, "Intel D965", STAC_D965_5ST),
-       SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2304, "Intel D965", STAC_D965_5ST),
-       SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2305, "Intel D965", STAC_D965_5ST),
-       SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2501, "Intel D965", STAC_D965_5ST),
-       SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2502, "Intel D965", STAC_D965_5ST),
-       SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2503, "Intel D965", STAC_D965_5ST),
-       SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2504, "Intel D965", STAC_D965_5ST),
+       SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2300,
+                          "Intel D965", STAC_D965_5ST),
+       SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2500,
+                          "Intel D965", STAC_D965_5ST),
        {} /* terminator */
 };
 
@@ -2196,19 +2270,25 @@ static unsigned int *stac9205_brd_tbl[STAC_9205_MODELS] = {
        [STAC_9205_DELL_M42] = dell_9205_m42_pin_configs,
        [STAC_9205_DELL_M43] = dell_9205_m43_pin_configs,
        [STAC_9205_DELL_M44] = dell_9205_m44_pin_configs,
+       [STAC_9205_EAPD] = NULL,
 };
 
 static const char *stac9205_models[STAC_9205_MODELS] = {
+       [STAC_9205_AUTO] = "auto",
        [STAC_9205_REF] = "ref",
        [STAC_9205_DELL_M42] = "dell-m42",
        [STAC_9205_DELL_M43] = "dell-m43",
        [STAC_9205_DELL_M44] = "dell-m44",
+       [STAC_9205_EAPD] = "eapd",
 };
 
 static struct snd_pci_quirk stac9205_cfg_tbl[] = {
        /* SigmaTel reference board */
        SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
                      "DFI LanParty", STAC_9205_REF),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
+                     "DFI LanParty", STAC_9205_REF),
+       /* Dell */
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f1,
                      "unknown Dell", STAC_9205_DELL_M42),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f2,
@@ -2239,101 +2319,24 @@ static struct snd_pci_quirk stac9205_cfg_tbl[] = {
                      "Dell Inspiron", STAC_9205_DELL_M44),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0228,
                      "Dell Vostro 1500", STAC_9205_DELL_M42),
+       /* Gateway */
+       SND_PCI_QUIRK(0x107b, 0x0565, "Gateway T1616", STAC_9205_EAPD),
        {} /* terminator */
 };
 
-static int stac92xx_save_bios_config_regs(struct hda_codec *codec)
-{
-       int i;
-       struct sigmatel_spec *spec = codec->spec;
-       
-       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];
-               unsigned int pin_cfg;
-               
-               pin_cfg = snd_hda_codec_read(codec, nid, 0, 
-                       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->pin_configs[i] = pin_cfg;
-       }
-       
-       return 0;
-}
-
-static void stac92xx_set_config_reg(struct hda_codec *codec,
-                                   hda_nid_t pin_nid, unsigned int pin_config)
-{
-       int i;
-       snd_hda_codec_write(codec, pin_nid, 0,
-                           AC_VERB_SET_CONFIG_DEFAULT_BYTES_0,
-                           pin_config & 0x000000ff);
-       snd_hda_codec_write(codec, pin_nid, 0,
-                           AC_VERB_SET_CONFIG_DEFAULT_BYTES_1,
-                           (pin_config & 0x0000ff00) >> 8);
-       snd_hda_codec_write(codec, pin_nid, 0,
-                           AC_VERB_SET_CONFIG_DEFAULT_BYTES_2,
-                           (pin_config & 0x00ff0000) >> 16);
-       snd_hda_codec_write(codec, pin_nid, 0,
-                           AC_VERB_SET_CONFIG_DEFAULT_BYTES_3,
-                           pin_config >> 24);
-       i = snd_hda_codec_read(codec, pin_nid, 0,
-                              AC_VERB_GET_CONFIG_DEFAULT,
-                              0x00);   
-       snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x pin config %8.8x\n",
-                   pin_nid, i);
-}
-
-static void stac92xx_set_config_regs(struct hda_codec *codec)
+static void stac92xx_set_config_regs(struct hda_codec *codec,
+                                    unsigned int *pincfgs)
 {
        int i;
        struct sigmatel_spec *spec = codec->spec;
 
-       if (!spec->pin_configs)
-               return;
+       if (!pincfgs)
+               return;
 
        for (i = 0; i < spec->num_pins; i++)
-               stac92xx_set_config_reg(codec, spec->pin_nids[i],
-                                       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;
-               }
-       }
+               if (spec->pin_nids[i] && pincfgs[i])
+                       snd_hda_codec_set_pincfg(codec, spec->pin_nids[i],
+                                                pincfgs[i]);
 }
 
 /*
@@ -2398,6 +2401,14 @@ static int stac92xx_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
                                             stream_tag, format, substream);
 }
 
+static int stac92xx_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                                       struct hda_codec *codec,
+                                       struct snd_pcm_substream *substream)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
+}
+
 
 /*
  * Analog capture callbacks
@@ -2442,7 +2453,8 @@ static struct hda_pcm_stream stac92xx_pcm_digital_playback = {
        .ops = {
                .open = stac92xx_dig_playback_pcm_open,
                .close = stac92xx_dig_playback_pcm_close,
-               .prepare = stac92xx_dig_playback_pcm_prepare
+               .prepare = stac92xx_dig_playback_pcm_prepare,
+               .cleanup = stac92xx_dig_playback_pcm_cleanup
        },
 };
 
@@ -2497,6 +2509,8 @@ static int stac92xx_build_pcms(struct hda_codec *codec)
 
        info->name = "STAC92xx Analog";
        info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_analog_playback;
+       info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
+               spec->multiout.dac_nids[0];
        info->stream[SNDRV_PCM_STREAM_CAPTURE] = stac92xx_pcm_analog_capture;
        info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
        info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adcs;
@@ -2512,7 +2526,7 @@ static int stac92xx_build_pcms(struct hda_codec *codec)
                codec->num_pcms++;
                info++;
                info->name = "STAC92xx Digital";
-               info->pcm_type = HDA_PCM_TYPE_SPDIF;
+               info->pcm_type = spec->autocfg.dig_out_type[0];
                if (spec->multiout.dig_out_nid) {
                        info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_digital_playback;
                        info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
@@ -2528,8 +2542,7 @@ static int stac92xx_build_pcms(struct hda_codec *codec)
 
 static unsigned int stac92xx_get_vref(struct hda_codec *codec, hda_nid_t nid)
 {
-       unsigned int pincap = snd_hda_param_read(codec, nid,
-                                                AC_PAR_PIN_CAP);
+       unsigned int pincap = snd_hda_query_pin_caps(codec, nid);
        pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
        if (pincap & AC_PINCAP_VREF_100)
                return AC_PINCTL_VREF_100;
@@ -2704,22 +2717,37 @@ static struct snd_kcontrol_new stac92xx_control_templates[] = {
 };
 
 /* add dynamic controls */
-static int stac92xx_add_control_temp(struct sigmatel_spec *spec,
-                                    struct snd_kcontrol_new *ktemp,
-                                    int idx, const char *name,
-                                    unsigned long val)
+static struct snd_kcontrol_new *
+stac_control_new(struct sigmatel_spec *spec,
+                struct snd_kcontrol_new *ktemp,
+                const char *name)
 {
        struct snd_kcontrol_new *knew;
 
        snd_array_init(&spec->kctls, sizeof(*knew), 32);
        knew = snd_array_new(&spec->kctls);
        if (!knew)
-               return -ENOMEM;
+               return NULL;
        *knew = *ktemp;
-       knew->index = idx;
        knew->name = kstrdup(name, GFP_KERNEL);
-       if (!knew->name)
+       if (!knew->name) {
+               /* roolback */
+               memset(knew, 0, sizeof(*knew));
+               spec->kctls.alloced--;
+               return NULL;
+       }
+       return knew;
+}
+
+static int stac92xx_add_control_temp(struct sigmatel_spec *spec,
+                                    struct snd_kcontrol_new *ktemp,
+                                    int idx, const char *name,
+                                    unsigned long val)
+{
+       struct snd_kcontrol_new *knew = stac_control_new(spec, ktemp, name);
+       if (!knew)
                return -ENOMEM;
+       knew->index = idx;
        knew->private_value = val;
        return 0;
 }
@@ -2741,70 +2769,75 @@ static inline int stac92xx_add_control(struct sigmatel_spec *spec, int type,
        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)
+static struct snd_kcontrol_new stac_input_src_temp = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "Input Source",
+       .info = stac92xx_mux_enum_info,
+       .get = stac92xx_mux_enum_get,
+       .put = stac92xx_mux_enum_put,
+};
+
+static int stac92xx_add_input_source(struct sigmatel_spec *spec)
+{
+       struct snd_kcontrol_new *knew;
+       struct hda_input_mux *imux = &spec->private_imux;
+
+       if (!spec->num_adcs || imux->num_items <= 1)
+               return 0; /* no need for input source control */
+       knew = stac_control_new(spec, &stac_input_src_temp,
+                               stac_input_src_temp.name);
+       if (!knew)
+               return -ENOMEM;
+       knew->count = spec->num_adcs;
+       return 0;
+}
+
+/* check whether the line-input can be used as line-out */
+static hda_nid_t check_line_out_switch(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec = codec->spec;
-       unsigned int wcaps, wtype;
-       int i, num_dacs = 0;
-       
-       /* use the wcaps cache to count all DACs available for line-outs */
-       for (i = 0; i < codec->num_nodes; i++) {
-               wcaps = codec->wcaps[i];
-               wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       hda_nid_t nid;
+       unsigned int pincap;
 
-               if (wtype == AC_WID_AUD_OUT && !(wcaps & AC_WCAP_DIGITAL))
-                       num_dacs++;
-       }
+       if (cfg->line_out_type != AUTO_PIN_LINE_OUT)
+               return 0;
+       nid = cfg->input_pins[AUTO_PIN_LINE];
+       pincap = snd_hda_query_pin_caps(codec, nid);
+       if (pincap & AC_PINCAP_OUT)
+               return nid;
+       return 0;
+}
 
-       snd_printdd("%s: total dac count=%d\n", __func__, num_dacs);
-       
-       switch (cfg->line_outs) {
-       case 3:
-               /* add line-in as side */
-               if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 3) {
-                       cfg->line_out_pins[cfg->line_outs] =
-                               cfg->input_pins[AUTO_PIN_LINE];
-                       spec->line_switch = 1;
-                       cfg->line_outs++;
-               }
-               break;
-       case 2:
-               /* add line-in as clfe and mic as side */
-               if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 2) {
-                       cfg->line_out_pins[cfg->line_outs] =
-                               cfg->input_pins[AUTO_PIN_LINE];
-                       spec->line_switch = 1;
-                       cfg->line_outs++;
-               }
-               if (cfg->input_pins[AUTO_PIN_MIC] && num_dacs > 3) {
-                       cfg->line_out_pins[cfg->line_outs] =
-                               cfg->input_pins[AUTO_PIN_MIC];
-                       spec->mic_switch = 1;
-                       cfg->line_outs++;
-               }
-               break;
-       case 1:
-               /* add line-in as surr and mic as clfe */
-               if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 1) {
-                       cfg->line_out_pins[cfg->line_outs] =
-                               cfg->input_pins[AUTO_PIN_LINE];
-                       spec->line_switch = 1;
-                       cfg->line_outs++;
-               }
-               if (cfg->input_pins[AUTO_PIN_MIC] && num_dacs > 2) {
-                       cfg->line_out_pins[cfg->line_outs] =
-                               cfg->input_pins[AUTO_PIN_MIC];
-                       spec->mic_switch = 1;
-                       cfg->line_outs++;
+/* check whether the mic-input can be used as line-out */
+static hda_nid_t check_mic_out_switch(struct hda_codec *codec)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       unsigned int def_conf, pincap;
+       unsigned int mic_pin;
+
+       if (cfg->line_out_type != AUTO_PIN_LINE_OUT)
+               return 0;
+       mic_pin = AUTO_PIN_MIC;
+       for (;;) {
+               hda_nid_t nid = cfg->input_pins[mic_pin];
+               def_conf = snd_hda_codec_get_pincfg(codec, nid);
+               /* some laptops have an internal analog microphone
+                * which can't be used as a output */
+               if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED) {
+                       pincap = snd_hda_query_pin_caps(codec, nid);
+                       if (pincap & AC_PINCAP_OUT)
+                               return nid;
                }
-               break;
+               if (mic_pin == AUTO_PIN_MIC)
+                       mic_pin = AUTO_PIN_FRONT_MIC;
+               else
+                       break;
        }
-
        return 0;
 }
 
-
 static int is_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
 {
        int i;
@@ -2817,6 +2850,61 @@ static int is_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
        return 0;
 }
 
+static int check_all_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
+{
+       int i;
+       if (is_in_dac_nids(spec, nid))
+               return 1;
+       for (i = 0; i < spec->autocfg.hp_outs; i++)
+               if (spec->hp_dacs[i] == nid)
+                       return 1;
+       for (i = 0; i < spec->autocfg.speaker_outs; i++)
+               if (spec->speaker_dacs[i] == nid)
+                       return 1;
+       return 0;
+}
+
+static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t nid)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       int j, conn_len;
+       hda_nid_t conn[HDA_MAX_CONNECTIONS];
+       unsigned int wcaps, wtype;
+
+       conn_len = snd_hda_get_connections(codec, nid, conn,
+                                          HDA_MAX_CONNECTIONS);
+       for (j = 0; j < conn_len; j++) {
+               wcaps = get_wcaps(codec, conn[j]);
+               wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+               /* we check only analog outputs */
+               if (wtype != AC_WID_AUD_OUT || (wcaps & AC_WCAP_DIGITAL))
+                       continue;
+               /* if this route has a free DAC, assign it */
+               if (!check_all_dac_nids(spec, conn[j])) {
+                       if (conn_len > 1) {
+                               /* select this DAC in the pin's input mux */
+                               snd_hda_codec_write_cache(codec, nid, 0,
+                                                 AC_VERB_SET_CONNECT_SEL, j);
+                       }
+                       return conn[j];
+               }
+       }
+       /* if all DACs are already assigned, connect to the primary DAC */
+       if (conn_len > 1) {
+               for (j = 0; j < conn_len; j++) {
+                       if (conn[j] == spec->multiout.dac_nids[0]) {
+                               snd_hda_codec_write_cache(codec, nid, 0,
+                                                 AC_VERB_SET_CONNECT_SEL, j);
+                               break;
+                       }
+               }
+       }
+       return 0;
+}
+
+static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid);
+static int add_spec_extra_dacs(struct sigmatel_spec *spec, hda_nid_t nid);
+
 /*
  * Fill in the dac_nids table from the parsed pin configuration
  * This function only works when every pin in line_out_pins[]
@@ -2824,31 +2912,17 @@ static int is_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
  * codecs are not connected directly to a DAC, such as the 9200
  * and 9202/925x. For those, dac_nids[] must be hard-coded.
  */
-static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec,
-                                      struct auto_pin_cfg *cfg)
+static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec = codec->spec;
-       int i, j, conn_len = 0; 
-       hda_nid_t nid, conn[HDA_MAX_CONNECTIONS];
-       unsigned int wcaps, wtype;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       int i;
+       hda_nid_t nid, dac;
        
        for (i = 0; i < cfg->line_outs; i++) {
                nid = cfg->line_out_pins[i];
-               conn_len = snd_hda_get_connections(codec, nid, conn,
-                                                  HDA_MAX_CONNECTIONS);
-               for (j = 0; j < conn_len; j++) {
-                       wcaps = snd_hda_param_read(codec, conn[j],
-                                                  AC_PAR_AUDIO_WIDGET_CAP);
-                       wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
-                       if (wtype != AC_WID_AUD_OUT ||
-                           (wcaps & AC_WCAP_DIGITAL))
-                               continue;
-                       /* conn[j] is a DAC routed to this line-out */
-                       if (!is_in_dac_nids(spec, conn[j]))
-                               break;
-               }
-
-               if (j == conn_len) {
+               dac = get_unassigned_dac(codec, nid);
+               if (!dac) {
                        if (spec->multiout.num_dacs > 0) {
                                /* we have already working output pins,
                                 * so let's drop the broken ones again
@@ -2862,51 +2936,112 @@ static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec,
                                   __func__, nid);
                        return -ENODEV;
                }
+               add_spec_dacs(spec, dac);
+       }
 
-               spec->multiout.dac_nids[i] = conn[j];
-               spec->multiout.num_dacs++;
-               if (conn_len > 1) {
-                       /* select this DAC in the pin's input mux */
-                       snd_hda_codec_write_cache(codec, nid, 0,
-                                                 AC_VERB_SET_CONNECT_SEL, j);
-
+       for (i = 0; i < cfg->hp_outs; i++) {
+               nid = cfg->hp_pins[i];
+               dac = get_unassigned_dac(codec, nid);
+               if (dac) {
+                       if (!spec->multiout.hp_nid)
+                               spec->multiout.hp_nid = dac;
+                       else
+                               add_spec_extra_dacs(spec, dac);
                }
+               spec->hp_dacs[i] = dac;
        }
 
-       snd_printd("dac_nids=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
-                  spec->multiout.num_dacs,
+       for (i = 0; i < cfg->speaker_outs; i++) {
+               nid = cfg->speaker_pins[i];
+               dac = get_unassigned_dac(codec, nid);
+               if (dac)
+                       add_spec_extra_dacs(spec, dac);
+               spec->speaker_dacs[i] = dac;
+       }
+
+       /* add line-in as output */
+       nid = check_line_out_switch(codec);
+       if (nid) {
+               dac = get_unassigned_dac(codec, nid);
+               if (dac) {
+                       snd_printdd("STAC: Add line-in 0x%x as output %d\n",
+                                   nid, cfg->line_outs);
+                       cfg->line_out_pins[cfg->line_outs] = nid;
+                       cfg->line_outs++;
+                       spec->line_switch = nid;
+                       add_spec_dacs(spec, dac);
+               }
+       }
+       /* add mic as output */
+       nid = check_mic_out_switch(codec);
+       if (nid) {
+               dac = get_unassigned_dac(codec, nid);
+               if (dac) {
+                       snd_printdd("STAC: Add mic-in 0x%x as output %d\n",
+                                   nid, cfg->line_outs);
+                       cfg->line_out_pins[cfg->line_outs] = nid;
+                       cfg->line_outs++;
+                       spec->mic_switch = nid;
+                       add_spec_dacs(spec, dac);
+               }
+       }
+
+       snd_printd("stac92xx: dac_nids=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
+                  spec->multiout.num_dacs,
                   spec->multiout.dac_nids[0],
                   spec->multiout.dac_nids[1],
                   spec->multiout.dac_nids[2],
                   spec->multiout.dac_nids[3],
                   spec->multiout.dac_nids[4]);
+
        return 0;
 }
 
 /* create volume control/switch for the given prefx type */
-static int create_controls(struct sigmatel_spec *spec, const char *pfx, hda_nid_t nid, int chs)
+static int create_controls_idx(struct hda_codec *codec, const char *pfx,
+                              int idx, hda_nid_t nid, int chs)
 {
+       struct sigmatel_spec *spec = codec->spec;
        char name[32];
        int err;
 
+       if (!spec->check_volume_offset) {
+               unsigned int caps, step, nums, db_scale;
+               caps = query_amp_caps(codec, nid, HDA_OUTPUT);
+               step = (caps & AC_AMPCAP_STEP_SIZE) >>
+                       AC_AMPCAP_STEP_SIZE_SHIFT;
+               step = (step + 1) * 25; /* in .01dB unit */
+               nums = (caps & AC_AMPCAP_NUM_STEPS) >>
+                       AC_AMPCAP_NUM_STEPS_SHIFT;
+               db_scale = nums * step;
+               /* if dB scale is over -64dB, and finer enough,
+                * let's reduce it to half
+                */
+               if (db_scale > 6400 && nums >= 0x1f)
+                       spec->volume_offset = nums / 2;
+               spec->check_volume_offset = 1;
+       }
+
        sprintf(name, "%s Playback Volume", pfx);
-       err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, name,
-                                  HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
+       err = stac92xx_add_control_idx(spec, STAC_CTL_WIDGET_VOL, idx, name,
+               HDA_COMPOSE_AMP_VAL_OFS(nid, chs, 0, HDA_OUTPUT,
+                                       spec->volume_offset));
        if (err < 0)
                return err;
        sprintf(name, "%s Playback Switch", pfx);
-       err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, name,
+       err = stac92xx_add_control_idx(spec, STAC_CTL_WIDGET_MUTE, idx, name,
                                   HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
        if (err < 0)
                return err;
        return 0;
 }
 
+#define create_controls(codec, pfx, nid, chs) \
+       create_controls_idx(codec, pfx, 0, nid, chs)
+
 static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid)
 {
-       if (!spec->multiout.hp_nid)
-               spec->multiout.hp_nid = nid;
-       else if (spec->multiout.num_dacs > 4) {
+       if (spec->multiout.num_dacs > 4) {
                printk(KERN_WARNING "stac92xx: No space for DAC 0x%x\n", nid);
                return 1;
        } else {
@@ -2916,41 +3051,50 @@ static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid)
        return 0;
 }
 
-static int check_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
+static int add_spec_extra_dacs(struct sigmatel_spec *spec, hda_nid_t nid)
 {
-       if (is_in_dac_nids(spec, nid))
-               return 1;
-       if (spec->multiout.hp_nid == nid)
-               return 1;
-       return 0;
+       int i;
+       for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++) {
+               if (!spec->multiout.extra_out_nid[i]) {
+                       spec->multiout.extra_out_nid[i] = nid;
+                       return 0;
+               }
+       }
+       printk(KERN_WARNING "stac92xx: No space for extra DAC 0x%x\n", nid);
+       return 1;
 }
 
-/* add playback controls from the parsed DAC table */
-static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
-                                              const struct auto_pin_cfg *cfg)
+/* Create output controls
+ * The mixer elements are named depending on the given type (AUTO_PIN_XXX_OUT)
+ */
+static int create_multi_out_ctls(struct hda_codec *codec, int num_outs,
+                                const hda_nid_t *pins,
+                                const hda_nid_t *dac_nids,
+                                int type)
 {
+       struct sigmatel_spec *spec = codec->spec;
        static const char *chname[4] = {
                "Front", "Surround", NULL /*CLFE*/, "Side"
        };
-       hda_nid_t nid = 0;
+       hda_nid_t nid;
        int i, err;
+       unsigned int wid_caps;
 
-       struct sigmatel_spec *spec = codec->spec;
-       unsigned int wid_caps, pincap;
-
-
-       for (i = 0; i < cfg->line_outs && i < spec->multiout.num_dacs; i++) {
-               if (!spec->multiout.dac_nids[i])
+       for (i = 0; i < num_outs && i < ARRAY_SIZE(chname); i++) {
+               if (type == AUTO_PIN_HP_OUT && !spec->hp_detect) {
+                       wid_caps = get_wcaps(codec, pins[i]);
+                       if (wid_caps & AC_WCAP_UNSOL_CAP)
+                               spec->hp_detect = 1;
+               }
+               nid = dac_nids[i];
+               if (!nid)
                        continue;
-
-               nid = spec->multiout.dac_nids[i];
-
-               if (i == 2) {
+               if (type != AUTO_PIN_HP_OUT && i == 2) {
                        /* Center/LFE */
-                       err = create_controls(spec, "Center", nid, 1);
+                       err = create_controls(codec, "Center", nid, 1);
                        if (err < 0)
                                return err;
-                       err = create_controls(spec, "LFE", nid, 2);
+                       err = create_controls(codec, "LFE", nid, 2);
                        if (err < 0)
                                return err;
 
@@ -2966,15 +3110,42 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
                        }
 
                } else {
-                       err = create_controls(spec, chname[i], nid, 3);
+                       const char *name;
+                       int idx;
+                       switch (type) {
+                       case AUTO_PIN_HP_OUT:
+                               name = "Headphone";
+                               idx = i;
+                               break;
+                       case AUTO_PIN_SPEAKER_OUT:
+                               name = "Speaker";
+                               idx = i;
+                               break;
+                       default:
+                               name = chname[i];
+                               idx = 0;
+                               break;
+                       }
+                       err = create_controls_idx(codec, name, idx, nid, 3);
                        if (err < 0)
                                return err;
                }
        }
+       return 0;
+}
 
-       if ((spec->multiout.num_dacs - cfg->line_outs) > 0 &&
-                       cfg->hp_outs && !spec->multiout.hp_nid)
-               spec->multiout.hp_nid = nid;
+/* add playback controls from the parsed DAC table */
+static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
+                                              const struct auto_pin_cfg *cfg)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       int err;
+
+       err = create_multi_out_ctls(codec, cfg->line_outs, cfg->line_out_pins,
+                                   spec->multiout.dac_nids,
+                                   cfg->line_out_type);
+       if (err < 0)
+               return err;
 
        if (cfg->hp_outs > 1 && cfg->line_out_type == AUTO_PIN_LINE_OUT) {
                err = stac92xx_add_control(spec,
@@ -2986,45 +3157,19 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
        }
 
        if (spec->line_switch) {
-               nid = cfg->input_pins[AUTO_PIN_LINE];
-               pincap = snd_hda_param_read(codec, nid,
-                                               AC_PAR_PIN_CAP);
-               if (pincap & AC_PINCAP_OUT) {
-                       err = stac92xx_add_control(spec,
-                               STAC_CTL_WIDGET_IO_SWITCH,
-                               "Line In as Output Switch", nid << 8);
-                       if (err < 0)
-                               return err;
-               }
+               err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH,
+                                          "Line In as Output Switch",
+                                          spec->line_switch << 8);
+               if (err < 0)
+                       return err;
        }
 
        if (spec->mic_switch) {
-               unsigned int def_conf;
-               unsigned int mic_pin = AUTO_PIN_MIC;
-again:
-               nid = cfg->input_pins[mic_pin];
-               def_conf = snd_hda_codec_read(codec, nid, 0,
-                                               AC_VERB_GET_CONFIG_DEFAULT, 0);
-               /* some laptops have an internal analog microphone
-                * which can't be used as a output */
-               if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED) {
-                       pincap = snd_hda_param_read(codec, nid,
-                                                       AC_PAR_PIN_CAP);
-                       if (pincap & AC_PINCAP_OUT) {
-                               err = stac92xx_add_control(spec,
-                                       STAC_CTL_WIDGET_IO_SWITCH,
-                                       "Mic as Output Switch", (nid << 8) | 1);
-                               nid = snd_hda_codec_read(codec, nid, 0,
-                                        AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
-                               if (!check_in_dac_nids(spec, nid))
-                                       add_spec_dacs(spec, nid);
-                               if (err < 0)
-                                       return err;
-                       }
-               } else if (mic_pin == AUTO_PIN_MIC) {
-                       mic_pin = AUTO_PIN_FRONT_MIC;
-                       goto again;
-               }
+               err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH,
+                                          "Mic as Output Switch",
+                                          (spec->mic_switch << 8) | 1);
+               if (err < 0)
+                       return err;
        }
 
        return 0;
@@ -3035,55 +3180,17 @@ static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec,
                                        struct auto_pin_cfg *cfg)
 {
        struct sigmatel_spec *spec = codec->spec;
-       hda_nid_t nid;
-       int i, old_num_dacs, err;
+       int err;
 
-       old_num_dacs = spec->multiout.num_dacs;
-       for (i = 0; i < cfg->hp_outs; i++) {
-               unsigned int wid_caps = get_wcaps(codec, cfg->hp_pins[i]);
-               if (wid_caps & AC_WCAP_UNSOL_CAP)
-                       spec->hp_detect = 1;
-               nid = snd_hda_codec_read(codec, cfg->hp_pins[i], 0,
-                                        AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
-               if (check_in_dac_nids(spec, nid))
-                       nid = 0;
-               if (! nid)
-                       continue;
-               add_spec_dacs(spec, nid);
-       }
-       for (i = 0; i < cfg->speaker_outs; i++) {
-               nid = snd_hda_codec_read(codec, cfg->speaker_pins[i], 0,
-                                        AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
-               if (check_in_dac_nids(spec, nid))
-                       nid = 0;
-               if (! nid)
-                       continue;
-               add_spec_dacs(spec, nid);
-       }
-       for (i = 0; i < cfg->line_outs; i++) {
-               nid = snd_hda_codec_read(codec, cfg->line_out_pins[i], 0,
-                                       AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
-               if (check_in_dac_nids(spec, nid))
-                       nid = 0;
-               if (! nid)
-                       continue;
-               add_spec_dacs(spec, nid);
-       }
-       for (i = old_num_dacs; i < spec->multiout.num_dacs; i++) {
-               static const char *pfxs[] = {
-                       "Speaker", "External Speaker", "Speaker2",
-               };
-               err = create_controls(spec, pfxs[i - old_num_dacs],
-                                     spec->multiout.dac_nids[i], 3);
-               if (err < 0)
-                       return err;
-       }
-       if (spec->multiout.hp_nid) {
-               err = create_controls(spec, "Headphone",
-                                     spec->multiout.hp_nid, 3);
-               if (err < 0)
-                       return err;
-       }
+       err = create_multi_out_ctls(codec, cfg->hp_outs, cfg->hp_pins,
+                                   spec->hp_dacs, AUTO_PIN_HP_OUT);
+       if (err < 0)
+               return err;
+
+       err = create_multi_out_ctls(codec, cfg->speaker_outs, cfg->speaker_pins,
+                                   spec->speaker_dacs, AUTO_PIN_SPEAKER_OUT);
+       if (err < 0)
+               return err;
 
        return 0;
 }
@@ -3293,11 +3400,7 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
                unsigned int wcaps;
                unsigned int def_conf;
 
-               def_conf = snd_hda_codec_read(codec,
-                                             spec->dmic_nids[i],
-                                             0,
-                                             AC_VERB_GET_CONFIG_DEFAULT,
-                                             0);
+               def_conf = snd_hda_codec_get_pincfg(codec, spec->dmic_nids[i]);
                if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
                        continue;
 
@@ -3421,8 +3524,8 @@ static void stac92xx_auto_init_hp_out(struct hda_codec *codec)
 static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out, hda_nid_t dig_in)
 {
        struct sigmatel_spec *spec = codec->spec;
+       int hp_swap = 0;
        int err;
-       int hp_speaker_swap = 0;
 
        if ((err = snd_hda_parse_pin_def_config(codec,
                                                &spec->autocfg,
@@ -3440,13 +3543,16 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
                 * speaker_outs so that the following routines can handle
                 * HP pins as primary outputs.
                 */
+               snd_printdd("stac92xx: Enabling multi-HPs workaround\n");
                memcpy(spec->autocfg.speaker_pins, spec->autocfg.line_out_pins,
                       sizeof(spec->autocfg.line_out_pins));
                spec->autocfg.speaker_outs = spec->autocfg.line_outs;
                memcpy(spec->autocfg.line_out_pins, spec->autocfg.hp_pins,
                       sizeof(spec->autocfg.hp_pins));
                spec->autocfg.line_outs = spec->autocfg.hp_outs;
-               hp_speaker_swap = 1;
+               spec->autocfg.line_out_type = AUTO_PIN_HP_OUT;
+               spec->autocfg.hp_outs = 0;
+               hp_swap = 1;
        }
        if (spec->autocfg.mono_out_pin) {
                int dir = get_wcaps(codec, spec->autocfg.mono_out_pin) &
@@ -3498,16 +3604,15 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
                                         AC_PINCTL_OUT_EN);
        }
 
-       if ((err = stac92xx_add_dyn_out_pins(codec, &spec->autocfg)) < 0)
-               return err;
-       if (spec->multiout.num_dacs == 0)
-               if ((err = stac92xx_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
+       if (!spec->multiout.num_dacs) {
+               err = stac92xx_auto_fill_dac_nids(codec);
+               if (err < 0)
                        return err;
-
-       err = stac92xx_auto_create_multi_out_ctls(codec, &spec->autocfg);
-
-       if (err < 0)
-               return err;
+               err = stac92xx_auto_create_multi_out_ctls(codec,
+                                                         &spec->autocfg);
+               if (err < 0)
+                       return err;
+       }
 
        /* setup analog beep controls */
        if (spec->anabeep_nid > 0) {
@@ -3540,26 +3645,20 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
        }
 #endif
 
-       if (hp_speaker_swap == 1) {
-               /* Restore the hp_outs and line_outs */
-               memcpy(spec->autocfg.hp_pins, spec->autocfg.line_out_pins,
-                      sizeof(spec->autocfg.line_out_pins));
-               spec->autocfg.hp_outs = spec->autocfg.line_outs;
-               memcpy(spec->autocfg.line_out_pins, spec->autocfg.speaker_pins,
-                      sizeof(spec->autocfg.speaker_pins));
-               spec->autocfg.line_outs = spec->autocfg.speaker_outs;
-               memset(spec->autocfg.speaker_pins, 0,
-                      sizeof(spec->autocfg.speaker_pins));
-               spec->autocfg.speaker_outs = 0;
-       }
-
        err = stac92xx_auto_create_hp_ctls(codec, &spec->autocfg);
-
        if (err < 0)
                return err;
 
-       err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg);
+       /* All output parsing done, now restore the swapped hp pins */
+       if (hp_swap) {
+               memcpy(spec->autocfg.hp_pins, spec->autocfg.line_out_pins,
+                      sizeof(spec->autocfg.hp_pins));
+               spec->autocfg.hp_outs = spec->autocfg.line_outs;
+               spec->autocfg.line_out_type = AUTO_PIN_HP_OUT;
+               spec->autocfg.line_outs = 0;
+       }
 
+       err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg);
        if (err < 0)
                return err;
 
@@ -3588,11 +3687,15 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
                        return err;
        }
 
+       err = stac92xx_add_input_source(spec);
+       if (err < 0)
+               return err;
+
        spec->multiout.max_channels = spec->multiout.num_dacs * 2;
        if (spec->multiout.max_channels > 2)
                spec->surr_switch = 1;
 
-       if (spec->autocfg.dig_out_pin)
+       if (spec->autocfg.dig_outs)
                spec->multiout.dig_out_nid = dig_out;
        if (dig_in && spec->autocfg.dig_in_pin)
                spec->dig_in_nid = dig_in;
@@ -3601,7 +3704,8 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
                spec->mixers[spec->num_mixers++] = spec->kctls.list;
 
        spec->input_mux = &spec->private_imux;
-       spec->dinput_mux = &spec->private_dimux;
+       if (!spec->dinput_mux)
+               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;
@@ -3654,9 +3758,7 @@ static int stac9200_auto_create_lfe_ctls(struct hda_codec *codec,
                for (i = 0; i < spec->autocfg.line_outs && lfe_pin == 0x0; i++) {
                        hda_nid_t pin = spec->autocfg.line_out_pins[i];
                        unsigned int defcfg;
-                       defcfg = snd_hda_codec_read(codec, pin, 0,
-                                                AC_VERB_GET_CONFIG_DEFAULT,
-                                                0x00);
+                       defcfg = snd_hda_codec_get_pincfg(codec, pin);
                        if (get_defcfg_device(defcfg) == AC_JACK_SPEAKER) {
                                unsigned int wcaps = get_wcaps(codec, pin);
                                wcaps &= (AC_WCAP_STEREO | AC_WCAP_OUT_AMP);
@@ -3669,7 +3771,7 @@ static int stac9200_auto_create_lfe_ctls(struct hda_codec *codec,
        }
 
        if (lfe_pin) {
-               err = create_controls(spec, "LFE", lfe_pin, 1);
+               err = create_controls(codec, "LFE", lfe_pin, 1);
                if (err < 0)
                        return err;
        }
@@ -3700,7 +3802,11 @@ static int stac9200_parse_auto_config(struct hda_codec *codec)
                        return err;
        }
 
-       if (spec->autocfg.dig_out_pin)
+       err = stac92xx_add_input_source(spec);
+       if (err < 0)
+               return err;
+
+       if (spec->autocfg.dig_outs)
                spec->multiout.dig_out_nid = 0x05;
        if (spec->autocfg.dig_in_pin)
                spec->dig_in_nid = 0x04;
@@ -3750,16 +3856,25 @@ static void stac_gpio_set(struct hda_codec *codec, unsigned int mask,
                           AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */
 }
 
+#ifdef CONFIG_SND_JACK
+static void stac92xx_free_jack_priv(struct snd_jack *jack)
+{
+       struct sigmatel_jack *jacks = jack->private_data;
+       jacks->nid = 0;
+       jacks->jack = NULL;
+}
+#endif
+
 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 def_conf = snd_hda_codec_get_pincfg(codec, nid);
        int connectivity = get_defcfg_connect(def_conf);
        char name[32];
+       int err;
 
        if (connectivity && connectivity != AC_JACK_PORT_FIXED)
                return 0;
@@ -3776,10 +3891,15 @@ static int stac92xx_add_jack(struct hda_codec *codec,
                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;
+       err = snd_jack_new(codec->bus->card, name, type, &jack->jack);
+       if (err < 0) {
+               jack->nid = 0;
+               return err;
+       }
+       jack->jack->private_data = jack;
+       jack->jack->private_free = stac92xx_free_jack_priv;
 #endif
+       return 0;
 }
 
 static int stac_add_event(struct sigmatel_spec *spec, hda_nid_t nid,
@@ -3864,8 +3984,7 @@ static void stac92xx_power_down(struct hda_codec *codec)
        /* power down inactive DACs */
        hda_nid_t *dac;
        for (dac = spec->dac_list; *dac; dac++)
-               if (!is_in_dac_nids(spec, *dac) &&
-                       spec->multiout.hp_nid != *dac)
+               if (!check_all_dac_nids(spec, *dac))
                        snd_hda_codec_write(codec, *dac, 0,
                                        AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
 }
@@ -3873,6 +3992,36 @@ static void stac92xx_power_down(struct hda_codec *codec)
 static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid,
                                  int enable);
 
+/* override some hints from the hwdep entry */
+static void stac_store_hints(struct hda_codec *codec)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       const char *p;
+       int val;
+
+       val = snd_hda_get_bool_hint(codec, "hp_detect");
+       if (val >= 0)
+               spec->hp_detect = val;
+       p = snd_hda_get_hint(codec, "gpio_mask");
+       if (p) {
+               spec->gpio_mask = simple_strtoul(p, NULL, 0);
+               spec->eapd_mask = spec->gpio_dir = spec->gpio_data =
+                       spec->gpio_mask;
+       }
+       p = snd_hda_get_hint(codec, "gpio_dir");
+       if (p)
+               spec->gpio_dir = simple_strtoul(p, NULL, 0) & spec->gpio_mask;
+       p = snd_hda_get_hint(codec, "gpio_data");
+       if (p)
+               spec->gpio_data = simple_strtoul(p, NULL, 0) & spec->gpio_mask;
+       p = snd_hda_get_hint(codec, "eapd_mask");
+       if (p)
+               spec->eapd_mask = simple_strtoul(p, NULL, 0) & spec->gpio_mask;
+       val = snd_hda_get_bool_hint(codec, "eapd_switch");
+       if (val >= 0)
+               spec->eapd_switch = val;
+}
+
 static int stac92xx_init(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec = codec->spec;
@@ -3889,6 +4038,9 @@ static int stac92xx_init(struct hda_codec *codec)
                                spec->adc_nids[i], 0,
                                AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
 
+       /* override some hints */
+       stac_store_hints(codec);
+
        /* set up GPIO */
        gpio = spec->gpio_data;
        /* turn on EAPD statically when spec->eapd_switch isn't set.
@@ -3916,31 +4068,42 @@ static int stac92xx_init(struct hda_codec *codec)
        } else {
                stac92xx_auto_init_multi_out(codec);
                stac92xx_auto_init_hp_out(codec);
+               for (i = 0; i < cfg->hp_outs; i++)
+                       stac_toggle_power_map(codec, cfg->hp_pins[i], 1);
        }
        for (i = 0; i < AUTO_PIN_LAST; i++) {
                hda_nid_t nid = cfg->input_pins[i];
                if (nid) {
-                       unsigned int pinctl;
+                       unsigned int pinctl, conf;
                        if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC) {
                                /* for mic pins, force to initialize */
                                pinctl = stac92xx_get_vref(codec, nid);
+                               pinctl |= AC_PINCTL_IN_EN;
+                               stac92xx_auto_set_pinctl(codec, nid, pinctl);
                        } else {
                                pinctl = snd_hda_codec_read(codec, nid, 0,
                                        AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
                                /* if PINCTL already set then skip */
-                               if (pinctl & AC_PINCTL_IN_EN)
-                                       continue;
+                               if (!(pinctl & AC_PINCTL_IN_EN)) {
+                                       pinctl |= AC_PINCTL_IN_EN;
+                                       stac92xx_auto_set_pinctl(codec, nid,
+                                                                pinctl);
+                               }
+                       }
+                       conf = snd_hda_codec_get_pincfg(codec, nid);
+                       if (get_defcfg_connect(conf) != AC_JACK_PORT_FIXED) {
+                               enable_pin_detect(codec, nid,
+                                                 STAC_INSERT_EVENT);
+                               stac_issue_unsol_event(codec, nid,
+                                                      STAC_INSERT_EVENT);
                        }
-                       pinctl |= AC_PINCTL_IN_EN;
-                       stac92xx_auto_set_pinctl(codec, nid, pinctl);
-                       enable_pin_detect(codec, nid, STAC_INSERT_EVENT);
                }
        }
        for (i = 0; i < spec->num_dmics; i++)
                stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i],
                                        AC_PINCTL_IN_EN);
-       if (cfg->dig_out_pin)
-               stac92xx_auto_set_pinctl(codec, cfg->dig_out_pin,
+       if (cfg->dig_out_pins[0])
+               stac92xx_auto_set_pinctl(codec, cfg->dig_out_pins[0],
                                         AC_PINCTL_OUT_EN);
        if (cfg->dig_in_pin)
                stac92xx_auto_set_pinctl(codec, cfg->dig_in_pin,
@@ -3949,7 +4112,13 @@ static int stac92xx_init(struct hda_codec *codec)
                hda_nid_t nid = spec->pwr_nids[i];
                int pinctl, def_conf;
 
-               if (is_nid_hp_pin(cfg, nid) && spec->hp_detect)
+               /* power on when no jack detection is available */
+               if (!spec->hp_detect) {
+                       stac_toggle_power_map(codec, nid, 1);
+                       continue;
+               }
+
+               if (is_nid_hp_pin(cfg, nid))
                        continue; /* already has an unsol event */
 
                pinctl = snd_hda_codec_read(codec, nid, 0,
@@ -3958,10 +4127,11 @@ static int stac92xx_init(struct hda_codec *codec)
                 * any attempts on powering down a input port cause the
                 * referenced VREF to act quirky.
                 */
-               if (pinctl & AC_PINCTL_IN_EN)
+               if (pinctl & AC_PINCTL_IN_EN) {
+                       stac_toggle_power_map(codec, nid, 1);
                        continue;
-               def_conf = snd_hda_codec_read(codec, nid, 0,
-                                             AC_VERB_GET_CONFIG_DEFAULT, 0);
+               }
+               def_conf = snd_hda_codec_get_pincfg(codec, nid);
                def_conf = get_defcfg_connect(def_conf);
                /* skip any ports that don't have jacks since presence
                 * detection is useless */
@@ -3970,8 +4140,10 @@ static int stac92xx_init(struct hda_codec *codec)
                                stac_toggle_power_map(codec, nid, 1);
                        continue;
                }
-               enable_pin_detect(codec, nid, STAC_PWR_EVENT);
-               stac_issue_unsol_event(codec, nid, STAC_PWR_EVENT);
+               if (!stac_get_event(codec, nid, STAC_INSERT_EVENT)) {
+                       enable_pin_detect(codec, nid, STAC_PWR_EVENT);
+                       stac_issue_unsol_event(codec, nid, STAC_PWR_EVENT);
+               }
        }
        if (spec->dac_list)
                stac92xx_power_down(codec);
@@ -3986,8 +4158,10 @@ static void stac92xx_free_jacks(struct hda_codec *codec)
        if (!codec->bus->shutdown && spec->jacks.list) {
                struct sigmatel_jack *jacks = spec->jacks.list;
                int i;
-               for (i = 0; i < spec->jacks.used; i++)
-                       snd_device_free(codec->bus->card, &jacks[i].jack);
+               for (i = 0; i < spec->jacks.used; i++, jacks++) {
+                       if (jacks->jack)
+                               snd_device_free(codec->bus->card, jacks->jack);
+               }
        }
        snd_array_free(&spec->jacks);
 #endif
@@ -4013,7 +4187,6 @@ static void stac92xx_free(struct hda_codec *codec)
        if (! spec)
                return;
 
-       kfree(spec->pin_configs);
        stac92xx_free_jacks(codec);
        snd_array_free(&spec->events);
 
@@ -4024,7 +4197,9 @@ static void stac92xx_free(struct hda_codec *codec)
 static void stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid,
                                unsigned int flag)
 {
-       unsigned int pin_ctl = snd_hda_codec_read(codec, nid,
+       unsigned int old_ctl, pin_ctl;
+
+       pin_ctl = snd_hda_codec_read(codec, nid,
                        0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00);
 
        if (pin_ctl & AC_PINCTL_IN_EN) {
@@ -4034,22 +4209,21 @@ static void stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid,
                 * "xxx as Output" mixer switch
                 */
                struct sigmatel_spec *spec = codec->spec;
-               struct auto_pin_cfg *cfg = &spec->autocfg;
-               if ((nid == cfg->input_pins[AUTO_PIN_LINE] &&
-                    spec->line_switch) ||
-                   (nid == cfg->input_pins[AUTO_PIN_MIC] &&
-                    spec->mic_switch))
+               if (nid == spec->line_switch || nid == spec->mic_switch)
                        return;
        }
 
+       old_ctl = pin_ctl;
        /* if setting pin direction bits, clear the current
           direction bits first */
        if (flag & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN))
                pin_ctl &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
        
-       snd_hda_codec_write_cache(codec, nid, 0,
-                       AC_VERB_SET_PIN_WIDGET_CONTROL,
-                       pin_ctl | flag);
+       pin_ctl |= flag;
+       if (old_ctl != pin_ctl)
+               snd_hda_codec_write_cache(codec, nid, 0,
+                                         AC_VERB_SET_PIN_WIDGET_CONTROL,
+                                         pin_ctl);
 }
 
 static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid,
@@ -4057,25 +4231,19 @@ static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid,
 {
        unsigned int pin_ctl = snd_hda_codec_read(codec, nid,
                        0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00);
-       snd_hda_codec_write_cache(codec, nid, 0,
-                       AC_VERB_SET_PIN_WIDGET_CONTROL,
-                       pin_ctl & ~flag);
+       if (pin_ctl & flag)
+               snd_hda_codec_write_cache(codec, nid, 0,
+                                         AC_VERB_SET_PIN_WIDGET_CONTROL,
+                                         pin_ctl & ~flag);
 }
 
-static int get_hp_pin_presence(struct hda_codec *codec, hda_nid_t nid)
+static int get_pin_presence(struct hda_codec *codec, hda_nid_t nid)
 {
        if (!nid)
                return 0;
        if (snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0x00)
-           & (1 << 31)) {
-               unsigned int pinctl;
-               pinctl = snd_hda_codec_read(codec, nid, 0,
-                                           AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
-               if (pinctl & AC_PINCTL_IN_EN)
-                       return 0; /* mic- or line-input */
-               else
-                       return 1; /* HP-output */
-       }
+           & (1 << 31))
+               return 1;
        return 0;
 }
 
@@ -4087,11 +4255,9 @@ static int no_hp_sensing(struct sigmatel_spec *spec, int i)
        struct auto_pin_cfg *cfg = &spec->autocfg;
 
        /* ignore sensing of shared line and mic jacks */
-       if (spec->line_switch &&
-           cfg->hp_pins[i] == cfg->input_pins[AUTO_PIN_LINE])
+       if (cfg->hp_pins[i] == spec->line_switch)
                return 1;
-       if (spec->mic_switch &&
-           cfg->hp_pins[i] == cfg->input_pins[AUTO_PIN_MIC])
+       if (cfg->hp_pins[i] == spec->mic_switch)
                return 1;
        /* ignore if the pin is set as line-out */
        if (cfg->hp_pins[i] == spec->hp_switch)
@@ -4115,7 +4281,14 @@ static void stac92xx_hp_detect(struct hda_codec *codec)
                        break;
                if (no_hp_sensing(spec, i))
                        continue;
-               presence = get_hp_pin_presence(codec, cfg->hp_pins[i]);
+               presence = get_pin_presence(codec, cfg->hp_pins[i]);
+               if (presence) {
+                       unsigned int pinctl;
+                       pinctl = snd_hda_codec_read(codec, cfg->hp_pins[i], 0,
+                                           AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+                       if (pinctl & AC_PINCTL_IN_EN)
+                               presence = 0; /* mic- or line-input */
+               }
        }
 
        if (presence) {
@@ -4156,8 +4329,19 @@ static void stac92xx_hp_detect(struct hda_codec *codec)
                        continue;
                if (presence)
                        stac92xx_set_pinctl(codec, cfg->hp_pins[i], val);
+#if 0 /* FIXME */
+/* Resetting the pinctl like below may lead to (a sort of) regressions
+ * on some devices since they use the HP pin actually for line/speaker
+ * outs although the default pin config shows a different pin (that is
+ * wrong and useless).
+ *
+ * So, it's basically a problem of default pin configs, likely a BIOS issue.
+ * But, disabling the code below just works around it, and I'm too tired of
+ * bug reports with such devices... 
+ */
                else
                        stac92xx_reset_pinctl(codec, cfg->hp_pins[i], val);
+#endif /* FIXME */
        }
 } 
 
@@ -4192,7 +4376,7 @@ static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid,
 
 static void stac92xx_pin_sense(struct hda_codec *codec, hda_nid_t nid)
 {
-       stac_toggle_power_map(codec, nid, get_hp_pin_presence(codec, nid));
+       stac_toggle_power_map(codec, nid, get_pin_presence(codec, nid));
 }
 
 static void stac92xx_report_jack(struct hda_codec *codec, hda_nid_t nid)
@@ -4214,7 +4398,7 @@ static void stac92xx_report_jack(struct hda_codec *codec, hda_nid_t nid)
                                        type = (pin_ctl & AC_PINCTL_HP_EN)
                                        ? SND_JACK_HEADPHONE : SND_JACK_LINEOUT;
                                snd_jack_report(jacks->jack,
-                                       get_hp_pin_presence(codec, nid)
+                                       get_pin_presence(codec, nid)
                                        ? type : 0);
                        }
                        jacks++;
@@ -4251,6 +4435,24 @@ static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
                if (spec->num_pwrs > 0)
                        stac92xx_pin_sense(codec, event->nid);
                stac92xx_report_jack(codec, event->nid);
+
+               switch (codec->subsystem_id) {
+               case 0x103c308f:
+                       if (event->nid == 0xb) {
+                               int pin = AC_PINCTL_IN_EN;
+
+                               if (get_pin_presence(codec, 0xa)
+                                               && get_pin_presence(codec, 0xb))
+                                       pin |= AC_PINCTL_VREF_80;
+                               if (!get_pin_presence(codec, 0xb))
+                                       pin |= AC_PINCTL_VREF_80;
+
+                               /* toggle VREF state based on mic + hp pin
+                                * status
+                                */
+                               stac92xx_auto_set_pinctl(codec, 0x0a, pin);
+                       }
+               }
                break;
        case STAC_VREF_EVENT:
                data = snd_hda_codec_read(codec, codec->afg, 0,
@@ -4313,7 +4515,6 @@ static int stac92xx_resume(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec = codec->spec;
 
-       stac92xx_set_config_regs(codec);
        stac92xx_init(codec);
        snd_hda_codec_resume_amp(codec);
        snd_hda_codec_resume_cache(codec);
@@ -4324,6 +4525,48 @@ static int stac92xx_resume(struct hda_codec *codec)
        return 0;
 }
 
+/*
+ * using power check for controlling mute led of HP notebooks
+ * check for mute state only on Speakers (nid = 0x10)
+ *
+ * For this feature CONFIG_SND_HDA_POWER_SAVE is needed, otherwise
+ * the LED is NOT working properly !
+ *
+ * Changed name to reflect that it now works for any designated
+ * model, not just HP HDX.
+ */
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static int stac92xx_hp_check_power_status(struct hda_codec *codec,
+                                             hda_nid_t nid)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       unsigned int gpio_bit = 0; /* gets rid of compiler warning */
+
+       switch (spec->board_config) {
+       case STAC_HP_DV4_1222NR:
+               gpio_bit = 0x01;
+               break;
+       case STAC_HP_HDX:
+               gpio_bit = 0x08;
+       }
+
+       if (nid == 0x10) {
+               if (snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) &
+                   HDA_AMP_MUTE)
+                       spec->gpio_data &= ~gpio_bit;  /* orange */
+               else
+                       spec->gpio_data |= gpio_bit;   /* white */
+
+               stac_gpio_set(codec, spec->gpio_mask,
+                             spec->gpio_dir,
+                             spec->gpio_data);
+       }
+
+       return 0;
+}
+#endif
+
 static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state)
 {
        struct sigmatel_spec *spec = codec->spec;
@@ -4362,16 +4605,11 @@ static int patch_stac9200(struct hda_codec *codec)
        spec->board_config = snd_hda_check_board_config(codec, STAC_9200_MODELS,
                                                        stac9200_models,
                                                        stac9200_cfg_tbl);
-       if (spec->board_config < 0) {
+       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);
-       } else
-               err = stac_save_pin_cfgs(codec,
+       else
+               stac92xx_set_config_regs(codec,
                                         stac9200_brd_tbl[spec->board_config]);
-       if (err < 0) {
-               stac92xx_free(codec);
-               return err;
-       }
 
        spec->multiout.max_channels = 2;
        spec->multiout.num_dacs = 1;
@@ -4383,7 +4621,8 @@ static int patch_stac9200(struct hda_codec *codec)
        spec->num_adcs = 1;
        spec->num_pwrs = 0;
 
-       if (spec->board_config == STAC_9200_GATEWAY ||
+       if (spec->board_config == STAC_9200_M4 ||
+           spec->board_config == STAC_9200_M4_2 ||
            spec->board_config == STAC_9200_OQO)
                spec->init = stac9200_eapd_init;
        else
@@ -4401,6 +4640,12 @@ static int patch_stac9200(struct hda_codec *codec)
                return err;
        }
 
+       /* CF-74 has no headphone detection, and the driver should *NOT*
+        * do detection and HP/speaker toggle because the hardware does it.
+        */
+       if (spec->board_config == STAC_9200_PANASONIC)
+               spec->hp_detect = 0;
+
        codec->patch_ops = stac92xx_patch_ops;
 
        return 0;
@@ -4418,21 +4663,26 @@ static int patch_stac925x(struct hda_codec *codec)
        codec->spec = spec;
        spec->num_pins = ARRAY_SIZE(stac925x_pin_nids);
        spec->pin_nids = stac925x_pin_nids;
-       spec->board_config = snd_hda_check_board_config(codec, STAC_925x_MODELS,
+
+       /* Check first for codec ID */
+       spec->board_config = snd_hda_check_board_codec_sid_config(codec,
+                                                       STAC_925x_MODELS,
+                                                       stac925x_models,
+                                                       stac925x_codec_id_cfg_tbl);
+
+       /* Now checks for PCI ID, if codec ID is not found */
+       if (spec->board_config < 0)
+               spec->board_config = snd_hda_check_board_config(codec,
+                                                       STAC_925x_MODELS,
                                                        stac925x_models,
                                                        stac925x_cfg_tbl);
  again:
-       if (spec->board_config < 0) {
-               snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC925x," 
+       if (spec->board_config < 0)
+               snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC925x,"
                                      "using BIOS defaults\n");
-               err = stac92xx_save_bios_config_regs(codec);
-       } else
-               err = stac_save_pin_cfgs(codec,
+       else
+               stac92xx_set_config_regs(codec,
                                         stac925x_brd_tbl[spec->board_config]);
-       if (err < 0) {
-               stac92xx_free(codec);
-               return err;
-       }
 
        spec->multiout.max_channels = 2;
        spec->multiout.num_dacs = 1;
@@ -4495,6 +4745,7 @@ static int patch_stac92hd73xx(struct hda_codec *codec)
        struct sigmatel_spec *spec;
        hda_nid_t conn[STAC92HD73_DAC_COUNT + 2];
        int err = 0;
+       int num_dacs;
 
        spec  = kzalloc(sizeof(*spec), GFP_KERNEL);
        if (spec == NULL)
@@ -4509,42 +4760,40 @@ static int patch_stac92hd73xx(struct hda_codec *codec)
                                                        stac92hd73xx_models,
                                                        stac92hd73xx_cfg_tbl);
 again:
-       if (spec->board_config < 0) {
+       if (spec->board_config < 0)
                snd_printdd(KERN_INFO "hda_codec: Unknown model for"
                        " STAC92HD73XX, using BIOS defaults\n");
-               err = stac92xx_save_bios_config_regs(codec);
-       } else
-               err = stac_save_pin_cfgs(codec,
+       else
+               stac92xx_set_config_regs(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,
+       num_dacs = snd_hda_get_connections(codec, 0x0a,
                        conn, STAC92HD73_DAC_COUNT + 2) - 1;
 
-       if (spec->multiout.num_dacs < 0) {
+       if (num_dacs < 3 || num_dacs > 5) {
                printk(KERN_WARNING "hda_codec: Could not determine "
                       "number of channels defaulting to DAC count\n");
-               spec->multiout.num_dacs = STAC92HD73_DAC_COUNT;
+               num_dacs = STAC92HD73_DAC_COUNT;
        }
-
-       switch (spec->multiout.num_dacs) {
+       switch (num_dacs) {
        case 0x3: /* 6 Channel */
                spec->mixer = stac92hd73xx_6ch_mixer;
                spec->init = stac92hd73xx_6ch_core_init;
+               spec->aloopback_ctl = stac92hd73xx_6ch_loopback;
                break;
        case 0x4: /* 8 Channel */
                spec->mixer = stac92hd73xx_8ch_mixer;
                spec->init = stac92hd73xx_8ch_core_init;
+               spec->aloopback_ctl = stac92hd73xx_8ch_loopback;
                break;
        case 0x5: /* 10 Channel */
                spec->mixer = stac92hd73xx_10ch_mixer;
                spec->init = stac92hd73xx_10ch_core_init;
-       };
+               spec->aloopback_ctl = stac92hd73xx_10ch_loopback;
+               break;
+       }
+       spec->multiout.dac_nids = spec->dac_nids;
 
-       spec->multiout.dac_nids = stac92hd73xx_dac_nids;
        spec->aloopback_mask = 0x01;
        spec->aloopback_shift = 8;
 
@@ -4576,22 +4825,22 @@ again:
                spec->eapd_switch = 0;
                spec->num_amps = 1;
 
-               if (!spec->init)
+               if (spec->board_config != STAC_DELL_EQ)
                        spec->init = dell_m6_core_init;
                switch (spec->board_config) {
                case STAC_DELL_M6_AMIC: /* Analog Mics */
-                       stac92xx_set_config_reg(codec, 0x0b, 0x90A70170);
+                       snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170);
                        spec->num_dmics = 0;
                        spec->private_dimux.num_items = 1;
                        break;
                case STAC_DELL_M6_DMIC: /* Digital Mics */
-                       stac92xx_set_config_reg(codec, 0x13, 0x90A60160);
+                       snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160);
                        spec->num_dmics = 1;
                        spec->private_dimux.num_items = 2;
                        break;
                case STAC_DELL_M6_BOTH: /* Both */
-                       stac92xx_set_config_reg(codec, 0x0b, 0x90A70170);
-                       stac92xx_set_config_reg(codec, 0x13, 0x90A60160);
+                       snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170);
+                       snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160);
                        spec->num_dmics = 1;
                        spec->private_dimux.num_items = 2;
                        break;
@@ -4629,6 +4878,9 @@ again:
                return err;
        }
 
+       if (spec->board_config == STAC_92HD73XX_NO_JD)
+               spec->hp_detect = 0;
+
        codec->patch_ops = stac92xx_patch_ops;
 
        codec->proc_widget_hook = stac92hd7x_proc_hook;
@@ -4648,7 +4900,10 @@ static struct hda_input_mux stac92hd83xxx_dmux = {
 static int patch_stac92hd83xxx(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec;
+       hda_nid_t conn[STAC92HD83_DAC_COUNT + 1];
        int err;
+       int num_dacs;
+       hda_nid_t nid;
 
        spec  = kzalloc(sizeof(*spec), GFP_KERNEL);
        if (spec == NULL)
@@ -4662,25 +4917,17 @@ static int patch_stac92hd83xxx(struct hda_codec *codec)
        spec->dmux_nids = stac92hd83xxx_dmux_nids;
        spec->adc_nids = stac92hd83xxx_adc_nids;
        spec->pwr_nids = stac92hd83xxx_pwr_nids;
+       spec->amp_nids = stac92hd83xxx_amp_nids;
        spec->pwr_mapping = stac92hd83xxx_pwr_mapping;
        spec->num_pwrs = ARRAY_SIZE(stac92hd83xxx_pwr_nids);
-       spec->multiout.dac_nids = stac92hd83xxx_dac_nids;
+       spec->multiout.dac_nids = spec->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_amps = ARRAY_SIZE(stac92hd83xxx_amp_nids);
        spec->num_dmics = STAC92HD83XXX_NUM_DMICS;
        spec->dinput_mux = &stac92hd83xxx_dmux;
        spec->pin_nids = stac92hd83xxx_pin_nids;
@@ -4689,16 +4936,21 @@ static int patch_stac92hd83xxx(struct hda_codec *codec)
                                                        stac92hd83xxx_models,
                                                        stac92hd83xxx_cfg_tbl);
 again:
-       if (spec->board_config < 0) {
+       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);
-       } else
-               err = stac_save_pin_cfgs(codec,
+       else
+               stac92xx_set_config_regs(codec,
                                stac92hd83xxx_brd_tbl[spec->board_config]);
-       if (err < 0) {
-               stac92xx_free(codec);
-               return err;
+
+       switch (codec->vendor_id) {
+       case 0x111d7604:
+       case 0x111d7605:
+       case 0x111d76d5:
+               if (spec->board_config == STAC_92HD83XXX_PWR_REF)
+                       break;
+               spec->num_pwrs = 0;
+               break;
        }
 
        err = stac92xx_parse_auto_config(codec, 0x1d, 0);
@@ -4717,6 +4969,23 @@ again:
                return err;
        }
 
+       switch (spec->board_config) {
+       case STAC_DELL_S14:
+               nid = 0xf;
+               break;
+       default:
+               nid = 0xe;
+               break;
+       }
+
+       num_dacs = snd_hda_get_connections(codec, nid,
+                               conn, STAC92HD83_DAC_COUNT + 1) - 1;
+
+       /* set port X to select the last DAC
+        */
+       snd_hda_codec_write_cache(codec, nid, 0,
+                       AC_VERB_SET_CONNECT_SEL, num_dacs);
+
        codec->patch_ops = stac92xx_patch_ops;
 
        codec->proc_widget_hook = stac92hd_proc_hook;
@@ -4724,7 +4993,16 @@ again:
        return 0;
 }
 
-static struct hda_input_mux stac92hd71bxx_dmux = {
+static struct hda_input_mux stac92hd71bxx_dmux_nomixer = {
+       .num_items = 3,
+       .items = {
+               { "Analog Inputs", 0x00 },
+               { "Digital Mic 1", 0x02 },
+               { "Digital Mic 2", 0x03 },
+       }
+};
+
+static struct hda_input_mux stac92hd71bxx_dmux_amixer = {
        .num_items = 4,
        .items = {
                { "Analog Inputs", 0x00 },
@@ -4734,10 +5012,67 @@ static struct hda_input_mux stac92hd71bxx_dmux = {
        }
 };
 
+/* get the pin connection (fixed, none, etc) */
+static unsigned int stac_get_defcfg_connect(struct hda_codec *codec, int idx)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       unsigned int cfg;
+
+       cfg = snd_hda_codec_get_pincfg(codec, spec->pin_nids[idx]);
+       return get_defcfg_connect(cfg);
+}
+
+static int stac92hd71bxx_connected_ports(struct hda_codec *codec,
+                                        hda_nid_t *nids, int num_nids)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       int idx, num;
+       unsigned int def_conf;
+
+       for (num = 0; num < num_nids; num++) {
+               for (idx = 0; idx < spec->num_pins; idx++)
+                       if (spec->pin_nids[idx] == nids[num])
+                               break;
+               if (idx >= spec->num_pins)
+                       break;
+               def_conf = stac_get_defcfg_connect(codec, idx);
+               if (def_conf == AC_JACK_PORT_NONE)
+                       break;
+       }
+       return num;
+}
+
+static int stac92hd71bxx_connected_smuxes(struct hda_codec *codec,
+                                         hda_nid_t dig0pin)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       int idx;
+
+       for (idx = 0; idx < spec->num_pins; idx++)
+               if (spec->pin_nids[idx] == dig0pin)
+                       break;
+       if ((idx + 2) >= spec->num_pins)
+               return 0;
+
+       /* dig1pin case */
+       if (stac_get_defcfg_connect(codec, idx + 1) != AC_JACK_PORT_NONE)
+               return 2;
+
+       /* dig0pin + dig2pin case */
+       if (stac_get_defcfg_connect(codec, idx + 2) != AC_JACK_PORT_NONE)
+               return 2;
+       if (stac_get_defcfg_connect(codec, idx) != AC_JACK_PORT_NONE)
+               return 1;
+       else
+               return 0;
+}
+
 static int patch_stac92hd71bxx(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec;
+       struct hda_verb *unmute_init = stac92hd71bxx_unmute_core_init;
        int err = 0;
+       unsigned int ndmic_nids = 0;
 
        spec  = kzalloc(sizeof(*spec), GFP_KERNEL);
        if (spec == NULL)
@@ -4745,27 +5080,32 @@ static int patch_stac92hd71bxx(struct hda_codec *codec)
 
        codec->spec = spec;
        codec->patch_ops = stac92xx_patch_ops;
-       spec->num_pins = ARRAY_SIZE(stac92hd71bxx_pin_nids);
+       spec->num_pins = STAC92HD71BXX_NUM_PINS;
+       switch (codec->vendor_id) {
+       case 0x111d76b6:
+       case 0x111d76b7:
+               spec->pin_nids = stac92hd71bxx_pin_nids_4port;
+               break;
+       case 0x111d7603:
+       case 0x111d7608:
+               /* On 92HD75Bx 0x27 isn't a pin nid */
+               spec->num_pins--;
+               /* fallthrough */
+       default:
+               spec->pin_nids = stac92hd71bxx_pin_nids_6port;
+       }
        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,
                                                        stac92hd71bxx_cfg_tbl);
 again:
-       if (spec->board_config < 0) {
+       if (spec->board_config < 0)
                snd_printdd(KERN_INFO "hda_codec: Unknown model for"
                        " STAC92HD71BXX, using BIOS defaults\n");
-               err = stac92xx_save_bios_config_regs(codec);
-       } else
-               err = stac_save_pin_cfgs(codec,
+       else
+               stac92xx_set_config_regs(codec,
                                stac92hd71bxx_brd_tbl[spec->board_config]);
-       if (err < 0) {
-               stac92xx_free(codec);
-               return err;
-       }
 
        if (spec->board_config > STAC_92HD71BXX_REF) {
                /* GPIO0 = EAPD */
@@ -4774,16 +5114,34 @@ again:
                spec->gpio_data = 0x01;
        }
 
+       spec->dmic_nids = stac92hd71bxx_dmic_nids;
+       spec->dmux_nids = stac92hd71bxx_dmux_nids;
+
        switch (codec->vendor_id) {
        case 0x111d76b6: /* 4 Port without Analog Mixer */
        case 0x111d76b7:
+               unmute_init++;
+               /* fallthru */
        case 0x111d76b4: /* 6 Port without Analog Mixer */
        case 0x111d76b5:
+               memcpy(&spec->private_dimux, &stac92hd71bxx_dmux_nomixer,
+                      sizeof(stac92hd71bxx_dmux_nomixer));
                spec->mixer = stac92hd71bxx_mixer;
                spec->init = stac92hd71bxx_core_init;
                codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
+               spec->num_dmics = stac92hd71bxx_connected_ports(codec,
+                                       stac92hd71bxx_dmic_nids,
+                                       STAC92HD71BXX_NUM_DMICS);
+               if (spec->num_dmics) {
+                       spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids);
+                       spec->dinput_mux = &spec->private_dimux;
+                       ndmic_nids = ARRAY_SIZE(stac92hd71bxx_dmic_nids) - 1;
+               }
                break;
        case 0x111d7608: /* 5 Port with Analog Mixer */
+               memcpy(&spec->private_dimux, &stac92hd71bxx_dmux_amixer,
+                      sizeof(stac92hd71bxx_dmux_amixer));
+               spec->private_dimux.num_items--;
                switch (spec->board_config) {
                case STAC_HP_M4:
                        /* Enable VREF power saving on GPIO1 detect */
@@ -4810,7 +5168,15 @@ again:
 
                /* disable VSW */
                spec->init = &stac92hd71bxx_analog_core_init[HD_DISABLE_PORTF];
-               stac_change_pin_config(codec, 0xf, 0x40f000f0);
+               unmute_init++;
+               snd_hda_codec_set_pincfg(codec, 0x0f, 0x40f000f0);
+               snd_hda_codec_set_pincfg(codec, 0x19, 0x40f000f3);
+               stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS - 1] = 0;
+               spec->num_dmics = stac92hd71bxx_connected_ports(codec,
+                                       stac92hd71bxx_dmic_nids,
+                                       STAC92HD71BXX_NUM_DMICS - 1);
+               spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids);
+               ndmic_nids = ARRAY_SIZE(stac92hd71bxx_dmic_nids) - 2;
                break;
        case 0x111d7603: /* 6 Port with Analog Mixer */
                if ((codec->revision_id & 0xf) == 1)
@@ -4820,12 +5186,23 @@ again:
                spec->num_pwrs = 0;
                /* fallthru */
        default:
+               memcpy(&spec->private_dimux, &stac92hd71bxx_dmux_amixer,
+                      sizeof(stac92hd71bxx_dmux_amixer));
                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->num_dmics = stac92hd71bxx_connected_ports(codec,
+                                       stac92hd71bxx_dmic_nids,
+                                       STAC92HD71BXX_NUM_DMICS);
+               spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids);
+               ndmic_nids = ARRAY_SIZE(stac92hd71bxx_dmic_nids) - 1;
        }
 
+       if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP)
+               snd_hda_sequence_write_cache(codec, unmute_init);
+
+       spec->aloopback_ctl = stac92hd71bxx_loopback;
        spec->aloopback_mask = 0x50;
        spec->aloopback_shift = 0;
 
@@ -4833,18 +5210,17 @@ again:
        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_smuxes = stac92hd71bxx_connected_smuxes(codec, 0x1e);
 
        switch (spec->board_config) {
        case STAC_HP_M4:
                /* enable internal microphone */
-               stac_change_pin_config(codec, 0x0e, 0x01813040);
+               snd_hda_codec_set_pincfg(codec, 0x0e, 0x01813040);
                stac92xx_auto_set_pinctl(codec, 0x0e,
                        AC_PINCTL_IN_EN | AC_PINCTL_VREF_80);
                /* fallthru */
@@ -4857,23 +5233,54 @@ again:
        case STAC_DELL_M4_3:
                spec->num_dmics = 1;
                spec->num_smuxes = 0;
-               spec->num_dmuxes = 0;
+               spec->num_dmuxes = 1;
+               break;
+       case STAC_HP_DV4_1222NR:
+               spec->num_dmics = 1;
+               /* I don't know if it needs 1 or 2 smuxes - will wait for
+                * bug reports to fix if needed
+                */
+               spec->num_smuxes = 1;
+               spec->num_dmuxes = 1;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+               /* This controls MUTE LED */
+               spec->gpio_mask |= 0x01;
+               spec->gpio_dir  |= 0x01;
+               spec->gpio_data |= 0x01;
+               codec->patch_ops.check_power_status =
+                   stac92xx_hp_check_power_status;
+#endif
+               /* fallthrough */
+       case STAC_HP_DV5:
+               snd_hda_codec_set_pincfg(codec, 0x0d, 0x90170010);
+               stac92xx_auto_set_pinctl(codec, 0x0d, AC_PINCTL_OUT_EN);
+               break;
+       case STAC_HP_HDX:
+               spec->num_dmics = 1;
+               spec->num_dmuxes = 1;
+               spec->num_smuxes = 1;
+               /*
+                * For controlling MUTE LED on HP HDX16/HDX18 notebooks,
+                * the CONFIG_SND_HDA_POWER_SAVE is needed to be set.
+                */
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+               /* orange/white mute led on GPIO3, orange=0, white=1 */
+               spec->gpio_mask |= 0x08;
+               spec->gpio_dir  |= 0x08;
+               spec->gpio_data |= 0x08;  /* set to white */
+
+               /* register check_power_status callback. */
+               codec->patch_ops.check_power_status =
+                   stac92xx_hp_check_power_status;
+#endif 
                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;
+       spec->multiout.dac_nids = spec->dac_nids;
        if (spec->dinput_mux)
-               spec->private_dimux.num_items +=
-                       spec->num_dmics -
-                               (ARRAY_SIZE(stac92hd71bxx_dmic_nids) - 1);
+               spec->private_dimux.num_items += spec->num_dmics - ndmic_nids;
 
-       err = stac92xx_parse_auto_config(codec, 0x21, 0x23);
+       err = stac92xx_parse_auto_config(codec, 0x21, 0);
        if (!err) {
                if (spec->board_config < 0) {
                        printk(KERN_WARNING "hda_codec: No auto-config is "
@@ -4948,17 +5355,12 @@ static int patch_stac922x(struct hda_codec *codec)
        }
 
  again:
-       if (spec->board_config < 0) {
+       if (spec->board_config < 0)
                snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC922x, "
                        "using BIOS defaults\n");
-               err = stac92xx_save_bios_config_regs(codec);
-       } else
-               err = stac_save_pin_cfgs(codec,
+       else
+               stac92xx_set_config_regs(codec,
                                stac922x_brd_tbl[spec->board_config]);
-       if (err < 0) {
-               stac92xx_free(codec);
-               return err;
-       }
 
        spec->adc_nids = stac922x_adc_nids;
        spec->mux_nids = stac922x_mux_nids;
@@ -5009,24 +5411,19 @@ static int patch_stac927x(struct hda_codec *codec)
                return -ENOMEM;
 
        codec->spec = spec;
+       codec->slave_dig_outs = stac927x_slave_dig_outs;
        spec->num_pins = ARRAY_SIZE(stac927x_pin_nids);
        spec->pin_nids = stac927x_pin_nids;
        spec->board_config = snd_hda_check_board_config(codec, STAC_927X_MODELS,
                                                        stac927x_models,
                                                        stac927x_cfg_tbl);
  again:
-       if (spec->board_config < 0 || !stac927x_brd_tbl[spec->board_config]) {
-               if (spec->board_config < 0)
-                       snd_printdd(KERN_INFO "hda_codec: Unknown model for"
-                                   "STAC927x, using BIOS defaults\n");
-               err = stac92xx_save_bios_config_regs(codec);
-       } else
-               err = stac_save_pin_cfgs(codec,
+       if (spec->board_config < 0)
+               snd_printdd(KERN_INFO "hda_codec: Unknown model for"
+                           "STAC927x, using BIOS defaults\n");
+       else
+               stac92xx_set_config_regs(codec,
                                stac927x_brd_tbl[spec->board_config]);
-       if (err < 0) {
-               stac92xx_free(codec);
-               return err;
-       }
 
        spec->digbeep_nid = 0x23;
        spec->adc_nids = stac927x_adc_nids;
@@ -5055,15 +5452,15 @@ static int patch_stac927x(struct hda_codec *codec)
                case 0x10280209:
                case 0x1028022e:
                        /* correct the device field to SPDIF out */
-                       stac_change_pin_config(codec, 0x21, 0x01442070);
+                       snd_hda_codec_set_pincfg(codec, 0x21, 0x01442070);
                        break;
                };
                /* configure the analog microphone on some laptops */
-               stac_change_pin_config(codec, 0x0c, 0x90a79130);
+               snd_hda_codec_set_pincfg(codec, 0x0c, 0x90a79130);
                /* correct the front output jack as a hp out */
-               stac_change_pin_config(codec, 0x0f, 0x0227011f);
+               snd_hda_codec_set_pincfg(codec, 0x0f, 0x0227011f);
                /* correct the front input jack as a mic */
-               stac_change_pin_config(codec, 0x0e, 0x02a79130);
+               snd_hda_codec_set_pincfg(codec, 0x0e, 0x02a79130);
                /* fallthru */
        case STAC_DELL_3ST:
                /* GPIO2 High = Enable EAPD */
@@ -5090,6 +5487,7 @@ static int patch_stac927x(struct hda_codec *codec)
        }
 
        spec->num_pwrs = 0;
+       spec->aloopback_ctl = stac927x_loopback;
        spec->aloopback_mask = 0x40;
        spec->aloopback_shift = 0;
        spec->eapd_switch = 1;
@@ -5125,6 +5523,10 @@ static int patch_stac927x(struct hda_codec *codec)
         */
        codec->bus->needs_damn_long_delay = 1;
 
+       /* no jack detecion for ref-no-jd model */
+       if (spec->board_config == STAC_D965_REF_NO_JD)
+               spec->hp_detect = 0;
+
        return 0;
 }
 
@@ -5144,16 +5546,11 @@ static int patch_stac9205(struct hda_codec *codec)
                                                        stac9205_models,
                                                        stac9205_cfg_tbl);
  again:
-       if (spec->board_config < 0) {
+       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);
-       } else
-               err = stac_save_pin_cfgs(codec,
+       else
+               stac92xx_set_config_regs(codec,
                                         stac9205_brd_tbl[spec->board_config]);
-       if (err < 0) {
-               stac92xx_free(codec);
-               return err;
-       }
 
        spec->digbeep_nid = 0x23;
        spec->adc_nids = stac9205_adc_nids;
@@ -5170,17 +5567,20 @@ static int patch_stac9205(struct hda_codec *codec)
 
        spec->init = stac9205_core_init;
        spec->mixer = stac9205_mixer;
+       spec->aloopback_ctl = stac9205_loopback;
 
        spec->aloopback_mask = 0x40;
        spec->aloopback_shift = 0;
-       spec->eapd_switch = 1;
+       /* Turn on/off EAPD per HP plugging */
+       if (spec->board_config != STAC_9205_EAPD)
+               spec->eapd_switch = 1;
        spec->multiout.dac_nids = spec->dac_nids;
        
        switch (spec->board_config){
        case STAC_9205_DELL_M43:
                /* Enable SPDIF in/out */
-               stac_change_pin_config(codec, 0x1f, 0x01441030);
-               stac_change_pin_config(codec, 0x20, 0x1c410030);
+               snd_hda_codec_set_pincfg(codec, 0x1f, 0x01441030);
+               snd_hda_codec_set_pincfg(codec, 0x20, 0x1c410030);
 
                /* Enable unsol response for GPIO4/Dock HP connection */
                err = stac_add_event(spec, codec->afg, STAC_VREF_EVENT, 0x01);
@@ -5237,223 +5637,87 @@ static int patch_stac9205(struct hda_codec *codec)
  * STAC9872 hack
  */
 
-/* static config for Sony VAIO FE550G and Sony VAIO AR */
-static hda_nid_t vaio_dacs[] = { 0x2 };
-#define VAIO_HP_DAC    0x5
-static hda_nid_t vaio_adcs[] = { 0x8 /*,0x6*/ };
-static hda_nid_t vaio_mux_nids[] = { 0x15 };
-
-static struct hda_input_mux vaio_mux = {
-       .num_items = 3,
-       .items = {
-               /* { "HP", 0x0 }, */
-               { "Mic Jack", 0x1 },
-               { "Internal Mic", 0x2 },
-               { "PCM", 0x3 },
-       }
-};
-
-static struct hda_verb vaio_init[] = {
-       {0x0a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* HP <- 0x2 */
-       {0x0a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | STAC_HP_EVENT},
-       {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Speaker <- 0x5 */
-       {0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? (<- 0x2) */
-       {0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD */
-       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? */
+static struct hda_verb stac9872_core_init[] = {
        {0x15, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mic-sel: 0a,0d,14,02 */
-       {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* HP */
-       {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Speaker */
-       {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* capture sw/vol -> 0x8 */
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* CD-in -> 0x6 */
        {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Mic-in -> 0x9 */
        {}
 };
 
-static struct hda_verb vaio_ar_init[] = {
-       {0x0a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* HP <- 0x2 */
-       {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Speaker <- 0x5 */
-       {0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? (<- 0x2) */
-       {0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD */
-/*     {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },*/ /* Optical Out */
-       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? */
-       {0x15, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mic-sel: 0a,0d,14,02 */
-       {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* HP */
-       {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Speaker */
-/*     {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},*/ /* Optical Out */
-       {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* capture sw/vol -> 0x8 */
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* CD-in -> 0x6 */
-       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Mic-in -> 0x9 */
-       {}
-};
-
-static struct snd_kcontrol_new vaio_mixer[] = {
-       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), */
+static struct snd_kcontrol_new stac9872_mixer[] = {
        HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_INPUT),
        HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_INPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Capture Source",
-               .count = 1,
-               .info = stac92xx_mux_enum_info,
-               .get = stac92xx_mux_enum_get,
-               .put = stac92xx_mux_enum_put,
-       },
-       {}
+       { } /* end */
 };
 
-static struct snd_kcontrol_new vaio_ar_mixer[] = {
-       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),
-       /*HDA_CODEC_MUTE("Optical Out Switch", 0x10, 0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Optical Out Volume", 0x10, 0, HDA_OUTPUT),*/
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Capture Source",
-               .count = 1,
-               .info = stac92xx_mux_enum_info,
-               .get = stac92xx_mux_enum_get,
-               .put = stac92xx_mux_enum_put,
-       },
-       {}
+static hda_nid_t stac9872_pin_nids[] = {
+       0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+       0x11, 0x13, 0x14,
 };
 
-static struct hda_codec_ops stac9872_patch_ops = {
-       .build_controls = stac92xx_build_controls,
-       .build_pcms = stac92xx_build_pcms,
-       .init = stac92xx_init,
-       .free = stac92xx_free,
-#ifdef SND_HDA_NEEDS_RESUME
-       .resume = stac92xx_resume,
-#endif
+static hda_nid_t stac9872_adc_nids[] = {
+       0x8 /*,0x6*/
 };
 
-static int stac9872_vaio_init(struct hda_codec *codec)
-{
-       int err;
-
-       err = stac92xx_init(codec);
-       if (err < 0)
-               return err;
-       if (codec->patch_ops.unsol_event)
-               codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
-       return 0;
-}
-
-static void stac9872_vaio_hp_detect(struct hda_codec *codec, unsigned int res)
-{
-       if (get_hp_pin_presence(codec, 0x0a)) {
-               stac92xx_reset_pinctl(codec, 0x0f, AC_PINCTL_OUT_EN);
-               stac92xx_set_pinctl(codec, 0x0a, AC_PINCTL_OUT_EN);
-       } else {
-               stac92xx_reset_pinctl(codec, 0x0a, AC_PINCTL_OUT_EN);
-               stac92xx_set_pinctl(codec, 0x0f, AC_PINCTL_OUT_EN);
-       }
-} 
-
-static void stac9872_vaio_unsol_event(struct hda_codec *codec, unsigned int res)
-{
-       switch (res >> 26) {
-       case STAC_HP_EVENT:
-               stac9872_vaio_hp_detect(codec, res);
-               break;
-       }
-}
-
-static struct hda_codec_ops stac9872_vaio_patch_ops = {
-       .build_controls = stac92xx_build_controls,
-       .build_pcms = stac92xx_build_pcms,
-       .init = stac9872_vaio_init,
-       .free = stac92xx_free,
-       .unsol_event = stac9872_vaio_unsol_event,
-#ifdef CONFIG_PM
-       .resume = stac92xx_resume,
-#endif
+static hda_nid_t stac9872_mux_nids[] = {
+       0x15
 };
 
-enum { /* FE and SZ series. id=0x83847661 and subsys=0x104D0700 or 104D1000. */
-       CXD9872RD_VAIO,
-       /* Unknown. id=0x83847662 and subsys=0x104D1200 or 104D1000. */
-       STAC9872AK_VAIO, 
-       /* Unknown. id=0x83847661 and subsys=0x104D1200. */
-       STAC9872K_VAIO,
-       /* AR Series. id=0x83847664 and subsys=104D1300 */
-       CXD9872AKD_VAIO,
-       STAC_9872_MODELS,
+static unsigned int stac9872_vaio_pin_configs[9] = {
+       0x03211020, 0x411111f0, 0x411111f0, 0x03a15030,
+       0x411111f0, 0x90170110, 0x411111f0, 0x411111f0,
+       0x90a7013e
 };
 
 static const char *stac9872_models[STAC_9872_MODELS] = {
-       [CXD9872RD_VAIO]        = "vaio",
-       [CXD9872AKD_VAIO]       = "vaio-ar",
+       [STAC_9872_AUTO] = "auto",
+       [STAC_9872_VAIO] = "vaio",
+};
+
+static unsigned int *stac9872_brd_tbl[STAC_9872_MODELS] = {
+       [STAC_9872_VAIO] = stac9872_vaio_pin_configs,
 };
 
 static struct snd_pci_quirk stac9872_cfg_tbl[] = {
-       SND_PCI_QUIRK(0x104d, 0x81e6, "Sony VAIO F/S", CXD9872RD_VAIO),
-       SND_PCI_QUIRK(0x104d, 0x81ef, "Sony VAIO F/S", CXD9872RD_VAIO),
-       SND_PCI_QUIRK(0x104d, 0x81fd, "Sony VAIO AR", CXD9872AKD_VAIO),
-       SND_PCI_QUIRK(0x104d, 0x8205, "Sony VAIO AR", CXD9872AKD_VAIO),
-       {}
+       {} /* terminator */
 };
 
 static int patch_stac9872(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec;
-       int board_config;
+       int err;
 
-       board_config = snd_hda_check_board_config(codec, STAC_9872_MODELS,
-                                                 stac9872_models,
-                                                 stac9872_cfg_tbl);
-       if (board_config < 0)
-               /* unknown config, let generic-parser do its job... */
-               return snd_hda_parse_generic_codec(codec);
-       
        spec  = kzalloc(sizeof(*spec), GFP_KERNEL);
        if (spec == NULL)
                return -ENOMEM;
-
        codec->spec = spec;
-       switch (board_config) {
-       case CXD9872RD_VAIO:
-       case STAC9872AK_VAIO:
-       case STAC9872K_VAIO:
-               spec->mixer = vaio_mixer;
-               spec->init = vaio_init;
-               spec->multiout.max_channels = 2;
-               spec->multiout.num_dacs = ARRAY_SIZE(vaio_dacs);
-               spec->multiout.dac_nids = vaio_dacs;
-               spec->multiout.hp_nid = VAIO_HP_DAC;
-               spec->num_adcs = ARRAY_SIZE(vaio_adcs);
-               spec->adc_nids = vaio_adcs;
-               spec->num_pwrs = 0;
-               spec->input_mux = &vaio_mux;
-               spec->mux_nids = vaio_mux_nids;
-               codec->patch_ops = stac9872_vaio_patch_ops;
-               break;
-       
-       case CXD9872AKD_VAIO:
-               spec->mixer = vaio_ar_mixer;
-               spec->init = vaio_ar_init;
-               spec->multiout.max_channels = 2;
-               spec->multiout.num_dacs = ARRAY_SIZE(vaio_dacs);
-               spec->multiout.dac_nids = vaio_dacs;
-               spec->multiout.hp_nid = VAIO_HP_DAC;
-               spec->num_adcs = ARRAY_SIZE(vaio_adcs);
-               spec->num_pwrs = 0;
-               spec->adc_nids = vaio_adcs;
-               spec->input_mux = &vaio_mux;
-               spec->mux_nids = vaio_mux_nids;
-               codec->patch_ops = stac9872_patch_ops;
-               break;
-       }
 
+       spec->board_config = snd_hda_check_board_config(codec, STAC_9872_MODELS,
+                                                       stac9872_models,
+                                                       stac9872_cfg_tbl);
+       if (spec->board_config < 0)
+               snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9872, "
+                           "using BIOS defaults\n");
+       else
+               stac92xx_set_config_regs(codec,
+                                        stac9872_brd_tbl[spec->board_config]);
+
+       spec->num_pins = ARRAY_SIZE(stac9872_pin_nids);
+       spec->pin_nids = stac9872_pin_nids;
+       spec->multiout.dac_nids = spec->dac_nids;
+       spec->num_adcs = ARRAY_SIZE(stac9872_adc_nids);
+       spec->adc_nids = stac9872_adc_nids;
+       spec->num_muxes = ARRAY_SIZE(stac9872_mux_nids);
+       spec->mux_nids = stac9872_mux_nids;
+       spec->mixer = stac9872_mixer;
+       spec->init = stac9872_core_init;
+
+       err = stac92xx_parse_auto_config(codec, 0x10, 0x12);
+       if (err < 0) {
+               stac92xx_free(codec);
+               return -EINVAL;
+       }
+       spec->input_mux = &spec->private_imux;
+       codec->patch_ops = stac92xx_patch_ops;
        return 0;
 }
 
@@ -5461,7 +5725,7 @@ static int patch_stac9872(struct hda_codec *codec)
 /*
  * patch entries
  */
-struct hda_codec_preset snd_hda_preset_sigmatel[] = {
+static struct hda_codec_preset snd_hda_preset_sigmatel[] = {
        { .id = 0x83847690, .name = "STAC9200", .patch = patch_stac9200 },
        { .id = 0x83847882, .name = "STAC9220 A1", .patch = patch_stac922x },
        { .id = 0x83847680, .name = "STAC9221 A1", .patch = patch_stac922x },
@@ -5511,6 +5775,7 @@ struct hda_codec_preset snd_hda_preset_sigmatel[] = {
        { .id = 0x111d7603, .name = "92HD75B3X5", .patch = patch_stac92hd71bxx},
        { .id = 0x111d7604, .name = "92HD83C1X5", .patch = patch_stac92hd83xxx},
        { .id = 0x111d7605, .name = "92HD81B1X5", .patch = patch_stac92hd83xxx},
+       { .id = 0x111d76d5, .name = "92HD81B1C5", .patch = patch_stac92hd83xxx},
        { .id = 0x111d7608, .name = "92HD75B2X5", .patch = patch_stac92hd71bxx},
        { .id = 0x111d7674, .name = "92HD73D1X5", .patch = patch_stac92hd73xx },
        { .id = 0x111d7675, .name = "92HD73C1X5", .patch = patch_stac92hd73xx },
@@ -5525,3 +5790,27 @@ struct hda_codec_preset snd_hda_preset_sigmatel[] = {
        { .id = 0x111d76b7, .name = "92HD71B5X", .patch = patch_stac92hd71bxx },
        {} /* terminator */
 };
+
+MODULE_ALIAS("snd-hda-codec-id:8384*");
+MODULE_ALIAS("snd-hda-codec-id:111d*");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("IDT/Sigmatel HD-audio codec");
+
+static struct hda_codec_preset_list sigmatel_list = {
+       .preset = snd_hda_preset_sigmatel,
+       .owner = THIS_MODULE,
+};
+
+static int __init patch_sigmatel_init(void)
+{
+       return snd_hda_add_codec_preset(&sigmatel_list);
+}
+
+static void __exit patch_sigmatel_exit(void)
+{
+       snd_hda_delete_codec_preset(&sigmatel_list);
+}
+
+module_init(patch_sigmatel_init)
+module_exit(patch_sigmatel_exit)