[alsa-devel] [PATCH v8 0/3] Huawei laptops
This patch set is based on new audio LED triggers in branch topic/leds-trigger from git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
Changes from v7: * Switch to wmi_driver * Use devm to allocate and register devices * Skip registering LED subsystem if a ACPI controller method was not found
Changes from v6: * Use audio LED triggers patch set * Use KEY_CONFIG (XF86Tools) instead of KEY_PROG1. In Windows, the key is used to launch Huawei PC manager. XF86Tools is used by default on most desktop environments i.e. Gnome.
Changes from v5: * Review tags
Changes from v4: * Consistency in file names * How module would be enabled (Kconfig) * Match license in SPDX and MODULE_LICENSE
Changes from v3: * Code formatting
Changes from v2: * Support for Huawei MBX * Style and formatting issues
Ayman Bagabas (3): ALSA: hda: fix front speakers on Huawei MBXP. x86: add support for Huawei WMI hotkeys. ALSA: hda: add support for Huawei WMI micmute LED
drivers/platform/x86/Kconfig | 16 ++ drivers/platform/x86/Makefile | 1 + drivers/platform/x86/huawei-wmi.c | 238 ++++++++++++++++++++++++++++++ sound/pci/hda/patch_realtek.c | 22 +++ 4 files changed, 277 insertions(+) create mode 100644 drivers/platform/x86/huawei-wmi.c
This patch solves bug 200501 'Only 2 of 4 speakers playing sound.' https://bugzilla.kernel.org/show_bug.cgi?id=200501 It enables the front speakers on Huawei Matebook X Pro laptops. These laptops come with Dolby Atmos sound system and these pins configuration enables the front speakers.
Reviewed-by: Takashi Iwai tiwai@suse.de Signed-off-by: Ayman Bagabas ayman.bagabas@gmail.com --- sound/pci/hda/patch_realtek.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 993d34c141c2..1326f32f4574 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5490,6 +5490,7 @@ enum { ALC298_FIXUP_TPT470_DOCK, ALC255_FIXUP_DUMMY_LINEOUT_VERB, ALC255_FIXUP_DELL_HEADSET_MIC, + ALC256_FIXUP_HUAWEI_MBXP_PINS, ALC295_FIXUP_HP_X360, ALC221_FIXUP_HP_HEADSET_MIC, }; @@ -5761,6 +5762,22 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_HEADSET_MIC }, + [ALC256_FIXUP_HUAWEI_MBXP_PINS] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + {0x12, 0x90a60130}, + {0x13, 0x40000000}, + {0x14, 0x90170110}, + {0x18, 0x411111f0}, + {0x19, 0x04a11040}, + {0x1a, 0x411111f0}, + {0x1b, 0x90170112}, + {0x1d, 0x40759a05}, + {0x1e, 0x411111f0}, + {0x21, 0x04211020}, + { } + } + }, [ALC269_FIXUP_ASUS_X101_FUNC] = { .type = HDA_FIXUP_FUNC, .v.func = alc269_fixup_x101_headset_mic, @@ -6591,6 +6608,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x511f, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K), SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD), + SND_PCI_QUIRK(0x19e5, 0x3204, "Huawei MBXP", ALC256_FIXUP_HUAWEI_MBXP_PINS), SND_PCI_QUIRK(0x1b7d, 0xa831, "Ordissimo EVE2 ", ALC269VB_FIXUP_ORDISSIMO_EVE2), /* Also known as Malata PC-B1303 */
#if 0
This driver adds support for missing hotkeys on some Huawei laptops. Laptops such as the Matebook X have non functioning hotkeys. Whereas newer laptops such as the Matebook X Pro come with working hotkeys out of the box.
Old laptops, such as the Matebook X, report hotkey events through ACPI device "\WMI0". However, new laptops, such as the Matebook X Pro, does not have this WMI device.
All the hotkeys on the Matebook X Pro work fine without this patch except (micmute, wlan, and huawei key). These keys and the brightness keys report events to "\AMW0" ACPI device. One problem is that brightness keys on the Matebook X Pro work without this patch. This results in reporting two brightness key press events one is captured by ACPI and another by this driver.
A solution would be to check if such event came from the "\AMW0" WMI driver then skip reporting event. Another solution would be to leave this to user-space to handle. Which can be achieved by using "hwdb" tables and remap those keys to "unknown". This solution seems more natural to me because it leaves the decision to user-space.
Signed-off-by: Ayman Bagabas ayman.bagabas@gmail.com --- drivers/platform/x86/Kconfig | 16 ++ drivers/platform/x86/Makefile | 1 + drivers/platform/x86/huawei-wmi.c | 238 ++++++++++++++++++++++++++++++ 3 files changed, 255 insertions(+) create mode 100644 drivers/platform/x86/huawei-wmi.c
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 87f70e8f4dd0..27db3ce7a1e1 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -1292,6 +1292,22 @@ config INTEL_ATOMISP2_PM To compile this driver as a module, choose M here: the module will be called intel_atomisp2_pm.
+config HUAWEI_WMI + tristate "Huawei WMI hotkeys driver" + depends on ACPI_WMI + depends on INPUT + select INPUT_SPARSEKMAP + select LEDS_CLASS + select LEDS_TRIGGERS + select LEDS_TRIGGER_AUDIO + help + This driver provides support for Huawei WMI hotkeys. + It enables the missing keys and adds support to the micmute + LED found on some of these laptops. + + To compile this driver as a module, choose M here: the module + will be called huawei-wmi. + endif # X86_PLATFORM_DEVICES
config PMC_ATOM diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 39ae94135406..d841c550e3cc 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_ACERHDF) += acerhdf.o obj-$(CONFIG_HP_ACCEL) += hp_accel.o obj-$(CONFIG_HP_WIRELESS) += hp-wireless.o obj-$(CONFIG_HP_WMI) += hp-wmi.o +obj-$(CONFIG_HUAWEI_WMI) += huawei-wmi.o obj-$(CONFIG_AMILO_RFKILL) += amilo-rfkill.o obj-$(CONFIG_GPD_POCKET_FAN) += gpd-pocket-fan.o obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o diff --git a/drivers/platform/x86/huawei-wmi.c b/drivers/platform/x86/huawei-wmi.c new file mode 100644 index 000000000000..5371c8e2ded7 --- /dev/null +++ b/drivers/platform/x86/huawei-wmi.c @@ -0,0 +1,238 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Huawei WMI hotkeys + * + * Copyright (C) 2018 Ayman Bagabas ayman.bagabas@gmail.com + */ + +#include <linux/acpi.h> +#include <linux/input.h> +#include <linux/input/sparse-keymap.h> +#include <linux/leds.h> +#include <linux/module.h> +#include <linux/wmi.h> + +/* + * Huawei WMI Events GUIDs + */ +#define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100" +#define AMW0_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C90629100000" + +struct huawei_wmi_priv { + struct input_dev *idev; + struct led_classdev cdev; + acpi_handle handle; + char *acpi_method; +}; + +static const struct key_entry huawei_wmi_keymap[] __initconst = { + { KE_KEY, 0x281, { KEY_BRIGHTNESSDOWN } }, + { KE_KEY, 0x282, { KEY_BRIGHTNESSUP } }, + { KE_KEY, 0x284, { KEY_MUTE } }, + { KE_KEY, 0x285, { KEY_VOLUMEDOWN } }, + { KE_KEY, 0x286, { KEY_VOLUMEUP } }, + { KE_KEY, 0x287, { KEY_MICMUTE } }, + { KE_KEY, 0x289, { KEY_WLAN } }, + // Huawei |M| button + { KE_KEY, 0x28a, { KEY_CONFIG } }, + // Keyboard light + { KE_IGNORE, 0x293, { KEY_KBDILLUMTOGGLE } }, + { KE_IGNORE, 0x294, { KEY_KBDILLUMUP } }, + { KE_IGNORE, 0x295, { KEY_KBDILLUMUP } }, + { KE_END, 0 } +}; + +static int huawei_wmi_micmute_led_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct huawei_wmi_priv *priv = dev_get_drvdata(led_cdev->dev->parent); + acpi_status status; + union acpi_object args[3]; + struct acpi_object_list arg_list = { + .pointer = args, + .count = ARRAY_SIZE(args), + }; + + args[0].type = args[1].type = args[2].type = ACPI_TYPE_INTEGER; + args[0].integer.value = 0; + args[1].integer.value = 0x04; + args[2].integer.value = brightness ? 1 : 0; + + if (strcmp(priv->acpi_method, "WPIN") == 0) { + args[0].integer.value = 1; + args[2].integer.value = brightness ? 0 : 1; + } + + status = acpi_evaluate_object(priv->handle, priv->acpi_method, &arg_list, NULL); + if (ACPI_FAILURE(status)) + return -ENXIO; + + return 0; +} + +static int huawei_wmi_leds_setup(struct wmi_device *wdev) +{ + struct huawei_wmi_priv *priv = dev_get_drvdata(&wdev->dev); + acpi_status status; + + // Skip registering LED subsystem if no ACPI method was found. + status = acpi_get_handle(priv->handle, "\_SB.PCI0.LPCB.EC0", &priv->handle); + if (ACPI_FAILURE(status)) + return 0; + + if (acpi_has_method(priv->handle, "SPIN")) + priv->acpi_method = "SPIN"; + else if (acpi_has_method(priv->handle, "WPIN")) + priv->acpi_method = "WPIN"; + else + return 0; + + priv->cdev.name = "platform::micmute"; + priv->cdev.max_brightness = 1; + priv->cdev.brightness_set_blocking = huawei_wmi_micmute_led_set; + priv->cdev.default_trigger = "audio-micmute"; + priv->cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE); + priv->cdev.dev = &wdev->dev; + + return devm_led_classdev_register(&wdev->dev, &priv->cdev); +} + +static void huawei_wmi_process_key(struct wmi_device *wdev, int code) +{ + struct huawei_wmi_priv *priv = dev_get_drvdata(&wdev->dev); + const struct key_entry *key; + + /* + * WMI0 uses code 0x80 to indicate a hotkey event. + * The actual key is fetched from the method WQ00. + */ + if (code == 0x80) { + acpi_status status; + acpi_handle handle; + unsigned long long result; + union acpi_object args[1]; + struct acpi_object_list arg_list = { + .pointer = args, + .count = ARRAY_SIZE(args), + }; + + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = 0; + + status = acpi_get_handle(NULL, "\WMI0", &handle); + if (ACPI_FAILURE(status)) { + dev_err(&wdev->dev, "Unable to get ACPI handle\n"); + return; + } + + status = acpi_evaluate_integer(NULL, "WQ00", &arg_list, &result); + if (ACPI_FAILURE(status)) { + dev_err(&wdev->dev, "Unable to evaluate ACPI method\n"); + return; + } + + code = result; + } + + key = sparse_keymap_entry_from_scancode(priv->idev, code); + if (!key) { + dev_info(&wdev->dev, "Unknown key pressed, code: 0x%04x\n", code); + return; + } + + sparse_keymap_report_entry(priv->idev, key, 1, true); +} + +static void huawei_wmi_notify(struct wmi_device *wdev, + union acpi_object *obj) +{ + if (obj->type == ACPI_TYPE_INTEGER) + huawei_wmi_process_key(wdev, obj->integer.value); + else + dev_info(&wdev->dev, "Bad response type %d\n", obj->type); +} + +static int huawei_wmi_input_setup(struct wmi_device *wdev) +{ + struct huawei_wmi_priv *priv = dev_get_drvdata(&wdev->dev); + int err; + + priv->idev = devm_input_allocate_device(&wdev->dev); + if (!priv->idev) + return -ENOMEM; + + priv->idev->name = "Huawei WMI hotkeys"; + priv->idev->phys = "wmi/input0"; + priv->idev->id.bustype = BUS_HOST; + priv->idev->dev.parent = &wdev->dev; + + err = sparse_keymap_setup(priv->idev, huawei_wmi_keymap, NULL); + if (err) + return err; + + err = input_register_device(priv->idev); + if (err) + return err; + + return 0; +} + +static int huawei_wmi_probe(struct wmi_device *wdev) +{ + struct huawei_wmi_priv *priv; + int err; + + priv = devm_kzalloc(&wdev->dev, sizeof(struct huawei_wmi_priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + dev_set_drvdata(&wdev->dev, priv); + + err = huawei_wmi_input_setup(wdev); + if (err) + return err; + + err = huawei_wmi_leds_setup(wdev); + if (err) + return err; + + return 0; +} + +static const struct wmi_device_id huawei_wmi_id_table[] = { + { .guid_string = WMI0_EVENT_GUID }, + { .guid_string = AMW0_EVENT_GUID }, + { } +}; + +static struct wmi_driver huawei_wmi_driver = { + .driver = { + .name = "huawei-wmi", + }, + .id_table = huawei_wmi_id_table, + .probe = huawei_wmi_probe, + .notify = huawei_wmi_notify, +}; + +static int __init huawei_wmi_init(void) +{ + if (!(wmi_has_guid(WMI0_EVENT_GUID) || wmi_has_guid(AMW0_EVENT_GUID))) { + pr_debug("Compatible WMI GUID not found\n"); + return -ENODEV; + } + + return wmi_driver_register(&huawei_wmi_driver); +} + +static void __exit huawei_wmi_exit(void) +{ + wmi_driver_unregister(&huawei_wmi_driver); +} + +module_init(huawei_wmi_init); +module_exit(huawei_wmi_exit); + +MODULE_ALIAS("wmi:"WMI0_EVENT_GUID); +MODULE_ALIAS("wmi:"AMW0_EVENT_GUID); +MODULE_AUTHOR("Ayman Bagabas ayman.bagabas@gmail.com"); +MODULE_DESCRIPTION("Huawei WMI hotkeys"); +MODULE_LICENSE("GPL v2");
On Fri, 30 Nov 2018 00:57:37 +0100, Ayman Bagabas wrote:
This driver adds support for missing hotkeys on some Huawei laptops. Laptops such as the Matebook X have non functioning hotkeys. Whereas newer laptops such as the Matebook X Pro come with working hotkeys out of the box.
Old laptops, such as the Matebook X, report hotkey events through ACPI device "\WMI0". However, new laptops, such as the Matebook X Pro, does not have this WMI device.
All the hotkeys on the Matebook X Pro work fine without this patch except (micmute, wlan, and huawei key). These keys and the brightness keys report events to "\AMW0" ACPI device. One problem is that brightness keys on the Matebook X Pro work without this patch. This results in reporting two brightness key press events one is captured by ACPI and another by this driver.
A solution would be to check if such event came from the "\AMW0" WMI driver then skip reporting event. Another solution would be to leave this to user-space to handle. Which can be achieved by using "hwdb" tables and remap those keys to "unknown". This solution seems more natural to me because it leaves the decision to user-space.
Signed-off-by: Ayman Bagabas ayman.bagabas@gmail.com
The new patch looks much better than the previous one, thanks for working on it.
Just a few comments:
+struct huawei_wmi_priv {
- struct input_dev *idev;
- struct led_classdev cdev;
- acpi_handle handle;
Is this handle set in anywhere? I couldn't see it in your patch. If it's supposed to be NULL, passing NULL explicitly makes your intention clearer.
+static int huawei_wmi_leds_setup(struct wmi_device *wdev) +{
- struct huawei_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
- acpi_status status;
- // Skip registering LED subsystem if no ACPI method was found.
- status = acpi_get_handle(priv->handle, "\_SB.PCI0.LPCB.EC0", &priv->handle);
- if (ACPI_FAILURE(status))
return 0;
- if (acpi_has_method(priv->handle, "SPIN"))
priv->acpi_method = "SPIN";
- else if (acpi_has_method(priv->handle, "WPIN"))
priv->acpi_method = "WPIN";
- else
return 0;
- priv->cdev.name = "platform::micmute";
- priv->cdev.max_brightness = 1;
- priv->cdev.brightness_set_blocking = huawei_wmi_micmute_led_set;
- priv->cdev.default_trigger = "audio-micmute";
- priv->cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
- priv->cdev.dev = &wdev->dev;
What about suspend/resume? When the driver is bound wit HD-audio, the HD-audio will restore the state at resume, so it would work. But, by providing the LED class device, it is supposed to work even without HD-audio, so it might make sense to pass LED_CORE_SUSPENDRESUME, too.
+static int __init huawei_wmi_init(void) +{
- if (!(wmi_has_guid(WMI0_EVENT_GUID) || wmi_has_guid(AMW0_EVENT_GUID))) {
pr_debug("Compatible WMI GUID not found\n");
return -ENODEV;
- }
This is superfluous when you implement with wmi_driver. In theory, the supported GUID can be added dynamically via sysfs, too.
thanks,
Takashi
On Mon, Dec 3, 2018 at 2:00 PM Takashi Iwai tiwai@suse.de wrote:
On Fri, 30 Nov 2018 00:57:37 +0100, Ayman Bagabas wrote:
+struct huawei_wmi_priv {
struct input_dev *idev;
struct led_classdev cdev;
acpi_handle handle;
Is this handle set in anywhere? I couldn't see it in your patch. If it's supposed to be NULL, passing NULL explicitly makes your intention clearer.
Isn't below an answer?
// Skip registering LED subsystem if no ACPI method was found.
status = acpi_get_handle(priv->handle, "\\_SB.PCI0.LPCB.EC0", &priv->handle);
if (ACPI_FAILURE(status))
return 0;
On Mon, 03 Dec 2018 15:18:54 +0100, Andy Shevchenko wrote:
On Mon, Dec 3, 2018 at 2:00 PM Takashi Iwai tiwai@suse.de wrote:
On Fri, 30 Nov 2018 00:57:37 +0100, Ayman Bagabas wrote:
+struct huawei_wmi_priv {
struct input_dev *idev;
struct led_classdev cdev;
acpi_handle handle;
Is this handle set in anywhere? I couldn't see it in your patch. If it's supposed to be NULL, passing NULL explicitly makes your intention clearer.
Isn't below an answer?
// Skip registering LED subsystem if no ACPI method was found.
status = acpi_get_handle(priv->handle, "\\_SB.PCI0.LPCB.EC0", &priv->handle);
Oh indeed, but why it passes priv->handle at the first argument...?
thanks,
Takashi
On Mon, Dec 3, 2018 at 4:22 PM Takashi Iwai tiwai@suse.de wrote:
On Mon, 03 Dec 2018 15:18:54 +0100, Andy Shevchenko wrote:
On Mon, Dec 3, 2018 at 2:00 PM Takashi Iwai tiwai@suse.de wrote:
On Fri, 30 Nov 2018 00:57:37 +0100, Ayman Bagabas wrote:
+struct huawei_wmi_priv {
struct input_dev *idev;
struct led_classdev cdev;
acpi_handle handle;
Is this handle set in anywhere? I couldn't see it in your patch. If it's supposed to be NULL, passing NULL explicitly makes your intention clearer.
Isn't below an answer?
// Skip registering LED subsystem if no ACPI method was found.
status = acpi_get_handle(priv->handle, "\\_SB.PCI0.LPCB.EC0", &priv->handle);
Oh indeed, but why it passes priv->handle at the first argument...?
Yes, since it's an absolute, I guess NULL in this case will be OK.
On Mon, 2018-12-03 at 13:00 +0100, Takashi Iwai wrote:
On Fri, 30 Nov 2018 00:57:37 +0100, Ayman Bagabas wrote:
This driver adds support for missing hotkeys on some Huawei laptops. Laptops such as the Matebook X have non functioning hotkeys. Whereas newer laptops such as the Matebook X Pro come with working hotkeys out of the box.
Old laptops, such as the Matebook X, report hotkey events through ACPI device "\WMI0". However, new laptops, such as the Matebook X Pro, does not have this WMI device.
All the hotkeys on the Matebook X Pro work fine without this patch except (micmute, wlan, and huawei key). These keys and the brightness keys report events to "\AMW0" ACPI device. One problem is that brightness keys on the Matebook X Pro work without this patch. This results in reporting two brightness key press events one is captured by ACPI and another by this driver.
A solution would be to check if such event came from the "\AMW0" WMI driver then skip reporting event. Another solution would be to leave this to user-space to handle. Which can be achieved by using "hwdb" tables and remap those keys to "unknown". This solution seems more natural to me because it leaves the decision to user-space.
Signed-off-by: Ayman Bagabas ayman.bagabas@gmail.com
The new patch looks much better than the previous one, thanks for working on it.
Just a few comments:
+struct huawei_wmi_priv {
- struct input_dev *idev;
- struct led_classdev cdev;
- acpi_handle handle;
Is this handle set in anywhere? I couldn't see it in your patch. If it's supposed to be NULL, passing NULL explicitly makes your intention clearer.
+static int huawei_wmi_leds_setup(struct wmi_device *wdev) +{
- struct huawei_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
- acpi_status status;
- // Skip registering LED subsystem if no ACPI method was found.
- status = acpi_get_handle(priv->handle, "\_SB.PCI0.LPCB.EC0",
&priv->handle);
- if (ACPI_FAILURE(status))
return 0;
- if (acpi_has_method(priv->handle, "SPIN"))
priv->acpi_method = "SPIN";
- else if (acpi_has_method(priv->handle, "WPIN"))
priv->acpi_method = "WPIN";
- else
return 0;
- priv->cdev.name = "platform::micmute";
- priv->cdev.max_brightness = 1;
- priv->cdev.brightness_set_blocking =
huawei_wmi_micmute_led_set;
- priv->cdev.default_trigger = "audio-micmute";
- priv->cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
- priv->cdev.dev = &wdev->dev;
What about suspend/resume? When the driver is bound wit HD-audio, the HD-audio will restore the state at resume, so it would work. But, by providing the LED class device, it is supposed to work even without HD-audio, so it might make sense to pass LED_CORE_SUSPENDRESUME, too.
Besides that, is there anything needed for wmi_device suspend/resume?
+static int __init huawei_wmi_init(void) +{
- if (!(wmi_has_guid(WMI0_EVENT_GUID) ||
wmi_has_guid(AMW0_EVENT_GUID))) {
pr_debug("Compatible WMI GUID not found\n");
return -ENODEV;
- }
This is superfluous when you implement with wmi_driver. In theory, the supported GUID can be added dynamically via sysfs, too.
I left it that way so it doesn't insert the module if these GUIDs were not found. Should I drop that and use module_wmi_driver instead?
thanks,
Takashi
On Mon, 03 Dec 2018 16:46:01 +0100, ayman.bagabas@gmail.com wrote:
- priv->cdev.name = "platform::micmute";
- priv->cdev.max_brightness = 1;
- priv->cdev.brightness_set_blocking =
huawei_wmi_micmute_led_set;
- priv->cdev.default_trigger = "audio-micmute";
- priv->cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
- priv->cdev.dev = &wdev->dev;
What about suspend/resume? When the driver is bound wit HD-audio, the HD-audio will restore the state at resume, so it would work. But, by providing the LED class device, it is supposed to work even without HD-audio, so it might make sense to pass LED_CORE_SUSPENDRESUME, too.
Besides that, is there anything needed for wmi_device suspend/resume?
AFAIK, the wmi_driver itself doesn't need anything. The input also doesn't need, so most likely only LED.
+static int __init huawei_wmi_init(void) +{
- if (!(wmi_has_guid(WMI0_EVENT_GUID) ||
wmi_has_guid(AMW0_EVENT_GUID))) {
pr_debug("Compatible WMI GUID not found\n");
return -ENODEV;
- }
This is superfluous when you implement with wmi_driver. In theory, the supported GUID can be added dynamically via sysfs, too.
I left it that way so it doesn't insert the module if these GUIDs were not found.
But they aren't loaded on such devices unless you do explicitly. If they are done explicitly, there must be a reason to do so, hence you don't need to block it :)
Should I drop that and use module_wmi_driver instead?
Yes, that's cleaner. Let's try to make it as simple as possible at first.
thanks,
Takashi
On Fri, Nov 30, 2018 at 1:58 AM Ayman Bagabas ayman.bagabas@gmail.com wrote:
+static int huawei_wmi_micmute_led_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
+{
struct huawei_wmi_priv *priv = dev_get_drvdata(led_cdev->dev->parent);
acpi_status status;
union acpi_object args[3];
struct acpi_object_list arg_list = {
.pointer = args,
.count = ARRAY_SIZE(args),
};
args[0].type = args[1].type = args[2].type = ACPI_TYPE_INTEGER;
args[0].integer.value = 0;
args[1].integer.value = 0x04;
args[2].integer.value = brightness ? 1 : 0;
if (strcmp(priv->acpi_method, "WPIN") == 0) {
args[0].integer.value = 1;
args[2].integer.value = brightness ? 0 : 1;
}
I would rather still see explicit settings for the other method as well. Something like:
if (strcmp(...)) { } else if (strcmp(...)) { } else { return -EINVAL; }
status = acpi_evaluate_object(priv->handle, priv->acpi_method, &arg_list, NULL);
if (ACPI_FAILURE(status))
return -ENXIO;
return 0;
+}
+static int huawei_wmi_leds_setup(struct wmi_device *wdev) +{
struct huawei_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
acpi_status status;
// Skip registering LED subsystem if no ACPI method was found.
status = acpi_get_handle(priv->handle, "\\_SB.PCI0.LPCB.EC0", &priv->handle);
if (ACPI_FAILURE(status))
return 0;
if (acpi_has_method(priv->handle, "SPIN"))
priv->acpi_method = "SPIN";
else if (acpi_has_method(priv->handle, "WPIN"))
priv->acpi_method = "WPIN";
else
return 0;
priv->cdev.name = "platform::micmute";
priv->cdev.max_brightness = 1;
priv->cdev.brightness_set_blocking = huawei_wmi_micmute_led_set;
priv->cdev.default_trigger = "audio-micmute";
priv->cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
priv->cdev.dev = &wdev->dev;
return devm_led_classdev_register(&wdev->dev, &priv->cdev);
+}
+static void huawei_wmi_process_key(struct wmi_device *wdev, int code) +{
struct huawei_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
const struct key_entry *key;
/*
* WMI0 uses code 0x80 to indicate a hotkey event.
* The actual key is fetched from the method WQ00.
*/
if (code == 0x80) {
acpi_status status;
acpi_handle handle;
unsigned long long result;
union acpi_object args[1];
struct acpi_object_list arg_list = {
.pointer = args,
.count = ARRAY_SIZE(args),
};
args[0].type = ACPI_TYPE_INTEGER;
args[0].integer.value = 0;
status = acpi_get_handle(NULL, "\\WMI0", &handle);
if (ACPI_FAILURE(status)) {
dev_err(&wdev->dev, "Unable to get ACPI handle\n");
return;
}
status = acpi_evaluate_integer(NULL, "WQ00", &arg_list, &result);
if (ACPI_FAILURE(status)) {
dev_err(&wdev->dev, "Unable to evaluate ACPI method\n");
return;
}
code = result;
}
key = sparse_keymap_entry_from_scancode(priv->idev, code);
if (!key) {
dev_info(&wdev->dev, "Unknown key pressed, code: 0x%04x\n", code);
return;
}
sparse_keymap_report_entry(priv->idev, key, 1, true);
+}
+static void huawei_wmi_notify(struct wmi_device *wdev,
union acpi_object *obj)
+{
if (obj->type == ACPI_TYPE_INTEGER)
huawei_wmi_process_key(wdev, obj->integer.value);
else
dev_info(&wdev->dev, "Bad response type %d\n", obj->type);
+}
+static int huawei_wmi_input_setup(struct wmi_device *wdev) +{
struct huawei_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
int err;
priv->idev = devm_input_allocate_device(&wdev->dev);
if (!priv->idev)
return -ENOMEM;
priv->idev->name = "Huawei WMI hotkeys";
priv->idev->phys = "wmi/input0";
priv->idev->id.bustype = BUS_HOST;
priv->idev->dev.parent = &wdev->dev;
err = sparse_keymap_setup(priv->idev, huawei_wmi_keymap, NULL);
if (err)
return err;
err = input_register_device(priv->idev);
if (err)
return err;
return 0;
+}
+static int huawei_wmi_probe(struct wmi_device *wdev) +{
struct huawei_wmi_priv *priv;
int err;
priv = devm_kzalloc(&wdev->dev, sizeof(struct huawei_wmi_priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
dev_set_drvdata(&wdev->dev, priv);
err = huawei_wmi_input_setup(wdev);
if (err)
return err;
err = huawei_wmi_leds_setup(wdev);
if (err)
return err;
return 0;
+}
+static const struct wmi_device_id huawei_wmi_id_table[] = {
{ .guid_string = WMI0_EVENT_GUID },
{ .guid_string = AMW0_EVENT_GUID },
{ }
+};
+static struct wmi_driver huawei_wmi_driver = {
.driver = {
.name = "huawei-wmi",
},
.id_table = huawei_wmi_id_table,
.probe = huawei_wmi_probe,
.notify = huawei_wmi_notify,
+};
+static int __init huawei_wmi_init(void) +{
if (!(wmi_has_guid(WMI0_EVENT_GUID) || wmi_has_guid(AMW0_EVENT_GUID))) {
pr_debug("Compatible WMI GUID not found\n");
return -ENODEV;
}
return wmi_driver_register(&huawei_wmi_driver);
+}
+static void __exit huawei_wmi_exit(void) +{
wmi_driver_unregister(&huawei_wmi_driver);
+}
+module_init(huawei_wmi_init); +module_exit(huawei_wmi_exit);
+MODULE_ALIAS("wmi:"WMI0_EVENT_GUID); +MODULE_ALIAS("wmi:"AMW0_EVENT_GUID); +MODULE_AUTHOR("Ayman Bagabas ayman.bagabas@gmail.com"); +MODULE_DESCRIPTION("Huawei WMI hotkeys");
+MODULE_LICENSE("GPL v2");
2.19.1
Some of Huawei laptops come with a LED in the micmute key. This patch enables the use of micmute LED for these devices: 1. Matebook X (19e5:3200), (19e5:3201) 2. Matebook X Pro (19e5:3204)
Signed-off-by: Ayman Bagabas ayman.bagabas@gmail.com --- sound/pci/hda/patch_realtek.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 1326f32f4574..9766fd249bdf 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5776,7 +5776,9 @@ static const struct hda_fixup alc269_fixups[] = { {0x1e, 0x411111f0}, {0x21, 0x04211020}, { } - } + }, + .chained = true, + .chain_id = ALC255_FIXUP_MIC_MUTE_LED }, [ALC269_FIXUP_ASUS_X101_FUNC] = { .type = HDA_FIXUP_FUNC, @@ -6608,6 +6610,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x511f, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K), SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD), + SND_PCI_QUIRK(0x19e5, 0x3200, "Huawei MBX", ALC255_FIXUP_MIC_MUTE_LED), + SND_PCI_QUIRK(0x19e5, 0x3201, "Huawei MBX", ALC255_FIXUP_MIC_MUTE_LED), SND_PCI_QUIRK(0x19e5, 0x3204, "Huawei MBXP", ALC256_FIXUP_HUAWEI_MBXP_PINS), SND_PCI_QUIRK(0x1b7d, 0xa831, "Ordissimo EVE2 ", ALC269VB_FIXUP_ORDISSIMO_EVE2), /* Also known as Malata PC-B1303 */
On Fri, 30 Nov 2018 00:57:38 +0100, Ayman Bagabas wrote:
Some of Huawei laptops come with a LED in the micmute key. This patch enables the use of micmute LED for these devices:
- Matebook X (19e5:3200), (19e5:3201)
- Matebook X Pro (19e5:3204)
Signed-off-by: Ayman Bagabas ayman.bagabas@gmail.com
Feel free to take my ack Reviewed-by: Takashi Iwai tiwai@suse.de
thanks,
Takashi
On Mon, 2018-12-03 at 13:00 +0100, Takashi Iwai wrote:
On Fri, 30 Nov 2018 00:57:38 +0100, Ayman Bagabas wrote:
Some of Huawei laptops come with a LED in the micmute key. This patch enables the use of micmute LED for these devices:
- Matebook X (19e5:3200), (19e5:3201)
- Matebook X Pro (19e5:3204)
Signed-off-by: Ayman Bagabas ayman.bagabas@gmail.com
Feel free to take my ack Reviewed-by: Takashi Iwai tiwai@suse.de
Thanks, i'll include that in next submission.
thanks,
Takashi
participants (4)
-
Andy Shevchenko
-
Ayman Bagabas
-
ayman.bagabas@gmail.com
-
Takashi Iwai