aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci/hda/patch_conexant.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/pci/hda/patch_conexant.c')
-rw-r--r--sound/pci/hda/patch_conexant.c122
1 files changed, 102 insertions, 20 deletions
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index f584f6d8ffcc..8c6523bbc797 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -70,6 +70,8 @@ struct conexant_spec {
70 const struct snd_kcontrol_new *mixers[5]; 70 const struct snd_kcontrol_new *mixers[5];
71 int num_mixers; 71 int num_mixers;
72 hda_nid_t vmaster_nid; 72 hda_nid_t vmaster_nid;
73 struct hda_vmaster_mute_hook vmaster_mute;
74 bool vmaster_mute_led;
73 75
74 const struct hda_verb *init_verbs[5]; /* initialization verbs 76 const struct hda_verb *init_verbs[5]; /* initialization verbs
75 * don't forget NULL 77 * don't forget NULL
@@ -465,21 +467,8 @@ static const struct snd_kcontrol_new cxt_beep_mixer[] = {
465}; 467};
466#endif 468#endif
467 469
468static const char * const slave_vols[] = { 470static const char * const slave_pfxs[] = {
469 "Headphone Playback Volume", 471 "Headphone", "Speaker", "Front", "Surround", "CLFE",
470 "Speaker Playback Volume",
471 "Front Playback Volume",
472 "Surround Playback Volume",
473 "CLFE Playback Volume",
474 NULL
475};
476
477static const char * const slave_sws[] = {
478 "Headphone Playback Switch",
479 "Speaker Playback Switch",
480 "Front Playback Switch",
481 "Surround Playback Switch",
482 "CLFE Playback Switch",
483 NULL 472 NULL
484}; 473};
485 474
@@ -519,14 +508,17 @@ static int conexant_build_controls(struct hda_codec *codec)
519 snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid, 508 snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
520 HDA_OUTPUT, vmaster_tlv); 509 HDA_OUTPUT, vmaster_tlv);
521 err = snd_hda_add_vmaster(codec, "Master Playback Volume", 510 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
522 vmaster_tlv, slave_vols); 511 vmaster_tlv, slave_pfxs,
512 "Playback Volume");
523 if (err < 0) 513 if (err < 0)
524 return err; 514 return err;
525 } 515 }
526 if (spec->vmaster_nid && 516 if (spec->vmaster_nid &&
527 !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { 517 !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
528 err = snd_hda_add_vmaster(codec, "Master Playback Switch", 518 err = __snd_hda_add_vmaster(codec, "Master Playback Switch",
529 NULL, slave_sws); 519 NULL, slave_pfxs,
520 "Playback Switch", true,
521 &spec->vmaster_mute.sw_kctl);
530 if (err < 0) 522 if (err < 0)
531 return err; 523 return err;
532 } 524 }
@@ -3034,7 +3026,6 @@ static const struct snd_pci_quirk cxt5066_cfg_tbl[] = {
3034 SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo U350", CXT5066_ASUS), 3026 SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo U350", CXT5066_ASUS),
3035 SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo G560", CXT5066_ASUS), 3027 SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo G560", CXT5066_ASUS),
3036 SND_PCI_QUIRK(0x17aa, 0x3938, "Lenovo G565", CXT5066_AUTO), 3028 SND_PCI_QUIRK(0x17aa, 0x3938, "Lenovo G565", CXT5066_AUTO),
3037 SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", CXT5066_IDEAPAD), /* Fallback for Lenovos without dock mic */
3038 SND_PCI_QUIRK(0x1b0a, 0x2092, "CyberpowerPC Gamer Xplorer N57001", CXT5066_AUTO), 3029 SND_PCI_QUIRK(0x1b0a, 0x2092, "CyberpowerPC Gamer Xplorer N57001", CXT5066_AUTO),
3039 {} 3030 {}
3040}; 3031};
@@ -3943,6 +3934,63 @@ static void enable_unsol_pins(struct hda_codec *codec, int num_pins,
3943 snd_hda_jack_detect_enable(codec, pins[i], action); 3934 snd_hda_jack_detect_enable(codec, pins[i], action);
3944} 3935}
3945 3936
3937static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
3938{
3939 int i;
3940 for (i = 0; i < nums; i++)
3941 if (list[i] == nid)
3942 return true;
3943 return false;
3944}
3945
3946/* is the given NID found in any of autocfg items? */
3947static bool found_in_autocfg(struct auto_pin_cfg *cfg, hda_nid_t nid)
3948{
3949 int i;
3950
3951 if (found_in_nid_list(nid, cfg->line_out_pins, cfg->line_outs) ||
3952 found_in_nid_list(nid, cfg->hp_pins, cfg->hp_outs) ||
3953 found_in_nid_list(nid, cfg->speaker_pins, cfg->speaker_outs) ||
3954 found_in_nid_list(nid, cfg->dig_out_pins, cfg->dig_outs))
3955 return true;
3956 for (i = 0; i < cfg->num_inputs; i++)
3957 if (cfg->inputs[i].pin == nid)
3958 return true;
3959 if (cfg->dig_in_pin == nid)
3960 return true;
3961 return false;
3962}
3963
3964/* clear unsol-event tags on unused pins; Conexant codecs seem to leave
3965 * invalid unsol tags by some reason
3966 */
3967static void clear_unsol_on_unused_pins(struct hda_codec *codec)
3968{
3969 struct conexant_spec *spec = codec->spec;
3970 struct auto_pin_cfg *cfg = &spec->autocfg;
3971 int i;
3972
3973 for (i = 0; i < codec->init_pins.used; i++) {
3974 struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
3975 if (!found_in_autocfg(cfg, pin->nid))
3976 snd_hda_codec_write(codec, pin->nid, 0,
3977 AC_VERB_SET_UNSOLICITED_ENABLE, 0);
3978 }
3979}
3980
3981/* turn on/off EAPD according to Master switch */
3982static void cx_auto_vmaster_hook(void *private_data, int enabled)
3983{
3984 struct hda_codec *codec = private_data;
3985 struct conexant_spec *spec = codec->spec;
3986
3987 if (enabled && spec->pin_eapd_ctrls) {
3988 cx_auto_update_speakers(codec);
3989 return;
3990 }
3991 cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, enabled);
3992}
3993
3946static void cx_auto_init_output(struct hda_codec *codec) 3994static void cx_auto_init_output(struct hda_codec *codec)
3947{ 3995{
3948 struct conexant_spec *spec = codec->spec; 3996 struct conexant_spec *spec = codec->spec;
@@ -3983,6 +4031,7 @@ static void cx_auto_init_output(struct hda_codec *codec)
3983 /* turn on all EAPDs if no individual EAPD control is available */ 4031 /* turn on all EAPDs if no individual EAPD control is available */
3984 if (!spec->pin_eapd_ctrls) 4032 if (!spec->pin_eapd_ctrls)
3985 cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true); 4033 cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true);
4034 clear_unsol_on_unused_pins(codec);
3986} 4035}
3987 4036
3988static void cx_auto_init_input(struct hda_codec *codec) 4037static void cx_auto_init_input(struct hda_codec *codec)
@@ -4046,11 +4095,13 @@ static void cx_auto_init_digital(struct hda_codec *codec)
4046 4095
4047static int cx_auto_init(struct hda_codec *codec) 4096static int cx_auto_init(struct hda_codec *codec)
4048{ 4097{
4098 struct conexant_spec *spec = codec->spec;
4049 /*snd_hda_sequence_write(codec, cx_auto_init_verbs);*/ 4099 /*snd_hda_sequence_write(codec, cx_auto_init_verbs);*/
4050 cx_auto_init_output(codec); 4100 cx_auto_init_output(codec);
4051 cx_auto_init_input(codec); 4101 cx_auto_init_input(codec);
4052 cx_auto_init_digital(codec); 4102 cx_auto_init_digital(codec);
4053 snd_hda_jack_report_sync(codec); 4103 snd_hda_jack_report_sync(codec);
4104 snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
4054 return 0; 4105 return 0;
4055} 4106}
4056 4107
@@ -4296,6 +4347,13 @@ static int cx_auto_build_controls(struct hda_codec *codec)
4296 err = snd_hda_jack_add_kctls(codec, &spec->autocfg); 4347 err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
4297 if (err < 0) 4348 if (err < 0)
4298 return err; 4349 return err;
4350 if (spec->vmaster_mute.sw_kctl) {
4351 spec->vmaster_mute.hook = cx_auto_vmaster_hook;
4352 err = snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute,
4353 spec->vmaster_mute_led);
4354 if (err < 0)
4355 return err;
4356 }
4299 return 0; 4357 return 0;
4300} 4358}
4301 4359
@@ -4320,7 +4378,6 @@ static int cx_auto_search_adcs(struct hda_codec *codec)
4320 return 0; 4378 return 0;
4321} 4379}
4322 4380
4323
4324static const struct hda_codec_ops cx_auto_patch_ops = { 4381static const struct hda_codec_ops cx_auto_patch_ops = {
4325 .build_controls = cx_auto_build_controls, 4382 .build_controls = cx_auto_build_controls,
4326 .build_pcms = conexant_build_pcms, 4383 .build_pcms = conexant_build_pcms,
@@ -4368,6 +4425,7 @@ static const struct cxt_pincfg cxt_pincfg_lenovo_x200[] = {
4368 { 0x16, 0x042140ff }, /* HP (seq# overridden) */ 4425 { 0x16, 0x042140ff }, /* HP (seq# overridden) */
4369 { 0x17, 0x21a11000 }, /* dock-mic */ 4426 { 0x17, 0x21a11000 }, /* dock-mic */
4370 { 0x19, 0x2121103f }, /* dock-HP */ 4427 { 0x19, 0x2121103f }, /* dock-HP */
4428 { 0x1c, 0x21440100 }, /* dock SPDIF out */
4371 {} 4429 {}
4372}; 4430};
4373 4431
@@ -4421,6 +4479,18 @@ static int patch_conexant_auto(struct hda_codec *codec)
4421 4479
4422 apply_pin_fixup(codec, cxt_fixups, cxt_pincfg_tbl); 4480 apply_pin_fixup(codec, cxt_fixups, cxt_pincfg_tbl);
4423 4481
4482 /* Show mute-led control only on HP laptops
4483 * This is a sort of white-list: on HP laptops, EAPD corresponds
4484 * only to the mute-LED without actualy amp function. Meanwhile,
4485 * others may use EAPD really as an amp switch, so it might be
4486 * not good to expose it blindly.
4487 */
4488 switch (codec->subsystem_id >> 16) {
4489 case 0x103c:
4490 spec->vmaster_mute_led = 1;
4491 break;
4492 }
4493
4424 err = cx_auto_search_adcs(codec); 4494 err = cx_auto_search_adcs(codec);
4425 if (err < 0) 4495 if (err < 0)
4426 return err; 4496 return err;
@@ -4434,6 +4504,18 @@ static int patch_conexant_auto(struct hda_codec *codec)
4434 codec->patch_ops = cx_auto_patch_ops; 4504 codec->patch_ops = cx_auto_patch_ops;
4435 if (spec->beep_amp) 4505 if (spec->beep_amp)
4436 snd_hda_attach_beep_device(codec, spec->beep_amp); 4506 snd_hda_attach_beep_device(codec, spec->beep_amp);
4507
4508 /* Some laptops with Conexant chips show stalls in S3 resume,
4509 * which falls into the single-cmd mode.
4510 * Better to make reset, then.
4511 */
4512 if (!codec->bus->sync_write) {
4513 snd_printd("hda_codec: "
4514 "Enable sync_write for stable communication\n");
4515 codec->bus->sync_write = 1;
4516 codec->bus->allow_bus_reset = 1;
4517 }
4518
4437 return 0; 4519 return 0;
4438} 4520}
4439 4521