From 08fb0d0ee1b9c7aef79f54a9ae24470621eb6447 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jan 2013 17:33:58 +0100 Subject: [PATCH] ALSA: hda/realtek - Generic mute LED implementation for HP laptops As David Henningsson recently suggested, some HP laptops use an unused mic pin for controlling a mute LED, and this information is provided via DMI string "HP_Mute_LED_X_Y" string. This patch adds the generic support for such cases, as we've already done in patch_sigmatel.c. This is applied generically to all devices with ID 0x103c. But as we don't know whether the device 103c:1586 really contains HP_Mute_LED_X_Y DMI string, still keep the static setup for this device using the mic2 pin 0x19. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 76 ++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 28 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index eb889d2e94ee..fab31d29fa23 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -82,6 +83,10 @@ struct alc_spec { unsigned int inv_dmic_muted:1; /* R-ch of inv d-mic is muted? */ hda_nid_t inv_dmic_pin; + /* mute LED for HP laptops, see alc269_fixup_mic_mute_hook() */ + int mute_led_polarity; + hda_nid_t mute_led_nid; + /* hooks */ void (*init_hook)(struct hda_codec *codec); #ifdef CONFIG_PM @@ -2653,39 +2658,54 @@ static void alc269_fixup_quanta_mute(struct hda_codec *codec, spec->gen.automute_hook = alc269_quanta_automute; } -/* update mute-LED according to the speaker mute state via mic1 VREF pin */ -static void alc269_fixup_mic1_mute_hook(void *private_data, int enabled) +/* update mute-LED according to the speaker mute state via mic VREF pin */ +static void alc269_fixup_mic_mute_hook(void *private_data, int enabled) { struct hda_codec *codec = private_data; - unsigned int pinval = AC_PINCTL_IN_EN + (enabled ? - AC_PINCTL_VREF_HIZ : AC_PINCTL_VREF_80); - snd_hda_set_pin_ctl_cache(codec, 0x18, pinval); + struct alc_spec *spec = codec->spec; + unsigned int pinval; + + if (spec->mute_led_polarity) + enabled = !enabled; + pinval = AC_PINCTL_IN_EN | + (enabled ? AC_PINCTL_VREF_HIZ : AC_PINCTL_VREF_80); + if (spec->mute_led_nid) + snd_hda_set_pin_ctl_cache(codec, spec->mute_led_nid, pinval); } -static void alc269_fixup_mic1_mute(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static void alc269_fixup_hp_mute_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->gen.vmaster_mute.hook = alc269_fixup_mic1_mute_hook; + const struct dmi_device *dev = NULL; + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + + while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) { + int pol, pin; + if (sscanf(dev->name, "HP_Mute_LED_%d_%x", &pol, &pin) != 2) + continue; + if (pin < 0x0a || pin >= 0x10) + break; + spec->mute_led_polarity = pol; + spec->mute_led_nid = pin - 0x0a + 0x18; + spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook; spec->gen.vmaster_mute_enum = 1; + snd_printd("Detected mute LED for %x:%d\n", spec->mute_led_nid, + spec->mute_led_polarity); + break; } } -/* update mute-LED according to the speaker mute state via mic2 VREF pin */ -static void alc269_fixup_mic2_mute_hook(void *private_data, int enabled) -{ - struct hda_codec *codec = private_data; - unsigned int pinval = enabled ? 0x20 : 0x24; - snd_hda_set_pin_ctl_cache(codec, 0x19, pinval); -} - -static void alc269_fixup_mic2_mute(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static void alc269_fixup_hp_mute_led_mic2(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->gen.vmaster_mute.hook = alc269_fixup_mic2_mute_hook; + spec->mute_led_polarity = 0; + spec->mute_led_nid = 0x19; + spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook; spec->gen.vmaster_mute_enum = 1; } } @@ -2721,8 +2741,8 @@ enum { ALC269_FIXUP_DMIC, ALC269VB_FIXUP_AMIC, ALC269VB_FIXUP_DMIC, - ALC269_FIXUP_MIC1_MUTE_LED, - ALC269_FIXUP_MIC2_MUTE_LED, + ALC269_FIXUP_HP_MUTE_LED, + ALC269_FIXUP_HP_MUTE_LED_MIC2, ALC269_FIXUP_INV_DMIC, ALC269_FIXUP_LENOVO_DOCK, ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT, @@ -2850,13 +2870,13 @@ static const struct hda_fixup alc269_fixups[] = { { } }, }, - [ALC269_FIXUP_MIC1_MUTE_LED] = { + [ALC269_FIXUP_HP_MUTE_LED] = { .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_mic1_mute, + .v.func = alc269_fixup_hp_mute_led, }, - [ALC269_FIXUP_MIC2_MUTE_LED] = { + [ALC269_FIXUP_HP_MUTE_LED_MIC2] = { .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_mic2_mute, + .v.func = alc269_fixup_hp_mute_led_mic2, }, [ALC269_FIXUP_INV_DMIC] = { .type = HDA_FIXUP_FUNC, @@ -2897,8 +2917,8 @@ static const struct hda_fixup alc269_fixups[] = { static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x029b, "Acer 1810TZ", ALC269_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x1025, 0x0349, "Acer AOD260", ALC269_FIXUP_INV_DMIC), - SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_MIC2_MUTE_LED), - SND_PCI_QUIRK(0x103c, 0x1972, "HP Pavilion 17", ALC269_FIXUP_MIC1_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2), + SND_PCI_QUIRK_VENDOR(0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_DMIC), SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_DMIC), SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW),