|
@@ -29,6 +29,7 @@
|
|
|
#include <linux/pci.h>
|
|
|
#include <linux/dmi.h>
|
|
|
#include <linux/module.h>
|
|
|
+#include <linux/input.h>
|
|
|
#include <sound/core.h>
|
|
|
#include <sound/jack.h>
|
|
|
#include "hda_codec.h"
|
|
@@ -120,6 +121,9 @@ struct alc_spec {
|
|
|
hda_nid_t pll_nid;
|
|
|
unsigned int pll_coef_idx, pll_coef_bit;
|
|
|
unsigned int coef0;
|
|
|
+#if IS_ENABLED(CONFIG_INPUT)
|
|
|
+ struct input_dev *kb_dev;
|
|
|
+#endif
|
|
|
};
|
|
|
|
|
|
/*
|
|
@@ -3472,6 +3476,84 @@ static void alc280_fixup_hp_gpio4(struct hda_codec *codec,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+#if IS_ENABLED(CONFIG_INPUT)
|
|
|
+static void gpio2_mic_hotkey_event(struct hda_codec *codec,
|
|
|
+ struct hda_jack_callback *event)
|
|
|
+{
|
|
|
+ struct alc_spec *spec = codec->spec;
|
|
|
+
|
|
|
+ /* GPIO2 just toggles on a keypress/keyrelease cycle. Therefore
|
|
|
+ send both key on and key off event for every interrupt. */
|
|
|
+ input_report_key(spec->kb_dev, KEY_MICMUTE, 1);
|
|
|
+ input_sync(spec->kb_dev);
|
|
|
+ input_report_key(spec->kb_dev, KEY_MICMUTE, 0);
|
|
|
+ input_sync(spec->kb_dev);
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
+static void alc280_fixup_hp_gpio2_mic_hotkey(struct hda_codec *codec,
|
|
|
+ const struct hda_fixup *fix, int action)
|
|
|
+{
|
|
|
+#if IS_ENABLED(CONFIG_INPUT)
|
|
|
+ /* GPIO1 = set according to SKU external amp
|
|
|
+ GPIO2 = mic mute hotkey
|
|
|
+ GPIO3 = mute LED
|
|
|
+ GPIO4 = mic mute LED */
|
|
|
+ static const struct hda_verb gpio_init[] = {
|
|
|
+ { 0x01, AC_VERB_SET_GPIO_MASK, 0x1e },
|
|
|
+ { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x1a },
|
|
|
+ { 0x01, AC_VERB_SET_GPIO_DATA, 0x02 },
|
|
|
+ {}
|
|
|
+ };
|
|
|
+
|
|
|
+ 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;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ snd_hda_add_verbs(codec, gpio_init);
|
|
|
+ snd_hda_codec_write_cache(codec, codec->afg, 0,
|
|
|
+ AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x04);
|
|
|
+ snd_hda_jack_detect_enable_callback(codec, codec->afg,
|
|
|
+ gpio2_mic_hotkey_event);
|
|
|
+
|
|
|
+ spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook;
|
|
|
+ spec->gen.cap_sync_hook = alc_fixup_gpio_mic_mute_hook;
|
|
|
+ spec->gpio_led = 0;
|
|
|
+ spec->mute_led_polarity = 0;
|
|
|
+ spec->gpio_mute_led_mask = 0x08;
|
|
|
+ spec->gpio_mic_led_mask = 0x10;
|
|
|
+ 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);
|
|
|
+ input_free_device(spec->kb_dev);
|
|
|
+ spec->kb_dev = NULL;
|
|
|
+ }
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
static void alc269_fixup_hp_line1_mic1_led(struct hda_codec *codec,
|
|
|
const struct hda_fixup *fix, int action)
|
|
|
{
|
|
@@ -4341,6 +4423,7 @@ enum {
|
|
|
ALC282_FIXUP_ASPIRE_V5_PINS,
|
|
|
ALC280_FIXUP_HP_GPIO4,
|
|
|
ALC286_FIXUP_HP_GPIO_LED,
|
|
|
+ ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY,
|
|
|
};
|
|
|
|
|
|
static const struct hda_fixup alc269_fixups[] = {
|
|
@@ -4814,6 +4897,10 @@ static const struct hda_fixup alc269_fixups[] = {
|
|
|
.type = HDA_FIXUP_FUNC,
|
|
|
.v.func = alc286_fixup_hp_gpio_led,
|
|
|
},
|
|
|
+ [ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY] = {
|
|
|
+ .type = HDA_FIXUP_FUNC,
|
|
|
+ .v.func = alc280_fixup_hp_gpio2_mic_hotkey,
|
|
|
+ },
|
|
|
};
|
|
|
|
|
|
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
|
@@ -4843,6 +4930,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
|
|
SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2),
|
|
|
SND_PCI_QUIRK(0x103c, 0x18e6, "HP", ALC269_FIXUP_HP_GPIO_LED),
|
|
|
SND_PCI_QUIRK(0x103c, 0x218b, "HP", ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED),
|
|
|
+ SND_PCI_QUIRK(0x103c, 0x225f, "HP", ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY),
|
|
|
/* ALC282 */
|
|
|
SND_PCI_QUIRK(0x103c, 0x2210, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
|
|
|
SND_PCI_QUIRK(0x103c, 0x2214, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
|