Message ID | 1420642213-21458-1-git-send-email-david.henningsson@canonical.com (mailing list archive) |
---|---|
State | Accepted |
Commit | 33f4acd3b214fee9662cbaf569a4876b8604f152 |
Headers | show |
At Wed, 7 Jan 2015 15:50:13 +0100, David Henningsson wrote: > > On this machine, the mic mute hotkey is connected to GPIO2. We therefore > create an extra input device for this key. > > Also enable LEDs connected to GPIO3 and GPIO4. > > (Note: if this patch is backported to older kernels, where KEY_MIC_MUTE is > not available, change the KEY_MIC_MUTE to KEY_F20, which was previously used > for mic mute keys.) > > BugLink: https://bugs.launchpad.net/bugs/1408295 > Tested-by: Keng-Yu Lin <keng-yu.lin@canonical.com> > Signed-off-by: David Henningsson <david.henningsson@canonical.com> > --- > > Changes since v1 (which was by mistake not posted to alsa-devel) > - fixes according to Takashi's review comments Thanks, applied now. Takashi > > sound/pci/hda/patch_realtek.c | 88 +++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 88 insertions(+) > > diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c > index 65f1f4e..09d2131 100644 > --- a/sound/pci/hda/patch_realtek.c > +++ b/sound/pci/hda/patch_realtek.c > @@ -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), > -- > 1.9.1 >
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 65f1f4e..09d2131 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -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),