[PATCH] ALSA: HDA: Fix Realtek automute for Headphone to Line Out
If the machine has "Headphone out" and "Line Out", but no "Speaker out", the Realtek autoparser currently creates an Automute kcontrol, but it isn't working. This patch enables automuting of "Line out" when headphones are plugged, provided that the automute kcontrol is set to "enabled".
BugLink: http://bugs.launchpad.net/bugs/851697 Signed-off-by: David Henningsson david.henningsson@canonical.com --- sound/pci/hda/patch_realtek.c | 92 ++++++++++++++++++++++++----------------- 1 files changed, 54 insertions(+), 38 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 70ba45e..8dff8d2 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -167,10 +167,12 @@ struct alc_spec { unsigned int master_mute:1; unsigned int auto_mic:1; unsigned int auto_mic_valid_imux:1; /* valid imux for auto-mic */ - unsigned int automute:1; /* HP automute enabled */ + unsigned int automute:1; /* Speaker automute enabled */ + unsigned int detect_hp:1; /* Headphones detection enabled */ unsigned int detect_line:1; /* Line-out detection enabled */ unsigned int automute_lines:1; /* automute line-out as well */ - unsigned int automute_hp_lo:1; /* both HP and LO available */ + unsigned int automute_speaker_possible:1; /* there are speakers and either LO or HP */ + unsigned int automute_lo_possible:1; /* there are line outs and HP */
/* other flags */ unsigned int no_analog :1; /* digital I/O only */ @@ -556,7 +558,7 @@ static void update_speakers(struct hda_codec *codec) if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0] || spec->autocfg.line_out_pins[0] == spec->autocfg.speaker_pins[0]) return; - if (!spec->automute_lines || !spec->automute) + if (!spec->automute_lines) on = 0; else on = spec->jack_present; @@ -582,7 +584,7 @@ static void alc_hp_automute(struct hda_codec *codec) spec->jack_present = detect_jacks(codec, ARRAY_SIZE(spec->autocfg.hp_pins), spec->autocfg.hp_pins); - if (!spec->automute) + if (!spec->detect_hp || (!spec->automute && !spec->automute_lines)) return; call_update_speakers(codec); } @@ -795,7 +797,7 @@ static int alc_automute_mode_info(struct snd_kcontrol *kcontrol,
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; - if (spec->automute_hp_lo) { + if (spec->automute_speaker_possible && spec->automute_lo_possible) { uinfo->value.enumerated.items = 3; texts = texts3; } else { @@ -814,13 +816,12 @@ static int alc_automute_mode_get(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; - unsigned int val; - if (!spec->automute) - val = 0; - else if (!spec->automute_lines) - val = 1; - else - val = 2; + unsigned int val = 0; + if (spec->automute) + val++; + if (spec->automute_lines) + val++; + ucontrol->value.enumerated.item[0] = val; return 0; } @@ -833,18 +834,29 @@ static int alc_automute_mode_put(struct snd_kcontrol *kcontrol,
switch (ucontrol->value.enumerated.item[0]) { case 0: - if (!spec->automute) + if (!spec->automute && !spec->automute_lines) return 0; spec->automute = 0; + spec->automute_lines = 0; break; case 1: - if (spec->automute && !spec->automute_lines) - return 0; - spec->automute = 1; - spec->automute_lines = 0; + if (spec->automute_speaker_possible) { + if (!spec->automute_lines && spec->automute) + return 0; + spec->automute = 1; + spec->automute_lines = 0; + } + else if (spec->automute_lo_possible) { + if (spec->automute_lines) + return 0; + spec->automute_lines = 1; + } + else + return -EINVAL; + break; case 2: - if (!spec->automute_hp_lo) + if (!spec->automute_lo_possible || !spec->automute_speaker_possible) return -EINVAL; if (spec->automute && spec->automute_lines) return 0; @@ -906,8 +918,6 @@ static void alc_init_auto_hp(struct hda_codec *codec) present++; if (present < 2) /* need two different output types */ return; - if (present == 3) - spec->automute_hp_lo = 1; /* both HP and LO automute */
if (!cfg->speaker_pins[0] && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { @@ -923,6 +933,8 @@ static void alc_init_auto_hp(struct hda_codec *codec) cfg->hp_outs = cfg->line_outs; }
+ spec->automute_mode = ALC_AUTOMUTE_PIN; + for (i = 0; i < cfg->hp_outs; i++) { hda_nid_t nid = cfg->hp_pins[i]; if (!is_jack_detectable(codec, nid)) @@ -932,28 +944,32 @@ static void alc_init_auto_hp(struct hda_codec *codec) snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT); - spec->automute = 1; - spec->automute_mode = ALC_AUTOMUTE_PIN; - } - if (spec->automute && cfg->line_out_pins[0] && - cfg->speaker_pins[0] && - cfg->line_out_pins[0] != cfg->hp_pins[0] && - cfg->line_out_pins[0] != cfg->speaker_pins[0]) { - for (i = 0; i < cfg->line_outs; i++) { - hda_nid_t nid = cfg->line_out_pins[i]; - if (!is_jack_detectable(codec, nid)) - continue; - snd_printdd("realtek: Enable Line-Out auto-muting " - "on NID 0x%x\n", nid); - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | ALC_FRONT_EVENT); - spec->detect_line = 1; + spec->detect_hp = 1; + } + + if (cfg->line_out_type == AUTO_PIN_LINE_OUT && cfg->line_outs) { + if (cfg->speaker_outs) + for (i = 0; i < cfg->line_outs; i++) { + hda_nid_t nid = cfg->line_out_pins[i]; + if (!is_jack_detectable(codec, nid)) + continue; + snd_printdd("realtek: Enable Line-Out " + "auto-muting on NID 0x%x\n", nid); + snd_hda_codec_write_cache(codec, nid, 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | ALC_FRONT_EVENT); + spec->detect_line = 1; } - spec->automute_lines = spec->detect_line; + spec->automute_lo_possible = spec->detect_hp; }
- if (spec->automute) { + spec->automute_speaker_possible = cfg->speaker_outs && + (spec->detect_hp || spec->detect_line); + + spec->automute_lines = spec->automute_lo_possible; + spec->automute = spec->automute_speaker_possible; + + if (spec->automute_speaker_possible || spec->automute_lo_possible) { /* create a control for automute mode */ alc_add_automute_mode_enum(codec); spec->unsol_event = alc_sku_unsol_event;
participants (1)
-
David Henningsson