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_TPAD_LED 0x08
71 #define INIT_KBD_LED 0x10
72 #define INIT_SPARSE_KEYMAP 0x80
74 static const struct key_entry wmi_keymap
[] = {
75 {KE_KEY
, 0x70, {KEY_F15
} }, /* LG control panel (F1) */
76 {KE_KEY
, 0x74, {KEY_F13
} }, /* Touchpad toggle (F5) */
77 {KE_KEY
, 0xf020000, {KEY_F14
} }, /* Read mode (F9) */
78 {KE_KEY
, 0x10000000, {KEY_F16
} },/* Keyboard backlight (F8) - pressing
79 * this key both sends an event and
80 * changes backlight level.
82 {KE_KEY
, 0x80, {KEY_RFKILL
} },
86 static int ggov(u32 arg0
)
88 union acpi_object args
[1];
92 struct acpi_object_list arg
;
93 struct acpi_buffer buffer
= { ACPI_ALLOCATE_BUFFER
, NULL
};
96 args
[0].type
= ACPI_TYPE_INTEGER
;
97 args
[0].integer
.value
= arg0
;
99 status
= acpi_get_handle(NULL
, (acpi_string
) SB_GGOV_METHOD
, &handle
);
100 if (ACPI_FAILURE(status
)) {
101 pr_err("Cannot get handle");
108 status
= acpi_evaluate_object(handle
, NULL
, &arg
, &buffer
);
109 if (ACPI_FAILURE(status
)) {
110 acpi_handle_err(handle
, "GGOV: call failed.\n");
115 if (r
->type
!= ACPI_TYPE_INTEGER
) {
120 res
= r
->integer
.value
;
126 static union acpi_object
*lg_wmab(u32 method
, u32 arg1
, u32 arg2
)
128 union acpi_object args
[3];
131 struct acpi_object_list arg
;
132 struct acpi_buffer buffer
= { ACPI_ALLOCATE_BUFFER
, NULL
};
134 args
[0].type
= ACPI_TYPE_INTEGER
;
135 args
[0].integer
.value
= method
;
136 args
[1].type
= ACPI_TYPE_INTEGER
;
137 args
[1].integer
.value
= arg1
;
138 args
[2].type
= ACPI_TYPE_INTEGER
;
139 args
[2].integer
.value
= arg2
;
141 status
= acpi_get_handle(NULL
, (acpi_string
) WMAB_METHOD
, &handle
);
142 if (ACPI_FAILURE(status
)) {
143 pr_err("Cannot get handle");
150 status
= acpi_evaluate_object(handle
, NULL
, &arg
, &buffer
);
151 if (ACPI_FAILURE(status
)) {
152 acpi_handle_err(handle
, "WMAB: call failed.\n");
156 return buffer
.pointer
;
159 static union acpi_object
*lg_wmbb(u32 method_id
, u32 arg1
, u32 arg2
)
161 union acpi_object args
[3];
164 struct acpi_object_list arg
;
165 struct acpi_buffer buffer
= { ACPI_ALLOCATE_BUFFER
, NULL
};
168 *(u32
*)buf
= method_id
;
169 *(u32
*)(buf
+ 4) = arg1
;
170 *(u32
*)(buf
+ 16) = arg2
;
171 args
[0].type
= ACPI_TYPE_INTEGER
;
172 args
[0].integer
.value
= 0; /* ignored */
173 args
[1].type
= ACPI_TYPE_INTEGER
;
174 args
[1].integer
.value
= 1; /* Must be 1 or 2. Does not matter which */
175 args
[2].type
= ACPI_TYPE_BUFFER
;
176 args
[2].buffer
.length
= 32;
177 args
[2].buffer
.pointer
= buf
;
179 status
= acpi_get_handle(NULL
, (acpi_string
)WMBB_METHOD
, &handle
);
180 if (ACPI_FAILURE(status
)) {
181 pr_err("Cannot get handle");
188 status
= acpi_evaluate_object(handle
, NULL
, &arg
, &buffer
);
189 if (ACPI_FAILURE(status
)) {
190 acpi_handle_err(handle
, "WMAB: call failed.\n");
194 return (union acpi_object
*)buffer
.pointer
;
197 static void wmi_notify(u32 value
, void *context
)
199 struct acpi_buffer response
= { ACPI_ALLOCATE_BUFFER
, NULL
};
200 union acpi_object
*obj
;
202 long data
= (long)context
;
204 pr_debug("event guid %li\n", data
);
205 status
= wmi_get_event_data(value
, &response
);
206 if (ACPI_FAILURE(status
)) {
207 pr_err("Bad event status 0x%x\n", status
);
211 obj
= (union acpi_object
*)response
.pointer
;
215 if (obj
->type
== ACPI_TYPE_INTEGER
) {
216 int eventcode
= obj
->integer
.value
;
217 struct key_entry
*key
;
220 sparse_keymap_entry_from_scancode(wmi_input_dev
, eventcode
);
221 if (key
&& key
->type
== KE_KEY
)
222 sparse_keymap_report_entry(wmi_input_dev
, key
, 1, true);
225 pr_debug("Type: %i Eventcode: 0x%llx\n", obj
->type
,
227 kfree(response
.pointer
);
230 static void wmi_input_setup(void)
234 wmi_input_dev
= input_allocate_device();
236 wmi_input_dev
->name
= "LG WMI hotkeys";
237 wmi_input_dev
->phys
= "wmi/input0";
238 wmi_input_dev
->id
.bustype
= BUS_HOST
;
240 if (sparse_keymap_setup(wmi_input_dev
, wmi_keymap
, NULL
) ||
241 input_register_device(wmi_input_dev
)) {
242 pr_info("Cannot initialize input device");
243 input_free_device(wmi_input_dev
);
247 inited
|= INIT_SPARSE_KEYMAP
;
248 status
= wmi_install_notify_handler(WMI_EVENT_GUID0
, wmi_notify
,
250 if (ACPI_SUCCESS(status
))
251 inited
|= INIT_INPUT_WMI_0
;
253 status
= wmi_install_notify_handler(WMI_EVENT_GUID2
, wmi_notify
,
255 if (ACPI_SUCCESS(status
))
256 inited
|= INIT_INPUT_WMI_2
;
258 pr_info("Cannot allocate input device");
262 static void acpi_notify(struct acpi_device
*device
, u32 event
)
264 struct key_entry
*key
;
266 acpi_handle_debug(device
->handle
, "notify: %d\n", event
);
267 if (inited
& INIT_SPARSE_KEYMAP
) {
268 key
= sparse_keymap_entry_from_scancode(wmi_input_dev
, 0x80);
269 if (key
&& key
->type
== KE_KEY
)
270 sparse_keymap_report_entry(wmi_input_dev
, key
, 1, true);
274 static ssize_t
fan_mode_store(struct device
*dev
,
275 struct device_attribute
*attr
,
276 const char *buffer
, size_t count
)
279 union acpi_object
*r
;
283 ret
= kstrtobool(buffer
, &value
);
287 r
= lg_wmab(WM_FAN_MODE
, WM_GET
, 0);
291 if (r
->type
!= ACPI_TYPE_INTEGER
) {
296 m
= r
->integer
.value
;
298 r
= lg_wmab(WM_FAN_MODE
, WM_SET
, (m
& 0xffffff0f) | (value
<< 4));
300 r
= lg_wmab(WM_FAN_MODE
, WM_SET
, (m
& 0xfffffff0) | value
);
306 static ssize_t
fan_mode_show(struct device
*dev
,
307 struct device_attribute
*attr
, char *buffer
)
310 union acpi_object
*r
;
312 r
= lg_wmab(WM_FAN_MODE
, WM_GET
, 0);
316 if (r
->type
!= ACPI_TYPE_INTEGER
) {
321 status
= r
->integer
.value
& 0x01;
324 return snprintf(buffer
, PAGE_SIZE
, "%d\n", status
);
327 static ssize_t
usb_charge_store(struct device
*dev
,
328 struct device_attribute
*attr
,
329 const char *buffer
, size_t count
)
332 union acpi_object
*r
;
335 ret
= kstrtobool(buffer
, &value
);
339 r
= lg_wmbb(WMBB_USB_CHARGE
, WM_SET
, value
);
347 static ssize_t
usb_charge_show(struct device
*dev
,
348 struct device_attribute
*attr
, char *buffer
)
351 union acpi_object
*r
;
353 r
= lg_wmbb(WMBB_USB_CHARGE
, WM_GET
, 0);
357 if (r
->type
!= ACPI_TYPE_BUFFER
) {
362 status
= !!r
->buffer
.pointer
[0x10];
366 return snprintf(buffer
, PAGE_SIZE
, "%d\n", status
);
369 static ssize_t
reader_mode_store(struct device
*dev
,
370 struct device_attribute
*attr
,
371 const char *buffer
, size_t count
)
374 union acpi_object
*r
;
377 ret
= kstrtobool(buffer
, &value
);
381 r
= lg_wmab(WM_READER_MODE
, WM_SET
, value
);
389 static ssize_t
reader_mode_show(struct device
*dev
,
390 struct device_attribute
*attr
, char *buffer
)
393 union acpi_object
*r
;
395 r
= lg_wmab(WM_READER_MODE
, WM_GET
, 0);
399 if (r
->type
!= ACPI_TYPE_INTEGER
) {
404 status
= !!r
->integer
.value
;
408 return snprintf(buffer
, PAGE_SIZE
, "%d\n", status
);
411 static ssize_t
fn_lock_store(struct device
*dev
,
412 struct device_attribute
*attr
,
413 const char *buffer
, size_t count
)
416 union acpi_object
*r
;
419 ret
= kstrtobool(buffer
, &value
);
423 r
= lg_wmab(WM_FN_LOCK
, WM_SET
, value
);
431 static ssize_t
fn_lock_show(struct device
*dev
,
432 struct device_attribute
*attr
, char *buffer
)
435 union acpi_object
*r
;
437 r
= lg_wmab(WM_FN_LOCK
, WM_GET
, 0);
441 if (r
->type
!= ACPI_TYPE_BUFFER
) {
446 status
= !!r
->buffer
.pointer
[0];
449 return snprintf(buffer
, PAGE_SIZE
, "%d\n", status
);
452 static ssize_t
battery_care_limit_store(struct device
*dev
,
453 struct device_attribute
*attr
,
454 const char *buffer
, size_t count
)
459 ret
= kstrtoul(buffer
, 10, &value
);
463 if (value
== 100 || value
== 80) {
464 union acpi_object
*r
;
466 r
= lg_wmab(WM_BATT_LIMIT
, WM_SET
, value
);
477 static ssize_t
battery_care_limit_show(struct device
*dev
,
478 struct device_attribute
*attr
,
482 union acpi_object
*r
;
484 r
= lg_wmab(WM_BATT_LIMIT
, WM_GET
, 0);
488 if (r
->type
!= ACPI_TYPE_INTEGER
) {
493 status
= r
->integer
.value
;
495 if (status
!= 80 && status
!= 100)
498 return snprintf(buffer
, PAGE_SIZE
, "%d\n", status
);
501 static DEVICE_ATTR_RW(fan_mode
);
502 static DEVICE_ATTR_RW(usb_charge
);
503 static DEVICE_ATTR_RW(reader_mode
);
504 static DEVICE_ATTR_RW(fn_lock
);
505 static DEVICE_ATTR_RW(battery_care_limit
);
507 static struct attribute
*dev_attributes
[] = {
508 &dev_attr_fan_mode
.attr
,
509 &dev_attr_usb_charge
.attr
,
510 &dev_attr_reader_mode
.attr
,
511 &dev_attr_fn_lock
.attr
,
512 &dev_attr_battery_care_limit
.attr
,
516 static const struct attribute_group dev_attribute_group
= {
517 .attrs
= dev_attributes
,
520 static void tpad_led_set(struct led_classdev
*cdev
,
521 enum led_brightness brightness
)
523 union acpi_object
*r
;
525 r
= lg_wmab(WM_TLED
, WM_SET
, brightness
> LED_OFF
);
529 static enum led_brightness
tpad_led_get(struct led_classdev
*cdev
)
531 return ggov(GOV_TLED
) > 0 ? LED_ON
: LED_OFF
;
534 static LED_DEVICE(tpad_led
, 1);
536 static void kbd_backlight_set(struct led_classdev
*cdev
,
537 enum led_brightness brightness
)
540 union acpi_object
*r
;
543 if (brightness
<= LED_OFF
)
545 if (brightness
>= LED_FULL
)
547 r
= lg_wmab(WM_KEY_LIGHT
, WM_SET
, val
);
551 static enum led_brightness
kbd_backlight_get(struct led_classdev
*cdev
)
553 union acpi_object
*r
;
556 r
= lg_wmab(WM_KEY_LIGHT
, WM_GET
, 0);
561 if (r
->type
!= ACPI_TYPE_BUFFER
|| r
->buffer
.pointer
[1] != 0x05) {
566 switch (r
->buffer
.pointer
[0] & 0x27) {
582 static LED_DEVICE(kbd_backlight
, 255);
584 static void wmi_input_destroy(void)
586 if (inited
& INIT_INPUT_WMI_2
)
587 wmi_remove_notify_handler(WMI_EVENT_GUID2
);
589 if (inited
& INIT_INPUT_WMI_0
)
590 wmi_remove_notify_handler(WMI_EVENT_GUID0
);
592 if (inited
& INIT_SPARSE_KEYMAP
)
593 input_unregister_device(wmi_input_dev
);
595 inited
&= ~(INIT_INPUT_WMI_0
| INIT_INPUT_WMI_2
| INIT_SPARSE_KEYMAP
);
598 static struct platform_driver pf_driver
= {
600 .name
= PLATFORM_NAME
,
604 static int acpi_add(struct acpi_device
*device
)
611 ret
= platform_driver_register(&pf_driver
);
615 pf_device
= platform_device_register_simple(PLATFORM_NAME
,
618 if (IS_ERR(pf_device
)) {
619 ret
= PTR_ERR(pf_device
);
621 pr_err("unable to register platform device\n");
622 goto out_platform_registered
;
625 ret
= sysfs_create_group(&pf_device
->dev
.kobj
, &dev_attribute_group
);
627 goto out_platform_device
;
629 if (!led_classdev_register(&pf_device
->dev
, &kbd_backlight
))
630 inited
|= INIT_KBD_LED
;
632 if (!led_classdev_register(&pf_device
->dev
, &tpad_led
))
633 inited
|= INIT_TPAD_LED
;
640 platform_device_unregister(pf_device
);
641 out_platform_registered
:
642 platform_driver_unregister(&pf_driver
);
646 static int acpi_remove(struct acpi_device
*device
)
648 sysfs_remove_group(&pf_device
->dev
.kobj
, &dev_attribute_group
);
649 if (inited
& INIT_KBD_LED
)
650 led_classdev_unregister(&kbd_backlight
);
652 if (inited
& INIT_TPAD_LED
)
653 led_classdev_unregister(&tpad_led
);
656 platform_device_unregister(pf_device
);
658 platform_driver_unregister(&pf_driver
);
663 static const struct acpi_device_id device_ids
[] = {
667 MODULE_DEVICE_TABLE(acpi
, device_ids
);
669 static struct acpi_driver acpi_driver
= {
670 .name
= "LG Gram Laptop Support",
671 .class = "lg-laptop",
675 .remove
= acpi_remove
,
676 .notify
= acpi_notify
,
678 .owner
= THIS_MODULE
,
681 static int __init
acpi_init(void)
685 result
= acpi_bus_register_driver(&acpi_driver
);
687 ACPI_DEBUG_PRINT((ACPI_DB_ERROR
, "Error registering driver\n"));
694 static void __exit
acpi_exit(void)
696 acpi_bus_unregister_driver(&acpi_driver
);
699 module_init(acpi_init
);
700 module_exit(acpi_exit
);