* [PATCH 0/5] platform/x86: lg-laptop: Improve support for modern devices
@ 2026-06-22 19:39 Armin Wolf
2026-06-22 19:39 ` [PATCH 1/5] platform/x86: lg-laptop: Fix LED resource handling Armin Wolf
` (4 more replies)
0 siblings, 5 replies; 10+ messages in thread
From: Armin Wolf @ 2026-06-22 19:39 UTC (permalink / raw)
To: matan; +Cc: hansg, ilpo.jarvinen, platform-driver-x86, linux-kernel
Modern LG devices like the LG Gram 16T90SP currently do not work
with the lg-laptop driver due to a number of changes inside the
underlying ACPI interface:
- events are now exclusively delivered as native ACPI events, the
legacy WMI interface does not work anymore.
- the "WMAB" control method can return both integers and buffers.
- new events where introduced.
- strict keyboard backlight interface.
The first patch fixes and issue inside the LED resource handling,
while the remaining patches add support for the above changes.
The patches where tested by users on bugzilla
(https://bugzilla.kernel.org/show_bug.cgi?id=218901) and work on
both "old" and "new" LG devices.
Armin Wolf (5):
platform/x86: lg-laptop: Fix LED resource handling
platform/x86: lg-laptop: Add support for native ACPI events
platform/x86: lg-laptop: Add support for additional events
platform/x86: lg-laptop: Improve WMAB control method support
platform/x86: lg-laptop: Fix keyboard backlight support on LG Gram
16T90SP
drivers/platform/x86/Kconfig | 1 -
drivers/platform/x86/lg-laptop.c | 643 ++++++++++++++++++++-----------
2 files changed, 411 insertions(+), 233 deletions(-)
--
2.39.5
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 1/5] platform/x86: lg-laptop: Fix LED resource handling
2026-06-22 19:39 [PATCH 0/5] platform/x86: lg-laptop: Improve support for modern devices Armin Wolf
@ 2026-06-22 19:39 ` Armin Wolf
2026-06-22 19:39 ` [PATCH 2/5] platform/x86: lg-laptop: Add support for native ACPI events Armin Wolf
` (3 subsequent siblings)
4 siblings, 0 replies; 10+ messages in thread
From: Armin Wolf @ 2026-06-22 19:39 UTC (permalink / raw)
To: matan; +Cc: hansg, ilpo.jarvinen, platform-driver-x86, linux-kernel
The event notification callback might access kbd_backlight even
when it was not successfully registered with the LED subsystem.
The same happens inside acpi_remove(), where the LED devices are
unregistered unconditionally.
Fix this by tracking the availability of the kbd_backlight LED
device and use devm_led_classdev_register() to let devres take
care of unregistering the LED devices during removal. For this
the parent device of the LED devices is changed to the native
platform device.
Fixes: ae26278829a8 ("platform/x86: lg-laptop: Use correct event for keyboard backlight FN-key")
Signed-off-by: Armin Wolf <W_Armin@gmx.de>
---
drivers/platform/x86/lg-laptop.c | 21 ++++++++++++++-------
1 file changed, 14 insertions(+), 7 deletions(-)
diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c
index a8f2f465ef3f..a2db9657027e 100644
--- a/drivers/platform/x86/lg-laptop.c
+++ b/drivers/platform/x86/lg-laptop.c
@@ -100,6 +100,7 @@ static u32 inited;
#define INIT_SPARSE_KEYMAP 0x80
static int battery_limit_use_wmbb;
+static bool kbd_backlight_available;
static struct led_classdev kbd_backlight;
static enum led_brightness get_kbd_backlight_level(struct device *dev);
@@ -214,6 +215,7 @@ static union acpi_object *lg_wmbb(struct device *dev, u32 method_id, u32 arg1, u
static void wmi_notify(union acpi_object *obj, void *context)
{
long data = (long)context;
+ unsigned int brightness;
pr_debug("event guid %li\n", data);
if (!obj)
@@ -224,8 +226,11 @@ static void wmi_notify(union acpi_object *obj, void *context)
struct key_entry *key;
if (eventcode == 0x10000000) {
- led_classdev_notify_brightness_hw_changed(
- &kbd_backlight, get_kbd_backlight_level(kbd_backlight.dev->parent));
+ if (kbd_backlight_available) {
+ brightness = get_kbd_backlight_level(kbd_backlight.dev->parent);
+ led_classdev_notify_brightness_hw_changed(&kbd_backlight,
+ brightness);
+ }
} else {
key = sparse_keymap_entry_from_scancode(
wmi_input_dev, eventcode);
@@ -865,8 +870,13 @@ static int acpi_probe(struct platform_device *pdev)
goto out_platform_device;
/* LEDs are optional */
- led_classdev_register(&pf_device->dev, &kbd_backlight);
- led_classdev_register(&pf_device->dev, &tpad_led);
+ ret = devm_led_classdev_register(&pdev->dev, &kbd_backlight);
+ if (ret < 0)
+ kbd_backlight_available = false;
+ else
+ kbd_backlight_available = true;
+
+ devm_led_classdev_register(&pdev->dev, &tpad_led);
wmi_input_setup();
battery_hook_register(&battery_hook);
@@ -884,9 +894,6 @@ static void acpi_remove(struct platform_device *pdev)
{
sysfs_remove_group(&pf_device->dev.kobj, &dev_attribute_group);
- led_classdev_unregister(&tpad_led);
- led_classdev_unregister(&kbd_backlight);
-
battery_hook_unregister(&battery_hook);
wmi_input_destroy();
platform_device_unregister(pf_device);
--
2.39.5
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 2/5] platform/x86: lg-laptop: Add support for native ACPI events
2026-06-22 19:39 [PATCH 0/5] platform/x86: lg-laptop: Improve support for modern devices Armin Wolf
2026-06-22 19:39 ` [PATCH 1/5] platform/x86: lg-laptop: Fix LED resource handling Armin Wolf
@ 2026-06-22 19:39 ` Armin Wolf
2026-07-03 13:14 ` Ilpo Järvinen
2026-06-22 19:39 ` [PATCH 3/5] platform/x86: lg-laptop: Add support for additional events Armin Wolf
` (2 subsequent siblings)
4 siblings, 1 reply; 10+ messages in thread
From: Armin Wolf @ 2026-06-22 19:39 UTC (permalink / raw)
To: matan; +Cc: hansg, ilpo.jarvinen, platform-driver-x86, linux-kernel
LG devices support two interfaces for receiving events:
- the WMI-based interface currently being used by the driver
- a ACPI-based interface similar to the WMI-based interface
Older devices use the WMI-based interface by default and need
to be manually switched into ACPI mode. Newer devices however
only support the ACPI-based interface, preventing the current
driver from receiving events on them.
Fix this by always using the native ACPI-based interface for
receiving events.
Signed-off-by: Armin Wolf <W_Armin@gmx.de>
---
drivers/platform/x86/Kconfig | 1 -
drivers/platform/x86/lg-laptop.c | 301 ++++++++++++++++++++-----------
2 files changed, 199 insertions(+), 103 deletions(-)
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index b54b5212b204..957034f39e4e 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -802,7 +802,6 @@ config LG_LAPTOP
tristate "LG Laptop Extras"
depends on ACPI
depends on ACPI_BATTERY
- depends on ACPI_WMI
depends on INPUT
select INPUT_SPARSEKMAP
select NEW_LEDS
diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c
index a2db9657027e..ead0c3cc74a8 100644
--- a/drivers/platform/x86/lg-laptop.c
+++ b/drivers/platform/x86/lg-laptop.c
@@ -57,13 +57,18 @@ MODULE_PARM_DESC(fw_debug, "Enable printing of firmware debug messages");
#define LG_ADDRESS_SPACE_DEBUG_MSG_START_ADR 0x3E8
#define LG_ADDRESS_SPACE_DEBUG_MSG_END_ADR 0x5E8
-#define WMI_EVENT_GUID0 "E4FB94F9-7F2B-4173-AD1A-CD1D95086248"
-#define WMI_EVENT_GUID1 "023B133E-49D1-4E10-B313-698220140DC2"
-#define WMI_EVENT_GUID2 "37BE1AC0-C3F2-4B1F-BFBE-8FDEAF2814D6"
-#define WMI_EVENT_GUID3 "911BAD44-7DF8-4FBB-9319-BABA1C4B293B"
-#define WMI_METHOD_WMAB "C3A72B38-D3EF-42D3-8CBB-D5A57049F66D"
-#define WMI_METHOD_WMBB "2B4F501A-BD3C-4394-8DCF-00A7D2BC8210"
-#define WMI_EVENT_GUID WMI_EVENT_GUID0
+#define LG_NOTIFY_TABLET_MODE_OFF 0x50
+#define LG_NOTIFY_TABLET_MODE_ON 0x51
+#define LG_NOTIFY_HOTKEY 0x80
+#define LG_NOTIFY_THERMAL 0x81
+#define LG_NOTIFY_MISC 0x82
+
+#define LG_OREP_READ_EC 0
+#define LG_OREP_WRITE_EC 1
+#define LG_OREP_DEBUG 2
+#define LG_OREP_UPDATE_SYSTEM_STATE 3
+#define LG_OREP_INTERCEPT_WMI_EVENTS 4
+#define LG_OREP_WAKE_ON_LAN 6
#define SB_GGOV_METHOD "\\_SB.GGOV"
#define GOV_TLED 0x2020008
@@ -83,21 +88,7 @@ MODULE_PARM_DESC(fw_debug, "Enable printing of firmware debug messages");
#define PLATFORM_NAME "lg-laptop"
-MODULE_ALIAS("wmi:" WMI_EVENT_GUID0);
-MODULE_ALIAS("wmi:" WMI_EVENT_GUID1);
-MODULE_ALIAS("wmi:" WMI_EVENT_GUID2);
-MODULE_ALIAS("wmi:" WMI_EVENT_GUID3);
-MODULE_ALIAS("wmi:" WMI_METHOD_WMAB);
-MODULE_ALIAS("wmi:" WMI_METHOD_WMBB);
-
static struct platform_device *pf_device;
-static struct input_dev *wmi_input_dev;
-
-static u32 inited;
-#define INIT_INPUT_WMI_0 0x01
-#define INIT_INPUT_WMI_2 0x02
-#define INIT_INPUT_ACPI 0x04
-#define INIT_SPARSE_KEYMAP 0x80
static int battery_limit_use_wmbb;
static bool kbd_backlight_available;
@@ -115,6 +106,36 @@ static const struct key_entry wmi_keymap[] = {
{KE_END, 0}
};
+static int lg_laptop_execute_orep(acpi_handle handle, u64 command, u64 value,
+ unsigned long long *result)
+{
+ union acpi_object objs[] = {
+ {
+ .integer = {
+ .type = ACPI_TYPE_INTEGER,
+ .value = command,
+ },
+ },
+ {
+ .integer = {
+ .type = ACPI_TYPE_INTEGER,
+ .value = value,
+ },
+ }
+ };
+ struct acpi_object_list args = {
+ .count = ARRAY_SIZE(objs),
+ .pointer = objs,
+ };
+ acpi_status status;
+
+ status = acpi_evaluate_integer(handle, "OREP", &args, result);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ return 0;
+}
+
static int ggov(u32 arg0)
{
union acpi_object args[1];
@@ -212,70 +233,6 @@ static union acpi_object *lg_wmbb(struct device *dev, u32 method_id, u32 arg1, u
return (union acpi_object *)buffer.pointer;
}
-static void wmi_notify(union acpi_object *obj, void *context)
-{
- long data = (long)context;
- unsigned int brightness;
-
- pr_debug("event guid %li\n", data);
- if (!obj)
- return;
-
- if (obj->type == ACPI_TYPE_INTEGER) {
- int eventcode = obj->integer.value;
- struct key_entry *key;
-
- if (eventcode == 0x10000000) {
- if (kbd_backlight_available) {
- brightness = get_kbd_backlight_level(kbd_backlight.dev->parent);
- led_classdev_notify_brightness_hw_changed(&kbd_backlight,
- brightness);
- }
- } else {
- key = sparse_keymap_entry_from_scancode(
- wmi_input_dev, eventcode);
- if (key && key->type == KE_KEY)
- sparse_keymap_report_entry(wmi_input_dev,
- key, 1, true);
- }
- }
-
- pr_debug("Type: %i Eventcode: 0x%llx\n", obj->type,
- obj->integer.value);
-}
-
-static void wmi_input_setup(void)
-{
- acpi_status status;
-
- wmi_input_dev = input_allocate_device();
- if (wmi_input_dev) {
- wmi_input_dev->name = "LG WMI hotkeys";
- wmi_input_dev->phys = "wmi/input0";
- wmi_input_dev->id.bustype = BUS_HOST;
-
- if (sparse_keymap_setup(wmi_input_dev, wmi_keymap, NULL) ||
- input_register_device(wmi_input_dev)) {
- pr_info("Cannot initialize input device");
- input_free_device(wmi_input_dev);
- return;
- }
-
- inited |= INIT_SPARSE_KEYMAP;
- status = wmi_install_notify_handler(WMI_EVENT_GUID0, wmi_notify,
- (void *)0);
- if (ACPI_SUCCESS(status))
- inited |= INIT_INPUT_WMI_0;
-
- status = wmi_install_notify_handler(WMI_EVENT_GUID2, wmi_notify,
- (void *)2);
- if (ACPI_SUCCESS(status))
- inited |= INIT_INPUT_WMI_2;
- } else {
- pr_info("Cannot allocate input device");
- }
-}
-
static ssize_t fan_mode_store(struct device *dev,
struct device_attribute *attr,
const char *buffer, size_t count)
@@ -634,26 +591,164 @@ static enum led_brightness kbd_backlight_get(struct led_classdev *cdev)
static LED_DEVICE(kbd_backlight, 255, LED_BRIGHT_HW_CHANGED);
-static void wmi_input_destroy(void)
+static struct platform_driver pf_driver = {
+ .driver = {
+ .name = PLATFORM_NAME,
+ }
+};
+
+static int lg_laptop_get_event_data(acpi_handle handle, u32 value, u32 *data)
{
- if (inited & INIT_INPUT_WMI_2)
- wmi_remove_notify_handler(WMI_EVENT_GUID2);
+ union acpi_object objs[] = {
+ {
+ .integer = {
+ .type = ACPI_TYPE_INTEGER,
+ .value = value,
+ },
+ }
+ };
+ struct acpi_object_list args = {
+ .count = ARRAY_SIZE(objs),
+ .pointer = objs,
+ };
+ unsigned long long result;
+ acpi_status status;
+
+ status = acpi_evaluate_integer(handle, "_WED", &args, &result);
+ if (ACPI_FAILURE(status))
+ return -EIO;
- if (inited & INIT_INPUT_WMI_0)
- wmi_remove_notify_handler(WMI_EVENT_GUID0);
+ if (result > U32_MAX)
+ return -EPROTO;
- if (inited & INIT_SPARSE_KEYMAP)
- input_unregister_device(wmi_input_dev);
+ *data = result;
- inited &= ~(INIT_INPUT_WMI_0 | INIT_INPUT_WMI_2 | INIT_SPARSE_KEYMAP);
+ return 0;
}
-static struct platform_driver pf_driver = {
- .driver = {
- .name = PLATFORM_NAME,
+static void lg_laptop_handle_input_event(struct input_dev *input_dev, u32 value, u32 data)
+{
+ unsigned int kbd_brightness;
+
+ switch (value) {
+ case LG_NOTIFY_HOTKEY:
+ sparse_keymap_report_event(input_dev, data, 1, true);
+ break;
+ case LG_NOTIFY_THERMAL:
+ /* Currently not supported */
+ break;
+ case LG_NOTIFY_MISC:
+ switch (data) {
+ case 0x10000000:
+ if (!kbd_backlight_available)
+ break;
+
+ kbd_brightness = get_kbd_backlight_level(kbd_backlight.dev->parent);
+ led_classdev_notify_brightness_hw_changed(&kbd_backlight, kbd_brightness);
+ break;
+ default:
+ sparse_keymap_report_event(input_dev, data, 1, true);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void lg_laptop_notify_handler(acpi_handle handle, u32 value, void *context)
+{
+ struct input_dev *input_dev = context;
+ u32 data;
+ int ret;
+
+ switch (value) {
+ case LG_NOTIFY_TABLET_MODE_OFF:
+ case LG_NOTIFY_TABLET_MODE_ON:
+ /* Already handled by intel-hid */
+ return;
+ case LG_NOTIFY_HOTKEY:
+ case LG_NOTIFY_THERMAL:
+ case LG_NOTIFY_MISC:
+ ret = lg_laptop_get_event_data(handle, value, &data);
+ if (ret < 0) {
+ dev_notice(input_dev->dev.parent, "Failed to get event data: %d\n", ret);
+ return;
+ }
+
+ dev_dbg(input_dev->dev.parent, "Received event %u (%u)\n", value, data);
+
+ lg_laptop_handle_input_event(input_dev, value, data);
+ return;
+ default:
+ dev_notice(input_dev->dev.parent, "Received unknown event %u\n", value);
}
};
+static void lg_laptop_remove_notify_handler(void *context)
+{
+ acpi_handle handle = context;
+
+ acpi_remove_notify_handler(handle, ACPI_ALL_NOTIFY, lg_laptop_notify_handler);
+}
+
+static void lg_laptop_reenable_wmi_events(void *context)
+{
+ acpi_handle handle = context;
+ unsigned long long dummy;
+
+ lg_laptop_execute_orep(handle, LG_OREP_INTERCEPT_WMI_EVENTS, 0, &dummy);
+}
+
+static int lg_laptop_input_init(struct device *dev, acpi_handle handle)
+{
+ struct input_dev *input_dev;
+ unsigned long long result;
+ acpi_status status;
+ int ret;
+
+ if (!acpi_has_method(handle, "_WED"))
+ return 0;
+
+ input_dev = devm_input_allocate_device(dev);
+ if (!input_dev)
+ return -ENOMEM;
+
+ input_dev->name = "LG WMI hotkeys";
+ input_dev->phys = "wmi/input0";
+ input_dev->id.bustype = BUS_HOST;
+ ret = sparse_keymap_setup(input_dev, wmi_keymap, NULL);
+ if (ret < 0)
+ return ret;
+
+ ret = input_register_device(input_dev);
+ if (ret < 0)
+ return ret;
+
+ status = acpi_install_notify_handler(handle, ACPI_ALL_NOTIFY, lg_laptop_notify_handler,
+ input_dev);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ ret = devm_add_action_or_reset(dev, lg_laptop_remove_notify_handler, handle);
+ if (ret < 0)
+ return ret;
+
+ if (acpi_has_method(handle, "OREP")) {
+ ret = lg_laptop_execute_orep(handle, LG_OREP_INTERCEPT_WMI_EVENTS, 1, &result);
+ if (ret < 0)
+ return ret;
+
+ if (result)
+ return -EIO;
+
+ ret = devm_add_action_or_reset(dev, lg_laptop_reenable_wmi_events, handle);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
static acpi_status lg_laptop_address_space_write(struct device *dev, acpi_physical_address address,
size_t size, u64 value)
{
@@ -865,10 +960,6 @@ static int acpi_probe(struct platform_device *pdev)
if (year >= 2019)
battery_limit_use_wmbb = 1;
- ret = sysfs_create_group(&pf_device->dev.kobj, &dev_attribute_group);
- if (ret)
- goto out_platform_device;
-
/* LEDs are optional */
ret = devm_led_classdev_register(&pdev->dev, &kbd_backlight);
if (ret < 0)
@@ -878,7 +969,14 @@ static int acpi_probe(struct platform_device *pdev)
devm_led_classdev_register(&pdev->dev, &tpad_led);
- wmi_input_setup();
+ ret = lg_laptop_input_init(&pdev->dev, device->handle);
+ if (ret < 0)
+ goto out_platform_device;
+
+ ret = sysfs_create_group(&pf_device->dev.kobj, &dev_attribute_group);
+ if (ret)
+ goto out_platform_device;
+
battery_hook_register(&battery_hook);
return 0;
@@ -895,7 +993,6 @@ static void acpi_remove(struct platform_device *pdev)
sysfs_remove_group(&pf_device->dev.kobj, &dev_attribute_group);
battery_hook_unregister(&battery_hook);
- wmi_input_destroy();
platform_device_unregister(pf_device);
pf_device = NULL;
platform_driver_unregister(&pf_driver);
--
2.39.5
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 3/5] platform/x86: lg-laptop: Add support for additional events
2026-06-22 19:39 [PATCH 0/5] platform/x86: lg-laptop: Improve support for modern devices Armin Wolf
2026-06-22 19:39 ` [PATCH 1/5] platform/x86: lg-laptop: Fix LED resource handling Armin Wolf
2026-06-22 19:39 ` [PATCH 2/5] platform/x86: lg-laptop: Add support for native ACPI events Armin Wolf
@ 2026-06-22 19:39 ` Armin Wolf
2026-06-22 19:39 ` [PATCH 4/5] platform/x86: lg-laptop: Improve WMAB control method support Armin Wolf
2026-06-22 19:39 ` [PATCH 5/5] platform/x86: lg-laptop: Fix keyboard backlight support on LG Gram 16T90SP Armin Wolf
4 siblings, 0 replies; 10+ messages in thread
From: Armin Wolf @ 2026-06-22 19:39 UTC (permalink / raw)
To: matan; +Cc: hansg, ilpo.jarvinen, platform-driver-x86, linux-kernel
Newer devices support a number of additional events:
- 0x0: Placeholder event
- 0x78: Mute audio
- 0xf070002: Open settings
- 0x30010000/0x30010001: Hotkey combination pressed/released
- 0x40020000: Disable camera
- 0x40020001: Mute microphone
- 0x40030001: Fn-lock
Map those events onto appropriate key codes.
Signed-off-by: Armin Wolf <W_Armin@gmx.de>
---
drivers/platform/x86/lg-laptop.c | 33 +++++++++++++++++++++++++-------
1 file changed, 26 insertions(+), 7 deletions(-)
diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c
index ead0c3cc74a8..c8e45e8b9d74 100644
--- a/drivers/platform/x86/lg-laptop.c
+++ b/drivers/platform/x86/lg-laptop.c
@@ -96,13 +96,32 @@ static struct led_classdev kbd_backlight;
static enum led_brightness get_kbd_backlight_level(struct device *dev);
static const struct key_entry wmi_keymap[] = {
- {KE_KEY, 0x70, {KEY_F15} }, /* LG control panel (F1) */
- {KE_KEY, 0x74, {KEY_F21} }, /* Touchpad toggle (F5) */
- {KE_KEY, 0xf020000, {KEY_F14} }, /* Read mode (F9) */
- {KE_KEY, 0x10000000, {KEY_F16} },/* Keyboard backlight (F8) - pressing
- * this key both sends an event and
- * changes backlight level.
- */
+ /* Placeholder value send by multiple hotkeys handled by ACPI */
+ { KE_IGNORE, 0x0, { KEY_UNKNOWN }},
+ /* LG control panel */
+ { KE_KEY, 0x70, { KEY_F15 }},
+ /* Touchpad toggle */
+ { KE_KEY, 0x74, { KEY_F21 }},
+ /* Mute Audio, already handled by ACPI */
+ { KE_IGNORE, 0x78, { KEY_MUTE }},
+ /* Read mode */
+ { KE_KEY, 0xf020000, { KEY_F14 }},
+ /* Open settings */
+ { KE_KEY, 0xf070002, { KEY_CONFIG }},
+ /* Keyboard backlight - pressing this key both sends an event and changes backlight level */
+ { KE_KEY, 0x10000000, { KEY_F16 }},
+ /* Hotkey combination pressed */
+ { KE_IGNORE, 0x30010000, { KEY_UNKNOWN }},
+ /* Hotkey combination released */
+ { KE_IGNORE, 0x30010001, { KEY_UNKNOWN }},
+ /* Change fan mode */
+ { KE_KEY, 0x30010051, { KEY_PERFORMANCE }},
+ /* Disable camera */
+ { KE_KEY, 0x40020000, { KEY_CAMERA_ACCESS_TOGGLE }},
+ /* Mute microphone */
+ { KE_KEY, 0x40020001, { KEY_MICMUTE }},
+ /* Fn-Lock */
+ { KE_KEY, 0x40030001, { KEY_FN_ESC }},
{KE_END, 0}
};
--
2.39.5
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 4/5] platform/x86: lg-laptop: Improve WMAB control method support
2026-06-22 19:39 [PATCH 0/5] platform/x86: lg-laptop: Improve support for modern devices Armin Wolf
` (2 preceding siblings ...)
2026-06-22 19:39 ` [PATCH 3/5] platform/x86: lg-laptop: Add support for additional events Armin Wolf
@ 2026-06-22 19:39 ` Armin Wolf
2026-06-22 19:39 ` [PATCH 5/5] platform/x86: lg-laptop: Fix keyboard backlight support on LG Gram 16T90SP Armin Wolf
4 siblings, 0 replies; 10+ messages in thread
From: Armin Wolf @ 2026-06-22 19:39 UTC (permalink / raw)
To: matan; +Cc: hansg, ilpo.jarvinen, platform-driver-x86, linux-kernel
The WMAB ACPI control method can return two kinds of results:
1. A ACPI integer containing the result.
2. A ACPI buffer containing the result and an error code.
Devices implement one of those mechanisms, but not both. Extend
lg_wmab() to abstract away the differences between both mechanisms
to fix keyboard backlight support on newer devices.
Signed-off-by: Armin Wolf <W_Armin@gmx.de>
---
drivers/platform/x86/lg-laptop.c | 267 +++++++++++++++++--------------
1 file changed, 149 insertions(+), 118 deletions(-)
diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c
index c8e45e8b9d74..d1b33a6b5206 100644
--- a/drivers/platform/x86/lg-laptop.c
+++ b/drivers/platform/x86/lg-laptop.c
@@ -10,6 +10,8 @@
#include <linux/acpi.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
+#include <linux/compiler_attributes.h>
+#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/dev_printk.h>
#include <linux/dmi.h>
@@ -21,6 +23,7 @@
#include <linux/platform_device.h>
#include <linux/string_choices.h>
#include <linux/types.h>
+#include <linux/unaligned.h>
#include <acpi/battery.h>
@@ -88,6 +91,11 @@ MODULE_PARM_DESC(fw_debug, "Enable printing of firmware debug messages");
#define PLATFORM_NAME "lg-laptop"
+struct lg_wmab_buffer_result {
+ __le32 value;
+ __le32 status;
+} __packed;
+
static struct platform_device *pf_device;
static int battery_limit_use_wmbb;
@@ -195,30 +203,97 @@ static int ggov(u32 arg0)
return res;
}
-static union acpi_object *lg_wmab(struct device *dev, u32 method, u32 arg1, u32 arg2)
+static int lg_wmab_get(struct device *dev, u32 method, u32 *result)
{
- union acpi_object args[3];
- acpi_status status;
- struct acpi_object_list arg;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ union acpi_object in[] = {
+ {
+ .integer = {
+ .type = ACPI_TYPE_INTEGER,
+ .value = method,
+ },
+ },
+ {
+ .integer = {
+ .type = ACPI_TYPE_INTEGER,
+ .value = WM_GET,
+ },
+ },
+ {
+ .integer = {
+ .type = ACPI_TYPE_INTEGER,
+ .value = 0,
+ },
+ },
+ };
+ struct lg_wmab_buffer_result *buffer_result;
+ struct acpi_object_list input = {
+ .count = ARRAY_SIZE(in),
+ .pointer = in,
+ };
+ acpi_status status;
- args[0].type = ACPI_TYPE_INTEGER;
- args[0].integer.value = method;
- args[1].type = ACPI_TYPE_INTEGER;
- args[1].integer.value = arg1;
- args[2].type = ACPI_TYPE_INTEGER;
- args[2].integer.value = arg2;
+ status = acpi_evaluate_object(ACPI_HANDLE(dev), "WMAB", &input, &buffer);
+ if (ACPI_FAILURE(status))
+ return -EIO;
- arg.count = 3;
- arg.pointer = args;
+ union acpi_object *obj __free(kfree) = buffer.pointer;
- status = acpi_evaluate_object(ACPI_HANDLE(dev), "WMAB", &arg, &buffer);
- if (ACPI_FAILURE(status)) {
- dev_err(dev, "WMAB: call failed.\n");
- return NULL;
+ if (!obj)
+ return -ENODATA;
+
+ switch (obj->type) {
+ case ACPI_TYPE_INTEGER:
+ *result = obj->integer.value;
+ return 0;
+ case ACPI_TYPE_BUFFER:
+ if (obj->buffer.length != sizeof(*buffer_result))
+ return -EPROTO;
+
+ buffer_result = (struct lg_wmab_buffer_result *)obj->buffer.pointer;
+ if (get_unaligned_le32(&buffer_result->status))
+ return -EIO;
+
+ *result = get_unaligned_le32(&buffer_result->value);
+ return 0;
+ default:
+ return -EPROTO;
}
+}
- return buffer.pointer;
+static int lg_wmab_set(struct device *dev, u32 method, u32 arg)
+{
+ union acpi_object in[] = {
+ {
+ .integer = {
+ .type = ACPI_TYPE_INTEGER,
+ .value = method,
+ },
+ },
+ {
+ .integer = {
+ .type = ACPI_TYPE_INTEGER,
+ .value = WM_SET,
+ },
+ },
+ {
+ .integer = {
+ .type = ACPI_TYPE_INTEGER,
+ .value = arg,
+ },
+ },
+ };
+ struct acpi_object_list input = {
+ .count = ARRAY_SIZE(in),
+ .pointer = in,
+ };
+ acpi_status status;
+
+ status = acpi_evaluate_object(ACPI_HANDLE(dev), "WMAB", &input, NULL);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ return 0;
}
static union acpi_object *lg_wmbb(struct device *dev, u32 method_id, u32 arg1, u32 arg2)
@@ -257,7 +332,6 @@ static ssize_t fan_mode_store(struct device *dev,
const char *buffer, size_t count)
{
unsigned long value;
- union acpi_object *r;
int ret;
ret = kstrtoul(buffer, 10, &value);
@@ -266,10 +340,10 @@ static ssize_t fan_mode_store(struct device *dev,
if (value >= 3)
return -EINVAL;
- r = lg_wmab(dev, WM_FAN_MODE, WM_SET,
- FIELD_PREP(FAN_MODE_LOWER, value) |
- FIELD_PREP(FAN_MODE_UPPER, value));
- kfree(r);
+ ret = lg_wmab_set(dev, WM_FAN_MODE, FIELD_PREP(FAN_MODE_LOWER, value) |
+ FIELD_PREP(FAN_MODE_UPPER, value));
+ if (ret < 0)
+ return ret;
return count;
}
@@ -277,22 +351,14 @@ static ssize_t fan_mode_store(struct device *dev,
static ssize_t fan_mode_show(struct device *dev,
struct device_attribute *attr, char *buffer)
{
- unsigned int mode;
- union acpi_object *r;
-
- r = lg_wmab(dev, WM_FAN_MODE, WM_GET, 0);
- if (!r)
- return -EIO;
-
- if (r->type != ACPI_TYPE_INTEGER) {
- kfree(r);
- return -EIO;
- }
+ u32 mode;
+ int ret;
- mode = FIELD_GET(FAN_MODE_LOWER, r->integer.value);
- kfree(r);
+ ret = lg_wmab_get(dev, WM_FAN_MODE, &mode);
+ if (ret < 0)
+ return ret;
- return sysfs_emit(buffer, "%d\n", mode);
+ return sysfs_emit(buffer, "%lu\n", FIELD_GET(FAN_MODE_LOWER, mode));
}
static ssize_t usb_charge_store(struct device *dev,
@@ -342,41 +408,30 @@ static ssize_t reader_mode_store(struct device *dev,
const char *buffer, size_t count)
{
bool value;
- union acpi_object *r;
int ret;
ret = kstrtobool(buffer, &value);
if (ret)
return ret;
- r = lg_wmab(dev, WM_READER_MODE, WM_SET, value);
- if (!r)
- return -EIO;
+ ret = lg_wmab_set(dev, WM_READER_MODE, value);
+ if (ret < 0)
+ return ret;
- kfree(r);
return count;
}
static ssize_t reader_mode_show(struct device *dev,
struct device_attribute *attr, char *buffer)
{
- unsigned int status;
- union acpi_object *r;
-
- r = lg_wmab(dev, WM_READER_MODE, WM_GET, 0);
- if (!r)
- return -EIO;
-
- if (r->type != ACPI_TYPE_INTEGER) {
- kfree(r);
- return -EIO;
- }
-
- status = !!r->integer.value;
+ u32 status;
+ int ret;
- kfree(r);
+ ret = lg_wmab_get(dev, WM_READER_MODE, &status);
+ if (ret < 0)
+ return ret;
- return sysfs_emit(buffer, "%d\n", status);
+ return sysfs_emit(buffer, "%d\n", !!status);
}
static ssize_t fn_lock_store(struct device *dev,
@@ -384,40 +439,30 @@ static ssize_t fn_lock_store(struct device *dev,
const char *buffer, size_t count)
{
bool value;
- union acpi_object *r;
int ret;
ret = kstrtobool(buffer, &value);
if (ret)
return ret;
- r = lg_wmab(dev, WM_FN_LOCK, WM_SET, value);
- if (!r)
- return -EIO;
+ ret = lg_wmab_set(dev, WM_FN_LOCK, value);
+ if (ret < 0)
+ return ret;
- kfree(r);
return count;
}
static ssize_t fn_lock_show(struct device *dev,
struct device_attribute *attr, char *buffer)
{
- unsigned int status;
- union acpi_object *r;
-
- r = lg_wmab(dev, WM_FN_LOCK, WM_GET, 0);
- if (!r)
- return -EIO;
-
- if (r->type != ACPI_TYPE_BUFFER) {
- kfree(r);
- return -EIO;
- }
+ u32 status;
+ int ret;
- status = !!r->buffer.pointer[0];
- kfree(r);
+ ret = lg_wmab_get(dev, WM_FN_LOCK, &status);
+ if (ret < 0)
+ return ret;
- return sysfs_emit(buffer, "%d\n", status);
+ return sysfs_emit(buffer, "%d\n", !!status);
}
static ssize_t charge_control_end_threshold_store(struct device *dev,
@@ -434,14 +479,18 @@ static ssize_t charge_control_end_threshold_store(struct device *dev,
if (value == 100 || value == 80) {
union acpi_object *r;
- if (battery_limit_use_wmbb)
+ if (battery_limit_use_wmbb) {
r = lg_wmbb(&pf_device->dev, WMBB_BATT_LIMIT, WM_SET, value);
- else
- r = lg_wmab(&pf_device->dev, WM_BATT_LIMIT, WM_SET, value);
- if (!r)
- return -EIO;
+ if (!r)
+ return -EIO;
+
+ kfree(r);
+ } else {
+ ret = lg_wmab_set(&pf_device->dev, WM_BATT_LIMIT, value);
+ if (ret < 0)
+ return ret;
+ }
- kfree(r);
return count;
}
@@ -452,8 +501,9 @@ static ssize_t charge_control_end_threshold_show(struct device *device,
struct device_attribute *attr,
char *buf)
{
- unsigned int status;
union acpi_object *r;
+ u32 status;
+ int ret;
if (battery_limit_use_wmbb) {
r = lg_wmbb(&pf_device->dev, WMBB_BATT_LIMIT, WM_GET, 0);
@@ -466,19 +516,13 @@ static ssize_t charge_control_end_threshold_show(struct device *device,
}
status = r->buffer.pointer[0x10];
+ kfree(r);
} else {
- r = lg_wmab(&pf_device->dev, WM_BATT_LIMIT, WM_GET, 0);
- if (!r)
- return -EIO;
-
- if (r->type != ACPI_TYPE_INTEGER) {
- kfree(r);
- return -EIO;
- }
-
- status = r->integer.value;
+ ret = lg_wmab_get(&pf_device->dev, WM_BATT_LIMIT, &status);
+ if (ret < 0)
+ return ret;
}
- kfree(r);
+
if (status != 80 && status != 100)
status = 0;
@@ -544,10 +588,7 @@ static const struct attribute_group dev_attribute_group = {
static void tpad_led_set(struct led_classdev *cdev,
enum led_brightness brightness)
{
- union acpi_object *r;
-
- r = lg_wmab(cdev->dev->parent, WM_TLED, WM_SET, brightness > LED_OFF);
- kfree(r);
+ lg_wmab_set(cdev->dev->parent, WM_TLED, brightness > LED_OFF);
}
static enum led_brightness tpad_led_get(struct led_classdev *cdev)
@@ -561,46 +602,36 @@ static void kbd_backlight_set(struct led_classdev *cdev,
enum led_brightness brightness)
{
u32 val;
- union acpi_object *r;
val = 0x22;
if (brightness <= LED_OFF)
val = 0;
if (brightness >= LED_FULL)
val = 0x24;
- r = lg_wmab(cdev->dev->parent, WM_KEY_LIGHT, WM_SET, val);
- kfree(r);
+
+ lg_wmab_set(cdev->dev->parent, WM_KEY_LIGHT, val);
}
static enum led_brightness get_kbd_backlight_level(struct device *dev)
{
- union acpi_object *r;
- int val;
-
- r = lg_wmab(dev, WM_KEY_LIGHT, WM_GET, 0);
+ u32 value;
+ int ret;
- if (!r)
+ ret = lg_wmab_get(dev, WM_KEY_LIGHT, &value);
+ if (ret < 0)
return LED_OFF;
- if (r->type != ACPI_TYPE_BUFFER || r->buffer.pointer[1] != 0x05) {
- kfree(r);
+ if ((value & 0xFF00) != 0x0500)
return LED_OFF;
- }
- switch (r->buffer.pointer[0] & 0x27) {
+ switch (value & 0x27) {
case 0x24:
- val = LED_FULL;
- break;
+ return LED_FULL;
case 0x22:
- val = LED_HALF;
- break;
+ return LED_HALF;
default:
- val = LED_OFF;
+ return LED_OFF;
}
-
- kfree(r);
-
- return val;
}
static enum led_brightness kbd_backlight_get(struct led_classdev *cdev)
--
2.39.5
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 5/5] platform/x86: lg-laptop: Fix keyboard backlight support on LG Gram 16T90SP
2026-06-22 19:39 [PATCH 0/5] platform/x86: lg-laptop: Improve support for modern devices Armin Wolf
` (3 preceding siblings ...)
2026-06-22 19:39 ` [PATCH 4/5] platform/x86: lg-laptop: Improve WMAB control method support Armin Wolf
@ 2026-06-22 19:39 ` Armin Wolf
2026-07-03 13:22 ` Ilpo Järvinen
4 siblings, 1 reply; 10+ messages in thread
From: Armin Wolf @ 2026-06-22 19:39 UTC (permalink / raw)
To: matan; +Cc: hansg, ilpo.jarvinen, platform-driver-x86, linux-kernel
On the LG Gram 16T90SP, the ACPI firmware will only accept the values
0x80, 0xA2 and 0xA4 when setting the keyboard backlight. After a bit
of research using ACPI tables from older models it seems that bit 8
is supposed to be always written as 1.
Fix the keyboard backlight support on this model by always setting
bit 8 inside the argument passed to the WMAB ACPI control method.
Signed-off-by: Armin Wolf <W_Armin@gmx.de>
---
drivers/platform/x86/lg-laptop.c | 47 ++++++++++++++++++++++++--------
1 file changed, 36 insertions(+), 11 deletions(-)
diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c
index d1b33a6b5206..22f8a6368f7c 100644
--- a/drivers/platform/x86/lg-laptop.c
+++ b/drivers/platform/x86/lg-laptop.c
@@ -86,6 +86,18 @@ MODULE_PARM_DESC(fw_debug, "Enable printing of firmware debug messages");
#define WMBB_USB_CHARGE 0x10B
#define WMBB_BATT_LIMIT 0x10C
+#define KBD_LED_BRIGHTNESS_MASK GENMASK(4, 0)
+#define KBD_LED_BRIGHTNESS_OFF 0x0
+#define KBD_LED_BRIGHTNESS_HALF 0x2
+#define KBD_LED_BRIGHTNESS_FULL 0x4
+#define KBD_LED_MODE_MASK GENMASK(6, 5)
+#define KBD_LED_MODE_OFF 0x0
+#define KBD_LED_MODE_ON 0x1
+#define KBD_LED_STATUS BIT(7)
+#define KBD_LED_MAGIC_MASK GENMASK(15, 8)
+/* Exact purpose is unknown, maybe some sort of brightness limit? */
+#define KBD_LED_MAGIC 0x05
+
#define FAN_MODE_LOWER GENMASK(1, 0)
#define FAN_MODE_UPPER GENMASK(5, 4)
@@ -601,15 +613,25 @@ static LED_DEVICE(tpad_led, 1, 0);
static void kbd_backlight_set(struct led_classdev *cdev,
enum led_brightness brightness)
{
- u32 val;
+ /* Must always be written */
+ u32 value = KBD_LED_STATUS;
+ u32 mode, bright;
+
+ if (brightness <= LED_OFF) {
+ mode = KBD_LED_MODE_OFF;
+ bright = KBD_LED_BRIGHTNESS_OFF;
+ } else {
+ mode = KBD_LED_MODE_ON;
+ if (brightness >= LED_FULL)
+ bright = KBD_LED_BRIGHTNESS_FULL;
+ else
+ bright = KBD_LED_BRIGHTNESS_HALF;
+ }
- val = 0x22;
- if (brightness <= LED_OFF)
- val = 0;
- if (brightness >= LED_FULL)
- val = 0x24;
+ value |= FIELD_PREP(KBD_LED_BRIGHTNESS_MASK, bright);
+ value |= FIELD_PREP(KBD_LED_MODE_MASK, mode);
- lg_wmab_set(cdev->dev->parent, WM_KEY_LIGHT, val);
+ lg_wmab_set(cdev->dev->parent, WM_KEY_LIGHT, value);
}
static enum led_brightness get_kbd_backlight_level(struct device *dev)
@@ -621,13 +643,16 @@ static enum led_brightness get_kbd_backlight_level(struct device *dev)
if (ret < 0)
return LED_OFF;
- if ((value & 0xFF00) != 0x0500)
+ if (FIELD_GET(KBD_LED_MAGIC_MASK, value) != KBD_LED_MAGIC)
+ return LED_OFF;
+
+ if (FIELD_GET(KBD_LED_MODE_MASK, value) == KBD_LED_MODE_OFF)
return LED_OFF;
- switch (value & 0x27) {
- case 0x24:
+ switch (FIELD_GET(KBD_LED_BRIGHTNESS_MASK, value)) {
+ case KBD_LED_BRIGHTNESS_FULL:
return LED_FULL;
- case 0x22:
+ case KBD_LED_BRIGHTNESS_HALF:
return LED_HALF;
default:
return LED_OFF;
--
2.39.5
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH 2/5] platform/x86: lg-laptop: Add support for native ACPI events
2026-06-22 19:39 ` [PATCH 2/5] platform/x86: lg-laptop: Add support for native ACPI events Armin Wolf
@ 2026-07-03 13:14 ` Ilpo Järvinen
2026-07-03 17:59 ` Armin Wolf
0 siblings, 1 reply; 10+ messages in thread
From: Ilpo Järvinen @ 2026-07-03 13:14 UTC (permalink / raw)
To: Armin Wolf; +Cc: matan, Hans de Goede, platform-driver-x86, LKML
On Mon, 22 Jun 2026, Armin Wolf wrote:
> LG devices support two interfaces for receiving events:
> - the WMI-based interface currently being used by the driver
> - a ACPI-based interface similar to the WMI-based interface
>
> Older devices use the WMI-based interface by default and need
> to be manually switched into ACPI mode. Newer devices however
> only support the ACPI-based interface, preventing the current
> driver from receiving events on them.
>
> Fix this by always using the native ACPI-based interface for
> receiving events.
>
> Signed-off-by: Armin Wolf <W_Armin@gmx.de>
> ---
> drivers/platform/x86/Kconfig | 1 -
> drivers/platform/x86/lg-laptop.c | 301 ++++++++++++++++++++-----------
> 2 files changed, 199 insertions(+), 103 deletions(-)
>
> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
> index b54b5212b204..957034f39e4e 100644
> --- a/drivers/platform/x86/Kconfig
> +++ b/drivers/platform/x86/Kconfig
> @@ -802,7 +802,6 @@ config LG_LAPTOP
> tristate "LG Laptop Extras"
> depends on ACPI
> depends on ACPI_BATTERY
> - depends on ACPI_WMI
> depends on INPUT
> select INPUT_SPARSEKMAP
> select NEW_LEDS
> diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c
> index a2db9657027e..ead0c3cc74a8 100644
> --- a/drivers/platform/x86/lg-laptop.c
> +++ b/drivers/platform/x86/lg-laptop.c
> @@ -57,13 +57,18 @@ MODULE_PARM_DESC(fw_debug, "Enable printing of firmware debug messages");
> #define LG_ADDRESS_SPACE_DEBUG_MSG_START_ADR 0x3E8
> #define LG_ADDRESS_SPACE_DEBUG_MSG_END_ADR 0x5E8
>
> -#define WMI_EVENT_GUID0 "E4FB94F9-7F2B-4173-AD1A-CD1D95086248"
> -#define WMI_EVENT_GUID1 "023B133E-49D1-4E10-B313-698220140DC2"
> -#define WMI_EVENT_GUID2 "37BE1AC0-C3F2-4B1F-BFBE-8FDEAF2814D6"
> -#define WMI_EVENT_GUID3 "911BAD44-7DF8-4FBB-9319-BABA1C4B293B"
> -#define WMI_METHOD_WMAB "C3A72B38-D3EF-42D3-8CBB-D5A57049F66D"
> -#define WMI_METHOD_WMBB "2B4F501A-BD3C-4394-8DCF-00A7D2BC8210"
> -#define WMI_EVENT_GUID WMI_EVENT_GUID0
> +#define LG_NOTIFY_TABLET_MODE_OFF 0x50
> +#define LG_NOTIFY_TABLET_MODE_ON 0x51
> +#define LG_NOTIFY_HOTKEY 0x80
> +#define LG_NOTIFY_THERMAL 0x81
> +#define LG_NOTIFY_MISC 0x82
> +
> +#define LG_OREP_READ_EC 0
> +#define LG_OREP_WRITE_EC 1
> +#define LG_OREP_DEBUG 2
> +#define LG_OREP_UPDATE_SYSTEM_STATE 3
> +#define LG_OREP_INTERCEPT_WMI_EVENTS 4
> +#define LG_OREP_WAKE_ON_LAN 6
>
> #define SB_GGOV_METHOD "\\_SB.GGOV"
> #define GOV_TLED 0x2020008
> @@ -83,21 +88,7 @@ MODULE_PARM_DESC(fw_debug, "Enable printing of firmware debug messages");
>
> #define PLATFORM_NAME "lg-laptop"
>
> -MODULE_ALIAS("wmi:" WMI_EVENT_GUID0);
> -MODULE_ALIAS("wmi:" WMI_EVENT_GUID1);
> -MODULE_ALIAS("wmi:" WMI_EVENT_GUID2);
> -MODULE_ALIAS("wmi:" WMI_EVENT_GUID3);
> -MODULE_ALIAS("wmi:" WMI_METHOD_WMAB);
> -MODULE_ALIAS("wmi:" WMI_METHOD_WMBB);
> -
> static struct platform_device *pf_device;
> -static struct input_dev *wmi_input_dev;
> -
> -static u32 inited;
> -#define INIT_INPUT_WMI_0 0x01
> -#define INIT_INPUT_WMI_2 0x02
> -#define INIT_INPUT_ACPI 0x04
> -#define INIT_SPARSE_KEYMAP 0x80
>
> static int battery_limit_use_wmbb;
> static bool kbd_backlight_available;
> @@ -115,6 +106,36 @@ static const struct key_entry wmi_keymap[] = {
> {KE_END, 0}
> };
>
> +static int lg_laptop_execute_orep(acpi_handle handle, u64 command, u64 value,
> + unsigned long long *result)
> +{
> + union acpi_object objs[] = {
> + {
> + .integer = {
> + .type = ACPI_TYPE_INTEGER,
> + .value = command,
> + },
> + },
> + {
> + .integer = {
> + .type = ACPI_TYPE_INTEGER,
> + .value = value,
> + },
> + }
> + };
> + struct acpi_object_list args = {
> + .count = ARRAY_SIZE(objs),
> + .pointer = objs,
> + };
> + acpi_status status;
> +
> + status = acpi_evaluate_integer(handle, "OREP", &args, result);
> + if (ACPI_FAILURE(status))
> + return -EIO;
> +
> + return 0;
> +}
> +
> static int ggov(u32 arg0)
> {
> union acpi_object args[1];
> @@ -212,70 +233,6 @@ static union acpi_object *lg_wmbb(struct device *dev, u32 method_id, u32 arg1, u
> return (union acpi_object *)buffer.pointer;
> }
>
> -static void wmi_notify(union acpi_object *obj, void *context)
> -{
> - long data = (long)context;
> - unsigned int brightness;
> -
> - pr_debug("event guid %li\n", data);
> - if (!obj)
> - return;
> -
> - if (obj->type == ACPI_TYPE_INTEGER) {
> - int eventcode = obj->integer.value;
> - struct key_entry *key;
> -
> - if (eventcode == 0x10000000) {
> - if (kbd_backlight_available) {
> - brightness = get_kbd_backlight_level(kbd_backlight.dev->parent);
> - led_classdev_notify_brightness_hw_changed(&kbd_backlight,
> - brightness);
> - }
> - } else {
> - key = sparse_keymap_entry_from_scancode(
> - wmi_input_dev, eventcode);
> - if (key && key->type == KE_KEY)
> - sparse_keymap_report_entry(wmi_input_dev,
> - key, 1, true);
> - }
> - }
> -
> - pr_debug("Type: %i Eventcode: 0x%llx\n", obj->type,
> - obj->integer.value);
> -}
> -
> -static void wmi_input_setup(void)
> -{
> - acpi_status status;
> -
> - wmi_input_dev = input_allocate_device();
> - if (wmi_input_dev) {
> - wmi_input_dev->name = "LG WMI hotkeys";
> - wmi_input_dev->phys = "wmi/input0";
> - wmi_input_dev->id.bustype = BUS_HOST;
> -
> - if (sparse_keymap_setup(wmi_input_dev, wmi_keymap, NULL) ||
> - input_register_device(wmi_input_dev)) {
> - pr_info("Cannot initialize input device");
> - input_free_device(wmi_input_dev);
> - return;
> - }
> -
> - inited |= INIT_SPARSE_KEYMAP;
> - status = wmi_install_notify_handler(WMI_EVENT_GUID0, wmi_notify,
> - (void *)0);
> - if (ACPI_SUCCESS(status))
> - inited |= INIT_INPUT_WMI_0;
> -
> - status = wmi_install_notify_handler(WMI_EVENT_GUID2, wmi_notify,
> - (void *)2);
> - if (ACPI_SUCCESS(status))
> - inited |= INIT_INPUT_WMI_2;
> - } else {
> - pr_info("Cannot allocate input device");
> - }
> -}
> -
> static ssize_t fan_mode_store(struct device *dev,
> struct device_attribute *attr,
> const char *buffer, size_t count)
> @@ -634,26 +591,164 @@ static enum led_brightness kbd_backlight_get(struct led_classdev *cdev)
>
> static LED_DEVICE(kbd_backlight, 255, LED_BRIGHT_HW_CHANGED);
>
> -static void wmi_input_destroy(void)
> +static struct platform_driver pf_driver = {
> + .driver = {
> + .name = PLATFORM_NAME,
> + }
> +};
> +
> +static int lg_laptop_get_event_data(acpi_handle handle, u32 value, u32 *data)
> {
> - if (inited & INIT_INPUT_WMI_2)
> - wmi_remove_notify_handler(WMI_EVENT_GUID2);
> + union acpi_object objs[] = {
> + {
> + .integer = {
> + .type = ACPI_TYPE_INTEGER,
> + .value = value,
> + },
> + }
> + };
> + struct acpi_object_list args = {
> + .count = ARRAY_SIZE(objs),
Add the include.
> + .pointer = objs,
> + };
> + unsigned long long result;
> + acpi_status status;
> +
> + status = acpi_evaluate_integer(handle, "_WED", &args, &result);
> + if (ACPI_FAILURE(status))
> + return -EIO;
>
> - if (inited & INIT_INPUT_WMI_0)
> - wmi_remove_notify_handler(WMI_EVENT_GUID0);
> + if (result > U32_MAX)
Add include.
> + return -EPROTO;
>
> - if (inited & INIT_SPARSE_KEYMAP)
> - input_unregister_device(wmi_input_dev);
> + *data = result;
>
> - inited &= ~(INIT_INPUT_WMI_0 | INIT_INPUT_WMI_2 | INIT_SPARSE_KEYMAP);
> + return 0;
> }
>
> -static struct platform_driver pf_driver = {
> - .driver = {
> - .name = PLATFORM_NAME,
> +static void lg_laptop_handle_input_event(struct input_dev *input_dev, u32 value, u32 data)
> +{
> + unsigned int kbd_brightness;
> +
> + switch (value) {
> + case LG_NOTIFY_HOTKEY:
> + sparse_keymap_report_event(input_dev, data, 1, true);
> + break;
> + case LG_NOTIFY_THERMAL:
> + /* Currently not supported */
> + break;
> + case LG_NOTIFY_MISC:
> + switch (data) {
> + case 0x10000000:
> + if (!kbd_backlight_available)
> + break;
> +
> + kbd_brightness = get_kbd_backlight_level(kbd_backlight.dev->parent);
> + led_classdev_notify_brightness_hw_changed(&kbd_backlight, kbd_brightness);
> + break;
> + default:
> + sparse_keymap_report_event(input_dev, data, 1, true);
> + }
> + break;
> + default:
> + break;
> + }
> +}
> +
> +static void lg_laptop_notify_handler(acpi_handle handle, u32 value, void *context)
> +{
> + struct input_dev *input_dev = context;
> + u32 data;
> + int ret;
> +
> + switch (value) {
> + case LG_NOTIFY_TABLET_MODE_OFF:
> + case LG_NOTIFY_TABLET_MODE_ON:
> + /* Already handled by intel-hid */
> + return;
> + case LG_NOTIFY_HOTKEY:
> + case LG_NOTIFY_THERMAL:
> + case LG_NOTIFY_MISC:
> + ret = lg_laptop_get_event_data(handle, value, &data);
> + if (ret < 0) {
> + dev_notice(input_dev->dev.parent, "Failed to get event data: %d\n", ret);
> + return;
> + }
> +
> + dev_dbg(input_dev->dev.parent, "Received event %u (%u)\n", value, data);
> +
> + lg_laptop_handle_input_event(input_dev, value, data);
> + return;
> + default:
> + dev_notice(input_dev->dev.parent, "Received unknown event %u\n", value);
> }
> };
>
> +static void lg_laptop_remove_notify_handler(void *context)
> +{
> + acpi_handle handle = context;
> +
> + acpi_remove_notify_handler(handle, ACPI_ALL_NOTIFY, lg_laptop_notify_handler);
> +}
> +
> +static void lg_laptop_reenable_wmi_events(void *context)
> +{
> + acpi_handle handle = context;
> + unsigned long long dummy;
> +
> + lg_laptop_execute_orep(handle, LG_OREP_INTERCEPT_WMI_EVENTS, 0, &dummy);
> +}
> +
> +static int lg_laptop_input_init(struct device *dev, acpi_handle handle)
> +{
> + struct input_dev *input_dev;
> + unsigned long long result;
> + acpi_status status;
> + int ret;
> +
> + if (!acpi_has_method(handle, "_WED"))
> + return 0;
> +
> + input_dev = devm_input_allocate_device(dev);
> + if (!input_dev)
> + return -ENOMEM;
> +
> + input_dev->name = "LG WMI hotkeys";
> + input_dev->phys = "wmi/input0";
> + input_dev->id.bustype = BUS_HOST;
> + ret = sparse_keymap_setup(input_dev, wmi_keymap, NULL);
> + if (ret < 0)
> + return ret;
> +
> + ret = input_register_device(input_dev);
> + if (ret < 0)
> + return ret;
> +
> + status = acpi_install_notify_handler(handle, ACPI_ALL_NOTIFY, lg_laptop_notify_handler,
> + input_dev);
> + if (ACPI_FAILURE(status))
> + return -EIO;
> +
> + ret = devm_add_action_or_reset(dev, lg_laptop_remove_notify_handler, handle);
> + if (ret < 0)
> + return ret;
> +
> + if (acpi_has_method(handle, "OREP")) {
> + ret = lg_laptop_execute_orep(handle, LG_OREP_INTERCEPT_WMI_EVENTS, 1, &result);
> + if (ret < 0)
> + return ret;
> +
> + if (result)
This still looks to be part of the error handling so please remove the
blank line before it.
> + return -EIO;
> +
> + ret = devm_add_action_or_reset(dev, lg_laptop_reenable_wmi_events, handle);
> + if (ret < 0)
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> static acpi_status lg_laptop_address_space_write(struct device *dev, acpi_physical_address address,
> size_t size, u64 value)
> {
> @@ -865,10 +960,6 @@ static int acpi_probe(struct platform_device *pdev)
> if (year >= 2019)
> battery_limit_use_wmbb = 1;
>
> - ret = sysfs_create_group(&pf_device->dev.kobj, &dev_attribute_group);
> - if (ret)
> - goto out_platform_device;
> -
> /* LEDs are optional */
> ret = devm_led_classdev_register(&pdev->dev, &kbd_backlight);
> if (ret < 0)
> @@ -878,7 +969,14 @@ static int acpi_probe(struct platform_device *pdev)
>
> devm_led_classdev_register(&pdev->dev, &tpad_led);
>
> - wmi_input_setup();
> + ret = lg_laptop_input_init(&pdev->dev, device->handle);
> + if (ret < 0)
> + goto out_platform_device;
> +
> + ret = sysfs_create_group(&pf_device->dev.kobj, &dev_attribute_group);
> + if (ret)
> + goto out_platform_device;
Not directly related to the patch, but can't .dev_groups be used?
> battery_hook_register(&battery_hook);
>
> return 0;
> @@ -895,7 +993,6 @@ static void acpi_remove(struct platform_device *pdev)
> sysfs_remove_group(&pf_device->dev.kobj, &dev_attribute_group);
>
> battery_hook_unregister(&battery_hook);
> - wmi_input_destroy();
> platform_device_unregister(pf_device);
> pf_device = NULL;
> platform_driver_unregister(&pf_driver);
>
--
i.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 5/5] platform/x86: lg-laptop: Fix keyboard backlight support on LG Gram 16T90SP
2026-06-22 19:39 ` [PATCH 5/5] platform/x86: lg-laptop: Fix keyboard backlight support on LG Gram 16T90SP Armin Wolf
@ 2026-07-03 13:22 ` Ilpo Järvinen
2026-07-03 18:05 ` Armin Wolf
0 siblings, 1 reply; 10+ messages in thread
From: Ilpo Järvinen @ 2026-07-03 13:22 UTC (permalink / raw)
To: Armin Wolf; +Cc: matan, Hans de Goede, platform-driver-x86, LKML
On Mon, 22 Jun 2026, Armin Wolf wrote:
> On the LG Gram 16T90SP, the ACPI firmware will only accept the values
> 0x80, 0xA2 and 0xA4 when setting the keyboard backlight. After a bit
> of research using ACPI tables from older models it seems that bit 8
> is supposed to be always written as 1.
>
> Fix the keyboard backlight support on this model by always setting
> bit 8 inside the argument passed to the WMAB ACPI control method.
>
> Signed-off-by: Armin Wolf <W_Armin@gmx.de>
> ---
> drivers/platform/x86/lg-laptop.c | 47 ++++++++++++++++++++++++--------
> 1 file changed, 36 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c
> index d1b33a6b5206..22f8a6368f7c 100644
> --- a/drivers/platform/x86/lg-laptop.c
> +++ b/drivers/platform/x86/lg-laptop.c
> @@ -86,6 +86,18 @@ MODULE_PARM_DESC(fw_debug, "Enable printing of firmware debug messages");
> #define WMBB_USB_CHARGE 0x10B
> #define WMBB_BATT_LIMIT 0x10C
>
> +#define KBD_LED_BRIGHTNESS_MASK GENMASK(4, 0)
> +#define KBD_LED_BRIGHTNESS_OFF 0x0
> +#define KBD_LED_BRIGHTNESS_HALF 0x2
> +#define KBD_LED_BRIGHTNESS_FULL 0x4
> +#define KBD_LED_MODE_MASK GENMASK(6, 5)
bits.h needed.
> +#define KBD_LED_MODE_OFF 0x0
> +#define KBD_LED_MODE_ON 0x1
> +#define KBD_LED_STATUS BIT(7)
> +#define KBD_LED_MAGIC_MASK GENMASK(15, 8)
> +/* Exact purpose is unknown, maybe some sort of brightness limit? */
> +#define KBD_LED_MAGIC 0x05
> +
> #define FAN_MODE_LOWER GENMASK(1, 0)
> #define FAN_MODE_UPPER GENMASK(5, 4)
>
> @@ -601,15 +613,25 @@ static LED_DEVICE(tpad_led, 1, 0);
> static void kbd_backlight_set(struct led_classdev *cdev,
> enum led_brightness brightness)
> {
> - u32 val;
> + /* Must always be written */
> + u32 value = KBD_LED_STATUS;
> + u32 mode, bright;
> +
> + if (brightness <= LED_OFF) {
> + mode = KBD_LED_MODE_OFF;
> + bright = KBD_LED_BRIGHTNESS_OFF;
> + } else {
> + mode = KBD_LED_MODE_ON;
> + if (brightness >= LED_FULL)
> + bright = KBD_LED_BRIGHTNESS_FULL;
> + else
> + bright = KBD_LED_BRIGHTNESS_HALF;
> + }
>
> - val = 0x22;
> - if (brightness <= LED_OFF)
> - val = 0;
> - if (brightness >= LED_FULL)
> - val = 0x24;
> + value |= FIELD_PREP(KBD_LED_BRIGHTNESS_MASK, bright);
> + value |= FIELD_PREP(KBD_LED_MODE_MASK, mode);
>
> - lg_wmab_set(cdev->dev->parent, WM_KEY_LIGHT, val);
> + lg_wmab_set(cdev->dev->parent, WM_KEY_LIGHT, value);
> }
>
> static enum led_brightness get_kbd_backlight_level(struct device *dev)
> @@ -621,13 +643,16 @@ static enum led_brightness get_kbd_backlight_level(struct device *dev)
> if (ret < 0)
> return LED_OFF;
>
> - if ((value & 0xFF00) != 0x0500)
> + if (FIELD_GET(KBD_LED_MAGIC_MASK, value) != KBD_LED_MAGIC)
> + return LED_OFF;
> +
> + if (FIELD_GET(KBD_LED_MODE_MASK, value) == KBD_LED_MODE_OFF)
> return LED_OFF;
>
> - switch (value & 0x27) {
> - case 0x24:
> + switch (FIELD_GET(KBD_LED_BRIGHTNESS_MASK, value)) {
> + case KBD_LED_BRIGHTNESS_FULL:
> return LED_FULL;
> - case 0x22:
> + case KBD_LED_BRIGHTNESS_HALF:
> return LED_HALF;
> default:
> return LED_OFF;
Superb, I was already wanting to see this in patch 4 but had enough
patience to look the subsequent patch first. Thanks for naming them!
--
i.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/5] platform/x86: lg-laptop: Add support for native ACPI events
2026-07-03 13:14 ` Ilpo Järvinen
@ 2026-07-03 17:59 ` Armin Wolf
0 siblings, 0 replies; 10+ messages in thread
From: Armin Wolf @ 2026-07-03 17:59 UTC (permalink / raw)
To: Ilpo Järvinen; +Cc: matan, Hans de Goede, platform-driver-x86, LKML
Am 03.07.26 um 15:14 schrieb Ilpo Järvinen:
> On Mon, 22 Jun 2026, Armin Wolf wrote:
>
>> LG devices support two interfaces for receiving events:
>> - the WMI-based interface currently being used by the driver
>> - a ACPI-based interface similar to the WMI-based interface
>>
>> Older devices use the WMI-based interface by default and need
>> to be manually switched into ACPI mode. Newer devices however
>> only support the ACPI-based interface, preventing the current
>> driver from receiving events on them.
>>
>> Fix this by always using the native ACPI-based interface for
>> receiving events.
>>
>> Signed-off-by: Armin Wolf <W_Armin@gmx.de>
>> ---
>> drivers/platform/x86/Kconfig | 1 -
>> drivers/platform/x86/lg-laptop.c | 301 ++++++++++++++++++++-----------
>> 2 files changed, 199 insertions(+), 103 deletions(-)
>>
>> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
>> index b54b5212b204..957034f39e4e 100644
>> --- a/drivers/platform/x86/Kconfig
>> +++ b/drivers/platform/x86/Kconfig
>> @@ -802,7 +802,6 @@ config LG_LAPTOP
>> tristate "LG Laptop Extras"
>> depends on ACPI
>> depends on ACPI_BATTERY
>> - depends on ACPI_WMI
>> depends on INPUT
>> select INPUT_SPARSEKMAP
>> select NEW_LEDS
>> diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c
>> index a2db9657027e..ead0c3cc74a8 100644
>> --- a/drivers/platform/x86/lg-laptop.c
>> +++ b/drivers/platform/x86/lg-laptop.c
>> @@ -57,13 +57,18 @@ MODULE_PARM_DESC(fw_debug, "Enable printing of firmware debug messages");
>> #define LG_ADDRESS_SPACE_DEBUG_MSG_START_ADR 0x3E8
>> #define LG_ADDRESS_SPACE_DEBUG_MSG_END_ADR 0x5E8
>>
>> -#define WMI_EVENT_GUID0 "E4FB94F9-7F2B-4173-AD1A-CD1D95086248"
>> -#define WMI_EVENT_GUID1 "023B133E-49D1-4E10-B313-698220140DC2"
>> -#define WMI_EVENT_GUID2 "37BE1AC0-C3F2-4B1F-BFBE-8FDEAF2814D6"
>> -#define WMI_EVENT_GUID3 "911BAD44-7DF8-4FBB-9319-BABA1C4B293B"
>> -#define WMI_METHOD_WMAB "C3A72B38-D3EF-42D3-8CBB-D5A57049F66D"
>> -#define WMI_METHOD_WMBB "2B4F501A-BD3C-4394-8DCF-00A7D2BC8210"
>> -#define WMI_EVENT_GUID WMI_EVENT_GUID0
>> +#define LG_NOTIFY_TABLET_MODE_OFF 0x50
>> +#define LG_NOTIFY_TABLET_MODE_ON 0x51
>> +#define LG_NOTIFY_HOTKEY 0x80
>> +#define LG_NOTIFY_THERMAL 0x81
>> +#define LG_NOTIFY_MISC 0x82
>> +
>> +#define LG_OREP_READ_EC 0
>> +#define LG_OREP_WRITE_EC 1
>> +#define LG_OREP_DEBUG 2
>> +#define LG_OREP_UPDATE_SYSTEM_STATE 3
>> +#define LG_OREP_INTERCEPT_WMI_EVENTS 4
>> +#define LG_OREP_WAKE_ON_LAN 6
>>
>> #define SB_GGOV_METHOD "\\_SB.GGOV"
>> #define GOV_TLED 0x2020008
>> @@ -83,21 +88,7 @@ MODULE_PARM_DESC(fw_debug, "Enable printing of firmware debug messages");
>>
>> #define PLATFORM_NAME "lg-laptop"
>>
>> -MODULE_ALIAS("wmi:" WMI_EVENT_GUID0);
>> -MODULE_ALIAS("wmi:" WMI_EVENT_GUID1);
>> -MODULE_ALIAS("wmi:" WMI_EVENT_GUID2);
>> -MODULE_ALIAS("wmi:" WMI_EVENT_GUID3);
>> -MODULE_ALIAS("wmi:" WMI_METHOD_WMAB);
>> -MODULE_ALIAS("wmi:" WMI_METHOD_WMBB);
>> -
>> static struct platform_device *pf_device;
>> -static struct input_dev *wmi_input_dev;
>> -
>> -static u32 inited;
>> -#define INIT_INPUT_WMI_0 0x01
>> -#define INIT_INPUT_WMI_2 0x02
>> -#define INIT_INPUT_ACPI 0x04
>> -#define INIT_SPARSE_KEYMAP 0x80
>>
>> static int battery_limit_use_wmbb;
>> static bool kbd_backlight_available;
>> @@ -115,6 +106,36 @@ static const struct key_entry wmi_keymap[] = {
>> {KE_END, 0}
>> };
>>
>> +static int lg_laptop_execute_orep(acpi_handle handle, u64 command, u64 value,
>> + unsigned long long *result)
>> +{
>> + union acpi_object objs[] = {
>> + {
>> + .integer = {
>> + .type = ACPI_TYPE_INTEGER,
>> + .value = command,
>> + },
>> + },
>> + {
>> + .integer = {
>> + .type = ACPI_TYPE_INTEGER,
>> + .value = value,
>> + },
>> + }
>> + };
>> + struct acpi_object_list args = {
>> + .count = ARRAY_SIZE(objs),
>> + .pointer = objs,
>> + };
>> + acpi_status status;
>> +
>> + status = acpi_evaluate_integer(handle, "OREP", &args, result);
>> + if (ACPI_FAILURE(status))
>> + return -EIO;
>> +
>> + return 0;
>> +}
>> +
>> static int ggov(u32 arg0)
>> {
>> union acpi_object args[1];
>> @@ -212,70 +233,6 @@ static union acpi_object *lg_wmbb(struct device *dev, u32 method_id, u32 arg1, u
>> return (union acpi_object *)buffer.pointer;
>> }
>>
>> -static void wmi_notify(union acpi_object *obj, void *context)
>> -{
>> - long data = (long)context;
>> - unsigned int brightness;
>> -
>> - pr_debug("event guid %li\n", data);
>> - if (!obj)
>> - return;
>> -
>> - if (obj->type == ACPI_TYPE_INTEGER) {
>> - int eventcode = obj->integer.value;
>> - struct key_entry *key;
>> -
>> - if (eventcode == 0x10000000) {
>> - if (kbd_backlight_available) {
>> - brightness = get_kbd_backlight_level(kbd_backlight.dev->parent);
>> - led_classdev_notify_brightness_hw_changed(&kbd_backlight,
>> - brightness);
>> - }
>> - } else {
>> - key = sparse_keymap_entry_from_scancode(
>> - wmi_input_dev, eventcode);
>> - if (key && key->type == KE_KEY)
>> - sparse_keymap_report_entry(wmi_input_dev,
>> - key, 1, true);
>> - }
>> - }
>> -
>> - pr_debug("Type: %i Eventcode: 0x%llx\n", obj->type,
>> - obj->integer.value);
>> -}
>> -
>> -static void wmi_input_setup(void)
>> -{
>> - acpi_status status;
>> -
>> - wmi_input_dev = input_allocate_device();
>> - if (wmi_input_dev) {
>> - wmi_input_dev->name = "LG WMI hotkeys";
>> - wmi_input_dev->phys = "wmi/input0";
>> - wmi_input_dev->id.bustype = BUS_HOST;
>> -
>> - if (sparse_keymap_setup(wmi_input_dev, wmi_keymap, NULL) ||
>> - input_register_device(wmi_input_dev)) {
>> - pr_info("Cannot initialize input device");
>> - input_free_device(wmi_input_dev);
>> - return;
>> - }
>> -
>> - inited |= INIT_SPARSE_KEYMAP;
>> - status = wmi_install_notify_handler(WMI_EVENT_GUID0, wmi_notify,
>> - (void *)0);
>> - if (ACPI_SUCCESS(status))
>> - inited |= INIT_INPUT_WMI_0;
>> -
>> - status = wmi_install_notify_handler(WMI_EVENT_GUID2, wmi_notify,
>> - (void *)2);
>> - if (ACPI_SUCCESS(status))
>> - inited |= INIT_INPUT_WMI_2;
>> - } else {
>> - pr_info("Cannot allocate input device");
>> - }
>> -}
>> -
>> static ssize_t fan_mode_store(struct device *dev,
>> struct device_attribute *attr,
>> const char *buffer, size_t count)
>> @@ -634,26 +591,164 @@ static enum led_brightness kbd_backlight_get(struct led_classdev *cdev)
>>
>> static LED_DEVICE(kbd_backlight, 255, LED_BRIGHT_HW_CHANGED);
>>
>> -static void wmi_input_destroy(void)
>> +static struct platform_driver pf_driver = {
>> + .driver = {
>> + .name = PLATFORM_NAME,
>> + }
>> +};
>> +
>> +static int lg_laptop_get_event_data(acpi_handle handle, u32 value, u32 *data)
>> {
>> - if (inited & INIT_INPUT_WMI_2)
>> - wmi_remove_notify_handler(WMI_EVENT_GUID2);
>> + union acpi_object objs[] = {
>> + {
>> + .integer = {
>> + .type = ACPI_TYPE_INTEGER,
>> + .value = value,
>> + },
>> + }
>> + };
>> + struct acpi_object_list args = {
>> + .count = ARRAY_SIZE(objs),
> Add the include.
ok
>> + .pointer = objs,
>> + };
>> + unsigned long long result;
>> + acpi_status status;
>> +
>> + status = acpi_evaluate_integer(handle, "_WED", &args, &result);
>> + if (ACPI_FAILURE(status))
>> + return -EIO;
>>
>> - if (inited & INIT_INPUT_WMI_0)
>> - wmi_remove_notify_handler(WMI_EVENT_GUID0);
>> + if (result > U32_MAX)
> Add include.
ok
>> + return -EPROTO;
>>
>> - if (inited & INIT_SPARSE_KEYMAP)
>> - input_unregister_device(wmi_input_dev);
>> + *data = result;
>>
>> - inited &= ~(INIT_INPUT_WMI_0 | INIT_INPUT_WMI_2 | INIT_SPARSE_KEYMAP);
>> + return 0;
>> }
>>
>> -static struct platform_driver pf_driver = {
>> - .driver = {
>> - .name = PLATFORM_NAME,
>> +static void lg_laptop_handle_input_event(struct input_dev *input_dev, u32 value, u32 data)
>> +{
>> + unsigned int kbd_brightness;
>> +
>> + switch (value) {
>> + case LG_NOTIFY_HOTKEY:
>> + sparse_keymap_report_event(input_dev, data, 1, true);
>> + break;
>> + case LG_NOTIFY_THERMAL:
>> + /* Currently not supported */
>> + break;
>> + case LG_NOTIFY_MISC:
>> + switch (data) {
>> + case 0x10000000:
>> + if (!kbd_backlight_available)
>> + break;
>> +
>> + kbd_brightness = get_kbd_backlight_level(kbd_backlight.dev->parent);
>> + led_classdev_notify_brightness_hw_changed(&kbd_backlight, kbd_brightness);
>> + break;
>> + default:
>> + sparse_keymap_report_event(input_dev, data, 1, true);
>> + }
>> + break;
>> + default:
>> + break;
>> + }
>> +}
>> +
>> +static void lg_laptop_notify_handler(acpi_handle handle, u32 value, void *context)
>> +{
>> + struct input_dev *input_dev = context;
>> + u32 data;
>> + int ret;
>> +
>> + switch (value) {
>> + case LG_NOTIFY_TABLET_MODE_OFF:
>> + case LG_NOTIFY_TABLET_MODE_ON:
>> + /* Already handled by intel-hid */
>> + return;
>> + case LG_NOTIFY_HOTKEY:
>> + case LG_NOTIFY_THERMAL:
>> + case LG_NOTIFY_MISC:
>> + ret = lg_laptop_get_event_data(handle, value, &data);
>> + if (ret < 0) {
>> + dev_notice(input_dev->dev.parent, "Failed to get event data: %d\n", ret);
>> + return;
>> + }
>> +
>> + dev_dbg(input_dev->dev.parent, "Received event %u (%u)\n", value, data);
>> +
>> + lg_laptop_handle_input_event(input_dev, value, data);
>> + return;
>> + default:
>> + dev_notice(input_dev->dev.parent, "Received unknown event %u\n", value);
>> }
>> };
>>
>> +static void lg_laptop_remove_notify_handler(void *context)
>> +{
>> + acpi_handle handle = context;
>> +
>> + acpi_remove_notify_handler(handle, ACPI_ALL_NOTIFY, lg_laptop_notify_handler);
>> +}
>> +
>> +static void lg_laptop_reenable_wmi_events(void *context)
>> +{
>> + acpi_handle handle = context;
>> + unsigned long long dummy;
>> +
>> + lg_laptop_execute_orep(handle, LG_OREP_INTERCEPT_WMI_EVENTS, 0, &dummy);
>> +}
>> +
>> +static int lg_laptop_input_init(struct device *dev, acpi_handle handle)
>> +{
>> + struct input_dev *input_dev;
>> + unsigned long long result;
>> + acpi_status status;
>> + int ret;
>> +
>> + if (!acpi_has_method(handle, "_WED"))
>> + return 0;
>> +
>> + input_dev = devm_input_allocate_device(dev);
>> + if (!input_dev)
>> + return -ENOMEM;
>> +
>> + input_dev->name = "LG WMI hotkeys";
>> + input_dev->phys = "wmi/input0";
>> + input_dev->id.bustype = BUS_HOST;
>> + ret = sparse_keymap_setup(input_dev, wmi_keymap, NULL);
>> + if (ret < 0)
>> + return ret;
>> +
>> + ret = input_register_device(input_dev);
>> + if (ret < 0)
>> + return ret;
>> +
>> + status = acpi_install_notify_handler(handle, ACPI_ALL_NOTIFY, lg_laptop_notify_handler,
>> + input_dev);
>> + if (ACPI_FAILURE(status))
>> + return -EIO;
>> +
>> + ret = devm_add_action_or_reset(dev, lg_laptop_remove_notify_handler, handle);
>> + if (ret < 0)
>> + return ret;
>> +
>> + if (acpi_has_method(handle, "OREP")) {
>> + ret = lg_laptop_execute_orep(handle, LG_OREP_INTERCEPT_WMI_EVENTS, 1, &result);
>> + if (ret < 0)
>> + return ret;
>> +
>> + if (result)
> This still looks to be part of the error handling so please remove the
> blank line before it.
Personally i would prefer to keep the blank line because i think it looks cleaner having
one after a "simple" if statement.
>> + return -EIO;
>> +
>> + ret = devm_add_action_or_reset(dev, lg_laptop_reenable_wmi_events, handle);
>> + if (ret < 0)
>> + return ret;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> static acpi_status lg_laptop_address_space_write(struct device *dev, acpi_physical_address address,
>> size_t size, u64 value)
>> {
>> @@ -865,10 +960,6 @@ static int acpi_probe(struct platform_device *pdev)
>> if (year >= 2019)
>> battery_limit_use_wmbb = 1;
>>
>> - ret = sysfs_create_group(&pf_device->dev.kobj, &dev_attribute_group);
>> - if (ret)
>> - goto out_platform_device;
>> -
>> /* LEDs are optional */
>> ret = devm_led_classdev_register(&pdev->dev, &kbd_backlight);
>> if (ret < 0)
>> @@ -878,7 +969,14 @@ static int acpi_probe(struct platform_device *pdev)
>>
>> devm_led_classdev_register(&pdev->dev, &tpad_led);
>>
>> - wmi_input_setup();
>> + ret = lg_laptop_input_init(&pdev->dev, device->handle);
>> + if (ret < 0)
>> + goto out_platform_device;
>> +
>> + ret = sysfs_create_group(&pf_device->dev.kobj, &dev_attribute_group);
>> + if (ret)
>> + goto out_platform_device;
> Not directly related to the patch, but can't .dev_groups be used?
Theoretically yes, but this is something i leave to the driver maintainer. I only
handle this because i already handled the original issue on bugzilla.
Thanks,
Armin Wolf
>> battery_hook_register(&battery_hook);
>>
>> return 0;
>> @@ -895,7 +993,6 @@ static void acpi_remove(struct platform_device *pdev)
>> sysfs_remove_group(&pf_device->dev.kobj, &dev_attribute_group);
>>
>> battery_hook_unregister(&battery_hook);
>> - wmi_input_destroy();
>> platform_device_unregister(pf_device);
>> pf_device = NULL;
>> platform_driver_unregister(&pf_driver);
>>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 5/5] platform/x86: lg-laptop: Fix keyboard backlight support on LG Gram 16T90SP
2026-07-03 13:22 ` Ilpo Järvinen
@ 2026-07-03 18:05 ` Armin Wolf
0 siblings, 0 replies; 10+ messages in thread
From: Armin Wolf @ 2026-07-03 18:05 UTC (permalink / raw)
To: Ilpo Järvinen; +Cc: matan, Hans de Goede, platform-driver-x86, LKML
Am 03.07.26 um 15:22 schrieb Ilpo Järvinen:
> On Mon, 22 Jun 2026, Armin Wolf wrote:
>
>> On the LG Gram 16T90SP, the ACPI firmware will only accept the values
>> 0x80, 0xA2 and 0xA4 when setting the keyboard backlight. After a bit
>> of research using ACPI tables from older models it seems that bit 8
>> is supposed to be always written as 1.
>>
>> Fix the keyboard backlight support on this model by always setting
>> bit 8 inside the argument passed to the WMAB ACPI control method.
>>
>> Signed-off-by: Armin Wolf <W_Armin@gmx.de>
>> ---
>> drivers/platform/x86/lg-laptop.c | 47 ++++++++++++++++++++++++--------
>> 1 file changed, 36 insertions(+), 11 deletions(-)
>>
>> diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c
>> index d1b33a6b5206..22f8a6368f7c 100644
>> --- a/drivers/platform/x86/lg-laptop.c
>> +++ b/drivers/platform/x86/lg-laptop.c
>> @@ -86,6 +86,18 @@ MODULE_PARM_DESC(fw_debug, "Enable printing of firmware debug messages");
>> #define WMBB_USB_CHARGE 0x10B
>> #define WMBB_BATT_LIMIT 0x10C
>>
>> +#define KBD_LED_BRIGHTNESS_MASK GENMASK(4, 0)
>> +#define KBD_LED_BRIGHTNESS_OFF 0x0
>> +#define KBD_LED_BRIGHTNESS_HALF 0x2
>> +#define KBD_LED_BRIGHTNESS_FULL 0x4
>> +#define KBD_LED_MODE_MASK GENMASK(6, 5)
> bits.h needed.
Already exists.
>> +#define KBD_LED_MODE_OFF 0x0
>> +#define KBD_LED_MODE_ON 0x1
>> +#define KBD_LED_STATUS BIT(7)
>> +#define KBD_LED_MAGIC_MASK GENMASK(15, 8)
>> +/* Exact purpose is unknown, maybe some sort of brightness limit? */
>> +#define KBD_LED_MAGIC 0x05
>> +
>> #define FAN_MODE_LOWER GENMASK(1, 0)
>> #define FAN_MODE_UPPER GENMASK(5, 4)
>>
>> @@ -601,15 +613,25 @@ static LED_DEVICE(tpad_led, 1, 0);
>> static void kbd_backlight_set(struct led_classdev *cdev,
>> enum led_brightness brightness)
>> {
>> - u32 val;
>> + /* Must always be written */
>> + u32 value = KBD_LED_STATUS;
>> + u32 mode, bright;
>> +
>> + if (brightness <= LED_OFF) {
>> + mode = KBD_LED_MODE_OFF;
>> + bright = KBD_LED_BRIGHTNESS_OFF;
>> + } else {
>> + mode = KBD_LED_MODE_ON;
>> + if (brightness >= LED_FULL)
>> + bright = KBD_LED_BRIGHTNESS_FULL;
>> + else
>> + bright = KBD_LED_BRIGHTNESS_HALF;
>> + }
>>
>> - val = 0x22;
>> - if (brightness <= LED_OFF)
>> - val = 0;
>> - if (brightness >= LED_FULL)
>> - val = 0x24;
>> + value |= FIELD_PREP(KBD_LED_BRIGHTNESS_MASK, bright);
>> + value |= FIELD_PREP(KBD_LED_MODE_MASK, mode);
>>
>> - lg_wmab_set(cdev->dev->parent, WM_KEY_LIGHT, val);
>> + lg_wmab_set(cdev->dev->parent, WM_KEY_LIGHT, value);
>> }
>>
>> static enum led_brightness get_kbd_backlight_level(struct device *dev)
>> @@ -621,13 +643,16 @@ static enum led_brightness get_kbd_backlight_level(struct device *dev)
>> if (ret < 0)
>> return LED_OFF;
>>
>> - if ((value & 0xFF00) != 0x0500)
>> + if (FIELD_GET(KBD_LED_MAGIC_MASK, value) != KBD_LED_MAGIC)
>> + return LED_OFF;
>> +
>> + if (FIELD_GET(KBD_LED_MODE_MASK, value) == KBD_LED_MODE_OFF)
>> return LED_OFF;
>>
>> - switch (value & 0x27) {
>> - case 0x24:
>> + switch (FIELD_GET(KBD_LED_BRIGHTNESS_MASK, value)) {
>> + case KBD_LED_BRIGHTNESS_FULL:
>> return LED_FULL;
>> - case 0x22:
>> + case KBD_LED_BRIGHTNESS_HALF:
>> return LED_HALF;
>> default:
>> return LED_OFF;
> Superb, I was already wanting to see this in patch 4 but had enough
> patience to look the subsequent patch first. Thanks for naming them!
Thanks :)
Armin Wolf
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-07-03 18:05 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-22 19:39 [PATCH 0/5] platform/x86: lg-laptop: Improve support for modern devices Armin Wolf
2026-06-22 19:39 ` [PATCH 1/5] platform/x86: lg-laptop: Fix LED resource handling Armin Wolf
2026-06-22 19:39 ` [PATCH 2/5] platform/x86: lg-laptop: Add support for native ACPI events Armin Wolf
2026-07-03 13:14 ` Ilpo Järvinen
2026-07-03 17:59 ` Armin Wolf
2026-06-22 19:39 ` [PATCH 3/5] platform/x86: lg-laptop: Add support for additional events Armin Wolf
2026-06-22 19:39 ` [PATCH 4/5] platform/x86: lg-laptop: Improve WMAB control method support Armin Wolf
2026-06-22 19:39 ` [PATCH 5/5] platform/x86: lg-laptop: Fix keyboard backlight support on LG Gram 16T90SP Armin Wolf
2026-07-03 13:22 ` Ilpo Järvinen
2026-07-03 18:05 ` Armin Wolf
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.