xref: /openbmc/linux/drivers/platform/x86/lg-laptop.c (revision 26d0dfbb16fcb17d128a79dc70f3020ea6992af0)
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