diff options
Diffstat (limited to 'sound/pci/hda/patch_sigmatel.c')
-rw-r--r-- | sound/pci/hda/patch_sigmatel.c | 275 |
1 files changed, 233 insertions, 42 deletions
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index d2fd8ef6aef8..42f944bb641d 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -100,6 +100,7 @@ enum { STAC_HP_M4, STAC_HP_DV5, STAC_HP_HDX, + STAC_HP_DV4_1222NR, STAC_92HD71BXX_MODELS }; @@ -193,6 +194,7 @@ struct sigmatel_spec { unsigned int gpio_dir; unsigned int gpio_data; unsigned int gpio_mute; + unsigned int gpio_led; /* stream */ unsigned int stream_delay; @@ -634,6 +636,40 @@ static int stac92xx_smux_enum_put(struct snd_kcontrol *kcontrol, return 0; } +static unsigned int stac92xx_vref_set(struct hda_codec *codec, + hda_nid_t nid, unsigned int new_vref) +{ + unsigned int error; + unsigned int pincfg; + pincfg = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + + pincfg &= 0xff; + pincfg &= ~(AC_PINCTL_VREFEN | AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); + pincfg |= new_vref; + + if (new_vref == AC_PINCTL_VREF_HIZ) + pincfg |= AC_PINCTL_OUT_EN; + else + pincfg |= AC_PINCTL_IN_EN; + + error = snd_hda_codec_write_cache(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, pincfg); + if (error < 0) + return error; + else + return 1; +} + +static unsigned int stac92xx_vref_get(struct hda_codec *codec, hda_nid_t nid) +{ + unsigned int vref; + vref = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + vref &= AC_PINCTL_VREFEN; + return vref; +} + static int stac92xx_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); @@ -995,6 +1031,17 @@ static struct hda_verb stac9205_core_init[] = { .private_value = verb_read | (verb_write << 16), \ } +#define DC_BIAS(xname, idx, nid) \ + { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = idx, \ + .info = stac92xx_dc_bias_info, \ + .get = stac92xx_dc_bias_get, \ + .put = stac92xx_dc_bias_put, \ + .private_value = nid, \ + } + static struct snd_kcontrol_new stac9200_mixer[] = { HDA_CODEC_VOLUME("Master Playback Volume", 0xb, 0, HDA_OUTPUT), HDA_CODEC_MUTE("Master Playback Switch", 0xb, 0, HDA_OUTPUT), @@ -1837,6 +1884,7 @@ static unsigned int *stac92hd71bxx_brd_tbl[STAC_92HD71BXX_MODELS] = { [STAC_HP_M4] = NULL, [STAC_HP_DV5] = NULL, [STAC_HP_HDX] = NULL, + [STAC_HP_DV4_1222NR] = NULL, }; static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = { @@ -1848,6 +1896,7 @@ static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = { [STAC_HP_M4] = "hp-m4", [STAC_HP_DV5] = "hp-dv5", [STAC_HP_HDX] = "hp-hdx", + [STAC_HP_DV4_1222NR] = "hp-dv4-1222nr", }; static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = { @@ -1856,6 +1905,8 @@ static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = { "DFI LanParty", STAC_92HD71BXX_REF), SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, "DFI LanParty", STAC_92HD71BXX_REF), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30fb, + "HP dv4-1222nr", STAC_HP_DV4_1222NR), SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3080, "HP", STAC_HP_DV5), SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x30f0, @@ -2545,7 +2596,8 @@ static int stac92xx_build_pcms(struct hda_codec *codec) return 0; } -static unsigned int stac92xx_get_vref(struct hda_codec *codec, hda_nid_t nid) +static unsigned int stac92xx_get_default_vref(struct hda_codec *codec, + hda_nid_t nid) { unsigned int pincap = snd_hda_query_pin_caps(codec, nid); pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT; @@ -2599,15 +2651,108 @@ static int stac92xx_hp_switch_put(struct snd_kcontrol *kcontrol, return 1; } -#define stac92xx_io_switch_info snd_ctl_boolean_mono_info +static int stac92xx_dc_bias_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int i; + static char *texts[] = { + "Mic In", "Line In", "Line Out" + }; + + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + hda_nid_t nid = kcontrol->private_value; + + if (nid == spec->mic_switch || nid == spec->line_switch) + i = 3; + else + i = 2; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->value.enumerated.items = i; + uinfo->count = 1; + if (uinfo->value.enumerated.item >= i) + uinfo->value.enumerated.item = i-1; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + + return 0; +} + +static int stac92xx_dc_bias_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; + unsigned int vref = stac92xx_vref_get(codec, nid); + + if (vref == stac92xx_get_default_vref(codec, nid)) + ucontrol->value.enumerated.item[0] = 0; + else if (vref == AC_PINCTL_VREF_GRD) + ucontrol->value.enumerated.item[0] = 1; + else if (vref == AC_PINCTL_VREF_HIZ) + ucontrol->value.enumerated.item[0] = 2; + + return 0; +} + +static int stac92xx_dc_bias_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned int new_vref = 0; + unsigned int error; + hda_nid_t nid = kcontrol->private_value; + + if (ucontrol->value.enumerated.item[0] == 0) + new_vref = stac92xx_get_default_vref(codec, nid); + else if (ucontrol->value.enumerated.item[0] == 1) + new_vref = AC_PINCTL_VREF_GRD; + else if (ucontrol->value.enumerated.item[0] == 2) + new_vref = AC_PINCTL_VREF_HIZ; + else + return 0; + + if (new_vref != stac92xx_vref_get(codec, nid)) { + error = stac92xx_vref_set(codec, nid, new_vref); + return error; + } + + return 0; +} + +static int stac92xx_io_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *texts[2]; + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + + if (kcontrol->private_value == spec->line_switch) + texts[0] = "Line In"; + else + texts[0] = "Mic In"; + texts[1] = "Line Out"; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->value.enumerated.items = 2; + uinfo->count = 1; + + if (uinfo->value.enumerated.item >= 2) + uinfo->value.enumerated.item = 1; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + + return 0; +} static int stac92xx_io_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct sigmatel_spec *spec = codec->spec; - int io_idx = kcontrol-> private_value & 0xff; + hda_nid_t nid = kcontrol->private_value; + int io_idx = (nid == spec->mic_switch) ? 1 : 0; - ucontrol->value.integer.value[0] = spec->io_switch[io_idx]; + ucontrol->value.enumerated.item[0] = spec->io_switch[io_idx]; return 0; } @@ -2615,9 +2760,9 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct sigmatel_spec *spec = codec->spec; - hda_nid_t nid = kcontrol->private_value >> 8; - int io_idx = kcontrol-> private_value & 0xff; - unsigned short val = !!ucontrol->value.integer.value[0]; + hda_nid_t nid = kcontrol->private_value; + int io_idx = (nid == spec->mic_switch) ? 1 : 0; + unsigned short val = !!ucontrol->value.enumerated.item[0]; spec->io_switch[io_idx] = val; @@ -2626,7 +2771,7 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ else { unsigned int pinctl = AC_PINCTL_IN_EN; if (io_idx) /* set VREF for mic */ - pinctl |= stac92xx_get_vref(codec, nid); + pinctl |= stac92xx_get_default_vref(codec, nid); stac92xx_auto_set_pinctl(codec, nid, pinctl); } @@ -2707,7 +2852,8 @@ enum { STAC_CTL_WIDGET_AMP_VOL, STAC_CTL_WIDGET_HP_SWITCH, STAC_CTL_WIDGET_IO_SWITCH, - STAC_CTL_WIDGET_CLFE_SWITCH + STAC_CTL_WIDGET_CLFE_SWITCH, + STAC_CTL_WIDGET_DC_BIAS }; static struct snd_kcontrol_new stac92xx_control_templates[] = { @@ -2719,6 +2865,7 @@ static struct snd_kcontrol_new stac92xx_control_templates[] = { STAC_CODEC_HP_SWITCH(NULL), STAC_CODEC_IO_SWITCH(NULL, 0), STAC_CODEC_CLFE_SWITCH(NULL, 0), + DC_BIAS(NULL, 0, 0), }; /* add dynamic controls */ @@ -2782,6 +2929,34 @@ static struct snd_kcontrol_new stac_input_src_temp = { .put = stac92xx_mux_enum_put, }; +static inline int stac92xx_add_jack_mode_control(struct hda_codec *codec, + hda_nid_t nid, int idx) +{ + int def_conf = snd_hda_codec_get_pincfg(codec, nid); + int control = 0; + struct sigmatel_spec *spec = codec->spec; + char name[22]; + + if (!((get_defcfg_connect(def_conf)) & AC_JACK_PORT_FIXED)) { + if (stac92xx_get_default_vref(codec, nid) == AC_PINCTL_VREF_GRD + && nid == spec->line_switch) + control = STAC_CTL_WIDGET_IO_SWITCH; + else if (snd_hda_query_pin_caps(codec, nid) + & (AC_PINCAP_VREF_GRD << AC_PINCAP_VREF_SHIFT)) + control = STAC_CTL_WIDGET_DC_BIAS; + else if (nid == spec->mic_switch) + control = STAC_CTL_WIDGET_IO_SWITCH; + } + + if (control) { + strcpy(name, auto_pin_cfg_labels[idx]); + return stac92xx_add_control(codec->spec, control, + strcat(name, " Jack Mode"), nid); + } + + return 0; +} + static int stac92xx_add_input_source(struct sigmatel_spec *spec) { struct snd_kcontrol_new *knew; @@ -3144,7 +3319,9 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { struct sigmatel_spec *spec = codec->spec; + hda_nid_t nid; int err; + int idx; err = create_multi_out_ctls(codec, cfg->line_outs, cfg->line_out_pins, spec->multiout.dac_nids, @@ -3161,20 +3338,13 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, return err; } - if (spec->line_switch) { - err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH, - "Line In as Output Switch", - spec->line_switch << 8); - if (err < 0) - return err; - } - - if (spec->mic_switch) { - err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH, - "Mic as Output Switch", - (spec->mic_switch << 8) | 1); - if (err < 0) - return err; + for (idx = AUTO_PIN_MIC; idx <= AUTO_PIN_FRONT_LINE; idx++) { + nid = cfg->input_pins[idx]; + if (nid) { + err = stac92xx_add_jack_mode_control(codec, nid, idx); + if (err < 0) + return err; + } } return 0; @@ -3639,6 +3809,8 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out err = snd_hda_attach_beep_device(codec, nid); if (err < 0) return err; + /* IDT/STAC codecs have linear beep tone parameter */ + codec->beep->linear_tone = 1; /* if no beep switch is available, make its own one */ caps = query_amp_caps(codec, nid, HDA_OUTPUT); if (codec->beep && @@ -4082,7 +4254,7 @@ static int stac92xx_init(struct hda_codec *codec) unsigned int pinctl, conf; if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC) { /* for mic pins, force to initialize */ - pinctl = stac92xx_get_vref(codec, nid); + pinctl = stac92xx_get_default_vref(codec, nid); pinctl |= AC_PINCTL_IN_EN; stac92xx_auto_set_pinctl(codec, nid, pinctl); } else { @@ -4535,17 +4707,19 @@ static int stac92xx_resume(struct hda_codec *codec) return 0; } - /* - * using power check for controlling mute led of HP HDX notebooks + * using power check for controlling mute led of HP notebooks * check for mute state only on Speakers (nid = 0x10) * * For this feature CONFIG_SND_HDA_POWER_SAVE is needed, otherwise * the LED is NOT working properly ! + * + * Changed name to reflect that it now works for any designated + * model, not just HP HDX. */ #ifdef CONFIG_SND_HDA_POWER_SAVE -static int stac92xx_hp_hdx_check_power_status(struct hda_codec *codec, +static int stac92xx_hp_check_power_status(struct hda_codec *codec, hda_nid_t nid) { struct sigmatel_spec *spec = codec->spec; @@ -4553,9 +4727,9 @@ static int stac92xx_hp_hdx_check_power_status(struct hda_codec *codec, if (nid == 0x10) { if (snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) & HDA_AMP_MUTE) - spec->gpio_data &= ~0x08; /* orange */ + spec->gpio_data &= ~spec->gpio_led; /* orange */ else - spec->gpio_data |= 0x08; /* white */ + spec->gpio_data |= spec->gpio_led; /* white */ stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, @@ -5201,6 +5375,15 @@ again: if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP) snd_hda_sequence_write_cache(codec, unmute_init); + /* Some HP machines seem to have unstable codec communications + * especially with ATI fglrx driver. For recovering from the + * CORB/RIRB stall, allow the BUS reset and keep always sync + */ + if (spec->board_config == STAC_HP_DV5) { + codec->bus->sync_write = 1; + codec->bus->allow_bus_reset = 1; + } + spec->aloopback_ctl = stac92hd71bxx_loopback; spec->aloopback_mask = 0x50; spec->aloopback_shift = 0; @@ -5234,6 +5417,15 @@ again: spec->num_smuxes = 0; spec->num_dmuxes = 1; break; + case STAC_HP_DV4_1222NR: + spec->num_dmics = 1; + /* I don't know if it needs 1 or 2 smuxes - will wait for + * bug reports to fix if needed + */ + spec->num_smuxes = 1; + spec->num_dmuxes = 1; + spec->gpio_led = 0x01; + /* fallthrough */ case STAC_HP_DV5: snd_hda_codec_set_pincfg(codec, 0x0d, 0x90170010); stac92xx_auto_set_pinctl(codec, 0x0d, AC_PINCTL_OUT_EN); @@ -5242,22 +5434,21 @@ again: spec->num_dmics = 1; spec->num_dmuxes = 1; spec->num_smuxes = 1; - /* - * For controlling MUTE LED on HP HDX16/HDX18 notebooks, - * the CONFIG_SND_HDA_POWER_SAVE is needed to be set. - */ -#ifdef CONFIG_SND_HDA_POWER_SAVE /* orange/white mute led on GPIO3, orange=0, white=1 */ - spec->gpio_mask |= 0x08; - spec->gpio_dir |= 0x08; - spec->gpio_data |= 0x08; /* set to white */ + spec->gpio_led = 0x08; + break; + } +#ifdef CONFIG_SND_HDA_POWER_SAVE + if (spec->gpio_led) { + spec->gpio_mask |= spec->gpio_led; + spec->gpio_dir |= spec->gpio_led; + spec->gpio_data |= spec->gpio_led; /* register check_power_status callback. */ codec->patch_ops.check_power_status = - stac92xx_hp_hdx_check_power_status; + stac92xx_hp_check_power_status; + } #endif - break; - }; spec->multiout.dac_nids = spec->dac_nids; if (spec->dinput_mux) @@ -5282,7 +5473,7 @@ again: codec->proc_widget_hook = stac92hd7x_proc_hook; return 0; -}; +} static int patch_stac922x(struct hda_codec *codec) { @@ -5437,7 +5628,7 @@ static int patch_stac927x(struct hda_codec *codec) /* correct the device field to SPDIF out */ snd_hda_codec_set_pincfg(codec, 0x21, 0x01442070); break; - }; + } /* configure the analog microphone on some laptops */ snd_hda_codec_set_pincfg(codec, 0x0c, 0x90a79130); /* correct the front output jack as a hp out */ |