1dbf0c5a6SMatan Ziv-Av // SPDX-License-Identifier: GPL-2.0+
2dbf0c5a6SMatan Ziv-Av /*
3dbf0c5a6SMatan Ziv-Av * lg-laptop.c - LG Gram ACPI features and hotkeys Driver
4dbf0c5a6SMatan Ziv-Av *
5dbf0c5a6SMatan Ziv-Av * Copyright (C) 2018 Matan Ziv-Av <matan@svgalib.org>
6dbf0c5a6SMatan Ziv-Av */
7dbf0c5a6SMatan Ziv-Av
8dbf0c5a6SMatan Ziv-Av #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
9dbf0c5a6SMatan Ziv-Av
10dbf0c5a6SMatan Ziv-Av #include <linux/acpi.h>
118983bfd5SMatan Ziv-Av #include <linux/dmi.h>
12dbf0c5a6SMatan Ziv-Av #include <linux/input.h>
13dbf0c5a6SMatan Ziv-Av #include <linux/input/sparse-keymap.h>
14dbf0c5a6SMatan Ziv-Av #include <linux/kernel.h>
15dbf0c5a6SMatan Ziv-Av #include <linux/leds.h>
16dbf0c5a6SMatan Ziv-Av #include <linux/module.h>
17dbf0c5a6SMatan Ziv-Av #include <linux/platform_device.h>
18dbf0c5a6SMatan Ziv-Av #include <linux/types.h>
19dbf0c5a6SMatan Ziv-Av
2007f5ed0eSMatan Ziv-Av #include <acpi/battery.h>
2107f5ed0eSMatan Ziv-Av
22ae262788SMatan Ziv-Av #define LED_DEVICE(_name, max, flag) struct led_classdev _name = { \
23dbf0c5a6SMatan Ziv-Av .name = __stringify(_name), \
24dbf0c5a6SMatan Ziv-Av .max_brightness = max, \
25dbf0c5a6SMatan Ziv-Av .brightness_set = _name##_set, \
26dbf0c5a6SMatan Ziv-Av .brightness_get = _name##_get, \
27ae262788SMatan Ziv-Av .flags = flag, \
28dbf0c5a6SMatan Ziv-Av }
29dbf0c5a6SMatan Ziv-Av
30dbf0c5a6SMatan Ziv-Av MODULE_AUTHOR("Matan Ziv-Av");
31dbf0c5a6SMatan Ziv-Av MODULE_DESCRIPTION("LG WMI Hotkey Driver");
32dbf0c5a6SMatan Ziv-Av MODULE_LICENSE("GPL");
33dbf0c5a6SMatan Ziv-Av
34dbf0c5a6SMatan Ziv-Av #define WMI_EVENT_GUID0 "E4FB94F9-7F2B-4173-AD1A-CD1D95086248"
35dbf0c5a6SMatan Ziv-Av #define WMI_EVENT_GUID1 "023B133E-49D1-4E10-B313-698220140DC2"
36dbf0c5a6SMatan Ziv-Av #define WMI_EVENT_GUID2 "37BE1AC0-C3F2-4B1F-BFBE-8FDEAF2814D6"
37dbf0c5a6SMatan Ziv-Av #define WMI_EVENT_GUID3 "911BAD44-7DF8-4FBB-9319-BABA1C4B293B"
38dbf0c5a6SMatan Ziv-Av #define WMI_METHOD_WMAB "C3A72B38-D3EF-42D3-8CBB-D5A57049F66D"
39dbf0c5a6SMatan Ziv-Av #define WMI_METHOD_WMBB "2B4F501A-BD3C-4394-8DCF-00A7D2BC8210"
40dbf0c5a6SMatan Ziv-Av #define WMI_EVENT_GUID WMI_EVENT_GUID0
41dbf0c5a6SMatan Ziv-Av
42dbf0c5a6SMatan Ziv-Av #define SB_GGOV_METHOD "\\_SB.GGOV"
43dbf0c5a6SMatan Ziv-Av #define GOV_TLED 0x2020008
44dbf0c5a6SMatan Ziv-Av #define WM_GET 1
45dbf0c5a6SMatan Ziv-Av #define WM_SET 2
46dbf0c5a6SMatan Ziv-Av #define WM_KEY_LIGHT 0x400
47dbf0c5a6SMatan Ziv-Av #define WM_TLED 0x404
48dbf0c5a6SMatan Ziv-Av #define WM_FN_LOCK 0x407
49dbf0c5a6SMatan Ziv-Av #define WM_BATT_LIMIT 0x61
50dbf0c5a6SMatan Ziv-Av #define WM_READER_MODE 0xBF
51dbf0c5a6SMatan Ziv-Av #define WM_FAN_MODE 0x33
52dbf0c5a6SMatan Ziv-Av #define WMBB_USB_CHARGE 0x10B
53dbf0c5a6SMatan Ziv-Av #define WMBB_BATT_LIMIT 0x10C
54dbf0c5a6SMatan Ziv-Av
55dbf0c5a6SMatan Ziv-Av #define PLATFORM_NAME "lg-laptop"
56dbf0c5a6SMatan Ziv-Av
57dbf0c5a6SMatan Ziv-Av MODULE_ALIAS("wmi:" WMI_EVENT_GUID0);
58dbf0c5a6SMatan Ziv-Av MODULE_ALIAS("wmi:" WMI_EVENT_GUID1);
59dbf0c5a6SMatan Ziv-Av MODULE_ALIAS("wmi:" WMI_EVENT_GUID2);
60dbf0c5a6SMatan Ziv-Av MODULE_ALIAS("wmi:" WMI_EVENT_GUID3);
61dbf0c5a6SMatan Ziv-Av MODULE_ALIAS("wmi:" WMI_METHOD_WMAB);
62dbf0c5a6SMatan Ziv-Av MODULE_ALIAS("wmi:" WMI_METHOD_WMBB);
63dbf0c5a6SMatan Ziv-Av
64dbf0c5a6SMatan Ziv-Av static struct platform_device *pf_device;
65dbf0c5a6SMatan Ziv-Av static struct input_dev *wmi_input_dev;
66dbf0c5a6SMatan Ziv-Av
67dbf0c5a6SMatan Ziv-Av static u32 inited;
68dbf0c5a6SMatan Ziv-Av #define INIT_INPUT_WMI_0 0x01
69dbf0c5a6SMatan Ziv-Av #define INIT_INPUT_WMI_2 0x02
70dbf0c5a6SMatan Ziv-Av #define INIT_INPUT_ACPI 0x04
71dbf0c5a6SMatan Ziv-Av #define INIT_SPARSE_KEYMAP 0x80
72dbf0c5a6SMatan Ziv-Av
738983bfd5SMatan Ziv-Av static int battery_limit_use_wmbb;
74ae262788SMatan Ziv-Av static struct led_classdev kbd_backlight;
759f25bd70SArmin Wolf static enum led_brightness get_kbd_backlight_level(struct device *dev);
768983bfd5SMatan Ziv-Av
77dbf0c5a6SMatan Ziv-Av static const struct key_entry wmi_keymap[] = {
78dbf0c5a6SMatan Ziv-Av {KE_KEY, 0x70, {KEY_F15} }, /* LG control panel (F1) */
7985973bf4SMatan Ziv-Av {KE_KEY, 0x74, {KEY_F21} }, /* Touchpad toggle (F5) */
80dbf0c5a6SMatan Ziv-Av {KE_KEY, 0xf020000, {KEY_F14} }, /* Read mode (F9) */
81dbf0c5a6SMatan Ziv-Av {KE_KEY, 0x10000000, {KEY_F16} },/* Keyboard backlight (F8) - pressing
82dbf0c5a6SMatan Ziv-Av * this key both sends an event and
83dbf0c5a6SMatan Ziv-Av * changes backlight level.
84dbf0c5a6SMatan Ziv-Av */
85dbf0c5a6SMatan Ziv-Av {KE_END, 0}
86dbf0c5a6SMatan Ziv-Av };
87dbf0c5a6SMatan Ziv-Av
ggov(u32 arg0)88dbf0c5a6SMatan Ziv-Av static int ggov(u32 arg0)
89dbf0c5a6SMatan Ziv-Av {
90dbf0c5a6SMatan Ziv-Av union acpi_object args[1];
91dbf0c5a6SMatan Ziv-Av union acpi_object *r;
92dbf0c5a6SMatan Ziv-Av acpi_status status;
93dbf0c5a6SMatan Ziv-Av acpi_handle handle;
94dbf0c5a6SMatan Ziv-Av struct acpi_object_list arg;
95dbf0c5a6SMatan Ziv-Av struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
96dbf0c5a6SMatan Ziv-Av int res;
97dbf0c5a6SMatan Ziv-Av
98dbf0c5a6SMatan Ziv-Av args[0].type = ACPI_TYPE_INTEGER;
99dbf0c5a6SMatan Ziv-Av args[0].integer.value = arg0;
100dbf0c5a6SMatan Ziv-Av
101dbf0c5a6SMatan Ziv-Av status = acpi_get_handle(NULL, (acpi_string) SB_GGOV_METHOD, &handle);
102dbf0c5a6SMatan Ziv-Av if (ACPI_FAILURE(status)) {
103dbf0c5a6SMatan Ziv-Av pr_err("Cannot get handle");
104dbf0c5a6SMatan Ziv-Av return -ENODEV;
105dbf0c5a6SMatan Ziv-Av }
106dbf0c5a6SMatan Ziv-Av
107dbf0c5a6SMatan Ziv-Av arg.count = 1;
108dbf0c5a6SMatan Ziv-Av arg.pointer = args;
109dbf0c5a6SMatan Ziv-Av
110dbf0c5a6SMatan Ziv-Av status = acpi_evaluate_object(handle, NULL, &arg, &buffer);
111dbf0c5a6SMatan Ziv-Av if (ACPI_FAILURE(status)) {
112dbf0c5a6SMatan Ziv-Av acpi_handle_err(handle, "GGOV: call failed.\n");
113dbf0c5a6SMatan Ziv-Av return -EINVAL;
114dbf0c5a6SMatan Ziv-Av }
115dbf0c5a6SMatan Ziv-Av
116dbf0c5a6SMatan Ziv-Av r = buffer.pointer;
117dbf0c5a6SMatan Ziv-Av if (r->type != ACPI_TYPE_INTEGER) {
118dbf0c5a6SMatan Ziv-Av kfree(r);
119dbf0c5a6SMatan Ziv-Av return -EINVAL;
120dbf0c5a6SMatan Ziv-Av }
121dbf0c5a6SMatan Ziv-Av
122dbf0c5a6SMatan Ziv-Av res = r->integer.value;
123dbf0c5a6SMatan Ziv-Av kfree(r);
124dbf0c5a6SMatan Ziv-Av
125dbf0c5a6SMatan Ziv-Av return res;
126dbf0c5a6SMatan Ziv-Av }
127dbf0c5a6SMatan Ziv-Av
lg_wmab(struct device * dev,u32 method,u32 arg1,u32 arg2)1289f25bd70SArmin Wolf static union acpi_object *lg_wmab(struct device *dev, u32 method, u32 arg1, u32 arg2)
129dbf0c5a6SMatan Ziv-Av {
130dbf0c5a6SMatan Ziv-Av union acpi_object args[3];
131dbf0c5a6SMatan Ziv-Av acpi_status status;
132dbf0c5a6SMatan Ziv-Av struct acpi_object_list arg;
133dbf0c5a6SMatan Ziv-Av struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
134dbf0c5a6SMatan Ziv-Av
135dbf0c5a6SMatan Ziv-Av args[0].type = ACPI_TYPE_INTEGER;
136dbf0c5a6SMatan Ziv-Av args[0].integer.value = method;
137dbf0c5a6SMatan Ziv-Av args[1].type = ACPI_TYPE_INTEGER;
138dbf0c5a6SMatan Ziv-Av args[1].integer.value = arg1;
139dbf0c5a6SMatan Ziv-Av args[2].type = ACPI_TYPE_INTEGER;
140dbf0c5a6SMatan Ziv-Av args[2].integer.value = arg2;
141dbf0c5a6SMatan Ziv-Av
142dbf0c5a6SMatan Ziv-Av arg.count = 3;
143dbf0c5a6SMatan Ziv-Av arg.pointer = args;
144dbf0c5a6SMatan Ziv-Av
1459f25bd70SArmin Wolf status = acpi_evaluate_object(ACPI_HANDLE(dev), "WMAB", &arg, &buffer);
146dbf0c5a6SMatan Ziv-Av if (ACPI_FAILURE(status)) {
1479f25bd70SArmin Wolf dev_err(dev, "WMAB: call failed.\n");
148dbf0c5a6SMatan Ziv-Av return NULL;
149dbf0c5a6SMatan Ziv-Av }
150dbf0c5a6SMatan Ziv-Av
151dbf0c5a6SMatan Ziv-Av return buffer.pointer;
152dbf0c5a6SMatan Ziv-Av }
153dbf0c5a6SMatan Ziv-Av
lg_wmbb(struct device * dev,u32 method_id,u32 arg1,u32 arg2)1549f25bd70SArmin Wolf static union acpi_object *lg_wmbb(struct device *dev, u32 method_id, u32 arg1, u32 arg2)
155dbf0c5a6SMatan Ziv-Av {
156dbf0c5a6SMatan Ziv-Av union acpi_object args[3];
157dbf0c5a6SMatan Ziv-Av acpi_status status;
158dbf0c5a6SMatan Ziv-Av struct acpi_object_list arg;
159dbf0c5a6SMatan Ziv-Av struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
160dbf0c5a6SMatan Ziv-Av u8 buf[32];
161dbf0c5a6SMatan Ziv-Av
162dbf0c5a6SMatan Ziv-Av *(u32 *)buf = method_id;
163dbf0c5a6SMatan Ziv-Av *(u32 *)(buf + 4) = arg1;
164dbf0c5a6SMatan Ziv-Av *(u32 *)(buf + 16) = arg2;
165dbf0c5a6SMatan Ziv-Av args[0].type = ACPI_TYPE_INTEGER;
166dbf0c5a6SMatan Ziv-Av args[0].integer.value = 0; /* ignored */
167dbf0c5a6SMatan Ziv-Av args[1].type = ACPI_TYPE_INTEGER;
168dbf0c5a6SMatan Ziv-Av args[1].integer.value = 1; /* Must be 1 or 2. Does not matter which */
169dbf0c5a6SMatan Ziv-Av args[2].type = ACPI_TYPE_BUFFER;
170dbf0c5a6SMatan Ziv-Av args[2].buffer.length = 32;
171dbf0c5a6SMatan Ziv-Av args[2].buffer.pointer = buf;
172dbf0c5a6SMatan Ziv-Av
173dbf0c5a6SMatan Ziv-Av arg.count = 3;
174dbf0c5a6SMatan Ziv-Av arg.pointer = args;
175dbf0c5a6SMatan Ziv-Av
1769f25bd70SArmin Wolf status = acpi_evaluate_object(ACPI_HANDLE(dev), "WMBB", &arg, &buffer);
177dbf0c5a6SMatan Ziv-Av if (ACPI_FAILURE(status)) {
1789f25bd70SArmin Wolf dev_err(dev, "WMBB: call failed.\n");
179dbf0c5a6SMatan Ziv-Av return NULL;
180dbf0c5a6SMatan Ziv-Av }
181dbf0c5a6SMatan Ziv-Av
182dbf0c5a6SMatan Ziv-Av return (union acpi_object *)buffer.pointer;
183dbf0c5a6SMatan Ziv-Av }
184dbf0c5a6SMatan Ziv-Av
wmi_notify(u32 value,void * context)185dbf0c5a6SMatan Ziv-Av static void wmi_notify(u32 value, void *context)
186dbf0c5a6SMatan Ziv-Av {
187dbf0c5a6SMatan Ziv-Av struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
188dbf0c5a6SMatan Ziv-Av union acpi_object *obj;
189dbf0c5a6SMatan Ziv-Av acpi_status status;
190dbf0c5a6SMatan Ziv-Av long data = (long)context;
191dbf0c5a6SMatan Ziv-Av
192dbf0c5a6SMatan Ziv-Av pr_debug("event guid %li\n", data);
193dbf0c5a6SMatan Ziv-Av status = wmi_get_event_data(value, &response);
194dbf0c5a6SMatan Ziv-Av if (ACPI_FAILURE(status)) {
195dbf0c5a6SMatan Ziv-Av pr_err("Bad event status 0x%x\n", status);
196dbf0c5a6SMatan Ziv-Av return;
197dbf0c5a6SMatan Ziv-Av }
198dbf0c5a6SMatan Ziv-Av
199dbf0c5a6SMatan Ziv-Av obj = (union acpi_object *)response.pointer;
200dbf0c5a6SMatan Ziv-Av if (!obj)
201dbf0c5a6SMatan Ziv-Av return;
202dbf0c5a6SMatan Ziv-Av
203dbf0c5a6SMatan Ziv-Av if (obj->type == ACPI_TYPE_INTEGER) {
204dbf0c5a6SMatan Ziv-Av int eventcode = obj->integer.value;
205dbf0c5a6SMatan Ziv-Av struct key_entry *key;
206dbf0c5a6SMatan Ziv-Av
207ae262788SMatan Ziv-Av if (eventcode == 0x10000000) {
208ae262788SMatan Ziv-Av led_classdev_notify_brightness_hw_changed(
2099f25bd70SArmin Wolf &kbd_backlight, get_kbd_backlight_level(kbd_backlight.dev->parent));
210ae262788SMatan Ziv-Av } else {
211ae262788SMatan Ziv-Av key = sparse_keymap_entry_from_scancode(
212ae262788SMatan Ziv-Av wmi_input_dev, eventcode);
213dbf0c5a6SMatan Ziv-Av if (key && key->type == KE_KEY)
214ae262788SMatan Ziv-Av sparse_keymap_report_entry(wmi_input_dev,
215ae262788SMatan Ziv-Av key, 1, true);
216ae262788SMatan Ziv-Av }
217dbf0c5a6SMatan Ziv-Av }
218dbf0c5a6SMatan Ziv-Av
219dbf0c5a6SMatan Ziv-Av pr_debug("Type: %i Eventcode: 0x%llx\n", obj->type,
220dbf0c5a6SMatan Ziv-Av obj->integer.value);
221dbf0c5a6SMatan Ziv-Av kfree(response.pointer);
222dbf0c5a6SMatan Ziv-Av }
223dbf0c5a6SMatan Ziv-Av
wmi_input_setup(void)224dbf0c5a6SMatan Ziv-Av static void wmi_input_setup(void)
225dbf0c5a6SMatan Ziv-Av {
226dbf0c5a6SMatan Ziv-Av acpi_status status;
227dbf0c5a6SMatan Ziv-Av
228dbf0c5a6SMatan Ziv-Av wmi_input_dev = input_allocate_device();
229dbf0c5a6SMatan Ziv-Av if (wmi_input_dev) {
230dbf0c5a6SMatan Ziv-Av wmi_input_dev->name = "LG WMI hotkeys";
231dbf0c5a6SMatan Ziv-Av wmi_input_dev->phys = "wmi/input0";
232dbf0c5a6SMatan Ziv-Av wmi_input_dev->id.bustype = BUS_HOST;
233dbf0c5a6SMatan Ziv-Av
234dbf0c5a6SMatan Ziv-Av if (sparse_keymap_setup(wmi_input_dev, wmi_keymap, NULL) ||
235dbf0c5a6SMatan Ziv-Av input_register_device(wmi_input_dev)) {
236dbf0c5a6SMatan Ziv-Av pr_info("Cannot initialize input device");
237dbf0c5a6SMatan Ziv-Av input_free_device(wmi_input_dev);
238dbf0c5a6SMatan Ziv-Av return;
239dbf0c5a6SMatan Ziv-Av }
240dbf0c5a6SMatan Ziv-Av
241dbf0c5a6SMatan Ziv-Av inited |= INIT_SPARSE_KEYMAP;
242dbf0c5a6SMatan Ziv-Av status = wmi_install_notify_handler(WMI_EVENT_GUID0, wmi_notify,
243dbf0c5a6SMatan Ziv-Av (void *)0);
244dbf0c5a6SMatan Ziv-Av if (ACPI_SUCCESS(status))
245dbf0c5a6SMatan Ziv-Av inited |= INIT_INPUT_WMI_0;
246dbf0c5a6SMatan Ziv-Av
247dbf0c5a6SMatan Ziv-Av status = wmi_install_notify_handler(WMI_EVENT_GUID2, wmi_notify,
248dbf0c5a6SMatan Ziv-Av (void *)2);
249dbf0c5a6SMatan Ziv-Av if (ACPI_SUCCESS(status))
250dbf0c5a6SMatan Ziv-Av inited |= INIT_INPUT_WMI_2;
251dbf0c5a6SMatan Ziv-Av } else {
252dbf0c5a6SMatan Ziv-Av pr_info("Cannot allocate input device");
253dbf0c5a6SMatan Ziv-Av }
254dbf0c5a6SMatan Ziv-Av }
255dbf0c5a6SMatan Ziv-Av
acpi_notify(struct acpi_device * device,u32 event)256dbf0c5a6SMatan Ziv-Av static void acpi_notify(struct acpi_device *device, u32 event)
257dbf0c5a6SMatan Ziv-Av {
258dbf0c5a6SMatan Ziv-Av acpi_handle_debug(device->handle, "notify: %d\n", event);
259dbf0c5a6SMatan Ziv-Av }
260dbf0c5a6SMatan Ziv-Av
fan_mode_store(struct device * dev,struct device_attribute * attr,const char * buffer,size_t count)261dbf0c5a6SMatan Ziv-Av static ssize_t fan_mode_store(struct device *dev,
262dbf0c5a6SMatan Ziv-Av struct device_attribute *attr,
263dbf0c5a6SMatan Ziv-Av const char *buffer, size_t count)
264dbf0c5a6SMatan Ziv-Av {
265dbf0c5a6SMatan Ziv-Av bool value;
266dbf0c5a6SMatan Ziv-Av union acpi_object *r;
267dbf0c5a6SMatan Ziv-Av u32 m;
268dbf0c5a6SMatan Ziv-Av int ret;
269dbf0c5a6SMatan Ziv-Av
270dbf0c5a6SMatan Ziv-Av ret = kstrtobool(buffer, &value);
271dbf0c5a6SMatan Ziv-Av if (ret)
272dbf0c5a6SMatan Ziv-Av return ret;
273dbf0c5a6SMatan Ziv-Av
2749f25bd70SArmin Wolf r = lg_wmab(dev, WM_FAN_MODE, WM_GET, 0);
275dbf0c5a6SMatan Ziv-Av if (!r)
276dbf0c5a6SMatan Ziv-Av return -EIO;
277dbf0c5a6SMatan Ziv-Av
278dbf0c5a6SMatan Ziv-Av if (r->type != ACPI_TYPE_INTEGER) {
279dbf0c5a6SMatan Ziv-Av kfree(r);
280dbf0c5a6SMatan Ziv-Av return -EIO;
281dbf0c5a6SMatan Ziv-Av }
282dbf0c5a6SMatan Ziv-Av
283dbf0c5a6SMatan Ziv-Av m = r->integer.value;
284dbf0c5a6SMatan Ziv-Av kfree(r);
2859f25bd70SArmin Wolf r = lg_wmab(dev, WM_FAN_MODE, WM_SET, (m & 0xffffff0f) | (value << 4));
286dbf0c5a6SMatan Ziv-Av kfree(r);
2879f25bd70SArmin Wolf r = lg_wmab(dev, WM_FAN_MODE, WM_SET, (m & 0xfffffff0) | value);
288dbf0c5a6SMatan Ziv-Av kfree(r);
289dbf0c5a6SMatan Ziv-Av
290dbf0c5a6SMatan Ziv-Av return count;
291dbf0c5a6SMatan Ziv-Av }
292dbf0c5a6SMatan Ziv-Av
fan_mode_show(struct device * dev,struct device_attribute * attr,char * buffer)293dbf0c5a6SMatan Ziv-Av static ssize_t fan_mode_show(struct device *dev,
294dbf0c5a6SMatan Ziv-Av struct device_attribute *attr, char *buffer)
295dbf0c5a6SMatan Ziv-Av {
296dbf0c5a6SMatan Ziv-Av unsigned int status;
297dbf0c5a6SMatan Ziv-Av union acpi_object *r;
298dbf0c5a6SMatan Ziv-Av
2999f25bd70SArmin Wolf r = lg_wmab(dev, WM_FAN_MODE, WM_GET, 0);
300dbf0c5a6SMatan Ziv-Av if (!r)
301dbf0c5a6SMatan Ziv-Av return -EIO;
302dbf0c5a6SMatan Ziv-Av
303dbf0c5a6SMatan Ziv-Av if (r->type != ACPI_TYPE_INTEGER) {
304dbf0c5a6SMatan Ziv-Av kfree(r);
305dbf0c5a6SMatan Ziv-Av return -EIO;
306dbf0c5a6SMatan Ziv-Av }
307dbf0c5a6SMatan Ziv-Av
308dbf0c5a6SMatan Ziv-Av status = r->integer.value & 0x01;
309dbf0c5a6SMatan Ziv-Av kfree(r);
310dbf0c5a6SMatan Ziv-Av
31121d91e20SYe Guojin return sysfs_emit(buffer, "%d\n", status);
312dbf0c5a6SMatan Ziv-Av }
313dbf0c5a6SMatan Ziv-Av
usb_charge_store(struct device * dev,struct device_attribute * attr,const char * buffer,size_t count)314dbf0c5a6SMatan Ziv-Av static ssize_t usb_charge_store(struct device *dev,
315dbf0c5a6SMatan Ziv-Av struct device_attribute *attr,
316dbf0c5a6SMatan Ziv-Av const char *buffer, size_t count)
317dbf0c5a6SMatan Ziv-Av {
318dbf0c5a6SMatan Ziv-Av bool value;
319dbf0c5a6SMatan Ziv-Av union acpi_object *r;
320dbf0c5a6SMatan Ziv-Av int ret;
321dbf0c5a6SMatan Ziv-Av
322dbf0c5a6SMatan Ziv-Av ret = kstrtobool(buffer, &value);
323dbf0c5a6SMatan Ziv-Av if (ret)
324dbf0c5a6SMatan Ziv-Av return ret;
325dbf0c5a6SMatan Ziv-Av
3269f25bd70SArmin Wolf r = lg_wmbb(dev, WMBB_USB_CHARGE, WM_SET, value);
327dbf0c5a6SMatan Ziv-Av if (!r)
328dbf0c5a6SMatan Ziv-Av return -EIO;
329dbf0c5a6SMatan Ziv-Av
330dbf0c5a6SMatan Ziv-Av kfree(r);
331dbf0c5a6SMatan Ziv-Av return count;
332dbf0c5a6SMatan Ziv-Av }
333dbf0c5a6SMatan Ziv-Av
usb_charge_show(struct device * dev,struct device_attribute * attr,char * buffer)334dbf0c5a6SMatan Ziv-Av static ssize_t usb_charge_show(struct device *dev,
335dbf0c5a6SMatan Ziv-Av struct device_attribute *attr, char *buffer)
336dbf0c5a6SMatan Ziv-Av {
337dbf0c5a6SMatan Ziv-Av unsigned int status;
338dbf0c5a6SMatan Ziv-Av union acpi_object *r;
339dbf0c5a6SMatan Ziv-Av
3409f25bd70SArmin Wolf r = lg_wmbb(dev, WMBB_USB_CHARGE, WM_GET, 0);
341dbf0c5a6SMatan Ziv-Av if (!r)
342dbf0c5a6SMatan Ziv-Av return -EIO;
343dbf0c5a6SMatan Ziv-Av
344dbf0c5a6SMatan Ziv-Av if (r->type != ACPI_TYPE_BUFFER) {
345dbf0c5a6SMatan Ziv-Av kfree(r);
346dbf0c5a6SMatan Ziv-Av return -EIO;
347dbf0c5a6SMatan Ziv-Av }
348dbf0c5a6SMatan Ziv-Av
349dbf0c5a6SMatan Ziv-Av status = !!r->buffer.pointer[0x10];
350dbf0c5a6SMatan Ziv-Av
351dbf0c5a6SMatan Ziv-Av kfree(r);
352dbf0c5a6SMatan Ziv-Av
35321d91e20SYe Guojin return sysfs_emit(buffer, "%d\n", status);
354dbf0c5a6SMatan Ziv-Av }
355dbf0c5a6SMatan Ziv-Av
reader_mode_store(struct device * dev,struct device_attribute * attr,const char * buffer,size_t count)356dbf0c5a6SMatan Ziv-Av static ssize_t reader_mode_store(struct device *dev,
357dbf0c5a6SMatan Ziv-Av struct device_attribute *attr,
358dbf0c5a6SMatan Ziv-Av const char *buffer, size_t count)
359dbf0c5a6SMatan Ziv-Av {
360dbf0c5a6SMatan Ziv-Av bool value;
361dbf0c5a6SMatan Ziv-Av union acpi_object *r;
362dbf0c5a6SMatan Ziv-Av int ret;
363dbf0c5a6SMatan Ziv-Av
364dbf0c5a6SMatan Ziv-Av ret = kstrtobool(buffer, &value);
365dbf0c5a6SMatan Ziv-Av if (ret)
366dbf0c5a6SMatan Ziv-Av return ret;
367dbf0c5a6SMatan Ziv-Av
3689f25bd70SArmin Wolf r = lg_wmab(dev, WM_READER_MODE, WM_SET, value);
369dbf0c5a6SMatan Ziv-Av if (!r)
370dbf0c5a6SMatan Ziv-Av return -EIO;
371dbf0c5a6SMatan Ziv-Av
372dbf0c5a6SMatan Ziv-Av kfree(r);
373dbf0c5a6SMatan Ziv-Av return count;
374dbf0c5a6SMatan Ziv-Av }
375dbf0c5a6SMatan Ziv-Av
reader_mode_show(struct device * dev,struct device_attribute * attr,char * buffer)376dbf0c5a6SMatan Ziv-Av static ssize_t reader_mode_show(struct device *dev,
377dbf0c5a6SMatan Ziv-Av struct device_attribute *attr, char *buffer)
378dbf0c5a6SMatan Ziv-Av {
379dbf0c5a6SMatan Ziv-Av unsigned int status;
380dbf0c5a6SMatan Ziv-Av union acpi_object *r;
381dbf0c5a6SMatan Ziv-Av
3829f25bd70SArmin Wolf r = lg_wmab(dev, WM_READER_MODE, WM_GET, 0);
383dbf0c5a6SMatan Ziv-Av if (!r)
384dbf0c5a6SMatan Ziv-Av return -EIO;
385dbf0c5a6SMatan Ziv-Av
386dbf0c5a6SMatan Ziv-Av if (r->type != ACPI_TYPE_INTEGER) {
387dbf0c5a6SMatan Ziv-Av kfree(r);
388dbf0c5a6SMatan Ziv-Av return -EIO;
389dbf0c5a6SMatan Ziv-Av }
390dbf0c5a6SMatan Ziv-Av
391dbf0c5a6SMatan Ziv-Av status = !!r->integer.value;
392dbf0c5a6SMatan Ziv-Av
393dbf0c5a6SMatan Ziv-Av kfree(r);
394dbf0c5a6SMatan Ziv-Av
39521d91e20SYe Guojin return sysfs_emit(buffer, "%d\n", status);
396dbf0c5a6SMatan Ziv-Av }
397dbf0c5a6SMatan Ziv-Av
fn_lock_store(struct device * dev,struct device_attribute * attr,const char * buffer,size_t count)398dbf0c5a6SMatan Ziv-Av static ssize_t fn_lock_store(struct device *dev,
399dbf0c5a6SMatan Ziv-Av struct device_attribute *attr,
400dbf0c5a6SMatan Ziv-Av const char *buffer, size_t count)
401dbf0c5a6SMatan Ziv-Av {
402dbf0c5a6SMatan Ziv-Av bool value;
403dbf0c5a6SMatan Ziv-Av union acpi_object *r;
404dbf0c5a6SMatan Ziv-Av int ret;
405dbf0c5a6SMatan Ziv-Av
406dbf0c5a6SMatan Ziv-Av ret = kstrtobool(buffer, &value);
407dbf0c5a6SMatan Ziv-Av if (ret)
408dbf0c5a6SMatan Ziv-Av return ret;
409dbf0c5a6SMatan Ziv-Av
4109f25bd70SArmin Wolf r = lg_wmab(dev, WM_FN_LOCK, WM_SET, value);
411dbf0c5a6SMatan Ziv-Av if (!r)
412dbf0c5a6SMatan Ziv-Av return -EIO;
413dbf0c5a6SMatan Ziv-Av
414dbf0c5a6SMatan Ziv-Av kfree(r);
415dbf0c5a6SMatan Ziv-Av return count;
416dbf0c5a6SMatan Ziv-Av }
417dbf0c5a6SMatan Ziv-Av
fn_lock_show(struct device * dev,struct device_attribute * attr,char * buffer)418dbf0c5a6SMatan Ziv-Av static ssize_t fn_lock_show(struct device *dev,
419dbf0c5a6SMatan Ziv-Av struct device_attribute *attr, char *buffer)
420dbf0c5a6SMatan Ziv-Av {
421dbf0c5a6SMatan Ziv-Av unsigned int status;
422dbf0c5a6SMatan Ziv-Av union acpi_object *r;
423dbf0c5a6SMatan Ziv-Av
4249f25bd70SArmin Wolf r = lg_wmab(dev, WM_FN_LOCK, WM_GET, 0);
425dbf0c5a6SMatan Ziv-Av if (!r)
426dbf0c5a6SMatan Ziv-Av return -EIO;
427dbf0c5a6SMatan Ziv-Av
428dbf0c5a6SMatan Ziv-Av if (r->type != ACPI_TYPE_BUFFER) {
429dbf0c5a6SMatan Ziv-Av kfree(r);
430dbf0c5a6SMatan Ziv-Av return -EIO;
431dbf0c5a6SMatan Ziv-Av }
432dbf0c5a6SMatan Ziv-Av
433dbf0c5a6SMatan Ziv-Av status = !!r->buffer.pointer[0];
434dbf0c5a6SMatan Ziv-Av kfree(r);
435dbf0c5a6SMatan Ziv-Av
43621d91e20SYe Guojin return sysfs_emit(buffer, "%d\n", status);
437dbf0c5a6SMatan Ziv-Av }
438dbf0c5a6SMatan Ziv-Av
charge_control_end_threshold_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)43907f5ed0eSMatan Ziv-Av static ssize_t charge_control_end_threshold_store(struct device *dev,
440dbf0c5a6SMatan Ziv-Av struct device_attribute *attr,
44107f5ed0eSMatan Ziv-Av const char *buf, size_t count)
442dbf0c5a6SMatan Ziv-Av {
443dbf0c5a6SMatan Ziv-Av unsigned long value;
444dbf0c5a6SMatan Ziv-Av int ret;
445dbf0c5a6SMatan Ziv-Av
44607f5ed0eSMatan Ziv-Av ret = kstrtoul(buf, 10, &value);
447dbf0c5a6SMatan Ziv-Av if (ret)
448dbf0c5a6SMatan Ziv-Av return ret;
449dbf0c5a6SMatan Ziv-Av
450dbf0c5a6SMatan Ziv-Av if (value == 100 || value == 80) {
451dbf0c5a6SMatan Ziv-Av union acpi_object *r;
452dbf0c5a6SMatan Ziv-Av
4538983bfd5SMatan Ziv-Av if (battery_limit_use_wmbb)
4549f25bd70SArmin Wolf r = lg_wmbb(&pf_device->dev, WMBB_BATT_LIMIT, WM_SET, value);
4558983bfd5SMatan Ziv-Av else
4569f25bd70SArmin Wolf r = lg_wmab(&pf_device->dev, WM_BATT_LIMIT, WM_SET, value);
457dbf0c5a6SMatan Ziv-Av if (!r)
458dbf0c5a6SMatan Ziv-Av return -EIO;
459dbf0c5a6SMatan Ziv-Av
460dbf0c5a6SMatan Ziv-Av kfree(r);
461dbf0c5a6SMatan Ziv-Av return count;
462dbf0c5a6SMatan Ziv-Av }
463dbf0c5a6SMatan Ziv-Av
464dbf0c5a6SMatan Ziv-Av return -EINVAL;
465dbf0c5a6SMatan Ziv-Av }
466dbf0c5a6SMatan Ziv-Av
charge_control_end_threshold_show(struct device * device,struct device_attribute * attr,char * buf)46707f5ed0eSMatan Ziv-Av static ssize_t charge_control_end_threshold_show(struct device *device,
468dbf0c5a6SMatan Ziv-Av struct device_attribute *attr,
46907f5ed0eSMatan Ziv-Av char *buf)
470dbf0c5a6SMatan Ziv-Av {
471dbf0c5a6SMatan Ziv-Av unsigned int status;
472dbf0c5a6SMatan Ziv-Av union acpi_object *r;
473dbf0c5a6SMatan Ziv-Av
4748983bfd5SMatan Ziv-Av if (battery_limit_use_wmbb) {
4759f25bd70SArmin Wolf r = lg_wmbb(&pf_device->dev, WMBB_BATT_LIMIT, WM_GET, 0);
4768983bfd5SMatan Ziv-Av if (!r)
4778983bfd5SMatan Ziv-Av return -EIO;
4788983bfd5SMatan Ziv-Av
4798983bfd5SMatan Ziv-Av if (r->type != ACPI_TYPE_BUFFER) {
4808983bfd5SMatan Ziv-Av kfree(r);
4818983bfd5SMatan Ziv-Av return -EIO;
4828983bfd5SMatan Ziv-Av }
4838983bfd5SMatan Ziv-Av
4848983bfd5SMatan Ziv-Av status = r->buffer.pointer[0x10];
4858983bfd5SMatan Ziv-Av } else {
4869f25bd70SArmin Wolf r = lg_wmab(&pf_device->dev, WM_BATT_LIMIT, WM_GET, 0);
487dbf0c5a6SMatan Ziv-Av if (!r)
488dbf0c5a6SMatan Ziv-Av return -EIO;
489dbf0c5a6SMatan Ziv-Av
490dbf0c5a6SMatan Ziv-Av if (r->type != ACPI_TYPE_INTEGER) {
491dbf0c5a6SMatan Ziv-Av kfree(r);
492dbf0c5a6SMatan Ziv-Av return -EIO;
493dbf0c5a6SMatan Ziv-Av }
494dbf0c5a6SMatan Ziv-Av
495dbf0c5a6SMatan Ziv-Av status = r->integer.value;
4968983bfd5SMatan Ziv-Av }
497dbf0c5a6SMatan Ziv-Av kfree(r);
498dbf0c5a6SMatan Ziv-Av if (status != 80 && status != 100)
499dbf0c5a6SMatan Ziv-Av status = 0;
500dbf0c5a6SMatan Ziv-Av
50107f5ed0eSMatan Ziv-Av return sysfs_emit(buf, "%d\n", status);
50207f5ed0eSMatan Ziv-Av }
50307f5ed0eSMatan Ziv-Av
battery_care_limit_show(struct device * dev,struct device_attribute * attr,char * buffer)50407f5ed0eSMatan Ziv-Av static ssize_t battery_care_limit_show(struct device *dev,
50507f5ed0eSMatan Ziv-Av struct device_attribute *attr,
50607f5ed0eSMatan Ziv-Av char *buffer)
50707f5ed0eSMatan Ziv-Av {
50807f5ed0eSMatan Ziv-Av return charge_control_end_threshold_show(dev, attr, buffer);
50907f5ed0eSMatan Ziv-Av }
51007f5ed0eSMatan Ziv-Av
battery_care_limit_store(struct device * dev,struct device_attribute * attr,const char * buffer,size_t count)51107f5ed0eSMatan Ziv-Av static ssize_t battery_care_limit_store(struct device *dev,
51207f5ed0eSMatan Ziv-Av struct device_attribute *attr,
51307f5ed0eSMatan Ziv-Av const char *buffer, size_t count)
51407f5ed0eSMatan Ziv-Av {
51507f5ed0eSMatan Ziv-Av return charge_control_end_threshold_store(dev, attr, buffer, count);
516dbf0c5a6SMatan Ziv-Av }
517dbf0c5a6SMatan Ziv-Av
518dbf0c5a6SMatan Ziv-Av static DEVICE_ATTR_RW(fan_mode);
519dbf0c5a6SMatan Ziv-Av static DEVICE_ATTR_RW(usb_charge);
520dbf0c5a6SMatan Ziv-Av static DEVICE_ATTR_RW(reader_mode);
521dbf0c5a6SMatan Ziv-Av static DEVICE_ATTR_RW(fn_lock);
52207f5ed0eSMatan Ziv-Av static DEVICE_ATTR_RW(charge_control_end_threshold);
523dbf0c5a6SMatan Ziv-Av static DEVICE_ATTR_RW(battery_care_limit);
524dbf0c5a6SMatan Ziv-Av
lg_battery_add(struct power_supply * battery,struct acpi_battery_hook * hook)525878a82c2SArmin Wolf static int lg_battery_add(struct power_supply *battery, struct acpi_battery_hook *hook)
52607f5ed0eSMatan Ziv-Av {
52707f5ed0eSMatan Ziv-Av if (device_create_file(&battery->dev,
52807f5ed0eSMatan Ziv-Av &dev_attr_charge_control_end_threshold))
52907f5ed0eSMatan Ziv-Av return -ENODEV;
53007f5ed0eSMatan Ziv-Av
53107f5ed0eSMatan Ziv-Av return 0;
53207f5ed0eSMatan Ziv-Av }
53307f5ed0eSMatan Ziv-Av
lg_battery_remove(struct power_supply * battery,struct acpi_battery_hook * hook)534878a82c2SArmin Wolf static int lg_battery_remove(struct power_supply *battery, struct acpi_battery_hook *hook)
53507f5ed0eSMatan Ziv-Av {
53607f5ed0eSMatan Ziv-Av device_remove_file(&battery->dev,
53707f5ed0eSMatan Ziv-Av &dev_attr_charge_control_end_threshold);
53807f5ed0eSMatan Ziv-Av return 0;
53907f5ed0eSMatan Ziv-Av }
54007f5ed0eSMatan Ziv-Av
54107f5ed0eSMatan Ziv-Av static struct acpi_battery_hook battery_hook = {
54207f5ed0eSMatan Ziv-Av .add_battery = lg_battery_add,
54307f5ed0eSMatan Ziv-Av .remove_battery = lg_battery_remove,
54407f5ed0eSMatan Ziv-Av .name = "LG Battery Extension",
54507f5ed0eSMatan Ziv-Av };
54607f5ed0eSMatan Ziv-Av
547dbf0c5a6SMatan Ziv-Av static struct attribute *dev_attributes[] = {
548dbf0c5a6SMatan Ziv-Av &dev_attr_fan_mode.attr,
549dbf0c5a6SMatan Ziv-Av &dev_attr_usb_charge.attr,
550dbf0c5a6SMatan Ziv-Av &dev_attr_reader_mode.attr,
551dbf0c5a6SMatan Ziv-Av &dev_attr_fn_lock.attr,
552dbf0c5a6SMatan Ziv-Av &dev_attr_battery_care_limit.attr,
553dbf0c5a6SMatan Ziv-Av NULL
554dbf0c5a6SMatan Ziv-Av };
555dbf0c5a6SMatan Ziv-Av
556dbf0c5a6SMatan Ziv-Av static const struct attribute_group dev_attribute_group = {
557dbf0c5a6SMatan Ziv-Av .attrs = dev_attributes,
558dbf0c5a6SMatan Ziv-Av };
559dbf0c5a6SMatan Ziv-Av
tpad_led_set(struct led_classdev * cdev,enum led_brightness brightness)560dbf0c5a6SMatan Ziv-Av static void tpad_led_set(struct led_classdev *cdev,
561dbf0c5a6SMatan Ziv-Av enum led_brightness brightness)
562dbf0c5a6SMatan Ziv-Av {
563dbf0c5a6SMatan Ziv-Av union acpi_object *r;
564dbf0c5a6SMatan Ziv-Av
5659f25bd70SArmin Wolf r = lg_wmab(cdev->dev->parent, WM_TLED, WM_SET, brightness > LED_OFF);
566dbf0c5a6SMatan Ziv-Av kfree(r);
567dbf0c5a6SMatan Ziv-Av }
568dbf0c5a6SMatan Ziv-Av
tpad_led_get(struct led_classdev * cdev)569dbf0c5a6SMatan Ziv-Av static enum led_brightness tpad_led_get(struct led_classdev *cdev)
570dbf0c5a6SMatan Ziv-Av {
571dbf0c5a6SMatan Ziv-Av return ggov(GOV_TLED) > 0 ? LED_ON : LED_OFF;
572dbf0c5a6SMatan Ziv-Av }
573dbf0c5a6SMatan Ziv-Av
574ae262788SMatan Ziv-Av static LED_DEVICE(tpad_led, 1, 0);
575dbf0c5a6SMatan Ziv-Av
kbd_backlight_set(struct led_classdev * cdev,enum led_brightness brightness)576dbf0c5a6SMatan Ziv-Av static void kbd_backlight_set(struct led_classdev *cdev,
577dbf0c5a6SMatan Ziv-Av enum led_brightness brightness)
578dbf0c5a6SMatan Ziv-Av {
579dbf0c5a6SMatan Ziv-Av u32 val;
580dbf0c5a6SMatan Ziv-Av union acpi_object *r;
581dbf0c5a6SMatan Ziv-Av
582dbf0c5a6SMatan Ziv-Av val = 0x22;
583dbf0c5a6SMatan Ziv-Av if (brightness <= LED_OFF)
584dbf0c5a6SMatan Ziv-Av val = 0;
585dbf0c5a6SMatan Ziv-Av if (brightness >= LED_FULL)
586dbf0c5a6SMatan Ziv-Av val = 0x24;
5879f25bd70SArmin Wolf r = lg_wmab(cdev->dev->parent, WM_KEY_LIGHT, WM_SET, val);
588dbf0c5a6SMatan Ziv-Av kfree(r);
589dbf0c5a6SMatan Ziv-Av }
590dbf0c5a6SMatan Ziv-Av
get_kbd_backlight_level(struct device * dev)5919f25bd70SArmin Wolf static enum led_brightness get_kbd_backlight_level(struct device *dev)
592dbf0c5a6SMatan Ziv-Av {
593dbf0c5a6SMatan Ziv-Av union acpi_object *r;
594dbf0c5a6SMatan Ziv-Av int val;
595dbf0c5a6SMatan Ziv-Av
5969f25bd70SArmin Wolf r = lg_wmab(dev, WM_KEY_LIGHT, WM_GET, 0);
597dbf0c5a6SMatan Ziv-Av
598dbf0c5a6SMatan Ziv-Av if (!r)
599dbf0c5a6SMatan Ziv-Av return LED_OFF;
600dbf0c5a6SMatan Ziv-Av
601dbf0c5a6SMatan Ziv-Av if (r->type != ACPI_TYPE_BUFFER || r->buffer.pointer[1] != 0x05) {
602dbf0c5a6SMatan Ziv-Av kfree(r);
603dbf0c5a6SMatan Ziv-Av return LED_OFF;
604dbf0c5a6SMatan Ziv-Av }
605dbf0c5a6SMatan Ziv-Av
606dbf0c5a6SMatan Ziv-Av switch (r->buffer.pointer[0] & 0x27) {
607dbf0c5a6SMatan Ziv-Av case 0x24:
608dbf0c5a6SMatan Ziv-Av val = LED_FULL;
609dbf0c5a6SMatan Ziv-Av break;
610dbf0c5a6SMatan Ziv-Av case 0x22:
611dbf0c5a6SMatan Ziv-Av val = LED_HALF;
612dbf0c5a6SMatan Ziv-Av break;
613dbf0c5a6SMatan Ziv-Av default:
614dbf0c5a6SMatan Ziv-Av val = LED_OFF;
615dbf0c5a6SMatan Ziv-Av }
616dbf0c5a6SMatan Ziv-Av
617dbf0c5a6SMatan Ziv-Av kfree(r);
618dbf0c5a6SMatan Ziv-Av
619dbf0c5a6SMatan Ziv-Av return val;
620dbf0c5a6SMatan Ziv-Av }
621dbf0c5a6SMatan Ziv-Av
kbd_backlight_get(struct led_classdev * cdev)622ae262788SMatan Ziv-Av static enum led_brightness kbd_backlight_get(struct led_classdev *cdev)
623ae262788SMatan Ziv-Av {
6249f25bd70SArmin Wolf return get_kbd_backlight_level(cdev->dev->parent);
625ae262788SMatan Ziv-Av }
626ae262788SMatan Ziv-Av
627ae262788SMatan Ziv-Av static LED_DEVICE(kbd_backlight, 255, LED_BRIGHT_HW_CHANGED);
628dbf0c5a6SMatan Ziv-Av
wmi_input_destroy(void)629dbf0c5a6SMatan Ziv-Av static void wmi_input_destroy(void)
630dbf0c5a6SMatan Ziv-Av {
631dbf0c5a6SMatan Ziv-Av if (inited & INIT_INPUT_WMI_2)
632dbf0c5a6SMatan Ziv-Av wmi_remove_notify_handler(WMI_EVENT_GUID2);
633dbf0c5a6SMatan Ziv-Av
634dbf0c5a6SMatan Ziv-Av if (inited & INIT_INPUT_WMI_0)
635dbf0c5a6SMatan Ziv-Av wmi_remove_notify_handler(WMI_EVENT_GUID0);
636dbf0c5a6SMatan Ziv-Av
637dbf0c5a6SMatan Ziv-Av if (inited & INIT_SPARSE_KEYMAP)
638dbf0c5a6SMatan Ziv-Av input_unregister_device(wmi_input_dev);
639dbf0c5a6SMatan Ziv-Av
640dbf0c5a6SMatan Ziv-Av inited &= ~(INIT_INPUT_WMI_0 | INIT_INPUT_WMI_2 | INIT_SPARSE_KEYMAP);
641dbf0c5a6SMatan Ziv-Av }
642dbf0c5a6SMatan Ziv-Av
643dbf0c5a6SMatan Ziv-Av static struct platform_driver pf_driver = {
644dbf0c5a6SMatan Ziv-Av .driver = {
645dbf0c5a6SMatan Ziv-Av .name = PLATFORM_NAME,
646dbf0c5a6SMatan Ziv-Av }
647dbf0c5a6SMatan Ziv-Av };
648dbf0c5a6SMatan Ziv-Av
acpi_add(struct acpi_device * device)649dbf0c5a6SMatan Ziv-Av static int acpi_add(struct acpi_device *device)
650dbf0c5a6SMatan Ziv-Av {
6519f25bd70SArmin Wolf struct platform_device_info pdev_info = {
6529f25bd70SArmin Wolf .fwnode = acpi_fwnode_handle(device),
6539f25bd70SArmin Wolf .name = PLATFORM_NAME,
6549f25bd70SArmin Wolf .id = PLATFORM_DEVID_NONE,
6559f25bd70SArmin Wolf };
656dbf0c5a6SMatan Ziv-Av int ret;
6578983bfd5SMatan Ziv-Av const char *product;
6588983bfd5SMatan Ziv-Av int year = 2017;
659dbf0c5a6SMatan Ziv-Av
660dbf0c5a6SMatan Ziv-Av if (pf_device)
661dbf0c5a6SMatan Ziv-Av return 0;
662dbf0c5a6SMatan Ziv-Av
663dbf0c5a6SMatan Ziv-Av ret = platform_driver_register(&pf_driver);
664dbf0c5a6SMatan Ziv-Av if (ret)
665dbf0c5a6SMatan Ziv-Av return ret;
666dbf0c5a6SMatan Ziv-Av
6679f25bd70SArmin Wolf pf_device = platform_device_register_full(&pdev_info);
668dbf0c5a6SMatan Ziv-Av if (IS_ERR(pf_device)) {
669dbf0c5a6SMatan Ziv-Av ret = PTR_ERR(pf_device);
670dbf0c5a6SMatan Ziv-Av pf_device = NULL;
671dbf0c5a6SMatan Ziv-Av pr_err("unable to register platform device\n");
672dbf0c5a6SMatan Ziv-Av goto out_platform_registered;
673dbf0c5a6SMatan Ziv-Av }
6748983bfd5SMatan Ziv-Av product = dmi_get_system_info(DMI_PRODUCT_NAME);
6754c4a3d7cSMatan Ziv-Av if (product && strlen(product) > 4)
6768983bfd5SMatan Ziv-Av switch (product[4]) {
6778983bfd5SMatan Ziv-Av case '5':
67848d5e836SMatan Ziv-Av if (strlen(product) > 5)
67948d5e836SMatan Ziv-Av switch (product[5]) {
68048d5e836SMatan Ziv-Av case 'N':
68148d5e836SMatan Ziv-Av year = 2021;
68248d5e836SMatan Ziv-Av break;
68348d5e836SMatan Ziv-Av case '0':
68448d5e836SMatan Ziv-Av year = 2016;
68548d5e836SMatan Ziv-Av break;
68648d5e836SMatan Ziv-Av default:
68748d5e836SMatan Ziv-Av year = 2022;
68848d5e836SMatan Ziv-Av }
68948d5e836SMatan Ziv-Av break;
6908983bfd5SMatan Ziv-Av case '6':
6918983bfd5SMatan Ziv-Av year = 2016;
6928983bfd5SMatan Ziv-Av break;
6938983bfd5SMatan Ziv-Av case '7':
6948983bfd5SMatan Ziv-Av year = 2017;
6958983bfd5SMatan Ziv-Av break;
6968983bfd5SMatan Ziv-Av case '8':
6978983bfd5SMatan Ziv-Av year = 2018;
6988983bfd5SMatan Ziv-Av break;
6998983bfd5SMatan Ziv-Av case '9':
7008983bfd5SMatan Ziv-Av year = 2019;
7018983bfd5SMatan Ziv-Av break;
7028983bfd5SMatan Ziv-Av case '0':
7038983bfd5SMatan Ziv-Av if (strlen(product) > 5)
7048983bfd5SMatan Ziv-Av switch (product[5]) {
7058983bfd5SMatan Ziv-Av case 'N':
7068983bfd5SMatan Ziv-Av year = 2020;
7078983bfd5SMatan Ziv-Av break;
7088983bfd5SMatan Ziv-Av case 'P':
7098983bfd5SMatan Ziv-Av year = 2021;
7108983bfd5SMatan Ziv-Av break;
7118983bfd5SMatan Ziv-Av default:
7128983bfd5SMatan Ziv-Av year = 2022;
7138983bfd5SMatan Ziv-Av }
7148983bfd5SMatan Ziv-Av break;
7158983bfd5SMatan Ziv-Av default:
7168983bfd5SMatan Ziv-Av year = 2019;
7178983bfd5SMatan Ziv-Av }
718*c0076d2cSGergo Koteles pr_info("product: %s year: %d\n", product ?: "unknown", year);
7198983bfd5SMatan Ziv-Av
7208983bfd5SMatan Ziv-Av if (year >= 2019)
7218983bfd5SMatan Ziv-Av battery_limit_use_wmbb = 1;
722dbf0c5a6SMatan Ziv-Av
723dbf0c5a6SMatan Ziv-Av ret = sysfs_create_group(&pf_device->dev.kobj, &dev_attribute_group);
724dbf0c5a6SMatan Ziv-Av if (ret)
725dbf0c5a6SMatan Ziv-Av goto out_platform_device;
726dbf0c5a6SMatan Ziv-Av
727410a7724SAndy Shevchenko /* LEDs are optional */
728410a7724SAndy Shevchenko led_classdev_register(&pf_device->dev, &kbd_backlight);
729410a7724SAndy Shevchenko led_classdev_register(&pf_device->dev, &tpad_led);
730dbf0c5a6SMatan Ziv-Av
731dbf0c5a6SMatan Ziv-Av wmi_input_setup();
73207f5ed0eSMatan Ziv-Av battery_hook_register(&battery_hook);
733dbf0c5a6SMatan Ziv-Av
734dbf0c5a6SMatan Ziv-Av return 0;
735dbf0c5a6SMatan Ziv-Av
736dbf0c5a6SMatan Ziv-Av out_platform_device:
737dbf0c5a6SMatan Ziv-Av platform_device_unregister(pf_device);
738dbf0c5a6SMatan Ziv-Av out_platform_registered:
739dbf0c5a6SMatan Ziv-Av platform_driver_unregister(&pf_driver);
740dbf0c5a6SMatan Ziv-Av return ret;
741dbf0c5a6SMatan Ziv-Av }
742dbf0c5a6SMatan Ziv-Av
acpi_remove(struct acpi_device * device)7436c0eb5baSDawei Li static void acpi_remove(struct acpi_device *device)
744dbf0c5a6SMatan Ziv-Av {
745dbf0c5a6SMatan Ziv-Av sysfs_remove_group(&pf_device->dev.kobj, &dev_attribute_group);
746dbf0c5a6SMatan Ziv-Av
747dbf0c5a6SMatan Ziv-Av led_classdev_unregister(&tpad_led);
748410a7724SAndy Shevchenko led_classdev_unregister(&kbd_backlight);
749dbf0c5a6SMatan Ziv-Av
75007f5ed0eSMatan Ziv-Av battery_hook_unregister(&battery_hook);
751dbf0c5a6SMatan Ziv-Av wmi_input_destroy();
752dbf0c5a6SMatan Ziv-Av platform_device_unregister(pf_device);
753dbf0c5a6SMatan Ziv-Av pf_device = NULL;
754dbf0c5a6SMatan Ziv-Av platform_driver_unregister(&pf_driver);
755dbf0c5a6SMatan Ziv-Av }
756dbf0c5a6SMatan Ziv-Av
757dbf0c5a6SMatan Ziv-Av static const struct acpi_device_id device_ids[] = {
758477112afSArmin Wolf {"LGEX0820", 0},
759dbf0c5a6SMatan Ziv-Av {"", 0}
760dbf0c5a6SMatan Ziv-Av };
761dbf0c5a6SMatan Ziv-Av MODULE_DEVICE_TABLE(acpi, device_ids);
762dbf0c5a6SMatan Ziv-Av
763dbf0c5a6SMatan Ziv-Av static struct acpi_driver acpi_driver = {
764dbf0c5a6SMatan Ziv-Av .name = "LG Gram Laptop Support",
765dbf0c5a6SMatan Ziv-Av .class = "lg-laptop",
766dbf0c5a6SMatan Ziv-Av .ids = device_ids,
767dbf0c5a6SMatan Ziv-Av .ops = {
768dbf0c5a6SMatan Ziv-Av .add = acpi_add,
769dbf0c5a6SMatan Ziv-Av .remove = acpi_remove,
770dbf0c5a6SMatan Ziv-Av .notify = acpi_notify,
771dbf0c5a6SMatan Ziv-Av },
772dbf0c5a6SMatan Ziv-Av .owner = THIS_MODULE,
773dbf0c5a6SMatan Ziv-Av };
774dbf0c5a6SMatan Ziv-Av
acpi_init(void)775dbf0c5a6SMatan Ziv-Av static int __init acpi_init(void)
776dbf0c5a6SMatan Ziv-Av {
777dbf0c5a6SMatan Ziv-Av int result;
778dbf0c5a6SMatan Ziv-Av
779dbf0c5a6SMatan Ziv-Av result = acpi_bus_register_driver(&acpi_driver);
780dbf0c5a6SMatan Ziv-Av if (result < 0) {
78124789075SRafael J. Wysocki pr_debug("Error registering driver\n");
782dbf0c5a6SMatan Ziv-Av return -ENODEV;
783dbf0c5a6SMatan Ziv-Av }
784dbf0c5a6SMatan Ziv-Av
785dbf0c5a6SMatan Ziv-Av return 0;
786dbf0c5a6SMatan Ziv-Av }
787dbf0c5a6SMatan Ziv-Av
acpi_exit(void)788dbf0c5a6SMatan Ziv-Av static void __exit acpi_exit(void)
789dbf0c5a6SMatan Ziv-Av {
790dbf0c5a6SMatan Ziv-Av acpi_bus_unregister_driver(&acpi_driver);
791dbf0c5a6SMatan Ziv-Av }
792dbf0c5a6SMatan Ziv-Av
793dbf0c5a6SMatan Ziv-Av module_init(acpi_init);
794dbf0c5a6SMatan Ziv-Av module_exit(acpi_exit);
795