| From 3694cb2947db50753caf432db067487eafae7b9b Mon Sep 17 00:00:00 2001 |
| From: Kailang <kailang@realtek.com> |
| Date: Mon, 28 Dec 2015 11:35:24 +0800 |
| Subject: ALSA: hda - Add mic mute hotkey quirk for Lenovo ThinkCentre AIO |
| |
| From: Kailang <kailang@realtek.com> |
| |
| commit 3694cb2947db50753caf432db067487eafae7b9b upstream. |
| |
| The Lenovo ThinkCenter AIO uses Line2 (NID 0x1b) to implement the |
| micmute hotkey, here we register an input device and use Line2 unsol |
| event to collect the hotkey pressing or releasing. |
| |
| In the meanwhile, the micmute led is controlled by GPIO2, so we |
| use an existing function alc_fixup_gpio_mic_mute_hook() to control |
| the led. |
| |
| [Hui: And there are two places to register the input device, to make |
| the code simple and clean, move the two same code sections into a |
| function.] |
| |
| Signed-off-by: Kailang <kailang@realtek.com> |
| Signed-off-by: Hui Wang <hui.wang@canonical.com> |
| Signed-off-by: Takashi Iwai <tiwai@suse.de> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| sound/pci/hda/patch_realtek.c | 84 +++++++++++++++++++++++++++++++++++------- |
| 1 file changed, 71 insertions(+), 13 deletions(-) |
| |
| --- a/sound/pci/hda/patch_realtek.c |
| +++ b/sound/pci/hda/patch_realtek.c |
| @@ -3478,6 +3478,29 @@ static void gpio2_mic_hotkey_event(struc |
| input_sync(spec->kb_dev); |
| } |
| |
| +static int alc_register_micmute_input_device(struct hda_codec *codec) |
| +{ |
| + struct alc_spec *spec = codec->spec; |
| + |
| + spec->kb_dev = input_allocate_device(); |
| + if (!spec->kb_dev) { |
| + codec_err(codec, "Out of memory (input_allocate_device)\n"); |
| + return -ENOMEM; |
| + } |
| + spec->kb_dev->name = "Microphone Mute Button"; |
| + spec->kb_dev->evbit[0] = BIT_MASK(EV_KEY); |
| + spec->kb_dev->keybit[BIT_WORD(KEY_MICMUTE)] = BIT_MASK(KEY_MICMUTE); |
| + |
| + if (input_register_device(spec->kb_dev)) { |
| + codec_err(codec, "input_register_device failed\n"); |
| + input_free_device(spec->kb_dev); |
| + spec->kb_dev = NULL; |
| + return -ENOMEM; |
| + } |
| + |
| + return 0; |
| +} |
| + |
| static void alc280_fixup_hp_gpio2_mic_hotkey(struct hda_codec *codec, |
| const struct hda_fixup *fix, int action) |
| { |
| @@ -3495,20 +3518,8 @@ static void alc280_fixup_hp_gpio2_mic_ho |
| struct alc_spec *spec = codec->spec; |
| |
| if (action == HDA_FIXUP_ACT_PRE_PROBE) { |
| - spec->kb_dev = input_allocate_device(); |
| - if (!spec->kb_dev) { |
| - codec_err(codec, "Out of memory (input_allocate_device)\n"); |
| - return; |
| - } |
| - spec->kb_dev->name = "Microphone Mute Button"; |
| - spec->kb_dev->evbit[0] = BIT_MASK(EV_KEY); |
| - spec->kb_dev->keybit[BIT_WORD(KEY_MICMUTE)] = BIT_MASK(KEY_MICMUTE); |
| - if (input_register_device(spec->kb_dev)) { |
| - codec_err(codec, "input_register_device failed\n"); |
| - input_free_device(spec->kb_dev); |
| - spec->kb_dev = NULL; |
| + if (alc_register_micmute_input_device(codec) != 0) |
| return; |
| - } |
| |
| snd_hda_add_verbs(codec, gpio_init); |
| snd_hda_codec_write_cache(codec, codec->core.afg, 0, |
| @@ -3538,6 +3549,47 @@ static void alc280_fixup_hp_gpio2_mic_ho |
| } |
| } |
| |
| +static void alc233_fixup_lenovo_line2_mic_hotkey(struct hda_codec *codec, |
| + const struct hda_fixup *fix, int action) |
| +{ |
| + /* Line2 = mic mute hotkey |
| + GPIO2 = mic mute LED */ |
| + static const struct hda_verb gpio_init[] = { |
| + { 0x01, AC_VERB_SET_GPIO_MASK, 0x04 }, |
| + { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x04 }, |
| + {} |
| + }; |
| + |
| + struct alc_spec *spec = codec->spec; |
| + |
| + if (action == HDA_FIXUP_ACT_PRE_PROBE) { |
| + if (alc_register_micmute_input_device(codec) != 0) |
| + return; |
| + |
| + snd_hda_add_verbs(codec, gpio_init); |
| + snd_hda_jack_detect_enable_callback(codec, 0x1b, |
| + gpio2_mic_hotkey_event); |
| + |
| + spec->gen.cap_sync_hook = alc_fixup_gpio_mic_mute_hook; |
| + spec->gpio_led = 0; |
| + spec->mute_led_polarity = 0; |
| + spec->gpio_mic_led_mask = 0x04; |
| + return; |
| + } |
| + |
| + if (!spec->kb_dev) |
| + return; |
| + |
| + switch (action) { |
| + case HDA_FIXUP_ACT_PROBE: |
| + spec->init_amp = ALC_INIT_DEFAULT; |
| + break; |
| + case HDA_FIXUP_ACT_FREE: |
| + input_unregister_device(spec->kb_dev); |
| + spec->kb_dev = NULL; |
| + } |
| +} |
| + |
| static void alc269_fixup_hp_line1_mic1_led(struct hda_codec *codec, |
| const struct hda_fixup *fix, int action) |
| { |
| @@ -4638,6 +4690,7 @@ enum { |
| ALC275_FIXUP_DELL_XPS, |
| ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE, |
| ALC293_FIXUP_LENOVO_SPK_NOISE, |
| + ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY, |
| }; |
| |
| static const struct hda_fixup alc269_fixups[] = { |
| @@ -5247,6 +5300,10 @@ static const struct hda_fixup alc269_fix |
| .chained = true, |
| .chain_id = ALC269_FIXUP_THINKPAD_ACPI |
| }, |
| + [ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY] = { |
| + .type = HDA_FIXUP_FUNC, |
| + .v.func = alc233_fixup_lenovo_line2_mic_hotkey, |
| + }, |
| }; |
| |
| static const struct snd_pci_quirk alc269_fixup_tbl[] = { |
| @@ -5396,6 +5453,7 @@ static const struct snd_pci_quirk alc269 |
| SND_PCI_QUIRK(0x17aa, 0x2223, "ThinkPad T550", ALC292_FIXUP_TPT440_DOCK), |
| SND_PCI_QUIRK(0x17aa, 0x2226, "ThinkPad X250", ALC292_FIXUP_TPT440_DOCK), |
| SND_PCI_QUIRK(0x17aa, 0x2233, "Thinkpad", ALC293_FIXUP_LENOVO_SPK_NOISE), |
| + SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), |
| SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC), |
| SND_PCI_QUIRK(0x17aa, 0x3978, "IdeaPad Y410P", ALC269_FIXUP_NO_SHUTUP), |
| SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), |