diff options
author | Ingo Molnar <mingo@kernel.org> | 2012-04-14 13:18:27 +0200 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2012-04-14 13:19:04 +0200 |
commit | 6ac1ef482d7ae0c690f1640bf6eb818ff9a2d91e (patch) | |
tree | 021cc9f6b477146fcebe6f3be4752abfa2ba18a9 /sound/pci/hda/patch_conexant.c | |
parent | 682968e0c425c60f0dde37977e5beb2b12ddc4cc (diff) | |
parent | a385ec4f11bdcf81af094c03e2444ee9b7fad2e5 (diff) |
Merge branch 'perf/core' into perf/uprobes
Merge in latest upstream (and the latest perf development tree),
to prepare for tooling changes, and also to pick up v3.4 MM
changes that the uprobes code needs to take care of.
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'sound/pci/hda/patch_conexant.c')
-rw-r--r-- | sound/pci/hda/patch_conexant.c | 124 |
1 files changed, 103 insertions, 21 deletions
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index d29d6d37790..8c6523bbc79 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -70,6 +70,8 @@ struct conexant_spec { const struct snd_kcontrol_new *mixers[5]; int num_mixers; hda_nid_t vmaster_nid; + struct hda_vmaster_mute_hook vmaster_mute; + bool vmaster_mute_led; const struct hda_verb *init_verbs[5]; /* initialization verbs * don't forget NULL @@ -465,21 +467,8 @@ static const struct snd_kcontrol_new cxt_beep_mixer[] = { }; #endif -static const char * const slave_vols[] = { - "Headphone Playback Volume", - "Speaker Playback Volume", - "Front Playback Volume", - "Surround Playback Volume", - "CLFE Playback Volume", - NULL -}; - -static const char * const slave_sws[] = { - "Headphone Playback Switch", - "Speaker Playback Switch", - "Front Playback Switch", - "Surround Playback Switch", - "CLFE Playback Switch", +static const char * const slave_pfxs[] = { + "Headphone", "Speaker", "Front", "Surround", "CLFE", NULL }; @@ -519,14 +508,17 @@ static int conexant_build_controls(struct hda_codec *codec) 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); + vmaster_tlv, slave_pfxs, + "Playback Volume"); 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); + err = __snd_hda_add_vmaster(codec, "Master Playback Switch", + NULL, slave_pfxs, + "Playback Switch", true, + &spec->vmaster_mute.sw_kctl); if (err < 0) return err; } @@ -1643,7 +1635,7 @@ static void cxt5051_update_speaker(struct hda_codec *codec) pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0; snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl); - /* on ideapad there is an aditional speaker (subwoofer) to mute */ + /* on ideapad there is an additional speaker (subwoofer) to mute */ if (spec->ideapad) snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, @@ -3034,7 +3026,6 @@ static const struct snd_pci_quirk cxt5066_cfg_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo U350", CXT5066_ASUS), SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo G560", CXT5066_ASUS), SND_PCI_QUIRK(0x17aa, 0x3938, "Lenovo G565", CXT5066_AUTO), - SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", CXT5066_IDEAPAD), /* Fallback for Lenovos without dock mic */ SND_PCI_QUIRK(0x1b0a, 0x2092, "CyberpowerPC Gamer Xplorer N57001", CXT5066_AUTO), {} }; @@ -3943,6 +3934,63 @@ static void enable_unsol_pins(struct hda_codec *codec, int num_pins, snd_hda_jack_detect_enable(codec, pins[i], action); } +static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) +{ + int i; + for (i = 0; i < nums; i++) + if (list[i] == nid) + return true; + return false; +} + +/* is the given NID found in any of autocfg items? */ +static bool found_in_autocfg(struct auto_pin_cfg *cfg, hda_nid_t nid) +{ + int i; + + if (found_in_nid_list(nid, cfg->line_out_pins, cfg->line_outs) || + found_in_nid_list(nid, cfg->hp_pins, cfg->hp_outs) || + found_in_nid_list(nid, cfg->speaker_pins, cfg->speaker_outs) || + found_in_nid_list(nid, cfg->dig_out_pins, cfg->dig_outs)) + return true; + for (i = 0; i < cfg->num_inputs; i++) + if (cfg->inputs[i].pin == nid) + return true; + if (cfg->dig_in_pin == nid) + return true; + return false; +} + +/* clear unsol-event tags on unused pins; Conexant codecs seem to leave + * invalid unsol tags by some reason + */ +static void clear_unsol_on_unused_pins(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + + for (i = 0; i < codec->init_pins.used; i++) { + struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i); + if (!found_in_autocfg(cfg, pin->nid)) + snd_hda_codec_write(codec, pin->nid, 0, + AC_VERB_SET_UNSOLICITED_ENABLE, 0); + } +} + +/* turn on/off EAPD according to Master switch */ +static void cx_auto_vmaster_hook(void *private_data, int enabled) +{ + struct hda_codec *codec = private_data; + struct conexant_spec *spec = codec->spec; + + if (enabled && spec->pin_eapd_ctrls) { + cx_auto_update_speakers(codec); + return; + } + cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, enabled); +} + static void cx_auto_init_output(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; @@ -3983,6 +4031,7 @@ static void cx_auto_init_output(struct hda_codec *codec) /* turn on all EAPDs if no individual EAPD control is available */ if (!spec->pin_eapd_ctrls) cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true); + clear_unsol_on_unused_pins(codec); } static void cx_auto_init_input(struct hda_codec *codec) @@ -4046,11 +4095,13 @@ static void cx_auto_init_digital(struct hda_codec *codec) static int cx_auto_init(struct hda_codec *codec) { + struct conexant_spec *spec = codec->spec; /*snd_hda_sequence_write(codec, cx_auto_init_verbs);*/ cx_auto_init_output(codec); cx_auto_init_input(codec); cx_auto_init_digital(codec); snd_hda_jack_report_sync(codec); + snd_hda_sync_vmaster_hook(&spec->vmaster_mute); return 0; } @@ -4296,6 +4347,13 @@ static int cx_auto_build_controls(struct hda_codec *codec) err = snd_hda_jack_add_kctls(codec, &spec->autocfg); if (err < 0) return err; + if (spec->vmaster_mute.sw_kctl) { + spec->vmaster_mute.hook = cx_auto_vmaster_hook; + err = snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, + spec->vmaster_mute_led); + if (err < 0) + return err; + } return 0; } @@ -4320,7 +4378,6 @@ static int cx_auto_search_adcs(struct hda_codec *codec) return 0; } - static const struct hda_codec_ops cx_auto_patch_ops = { .build_controls = cx_auto_build_controls, .build_pcms = conexant_build_pcms, @@ -4368,6 +4425,7 @@ static const struct cxt_pincfg cxt_pincfg_lenovo_x200[] = { { 0x16, 0x042140ff }, /* HP (seq# overridden) */ { 0x17, 0x21a11000 }, /* dock-mic */ { 0x19, 0x2121103f }, /* dock-HP */ + { 0x1c, 0x21440100 }, /* dock SPDIF out */ {} }; @@ -4421,6 +4479,18 @@ static int patch_conexant_auto(struct hda_codec *codec) apply_pin_fixup(codec, cxt_fixups, cxt_pincfg_tbl); + /* Show mute-led control only on HP laptops + * This is a sort of white-list: on HP laptops, EAPD corresponds + * only to the mute-LED without actualy amp function. Meanwhile, + * others may use EAPD really as an amp switch, so it might be + * not good to expose it blindly. + */ + switch (codec->subsystem_id >> 16) { + case 0x103c: + spec->vmaster_mute_led = 1; + break; + } + err = cx_auto_search_adcs(codec); if (err < 0) return err; @@ -4434,6 +4504,18 @@ static int patch_conexant_auto(struct hda_codec *codec) codec->patch_ops = cx_auto_patch_ops; if (spec->beep_amp) snd_hda_attach_beep_device(codec, spec->beep_amp); + + /* Some laptops with Conexant chips show stalls in S3 resume, + * which falls into the single-cmd mode. + * Better to make reset, then. + */ + if (!codec->bus->sync_write) { + snd_printd("hda_codec: " + "Enable sync_write for stable communication\n"); + codec->bus->sync_write = 1; + codec->bus->allow_bus_reset = 1; + } + return 0; } |