[ALSA] hda-codec - Add auto-mute function to Sony VAIO with STAC9872
[pandora-kernel.git] / sound / pci / hda / patch_sigmatel.c
index 6dd4822..76ec32a 100644 (file)
@@ -44,6 +44,9 @@ enum {
 
 enum {
        STAC_9205_REF,
+       STAC_9205_DELL_M43,
+       STAC_9205_DELL_M44,
+       STAC_9205_M43xx,
        STAC_9205_MODELS
 };
 
@@ -51,6 +54,7 @@ enum {
        STAC_925x_REF,
        STAC_M2_2,
        STAC_MA6,
+       STAC_PA6,
        STAC_925x_MODELS
 };
 
@@ -58,10 +62,19 @@ enum {
        STAC_D945_REF,
        STAC_D945GTP3,
        STAC_D945GTP5,
+       STAC_922X_DELL,
+       STAC_INTEL_MAC_V1,
+       STAC_INTEL_MAC_V2,
+       STAC_INTEL_MAC_V3,
+       STAC_INTEL_MAC_V4,
+       STAC_INTEL_MAC_V5,
+       /* for backward compitability */
        STAC_MACMINI,
        STAC_MACBOOK,
        STAC_MACBOOK_PRO_V1,
        STAC_MACBOOK_PRO_V2,
+       STAC_IMAC_INTEL,
+       STAC_IMAC_INTEL_20,
        STAC_922X_MODELS
 };
 
@@ -69,6 +82,7 @@ enum {
        STAC_D965_REF,
        STAC_D965_3ST,
        STAC_D965_5ST,
+       STAC_DELL_3ST,
        STAC_927X_MODELS
 };
 
@@ -84,6 +98,8 @@ struct sigmatel_spec {
        unsigned int hp_detect: 1;
        unsigned int gpio_mute: 1;
 
+       unsigned int gpio_mask, gpio_data;
+
        /* playback */
        struct hda_multi_out multiout;
        hda_nid_t dac_nids[5];
@@ -151,6 +167,10 @@ static hda_nid_t stac925x_dac_nids[1] = {
         0x02,
 };
 
+static hda_nid_t stac925x_dmic_nids[1] = {
+       0x15, 
+};
+
 static hda_nid_t stac922x_adc_nids[2] = {
         0x06, 0x07,
 };
@@ -204,7 +224,6 @@ static hda_nid_t stac9205_pin_nids[12] = {
        0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
        0x0f, 0x14, 0x16, 0x17, 0x18,
        0x21, 0x22,
-       
 };
 
 static int stac92xx_dmux_enum_info(struct snd_kcontrol *kcontrol,
@@ -320,8 +339,6 @@ static struct snd_kcontrol_new stac9200_mixer[] = {
 };
 
 static struct snd_kcontrol_new stac925x_mixer[] = {
-       HDA_CODEC_VOLUME("Master Playback Volume", 0xe, 0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Master Playback Switch", 0xe, 0, HDA_OUTPUT),
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Input Source",
@@ -466,6 +483,16 @@ static struct snd_pci_quirk stac9200_cfg_tbl[] = {
                      "Dell XPS M1710", STAC_REF),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cf,
                      "Dell Precision M90", STAC_REF),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d6,
+                     "unknown Dell", STAC_REF),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d8,
+                     "Dell Inspiron 640m", STAC_REF),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f5,
+                     "Dell Inspiron 1501", STAC_REF),
+
+       /* Panasonic */
+       SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-74", STAC_REF),
+
        {} /* terminator */
 };
 
@@ -479,29 +506,38 @@ static unsigned int stac925x_MA6_pin_configs[8] = {
        0x90a70320, 0x90100211, 0x400003f1, 0x9033032e,
 };
 
+static unsigned int stac925x_PA6_pin_configs[8] = {
+       0x40c003f0, 0x424503f2, 0x01813022, 0x02a19021,
+       0x50a103f0, 0x90100211, 0x400003f1, 0x9033032e,
+};
+
 static unsigned int stac925xM2_2_pin_configs[8] = {
-       0x40c003f3, 0x424503f2, 0x041800f4, 0x02a19020,
-       0x50a103F0, 0x90100210, 0x400003f1, 0x9033032e,
+       0x40c003f3, 0x424503f2, 0x04180011, 0x02a19020,
+       0x50a103f0, 0x90100212, 0x400003f1, 0x9033032e,
 };
 
 static unsigned int *stac925x_brd_tbl[STAC_925x_MODELS] = {
        [STAC_REF] = ref925x_pin_configs,
        [STAC_M2_2] = stac925xM2_2_pin_configs,
        [STAC_MA6] = stac925x_MA6_pin_configs,
+       [STAC_PA6] = stac925x_PA6_pin_configs,
 };
 
 static const char *stac925x_models[STAC_925x_MODELS] = {
        [STAC_REF] = "ref",
        [STAC_M2_2] = "m2-2",
        [STAC_MA6] = "m6",
+       [STAC_PA6] = "pa6",
 };
 
 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(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),
        {} /* terminator */
 };
@@ -524,36 +560,78 @@ static unsigned int d945gtp5_pin_configs[10] = {
        0x02a19320, 0x40000100,
 };
 
-static unsigned int macbook_pro_v1_pin_configs[10] = {
-       0x0321e230, 0x03a1e020, 0x9017e110, 0x01014010,
-       0x01a19021, 0x0381e021, 0x1345e240, 0x13c5e22e,
-       0x02a19320, 0x400000fb
+static unsigned int intel_mac_v1_pin_configs[10] = {
+       0x0121e21f, 0x400000ff, 0x9017e110, 0x400000fd,
+       0x400000fe, 0x0181e020, 0x1145e030, 0x11c5e240,
+       0x400000fc, 0x400000fb,
 };
 
-static unsigned int macbook_pro_v2_pin_configs[10] = {
-       0x0221401f, 0x90a70120, 0x01813024, 0x01014010,
-       0x400000fd, 0x01016011, 0x1345e240, 0x13c5e22e,
+static unsigned int intel_mac_v2_pin_configs[10] = {
+       0x0121e21f, 0x90a7012e, 0x9017e110, 0x400000fd,
+       0x400000fe, 0x0181e020, 0x1145e230, 0x500000fa,
        0x400000fc, 0x400000fb,
 };
 
+static unsigned int intel_mac_v3_pin_configs[10] = {
+       0x0121e21f, 0x90a7012e, 0x9017e110, 0x400000fd,
+       0x400000fe, 0x0181e020, 0x1145e230, 0x11c5e240,
+       0x400000fc, 0x400000fb,
+};
+
+static unsigned int intel_mac_v4_pin_configs[10] = {
+       0x0321e21f, 0x03a1e02e, 0x9017e110, 0x9017e11f,
+       0x400000fe, 0x0381e020, 0x1345e230, 0x13c5e240,
+       0x400000fc, 0x400000fb,
+};
+
+static unsigned int intel_mac_v5_pin_configs[10] = {
+       0x0321e21f, 0x03a1e02e, 0x9017e110, 0x9017e11f,
+       0x400000fe, 0x0381e020, 0x1345e230, 0x13c5e240,
+       0x400000fc, 0x400000fb,
+};
+
+static unsigned int stac922x_dell_pin_configs[10] = {
+       0x0221121e, 0x408103ff, 0x02a1123e, 0x90100310,
+       0x408003f1, 0x0221122f, 0x03451340, 0x40c003f2,
+       0x50a003f3, 0x405003f4
+};
+
 static unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = {
        [STAC_D945_REF] = ref922x_pin_configs,
        [STAC_D945GTP3] = d945gtp3_pin_configs,
        [STAC_D945GTP5] = d945gtp5_pin_configs,
-       [STAC_MACMINI] = macbook_pro_v1_pin_configs,
-       [STAC_MACBOOK] = macbook_pro_v1_pin_configs,
-       [STAC_MACBOOK_PRO_V1] = macbook_pro_v1_pin_configs,
-       [STAC_MACBOOK_PRO_V2] = macbook_pro_v2_pin_configs,
+       [STAC_922X_DELL] = stac922x_dell_pin_configs,
+       [STAC_INTEL_MAC_V1] = intel_mac_v1_pin_configs,
+       [STAC_INTEL_MAC_V2] = intel_mac_v2_pin_configs,
+       [STAC_INTEL_MAC_V3] = intel_mac_v3_pin_configs,
+       [STAC_INTEL_MAC_V4] = intel_mac_v4_pin_configs,
+       [STAC_INTEL_MAC_V5] = intel_mac_v5_pin_configs,
+       /* for backward compitability */
+       [STAC_MACMINI] = intel_mac_v3_pin_configs,
+       [STAC_MACBOOK] = intel_mac_v5_pin_configs,
+       [STAC_MACBOOK_PRO_V1] = intel_mac_v3_pin_configs,
+       [STAC_MACBOOK_PRO_V2] = intel_mac_v3_pin_configs,
+       [STAC_IMAC_INTEL] = intel_mac_v2_pin_configs,
+       [STAC_IMAC_INTEL_20] = intel_mac_v3_pin_configs,
 };
 
 static const char *stac922x_models[STAC_922X_MODELS] = {
        [STAC_D945_REF] = "ref",
        [STAC_D945GTP5] = "5stack",
        [STAC_D945GTP3] = "3stack",
+       [STAC_922X_DELL] = "dell",
+       [STAC_INTEL_MAC_V1] = "intel-mac-v1",
+       [STAC_INTEL_MAC_V2] = "intel-mac-v2",
+       [STAC_INTEL_MAC_V3] = "intel-mac-v3",
+       [STAC_INTEL_MAC_V4] = "intel-mac-v4",
+       [STAC_INTEL_MAC_V5] = "intel-mac-v5",
+       /* for backward compitability */
        [STAC_MACMINI]  = "macmini",
        [STAC_MACBOOK]  = "macbook",
        [STAC_MACBOOK_PRO_V1]   = "macbook-pro-v1",
        [STAC_MACBOOK_PRO_V2]   = "macbook-pro",
+       [STAC_IMAC_INTEL] = "imac-intel",
+       [STAC_IMAC_INTEL_20] = "imac-intel-20",
 };
 
 static struct snd_pci_quirk stac922x_cfg_tbl[] = {
@@ -616,7 +694,10 @@ static struct snd_pci_quirk stac922x_cfg_tbl[] = {
        /* other systems  */
        /* Apple Mac Mini (early 2006) */
        SND_PCI_QUIRK(0x8384, 0x7680,
-                     "Mac Mini", STAC_MACMINI),
+                     "Mac Mini", STAC_INTEL_MAC_V3),
+       /* Dell */
+       SND_PCI_QUIRK(0x1028, 0x01d7, "Dell XPS M1210", STAC_922X_DELL),
+
        {} /* terminator */
 };
 
@@ -641,16 +722,25 @@ static unsigned int d965_5st_pin_configs[14] = {
        0x40000100, 0x40000100
 };
 
+static unsigned int dell_3st_pin_configs[14] = {
+       0x02211230, 0x02a11220, 0x01a19040, 0x01114210,
+       0x01111212, 0x01116211, 0x01813050, 0x01112214,
+       0x403003fa, 0x40000100, 0x40000100, 0x404003fb,
+       0x40c003fc, 0x40000100
+};
+
 static unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = {
        [STAC_D965_REF] = ref927x_pin_configs,
        [STAC_D965_3ST] = d965_3st_pin_configs,
        [STAC_D965_5ST] = d965_5st_pin_configs,
+       [STAC_DELL_3ST] = dell_3st_pin_configs,
 };
 
 static const char *stac927x_models[STAC_927X_MODELS] = {
        [STAC_D965_REF] = "ref",
        [STAC_D965_3ST] = "3stack",
        [STAC_D965_5ST] = "5stack",
+       [STAC_DELL_3ST] = "dell-3stack",
 };
 
 static struct snd_pci_quirk stac927x_cfg_tbl[] = {
@@ -677,6 +767,10 @@ static struct snd_pci_quirk stac927x_cfg_tbl[] = {
        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),
+       /* Dell 3 stack systems */
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x01dd, "Dell E520", STAC_DELL_3ST),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x01ed, "Dell     ", STAC_DELL_3ST),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x01f4, "Dell     ", STAC_DELL_3ST),
        /* 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),
@@ -696,18 +790,58 @@ static unsigned int ref9205_pin_configs[12] = {
        0x90a000f0, 0x90a000f0, 0x01441030, 0x01c41030
 };
 
+static unsigned int dell_m43_9205_pin_configs[12] = {
+       0x0321101f, 0x03a11020, 0x90a70330, 0x90170310,
+       0x400000fe, 0x400000ff, 0x400000fd, 0x40f000f9,
+       0x400000fa, 0x400000fc, 0x0144131f, 0x40c003f8,
+};
+
+static unsigned int dell_m44_9205_pin_configs[12] = {
+       0x0421101f, 0x04a11020, 0x400003fa, 0x90170310,
+       0x400003fb, 0x400003fc, 0x400003fd, 0x400003f9,
+       0x90a60330, 0x400003ff, 0x01441340, 0x40c003fe,
+};
+
+
 static unsigned int *stac9205_brd_tbl[STAC_9205_MODELS] = {
-       ref9205_pin_configs,
+       [STAC_9205_REF] = ref9205_pin_configs,
+       [STAC_9205_DELL_M43] = dell_m43_9205_pin_configs,
+       [STAC_9205_DELL_M44] = dell_m44_9205_pin_configs,
+       [STAC_9205_M43xx] = NULL,
 };
 
 static const char *stac9205_models[STAC_9205_MODELS] = {
        [STAC_9205_REF] = "ref",
+       [STAC_9205_DELL_M43] = "dell-m43",
+       [STAC_9205_DELL_M44] = "dell-m44",
 };
 
 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_DELL, 0x01f8,
+                     "Dell Precision", STAC_9205_M43xx),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f9,
+                     "Dell Precision", STAC_9205_DELL_M43),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fa,
+                     "Dell Precision", STAC_9205_DELL_M43),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fe,
+                     "Dell Precision", STAC_9205_DELL_M43),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ff,
+                     "Dell Precision", STAC_9205_DELL_M43),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0206,
+                     "Dell Precision", STAC_9205_DELL_M43),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f1,
+                     "Dell Inspiron", STAC_9205_DELL_M44),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f2,
+                     "Dell Inspiron", STAC_9205_DELL_M44),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fc,
+                     "Dell Inspiron", STAC_9205_DELL_M44),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fd,
+                     "Dell Inspiron", STAC_9205_DELL_M44),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021f,
+                     "Dell Inspiron", STAC_9205_DELL_M44),
        {} /* terminator */
 };
 
@@ -737,33 +871,56 @@ static int stac92xx_save_bios_config_regs(struct hda_codec *codec)
        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)
 {
        int i;
        struct sigmatel_spec *spec = codec->spec;
-       unsigned int pin_cfg;
 
-       if (! spec->pin_nids || ! spec->pin_configs)
-               return;
+       if (!spec->pin_configs)
+               return;
 
-       for (i = 0; i < spec->num_pins; i++) {
-               snd_hda_codec_write(codec, spec->pin_nids[i], 0,
-                                   AC_VERB_SET_CONFIG_DEFAULT_BYTES_0,
-                                   spec->pin_configs[i] & 0x000000ff);
-               snd_hda_codec_write(codec, spec->pin_nids[i], 0,
-                                   AC_VERB_SET_CONFIG_DEFAULT_BYTES_1,
-                                   (spec->pin_configs[i] & 0x0000ff00) >> 8);
-               snd_hda_codec_write(codec, spec->pin_nids[i], 0,
-                                   AC_VERB_SET_CONFIG_DEFAULT_BYTES_2,
-                                   (spec->pin_configs[i] & 0x00ff0000) >> 16);
-               snd_hda_codec_write(codec, spec->pin_nids[i], 0,
-                                   AC_VERB_SET_CONFIG_DEFAULT_BYTES_3,
-                                   spec->pin_configs[i] >> 24);
-               pin_cfg = snd_hda_codec_read(codec, spec->pin_nids[i], 0,
-                                            AC_VERB_GET_CONFIG_DEFAULT,
-                                            0x00);     
-               snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x pin config %8.8x\n", spec->pin_nids[i], pin_cfg);
-       }
+       for (i = 0; i < spec->num_pins; i++)
+               stac92xx_set_config_reg(codec, spec->pin_nids[i],
+                                       spec->pin_configs[i]);
+}
+
+static void stac92xx_enable_gpio_mask(struct hda_codec *codec)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       /* Configure GPIOx as output */
+       snd_hda_codec_write_cache(codec, codec->afg, 0,
+                                 AC_VERB_SET_GPIO_DIRECTION, spec->gpio_mask);
+       /* Configure GPIOx as CMOS */
+       snd_hda_codec_write_cache(codec, codec->afg, 0, 0x7e7, 0x00000000);
+       /* Assert GPIOx */
+       snd_hda_codec_write_cache(codec, codec->afg, 0,
+                                 AC_VERB_SET_GPIO_DATA, spec->gpio_data);
+       /* Enable GPIOx */
+       snd_hda_codec_write_cache(codec, codec->afg, 0,
+                                 AC_VERB_SET_GPIO_MASK, spec->gpio_mask);
 }
 
 /*
@@ -814,6 +971,17 @@ static int stac92xx_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
        return snd_hda_multi_out_dig_close(codec, &spec->multiout);
 }
 
+static int stac92xx_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+                                        struct hda_codec *codec,
+                                        unsigned int stream_tag,
+                                        unsigned int format,
+                                        struct snd_pcm_substream *substream)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
+                                            stream_tag, format, substream);
+}
+
 
 /*
  * Analog capture callbacks
@@ -848,7 +1016,8 @@ static struct hda_pcm_stream stac92xx_pcm_digital_playback = {
        /* NID is set in stac92xx_build_pcms */
        .ops = {
                .open = stac92xx_dig_playback_pcm_open,
-               .close = stac92xx_dig_playback_pcm_close
+               .close = stac92xx_dig_playback_pcm_close,
+               .prepare = stac92xx_dig_playback_pcm_prepare
        },
 };
 
@@ -950,17 +1119,11 @@ static unsigned int stac92xx_get_vref(struct hda_codec *codec, hda_nid_t nid)
 static void stac92xx_auto_set_pinctl(struct hda_codec *codec, hda_nid_t nid, int pin_type)
 
 {
-       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
+       snd_hda_codec_write_cache(codec, nid, 0,
+                                 AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
 }
 
-static int stac92xx_io_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
-       uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
-       uinfo->count = 1;
-       uinfo->value.integer.min = 0;
-       uinfo->value.integer.max = 1;
-       return 0;
-}
+#define stac92xx_io_switch_info                snd_ctl_boolean_mono_info
 
 static int stac92xx_io_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
@@ -1049,11 +1212,23 @@ static int stac92xx_add_control(struct sigmatel_spec *spec, int type, const char
 static int stac92xx_add_dyn_out_pins(struct hda_codec *codec, struct auto_pin_cfg *cfg)
 {
        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;
+               if (wtype == AC_WID_AUD_OUT && !(wcaps & AC_WCAP_DIGITAL))
+                       num_dacs++;
+       }
 
+       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]) {
+               if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 3) {
                        cfg->line_out_pins[3] = cfg->input_pins[AUTO_PIN_LINE];
                        spec->line_switch = 1;
                        cfg->line_outs++;
@@ -1061,12 +1236,12 @@ static int stac92xx_add_dyn_out_pins(struct hda_codec *codec, struct auto_pin_cf
                break;
        case 2:
                /* add line-in as clfe and mic as side */
-               if (cfg->input_pins[AUTO_PIN_LINE]) {
+               if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 2) {
                        cfg->line_out_pins[2] = cfg->input_pins[AUTO_PIN_LINE];
                        spec->line_switch = 1;
                        cfg->line_outs++;
                }
-               if (cfg->input_pins[AUTO_PIN_MIC]) {
+               if (cfg->input_pins[AUTO_PIN_MIC] && num_dacs > 3) {
                        cfg->line_out_pins[3] = cfg->input_pins[AUTO_PIN_MIC];
                        spec->mic_switch = 1;
                        cfg->line_outs++;
@@ -1074,12 +1249,12 @@ static int stac92xx_add_dyn_out_pins(struct hda_codec *codec, struct auto_pin_cf
                break;
        case 1:
                /* add line-in as surr and mic as clfe */
-               if (cfg->input_pins[AUTO_PIN_LINE]) {
+               if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 1) {
                        cfg->line_out_pins[1] = cfg->input_pins[AUTO_PIN_LINE];
                        spec->line_switch = 1;
                        cfg->line_outs++;
                }
-               if (cfg->input_pins[AUTO_PIN_MIC]) {
+               if (cfg->input_pins[AUTO_PIN_MIC] && num_dacs > 2) {
                        cfg->line_out_pins[2] = cfg->input_pins[AUTO_PIN_MIC];
                        spec->mic_switch = 1;
                        cfg->line_outs++;
@@ -1090,33 +1265,83 @@ static int stac92xx_add_dyn_out_pins(struct hda_codec *codec, struct auto_pin_cf
        return 0;
 }
 
+
+static int is_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
+{
+       int i;
+       
+       for (i = 0; i < spec->multiout.num_dacs; i++) {
+               if (spec->multiout.dac_nids[i] == nid)
+                       return 1;
+       }
+
+       return 0;
+}
+
 /*
- * XXX The line_out pin widget connection list may not be set to the
- * desired DAC nid. This is the case on 927x where ports A and B can
- * be routed to several DACs.
- *
- * This requires an analysis of the line-out/hp pin configuration
- * to provide a best fit for pin/DAC configurations that are routable.
- * For now, 927x DAC4 is not supported and 927x DAC1 output to ports
- * A and B is not supported.
+ * Fill in the dac_nids table from the parsed pin configuration
+ * This function only works when every pin in line_out_pins[]
+ * contains atleast one DAC in its connection list. Some 92xx
+ * codecs are not connected directly to a DAC, such as the 9200
+ * and 9202/925x. For those, dac_nids[] must be hard-coded.
  */
-/* fill in the dac_nids table from the parsed pin configuration */
 static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec,
-                                      const struct auto_pin_cfg *cfg)
+                                      struct auto_pin_cfg *cfg)
 {
        struct sigmatel_spec *spec = codec->spec;
-       hda_nid_t nid;
-       int i;
-
-       /* check the pins hardwired to audio widget */
+       int i, j, conn_len = 0; 
+       hda_nid_t nid, conn[HDA_MAX_CONNECTIONS];
+       unsigned int wcaps, wtype;
+       
        for (i = 0; i < cfg->line_outs; i++) {
                nid = cfg->line_out_pins[i];
-               spec->multiout.dac_nids[i] = snd_hda_codec_read(codec, nid, 0,
-                                       AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
-       }
+               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) {
+                       if (spec->multiout.num_dacs > 0) {
+                               /* we have already working output pins,
+                                * so let's drop the broken ones again
+                                */
+                               cfg->line_outs = spec->multiout.num_dacs;
+                               break;
+                       }
+                       /* error out, no available DAC found */
+                       snd_printk(KERN_ERR
+                                  "%s: No available DAC for pin 0x%x\n",
+                                  __func__, nid);
+                       return -ENODEV;
+               }
+
+               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);
 
-       spec->multiout.num_dacs = cfg->line_outs;
+               }
+       }
 
+       snd_printd("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;
 }
 
@@ -1183,12 +1408,8 @@ static int stac92xx_auto_create_multi_out_ctls(struct sigmatel_spec *spec,
 
 static int check_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
 {
-       int i;
-
-       for (i = 0; i < spec->multiout.num_dacs; i++) {
-               if (spec->multiout.dac_nids[i] == nid)
-                       return 1;
-       }
+       if (is_in_dac_nids(spec, nid))
+               return 1;
        if (spec->multiout.hp_nid == nid)
                return 1;
        return 0;
@@ -1230,17 +1451,23 @@ static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec,
                add_spec_dacs(spec, nid);
        }
        for (i = 0; i < cfg->speaker_outs; i++) {
-               nid = snd_hda_codec_read(codec, cfg->speaker_pins[0], 0,
+               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",
@@ -1349,16 +1576,16 @@ static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const
                imux->num_items++;
        }
 
-       if (imux->num_items == 1) {
+       if (imux->num_items) {
                /*
                 * Set the current input for the muxes.
                 * The STAC9221 has two input muxes with identical source
                 * NID lists.  Hopefully this won't get confused.
                 */
                for (i = 0; i < spec->num_muxes; i++) {
-                       snd_hda_codec_write(codec, spec->mux_nids[i], 0,
-                                           AC_VERB_SET_CONNECT_SEL,
-                                           imux->items[0].index);
+                       snd_hda_codec_write_cache(codec, spec->mux_nids[i], 0,
+                                                 AC_VERB_SET_CONNECT_SEL,
+                                                 imux->items[0].index);
                }
        }
 
@@ -1669,9 +1896,28 @@ static void stac92xx_set_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);
-       if (flag == AC_PINCTL_OUT_EN && (pin_ctl & AC_PINCTL_IN_EN))
-               return;
-       snd_hda_codec_write(codec, nid, 0,
+
+       if (pin_ctl & AC_PINCTL_IN_EN) {
+               /*
+                * we need to check the current set-up direction of
+                * shared input pins since they can be switched via
+                * "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))
+                       return;
+       }
+
+       /* 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);
 }
@@ -1681,7 +1927,7 @@ 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(codec, nid, 0,
+       snd_hda_codec_write_cache(codec, nid, 0,
                        AC_VERB_SET_PIN_WIDGET_CONTROL,
                        pin_ctl & ~flag);
 }
@@ -1737,21 +1983,13 @@ static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
        }
 }
 
-#ifdef CONFIG_PM
+#ifdef SND_HDA_NEEDS_RESUME
 static int stac92xx_resume(struct hda_codec *codec)
 {
-       struct sigmatel_spec *spec = codec->spec;
-       int i;
-
-       stac92xx_init(codec);
        stac92xx_set_config_regs(codec);
-       for (i = 0; i < spec->num_mixers; i++)
-               snd_hda_resume_ctls(codec, spec->mixers[i]);
-       if (spec->multiout.dig_out_nid)
-               snd_hda_resume_spdif_out(codec);
-       if (spec->dig_in_nid)
-               snd_hda_resume_spdif_in(codec);
-
+       stac92xx_init(codec);
+       snd_hda_codec_resume_amp(codec);
+       snd_hda_codec_resume_cache(codec);
        return 0;
 }
 #endif
@@ -1762,7 +2000,7 @@ static struct hda_codec_ops stac92xx_patch_ops = {
        .init = stac92xx_init,
        .free = stac92xx_free,
        .unsol_event = stac92xx_unsol_event,
-#ifdef CONFIG_PM
+#ifdef SND_HDA_NEEDS_RESUME
        .resume = stac92xx_resume,
 #endif
 };
@@ -1777,7 +2015,7 @@ static int patch_stac9200(struct hda_codec *codec)
                return -ENOMEM;
 
        codec->spec = spec;
-       spec->num_pins = 8;
+       spec->num_pins = ARRAY_SIZE(stac9200_pin_nids);
        spec->pin_nids = stac9200_pin_nids;
        spec->board_config = snd_hda_check_board_config(codec, STAC_9200_MODELS,
                                                        stac9200_models,
@@ -1827,14 +2065,15 @@ static int patch_stac925x(struct hda_codec *codec)
                return -ENOMEM;
 
        codec->spec = spec;
-       spec->num_pins = 8;
+       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,
                                                        stac925x_models,
                                                        stac925x_cfg_tbl);
  again:
        if (spec->board_config < 0) {
-               snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC925x, using BIOS defaults\n");
+               snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC925x," 
+                                     "using BIOS defaults\n");
                err = stac92xx_save_bios_config_regs(codec);
                if (err < 0) {
                        stac92xx_free(codec);
@@ -1852,7 +2091,18 @@ static int patch_stac925x(struct hda_codec *codec)
        spec->adc_nids = stac925x_adc_nids;
        spec->mux_nids = stac925x_mux_nids;
        spec->num_muxes = 1;
-       spec->num_dmics = 0;
+       switch (codec->vendor_id) {
+       case 0x83847632: /* STAC9202  */
+       case 0x83847633: /* STAC9202D */
+       case 0x83847636: /* STAC9251  */
+       case 0x83847637: /* STAC9251D */
+               spec->num_dmics = 1;
+               spec->dmic_nids = stac925x_dmic_nids;
+               break;
+       default:
+               spec->num_dmics = 0;
+               break;
+       }
 
        spec->init = stac925x_core_init;
        spec->mixer = stac925x_mixer;
@@ -1887,23 +2137,41 @@ static int patch_stac922x(struct hda_codec *codec)
                return -ENOMEM;
 
        codec->spec = spec;
-       spec->num_pins = 10;
+       spec->num_pins = ARRAY_SIZE(stac922x_pin_nids);
        spec->pin_nids = stac922x_pin_nids;
        spec->board_config = snd_hda_check_board_config(codec, STAC_922X_MODELS,
                                                        stac922x_models,
                                                        stac922x_cfg_tbl);
-       if (spec->board_config == STAC_MACMINI) {
+       if (spec->board_config == STAC_INTEL_MAC_V3) {
                spec->gpio_mute = 1;
                /* Intel Macs have all same PCI SSID, so we need to check
                 * codec SSID to distinguish the exact models
                 */
                printk(KERN_INFO "hda_codec: STAC922x, Apple subsys_id=%x\n", codec->subsystem_id);
                switch (codec->subsystem_id) {
-               case 0x106b0200: /* MacBook Pro first generation */
-                       spec->board_config = STAC_MACBOOK_PRO_V1;
+
+               case 0x106b0800:
+                       spec->board_config = STAC_INTEL_MAC_V1;
+                       break;
+               case 0x106b0600:
+               case 0x106b0700:
+                       spec->board_config = STAC_INTEL_MAC_V2;
                        break;
-               case 0x106b1e00: /* MacBook Pro second generation */
-                       spec->board_config = STAC_MACBOOK_PRO_V2;
+               case 0x106b0e00:
+               case 0x106b0f00:
+               case 0x106b1600:
+               case 0x106b1700:
+               case 0x106b0200:
+               case 0x106b1e00:
+                       spec->board_config = STAC_INTEL_MAC_V3;
+                       break;
+               case 0x106b1a00:
+               case 0x00000100:
+                       spec->board_config = STAC_INTEL_MAC_V4;
+                       break;
+               case 0x106b0a00:
+               case 0x106b2200:
+                       spec->board_config = STAC_INTEL_MAC_V5;
                        break;
                }
        }
@@ -1950,6 +2218,13 @@ static int patch_stac922x(struct hda_codec *codec)
 
        codec->patch_ops = stac92xx_patch_ops;
 
+       /* Fix Mux capture level; max to 2 */
+       snd_hda_override_amp_caps(codec, 0x12, HDA_OUTPUT,
+                                 (0 << AC_AMPCAP_OFFSET_SHIFT) |
+                                 (2 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+                                 (0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+                                 (0 << AC_AMPCAP_MUTE_SHIFT));
+
        return 0;
 }
 
@@ -1963,7 +2238,7 @@ static int patch_stac927x(struct hda_codec *codec)
                return -ENOMEM;
 
        codec->spec = spec;
-       spec->num_pins = 14;
+       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,
@@ -2009,7 +2284,10 @@ static int patch_stac927x(struct hda_codec *codec)
        }
 
        spec->multiout.dac_nids = spec->dac_nids;
-
+       /* GPIO0 High = Enable EAPD */
+       spec->gpio_mask = spec->gpio_data = 0x00000001;
+       stac92xx_enable_gpio_mask(codec); 
+       
        err = stac92xx_parse_auto_config(codec, 0x1e, 0x20);
        if (!err) {
                if (spec->board_config < 0) {
@@ -2040,7 +2318,7 @@ static int patch_stac9205(struct hda_codec *codec)
                return -ENOMEM;
 
        codec->spec = spec;
-       spec->num_pins = 14;
+       spec->num_pins = ARRAY_SIZE(stac9205_pin_nids);
        spec->pin_nids = stac9205_pin_nids;
        spec->board_config = snd_hda_check_board_config(codec, STAC_9205_MODELS,
                                                        stac9205_models,
@@ -2070,19 +2348,27 @@ static int patch_stac9205(struct hda_codec *codec)
        spec->mixer = stac9205_mixer;
 
        spec->multiout.dac_nids = spec->dac_nids;
+       
+       switch (spec->board_config){
+       case STAC_9205_M43xx:
+       case STAC_9205_DELL_M43:
+               /* Enable SPDIF in/out */
+               stac92xx_set_config_reg(codec, 0x1f, 0x01441030);
+               stac92xx_set_config_reg(codec, 0x20, 0x1c410030);
+
+               spec->gpio_mask = 0x00000007; /* GPIO0-2 */
+               /* GPIO0 High = EAPD, GPIO1 Low = DRM,
+                * GPIO2 High = Headphone Mute
+                */
+               spec->gpio_data = 0x00000005;
+               break;
+       default:
+               /* GPIO0 High = EAPD */
+               spec->gpio_mask = spec->gpio_data = 0x00000001;
+               break;
+       }
 
-       /* Configure GPIO0 as EAPD output */
-       snd_hda_codec_write(codec, codec->afg, 0,
-                           AC_VERB_SET_GPIO_DIRECTION, 0x00000001);
-       /* Configure GPIO0 as CMOS */
-       snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0x00000000);
-       /* Assert GPIO0 high */
-       snd_hda_codec_write(codec, codec->afg, 0,
-                           AC_VERB_SET_GPIO_DATA, 0x00000001);
-       /* Enable GPIO0 */
-       snd_hda_codec_write(codec, codec->afg, 0,
-                           AC_VERB_SET_GPIO_MASK, 0x00000001);
-
+       stac92xx_enable_gpio_mask(codec);
        err = stac92xx_parse_auto_config(codec, 0x1f, 0x20);
        if (!err) {
                if (spec->board_config < 0) {
@@ -2117,19 +2403,20 @@ static struct hda_input_mux vaio_mux = {
        .num_items = 2,
        .items = {
                /* { "HP", 0x0 }, */
-               { "Line", 0x1 },
-               { "Mic", 0x2 },
+               { "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? */
-       {0x15, AC_VERB_SET_CONNECT_SEL, 0x2}, /* mic-sel: 0a,0d,14,02 */
+       {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 */
@@ -2145,7 +2432,7 @@ static struct hda_verb vaio_ar_init[] = {
        {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, 0x2}, /* mic-sel: 0a,0d,14,02 */
+       {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 */
@@ -2156,61 +2443,28 @@ static struct hda_verb vaio_ar_init[] = {
 };
 
 /* bind volumes of both NID 0x02 and 0x05 */
-static int vaio_master_vol_put(struct snd_kcontrol *kcontrol,
-                              struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       long *valp = ucontrol->value.integer.value;
-       int change;
-
-       change = snd_hda_codec_amp_update(codec, 0x02, 0, HDA_OUTPUT, 0,
-                                         0x7f, valp[0] & 0x7f);
-       change |= snd_hda_codec_amp_update(codec, 0x02, 1, HDA_OUTPUT, 0,
-                                          0x7f, valp[1] & 0x7f);
-       snd_hda_codec_amp_update(codec, 0x05, 0, HDA_OUTPUT, 0,
-                                0x7f, valp[0] & 0x7f);
-       snd_hda_codec_amp_update(codec, 0x05, 1, HDA_OUTPUT, 0,
-                                0x7f, valp[1] & 0x7f);
-       return change;
-}
+static struct hda_bind_ctls vaio_bind_master_vol = {
+       .ops = &snd_hda_bind_vol,
+       .values = {
+               HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
+               HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
+               0
+       },
+};
 
 /* bind volumes of both NID 0x02 and 0x05 */
-static int vaio_master_sw_put(struct snd_kcontrol *kcontrol,
-                             struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       long *valp = ucontrol->value.integer.value;
-       int change;
-
-       change = snd_hda_codec_amp_update(codec, 0x02, 0, HDA_OUTPUT, 0,
-                                         0x80, (valp[0] ? 0 : 0x80));
-       change |= snd_hda_codec_amp_update(codec, 0x02, 1, HDA_OUTPUT, 0,
-                                          0x80, (valp[1] ? 0 : 0x80));
-       snd_hda_codec_amp_update(codec, 0x05, 0, HDA_OUTPUT, 0,
-                                0x80, (valp[0] ? 0 : 0x80));
-       snd_hda_codec_amp_update(codec, 0x05, 1, HDA_OUTPUT, 0,
-                                0x80, (valp[1] ? 0 : 0x80));
-       return change;
-}
+static struct hda_bind_ctls vaio_bind_master_sw = {
+       .ops = &snd_hda_bind_sw,
+       .values = {
+               HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
+               HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
+               0,
+       },
+};
 
 static struct snd_kcontrol_new vaio_mixer[] = {
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Master Playback Volume",
-               .info = snd_hda_mixer_amp_volume_info,
-               .get = snd_hda_mixer_amp_volume_get,
-               .put = vaio_master_vol_put,
-               .tlv = { .c = snd_hda_mixer_amp_tlv },
-               .private_value = HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
-       },
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Master Playback Switch",
-               .info = snd_hda_mixer_amp_switch_info,
-               .get = snd_hda_mixer_amp_switch_get,
-               .put = vaio_master_sw_put,
-               .private_value = HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
-       },
+       HDA_BIND_VOL("Master Playback Volume", &vaio_bind_master_vol),
+       HDA_BIND_SW("Master Playback Switch", &vaio_bind_master_sw),
        /* 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),
@@ -2226,22 +2480,8 @@ static struct snd_kcontrol_new vaio_mixer[] = {
 };
 
 static struct snd_kcontrol_new vaio_ar_mixer[] = {
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Master Playback Volume",
-               .info = snd_hda_mixer_amp_volume_info,
-               .get = snd_hda_mixer_amp_volume_get,
-               .put = vaio_master_vol_put,
-               .private_value = HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
-       },
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Master Playback Switch",
-               .info = snd_hda_mixer_amp_switch_info,
-               .get = snd_hda_mixer_amp_switch_get,
-               .put = vaio_master_sw_put,
-               .private_value = HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
-       },
+       HDA_BIND_VOL("Master Playback Volume", &vaio_bind_master_vol),
+       HDA_BIND_SW("Master Playback Switch", &vaio_bind_master_sw),
        /* 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),
@@ -2263,6 +2503,49 @@ static struct hda_codec_ops stac9872_patch_ops = {
        .build_pcms = stac92xx_build_pcms,
        .init = stac92xx_init,
        .free = stac92xx_free,
+#ifdef SND_HDA_NEEDS_RESUME
+       .resume = stac92xx_resume,
+#endif
+};
+
+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_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
@@ -2323,6 +2606,7 @@ static int patch_stac9872(struct hda_codec *codec)
                spec->adc_nids = vaio_adcs;
                spec->input_mux = &vaio_mux;
                spec->mux_nids = vaio_mux_nids;
+               codec->patch_ops = stac9872_vaio_patch_ops;
                break;
        
        case CXD9872AKD_VAIO:
@@ -2336,10 +2620,10 @@ static int patch_stac9872(struct hda_codec *codec)
                spec->adc_nids = vaio_adcs;
                spec->input_mux = &vaio_mux;
                spec->mux_nids = vaio_mux_nids;
+               codec->patch_ops = stac9872_patch_ops;
                break;
        }
 
-       codec->patch_ops = stac9872_patch_ops;
        return 0;
 }