|
29 | 29 | #include <linux/pci.h>
|
30 | 30 | #include <linux/dmi.h>
|
31 | 31 | #include <linux/module.h>
|
| 32 | +#include <linux/input.h> |
32 | 33 | #include <sound/core.h>
|
33 | 34 | #include <sound/jack.h>
|
34 | 35 | #include "hda_codec.h"
|
@@ -120,6 +121,9 @@ struct alc_spec {
|
120 | 121 | hda_nid_t pll_nid;
|
121 | 122 | unsigned int pll_coef_idx, pll_coef_bit;
|
122 | 123 | unsigned int coef0;
|
| 124 | +#if IS_ENABLED(CONFIG_INPUT) |
| 125 | + struct input_dev *kb_dev; |
| 126 | +#endif |
123 | 127 | };
|
124 | 128 |
|
125 | 129 | /*
|
@@ -3472,6 +3476,84 @@ static void alc280_fixup_hp_gpio4(struct hda_codec *codec,
|
3472 | 3476 | }
|
3473 | 3477 | }
|
3474 | 3478 |
|
| 3479 | +#if IS_ENABLED(CONFIG_INPUT) |
| 3480 | +static void gpio2_mic_hotkey_event(struct hda_codec *codec, |
| 3481 | + struct hda_jack_callback *event) |
| 3482 | +{ |
| 3483 | + struct alc_spec *spec = codec->spec; |
| 3484 | + |
| 3485 | + /* GPIO2 just toggles on a keypress/keyrelease cycle. Therefore |
| 3486 | + send both key on and key off event for every interrupt. */ |
| 3487 | + input_report_key(spec->kb_dev, KEY_MICMUTE, 1); |
| 3488 | + input_sync(spec->kb_dev); |
| 3489 | + input_report_key(spec->kb_dev, KEY_MICMUTE, 0); |
| 3490 | + input_sync(spec->kb_dev); |
| 3491 | +} |
| 3492 | +#endif |
| 3493 | + |
| 3494 | +static void alc280_fixup_hp_gpio2_mic_hotkey(struct hda_codec *codec, |
| 3495 | + const struct hda_fixup *fix, int action) |
| 3496 | +{ |
| 3497 | +#if IS_ENABLED(CONFIG_INPUT) |
| 3498 | + /* GPIO1 = set according to SKU external amp |
| 3499 | + GPIO2 = mic mute hotkey |
| 3500 | + GPIO3 = mute LED |
| 3501 | + GPIO4 = mic mute LED */ |
| 3502 | + static const struct hda_verb gpio_init[] = { |
| 3503 | + { 0x01, AC_VERB_SET_GPIO_MASK, 0x1e }, |
| 3504 | + { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x1a }, |
| 3505 | + { 0x01, AC_VERB_SET_GPIO_DATA, 0x02 }, |
| 3506 | + {} |
| 3507 | + }; |
| 3508 | + |
| 3509 | + struct alc_spec *spec = codec->spec; |
| 3510 | + |
| 3511 | + if (action == HDA_FIXUP_ACT_PRE_PROBE) { |
| 3512 | + spec->kb_dev = input_allocate_device(); |
| 3513 | + if (!spec->kb_dev) { |
| 3514 | + codec_err(codec, "Out of memory (input_allocate_device)\n"); |
| 3515 | + return; |
| 3516 | + } |
| 3517 | + spec->kb_dev->name = "Microphone Mute Button"; |
| 3518 | + spec->kb_dev->evbit[0] = BIT_MASK(EV_KEY); |
| 3519 | + spec->kb_dev->keybit[BIT_WORD(KEY_MICMUTE)] = BIT_MASK(KEY_MICMUTE); |
| 3520 | + if (input_register_device(spec->kb_dev)) { |
| 3521 | + codec_err(codec, "input_register_device failed\n"); |
| 3522 | + input_free_device(spec->kb_dev); |
| 3523 | + spec->kb_dev = NULL; |
| 3524 | + return; |
| 3525 | + } |
| 3526 | + |
| 3527 | + snd_hda_add_verbs(codec, gpio_init); |
| 3528 | + snd_hda_codec_write_cache(codec, codec->afg, 0, |
| 3529 | + AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x04); |
| 3530 | + snd_hda_jack_detect_enable_callback(codec, codec->afg, |
| 3531 | + gpio2_mic_hotkey_event); |
| 3532 | + |
| 3533 | + spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook; |
| 3534 | + spec->gen.cap_sync_hook = alc_fixup_gpio_mic_mute_hook; |
| 3535 | + spec->gpio_led = 0; |
| 3536 | + spec->mute_led_polarity = 0; |
| 3537 | + spec->gpio_mute_led_mask = 0x08; |
| 3538 | + spec->gpio_mic_led_mask = 0x10; |
| 3539 | + return; |
| 3540 | + } |
| 3541 | + |
| 3542 | + if (!spec->kb_dev) |
| 3543 | + return; |
| 3544 | + |
| 3545 | + switch (action) { |
| 3546 | + case HDA_FIXUP_ACT_PROBE: |
| 3547 | + spec->init_amp = ALC_INIT_DEFAULT; |
| 3548 | + break; |
| 3549 | + case HDA_FIXUP_ACT_FREE: |
| 3550 | + input_unregister_device(spec->kb_dev); |
| 3551 | + input_free_device(spec->kb_dev); |
| 3552 | + spec->kb_dev = NULL; |
| 3553 | + } |
| 3554 | +#endif |
| 3555 | +} |
| 3556 | + |
3475 | 3557 | static void alc269_fixup_hp_line1_mic1_led(struct hda_codec *codec,
|
3476 | 3558 | const struct hda_fixup *fix, int action)
|
3477 | 3559 | {
|
@@ -4341,6 +4423,7 @@ enum {
|
4341 | 4423 | ALC282_FIXUP_ASPIRE_V5_PINS,
|
4342 | 4424 | ALC280_FIXUP_HP_GPIO4,
|
4343 | 4425 | ALC286_FIXUP_HP_GPIO_LED,
|
| 4426 | + ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY, |
4344 | 4427 | };
|
4345 | 4428 |
|
4346 | 4429 | static const struct hda_fixup alc269_fixups[] = {
|
@@ -4814,6 +4897,10 @@ static const struct hda_fixup alc269_fixups[] = {
|
4814 | 4897 | .type = HDA_FIXUP_FUNC,
|
4815 | 4898 | .v.func = alc286_fixup_hp_gpio_led,
|
4816 | 4899 | },
|
| 4900 | + [ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY] = { |
| 4901 | + .type = HDA_FIXUP_FUNC, |
| 4902 | + .v.func = alc280_fixup_hp_gpio2_mic_hotkey, |
| 4903 | + }, |
4817 | 4904 | };
|
4818 | 4905 |
|
4819 | 4906 | static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
@@ -4843,6 +4930,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
4843 | 4930 | SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2),
|
4844 | 4931 | SND_PCI_QUIRK(0x103c, 0x18e6, "HP", ALC269_FIXUP_HP_GPIO_LED),
|
4845 | 4932 | SND_PCI_QUIRK(0x103c, 0x218b, "HP", ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED),
|
| 4933 | + SND_PCI_QUIRK(0x103c, 0x225f, "HP", ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY), |
4846 | 4934 | /* ALC282 */
|
4847 | 4935 | SND_PCI_QUIRK(0x103c, 0x2210, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
|
4848 | 4936 | SND_PCI_QUIRK(0x103c, 0x2214, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
|
|
0 commit comments