diff options
author | Takashi Iwai <tiwai@suse.de> | 2011-05-17 03:53:31 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2011-05-17 03:57:19 -0400 |
commit | 03697e2acce9b8818cdb5fc0ebd5e5199dea1c32 (patch) | |
tree | 2ec01404917d4a1d55041511a4b809ba4cbc96fb /sound | |
parent | a3a85d3983f7e18c46fba9f92c21d8a713de9791 (diff) |
ALSA: hda - Add automute-mode enum to Conexant auto-parser
Implement the same functionality as Realtek's auto-mute mode control.
Now Conexant auto-parser can also mutes line-out and provide the enum
control for different automute behavior.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/pci/hda/hda_local.h | 5 | ||||
-rw-r--r-- | sound/pci/hda/patch_conexant.c | 259 |
2 files changed, 215 insertions, 49 deletions
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 1ed6ee5a1e70..01a7cf6b5fb1 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h | |||
@@ -493,6 +493,11 @@ u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid); | |||
493 | u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid); | 493 | u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid); |
494 | int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid); | 494 | int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid); |
495 | 495 | ||
496 | static inline bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid) | ||
497 | { | ||
498 | return !!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_PRES_DETECT); | ||
499 | } | ||
500 | |||
496 | /* flags for hda_nid_item */ | 501 | /* flags for hda_nid_item */ |
497 | #define HDA_NID_ITEM_AMP (1<<0) | 502 | #define HDA_NID_ITEM_AMP (1<<0) |
498 | 503 | ||
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 703dda69ad7c..ac595363e0e0 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c | |||
@@ -39,6 +39,7 @@ | |||
39 | 39 | ||
40 | #define CONEXANT_HP_EVENT 0x37 | 40 | #define CONEXANT_HP_EVENT 0x37 |
41 | #define CONEXANT_MIC_EVENT 0x38 | 41 | #define CONEXANT_MIC_EVENT 0x38 |
42 | #define CONEXANT_LINE_EVENT 0x39 | ||
42 | 43 | ||
43 | /* Conexant 5051 specific */ | 44 | /* Conexant 5051 specific */ |
44 | 45 | ||
@@ -81,6 +82,7 @@ struct conexant_spec { | |||
81 | */ | 82 | */ |
82 | unsigned int cur_eapd; | 83 | unsigned int cur_eapd; |
83 | unsigned int hp_present; | 84 | unsigned int hp_present; |
85 | unsigned int line_present; | ||
84 | unsigned int auto_mic; | 86 | unsigned int auto_mic; |
85 | int auto_mic_ext; /* imux_pins[] index for ext mic */ | 87 | int auto_mic_ext; /* imux_pins[] index for ext mic */ |
86 | unsigned int need_dac_fix; | 88 | unsigned int need_dac_fix; |
@@ -123,6 +125,9 @@ struct conexant_spec { | |||
123 | 125 | ||
124 | unsigned int port_d_mode; | 126 | unsigned int port_d_mode; |
125 | unsigned int auto_mute:1; /* used in auto-parser */ | 127 | unsigned int auto_mute:1; /* used in auto-parser */ |
128 | unsigned int detect_line:1; /* Line-out detection enabled */ | ||
129 | unsigned int automute_lines:1; /* automute line-out as well */ | ||
130 | unsigned int automute_hp_lo:1; /* both HP and LO available */ | ||
126 | unsigned int dell_automute:1; | 131 | unsigned int dell_automute:1; |
127 | unsigned int dell_vostro:1; | 132 | unsigned int dell_vostro:1; |
128 | unsigned int ideapad:1; | 133 | unsigned int ideapad:1; |
@@ -3420,48 +3425,193 @@ static void cx_auto_parse_output(struct hda_codec *codec) | |||
3420 | spec->multiout.dac_nids = spec->private_dac_nids; | 3425 | spec->multiout.dac_nids = spec->private_dac_nids; |
3421 | spec->multiout.max_channels = spec->multiout.num_dacs * 2; | 3426 | spec->multiout.max_channels = spec->multiout.num_dacs * 2; |
3422 | 3427 | ||
3423 | if (cfg->hp_outs > 0) | 3428 | for (i = 0; i < cfg->hp_outs; i++) { |
3424 | spec->auto_mute = 1; | 3429 | if (is_jack_detectable(codec, cfg->hp_pins[i])) { |
3430 | spec->auto_mute = 1; | ||
3431 | break; | ||
3432 | } | ||
3433 | } | ||
3434 | if (spec->auto_mute && cfg->line_out_pins[0] && | ||
3435 | cfg->line_out_pins[0] != cfg->hp_pins[0] && | ||
3436 | cfg->line_out_pins[0] != cfg->speaker_pins[0]) { | ||
3437 | for (i = 0; i < cfg->line_outs; i++) { | ||
3438 | if (is_jack_detectable(codec, cfg->line_out_pins[i])) { | ||
3439 | spec->detect_line = 1; | ||
3440 | break; | ||
3441 | } | ||
3442 | } | ||
3443 | spec->automute_lines = spec->detect_line; | ||
3444 | } | ||
3445 | |||
3425 | spec->vmaster_nid = spec->private_dac_nids[0]; | 3446 | spec->vmaster_nid = spec->private_dac_nids[0]; |
3426 | } | 3447 | } |
3427 | 3448 | ||
3428 | static void cx_auto_turn_eapd(struct hda_codec *codec, int num_pins, | 3449 | static void cx_auto_turn_eapd(struct hda_codec *codec, int num_pins, |
3429 | hda_nid_t *pins, bool on); | 3450 | hda_nid_t *pins, bool on); |
3430 | 3451 | ||
3452 | static void do_automute(struct hda_codec *codec, int num_pins, | ||
3453 | hda_nid_t *pins, bool on) | ||
3454 | { | ||
3455 | int i; | ||
3456 | for (i = 0; i < num_pins; i++) | ||
3457 | snd_hda_codec_write(codec, pins[i], 0, | ||
3458 | AC_VERB_SET_PIN_WIDGET_CONTROL, | ||
3459 | on ? PIN_OUT : 0); | ||
3460 | cx_auto_turn_eapd(codec, num_pins, pins, on); | ||
3461 | } | ||
3462 | |||
3463 | static int detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins) | ||
3464 | { | ||
3465 | int i, present = 0; | ||
3466 | |||
3467 | for (i = 0; i < num_pins; i++) { | ||
3468 | hda_nid_t nid = pins[i]; | ||
3469 | if (!nid || !is_jack_detectable(codec, nid)) | ||
3470 | break; | ||
3471 | snd_hda_input_jack_report(codec, nid); | ||
3472 | present |= snd_hda_jack_detect(codec, nid); | ||
3473 | } | ||
3474 | return present; | ||
3475 | } | ||
3476 | |||
3431 | /* auto-mute/unmute speaker and line outs according to headphone jack */ | 3477 | /* auto-mute/unmute speaker and line outs according to headphone jack */ |
3478 | static void cx_auto_update_speakers(struct hda_codec *codec) | ||
3479 | { | ||
3480 | struct conexant_spec *spec = codec->spec; | ||
3481 | struct auto_pin_cfg *cfg = &spec->autocfg; | ||
3482 | int on; | ||
3483 | |||
3484 | if (!spec->auto_mute) | ||
3485 | on = 0; | ||
3486 | else | ||
3487 | on = spec->hp_present | spec->line_present; | ||
3488 | cx_auto_turn_eapd(codec, cfg->hp_outs, cfg->hp_pins, on); | ||
3489 | do_automute(codec, cfg->speaker_outs, cfg->speaker_pins, !on); | ||
3490 | |||
3491 | /* toggle line-out mutes if needed, too */ | ||
3492 | /* if LO is a copy of either HP or Speaker, don't need to handle it */ | ||
3493 | if (cfg->line_out_pins[0] == cfg->hp_pins[0] || | ||
3494 | cfg->line_out_pins[0] == cfg->speaker_pins[0]) | ||
3495 | return; | ||
3496 | if (!spec->automute_lines || !spec->auto_mute) | ||
3497 | on = 0; | ||
3498 | else | ||
3499 | on = spec->hp_present; | ||
3500 | do_automute(codec, cfg->line_outs, cfg->line_out_pins, !on); | ||
3501 | } | ||
3502 | |||
3432 | static void cx_auto_hp_automute(struct hda_codec *codec) | 3503 | static void cx_auto_hp_automute(struct hda_codec *codec) |
3433 | { | 3504 | { |
3434 | struct conexant_spec *spec = codec->spec; | 3505 | struct conexant_spec *spec = codec->spec; |
3435 | struct auto_pin_cfg *cfg = &spec->autocfg; | 3506 | struct auto_pin_cfg *cfg = &spec->autocfg; |
3436 | int i, present; | ||
3437 | 3507 | ||
3438 | if (!spec->auto_mute) | 3508 | if (!spec->auto_mute) |
3439 | return; | 3509 | return; |
3440 | present = 0; | 3510 | spec->hp_present = detect_jacks(codec, cfg->hp_outs, cfg->hp_pins); |
3441 | for (i = 0; i < cfg->hp_outs; i++) { | 3511 | cx_auto_update_speakers(codec); |
3442 | if (snd_hda_jack_detect(codec, cfg->hp_pins[i])) { | 3512 | } |
3443 | present = 1; | 3513 | |
3444 | break; | 3514 | static void cx_auto_line_automute(struct hda_codec *codec) |
3445 | } | 3515 | { |
3446 | } | 3516 | struct conexant_spec *spec = codec->spec; |
3447 | cx_auto_turn_eapd(codec, cfg->hp_outs, cfg->hp_pins, present); | 3517 | struct auto_pin_cfg *cfg = &spec->autocfg; |
3448 | for (i = 0; i < cfg->line_outs; i++) { | 3518 | |
3449 | snd_hda_codec_write(codec, cfg->line_out_pins[i], 0, | 3519 | if (!spec->auto_mute || !spec->detect_line) |
3450 | AC_VERB_SET_PIN_WIDGET_CONTROL, | 3520 | return; |
3451 | present ? 0 : PIN_OUT); | 3521 | spec->line_present = detect_jacks(codec, cfg->line_outs, |
3522 | cfg->line_out_pins); | ||
3523 | cx_auto_update_speakers(codec); | ||
3524 | } | ||
3525 | |||
3526 | static int cx_automute_mode_info(struct snd_kcontrol *kcontrol, | ||
3527 | struct snd_ctl_elem_info *uinfo) | ||
3528 | { | ||
3529 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | ||
3530 | struct conexant_spec *spec = codec->spec; | ||
3531 | static const char * const texts2[] = { | ||
3532 | "Disabled", "Enabled" | ||
3533 | }; | ||
3534 | static const char * const texts3[] = { | ||
3535 | "Disabled", "Speaker Only", "Line-Out+Speaker" | ||
3536 | }; | ||
3537 | const char * const *texts; | ||
3538 | |||
3539 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
3540 | uinfo->count = 1; | ||
3541 | if (spec->automute_hp_lo) { | ||
3542 | uinfo->value.enumerated.items = 3; | ||
3543 | texts = texts3; | ||
3544 | } else { | ||
3545 | uinfo->value.enumerated.items = 2; | ||
3546 | texts = texts2; | ||
3452 | } | 3547 | } |
3453 | cx_auto_turn_eapd(codec, cfg->line_outs, cfg->line_out_pins, !present); | 3548 | if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) |
3454 | for (i = 0; !present && i < cfg->line_outs; i++) | 3549 | uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; |
3455 | if (snd_hda_jack_detect(codec, cfg->line_out_pins[i])) | 3550 | strcpy(uinfo->value.enumerated.name, |
3456 | present = 1; | 3551 | texts[uinfo->value.enumerated.item]); |
3457 | for (i = 0; i < cfg->speaker_outs; i++) { | 3552 | return 0; |
3458 | snd_hda_codec_write(codec, cfg->speaker_pins[i], 0, | 3553 | } |
3459 | AC_VERB_SET_PIN_WIDGET_CONTROL, | 3554 | |
3460 | present ? 0 : PIN_OUT); | 3555 | static int cx_automute_mode_get(struct snd_kcontrol *kcontrol, |
3556 | struct snd_ctl_elem_value *ucontrol) | ||
3557 | { | ||
3558 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | ||
3559 | struct conexant_spec *spec = codec->spec; | ||
3560 | unsigned int val; | ||
3561 | if (!spec->auto_mute) | ||
3562 | val = 0; | ||
3563 | else if (!spec->automute_lines) | ||
3564 | val = 1; | ||
3565 | else | ||
3566 | val = 2; | ||
3567 | ucontrol->value.enumerated.item[0] = val; | ||
3568 | return 0; | ||
3569 | } | ||
3570 | |||
3571 | static int cx_automute_mode_put(struct snd_kcontrol *kcontrol, | ||
3572 | struct snd_ctl_elem_value *ucontrol) | ||
3573 | { | ||
3574 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | ||
3575 | struct conexant_spec *spec = codec->spec; | ||
3576 | |||
3577 | switch (ucontrol->value.enumerated.item[0]) { | ||
3578 | case 0: | ||
3579 | if (!spec->auto_mute) | ||
3580 | return 0; | ||
3581 | spec->auto_mute = 0; | ||
3582 | break; | ||
3583 | case 1: | ||
3584 | if (spec->auto_mute && !spec->automute_lines) | ||
3585 | return 0; | ||
3586 | spec->auto_mute = 1; | ||
3587 | spec->automute_lines = 0; | ||
3588 | break; | ||
3589 | case 2: | ||
3590 | if (!spec->automute_hp_lo) | ||
3591 | return -EINVAL; | ||
3592 | if (spec->auto_mute && spec->automute_lines) | ||
3593 | return 0; | ||
3594 | spec->auto_mute = 1; | ||
3595 | spec->automute_lines = 1; | ||
3596 | break; | ||
3597 | default: | ||
3598 | return -EINVAL; | ||
3461 | } | 3599 | } |
3462 | cx_auto_turn_eapd(codec, cfg->speaker_outs, cfg->speaker_pins, !present); | 3600 | cx_auto_update_speakers(codec); |
3601 | return 1; | ||
3463 | } | 3602 | } |
3464 | 3603 | ||
3604 | static const struct snd_kcontrol_new cx_automute_mode_enum[] = { | ||
3605 | { | ||
3606 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
3607 | .name = "Auto-Mute Mode", | ||
3608 | .info = cx_automute_mode_info, | ||
3609 | .get = cx_automute_mode_get, | ||
3610 | .put = cx_automute_mode_put, | ||
3611 | }, | ||
3612 | { } | ||
3613 | }; | ||
3614 | |||
3465 | static int cx_auto_mux_enum_info(struct snd_kcontrol *kcontrol, | 3615 | static int cx_auto_mux_enum_info(struct snd_kcontrol *kcontrol, |
3466 | struct snd_ctl_elem_info *uinfo) | 3616 | struct snd_ctl_elem_info *uinfo) |
3467 | { | 3617 | { |
@@ -3607,7 +3757,9 @@ static void cx_auto_unsol_event(struct hda_codec *codec, unsigned int res) | |||
3607 | switch (res >> 26) { | 3757 | switch (res >> 26) { |
3608 | case CONEXANT_HP_EVENT: | 3758 | case CONEXANT_HP_EVENT: |
3609 | cx_auto_hp_automute(codec); | 3759 | cx_auto_hp_automute(codec); |
3610 | snd_hda_input_jack_report(codec, nid); | 3760 | break; |
3761 | case CONEXANT_LINE_EVENT: | ||
3762 | cx_auto_line_automute(codec); | ||
3611 | break; | 3763 | break; |
3612 | case CONEXANT_MIC_EVENT: | 3764 | case CONEXANT_MIC_EVENT: |
3613 | cx_auto_automic(codec); | 3765 | cx_auto_automic(codec); |
@@ -3630,7 +3782,7 @@ static int is_ext_mic(struct hda_codec *codec, hda_nid_t pin) | |||
3630 | unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin); | 3782 | unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin); |
3631 | return get_defcfg_device(def_conf) == AC_JACK_MIC_IN && | 3783 | return get_defcfg_device(def_conf) == AC_JACK_MIC_IN && |
3632 | snd_hda_get_input_pin_attr(def_conf) >= INPUT_PIN_ATTR_NORMAL && | 3784 | snd_hda_get_input_pin_attr(def_conf) >= INPUT_PIN_ATTR_NORMAL && |
3633 | (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_PRES_DETECT); | 3785 | is_jack_detectable(codec, pin); |
3634 | } | 3786 | } |
3635 | 3787 | ||
3636 | /* check whether the pin config is suitable for auto-mic switching; | 3788 | /* check whether the pin config is suitable for auto-mic switching; |
@@ -3794,6 +3946,16 @@ static void mute_outputs(struct hda_codec *codec, int num_nids, | |||
3794 | } | 3946 | } |
3795 | } | 3947 | } |
3796 | 3948 | ||
3949 | static void enable_unsol_pins(struct hda_codec *codec, int num_pins, | ||
3950 | hda_nid_t *pins, unsigned int tag) | ||
3951 | { | ||
3952 | int i; | ||
3953 | for (i = 0; i < num_pins; i++) | ||
3954 | snd_hda_codec_write(codec, pins[i], 0, | ||
3955 | AC_VERB_SET_UNSOLICITED_ENABLE, | ||
3956 | AC_USRSP_EN | tag); | ||
3957 | } | ||
3958 | |||
3797 | static void cx_auto_init_output(struct hda_codec *codec) | 3959 | static void cx_auto_init_output(struct hda_codec *codec) |
3798 | { | 3960 | { |
3799 | struct conexant_spec *spec = codec->spec; | 3961 | struct conexant_spec *spec = codec->spec; |
@@ -3808,35 +3970,27 @@ static void cx_auto_init_output(struct hda_codec *codec) | |||
3808 | mute_outputs(codec, cfg->hp_outs, cfg->hp_pins); | 3970 | mute_outputs(codec, cfg->hp_outs, cfg->hp_pins); |
3809 | mute_outputs(codec, cfg->line_outs, cfg->line_out_pins); | 3971 | mute_outputs(codec, cfg->line_outs, cfg->line_out_pins); |
3810 | mute_outputs(codec, cfg->speaker_outs, cfg->speaker_pins); | 3972 | mute_outputs(codec, cfg->speaker_outs, cfg->speaker_pins); |
3811 | if (spec->auto_mute) { | ||
3812 | for (i = 0; i < cfg->hp_outs; i++) { | ||
3813 | snd_hda_codec_write(codec, cfg->hp_pins[i], 0, | ||
3814 | AC_VERB_SET_UNSOLICITED_ENABLE, | ||
3815 | AC_USRSP_EN | CONEXANT_HP_EVENT); | ||
3816 | } | ||
3817 | cx_auto_hp_automute(codec); | ||
3818 | } else { | ||
3819 | for (i = 0; i < cfg->line_outs; i++) | ||
3820 | snd_hda_codec_write(codec, cfg->line_out_pins[i], 0, | ||
3821 | AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); | ||
3822 | for (i = 0; i < cfg->speaker_outs; i++) | ||
3823 | snd_hda_codec_write(codec, cfg->speaker_pins[i], 0, | ||
3824 | AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); | ||
3825 | /* turn on EAPD */ | ||
3826 | cx_auto_turn_eapd(codec, cfg->line_outs, cfg->line_out_pins, | ||
3827 | true); | ||
3828 | cx_auto_turn_eapd(codec, cfg->hp_outs, cfg->hp_pins, | ||
3829 | true); | ||
3830 | cx_auto_turn_eapd(codec, cfg->speaker_outs, cfg->speaker_pins, | ||
3831 | true); | ||
3832 | } | ||
3833 | |||
3834 | for (i = 0; i < spec->dac_info_filled; i++) { | 3973 | for (i = 0; i < spec->dac_info_filled; i++) { |
3835 | nid = spec->dac_info[i].dac; | 3974 | nid = spec->dac_info[i].dac; |
3836 | if (!nid) | 3975 | if (!nid) |
3837 | nid = spec->multiout.dac_nids[0]; | 3976 | nid = spec->multiout.dac_nids[0]; |
3838 | select_connection(codec, spec->dac_info[i].pin, nid); | 3977 | select_connection(codec, spec->dac_info[i].pin, nid); |
3839 | } | 3978 | } |
3979 | if (spec->auto_mute) { | ||
3980 | enable_unsol_pins(codec, cfg->hp_outs, cfg->hp_pins, | ||
3981 | CONEXANT_HP_EVENT); | ||
3982 | spec->hp_present = detect_jacks(codec, cfg->hp_outs, | ||
3983 | cfg->hp_pins); | ||
3984 | if (spec->detect_line) { | ||
3985 | enable_unsol_pins(codec, cfg->line_outs, | ||
3986 | cfg->line_out_pins, | ||
3987 | CONEXANT_LINE_EVENT); | ||
3988 | spec->line_present = | ||
3989 | detect_jacks(codec, cfg->line_outs, | ||
3990 | cfg->line_out_pins); | ||
3991 | } | ||
3992 | } | ||
3993 | cx_auto_update_speakers(codec); | ||
3840 | } | 3994 | } |
3841 | 3995 | ||
3842 | static void cx_auto_init_input(struct hda_codec *codec) | 3996 | static void cx_auto_init_input(struct hda_codec *codec) |
@@ -3992,6 +4146,13 @@ static int cx_auto_build_output_controls(struct hda_codec *codec) | |||
3992 | if (err < 0) | 4146 | if (err < 0) |
3993 | return err; | 4147 | return err; |
3994 | } | 4148 | } |
4149 | |||
4150 | if (spec->auto_mute) { | ||
4151 | err = snd_hda_add_new_ctls(codec, cx_automute_mode_enum); | ||
4152 | if (err < 0) | ||
4153 | return err; | ||
4154 | } | ||
4155 | |||
3995 | return 0; | 4156 | return 0; |
3996 | } | 4157 | } |
3997 | 4158 | ||