ALSA: hda - Fix detection of dual headphones
[safe/jmp/linux-2.6] / sound / pci / hda / patch_sigmatel.c
index 0201f2c..39001c4 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/pci.h>
+#include <linux/dmi.h>
 #include <sound/core.h>
 #include <sound/asoundef.h>
 #include <sound/jack.h>
@@ -158,6 +159,7 @@ enum {
        STAC_D965_5ST_NO_FP,
        STAC_DELL_3ST,
        STAC_DELL_BIOS,
+       STAC_927X_VOLKNOB,
        STAC_927X_MODELS
 };
 
@@ -182,8 +184,8 @@ struct sigmatel_jack {
 
 struct sigmatel_mic_route {
        hda_nid_t pin;
-       unsigned char mux_idx;
-       unsigned char dmux_idx;
+       signed char mux_idx;
+       signed char dmux_idx;
 };
 
 struct sigmatel_spec {
@@ -907,6 +909,16 @@ static struct hda_verb d965_core_init[] = {
        {}
 };
 
+static struct hda_verb dell_3st_core_init[] = {
+       /* don't set delta bit */
+       {0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0x7f},
+       /* unmute node 0x1b */
+       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+       /* select node 0x03 as DAC */
+       {0x0b, AC_VERB_SET_CONNECT_SEL, 0x01},
+       {}
+};
+
 static struct hda_verb stac927x_core_init[] = {
        /* set master volume and direct control */      
        { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
@@ -915,6 +927,14 @@ static struct hda_verb stac927x_core_init[] = {
        {}
 };
 
+static struct hda_verb stac927x_volknob_core_init[] = {
+       /* don't set delta bit */
+       {0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0x7f},
+       /* enable analog pc beep path */
+       {0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5},
+       {}
+};
+
 static struct hda_verb stac9205_core_init[] = {
        /* set master volume and direct control */      
        { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
@@ -1065,7 +1085,7 @@ static int stac92xx_build_controls(struct hda_codec *codec)
        if (!spec->auto_mic && spec->num_dmuxes > 0 &&
            snd_hda_get_bool_hint(codec, "separate_dmux") == 1) {
                stac_dmux_mixer.count = spec->num_dmuxes;
-               err = snd_hda_ctl_add(codec,
+               err = snd_hda_ctl_add(codec, 0,
                                  snd_ctl_new1(&stac_dmux_mixer, codec));
                if (err < 0)
                        return err;
@@ -1081,7 +1101,7 @@ static int stac92xx_build_controls(struct hda_codec *codec)
                        spec->spdif_mute = 1;
                }
                stac_smux_mixer.count = spec->num_smuxes;
-               err = snd_hda_ctl_add(codec,
+               err = snd_hda_ctl_add(codec, 0,
                                  snd_ctl_new1(&stac_smux_mixer, codec));
                if (err < 0)
                        return err;
@@ -1570,6 +1590,8 @@ static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = {
                                "Dell Studio 17", STAC_DELL_M6_DMIC),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02be,
                                "Dell Studio 1555", STAC_DELL_M6_DMIC),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02bd,
+                               "Dell Studio 1557", STAC_DELL_M6_DMIC),
        {} /* terminator */
 };
 
@@ -1586,8 +1608,8 @@ static unsigned int ref92hd83xxx_pin_configs[10] = {
 };
 
 static unsigned int dell_s14_pin_configs[10] = {
-       0x02214030, 0x02211010, 0x02a19020, 0x01014050,
-       0x40f000f0, 0x01819040, 0x40f000f0, 0x90a60160,
+       0x0221403f, 0x0221101f, 0x02a19020, 0x90170110,
+       0x40f000f0, 0x40f000f0, 0x40f000f0, 0x90a60160,
        0x40f000f0, 0x40f000f0,
 };
 
@@ -1674,6 +1696,8 @@ static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
                      "DFI LanParty", STAC_92HD71BXX_REF),
        SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30fb,
                      "HP dv4-1222nr", STAC_HP_DV4_1222NR),
+       SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x1720,
+                         "HP", STAC_HP_DV5),
        SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3080,
                      "HP", STAC_HP_DV5),
        SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x30f0,
@@ -1999,6 +2023,7 @@ static unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = {
        [STAC_D965_5ST_NO_FP]  = d965_5st_no_fp_pin_configs,
        [STAC_DELL_3ST]  = dell_3st_pin_configs,
        [STAC_DELL_BIOS] = NULL,
+       [STAC_927X_VOLKNOB] = NULL,
 };
 
 static const char *stac927x_models[STAC_927X_MODELS] = {
@@ -2010,6 +2035,7 @@ static const char *stac927x_models[STAC_927X_MODELS] = {
        [STAC_D965_5ST_NO_FP]   = "5stack-no-fp",
        [STAC_DELL_3ST]         = "dell-3stack",
        [STAC_DELL_BIOS]        = "dell-bios",
+       [STAC_927X_VOLKNOB]     = "volknob",
 };
 
 static struct snd_pci_quirk stac927x_cfg_tbl[] = {
@@ -2045,6 +2071,8 @@ static struct snd_pci_quirk stac927x_cfg_tbl[] = {
                           "Intel D965", STAC_D965_5ST),
        SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2500,
                           "Intel D965", STAC_D965_5ST),
+       /* volume-knob fixes */
+       SND_PCI_QUIRK_VENDOR(0x10cf, "FSC", STAC_927X_VOLKNOB),
        {} /* terminator */
 };
 
@@ -2620,6 +2648,7 @@ static int stac92xx_clfe_switch_put(struct snd_kcontrol *kcontrol,
 enum {
        STAC_CTL_WIDGET_VOL,
        STAC_CTL_WIDGET_MUTE,
+       STAC_CTL_WIDGET_MUTE_BEEP,
        STAC_CTL_WIDGET_MONO_MUX,
        STAC_CTL_WIDGET_HP_SWITCH,
        STAC_CTL_WIDGET_IO_SWITCH,
@@ -2630,6 +2659,7 @@ enum {
 static struct snd_kcontrol_new stac92xx_control_templates[] = {
        HDA_CODEC_VOLUME(NULL, 0, 0, 0),
        HDA_CODEC_MUTE(NULL, 0, 0, 0),
+       HDA_CODEC_MUTE_BEEP(NULL, 0, 0, 0),
        STAC_MONO_MUX,
        STAC_CODEC_HP_SWITCH(NULL),
        STAC_CODEC_IO_SWITCH(NULL, 0),
@@ -2641,7 +2671,8 @@ static struct snd_kcontrol_new stac92xx_control_templates[] = {
 static struct snd_kcontrol_new *
 stac_control_new(struct sigmatel_spec *spec,
                 struct snd_kcontrol_new *ktemp,
-                const char *name)
+                const char *name,
+                hda_nid_t nid)
 {
        struct snd_kcontrol_new *knew;
 
@@ -2657,6 +2688,8 @@ stac_control_new(struct sigmatel_spec *spec,
                spec->kctls.alloced--;
                return NULL;
        }
+       if (nid)
+               knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
        return knew;
 }
 
@@ -2665,7 +2698,8 @@ static int stac92xx_add_control_temp(struct sigmatel_spec *spec,
                                     int idx, const char *name,
                                     unsigned long val)
 {
-       struct snd_kcontrol_new *knew = stac_control_new(spec, ktemp, name);
+       struct snd_kcontrol_new *knew = stac_control_new(spec, ktemp, name,
+                                                        get_amp_nid_(val));
        if (!knew)
                return -ENOMEM;
        knew->index = idx;
@@ -2736,7 +2770,7 @@ static int stac92xx_add_input_source(struct sigmatel_spec *spec)
        if (!spec->num_adcs || imux->num_items <= 1)
                return 0; /* no need for input source control */
        knew = stac_control_new(spec, &stac_input_src_temp,
-                               stac_input_src_temp.name);
+                               stac_input_src_temp.name, 0);
        if (!knew)
                return -ENOMEM;
        knew->count = spec->num_adcs;
@@ -3193,12 +3227,15 @@ static int stac92xx_auto_create_beep_ctls(struct hda_codec *codec,
 {
        struct sigmatel_spec *spec = codec->spec;
        u32 caps = query_amp_caps(codec, nid, HDA_OUTPUT);
-       int err;
+       int err, type = STAC_CTL_WIDGET_MUTE_BEEP;
+
+       if (spec->anabeep_nid == nid)
+               type = STAC_CTL_WIDGET_MUTE;
 
        /* check for mute support for the the amp */
        if ((caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT) {
-               err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE,
-                       "PC Beep Playback Switch",
+               err = stac92xx_add_control(spec, type,
+                       "Beep Playback Switch",
                        HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT));
                        if (err < 0)
                                return err;
@@ -3207,7 +3244,7 @@ static int stac92xx_auto_create_beep_ctls(struct hda_codec *codec,
        /* check to see if there is volume support for the amp */
        if ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) {
                err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL,
-                       "PC Beep Playback Volume",
+                       "Beep Playback Volume",
                        HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT));
                        if (err < 0)
                                return err;
@@ -3230,12 +3267,7 @@ static int stac92xx_dig_beep_switch_put(struct snd_kcontrol *kcontrol,
                                        struct snd_ctl_elem_value *ucontrol)
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       int enabled = !!ucontrol->value.integer.value[0];
-       if (codec->beep->enabled != enabled) {
-               codec->beep->enabled = enabled;
-               return 1;
-       }
-       return 0;
+       return snd_hda_enable_beep_device(codec, ucontrol->value.integer.value[0]);
 }
 
 static struct snd_kcontrol_new stac92xx_dig_beep_ctrl = {
@@ -3248,7 +3280,7 @@ static struct snd_kcontrol_new stac92xx_dig_beep_ctrl = {
 static int stac92xx_beep_switch_ctl(struct hda_codec *codec)
 {
        return stac92xx_add_control_temp(codec->spec, &stac92xx_dig_beep_ctrl,
-                                        0, "PC Beep Playback Switch", 0);
+                                        0, "Beep Playback Switch", 0);
 }
 #endif
 
@@ -3469,18 +3501,26 @@ static int set_mic_route(struct hda_codec *codec,
                        break;
        if (i <= AUTO_PIN_FRONT_MIC) {
                /* analog pin */
-               mic->dmux_idx = 0;
                i = get_connection_index(codec, spec->mux_nids[0], pin);
                if (i < 0)
                        return -1;
                mic->mux_idx = i;
+               mic->dmux_idx = -1;
+               if (spec->dmux_nids)
+                       mic->dmux_idx = get_connection_index(codec,
+                                                            spec->dmux_nids[0],
+                                                            spec->mux_nids[0]);
        }  else if (spec->dmux_nids) {
                /* digital pin */
-               mic->mux_idx = 0;
                i = get_connection_index(codec, spec->dmux_nids[0], pin);
                if (i < 0)
                        return -1;
                mic->dmux_idx = i;
+               mic->mux_idx = -1;
+               if (spec->mux_nids)
+                       mic->mux_idx = get_connection_index(codec,
+                                                           spec->mux_nids[0],
+                                                           spec->dmux_nids[0]);
        }
        return 0;
 }
@@ -3595,6 +3635,26 @@ static void stac92xx_auto_init_hp_out(struct hda_codec *codec)
        }
 }
 
+static int is_dual_headphones(struct hda_codec *codec)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       int i, valid_hps;
+
+       if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT ||
+           spec->autocfg.hp_outs <= 1)
+               return 0;
+       valid_hps = 0;
+       for (i = 0; i < spec->autocfg.hp_outs; i++) {
+               hda_nid_t nid = spec->autocfg.hp_pins[i];
+               unsigned int cfg = snd_hda_codec_get_pincfg(codec, nid);
+               if (get_defcfg_location(cfg) & AC_JACK_LOC_SEPARATE)
+                       continue;
+               valid_hps++;
+       }
+       return (valid_hps > 1);
+}
+
+
 static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out, hda_nid_t dig_in)
 {
        struct sigmatel_spec *spec = codec->spec;
@@ -3611,8 +3671,7 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
        /* If we have no real line-out pin and multiple hp-outs, HPs should
         * be set up as multi-channel outputs.
         */
-       if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT &&
-           spec->autocfg.hp_outs > 1) {
+       if (is_dual_headphones(codec)) {
                /* Copy hp_outs to line_outs, backup line_outs in
                 * speaker_outs so that the following routines can handle
                 * HP pins as primary outputs.
@@ -4293,6 +4352,28 @@ static void stac92xx_free_kctls(struct hda_codec *codec)
        snd_array_free(&spec->kctls);
 }
 
+static void stac92xx_shutup(struct hda_codec *codec)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       int i;
+       hda_nid_t nid;
+
+       /* reset each pin before powering down DAC/ADC to avoid click noise */
+       nid = codec->start_nid;
+       for (i = 0; i < codec->num_nodes; i++, nid++) {
+               unsigned int wcaps = get_wcaps(codec, nid);
+               unsigned int wid_type = get_wcaps_type(wcaps);
+               if (wid_type == AC_WID_PIN)
+                       snd_hda_codec_read(codec, nid, 0,
+                               AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
+       }
+
+       if (spec->eapd_mask)
+               stac_gpio_set(codec, spec->gpio_mask,
+                               spec->gpio_dir, spec->gpio_data &
+                               ~spec->eapd_mask);
+}
+
 static void stac92xx_free(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec = codec->spec;
@@ -4300,6 +4381,7 @@ static void stac92xx_free(struct hda_codec *codec)
        if (! spec)
                return;
 
+       stac92xx_shutup(codec);
        stac92xx_free_jacks(codec);
        snd_array_free(&spec->events);
 
@@ -4350,14 +4432,11 @@ static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid,
                                          pin_ctl & ~flag);
 }
 
-static int get_pin_presence(struct hda_codec *codec, hda_nid_t nid)
+static inline int get_pin_presence(struct hda_codec *codec, hda_nid_t nid)
 {
        if (!nid)
                return 0;
-       if (snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0x00)
-           & (1 << 31))
-               return 1;
-       return 0;
+       return snd_hda_jack_detect(codec, nid);
 }
 
 static void stac92xx_line_out_detect(struct hda_codec *codec,
@@ -4557,11 +4636,11 @@ static void stac92xx_mic_detect(struct hda_codec *codec)
                mic = &spec->ext_mic;
        else
                mic = &spec->int_mic;
-       if (mic->dmux_idx)
+       if (mic->dmux_idx >= 0)
                snd_hda_codec_write_cache(codec, spec->dmux_nids[0], 0,
                                          AC_VERB_SET_CONNECT_SEL,
                                          mic->dmux_idx);
-       else
+       if (mic->mux_idx >= 0)
                snd_hda_codec_write_cache(codec, spec->mux_nids[0], 0,
                                          AC_VERB_SET_CONNECT_SEL,
                                          mic->mux_idx);
@@ -4634,6 +4713,26 @@ static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
        }
 }
 
+static int hp_bseries_system(u32 subsystem_id)
+{
+       switch (subsystem_id) {
+       case 0x103c307e:
+       case 0x103c307f:
+       case 0x103c3080:
+       case 0x103c3081:
+       case 0x103c1722:
+       case 0x103c1723:
+       case 0x103c1724:
+       case 0x103c1725:
+       case 0x103c1726:
+       case 0x103c1727:
+       case 0x103c1728:
+       case 0x103c1729:
+               return 1;
+       }
+       return 0;
+}
+
 #ifdef CONFIG_PROC_FS
 static void stac92hd_proc_hook(struct snd_info_buffer *buffer,
                               struct hda_codec *codec, hda_nid_t nid)
@@ -4723,6 +4822,11 @@ static int stac92xx_hp_check_power_status(struct hda_codec *codec,
                else
                        spec->gpio_data |= spec->gpio_led; /* white */
 
+               if (hp_bseries_system(codec->subsystem_id)) {
+                       /* LED state is inverted on these systems */
+                       spec->gpio_data ^= spec->gpio_led;
+               }
+
                stac_gpio_set(codec, spec->gpio_mask,
                              spec->gpio_dir,
                              spec->gpio_data);
@@ -4734,24 +4838,7 @@ static int stac92xx_hp_check_power_status(struct hda_codec *codec,
 
 static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state)
 {
-       struct sigmatel_spec *spec = codec->spec;
-       int i;
-       hda_nid_t nid;
-
-       /* reset each pin before powering down DAC/ADC to avoid click noise */
-       nid = codec->start_nid;
-       for (i = 0; i < codec->num_nodes; i++, nid++) {
-               unsigned int wcaps = get_wcaps(codec, nid);
-               unsigned int wid_type = get_wcaps_type(wcaps);
-               if (wid_type == AC_WID_PIN)
-                       snd_hda_codec_read(codec, nid, 0,
-                               AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
-       }
-
-       if (spec->eapd_mask)
-               stac_gpio_set(codec, spec->gpio_mask,
-                               spec->gpio_dir, spec->gpio_data &
-                               ~spec->eapd_mask);
+       stac92xx_shutup(codec);
        return 0;
 }
 #endif
@@ -4766,6 +4853,7 @@ static struct hda_codec_ops stac92xx_patch_ops = {
        .suspend = stac92xx_suspend,
        .resume = stac92xx_resume,
 #endif
+       .reboot_notify = stac92xx_shutup,
 };
 
 static int patch_stac9200(struct hda_codec *codec)
@@ -5212,6 +5300,7 @@ static int patch_stac92hd71bxx(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec;
        struct hda_verb *unmute_init = stac92hd71bxx_unmute_core_init;
+       unsigned int pin_cfg;
        int err = 0;
 
        spec  = kzalloc(sizeof(*spec), GFP_KERNEL);
@@ -5395,6 +5484,45 @@ again:
                break;
        }
 
+       if (hp_bseries_system(codec->subsystem_id)) {
+               pin_cfg = snd_hda_codec_get_pincfg(codec, 0x0f);
+               if (get_defcfg_device(pin_cfg) == AC_JACK_LINE_OUT ||
+                       get_defcfg_device(pin_cfg) == AC_JACK_SPEAKER  ||
+                       get_defcfg_device(pin_cfg) == AC_JACK_HP_OUT) {
+                       /* It was changed in the BIOS to just satisfy MS DTM.
+                        * Lets turn it back into slaved HP
+                        */
+                       pin_cfg = (pin_cfg & (~AC_DEFCFG_DEVICE))
+                                       | (AC_JACK_HP_OUT <<
+                                               AC_DEFCFG_DEVICE_SHIFT);
+                       pin_cfg = (pin_cfg & (~(AC_DEFCFG_DEF_ASSOC
+                                                       | AC_DEFCFG_SEQUENCE)))
+                                                               | 0x1f;
+                       snd_hda_codec_set_pincfg(codec, 0x0f, pin_cfg);
+               }
+       }
+
+       if ((codec->subsystem_id >> 16) == PCI_VENDOR_ID_HP) {
+               const struct dmi_device *dev = NULL;
+               while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING,
+                                             NULL, dev))) {
+                       if (strcmp(dev->name, "HP_Mute_LED_1")) {
+                               switch (codec->vendor_id) {
+                               case 0x111d7608:
+                                       spec->gpio_led = 0x01;
+                                       break;
+                               case 0x111d7600:
+                               case 0x111d7601:
+                               case 0x111d7602:
+                               case 0x111d7603:
+                                       spec->gpio_led = 0x08;
+                                       break;
+                               }
+                               break;
+                       }
+               }
+       }
+
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        if (spec->gpio_led) {
                spec->gpio_mask |= spec->gpio_led;
@@ -5604,10 +5732,14 @@ static int patch_stac927x(struct hda_codec *codec)
                spec->dmic_nids = stac927x_dmic_nids;
                spec->num_dmics = STAC927X_NUM_DMICS;
 
-               spec->init = d965_core_init;
+               spec->init = dell_3st_core_init;
                spec->dmux_nids = stac927x_dmux_nids;
                spec->num_dmuxes = ARRAY_SIZE(stac927x_dmux_nids);
                break;
+       case STAC_927X_VOLKNOB:
+               spec->num_dmics = 0;
+               spec->init = stac927x_volknob_core_init;
+               break;
        default:
                spec->num_dmics = 0;
                spec->init = stac927x_core_init;