ALSA: hda: enable SPDIF output for Conexant 5051/Lenovo docking stations
[safe/jmp/linux-2.6] / sound / pci / hda / patch_conexant.c
index 46e93c6..0aa122e 100644 (file)
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/pci.h>
 #include <sound/core.h>
+#include <sound/jack.h>
+
 #include "hda_codec.h"
 #include "hda_local.h"
+#include "hda_beep.h"
 
 #define CXT_PIN_DIR_IN              0x00
 #define CXT_PIN_DIR_OUT             0x01
 #define CONEXANT_HP_EVENT      0x37
 #define CONEXANT_MIC_EVENT     0x38
 
+/* Conexant 5051 specific */
+
+#define CXT5051_SPDIF_OUT      0x12
+#define CXT5051_PORTB_EVENT    0x38
+#define CXT5051_PORTC_EVENT    0x39
+
+#define AUTO_MIC_PORTB         (1 << 1)
+#define AUTO_MIC_PORTC         (1 << 2)
+
+struct conexant_jack {
 
+       hda_nid_t nid;
+       int type;
+       struct snd_jack *jack;
+
+};
 
 struct conexant_spec {
 
        struct snd_kcontrol_new *mixers[5];
        int num_mixers;
+       hda_nid_t vmaster_nid;
 
        const struct hda_verb *init_verbs[5];   /* initialization verbs
                                                 * don't forget NULL
@@ -58,6 +76,7 @@ struct conexant_spec {
                                         */
        unsigned int cur_eapd;
        unsigned int hp_present;
+       unsigned int auto_mic;
        unsigned int need_dac_fix;
 
        /* capture */
@@ -65,6 +84,11 @@ struct conexant_spec {
        hda_nid_t *adc_nids;
        hda_nid_t dig_in_nid;           /* digital-in NID; optional */
 
+       unsigned int cur_adc_idx;
+       hda_nid_t cur_adc;
+       unsigned int cur_adc_stream_tag;
+       unsigned int cur_adc_format;
+
        /* capture source */
        const struct hda_input_mux *input_mux;
        hda_nid_t *capsrc_nids;
@@ -77,16 +101,36 @@ struct conexant_spec {
        /* PCM information */
        struct hda_pcm pcm_rec[2];      /* used in build_pcms() */
 
-       struct mutex amp_mutex; /* PCM volume/mute control mutex */
        unsigned int spdif_route;
 
+       /* jack detection */
+       struct snd_array jacks;
+
        /* dynamic controls, init_verbs and input_mux */
        struct auto_pin_cfg autocfg;
-       unsigned int num_kctl_alloc, num_kctl_used;
-       struct snd_kcontrol_new *kctl_alloc;
        struct hda_input_mux private_imux;
-       hda_nid_t private_dac_nids[4];
-
+       hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
+
+       unsigned int dell_automute;
+       unsigned int port_d_mode;
+       unsigned int dell_vostro:1;
+       unsigned int ideapad:1;
+       unsigned int thinkpad:1;
+
+       unsigned int ext_mic_present;
+       unsigned int recording;
+       void (*capture_prepare)(struct hda_codec *codec);
+       void (*capture_cleanup)(struct hda_codec *codec);
+
+       /* OLPC XO-1.5 supports DC input mode (e.g. for use with analog sensors)
+        * through the microphone jack.
+        * When the user enables this through a mixer switch, both internal and
+        * external microphones are disabled. Gain is fixed at 0dB. In this mode,
+        * we also allow the bias to be configured through a separate mixer
+        * control. */
+       unsigned int dc_enable;
+       unsigned int dc_input_bias; /* offset into cxt5066_olpc_dc_bias */
+       unsigned int mic_boost; /* offset into cxt5066_analog_mic_boost */
 };
 
 static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo,
@@ -94,7 +138,8 @@ static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo,
                                      struct snd_pcm_substream *substream)
 {
        struct conexant_spec *spec = codec->spec;
-       return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
+       return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
+                                            hinfo);
 }
 
 static int conexant_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
@@ -136,6 +181,18 @@ static int conexant_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
        return snd_hda_multi_out_dig_close(codec, &spec->multiout);
 }
 
+static int conexant_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 conexant_spec *spec = codec->spec;
+       return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
+                                            stream_tag,
+                                            format, substream);
+}
+
 /*
  * Analog capture
  */
@@ -146,6 +203,8 @@ static int conexant_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
                                      struct snd_pcm_substream *substream)
 {
        struct conexant_spec *spec = codec->spec;
+       if (spec->capture_prepare)
+               spec->capture_prepare(codec);
        snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
                                   stream_tag, 0, format);
        return 0;
@@ -156,8 +215,9 @@ static int conexant_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
                                      struct snd_pcm_substream *substream)
 {
        struct conexant_spec *spec = codec->spec;
-       snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
-                                  0, 0, 0);
+       snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
+       if (spec->capture_cleanup)
+               spec->capture_cleanup(codec);
        return 0;
 }
 
@@ -194,7 +254,8 @@ static struct hda_pcm_stream conexant_pcm_digital_playback = {
        .nid = 0, /* fill later */
        .ops = {
                .open = conexant_dig_playback_pcm_open,
-               .close = conexant_dig_playback_pcm_close
+               .close = conexant_dig_playback_pcm_close,
+               .prepare = conexant_dig_playback_pcm_prepare
        },
 };
 
@@ -205,6 +266,41 @@ static struct hda_pcm_stream conexant_pcm_digital_capture = {
        /* NID is set in alc_build_pcms */
 };
 
+static int cx5051_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+                                     struct hda_codec *codec,
+                                     unsigned int stream_tag,
+                                     unsigned int format,
+                                     struct snd_pcm_substream *substream)
+{
+       struct conexant_spec *spec = codec->spec;
+       spec->cur_adc = spec->adc_nids[spec->cur_adc_idx];
+       spec->cur_adc_stream_tag = stream_tag;
+       spec->cur_adc_format = format;
+       snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
+       return 0;
+}
+
+static int cx5051_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                                     struct hda_codec *codec,
+                                     struct snd_pcm_substream *substream)
+{
+       struct conexant_spec *spec = codec->spec;
+       snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
+       spec->cur_adc = 0;
+       return 0;
+}
+
+static struct hda_pcm_stream cx5051_pcm_analog_capture = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+       .nid = 0, /* fill later */
+       .ops = {
+               .prepare = cx5051_capture_pcm_prepare,
+               .cleanup = cx5051_capture_pcm_cleanup
+       },
+};
+
 static int conexant_build_pcms(struct hda_codec *codec)
 {
        struct conexant_spec *spec = codec->spec;
@@ -219,7 +315,12 @@ static int conexant_build_pcms(struct hda_codec *codec)
                spec->multiout.max_channels;
        info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
                spec->multiout.dac_nids[0];
-       info->stream[SNDRV_PCM_STREAM_CAPTURE] = conexant_pcm_analog_capture;
+       if (codec->vendor_id == 0x14f15051)
+               info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+                       cx5051_pcm_analog_capture;
+       else
+               info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+                       conexant_pcm_analog_capture;
        info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids;
        info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
 
@@ -227,6 +328,7 @@ static int conexant_build_pcms(struct hda_codec *codec)
                info++;
                codec->num_pcms++;
                info->name = "Conexant Digital";
+               info->pcm_type = HDA_PCM_TYPE_SPDIF;
                info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
                        conexant_pcm_digital_playback;
                info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
@@ -274,46 +376,156 @@ static int conexant_mux_enum_put(struct snd_kcontrol *kcontrol,
                                     &spec->cur_mux[adc_idx]);
 }
 
-static int conexant_init(struct hda_codec *codec)
+#ifdef CONFIG_SND_HDA_INPUT_JACK
+static void conexant_free_jack_priv(struct snd_jack *jack)
 {
-       struct conexant_spec *spec = codec->spec;
-       int i;
+       struct conexant_jack *jacks = jack->private_data;
+       jacks->nid = 0;
+       jacks->jack = NULL;
+}
 
-       for (i = 0; i < spec->num_init_verbs; i++)
-               snd_hda_sequence_write(codec, spec->init_verbs[i]);
+static int conexant_add_jack(struct hda_codec *codec,
+               hda_nid_t nid, int type)
+{
+       struct conexant_spec *spec;
+       struct conexant_jack *jack;
+       const char *name;
+       int err;
+
+       spec = codec->spec;
+       snd_array_init(&spec->jacks, sizeof(*jack), 32);
+       jack = snd_array_new(&spec->jacks);
+       name = (type == SND_JACK_HEADPHONE) ? "Headphone" : "Mic" ;
+
+       if (!jack)
+               return -ENOMEM;
+
+       jack->nid = nid;
+       jack->type = type;
+
+       err = snd_jack_new(codec->bus->card, name, type, &jack->jack);
+       if (err < 0)
+               return err;
+       jack->jack->private_data = jack;
+       jack->jack->private_free = conexant_free_jack_priv;
        return 0;
 }
 
-static void conexant_free(struct hda_codec *codec)
+static void conexant_report_jack(struct hda_codec *codec, hda_nid_t nid)
 {
-        struct conexant_spec *spec = codec->spec;
-        unsigned int i;
+       struct conexant_spec *spec = codec->spec;
+       struct conexant_jack *jacks = spec->jacks.list;
 
-        if (spec->kctl_alloc) {
-                for (i = 0; i < spec->num_kctl_used; i++)
-                        kfree(spec->kctl_alloc[i].name);
-                kfree(spec->kctl_alloc);
-        }
+       if (jacks) {
+               int i;
+               for (i = 0; i < spec->jacks.used; i++) {
+                       if (jacks->nid == nid) {
+                               unsigned int present;
+                               present = snd_hda_jack_detect(codec, nid);
 
-       kfree(codec->spec);
+                               present = (present) ? jacks->type : 0 ;
+
+                               snd_jack_report(jacks->jack,
+                                               present);
+                       }
+                       jacks++;
+               }
+       }
+}
+
+static int conexant_init_jacks(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+       int i;
+
+       for (i = 0; i < spec->num_init_verbs; i++) {
+               const struct hda_verb *hv;
+
+               hv = spec->init_verbs[i];
+               while (hv->nid) {
+                       int err = 0;
+                       switch (hv->param ^ AC_USRSP_EN) {
+                       case CONEXANT_HP_EVENT:
+                               err = conexant_add_jack(codec, hv->nid,
+                                               SND_JACK_HEADPHONE);
+                               conexant_report_jack(codec, hv->nid);
+                               break;
+                       case CXT5051_PORTC_EVENT:
+                       case CONEXANT_MIC_EVENT:
+                               err = conexant_add_jack(codec, hv->nid,
+                                               SND_JACK_MICROPHONE);
+                               conexant_report_jack(codec, hv->nid);
+                               break;
+                       }
+                       if (err < 0)
+                               return err;
+                       ++hv;
+               }
+       }
+       return 0;
+
+}
+#else
+static inline void conexant_report_jack(struct hda_codec *codec, hda_nid_t nid)
+{
+}
+
+static inline int conexant_init_jacks(struct hda_codec *codec)
+{
+       return 0;
 }
+#endif
 
-#ifdef CONFIG_PM
-static int conexant_resume(struct hda_codec *codec)
+static int conexant_init(struct hda_codec *codec)
 {
        struct conexant_spec *spec = codec->spec;
        int i;
 
-       codec->patch_ops.init(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);
+       for (i = 0; i < spec->num_init_verbs; i++)
+               snd_hda_sequence_write(codec, spec->init_verbs[i]);
        return 0;
 }
+
+static void conexant_free(struct hda_codec *codec)
+{
+#ifdef CONFIG_SND_HDA_INPUT_JACK
+       struct conexant_spec *spec = codec->spec;
+       if (spec->jacks.list) {
+               struct conexant_jack *jacks = spec->jacks.list;
+               int i;
+               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
+       snd_hda_detach_beep_device(codec);
+       kfree(codec->spec);
+}
+
+static struct snd_kcontrol_new cxt_capture_mixers[] = {
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Capture Source",
+               .info = conexant_mux_enum_info,
+               .get = conexant_mux_enum_get,
+               .put = conexant_mux_enum_put
+       },
+       {}
+};
+
+static const char *slave_vols[] = {
+       "Headphone Playback Volume",
+       "Speaker Playback Volume",
+       NULL
+};
+
+static const char *slave_sws[] = {
+       "Headphone Playback Switch",
+       "Speaker Playback Switch",
+       NULL
+};
 
 static int conexant_build_controls(struct hda_codec *codec)
 {
@@ -331,12 +543,43 @@ static int conexant_build_controls(struct hda_codec *codec)
                                                    spec->multiout.dig_out_nid);
                if (err < 0)
                        return err;
+               err = snd_hda_create_spdif_share_sw(codec,
+                                                   &spec->multiout);
+               if (err < 0)
+                       return err;
+               spec->multiout.share_spdif = 1;
        } 
        if (spec->dig_in_nid) {
                err = snd_hda_create_spdif_in_ctls(codec,spec->dig_in_nid);
                if (err < 0)
                        return err;
        }
+
+       /* if we have no master control, let's create it */
+       if (spec->vmaster_nid &&
+           !snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
+               unsigned int vmaster_tlv[4];
+               snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
+                                       HDA_OUTPUT, vmaster_tlv);
+               err = snd_hda_add_vmaster(codec, "Master Playback Volume",
+                                         vmaster_tlv, slave_vols);
+               if (err < 0)
+                       return err;
+       }
+       if (spec->vmaster_nid &&
+           !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
+               err = snd_hda_add_vmaster(codec, "Master Playback Switch",
+                                         NULL, slave_sws);
+               if (err < 0)
+                       return err;
+       }
+
+       if (spec->input_mux) {
+               err = snd_hda_add_new_ctls(codec, cxt_capture_mixers);
+               if (err < 0)
+                       return err;
+       }
+
        return 0;
 }
 
@@ -345,9 +588,6 @@ static struct hda_codec_ops conexant_patch_ops = {
        .build_pcms = conexant_build_pcms,
        .init = conexant_init,
        .free = conexant_free,
-#ifdef CONFIG_PM
-       .resume = conexant_resume,
-#endif
 };
 
 /*
@@ -355,15 +595,7 @@ static struct hda_codec_ops conexant_patch_ops = {
  * the private value = nid | (invert << 8)
  */
 
-static int cxt_eapd_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 cxt_eapd_info          snd_ctl_boolean_mono_info
 
 static int cxt_eapd_get(struct snd_kcontrol *kcontrol,
                             struct snd_ctl_elem_value *ucontrol)
@@ -388,16 +620,16 @@ static int cxt_eapd_put(struct snd_kcontrol *kcontrol,
        hda_nid_t nid = kcontrol->private_value & 0xff;
        unsigned int eapd;
 
-       eapd = ucontrol->value.integer.value[0];
+       eapd = !!ucontrol->value.integer.value[0];
        if (invert)
                eapd = !eapd;
-       if (eapd == spec->cur_eapd && !codec->in_resume)
+       if (eapd == spec->cur_eapd)
                return 0;
        
        spec->cur_eapd = eapd;
-       snd_hda_codec_write(codec, nid,
-                           0, AC_VERB_SET_EAPD_BTLENABLE,
-                           eapd ? 0x02 : 0x00);
+       snd_hda_codec_write_cache(codec, nid,
+                                 0, AC_VERB_SET_EAPD_BTLENABLE,
+                                 eapd ? 0x02 : 0x00);
        return 1;
 }
 
@@ -452,115 +684,6 @@ static int conexant_ch_mode_put(struct snd_kcontrol *kcontrol,
          .put = conexant_ch_mode_put, \
          .private_value = nid | (dir<<16) }
 
-static int cxt_gpio_data_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;
-}                                
-
-static int cxt_gpio_data_get(struct snd_kcontrol *kcontrol,
-                            struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       hda_nid_t nid = kcontrol->private_value & 0xffff;
-       unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
-       long *valp = ucontrol->value.integer.value;
-       unsigned int val = snd_hda_codec_read(codec, nid, 0,
-                                             AC_VERB_GET_GPIO_DATA, 0x00);
-
-       *valp = (val & mask) != 0;
-       return 0;
-}
-
-static int cxt_gpio_data_put(struct snd_kcontrol *kcontrol,
-                            struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       hda_nid_t nid = kcontrol->private_value & 0xffff;
-       unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
-       long val = *ucontrol->value.integer.value;
-       unsigned int gpio_data = snd_hda_codec_read(codec, nid, 0,
-                                                   AC_VERB_GET_GPIO_DATA,
-                                                   0x00);
-       unsigned int old_data = gpio_data;
-
-       /* Set/unset the masked GPIO bit(s) as needed */
-       if (val == 0)
-               gpio_data &= ~mask;
-       else
-               gpio_data |= mask;
-       if (gpio_data == old_data && !codec->in_resume)
-               return 0;
-       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_GPIO_DATA, gpio_data);
-       return 1;
-}
-
-#define CXT_GPIO_DATA_SWITCH(xname, nid, mask) \
-       { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0,  \
-         .info = cxt_gpio_data_info, \
-         .get = cxt_gpio_data_get, \
-         .put = cxt_gpio_data_put, \
-         .private_value = nid | (mask<<16) }
-#if 0
-static int cxt_spdif_ctrl_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;
-}                                
-
-static int cxt_spdif_ctrl_get(struct snd_kcontrol *kcontrol,
-                             struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       hda_nid_t nid = kcontrol->private_value & 0xffff;
-       unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
-       long *valp = ucontrol->value.integer.value;
-       unsigned int val = snd_hda_codec_read(codec, nid, 0,
-                                             AC_VERB_GET_DIGI_CONVERT, 0x00);
-
-       *valp = (val & mask) != 0;
-       return 0;
-}
-
-static int cxt_spdif_ctrl_put(struct snd_kcontrol *kcontrol,
-                             struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       hda_nid_t nid = kcontrol->private_value & 0xffff;
-       unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
-       long val = *ucontrol->value.integer.value;
-       unsigned int ctrl_data = snd_hda_codec_read(codec, nid, 0,
-                                                   AC_VERB_GET_DIGI_CONVERT,
-                                                   0x00);
-       unsigned int old_data = ctrl_data;
-
-       /* Set/unset the masked control bit(s) as needed */
-       if (val == 0)
-               ctrl_data &= ~mask;
-       else
-               ctrl_data |= mask;
-       if (ctrl_data == old_data && !codec->in_resume)
-               return 0;
-       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
-                           ctrl_data);
-       return 1;
-}
-
-#define CXT_SPDIF_CTRL_SWITCH(xname, nid, mask) \
-       { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0,  \
-         .info = cxt_spdif_ctrl_info, \
-         .get = cxt_spdif_ctrl_get, \
-         .put = cxt_spdif_ctrl_put, \
-         .private_value = nid | (mask<<16) }
-#endif
 #endif /* CONFIG_SND_DEBUG */
 
 /* Conexant 5045 specific */
@@ -568,7 +691,7 @@ static int cxt_spdif_ctrl_put(struct snd_kcontrol *kcontrol,
 static hda_nid_t cxt5045_dac_nids[1] = { 0x19 };
 static hda_nid_t cxt5045_adc_nids[1] = { 0x1a };
 static hda_nid_t cxt5045_capsrc_nids[1] = { 0x1a };
-#define CXT5045_SPDIF_OUT      0x13
+#define CXT5045_SPDIF_OUT      0x18
 
 static struct hda_channel_mode cxt5045_modes[1] = {
        { 2, NULL },
@@ -578,7 +701,26 @@ static struct hda_input_mux cxt5045_capture_source = {
        .num_items = 2,
        .items = {
                { "IntMic", 0x1 },
-               { "LineIn", 0x2 },
+               { "ExtMic", 0x2 },
+       }
+};
+
+static struct hda_input_mux cxt5045_capture_source_benq = {
+       .num_items = 5,
+       .items = {
+               { "IntMic", 0x1 },
+               { "ExtMic", 0x2 },
+               { "LineIn", 0x3 },
+               { "CD",     0x4 },
+               { "Mixer",  0x0 },
+       }
+};
+
+static struct hda_input_mux cxt5045_capture_source_hp530 = {
+       .num_items = 2,
+       .items = {
+               { "ExtMic", 0x1 },
+               { "IntMic", 0x2 },
        }
 };
 
@@ -596,32 +738,46 @@ static int cxt5045_hp_master_sw_put(struct snd_kcontrol *kcontrol,
        /* toggle internal speakers mute depending of presence of
         * the headphone jack
         */
-       bits = (!spec->hp_present && spec->cur_eapd) ? 0 : 0x80;
-       snd_hda_codec_amp_update(codec, 0x10, 0, HDA_OUTPUT, 0, 0x80, bits);
-       snd_hda_codec_amp_update(codec, 0x10, 1, HDA_OUTPUT, 0, 0x80, bits);
-       bits = spec->cur_eapd ? 0 : 0x80;
-       snd_hda_codec_amp_update(codec, 0x11, 0, HDA_OUTPUT, 0, 0x80, bits);
-       snd_hda_codec_amp_update(codec, 0x11, 1, HDA_OUTPUT, 0, 0x80, bits);
+       bits = (!spec->hp_present && spec->cur_eapd) ? 0 : HDA_AMP_MUTE;
+       snd_hda_codec_amp_stereo(codec, 0x10, HDA_OUTPUT, 0,
+                                HDA_AMP_MUTE, bits);
+
+       bits = spec->cur_eapd ? 0 : HDA_AMP_MUTE;
+       snd_hda_codec_amp_stereo(codec, 0x11, HDA_OUTPUT, 0,
+                                HDA_AMP_MUTE, bits);
        return 1;
 }
 
 /* bind volumes of both NID 0x10 and 0x11 */
-static int cxt5045_hp_master_vol_put(struct snd_kcontrol *kcontrol,
-                                    struct snd_ctl_elem_value *ucontrol)
+static struct hda_bind_ctls cxt5045_hp_bind_master_vol = {
+       .ops = &snd_hda_bind_vol,
+       .values = {
+               HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT),
+               HDA_COMPOSE_AMP_VAL(0x11, 3, 0, HDA_OUTPUT),
+               0
+       },
+};
+
+/* toggle input of built-in and mic jack appropriately */
+static void cxt5045_hp_automic(struct hda_codec *codec)
 {
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       long *valp = ucontrol->value.integer.value;
-       int change;
+       static struct hda_verb mic_jack_on[] = {
+               {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+               {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+               {}
+       };
+       static struct hda_verb mic_jack_off[] = {
+               {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+               {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+               {}
+       };
+       unsigned int present;
 
-       change = snd_hda_codec_amp_update(codec, 0x10, 0, HDA_OUTPUT, 0,
-                                         0x7f, valp[0] & 0x7f);
-       change |= snd_hda_codec_amp_update(codec, 0x10, 1, HDA_OUTPUT, 0,
-                                          0x7f, valp[1] & 0x7f);
-       snd_hda_codec_amp_update(codec, 0x11, 0, HDA_OUTPUT, 0,
-                                0x7f, valp[0] & 0x7f);
-       snd_hda_codec_amp_update(codec, 0x11, 1, HDA_OUTPUT, 0,
-                                0x7f, valp[1] & 0x7f);
-       return change;
+       present = snd_hda_jack_detect(codec, 0x12);
+       if (present)
+               snd_hda_sequence_write(codec, mic_jack_on);
+       else
+               snd_hda_sequence_write(codec, mic_jack_off);
 }
 
 
@@ -631,12 +787,11 @@ static void cxt5045_hp_automute(struct hda_codec *codec)
        struct conexant_spec *spec = codec->spec;
        unsigned int bits;
 
-       spec->hp_present = snd_hda_codec_read(codec, 0x11, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       spec->hp_present = snd_hda_jack_detect(codec, 0x11);
 
-       bits = (spec->hp_present || !spec->cur_eapd) ? 0x80 : 0;
-       snd_hda_codec_amp_update(codec, 0x10, 0, HDA_OUTPUT, 0, 0x80, bits);
-       snd_hda_codec_amp_update(codec, 0x10, 1, HDA_OUTPUT, 0, 0x80, bits);
+       bits = (spec->hp_present || !spec->cur_eapd) ? HDA_AMP_MUTE : 0; 
+       snd_hda_codec_amp_stereo(codec, 0x10, HDA_OUTPUT, 0,
+                                HDA_AMP_MUTE, bits);
 }
 
 /* unsolicited event for HP jack sensing */
@@ -648,31 +803,66 @@ static void cxt5045_hp_unsol_event(struct hda_codec *codec,
        case CONEXANT_HP_EVENT:
                cxt5045_hp_automute(codec);
                break;
+       case CONEXANT_MIC_EVENT:
+               cxt5045_hp_automic(codec);
+               break;
+
        }
 }
 
 static struct snd_kcontrol_new cxt5045_mixers[] = {
+       HDA_CODEC_VOLUME("Int Mic Capture Volume", 0x1a, 0x01, HDA_INPUT),
+       HDA_CODEC_MUTE("Int Mic Capture Switch", 0x1a, 0x01, HDA_INPUT),
+       HDA_CODEC_VOLUME("Ext Mic Capture Volume", 0x1a, 0x02, HDA_INPUT),
+       HDA_CODEC_MUTE("Ext Mic Capture Switch", 0x1a, 0x02, HDA_INPUT),
+       HDA_CODEC_VOLUME("PCM Playback Volume", 0x17, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("PCM Playback Switch", 0x17, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x17, 0x1, HDA_INPUT),
+       HDA_CODEC_MUTE("Int Mic Playback Switch", 0x17, 0x1, HDA_INPUT),
+       HDA_CODEC_VOLUME("Ext Mic Playback Volume", 0x17, 0x2, HDA_INPUT),
+       HDA_CODEC_MUTE("Ext Mic Playback Switch", 0x17, 0x2, HDA_INPUT),
+       HDA_BIND_VOL("Master Playback Volume", &cxt5045_hp_bind_master_vol),
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Capture Source",
-               .info = conexant_mux_enum_info,
-               .get = conexant_mux_enum_get,
-               .put = conexant_mux_enum_put
-       },
-       HDA_CODEC_VOLUME("Int Mic Volume", 0x17, 0x01, HDA_INPUT),
-       HDA_CODEC_MUTE("Int Mic Switch", 0x17, 0x01, HDA_INPUT),
-       HDA_CODEC_VOLUME("Ext Mic Volume", 0x17, 0x02, HDA_INPUT),
-       HDA_CODEC_MUTE("Ext Mic Switch", 0x17, 0x02, HDA_INPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x1a, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x1a, 0x0, HDA_INPUT),
-       {
-               .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 = cxt5045_hp_master_vol_put,
-               .private_value = HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT),
+               .name = "Master Playback Switch",
+               .info = cxt_eapd_info,
+               .get = cxt_eapd_get,
+               .put = cxt5045_hp_master_sw_put,
+               .private_value = 0x10,
        },
+
+       {}
+};
+
+static struct snd_kcontrol_new cxt5045_benq_mixers[] = {
+       HDA_CODEC_VOLUME("CD Capture Volume", 0x1a, 0x04, HDA_INPUT),
+       HDA_CODEC_MUTE("CD Capture Switch", 0x1a, 0x04, HDA_INPUT),
+       HDA_CODEC_VOLUME("CD Playback Volume", 0x17, 0x4, HDA_INPUT),
+       HDA_CODEC_MUTE("CD Playback Switch", 0x17, 0x4, HDA_INPUT),
+
+       HDA_CODEC_VOLUME("Line In Capture Volume", 0x1a, 0x03, HDA_INPUT),
+       HDA_CODEC_MUTE("Line In Capture Switch", 0x1a, 0x03, HDA_INPUT),
+       HDA_CODEC_VOLUME("Line In Playback Volume", 0x17, 0x3, HDA_INPUT),
+       HDA_CODEC_MUTE("Line In Playback Switch", 0x17, 0x3, HDA_INPUT),
+
+       HDA_CODEC_VOLUME("Mixer Capture Volume", 0x1a, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("Mixer Capture Switch", 0x1a, 0x0, HDA_INPUT),
+
+       {}
+};
+
+static struct snd_kcontrol_new cxt5045_mixers_hp530[] = {
+       HDA_CODEC_VOLUME("Int Mic Capture Volume", 0x1a, 0x02, HDA_INPUT),
+       HDA_CODEC_MUTE("Int Mic Capture Switch", 0x1a, 0x02, HDA_INPUT),
+       HDA_CODEC_VOLUME("Ext Mic Capture Volume", 0x1a, 0x01, HDA_INPUT),
+       HDA_CODEC_MUTE("Ext Mic Capture Switch", 0x1a, 0x01, HDA_INPUT),
+       HDA_CODEC_VOLUME("PCM Playback Volume", 0x17, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("PCM Playback Switch", 0x17, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x17, 0x2, HDA_INPUT),
+       HDA_CODEC_MUTE("Int Mic Playback Switch", 0x17, 0x2, HDA_INPUT),
+       HDA_CODEC_VOLUME("Ext Mic Playback Volume", 0x17, 0x1, HDA_INPUT),
+       HDA_CODEC_MUTE("Ext Mic Playback Switch", 0x17, 0x1, HDA_INPUT),
+       HDA_BIND_VOL("Master Playback Volume", &cxt5045_hp_bind_master_vol),
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Master Playback Switch",
@@ -687,32 +877,68 @@ static struct snd_kcontrol_new cxt5045_mixers[] = {
 
 static struct hda_verb cxt5045_init_verbs[] = {
        /* Line in, Mic */
-       {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
-       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_50 },
+       {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 },
+       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 },
        /* HP, Amp  */
-       {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
-       {0x17, AC_VERB_SET_CONNECT_SEL,0x01},
-       {0x17, AC_VERB_SET_AMP_GAIN_MUTE,
-        AC_AMP_SET_OUTPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x01},
-       {0x17, AC_VERB_SET_AMP_GAIN_MUTE,
-        AC_AMP_SET_OUTPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x02},
-       {0x17, AC_VERB_SET_AMP_GAIN_MUTE,
-        AC_AMP_SET_OUTPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x03},
-       {0x17, AC_VERB_SET_AMP_GAIN_MUTE,
-        AC_AMP_SET_OUTPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x04},
+       {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x10, AC_VERB_SET_CONNECT_SEL, 0x1},
+       {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x11, AC_VERB_SET_CONNECT_SEL, 0x1},
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
        /* Record selector: Int mic */
-       {0x1a, AC_VERB_SET_CONNECT_SEL,0x0},
+       {0x1a, AC_VERB_SET_CONNECT_SEL,0x1},
        {0x1a, AC_VERB_SET_AMP_GAIN_MUTE,
         AC_AMP_SET_INPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x17},
        /* SPDIF route: PCM */
+       {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
        { 0x13, AC_VERB_SET_CONNECT_SEL, 0x0 },
-       /* pin sensing on HP and Mic jacks */
-       {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
        /* EAPD */
        {0x10, AC_VERB_SET_EAPD_BTLENABLE, 0x2 }, /* default on */ 
        { } /* end */
 };
 
+static struct hda_verb cxt5045_benq_init_verbs[] = {
+       /* Int Mic, Mic */
+       {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 },
+       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 },
+       /* Line In,HP, Amp  */
+       {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x10, AC_VERB_SET_CONNECT_SEL, 0x1},
+       {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+       {0x11, AC_VERB_SET_CONNECT_SEL, 0x1},
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+       /* Record selector: Int mic */
+       {0x1a, AC_VERB_SET_CONNECT_SEL, 0x1},
+       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE,
+        AC_AMP_SET_INPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x17},
+       /* SPDIF route: PCM */
+       {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x13, AC_VERB_SET_CONNECT_SEL, 0x0},
+       /* EAPD */
+       {0x10, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
+       { } /* end */
+};
+
+static struct hda_verb cxt5045_hp_sense_init_verbs[] = {
+       /* pin sensing on HP jack */
+       {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
+       { } /* end */
+};
+
+static struct hda_verb cxt5045_mic_sense_init_verbs[] = {
+       /* pin sensing on HP jack */
+       {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
+       { } /* end */
+};
+
 #ifdef CONFIG_SND_DEBUG
 /* Test configuration for debugging, modelled after the ALC260 test
  * configuration.
@@ -733,6 +959,10 @@ static struct snd_kcontrol_new cxt5045_test_mixer[] = {
        /* Output controls */
        HDA_CODEC_VOLUME("Speaker Playback Volume", 0x10, 0x0, HDA_OUTPUT),
        HDA_CODEC_MUTE("Speaker Playback Switch", 0x10, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Node 11 Playback Volume", 0x11, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Node 11 Playback Switch", 0x11, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Node 12 Playback Volume", 0x12, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Node 12 Playback Switch", 0x12, 0x0, HDA_OUTPUT),
        
        /* Modes for retasking pin widgets */
        CXT_PIN_MODE("HP-OUT pin mode", 0x11, CXT_PIN_DIR_INOUT),
@@ -742,25 +972,17 @@ static struct snd_kcontrol_new cxt5045_test_mixer[] = {
        CXT_EAPD_SWITCH("External Amplifier", 0x10, 0x0),
 
        /* Loopback mixer controls */
-       HDA_CODEC_VOLUME("MIC1 Playback Volume", 0x17, 0x01, HDA_INPUT),
-       HDA_CODEC_MUTE("MIC1 Playback Switch", 0x17, 0x01, HDA_INPUT),
-       HDA_CODEC_VOLUME("LINE loopback Playback Volume", 0x17, 0x02, HDA_INPUT),
-       HDA_CODEC_MUTE("LINE loopback Playback Switch", 0x17, 0x02, HDA_INPUT),
-       HDA_CODEC_VOLUME("HP-OUT loopback Playback Volume", 0x17, 0x03, HDA_INPUT),
-       HDA_CODEC_MUTE("HP-OUT loopback Playback Switch", 0x17, 0x03, HDA_INPUT),
-       HDA_CODEC_VOLUME("CD Playback Volume", 0x17, 0x04, HDA_INPUT),
-       HDA_CODEC_MUTE("CD Playback Switch", 0x17, 0x04, HDA_INPUT),
-
-       HDA_CODEC_VOLUME("Capture-1 Volume", 0x17, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture-1 Switch", 0x17, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Capture-2 Volume", 0x17, 0x1, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture-2 Switch", 0x17, 0x1, HDA_INPUT),
-       HDA_CODEC_VOLUME("Capture-3 Volume", 0x17, 0x2, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture-3 Switch", 0x17, 0x2, HDA_INPUT),
-       HDA_CODEC_VOLUME("Capture-4 Volume", 0x17, 0x3, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture-4 Switch", 0x17, 0x3, HDA_INPUT),
-       HDA_CODEC_VOLUME("Capture-5 Volume", 0x17, 0x4, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture-5 Switch", 0x17, 0x4, HDA_INPUT),
+
+       HDA_CODEC_VOLUME("Mixer-1 Volume", 0x17, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("Mixer-1 Switch", 0x17, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mixer-2 Volume", 0x17, 0x1, HDA_INPUT),
+       HDA_CODEC_MUTE("Mixer-2 Switch", 0x17, 0x1, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mixer-3 Volume", 0x17, 0x2, HDA_INPUT),
+       HDA_CODEC_MUTE("Mixer-3 Switch", 0x17, 0x2, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mixer-4 Volume", 0x17, 0x3, HDA_INPUT),
+       HDA_CODEC_MUTE("Mixer-4 Switch", 0x17, 0x3, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mixer-5 Volume", 0x17, 0x4, HDA_INPUT),
+       HDA_CODEC_MUTE("Mixer-5 Switch", 0x17, 0x4, HDA_INPUT),
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Input Source",
@@ -768,14 +990,28 @@ static struct snd_kcontrol_new cxt5045_test_mixer[] = {
                .get = conexant_mux_enum_get,
                .put = conexant_mux_enum_put,
        },
-
+       /* Audio input controls */
+       HDA_CODEC_VOLUME("Input-1 Volume", 0x1a, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("Input-1 Switch", 0x1a, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Input-2 Volume", 0x1a, 0x1, HDA_INPUT),
+       HDA_CODEC_MUTE("Input-2 Switch", 0x1a, 0x1, HDA_INPUT),
+       HDA_CODEC_VOLUME("Input-3 Volume", 0x1a, 0x2, HDA_INPUT),
+       HDA_CODEC_MUTE("Input-3 Switch", 0x1a, 0x2, HDA_INPUT),
+       HDA_CODEC_VOLUME("Input-4 Volume", 0x1a, 0x3, HDA_INPUT),
+       HDA_CODEC_MUTE("Input-4 Switch", 0x1a, 0x3, HDA_INPUT),
+       HDA_CODEC_VOLUME("Input-5 Volume", 0x1a, 0x4, HDA_INPUT),
+       HDA_CODEC_MUTE("Input-5 Switch", 0x1a, 0x4, HDA_INPUT),
        { } /* end */
 };
 
 static struct hda_verb cxt5045_test_init_verbs[] = {
+       /* Set connections */
+       { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
+       { 0x11, AC_VERB_SET_CONNECT_SEL, 0x0 },
+       { 0x12, AC_VERB_SET_CONNECT_SEL, 0x0 },
        /* Enable retasking pins as output, initially without power amp */
        {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-       {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
 
        /* Disable digital (SPDIF) pins initially, but users can enable
         * them via a mixer switch.  In the case of SPDIF-out, this initverb
@@ -783,7 +1019,8 @@ static struct hda_verb cxt5045_test_init_verbs[] = {
         * PCM format, copyright asserted, no pre-emphasis and no validity
         * control.
         */
-       {0x13, AC_VERB_SET_DIGI_CONVERT_1, 0},
+       {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x18, AC_VERB_SET_DIGI_CONVERT_1, 0},
 
        /* Start with output sum widgets muted and their output gains at min */
        {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
@@ -804,6 +1041,7 @@ static struct hda_verb cxt5045_test_init_verbs[] = {
         * pin)
         */
        {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
+       {0x17, AC_VERB_SET_CONNECT_SEL, 0x00},
 
        /* Mute all inputs to mixer widget (even unconnected ones) */
        {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* Mixer pin */
@@ -827,7 +1065,11 @@ static int cxt5045_init(struct hda_codec *codec)
 
 
 enum {
-       CXT5045_LAPTOP, /* Laptops w/ EAPD support */
+       CXT5045_LAPTOP_HPSENSE,
+       CXT5045_LAPTOP_MICSENSE,
+       CXT5045_LAPTOP_HPMICSENSE,
+       CXT5045_BENQ,
+       CXT5045_LAPTOP_HP530,
 #ifdef CONFIG_SND_DEBUG
        CXT5045_TEST,
 #endif
@@ -835,16 +1077,32 @@ enum {
 };
 
 static const char *cxt5045_models[CXT5045_MODELS] = {
-       [CXT5045_LAPTOP]        = "laptop",
+       [CXT5045_LAPTOP_HPSENSE]        = "laptop-hpsense",
+       [CXT5045_LAPTOP_MICSENSE]       = "laptop-micsense",
+       [CXT5045_LAPTOP_HPMICSENSE]     = "laptop-hpmicsense",
+       [CXT5045_BENQ]                  = "benq",
+       [CXT5045_LAPTOP_HP530]          = "laptop-hp530",
 #ifdef CONFIG_SND_DEBUG
        [CXT5045_TEST]          = "test",
 #endif
 };
 
 static struct snd_pci_quirk cxt5045_cfg_tbl[] = {
-       SND_PCI_QUIRK(0x103c, 0x30b7, "HP DV6000Z", CXT5045_LAPTOP),
-       SND_PCI_QUIRK(0x103c, 0x30bb, "HP DV8000", CXT5045_LAPTOP),
-       SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_LAPTOP),
+       SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT5045_LAPTOP_HP530),
+       SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3000, "HP DV Series",
+                          CXT5045_LAPTOP_HPSENSE),
+       SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P105", CXT5045_LAPTOP_MICSENSE),
+       SND_PCI_QUIRK(0x152d, 0x0753, "Benq R55E", CXT5045_BENQ),
+       SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_LAPTOP_MICSENSE),
+       SND_PCI_QUIRK(0x1734, 0x10cb, "Fujitsu Si3515", CXT5045_LAPTOP_HPMICSENSE),
+       SND_PCI_QUIRK(0x1734, 0x110e, "Fujitsu V5505",
+                     CXT5045_LAPTOP_HPMICSENSE),
+       SND_PCI_QUIRK(0x1509, 0x1e40, "FIC", CXT5045_LAPTOP_HPMICSENSE),
+       SND_PCI_QUIRK(0x1509, 0x2f05, "FIC", CXT5045_LAPTOP_HPMICSENSE),
+       SND_PCI_QUIRK(0x1509, 0x2f06, "FIC", CXT5045_LAPTOP_HPMICSENSE),
+       SND_PCI_QUIRK_MASK(0x1631, 0xff00, 0xc100, "Packard Bell",
+                          CXT5045_LAPTOP_HPMICSENSE),
+       SND_PCI_QUIRK(0x8086, 0x2111, "Conexant Reference board", CXT5045_LAPTOP_HPSENSE),
        {}
 };
 
@@ -856,8 +1114,8 @@ static int patch_cxt5045(struct hda_codec *codec)
        spec = kzalloc(sizeof(*spec), GFP_KERNEL);
        if (!spec)
                return -ENOMEM;
-       mutex_init(&spec->amp_mutex);
        codec->spec = spec;
+       codec->pin_amp_workaround = 1;
 
        spec->multiout.max_channels = 2;
        spec->multiout.num_dacs = ARRAY_SIZE(cxt5045_dac_nids);
@@ -877,26 +1135,81 @@ static int patch_cxt5045(struct hda_codec *codec)
 
 
        codec->patch_ops = conexant_patch_ops;
-       codec->patch_ops.unsol_event = cxt5045_hp_unsol_event;
 
        board_config = snd_hda_check_board_config(codec, CXT5045_MODELS,
                                                  cxt5045_models,
                                                  cxt5045_cfg_tbl);
        switch (board_config) {
-       case CXT5045_LAPTOP:
+       case CXT5045_LAPTOP_HPSENSE:
+               codec->patch_ops.unsol_event = cxt5045_hp_unsol_event;
                spec->input_mux = &cxt5045_capture_source;
                spec->num_init_verbs = 2;
-               spec->init_verbs[1] = cxt5045_init_verbs;
+               spec->init_verbs[1] = cxt5045_hp_sense_init_verbs;
                spec->mixers[0] = cxt5045_mixers;
                codec->patch_ops.init = cxt5045_init;
                break;
-#ifdef CONFIG_SND_DEBUG
-       case CXT5045_TEST:
-               spec->input_mux = &cxt5045_test_capture_source;
-               spec->mixers[0] = cxt5045_test_mixer;
-               spec->init_verbs[0] = cxt5045_test_init_verbs;
+       case CXT5045_LAPTOP_MICSENSE:
+               codec->patch_ops.unsol_event = cxt5045_hp_unsol_event;
+               spec->input_mux = &cxt5045_capture_source;
+               spec->num_init_verbs = 2;
+               spec->init_verbs[1] = cxt5045_mic_sense_init_verbs;
+               spec->mixers[0] = cxt5045_mixers;
+               codec->patch_ops.init = cxt5045_init;
+               break;
+       default:
+       case CXT5045_LAPTOP_HPMICSENSE:
+               codec->patch_ops.unsol_event = cxt5045_hp_unsol_event;
+               spec->input_mux = &cxt5045_capture_source;
+               spec->num_init_verbs = 3;
+               spec->init_verbs[1] = cxt5045_hp_sense_init_verbs;
+               spec->init_verbs[2] = cxt5045_mic_sense_init_verbs;
+               spec->mixers[0] = cxt5045_mixers;
+               codec->patch_ops.init = cxt5045_init;
+               break;
+       case CXT5045_BENQ:
+               codec->patch_ops.unsol_event = cxt5045_hp_unsol_event;
+               spec->input_mux = &cxt5045_capture_source_benq;
+               spec->num_init_verbs = 1;
+               spec->init_verbs[0] = cxt5045_benq_init_verbs;
+               spec->mixers[0] = cxt5045_mixers;
+               spec->mixers[1] = cxt5045_benq_mixers;
+               spec->num_mixers = 2;
+               codec->patch_ops.init = cxt5045_init;
+               break;
+       case CXT5045_LAPTOP_HP530:
+               codec->patch_ops.unsol_event = cxt5045_hp_unsol_event;
+               spec->input_mux = &cxt5045_capture_source_hp530;
+               spec->num_init_verbs = 2;
+               spec->init_verbs[1] = cxt5045_hp_sense_init_verbs;
+               spec->mixers[0] = cxt5045_mixers_hp530;
+               codec->patch_ops.init = cxt5045_init;
+               break;
+#ifdef CONFIG_SND_DEBUG
+       case CXT5045_TEST:
+               spec->input_mux = &cxt5045_test_capture_source;
+               spec->mixers[0] = cxt5045_test_mixer;
+               spec->init_verbs[0] = cxt5045_test_init_verbs;
+               break;
+               
 #endif 
        }
+
+       switch (codec->subsystem_id >> 16) {
+       case 0x103c:
+       case 0x1631:
+       case 0x1734:
+               /* HP, Packard Bell, & Fujitsu-Siemens laptops have really bad
+                * sound over 0dB on NID 0x17. Fix max PCM level to 0 dB
+                * (originally it has 0x2b steps with 0dB offset 0x14)
+                */
+               snd_hda_override_amp_caps(codec, 0x17, HDA_INPUT,
+                                         (0x14 << AC_AMPCAP_OFFSET_SHIFT) |
+                                         (0x14 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+                                         (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+                                         (1 << AC_AMPCAP_MUTE_SHIFT));
+               break;
+       }
+
        return 0;
 }
 
@@ -904,7 +1217,7 @@ static int patch_cxt5045(struct hda_codec *codec)
 /* Conexant 5047 specific */
 #define CXT5047_SPDIF_OUT      0x11
 
-static hda_nid_t cxt5047_dac_nids[2] = { 0x10, 0x1c };
+static hda_nid_t cxt5047_dac_nids[1] = { 0x10 }; /* 0x1c */
 static hda_nid_t cxt5047_adc_nids[1] = { 0x12 };
 static hda_nid_t cxt5047_capsrc_nids[1] = { 0x1a };
 
@@ -912,21 +1225,6 @@ static struct hda_channel_mode cxt5047_modes[1] = {
        { 2, NULL },
 };
 
-static struct hda_input_mux cxt5047_capture_source = {
-       .num_items = 2,
-       .items = {
-               { "ExtMic", 0x0 },
-               { "IntMic", 0x1 },
-       }
-};
-
-static struct hda_input_mux cxt5047_hp_capture_source = {
-       .num_items = 1,
-       .items = {
-               { "ExtMic", 0x2 },
-       }
-};
-
 static struct hda_input_mux cxt5047_toshiba_capture_source = {
        .num_items = 2,
        .items = {
@@ -949,68 +1247,49 @@ static int cxt5047_hp_master_sw_put(struct snd_kcontrol *kcontrol,
        /* toggle internal speakers mute depending of presence of
         * the headphone jack
         */
-       bits = (!spec->hp_present && spec->cur_eapd) ? 0 : 0x80;
-       snd_hda_codec_amp_update(codec, 0x1d, 0, HDA_OUTPUT, 0, 0x80, bits);
-       snd_hda_codec_amp_update(codec, 0x1d, 1, HDA_OUTPUT, 0, 0x80, bits);
-       bits = spec->cur_eapd ? 0 : 0x80;
-       snd_hda_codec_amp_update(codec, 0x13, 0, HDA_OUTPUT, 0, 0x80, bits);
-       snd_hda_codec_amp_update(codec, 0x13, 1, HDA_OUTPUT, 0, 0x80, bits);
+       bits = (!spec->hp_present && spec->cur_eapd) ? 0 : HDA_AMP_MUTE;
+       /* NOTE: Conexat codec needs the index for *OUTPUT* amp of
+        * pin widgets unlike other codecs.  In this case, we need to
+        * set index 0x01 for the volume from the mixer amp 0x19.
+        */
+       snd_hda_codec_amp_stereo(codec, 0x1d, HDA_OUTPUT, 0x01,
+                                HDA_AMP_MUTE, bits);
+       bits = spec->cur_eapd ? 0 : HDA_AMP_MUTE;
+       snd_hda_codec_amp_stereo(codec, 0x13, HDA_OUTPUT, 0,
+                                HDA_AMP_MUTE, bits);
        return 1;
 }
 
-/* bind volumes of both NID 0x13 (Headphones) and 0x1d (Speakers) */
-static int cxt5047_hp_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, 0x1d, 0, HDA_OUTPUT, 0,
-                                         0x7f, valp[0] & 0x7f);
-       change |= snd_hda_codec_amp_update(codec, 0x1d, 1, HDA_OUTPUT, 0,
-                                          0x7f, valp[1] & 0x7f);
-       snd_hda_codec_amp_update(codec, 0x13, 0, HDA_OUTPUT, 0,
-                                0x7f, valp[0] & 0x7f);
-       snd_hda_codec_amp_update(codec, 0x13, 1, HDA_OUTPUT, 0,
-                                0x7f, valp[1] & 0x7f);
-       return change;
-}
-
 /* mute internal speaker if HP is plugged */
 static void cxt5047_hp_automute(struct hda_codec *codec)
 {
        struct conexant_spec *spec = codec->spec;
        unsigned int bits;
 
-       spec->hp_present = snd_hda_codec_read(codec, 0x13, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       spec->hp_present = snd_hda_jack_detect(codec, 0x13);
 
-       bits = (spec->hp_present || !spec->cur_eapd) ? 0x80 : 0;
-       snd_hda_codec_amp_update(codec, 0x1d, 0, HDA_OUTPUT, 0, 0x80, bits);
-       snd_hda_codec_amp_update(codec, 0x1d, 1, HDA_OUTPUT, 0, 0x80, bits);
-       /* Mute/Unmute PCM 2 for good measure - some systems need this */
-       snd_hda_codec_amp_update(codec, 0x1c, 0, HDA_OUTPUT, 0, 0x80, bits);
-       snd_hda_codec_amp_update(codec, 0x1c, 1, HDA_OUTPUT, 0, 0x80, bits);
+       bits = (spec->hp_present || !spec->cur_eapd) ? HDA_AMP_MUTE : 0;
+       /* See the note in cxt5047_hp_master_sw_put */
+       snd_hda_codec_amp_stereo(codec, 0x1d, HDA_OUTPUT, 0x01,
+                                HDA_AMP_MUTE, bits);
 }
 
 /* toggle input of built-in and mic jack appropriately */
 static void cxt5047_hp_automic(struct hda_codec *codec)
 {
        static struct hda_verb mic_jack_on[] = {
-               {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-               {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+               {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+               {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
                {}
        };
        static struct hda_verb mic_jack_off[] = {
-               {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-               {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+               {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+               {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
                {}
        };
        unsigned int present;
 
-       present = snd_hda_codec_read(codec, 0x08, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       present = snd_hda_jack_detect(codec, 0x15);
        if (present)
                snd_hda_sequence_write(codec, mic_jack_on);
        else
@@ -1021,8 +1300,7 @@ static void cxt5047_hp_automic(struct hda_codec *codec)
 static void cxt5047_hp_unsol_event(struct hda_codec *codec,
                                  unsigned int res)
 {
-       res >>= 26;
-       switch (res) {
+       switch (res >> 26) {
        case CONEXANT_HP_EVENT:
                cxt5047_hp_automute(codec);
                break;
@@ -1032,30 +1310,14 @@ static void cxt5047_hp_unsol_event(struct hda_codec *codec,
        }
 }
 
-static struct snd_kcontrol_new cxt5047_mixers[] = {
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Capture Source",
-               .info = conexant_mux_enum_info,
-               .get = conexant_mux_enum_get,
-               .put = conexant_mux_enum_put
-       },
-       HDA_CODEC_VOLUME("Mic Bypass Capture Volume", 0x19, 0x02, HDA_INPUT),
-       HDA_CODEC_MUTE("Mic Bypass Capture Switch", 0x19, 0x02, HDA_INPUT),
+static struct snd_kcontrol_new cxt5047_base_mixers[] = {
+       HDA_CODEC_VOLUME("Mic Playback Volume", 0x19, 0x02, HDA_INPUT),
+       HDA_CODEC_MUTE("Mic Playback Switch", 0x19, 0x02, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Boost", 0x1a, 0x0, HDA_OUTPUT),
        HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x03, HDA_INPUT),
        HDA_CODEC_MUTE("Capture Switch", 0x12, 0x03, HDA_INPUT),
        HDA_CODEC_VOLUME("PCM Volume", 0x10, 0x00, HDA_OUTPUT),
        HDA_CODEC_MUTE("PCM Switch", 0x10, 0x00, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("PCM-2 Volume", 0x1c, 0x00, HDA_OUTPUT),
-       HDA_CODEC_MUTE("PCM-2 Switch", 0x1c, 0x00, HDA_OUTPUT),
-       {
-               .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 = cxt5047_hp_master_vol_put,
-               .private_value = HDA_COMPOSE_AMP_VAL(0x13, 3, 0, HDA_OUTPUT),
-       },
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Master Playback Switch",
@@ -1068,63 +1330,15 @@ static struct snd_kcontrol_new cxt5047_mixers[] = {
        {}
 };
 
-static struct snd_kcontrol_new cxt5047_toshiba_mixers[] = {
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Capture Source",
-               .info = conexant_mux_enum_info,
-               .get = conexant_mux_enum_get,
-               .put = conexant_mux_enum_put
-       },
-       HDA_CODEC_VOLUME("Mic Bypass Capture Volume", 0x19, 0x02, HDA_INPUT),
-       HDA_CODEC_MUTE("Mic Bypass Capture Switch", 0x19, 0x02, HDA_INPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x03, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x12, 0x03, HDA_INPUT),
-       HDA_CODEC_VOLUME("PCM Volume", 0x10, 0x00, HDA_OUTPUT),
-       HDA_CODEC_MUTE("PCM Switch", 0x10, 0x00, HDA_OUTPUT),
-       {
-               .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 = cxt5047_hp_master_vol_put,
-               .private_value = HDA_COMPOSE_AMP_VAL(0x13, 3, 0, HDA_OUTPUT),
-       },
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Master Playback Switch",
-               .info = cxt_eapd_info,
-               .get = cxt_eapd_get,
-               .put = cxt5047_hp_master_sw_put,
-               .private_value = 0x13,
-       },
-
+static struct snd_kcontrol_new cxt5047_hp_spk_mixers[] = {
+       /* See the note in cxt5047_hp_master_sw_put */
+       HDA_CODEC_VOLUME("Speaker Playback Volume", 0x1d, 0x01, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Headphone Playback Volume", 0x13, 0x00, HDA_OUTPUT),
        {}
 };
 
-static struct snd_kcontrol_new cxt5047_hp_mixers[] = {
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Capture Source",
-               .info = conexant_mux_enum_info,
-               .get = conexant_mux_enum_get,
-               .put = conexant_mux_enum_put
-       },
-       HDA_CODEC_VOLUME("Mic Bypass Capture Volume", 0x19, 0x02, HDA_INPUT),
-       HDA_CODEC_MUTE("Mic Bypass Capture Switch", 0x19,0x02,HDA_INPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x03, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x12, 0x03, HDA_INPUT),
-       HDA_CODEC_VOLUME("PCM Volume", 0x10, 0x00, HDA_OUTPUT),
-       HDA_CODEC_MUTE("PCM Switch", 0x10, 0x00, HDA_OUTPUT),
+static struct snd_kcontrol_new cxt5047_hp_only_mixers[] = {
        HDA_CODEC_VOLUME("Master Playback Volume", 0x13, 0x00, HDA_OUTPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Master Playback Switch",
-               .info = cxt_eapd_info,
-               .get = cxt_eapd_get,
-               .put = cxt5047_hp_master_sw_put,
-               .private_value = 0x13,
-       },
        { } /* end */
 };
 
@@ -1133,18 +1347,19 @@ static struct hda_verb cxt5047_init_verbs[] = {
        {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
        {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_50 },
        {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_50 },
-       /* HP, Amp, Speaker  */
-       {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
-       {0x1A, AC_VERB_SET_CONNECT_SEL,0x00},
+       /* HP, Speaker  */
+       {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
+       {0x13, AC_VERB_SET_CONNECT_SEL, 0x0}, /* mixer(0x19) */
+       {0x1d, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mixer(0x19) */
+       /* Record selector: Mic */
+       {0x12, AC_VERB_SET_CONNECT_SEL,0x03},
+       {0x19, AC_VERB_SET_AMP_GAIN_MUTE,
+        AC_AMP_SET_INPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x17},
+       {0x1A, AC_VERB_SET_CONNECT_SEL,0x02},
        {0x1A, AC_VERB_SET_AMP_GAIN_MUTE,
         AC_AMP_SET_OUTPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x00},
        {0x1A, AC_VERB_SET_AMP_GAIN_MUTE,
         AC_AMP_SET_OUTPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x03},
-       {0x1d, AC_VERB_SET_CONNECT_SEL,0x0},
-       /* Record selector: Front mic */
-       {0x12, AC_VERB_SET_CONNECT_SEL,0x03},
-       {0x19, AC_VERB_SET_AMP_GAIN_MUTE,
-        AC_AMP_SET_INPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x17},
        /* SPDIF route: PCM */
        { 0x18, AC_VERB_SET_CONNECT_SEL, 0x0 },
        /* Enable unsolicited events */
@@ -1155,28 +1370,7 @@ static struct hda_verb cxt5047_init_verbs[] = {
 
 /* configuration for Toshiba Laptops */
 static struct hda_verb cxt5047_toshiba_init_verbs[] = {
-       {0x13, AC_VERB_SET_EAPD_BTLENABLE, 0x0 }, /* default on */
-       /* pin sensing on HP and Mic jacks */
-       {0x13, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
-       {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
-       /* Speaker routing */
-       {0x1d, AC_VERB_SET_CONNECT_SEL,0x1},
-       /* Change default to ExtMic for recording */
-       {0x1a, AC_VERB_SET_CONNECT_SEL,0x2},
-       {}
-};
-
-/* configuration for HP Laptops */
-static struct hda_verb cxt5047_hp_init_verbs[] = {
-       /* pin sensing on HP jack */
-       {0x13, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
-       /* Record selector: Ext Mic */
-       {0x12, AC_VERB_SET_CONNECT_SEL,0x03},
-       {0x1a, AC_VERB_SET_CONNECT_SEL,0x02},
-       {0x19, AC_VERB_SET_AMP_GAIN_MUTE,
-        AC_AMP_SET_INPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x17},
-       /* Speaker routing */
-       {0x1d, AC_VERB_SET_CONNECT_SEL,0x1},
+       {0x13, AC_VERB_SET_EAPD_BTLENABLE, 0x0}, /* default off */
        {}
 };
 
@@ -1242,13 +1436,7 @@ static struct snd_kcontrol_new cxt5047_test_mixer[] = {
                .get = conexant_mux_enum_get,
                .put = conexant_mux_enum_put,
        },
-       /* Controls for GPIO pins, assuming they exist and are configured
-       * as outputs
-       */
-       CXT_GPIO_DATA_SWITCH("GPIO pin 0", 0x01, 0x01),
-       CXT_GPIO_DATA_SWITCH("GPIO pin 1", 0x01, 0x02),
-       CXT_GPIO_DATA_SWITCH("GPIO pin 2", 0x01, 0x04),
-       CXT_GPIO_DATA_SWITCH("GPIO pin 3", 0x01, 0x08),
+       HDA_CODEC_VOLUME("Mic Boost Volume", 0x1a, 0x0, HDA_OUTPUT),
 
        { } /* end */
 };
@@ -1338,10 +1526,9 @@ static const char *cxt5047_models[CXT5047_MODELS] = {
 };
 
 static struct snd_pci_quirk cxt5047_cfg_tbl[] = {
-       SND_PCI_QUIRK(0x103c, 0x30a0, "HP DV1000", CXT5047_LAPTOP),
-       SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV2000T/DV3000T", CXT5047_LAPTOP),
-       SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2000Z", CXT5047_LAPTOP),
        SND_PCI_QUIRK(0x103c, 0x30a5, "HP DV5200T/DV8000T", CXT5047_LAPTOP_HP),
+       SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3000, "HP DV Series",
+                          CXT5047_LAPTOP),
        SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P100", CXT5047_LAPTOP_EAPD),
        {}
 };
@@ -1354,8 +1541,8 @@ static int patch_cxt5047(struct hda_codec *codec)
        spec = kzalloc(sizeof(*spec), GFP_KERNEL);
        if (!spec)
                return -ENOMEM;
-       mutex_init(&spec->amp_mutex);
        codec->spec = spec;
+       codec->pin_amp_workaround = 1;
 
        spec->multiout.max_channels = 2;
        spec->multiout.num_dacs = ARRAY_SIZE(cxt5047_dac_nids);
@@ -1364,9 +1551,8 @@ static int patch_cxt5047(struct hda_codec *codec)
        spec->num_adc_nids = 1;
        spec->adc_nids = cxt5047_adc_nids;
        spec->capsrc_nids = cxt5047_capsrc_nids;
-       spec->input_mux = &cxt5047_capture_source;
        spec->num_mixers = 1;
-       spec->mixers[0] = cxt5047_mixers;
+       spec->mixers[0] = cxt5047_base_mixers;
        spec->num_init_verbs = 1;
        spec->init_verbs[0] = cxt5047_init_verbs;
        spec->spdif_route = 0;
@@ -1374,41 +1560,1592 @@ static int patch_cxt5047(struct hda_codec *codec)
        spec->channel_mode = cxt5047_modes,
 
        codec->patch_ops = conexant_patch_ops;
-       codec->patch_ops.unsol_event = cxt5047_hp_unsol_event;
 
        board_config = snd_hda_check_board_config(codec, CXT5047_MODELS,
                                                  cxt5047_models,
                                                  cxt5047_cfg_tbl);
        switch (board_config) {
        case CXT5047_LAPTOP:
+               spec->num_mixers = 2;
+               spec->mixers[1] = cxt5047_hp_spk_mixers;
+               codec->patch_ops.unsol_event = cxt5047_hp_unsol_event;
                break;
        case CXT5047_LAPTOP_HP:
-               spec->input_mux = &cxt5047_hp_capture_source;
-               spec->num_init_verbs = 2;
-               spec->init_verbs[1] = cxt5047_hp_init_verbs;
-               spec->mixers[0] = cxt5047_hp_mixers;
+               spec->num_mixers = 2;
+               spec->mixers[1] = cxt5047_hp_only_mixers;
+               codec->patch_ops.unsol_event = cxt5047_hp_unsol_event;
                codec->patch_ops.init = cxt5047_hp_init;
                break;
        case CXT5047_LAPTOP_EAPD:
                spec->input_mux = &cxt5047_toshiba_capture_source;
+               spec->num_mixers = 2;
+               spec->mixers[1] = cxt5047_hp_spk_mixers;
                spec->num_init_verbs = 2;
                spec->init_verbs[1] = cxt5047_toshiba_init_verbs;
-               spec->mixers[0] = cxt5047_toshiba_mixers;
+               codec->patch_ops.unsol_event = cxt5047_hp_unsol_event;
                break;
 #ifdef CONFIG_SND_DEBUG
        case CXT5047_TEST:
                spec->input_mux = &cxt5047_test_capture_source;
                spec->mixers[0] = cxt5047_test_mixer;
                spec->init_verbs[0] = cxt5047_test_init_verbs;
+               codec->patch_ops.unsol_event = cxt5047_hp_unsol_event;
 #endif 
        }
+       spec->vmaster_nid = 0x13;
+
+       switch (codec->subsystem_id >> 16) {
+       case 0x103c:
+               /* HP laptops have really bad sound over 0 dB on NID 0x10.
+                * Fix max PCM level to 0 dB (originally it has 0x1e steps
+                * with 0 dB offset 0x17)
+                */
+               snd_hda_override_amp_caps(codec, 0x10, HDA_INPUT,
+                                         (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
+                                         (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+                                         (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+                                         (1 << AC_AMPCAP_MUTE_SHIFT));
+               break;
+       }
+
        return 0;
 }
 
-struct hda_codec_preset snd_hda_preset_conexant[] = {
-       { .id = 0x14f15045, .name = "CX20549 (Venice)",
-         .patch = patch_cxt5045 },
-       { .id = 0x14f15047, .name = "CX20551 (Waikiki)",
-         .patch = patch_cxt5047 },
-       {} /* terminator */
+/* Conexant 5051 specific */
+static hda_nid_t cxt5051_dac_nids[1] = { 0x10 };
+static hda_nid_t cxt5051_adc_nids[2] = { 0x14, 0x15 };
+
+static struct hda_channel_mode cxt5051_modes[1] = {
+       { 2, NULL },
+};
+
+static void cxt5051_update_speaker(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+       unsigned int pinctl;
+       /* headphone pin */
+       pinctl = (spec->hp_present && spec->cur_eapd) ? PIN_HP : 0;
+       snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+                           pinctl);
+       /* speaker pin */
+       pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0;
+       snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+                           pinctl);
+}
+
+/* turn on/off EAPD (+ mute HP) as a master switch */
+static int cxt5051_hp_master_sw_put(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+
+       if (!cxt_eapd_put(kcontrol, ucontrol))
+               return 0;
+       cxt5051_update_speaker(codec);
+       return 1;
+}
+
+/* toggle input of built-in and mic jack appropriately */
+static void cxt5051_portb_automic(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+       unsigned int present;
+
+       if (!(spec->auto_mic & AUTO_MIC_PORTB))
+               return;
+       present = snd_hda_jack_detect(codec, 0x17);
+       snd_hda_codec_write(codec, 0x14, 0,
+                           AC_VERB_SET_CONNECT_SEL,
+                           present ? 0x01 : 0x00);
+}
+
+/* switch the current ADC according to the jack state */
+static void cxt5051_portc_automic(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+       unsigned int present;
+       hda_nid_t new_adc;
+
+       if (!(spec->auto_mic & AUTO_MIC_PORTC))
+               return;
+       present = snd_hda_jack_detect(codec, 0x18);
+       if (present)
+               spec->cur_adc_idx = 1;
+       else
+               spec->cur_adc_idx = 0;
+       new_adc = spec->adc_nids[spec->cur_adc_idx];
+       if (spec->cur_adc && spec->cur_adc != new_adc) {
+               /* stream is running, let's swap the current ADC */
+               snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
+               spec->cur_adc = new_adc;
+               snd_hda_codec_setup_stream(codec, new_adc,
+                                          spec->cur_adc_stream_tag, 0,
+                                          spec->cur_adc_format);
+       }
+}
+
+/* mute internal speaker if HP is plugged */
+static void cxt5051_hp_automute(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+
+       spec->hp_present = snd_hda_jack_detect(codec, 0x16);
+       cxt5051_update_speaker(codec);
+}
+
+/* unsolicited event for HP jack sensing */
+static void cxt5051_hp_unsol_event(struct hda_codec *codec,
+                                  unsigned int res)
+{
+       int nid = (res & AC_UNSOL_RES_SUBTAG) >> 20;
+       switch (res >> 26) {
+       case CONEXANT_HP_EVENT:
+               cxt5051_hp_automute(codec);
+               break;
+       case CXT5051_PORTB_EVENT:
+               cxt5051_portb_automic(codec);
+               break;
+       case CXT5051_PORTC_EVENT:
+               cxt5051_portc_automic(codec);
+               break;
+       }
+       conexant_report_jack(codec, nid);
+}
+
+static struct snd_kcontrol_new cxt5051_playback_mixers[] = {
+       HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Master Playback Switch",
+               .info = cxt_eapd_info,
+               .get = cxt_eapd_get,
+               .put = cxt5051_hp_master_sw_put,
+               .private_value = 0x1a,
+       },
+       {}
+};
+
+static struct snd_kcontrol_new cxt5051_capture_mixers[] = {
+       HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT),
+       HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT),
+       HDA_CODEC_VOLUME("External Mic Volume", 0x14, 0x01, HDA_INPUT),
+       HDA_CODEC_MUTE("External Mic Switch", 0x14, 0x01, HDA_INPUT),
+       HDA_CODEC_VOLUME("Docking Mic Volume", 0x15, 0x00, HDA_INPUT),
+       HDA_CODEC_MUTE("Docking Mic Switch", 0x15, 0x00, HDA_INPUT),
+       {}
+};
+
+static struct snd_kcontrol_new cxt5051_hp_mixers[] = {
+       HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT),
+       HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT),
+       HDA_CODEC_VOLUME("External Mic Volume", 0x15, 0x00, HDA_INPUT),
+       HDA_CODEC_MUTE("External Mic Switch", 0x15, 0x00, HDA_INPUT),
+       {}
+};
+
+static struct snd_kcontrol_new cxt5051_hp_dv6736_mixers[] = {
+       HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x00, HDA_INPUT),
+       HDA_CODEC_MUTE("Capture Switch", 0x14, 0x00, HDA_INPUT),
+       {}
+};
+
+static struct snd_kcontrol_new cxt5051_f700_mixers[] = {
+       HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x01, HDA_INPUT),
+       HDA_CODEC_MUTE("Capture Switch", 0x14, 0x01, HDA_INPUT),
+       {}
+};
+
+static struct snd_kcontrol_new cxt5051_toshiba_mixers[] = {
+       HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT),
+       HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT),
+       HDA_CODEC_VOLUME("External Mic Volume", 0x14, 0x01, HDA_INPUT),
+       HDA_CODEC_MUTE("External Mic Switch", 0x14, 0x01, HDA_INPUT),
+       {}
+};
+
+static struct hda_verb cxt5051_init_verbs[] = {
+       /* Line in, Mic */
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
+       {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
+       {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+       {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
+       /* SPK  */
+       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
+       /* HP, Amp  */
+       {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x16, AC_VERB_SET_CONNECT_SEL, 0x00},
+       /* DAC1 */      
+       {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       /* Record selector: Int mic */
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
+       /* SPDIF route: PCM */
+       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0},
+       /* EAPD */
+       {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ 
+       {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
+       { } /* end */
+};
+
+static struct hda_verb cxt5051_hp_dv6736_init_verbs[] = {
+       /* Line in, Mic */
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
+       {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0},
+       {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0},
+       /* SPK  */
+       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
+       /* HP, Amp  */
+       {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x16, AC_VERB_SET_CONNECT_SEL, 0x00},
+       /* DAC1 */
+       {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       /* Record selector: Int mic */
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
+       {0x14, AC_VERB_SET_CONNECT_SEL, 0x1},
+       /* SPDIF route: PCM */
+       {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0},
+       /* EAPD */
+       {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
+       {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
+       { } /* end */
+};
+
+static struct hda_verb cxt5051_lenovo_x200_init_verbs[] = {
+       /* Line in, Mic */
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
+       {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
+       {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+       {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
+       /* SPK  */
+       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
+       /* HP, Amp  */
+       {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x16, AC_VERB_SET_CONNECT_SEL, 0x00},
+       /* Docking HP */
+       {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x19, AC_VERB_SET_CONNECT_SEL, 0x00},
+       /* DAC1 */
+       {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       /* Record selector: Int mic */
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
+       /* SPDIF route: PCM */
+       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* needed for W500 Advanced Mini Dock 250410 */
+       {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0},
+       /* EAPD */
+       {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
+       {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
+       {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
+       { } /* end */
+};
+
+static struct hda_verb cxt5051_f700_init_verbs[] = {
+       /* Line in, Mic */
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
+       {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0},
+       {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0},
+       /* SPK  */
+       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
+       /* HP, Amp  */
+       {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x16, AC_VERB_SET_CONNECT_SEL, 0x00},
+       /* DAC1 */
+       {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       /* Record selector: Int mic */
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
+       {0x14, AC_VERB_SET_CONNECT_SEL, 0x1},
+       /* SPDIF route: PCM */
+       {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0},
+       /* EAPD */
+       {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
+       {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
+       { } /* end */
+};
+
+static void cxt5051_init_mic_port(struct hda_codec *codec, hda_nid_t nid,
+                                unsigned int event)
+{
+       snd_hda_codec_write(codec, nid, 0,
+                           AC_VERB_SET_UNSOLICITED_ENABLE,
+                           AC_USRSP_EN | event);
+#ifdef CONFIG_SND_HDA_INPUT_JACK
+       conexant_add_jack(codec, nid, SND_JACK_MICROPHONE);
+       conexant_report_jack(codec, nid);
+#endif
+}
+
+/* initialize jack-sensing, too */
+static int cxt5051_init(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+
+       conexant_init(codec);
+       conexant_init_jacks(codec);
+
+       if (spec->auto_mic & AUTO_MIC_PORTB)
+               cxt5051_init_mic_port(codec, 0x17, CXT5051_PORTB_EVENT);
+       if (spec->auto_mic & AUTO_MIC_PORTC)
+               cxt5051_init_mic_port(codec, 0x18, CXT5051_PORTC_EVENT);
+
+       if (codec->patch_ops.unsol_event) {
+               cxt5051_hp_automute(codec);
+               cxt5051_portb_automic(codec);
+               cxt5051_portc_automic(codec);
+       }
+       return 0;
+}
+
+
+enum {
+       CXT5051_LAPTOP,  /* Laptops w/ EAPD support */
+       CXT5051_HP,     /* no docking */
+       CXT5051_HP_DV6736,      /* HP without mic switch */
+       CXT5051_LENOVO_X200,    /* Lenovo X200 laptop, also used for Advanced Mini Dock 250410 */
+       CXT5051_F700,       /* HP Compaq Presario F700 */
+       CXT5051_TOSHIBA,        /* Toshiba M300 & co */
+       CXT5051_MODELS
 };
+
+static const char *cxt5051_models[CXT5051_MODELS] = {
+       [CXT5051_LAPTOP]        = "laptop",
+       [CXT5051_HP]            = "hp",
+       [CXT5051_HP_DV6736]     = "hp-dv6736",
+       [CXT5051_LENOVO_X200]   = "lenovo-x200",
+       [CXT5051_F700]          = "hp-700",
+       [CXT5051_TOSHIBA]       = "toshiba",
+};
+
+static struct snd_pci_quirk cxt5051_cfg_tbl[] = {
+       SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV6736", CXT5051_HP_DV6736),
+       SND_PCI_QUIRK(0x103c, 0x360b, "Compaq Presario CQ60", CXT5051_HP),
+       SND_PCI_QUIRK(0x103c, 0x30ea, "Compaq Presario F700", CXT5051_F700),
+       SND_PCI_QUIRK(0x1179, 0xff50, "Toshiba M30x", CXT5051_TOSHIBA),
+       SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board",
+                     CXT5051_LAPTOP),
+       SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP),
+       SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT5051_LENOVO_X200),
+       {}
+};
+
+static int patch_cxt5051(struct hda_codec *codec)
+{
+       struct conexant_spec *spec;
+       int board_config;
+
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (!spec)
+               return -ENOMEM;
+       codec->spec = spec;
+       codec->pin_amp_workaround = 1;
+
+       codec->patch_ops = conexant_patch_ops;
+       codec->patch_ops.init = cxt5051_init;
+
+       spec->multiout.max_channels = 2;
+       spec->multiout.num_dacs = ARRAY_SIZE(cxt5051_dac_nids);
+       spec->multiout.dac_nids = cxt5051_dac_nids;
+       spec->multiout.dig_out_nid = CXT5051_SPDIF_OUT;
+       spec->num_adc_nids = 1; /* not 2; via auto-mic switch */
+       spec->adc_nids = cxt5051_adc_nids;
+       spec->num_mixers = 2;
+       spec->mixers[0] = cxt5051_capture_mixers;
+       spec->mixers[1] = cxt5051_playback_mixers;
+       spec->num_init_verbs = 1;
+       spec->init_verbs[0] = cxt5051_init_verbs;
+       spec->spdif_route = 0;
+       spec->num_channel_mode = ARRAY_SIZE(cxt5051_modes);
+       spec->channel_mode = cxt5051_modes;
+       spec->cur_adc = 0;
+       spec->cur_adc_idx = 0;
+
+       codec->patch_ops.unsol_event = cxt5051_hp_unsol_event;
+
+       board_config = snd_hda_check_board_config(codec, CXT5051_MODELS,
+                                                 cxt5051_models,
+                                                 cxt5051_cfg_tbl);
+       spec->auto_mic = AUTO_MIC_PORTB | AUTO_MIC_PORTC;
+       switch (board_config) {
+       case CXT5051_HP:
+               spec->mixers[0] = cxt5051_hp_mixers;
+               break;
+       case CXT5051_HP_DV6736:
+               spec->init_verbs[0] = cxt5051_hp_dv6736_init_verbs;
+               spec->mixers[0] = cxt5051_hp_dv6736_mixers;
+               spec->auto_mic = 0;
+               break;
+       case CXT5051_LENOVO_X200:
+               spec->init_verbs[0] = cxt5051_lenovo_x200_init_verbs;
+               break;
+       case CXT5051_F700:
+               spec->init_verbs[0] = cxt5051_f700_init_verbs;
+               spec->mixers[0] = cxt5051_f700_mixers;
+               spec->auto_mic = 0;
+               break;
+       case CXT5051_TOSHIBA:
+               spec->mixers[0] = cxt5051_toshiba_mixers;
+               spec->auto_mic = AUTO_MIC_PORTB;
+               break;
+       }
+
+       return 0;
+}
+
+/* Conexant 5066 specific */
+
+static hda_nid_t cxt5066_dac_nids[1] = { 0x10 };
+static hda_nid_t cxt5066_adc_nids[3] = { 0x14, 0x15, 0x16 };
+static hda_nid_t cxt5066_capsrc_nids[1] = { 0x17 };
+#define CXT5066_SPDIF_OUT      0x21
+
+/* OLPC's microphone port is DC coupled for use with external sensors,
+ * therefore we use a 50% mic bias in order to center the input signal with
+ * the DC input range of the codec. */
+#define CXT5066_OLPC_EXT_MIC_BIAS PIN_VREF50
+
+static struct hda_channel_mode cxt5066_modes[1] = {
+       { 2, NULL },
+};
+
+static void cxt5066_update_speaker(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+       unsigned int pinctl;
+
+       snd_printdd("CXT5066: update speaker, hp_present=%d\n",
+               spec->hp_present);
+
+       /* Port A (HP) */
+       pinctl = ((spec->hp_present & 1) && spec->cur_eapd) ? PIN_HP : 0;
+       snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+                       pinctl);
+
+       /* Port D (HP/LO) */
+       pinctl = ((spec->hp_present & 2) && spec->cur_eapd)
+               ? spec->port_d_mode : 0;
+       /* Mute if Port A is connected on Thinkpad */
+       if (spec->thinkpad && (spec->hp_present & 1))
+               pinctl = 0;
+       snd_hda_codec_write(codec, 0x1c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+                       pinctl);
+
+       /* CLASS_D AMP */
+       pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0;
+       snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+                       pinctl);
+
+       if (spec->dell_automute) {
+               /* DELL AIO Port Rule: PortA > PortD > IntSpk */
+               pinctl = (!(spec->hp_present & 1) && spec->cur_eapd)
+                       ? PIN_OUT : 0;
+               snd_hda_codec_write(codec, 0x1c, 0,
+                       AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl);
+       }
+}
+
+/* turn on/off EAPD (+ mute HP) as a master switch */
+static int cxt5066_hp_master_sw_put(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+
+       if (!cxt_eapd_put(kcontrol, ucontrol))
+               return 0;
+
+       cxt5066_update_speaker(codec);
+       return 1;
+}
+
+static const struct hda_input_mux cxt5066_olpc_dc_bias = {
+       .num_items = 3,
+       .items = {
+               { "Off", PIN_IN },
+               { "50%", PIN_VREF50 },
+               { "80%", PIN_VREF80 },
+       },
+};
+
+static int cxt5066_set_olpc_dc_bias(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+       /* Even though port F is the DC input, the bias is controlled on port B.
+        * we also leave that port as an active input (but unselected) in DC mode
+        * just in case that is necessary to make the bias setting take effect. */
+       return snd_hda_codec_write_cache(codec, 0x1a, 0,
+               AC_VERB_SET_PIN_WIDGET_CONTROL,
+               cxt5066_olpc_dc_bias.items[spec->dc_input_bias].index);
+}
+
+/* OLPC defers mic widget control until when capture is started because the
+ * microphone LED comes on as soon as these settings are put in place. if we
+ * did this before recording, it would give the false indication that recording
+ * is happening when it is not. */
+static void cxt5066_olpc_select_mic(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+       if (!spec->recording)
+               return;
+
+       if (spec->dc_enable) {
+               /* in DC mode we ignore presence detection and just use the jack
+                * through our special DC port */
+               const struct hda_verb enable_dc_mode[] = {
+                       /* disble internal mic, port C */
+                       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+                       /* enable DC capture, port F */
+                       {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+                       {},
+               };
+
+               snd_hda_sequence_write(codec, enable_dc_mode);
+               /* port B input disabled (and bias set) through the following call */
+               cxt5066_set_olpc_dc_bias(codec);
+               return;
+       }
+
+       /* disable DC (port F) */
+       snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
+
+       /* external mic, port B */
+       snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+               spec->ext_mic_present ? CXT5066_OLPC_EXT_MIC_BIAS : 0);
+
+       /* internal mic, port C */
+       snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+               spec->ext_mic_present ? 0 : PIN_VREF80);
+}
+
+/* toggle input of built-in and mic jack appropriately */
+static void cxt5066_olpc_automic(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+       unsigned int present;
+
+       if (spec->dc_enable) /* don't do presence detection in DC mode */
+               return;
+
+       present = snd_hda_codec_read(codec, 0x1a, 0,
+                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       if (present)
+               snd_printdd("CXT5066: external microphone detected\n");
+       else
+               snd_printdd("CXT5066: external microphone absent\n");
+
+       snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_CONNECT_SEL,
+               present ? 0 : 1);
+       spec->ext_mic_present = !!present;
+
+       cxt5066_olpc_select_mic(codec);
+}
+
+/* toggle input of built-in digital mic and mic jack appropriately */
+static void cxt5066_vostro_automic(struct hda_codec *codec)
+{
+       unsigned int present;
+
+       struct hda_verb ext_mic_present[] = {
+               /* enable external mic, port B */
+               {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+
+               /* switch to external mic input */
+               {0x17, AC_VERB_SET_CONNECT_SEL, 0},
+               {0x14, AC_VERB_SET_CONNECT_SEL, 0},
+
+               /* disable internal digital mic */
+               {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+               {}
+       };
+       static struct hda_verb ext_mic_absent[] = {
+               /* enable internal mic, port C */
+               {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+
+               /* switch to internal mic input */
+               {0x14, AC_VERB_SET_CONNECT_SEL, 2},
+
+               /* disable external mic, port B */
+               {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+               {}
+       };
+
+       present = snd_hda_jack_detect(codec, 0x1a);
+       if (present) {
+               snd_printdd("CXT5066: external microphone detected\n");
+               snd_hda_sequence_write(codec, ext_mic_present);
+       } else {
+               snd_printdd("CXT5066: external microphone absent\n");
+               snd_hda_sequence_write(codec, ext_mic_absent);
+       }
+}
+
+/* toggle input of built-in digital mic and mic jack appropriately */
+static void cxt5066_ideapad_automic(struct hda_codec *codec)
+{
+       unsigned int present;
+
+       struct hda_verb ext_mic_present[] = {
+               {0x14, AC_VERB_SET_CONNECT_SEL, 0},
+               {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+               {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+               {}
+       };
+       static struct hda_verb ext_mic_absent[] = {
+               {0x14, AC_VERB_SET_CONNECT_SEL, 2},
+               {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+               {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+               {}
+       };
+
+       present = snd_hda_jack_detect(codec, 0x1b);
+       if (present) {
+               snd_printdd("CXT5066: external microphone detected\n");
+               snd_hda_sequence_write(codec, ext_mic_present);
+       } else {
+               snd_printdd("CXT5066: external microphone absent\n");
+               snd_hda_sequence_write(codec, ext_mic_absent);
+       }
+}
+
+/* toggle input of built-in digital mic and mic jack appropriately
+   order is: external mic -> dock mic -> interal mic */
+static void cxt5066_thinkpad_automic(struct hda_codec *codec)
+{
+       unsigned int ext_present, dock_present;
+
+       static struct hda_verb ext_mic_present[] = {
+               {0x14, AC_VERB_SET_CONNECT_SEL, 0},
+               {0x17, AC_VERB_SET_CONNECT_SEL, 1},
+               {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+               {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+               {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+               {}
+       };
+       static struct hda_verb dock_mic_present[] = {
+               {0x14, AC_VERB_SET_CONNECT_SEL, 0},
+               {0x17, AC_VERB_SET_CONNECT_SEL, 0},
+               {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+               {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+               {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+               {}
+       };
+       static struct hda_verb ext_mic_absent[] = {
+               {0x14, AC_VERB_SET_CONNECT_SEL, 2},
+               {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+               {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+               {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+               {}
+       };
+
+       ext_present = snd_hda_jack_detect(codec, 0x1b);
+       dock_present = snd_hda_jack_detect(codec, 0x1a);
+       if (ext_present) {
+               snd_printdd("CXT5066: external microphone detected\n");
+               snd_hda_sequence_write(codec, ext_mic_present);
+       } else if (dock_present) {
+               snd_printdd("CXT5066: dock microphone detected\n");
+               snd_hda_sequence_write(codec, dock_mic_present);
+       } else {
+               snd_printdd("CXT5066: external microphone absent\n");
+               snd_hda_sequence_write(codec, ext_mic_absent);
+       }
+}
+
+/* mute internal speaker if HP is plugged */
+static void cxt5066_hp_automute(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+       unsigned int portA, portD;
+
+       /* Port A */
+       portA = snd_hda_jack_detect(codec, 0x19);
+
+       /* Port D */
+       portD = snd_hda_jack_detect(codec, 0x1c);
+
+       spec->hp_present = !!(portA);
+       spec->hp_present |= portD ? 2 : 0;
+       snd_printdd("CXT5066: hp automute portA=%x portD=%x present=%d\n",
+               portA, portD, spec->hp_present);
+       cxt5066_update_speaker(codec);
+}
+
+/* unsolicited event for jack sensing */
+static void cxt5066_olpc_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+       struct conexant_spec *spec = codec->spec;
+       snd_printdd("CXT5066: unsol event %x (%x)\n", res, res >> 26);
+       switch (res >> 26) {
+       case CONEXANT_HP_EVENT:
+               cxt5066_hp_automute(codec);
+               break;
+       case CONEXANT_MIC_EVENT:
+               /* ignore mic events in DC mode; we're always using the jack */
+               if (!spec->dc_enable)
+                       cxt5066_olpc_automic(codec);
+               break;
+       }
+}
+
+/* unsolicited event for jack sensing */
+static void cxt5066_vostro_event(struct hda_codec *codec, unsigned int res)
+{
+       snd_printdd("CXT5066_vostro: unsol event %x (%x)\n", res, res >> 26);
+       switch (res >> 26) {
+       case CONEXANT_HP_EVENT:
+               cxt5066_hp_automute(codec);
+               break;
+       case CONEXANT_MIC_EVENT:
+               cxt5066_vostro_automic(codec);
+               break;
+       }
+}
+
+/* unsolicited event for jack sensing */
+static void cxt5066_ideapad_event(struct hda_codec *codec, unsigned int res)
+{
+       snd_printdd("CXT5066_ideapad: unsol event %x (%x)\n", res, res >> 26);
+       switch (res >> 26) {
+       case CONEXANT_HP_EVENT:
+               cxt5066_hp_automute(codec);
+               break;
+       case CONEXANT_MIC_EVENT:
+               cxt5066_ideapad_automic(codec);
+               break;
+       }
+}
+
+/* unsolicited event for jack sensing */
+static void cxt5066_thinkpad_event(struct hda_codec *codec, unsigned int res)
+{
+       snd_printdd("CXT5066_thinkpad: unsol event %x (%x)\n", res, res >> 26);
+       switch (res >> 26) {
+       case CONEXANT_HP_EVENT:
+               cxt5066_hp_automute(codec);
+               break;
+       case CONEXANT_MIC_EVENT:
+               cxt5066_thinkpad_automic(codec);
+               break;
+       }
+}
+
+static const struct hda_input_mux cxt5066_analog_mic_boost = {
+       .num_items = 5,
+       .items = {
+               { "0dB",  0 },
+               { "10dB", 1 },
+               { "20dB", 2 },
+               { "30dB", 3 },
+               { "40dB", 4 },
+       },
+};
+
+static void cxt5066_set_mic_boost(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+       snd_hda_codec_write_cache(codec, 0x17, 0,
+               AC_VERB_SET_AMP_GAIN_MUTE,
+               AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT |
+                       cxt5066_analog_mic_boost.items[spec->mic_boost].index);
+       if (spec->ideapad || spec->thinkpad) {
+               /* adjust the internal mic as well...it is not through 0x17 */
+               snd_hda_codec_write_cache(codec, 0x23, 0,
+                       AC_VERB_SET_AMP_GAIN_MUTE,
+                       AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_INPUT |
+                               cxt5066_analog_mic_boost.
+                                       items[spec->mic_boost].index);
+       }
+}
+
+static int cxt5066_mic_boost_mux_enum_info(struct snd_kcontrol *kcontrol,
+                                          struct snd_ctl_elem_info *uinfo)
+{
+       return snd_hda_input_mux_info(&cxt5066_analog_mic_boost, uinfo);
+}
+
+static int cxt5066_mic_boost_mux_enum_get(struct snd_kcontrol *kcontrol,
+                                         struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct conexant_spec *spec = codec->spec;
+       ucontrol->value.enumerated.item[0] = spec->mic_boost;
+       return 0;
+}
+
+static int cxt5066_mic_boost_mux_enum_put(struct snd_kcontrol *kcontrol,
+                                         struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct conexant_spec *spec = codec->spec;
+       const struct hda_input_mux *imux = &cxt5066_analog_mic_boost;
+       unsigned int idx;
+       idx = ucontrol->value.enumerated.item[0];
+       if (idx >= imux->num_items)
+               idx = imux->num_items - 1;
+
+       spec->mic_boost = idx;
+       if (!spec->dc_enable)
+               cxt5066_set_mic_boost(codec);
+       return 1;
+}
+
+static void cxt5066_enable_dc(struct hda_codec *codec)
+{
+       const struct hda_verb enable_dc_mode[] = {
+               /* disable gain */
+               {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+
+               /* switch to DC input */
+               {0x17, AC_VERB_SET_CONNECT_SEL, 3},
+               {}
+       };
+
+       /* configure as input source */
+       snd_hda_sequence_write(codec, enable_dc_mode);
+       cxt5066_olpc_select_mic(codec); /* also sets configured bias */
+}
+
+static void cxt5066_disable_dc(struct hda_codec *codec)
+{
+       /* reconfigure input source */
+       cxt5066_set_mic_boost(codec);
+       /* automic also selects the right mic if we're recording */
+       cxt5066_olpc_automic(codec);
+}
+
+static int cxt5066_olpc_dc_get(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct conexant_spec *spec = codec->spec;
+       ucontrol->value.integer.value[0] = spec->dc_enable;
+       return 0;
+}
+
+static int cxt5066_olpc_dc_put(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct conexant_spec *spec = codec->spec;
+       int dc_enable = !!ucontrol->value.integer.value[0];
+
+       if (dc_enable == spec->dc_enable)
+               return 0;
+
+       spec->dc_enable = dc_enable;
+       if (dc_enable)
+               cxt5066_enable_dc(codec);
+       else
+               cxt5066_disable_dc(codec);
+
+       return 1;
+}
+
+static int cxt5066_olpc_dc_bias_enum_info(struct snd_kcontrol *kcontrol,
+                                          struct snd_ctl_elem_info *uinfo)
+{
+       return snd_hda_input_mux_info(&cxt5066_olpc_dc_bias, uinfo);
+}
+
+static int cxt5066_olpc_dc_bias_enum_get(struct snd_kcontrol *kcontrol,
+                                         struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct conexant_spec *spec = codec->spec;
+       ucontrol->value.enumerated.item[0] = spec->dc_input_bias;
+       return 0;
+}
+
+static int cxt5066_olpc_dc_bias_enum_put(struct snd_kcontrol *kcontrol,
+                                         struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct conexant_spec *spec = codec->spec;
+       const struct hda_input_mux *imux = &cxt5066_analog_mic_boost;
+       unsigned int idx;
+
+       idx = ucontrol->value.enumerated.item[0];
+       if (idx >= imux->num_items)
+               idx = imux->num_items - 1;
+
+       spec->dc_input_bias = idx;
+       if (spec->dc_enable)
+               cxt5066_set_olpc_dc_bias(codec);
+       return 1;
+}
+
+static void cxt5066_olpc_capture_prepare(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+       /* mark as recording and configure the microphone widget so that the
+        * recording LED comes on. */
+       spec->recording = 1;
+       cxt5066_olpc_select_mic(codec);
+}
+
+static void cxt5066_olpc_capture_cleanup(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+       const struct hda_verb disable_mics[] = {
+               /* disable external mic, port B */
+               {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+               /* disble internal mic, port C */
+               {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+               /* disable DC capture, port F */
+               {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+               {},
+       };
+
+       snd_hda_sequence_write(codec, disable_mics);
+       spec->recording = 0;
+}
+
+static struct hda_input_mux cxt5066_capture_source = {
+       .num_items = 4,
+       .items = {
+               { "Mic B", 0 },
+               { "Mic C", 1 },
+               { "Mic E", 2 },
+               { "Mic F", 3 },
+       },
+};
+
+static struct hda_bind_ctls cxt5066_bind_capture_vol_others = {
+       .ops = &snd_hda_bind_vol,
+       .values = {
+               HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_INPUT),
+               HDA_COMPOSE_AMP_VAL(0x14, 3, 2, HDA_INPUT),
+               0
+       },
+};
+
+static struct hda_bind_ctls cxt5066_bind_capture_sw_others = {
+       .ops = &snd_hda_bind_sw,
+       .values = {
+               HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_INPUT),
+               HDA_COMPOSE_AMP_VAL(0x14, 3, 2, HDA_INPUT),
+               0
+       },
+};
+
+static struct snd_kcontrol_new cxt5066_mixer_master[] = {
+       HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT),
+       {}
+};
+
+static struct snd_kcontrol_new cxt5066_mixer_master_olpc[] = {
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Master Playback Volume",
+               .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+                                 SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+                                 SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,
+               .subdevice = HDA_SUBDEV_AMP_FLAG,
+               .info = snd_hda_mixer_amp_volume_info,
+               .get = snd_hda_mixer_amp_volume_get,
+               .put = snd_hda_mixer_amp_volume_put,
+               .tlv = { .c = snd_hda_mixer_amp_tlv },
+               /* offset by 28 volume steps to limit minimum gain to -46dB */
+               .private_value =
+                       HDA_COMPOSE_AMP_VAL_OFS(0x10, 3, 0, HDA_OUTPUT, 28),
+       },
+       {}
+};
+
+static struct snd_kcontrol_new cxt5066_mixer_olpc_dc[] = {
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "DC Mode Enable Switch",
+               .info = snd_ctl_boolean_mono_info,
+               .get = cxt5066_olpc_dc_get,
+               .put = cxt5066_olpc_dc_put,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "DC Input Bias Enum",
+               .info = cxt5066_olpc_dc_bias_enum_info,
+               .get = cxt5066_olpc_dc_bias_enum_get,
+               .put = cxt5066_olpc_dc_bias_enum_put,
+       },
+       {}
+};
+
+static struct snd_kcontrol_new cxt5066_mixers[] = {
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Master Playback Switch",
+               .info = cxt_eapd_info,
+               .get = cxt_eapd_get,
+               .put = cxt5066_hp_master_sw_put,
+               .private_value = 0x1d,
+       },
+
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Analog Mic Boost Capture Enum",
+               .info = cxt5066_mic_boost_mux_enum_info,
+               .get = cxt5066_mic_boost_mux_enum_get,
+               .put = cxt5066_mic_boost_mux_enum_put,
+       },
+
+       HDA_BIND_VOL("Capture Volume", &cxt5066_bind_capture_vol_others),
+       HDA_BIND_SW("Capture Switch", &cxt5066_bind_capture_sw_others),
+       {}
+};
+
+static struct snd_kcontrol_new cxt5066_vostro_mixers[] = {
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Int Mic Boost Capture Enum",
+               .info = cxt5066_mic_boost_mux_enum_info,
+               .get = cxt5066_mic_boost_mux_enum_get,
+               .put = cxt5066_mic_boost_mux_enum_put,
+               .private_value = 0x23 | 0x100,
+       },
+       HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
+       {}
+};
+
+static struct hda_verb cxt5066_init_verbs[] = {
+       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port B */
+       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port C */
+       {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port F */
+       {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port E */
+
+       /* Speakers  */
+       {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
+
+       /* HP, Amp  */
+       {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
+
+       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x1c, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
+
+       /* DAC1 */
+       {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+
+       /* Node 14 connections: 0x17 0x18 0x23 0x24 0x27 */
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2) | 0x50},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+
+       /* no digital microphone support yet */
+       {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+       /* Audio input selector */
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x3},
+
+       /* SPDIF route: PCM */
+       {0x20, AC_VERB_SET_CONNECT_SEL, 0x0},
+       {0x22, AC_VERB_SET_CONNECT_SEL, 0x0},
+
+       {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+
+       /* EAPD */
+       {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
+
+       /* not handling these yet */
+       {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
+       {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
+       {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
+       {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
+       {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
+       {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
+       {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
+       {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
+       { } /* end */
+};
+
+static struct hda_verb cxt5066_init_verbs_olpc[] = {
+       /* Port A: headphones */
+       {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
+
+       /* Port B: external microphone */
+       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+       /* Port C: internal microphone */
+       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+       /* Port D: unused */
+       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+       /* Port E: unused, but has primary EAPD */
+       {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+       {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
+
+       /* Port F: external DC input through microphone port */
+       {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+       /* Port G: internal speakers */
+       {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
+
+       /* DAC1 */
+       {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+
+       /* DAC2: unused */
+       {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+
+       /* Disable digital microphone port */
+       {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+       /* Audio input selectors */
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x3},
+       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+
+       /* Disable SPDIF */
+       {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+       {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+       /* enable unsolicited events for Port A and B */
+       {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
+       {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
+       { } /* end */
+};
+
+static struct hda_verb cxt5066_init_verbs_vostro[] = {
+       /* Port A: headphones */
+       {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+       {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
+
+       /* Port B: external microphone */
+       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+       /* Port C: unused */
+       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+       /* Port D: unused */
+       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+       /* Port E: unused, but has primary EAPD */
+       {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+       {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
+
+       /* Port F: unused */
+       {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+       /* Port G: internal speakers */
+       {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
+
+       /* DAC1 */
+       {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+
+       /* DAC2: unused */
+       {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+
+       /* Digital microphone port */
+       {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+
+       /* Audio input selectors */
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x3},
+       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+
+       /* Disable SPDIF */
+       {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+       {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+       /* enable unsolicited events for Port A and B */
+       {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
+       {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
+       { } /* end */
+};
+
+static struct hda_verb cxt5066_init_verbs_ideapad[] = {
+       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port B */
+       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port C */
+       {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port F */
+       {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port E */
+
+       /* Speakers  */
+       {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
+
+       /* HP, Amp  */
+       {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
+
+       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x1c, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
+
+       /* DAC1 */
+       {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+
+       /* Node 14 connections: 0x17 0x18 0x23 0x24 0x27 */
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2) | 0x50},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+       {0x14, AC_VERB_SET_CONNECT_SEL, 2},     /* default to internal mic */
+
+       /* Audio input selector */
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x2},
+       {0x17, AC_VERB_SET_CONNECT_SEL, 1},     /* route ext mic */
+
+       /* SPDIF route: PCM */
+       {0x20, AC_VERB_SET_CONNECT_SEL, 0x0},
+       {0x22, AC_VERB_SET_CONNECT_SEL, 0x0},
+
+       {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+
+       /* internal microphone */
+       {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* enable int mic */
+
+       /* EAPD */
+       {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
+
+       {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
+       {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
+       { } /* end */
+};
+
+static struct hda_verb cxt5066_init_verbs_thinkpad[] = {
+       {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port F */
+       {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port E */
+
+       /* Port G: internal speakers  */
+       {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
+
+       /* Port A: HP, Amp  */
+       {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+       {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
+
+       /* Port B: Mic Dock */
+       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+       /* Port C: Mic */
+       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+       /* Port D: HP Dock, Amp */
+       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+       {0x1c, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
+
+       /* DAC1 */
+       {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+
+       /* Node 14 connections: 0x17 0x18 0x23 0x24 0x27 */
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2) | 0x50},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+       {0x14, AC_VERB_SET_CONNECT_SEL, 2},     /* default to internal mic */
+
+       /* Audio input selector */
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x2},
+       {0x17, AC_VERB_SET_CONNECT_SEL, 1},     /* route ext mic */
+
+       /* SPDIF route: PCM */
+       {0x20, AC_VERB_SET_CONNECT_SEL, 0x0},
+       {0x22, AC_VERB_SET_CONNECT_SEL, 0x0},
+
+       {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+
+       /* internal microphone */
+       {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* enable int mic */
+
+       /* EAPD */
+       {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
+
+       /* enable unsolicited events for Port A, B, C and D */
+       {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
+       {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
+       {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
+       {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
+       { } /* end */
+};
+
+static struct hda_verb cxt5066_init_verbs_portd_lo[] = {
+       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       { } /* end */
+};
+
+/* initialize jack-sensing, too */
+static int cxt5066_init(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+
+       snd_printdd("CXT5066: init\n");
+       conexant_init(codec);
+       if (codec->patch_ops.unsol_event) {
+               cxt5066_hp_automute(codec);
+               if (spec->dell_vostro)
+                       cxt5066_vostro_automic(codec);
+               else if (spec->ideapad)
+                       cxt5066_ideapad_automic(codec);
+               else if (spec->thinkpad)
+                       cxt5066_thinkpad_automic(codec);
+       }
+       cxt5066_set_mic_boost(codec);
+       return 0;
+}
+
+static int cxt5066_olpc_init(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+       snd_printdd("CXT5066: init\n");
+       conexant_init(codec);
+       cxt5066_hp_automute(codec);
+       if (!spec->dc_enable) {
+               cxt5066_set_mic_boost(codec);
+               cxt5066_olpc_automic(codec);
+       } else {
+               cxt5066_enable_dc(codec);
+       }
+       return 0;
+}
+
+enum {
+       CXT5066_LAPTOP,         /* Laptops w/ EAPD support */
+       CXT5066_DELL_LAPTOP,    /* Dell Laptop */
+       CXT5066_OLPC_XO_1_5,    /* OLPC XO 1.5 */
+       CXT5066_DELL_VOSTO,     /* Dell Vostro 1015i */
+       CXT5066_IDEAPAD,        /* Lenovo IdeaPad U150 */
+       CXT5066_THINKPAD,       /* Lenovo ThinkPad T410s, others? */
+       CXT5066_MODELS
+};
+
+static const char *cxt5066_models[CXT5066_MODELS] = {
+       [CXT5066_LAPTOP]        = "laptop",
+       [CXT5066_DELL_LAPTOP]   = "dell-laptop",
+       [CXT5066_OLPC_XO_1_5]   = "olpc-xo-1_5",
+       [CXT5066_DELL_VOSTO]    = "dell-vostro",
+       [CXT5066_IDEAPAD]       = "ideapad",
+       [CXT5066_THINKPAD]      = "thinkpad",
+};
+
+static struct snd_pci_quirk cxt5066_cfg_tbl[] = {
+       SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board",
+                     CXT5066_LAPTOP),
+       SND_PCI_QUIRK(0x1028, 0x02f5, "Dell",
+                     CXT5066_DELL_LAPTOP),
+       SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5),
+       SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTO),
+       SND_PCI_QUIRK(0x1028, 0x0408, "Dell Inspiron One 19T", CXT5066_IDEAPAD),
+       SND_PCI_QUIRK(0x1179, 0xff50, "Toshiba Satellite P500-PSPGSC-01800T", CXT5066_OLPC_XO_1_5),
+       SND_PCI_QUIRK(0x1179, 0xffe0, "Toshiba Satellite Pro T130-15F", CXT5066_OLPC_XO_1_5),
+       SND_PCI_QUIRK(0x17aa, 0x3a0d, "ideapad", CXT5066_IDEAPAD),
+       SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo Thinkpad", CXT5066_THINKPAD),
+       {}
+};
+
+static int patch_cxt5066(struct hda_codec *codec)
+{
+       struct conexant_spec *spec;
+       int board_config;
+
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (!spec)
+               return -ENOMEM;
+       codec->spec = spec;
+
+       codec->patch_ops = conexant_patch_ops;
+       codec->patch_ops.init = conexant_init;
+
+       spec->dell_automute = 0;
+       spec->multiout.max_channels = 2;
+       spec->multiout.num_dacs = ARRAY_SIZE(cxt5066_dac_nids);
+       spec->multiout.dac_nids = cxt5066_dac_nids;
+       spec->multiout.dig_out_nid = CXT5066_SPDIF_OUT;
+       spec->num_adc_nids = 1;
+       spec->adc_nids = cxt5066_adc_nids;
+       spec->capsrc_nids = cxt5066_capsrc_nids;
+       spec->input_mux = &cxt5066_capture_source;
+
+       spec->port_d_mode = PIN_HP;
+
+       spec->num_init_verbs = 1;
+       spec->init_verbs[0] = cxt5066_init_verbs;
+       spec->num_channel_mode = ARRAY_SIZE(cxt5066_modes);
+       spec->channel_mode = cxt5066_modes;
+       spec->cur_adc = 0;
+       spec->cur_adc_idx = 0;
+
+       board_config = snd_hda_check_board_config(codec, CXT5066_MODELS,
+                                                 cxt5066_models, cxt5066_cfg_tbl);
+       switch (board_config) {
+       default:
+       case CXT5066_LAPTOP:
+               spec->mixers[spec->num_mixers++] = cxt5066_mixer_master;
+               spec->mixers[spec->num_mixers++] = cxt5066_mixers;
+               break;
+       case CXT5066_DELL_LAPTOP:
+               spec->mixers[spec->num_mixers++] = cxt5066_mixer_master;
+               spec->mixers[spec->num_mixers++] = cxt5066_mixers;
+
+               spec->port_d_mode = PIN_OUT;
+               spec->init_verbs[spec->num_init_verbs] = cxt5066_init_verbs_portd_lo;
+               spec->num_init_verbs++;
+               spec->dell_automute = 1;
+               break;
+       case CXT5066_OLPC_XO_1_5:
+               codec->patch_ops.init = cxt5066_olpc_init;
+               codec->patch_ops.unsol_event = cxt5066_olpc_unsol_event;
+               spec->init_verbs[0] = cxt5066_init_verbs_olpc;
+               spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc;
+               spec->mixers[spec->num_mixers++] = cxt5066_mixer_olpc_dc;
+               spec->mixers[spec->num_mixers++] = cxt5066_mixers;
+               spec->port_d_mode = 0;
+               spec->mic_boost = 3; /* default 30dB gain */
+
+               /* no S/PDIF out */
+               spec->multiout.dig_out_nid = 0;
+
+               /* input source automatically selected */
+               spec->input_mux = NULL;
+
+               /* our capture hooks which allow us to turn on the microphone LED
+                * at the right time */
+               spec->capture_prepare = cxt5066_olpc_capture_prepare;
+               spec->capture_cleanup = cxt5066_olpc_capture_cleanup;
+               break;
+       case CXT5066_DELL_VOSTO:
+               codec->patch_ops.init = cxt5066_init;
+               codec->patch_ops.unsol_event = cxt5066_vostro_event;
+               spec->init_verbs[0] = cxt5066_init_verbs_vostro;
+               spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc;
+               spec->mixers[spec->num_mixers++] = cxt5066_mixers;
+               spec->mixers[spec->num_mixers++] = cxt5066_vostro_mixers;
+               spec->port_d_mode = 0;
+               spec->dell_vostro = 1;
+               spec->mic_boost = 3; /* default 30dB gain */
+               snd_hda_attach_beep_device(codec, 0x13);
+
+               /* no S/PDIF out */
+               spec->multiout.dig_out_nid = 0;
+
+               /* input source automatically selected */
+               spec->input_mux = NULL;
+               break;
+       case CXT5066_IDEAPAD:
+               codec->patch_ops.init = cxt5066_init;
+               codec->patch_ops.unsol_event = cxt5066_ideapad_event;
+               spec->mixers[spec->num_mixers++] = cxt5066_mixer_master;
+               spec->mixers[spec->num_mixers++] = cxt5066_mixers;
+               spec->init_verbs[0] = cxt5066_init_verbs_ideapad;
+               spec->port_d_mode = 0;
+               spec->ideapad = 1;
+               spec->mic_boost = 2;    /* default 20dB gain */
+
+               /* no S/PDIF out */
+               spec->multiout.dig_out_nid = 0;
+
+               /* input source automatically selected */
+               spec->input_mux = NULL;
+               break;
+       case CXT5066_THINKPAD:
+               codec->patch_ops.init = cxt5066_init;
+               codec->patch_ops.unsol_event = cxt5066_thinkpad_event;
+               spec->mixers[spec->num_mixers++] = cxt5066_mixer_master;
+               spec->mixers[spec->num_mixers++] = cxt5066_mixers;
+               spec->init_verbs[0] = cxt5066_init_verbs_thinkpad;
+               spec->thinkpad = 1;
+               spec->port_d_mode = PIN_OUT;
+               spec->mic_boost = 2;    /* default 20dB gain */
+
+               /* no S/PDIF out */
+               spec->multiout.dig_out_nid = 0;
+
+               /* input source automatically selected */
+               spec->input_mux = NULL;
+               break;
+       }
+
+       return 0;
+}
+
+/*
+ */
+
+static struct hda_codec_preset snd_hda_preset_conexant[] = {
+       { .id = 0x14f15045, .name = "CX20549 (Venice)",
+         .patch = patch_cxt5045 },
+       { .id = 0x14f15047, .name = "CX20551 (Waikiki)",
+         .patch = patch_cxt5047 },
+       { .id = 0x14f15051, .name = "CX20561 (Hermosa)",
+         .patch = patch_cxt5051 },
+       { .id = 0x14f15066, .name = "CX20582 (Pebble)",
+         .patch = patch_cxt5066 },
+       { .id = 0x14f15067, .name = "CX20583 (Pebble HSF)",
+         .patch = patch_cxt5066 },
+       { .id = 0x14f15069, .name = "CX20585",
+         .patch = patch_cxt5066 },
+       {} /* terminator */
+};
+
+MODULE_ALIAS("snd-hda-codec-id:14f15045");
+MODULE_ALIAS("snd-hda-codec-id:14f15047");
+MODULE_ALIAS("snd-hda-codec-id:14f15051");
+MODULE_ALIAS("snd-hda-codec-id:14f15066");
+MODULE_ALIAS("snd-hda-codec-id:14f15067");
+MODULE_ALIAS("snd-hda-codec-id:14f15069");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Conexant HD-audio codec");
+
+static struct hda_codec_preset_list conexant_list = {
+       .preset = snd_hda_preset_conexant,
+       .owner = THIS_MODULE,
+};
+
+static int __init patch_conexant_init(void)
+{
+       return snd_hda_add_codec_preset(&conexant_list);
+}
+
+static void __exit patch_conexant_exit(void)
+{
+       snd_hda_delete_codec_preset(&conexant_list);
+}
+
+module_init(patch_conexant_init)
+module_exit(patch_conexant_exit)