]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - sound/pci/hda/patch_conexant.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6 into for-linus
[linux-2.6-omap-h63xx.git] / sound / pci / hda / patch_conexant.c
index 080e3001d9c548d0e775425b7ce94b743de88782..f7cd3a804b11b5faa9283646cf0817efce8069e1 100644 (file)
@@ -20,7 +20,6 @@
  *  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>
@@ -65,6 +64,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;
@@ -85,7 +89,7 @@ struct conexant_spec {
        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];
 
 };
 
@@ -218,6 +222,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_setup_stream(codec, spec->cur_adc, 0, 0, 0);
+       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;
@@ -232,7 +271,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];
 
@@ -373,7 +417,7 @@ 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)
@@ -444,7 +488,7 @@ static int conexant_ch_mode_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 },
@@ -454,7 +498,16 @@ 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 = 3,
+       .items = {
+               { "IntMic", 0x1 },
+               { "ExtMic", 0x2 },
+               { "LineIn", 0x3 },
        }
 };
 
@@ -554,10 +607,16 @@ static struct snd_kcontrol_new cxt5045_mixers[] = {
                .get = conexant_mux_enum_get,
                .put = conexant_mux_enum_put
        },
-       HDA_CODEC_VOLUME("Int Mic Volume", 0x1a, 0x01, HDA_INPUT),
-       HDA_CODEC_MUTE("Int Mic Switch", 0x1a, 0x01, HDA_INPUT),
-       HDA_CODEC_VOLUME("Ext Mic Volume", 0x1a, 0x02, HDA_INPUT),
-       HDA_CODEC_MUTE("Ext Mic Switch", 0x1a, 0x02, HDA_INPUT),
+       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,
@@ -571,32 +630,66 @@ static struct snd_kcontrol_new cxt5045_mixers[] = {
        {}
 };
 
+static struct snd_kcontrol_new cxt5045_benq_mixers[] = {
+       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),
+
+       {}
+};
+
 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_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,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_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 */
@@ -690,7 +783,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)},
@@ -735,8 +829,10 @@ static int cxt5045_init(struct hda_codec *codec)
 
 
 enum {
-       CXT5045_LAPTOP,  /* Laptops w/ EAPD support */
-       CXT5045_FUJITSU, /* Laptops w/ EAPD support */ 
+       CXT5045_LAPTOP_HPSENSE,
+       CXT5045_LAPTOP_MICSENSE,
+       CXT5045_LAPTOP_HPMICSENSE,
+       CXT5045_BENQ,
 #ifdef CONFIG_SND_DEBUG
        CXT5045_TEST,
 #endif
@@ -744,23 +840,35 @@ enum {
 };
 
 static const char *cxt5045_models[CXT5045_MODELS] = {
-       [CXT5045_LAPTOP]        = "laptop",
-       [CXT5045_FUJITSU]       = "fujitsu",
+       [CXT5045_LAPTOP_HPSENSE]        = "laptop-hpsense",
+       [CXT5045_LAPTOP_MICSENSE]       = "laptop-micsense",
+       [CXT5045_LAPTOP_HPMICSENSE]     = "laptop-hpmicsense",
+       [CXT5045_BENQ]                  = "benq",
 #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(0x103c, 0x30b2, "HP DV Series", CXT5045_LAPTOP),
-       SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2120", CXT5045_LAPTOP),
-       SND_PCI_QUIRK(0x103c, 0x30cd, "HP DV Series", CXT5045_LAPTOP),
-       SND_PCI_QUIRK(0x103c, 0x30d9, "HP Spartan", CXT5045_LAPTOP),
-       SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_FUJITSU),
-       SND_PCI_QUIRK(0x1734, 0x10cb, "Fujitsu Si3515", CXT5045_LAPTOP),
-       SND_PCI_QUIRK(0x8086, 0x2111, "Conexant Reference board", CXT5045_LAPTOP),
+       SND_PCI_QUIRK(0x103c, 0x30a5, "HP", CXT5045_LAPTOP_HPSENSE),
+       SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV Series", CXT5045_LAPTOP_HPSENSE),
+       SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2120", CXT5045_LAPTOP_HPSENSE),
+       SND_PCI_QUIRK(0x103c, 0x30b7, "HP DV6000Z", CXT5045_LAPTOP_HPSENSE),
+       SND_PCI_QUIRK(0x103c, 0x30bb, "HP DV8000", CXT5045_LAPTOP_HPSENSE),
+       SND_PCI_QUIRK(0x103c, 0x30cd, "HP DV Series", CXT5045_LAPTOP_HPSENSE),
+       SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV9533EG", CXT5045_LAPTOP_HPSENSE),
+       SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT5045_LAPTOP_HPSENSE),
+       SND_PCI_QUIRK(0x103c, 0x30d9, "HP Spartan", CXT5045_LAPTOP_HPSENSE),
+       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_HPSENSE),
+       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(0x1631, 0xc106, "Packard Bell", CXT5045_LAPTOP_HPMICSENSE),
+       SND_PCI_QUIRK(0x1631, 0xc107, "Packard Bell", CXT5045_LAPTOP_HPMICSENSE),
+       SND_PCI_QUIRK(0x8086, 0x2111, "Conexant Reference board", CXT5045_LAPTOP_HPSENSE),
        {}
 };
 
@@ -798,7 +906,7 @@ static int patch_cxt5045(struct hda_codec *codec)
                                                  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;
@@ -806,20 +914,53 @@ static int patch_cxt5045(struct hda_codec *codec)
                spec->mixers[0] = cxt5045_mixers;
                codec->patch_ops.init = cxt5045_init;
                break;
-       case CXT5045_FUJITSU:
+       case CXT5045_LAPTOP_MICSENSE:
                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;
 #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 
        }
+
+       /*
+        * Fix max PCM level to 0 dB
+        * (originall 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));
+
        return 0;
 }
 
@@ -928,13 +1069,13 @@ static void cxt5047_hp2_automute(struct hda_codec *codec)
 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;
@@ -951,8 +1092,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;
@@ -1161,6 +1301,17 @@ static struct snd_kcontrol_new cxt5047_test_mixer[] = {
                .get = conexant_mux_enum_get,
                .put = conexant_mux_enum_put,
        },
+       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 */
 };
 
@@ -1250,9 +1401,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, 0x30a5, "HP DV5200T/DV8000T", CXT5047_LAPTOP_HP),
        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(0x1179, 0xff31, "Toshiba P100", CXT5047_LAPTOP_EAPD),
        {}
 };
@@ -1319,10 +1470,260 @@ static int patch_cxt5047(struct hda_codec *codec)
        return 0;
 }
 
+/* Conexant 5051 specific */
+static hda_nid_t cxt5051_dac_nids[1] = { 0x10 };
+static hda_nid_t cxt5051_adc_nids[2] = { 0x14, 0x15 };
+#define CXT5051_SPDIF_OUT      0x1C
+#define CXT5051_PORTB_EVENT    0x38
+#define CXT5051_PORTC_EVENT    0x39
+
+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;
+       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)
+{
+       unsigned int present;
+
+       present = snd_hda_codec_read(codec, 0x17, 0,
+                                    AC_VERB_GET_PIN_SENSE, 0) &
+               AC_PINSENSE_PRESENCE;
+       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;
+
+       present = snd_hda_codec_read(codec, 0x18, 0,
+                                    AC_VERB_GET_PIN_SENSE, 0) &
+               AC_PINSENSE_PRESENCE;
+       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_setup_stream(codec, spec->cur_adc, 0, 0, 0);
+               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_codec_read(codec, 0x16, 0,
+                                    AC_VERB_GET_PIN_SENSE, 0) &
+               AC_PINSENSE_PRESENCE;
+       cxt5051_update_speaker(codec);
+}
+
+/* unsolicited event for HP jack sensing */
+static void cxt5051_hp_unsol_event(struct hda_codec *codec,
+                                  unsigned int res)
+{
+       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;
+       }
+}
+
+static struct snd_kcontrol_new cxt5051_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),
+       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_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),
+       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 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_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},
+       {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTB_EVENT},
+       {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTC_EVENT},
+       { } /* end */
+};
+
+/* initialize jack-sensing, too */
+static int cxt5051_init(struct hda_codec *codec)
+{
+       conexant_init(codec);
+       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_MODELS
+};
+
+static const char *cxt5051_models[CXT5051_MODELS] = {
+       [CXT5051_LAPTOP]        = "laptop",
+       [CXT5051_HP]            = "hp",
+};
+
+static struct snd_pci_quirk cxt5051_cfg_tbl[] = {
+       SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board",
+                     CXT5051_LAPTOP),
+       SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP),
+       {}
+};
+
+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;
+       mutex_init(&spec->amp_mutex);
+       codec->spec = spec;
+
+       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 = 1;
+       spec->mixers[0] = cxt5051_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;
+
+       board_config = snd_hda_check_board_config(codec, CXT5051_MODELS,
+                                                 cxt5051_models,
+                                                 cxt5051_cfg_tbl);
+       switch (board_config) {
+       case CXT5051_HP:
+               codec->patch_ops.unsol_event = cxt5051_hp_unsol_event;
+               spec->mixers[0] = cxt5051_hp_mixers;
+               break;
+       default:
+       case CXT5051_LAPTOP:
+               codec->patch_ops.unsol_event = cxt5051_hp_unsol_event;
+               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 },
+       { .id = 0x14f15051, .name = "CX20561 (Hermosa)",
+         .patch = patch_cxt5051 },
        {} /* terminator */
 };