1 // SPDX-License-Identifier: GPL-2.0+
3 * lg-laptop.c - LG Gram ACPI features and hotkeys Driver
5 * Copyright (C) 2018 Matan Ziv-Av <matan@svgalib.org>
8 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10 #include <linux/acpi.h>
11 #include <linux/input.h>
12 #include <linux/input/sparse-keymap.h>
13 #include <linux/kernel.h>
14 #include <linux/leds.h>
15 #include <linux/module.h>
16 #include <linux/platform_device.h>
17 #include <linux/types.h>
19 #define LED_DEVICE(_name, max) struct led_classdev _name = { \
20 .name = __stringify(_name), \
21 .max_brightness = max, \
22 .brightness_set = _name##_set, \
23 .brightness_get = _name##_get, \
26 MODULE_AUTHOR("Matan Ziv-Av");
27 MODULE_DESCRIPTION("LG WMI Hotkey Driver");
28 MODULE_LICENSE("GPL");
30 #define WMI_EVENT_GUID0 "E4FB94F9-7F2B-4173-AD1A-CD1D95086248"
31 #define WMI_EVENT_GUID1 "023B133E-49D1-4E10-B313-698220140DC2"
32 #define WMI_EVENT_GUID2 "37BE1AC0-C3F2-4B1F-BFBE-8FDEAF2814D6"
33 #define WMI_EVENT_GUID3 "911BAD44-7DF8-4FBB-9319-BABA1C4B293B"
34 #define WMI_METHOD_WMAB "C3A72B38-D3EF-42D3-8CBB-D5A57049F66D"
35 #define WMI_METHOD_WMBB "2B4F501A-BD3C-4394-8DCF-00A7D2BC8210"
36 #define WMI_EVENT_GUID WMI_EVENT_GUID0
38 #define WMAB_METHOD "\\XINI.WMAB"
39 #define WMBB_METHOD "\\XINI.WMBB"
40 #define SB_GGOV_METHOD "\\_SB.GGOV"
41 #define GOV_TLED 0x2020008
44 #define WM_KEY_LIGHT 0x400
46 #define WM_FN_LOCK 0x407
47 #define WM_BATT_LIMIT 0x61
48 #define WM_READER_MODE 0xBF
49 #define WM_FAN_MODE 0x33
50 #define WMBB_USB_CHARGE 0x10B
51 #define WMBB_BATT_LIMIT 0x10C
53 #define PLATFORM_NAME "lg-laptop"
55 MODULE_ALIAS("wmi:" WMI_EVENT_GUID0
);
56 MODULE_ALIAS("wmi:" WMI_EVENT_GUID1
);
57 MODULE_ALIAS("wmi:" WMI_EVENT_GUID2
);
58 MODULE_ALIAS("wmi:" WMI_EVENT_GUID3
);
59 MODULE_ALIAS("wmi:" WMI_METHOD_WMAB
);
60 MODULE_ALIAS("wmi:" WMI_METHOD_WMBB
);
61 MODULE_ALIAS("acpi*:LGEX0815:*");
63 static struct platform_device
*pf_device
;
64 static struct input_dev
*wmi_input_dev
;
67 #define INIT_INPUT_WMI_0 0x01
68 #define INIT_INPUT_WMI_2 0x02
69 #define INIT_INPUT_ACPI 0x04
70 #define INIT_SPARSE_KEYMAP 0x80
72 static const struct key_entry wmi_keymap
[] = {
73 {KE_KEY
, 0x70, {KEY_F15
} }, /* LG control panel (F1) */
74 {KE_KEY
, 0x74, {KEY_F13
} }, /* Touchpad toggle (F5) */
75 {KE_KEY
, 0xf020000, {KEY_F14
} }, /* Read mode (F9) */
76 {KE_KEY
, 0x10000000, {KEY_F16
} },/* Keyboard backlight (F8) - pressing
77 * this key both sends an event and
78 * changes backlight level.
80 {KE_KEY
, 0x80, {KEY_RFKILL
} },
84 static int ggov(u32 arg0
)
86 union acpi_object args
[1];
90 struct acpi_object_list arg
;
91 struct acpi_buffer buffer
= { ACPI_ALLOCATE_BUFFER
, NULL
};
94 args
[0].type
= ACPI_TYPE_INTEGER
;
95 args
[0].integer
.value
= arg0
;
97 status
= acpi_get_handle(NULL
, (acpi_string
) SB_GGOV_METHOD
, &handle
);
98 if (ACPI_FAILURE(status
)) {
99 pr_err("Cannot get handle");
106 status
= acpi_evaluate_object(handle
, NULL
, &arg
, &buffer
);
107 if (ACPI_FAILURE(status
)) {
108 acpi_handle_err(handle
, "GGOV: call failed.\n");
113 if (r
->type
!= ACPI_TYPE_INTEGER
) {
118 res
= r
->integer
.value
;
124 static union acpi_object
*lg_wmab(u32 method
, u32 arg1
, u32 arg2
)
126 union acpi_object args
[3];
129 struct acpi_object_list arg
;
130 struct acpi_buffer buffer
= { ACPI_ALLOCATE_BUFFER
, NULL
};
132 args
[0].type
= ACPI_TYPE_INTEGER
;
133 args
[0].integer
.value
= method
;
134 args
[1].type
= ACPI_TYPE_INTEGER
;
135 args
[1].integer
.value
= arg1
;
136 args
[2].type
= ACPI_TYPE_INTEGER
;
137 args
[2].integer
.value
= arg2
;
139 status
= acpi_get_handle(NULL
, (acpi_string
) WMAB_METHOD
, &handle
);
140 if (ACPI_FAILURE(status
)) {
141 pr_err("Cannot get handle");
148 status
= acpi_evaluate_object(handle
, NULL
, &arg
, &buffer
);
149 if (ACPI_FAILURE(status
)) {
150 acpi_handle_err(handle
, "WMAB: call failed.\n");
154 return buffer
.pointer
;
157 static union acpi_object
*lg_wmbb(u32 method_id
, u32 arg1
, u32 arg2
)
159 union acpi_object args
[3];
162 struct acpi_object_list arg
;
163 struct acpi_buffer buffer
= { ACPI_ALLOCATE_BUFFER
, NULL
};
166 *(u32
*)buf
= method_id
;
167 *(u32
*)(buf
+ 4) = arg1
;
168 *(u32
*)(buf
+ 16) = arg2
;
169 args
[0].type
= ACPI_TYPE_INTEGER
;
170 args
[0].integer
.value
= 0; /* ignored */
171 args
[1].type
= ACPI_TYPE_INTEGER
;
172 args
[1].integer
.value
= 1; /* Must be 1 or 2. Does not matter which */
173 args
[2].type
= ACPI_TYPE_BUFFER
;
174 args
[2].buffer
.length
= 32;
175 args
[2].buffer
.pointer
= buf
;
177 status
= acpi_get_handle(NULL
, (acpi_string
)WMBB_METHOD
, &handle
);
178 if (ACPI_FAILURE(status
)) {
179 pr_err("Cannot get handle");
186 status
= acpi_evaluate_object(handle
, NULL
, &arg
, &buffer
);
187 if (ACPI_FAILURE(status
)) {
188 acpi_handle_err(handle
, "WMAB: call failed.\n");
192 return (union acpi_object
*)buffer
.pointer
;
195 static void wmi_notify(u32 value
, void *context
)
197 struct acpi_buffer response
= { ACPI_ALLOCATE_BUFFER
, NULL
};
198 union acpi_object
*obj
;
200 long data
= (long)context
;
202 pr_debug("event guid %li\n", data
);
203 status
= wmi_get_event_data(value
, &response
);
204 if (ACPI_FAILURE(status
)) {
205 pr_err("Bad event status 0x%x\n", status
);
209 obj
= (union acpi_object
*)response
.pointer
;
213 if (obj
->type
== ACPI_TYPE_INTEGER
) {
214 int eventcode
= obj
->integer
.value
;
215 struct key_entry
*key
;
218 sparse_keymap_entry_from_scancode(wmi_input_dev
, eventcode
);
219 if (key
&& key
->type
== KE_KEY
)
220 sparse_keymap_report_entry(wmi_input_dev
, key
, 1, true);
223 pr_debug("Type: %i Eventcode: 0x%llx\n", obj
->type
,
225 kfree(response
.pointer
);
228 static void wmi_input_setup(void)
232 wmi_input_dev
= input_allocate_device();
234 wmi_input_dev
->name
= "LG WMI hotkeys";
235 wmi_input_dev
->phys
= "wmi/input0";
236 wmi_input_dev
->id
.bustype
= BUS_HOST
;
238 if (sparse_keymap_setup(wmi_input_dev
, wmi_keymap
, NULL
) ||
239 input_register_device(wmi_input_dev
)) {
240 pr_info("Cannot initialize input device");
241 input_free_device(wmi_input_dev
);
245 inited
|= INIT_SPARSE_KEYMAP
;
246 status
= wmi_install_notify_handler(WMI_EVENT_GUID0
, wmi_notify
,
248 if (ACPI_SUCCESS(status
))
249 inited
|= INIT_INPUT_WMI_0
;
251 status
= wmi_install_notify_handler(WMI_EVENT_GUID2
, wmi_notify
,
253 if (ACPI_SUCCESS(status
))
254 inited
|= INIT_INPUT_WMI_2
;
256 pr_info("Cannot allocate input device");
260 static void acpi_notify(struct acpi_device
*device
, u32 event
)
262 struct key_entry
*key
;
264 acpi_handle_debug(device
->handle
, "notify: %d\n", event
);
265 if (inited
& INIT_SPARSE_KEYMAP
) {
266 key
= sparse_keymap_entry_from_scancode(wmi_input_dev
, 0x80);
267 if (key
&& key
->type
== KE_KEY
)
268 sparse_keymap_report_entry(wmi_input_dev
, key
, 1, true);
272 static ssize_t
fan_mode_store(struct device
*dev
,
273 struct device_attribute
*attr
,
274 const char *buffer
, size_t count
)
277 union acpi_object
*r
;
281 ret
= kstrtobool(buffer
, &value
);
285 r
= lg_wmab(WM_FAN_MODE
, WM_GET
, 0);
289 if (r
->type
!= ACPI_TYPE_INTEGER
) {
294 m
= r
->integer
.value
;
296 r
= lg_wmab(WM_FAN_MODE
, WM_SET
, (m
& 0xffffff0f) | (value
<< 4));
298 r
= lg_wmab(WM_FAN_MODE
, WM_SET
, (m
& 0xfffffff0) | value
);
304 static ssize_t
fan_mode_show(struct device
*dev
,
305 struct device_attribute
*attr
, char *buffer
)
308 union acpi_object
*r
;
310 r
= lg_wmab(WM_FAN_MODE
, WM_GET
, 0);
314 if (r
->type
!= ACPI_TYPE_INTEGER
) {
319 status
= r
->integer
.value
& 0x01;
322 return snprintf(buffer
, PAGE_SIZE
, "%d\n", status
);
325 static ssize_t
usb_charge_store(struct device
*dev
,
326 struct device_attribute
*attr
,
327 const char *buffer
, size_t count
)
330 union acpi_object
*r
;
333 ret
= kstrtobool(buffer
, &value
);
337 r
= lg_wmbb(WMBB_USB_CHARGE
, WM_SET
, value
);
345 static ssize_t
usb_charge_show(struct device
*dev
,
346 struct device_attribute
*attr
, char *buffer
)
349 union acpi_object
*r
;
351 r
= lg_wmbb(WMBB_USB_CHARGE
, WM_GET
, 0);
355 if (r
->type
!= ACPI_TYPE_BUFFER
) {
360 status
= !!r
->buffer
.pointer
[0x10];
364 return snprintf(buffer
, PAGE_SIZE
, "%d\n", status
);
367 static ssize_t
reader_mode_store(struct device
*dev
,
368 struct device_attribute
*attr
,
369 const char *buffer
, size_t count
)
372 union acpi_object
*r
;
375 ret
= kstrtobool(buffer
, &value
);
379 r
= lg_wmab(WM_READER_MODE
, WM_SET
, value
);
387 static ssize_t
reader_mode_show(struct device
*dev
,
388 struct device_attribute
*attr
, char *buffer
)
391 union acpi_object
*r
;
393 r
= lg_wmab(WM_READER_MODE
, WM_GET
, 0);
397 if (r
->type
!= ACPI_TYPE_INTEGER
) {
402 status
= !!r
->integer
.value
;
406 return snprintf(buffer
, PAGE_SIZE
, "%d\n", status
);
409 static ssize_t
fn_lock_store(struct device
*dev
,
410 struct device_attribute
*attr
,
411 const char *buffer
, size_t count
)
414 union acpi_object
*r
;
417 ret
= kstrtobool(buffer
, &value
);
421 r
= lg_wmab(WM_FN_LOCK
, WM_SET
, value
);
429 static ssize_t
fn_lock_show(struct device
*dev
,
430 struct device_attribute
*attr
, char *buffer
)
433 union acpi_object
*r
;
435 r
= lg_wmab(WM_FN_LOCK
, WM_GET
, 0);
439 if (r
->type
!= ACPI_TYPE_BUFFER
) {
444 status
= !!r
->buffer
.pointer
[0];
447 return snprintf(buffer
, PAGE_SIZE
, "%d\n", status
);
450 static ssize_t
battery_care_limit_store(struct device
*dev
,
451 struct device_attribute
*attr
,
452 const char *buffer
, size_t count
)
457 ret
= kstrtoul(buffer
, 10, &value
);
461 if (value
== 100 || value
== 80) {
462 union acpi_object
*r
;
464 r
= lg_wmab(WM_BATT_LIMIT
, WM_SET
, value
);
475 static ssize_t
battery_care_limit_show(struct device
*dev
,
476 struct device_attribute
*attr
,
480 union acpi_object
*r
;
482 r
= lg_wmab(WM_BATT_LIMIT
, WM_GET
, 0);
486 if (r
->type
!= ACPI_TYPE_INTEGER
) {
491 status
= r
->integer
.value
;
493 if (status
!= 80 && status
!= 100)
496 return snprintf(buffer
, PAGE_SIZE
, "%d\n", status
);
499 static DEVICE_ATTR_RW(fan_mode
);
500 static DEVICE_ATTR_RW(usb_charge
);
501 static DEVICE_ATTR_RW(reader_mode
);
502 static DEVICE_ATTR_RW(fn_lock
);
503 static DEVICE_ATTR_RW(battery_care_limit
);
505 static struct attribute
*dev_attributes
[] = {
506 &dev_attr_fan_mode
.attr
,
507 &dev_attr_usb_charge
.attr
,
508 &dev_attr_reader_mode
.attr
,
509 &dev_attr_fn_lock
.attr
,
510 &dev_attr_battery_care_limit
.attr
,
514 static const struct attribute_group dev_attribute_group
= {
515 .attrs
= dev_attributes
,
518 static void tpad_led_set(struct led_classdev
*cdev
,
519 enum led_brightness brightness
)
521 union acpi_object
*r
;
523 r
= lg_wmab(WM_TLED
, WM_SET
, brightness
> LED_OFF
);
527 static enum led_brightness
tpad_led_get(struct led_classdev
*cdev
)
529 return ggov(GOV_TLED
) > 0 ? LED_ON
: LED_OFF
;
532 static LED_DEVICE(tpad_led
, 1);
534 static void kbd_backlight_set(struct led_classdev
*cdev
,
535 enum led_brightness brightness
)
538 union acpi_object
*r
;
541 if (brightness
<= LED_OFF
)
543 if (brightness
>= LED_FULL
)
545 r
= lg_wmab(WM_KEY_LIGHT
, WM_SET
, val
);
549 static enum led_brightness
kbd_backlight_get(struct led_classdev
*cdev
)
551 union acpi_object
*r
;
554 r
= lg_wmab(WM_KEY_LIGHT
, WM_GET
, 0);
559 if (r
->type
!= ACPI_TYPE_BUFFER
|| r
->buffer
.pointer
[1] != 0x05) {
564 switch (r
->buffer
.pointer
[0] & 0x27) {
580 static LED_DEVICE(kbd_backlight
, 255);
582 static void wmi_input_destroy(void)
584 if (inited
& INIT_INPUT_WMI_2
)
585 wmi_remove_notify_handler(WMI_EVENT_GUID2
);
587 if (inited
& INIT_INPUT_WMI_0
)
588 wmi_remove_notify_handler(WMI_EVENT_GUID0
);
590 if (inited
& INIT_SPARSE_KEYMAP
)
591 input_unregister_device(wmi_input_dev
);
593 inited
&= ~(INIT_INPUT_WMI_0
| INIT_INPUT_WMI_2
| INIT_SPARSE_KEYMAP
);
596 static struct platform_driver pf_driver
= {
598 .name
= PLATFORM_NAME
,
602 static int acpi_add(struct acpi_device
*device
)
609 ret
= platform_driver_register(&pf_driver
);
613 pf_device
= platform_device_register_simple(PLATFORM_NAME
,
616 if (IS_ERR(pf_device
)) {
617 ret
= PTR_ERR(pf_device
);
619 pr_err("unable to register platform device\n");
620 goto out_platform_registered
;
623 ret
= sysfs_create_group(&pf_device
->dev
.kobj
, &dev_attribute_group
);
625 goto out_platform_device
;
627 /* LEDs are optional */
628 led_classdev_register(&pf_device
->dev
, &kbd_backlight
);
629 led_classdev_register(&pf_device
->dev
, &tpad_led
);
636 platform_device_unregister(pf_device
);
637 out_platform_registered
:
638 platform_driver_unregister(&pf_driver
);
642 static int acpi_remove(struct acpi_device
*device
)
644 sysfs_remove_group(&pf_device
->dev
.kobj
, &dev_attribute_group
);
646 led_classdev_unregister(&tpad_led
);
647 led_classdev_unregister(&kbd_backlight
);
650 platform_device_unregister(pf_device
);
652 platform_driver_unregister(&pf_driver
);
657 static const struct acpi_device_id device_ids
[] = {
661 MODULE_DEVICE_TABLE(acpi
, device_ids
);
663 static struct acpi_driver acpi_driver
= {
664 .name
= "LG Gram Laptop Support",
665 .class = "lg-laptop",
669 .remove
= acpi_remove
,
670 .notify
= acpi_notify
,
672 .owner
= THIS_MODULE
,
675 static int __init
acpi_init(void)
679 result
= acpi_bus_register_driver(&acpi_driver
);
681 ACPI_DEBUG_PRINT((ACPI_DB_ERROR
, "Error registering driver\n"));
688 static void __exit
acpi_exit(void)
690 acpi_bus_unregister_driver(&acpi_driver
);
693 module_init(acpi_init
);
694 module_exit(acpi_exit
);