xref: /linux/drivers/platform/x86/fujitsu-laptop.c (revision e58e871becec2d3b04ed91c0c16fe8deac9c9dfa)
1 /*-*-linux-c-*-*/
2 
3 /*
4   Copyright (C) 2007,2008 Jonathan Woithe <jwoithe@just42.net>
5   Copyright (C) 2008 Peter Gruber <nokos@gmx.net>
6   Copyright (C) 2008 Tony Vroon <tony@linx.net>
7   Based on earlier work:
8     Copyright (C) 2003 Shane Spencer <shane@bogomip.com>
9     Adrian Yee <brewt-fujitsu@brewt.org>
10 
11   Templated from msi-laptop.c and thinkpad_acpi.c which is copyright
12   by its respective authors.
13 
14   This program is free software; you can redistribute it and/or modify
15   it under the terms of the GNU General Public License as published by
16   the Free Software Foundation; either version 2 of the License, or
17   (at your option) any later version.
18 
19   This program is distributed in the hope that it will be useful, but
20   WITHOUT ANY WARRANTY; without even the implied warranty of
21   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22   General Public License for more details.
23 
24   You should have received a copy of the GNU General Public License
25   along with this program; if not, write to the Free Software
26   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
27   02110-1301, USA.
28  */
29 
30 /*
31  * fujitsu-laptop.c - Fujitsu laptop support, providing access to additional
32  * features made available on a range of Fujitsu laptops including the
33  * P2xxx/P5xxx/S6xxx/S7xxx series.
34  *
35  * This driver implements a vendor-specific backlight control interface for
36  * Fujitsu laptops and provides support for hotkeys present on certain Fujitsu
37  * laptops.
38  *
39  * This driver has been tested on a Fujitsu Lifebook S6410, S7020 and
40  * P8010.  It should work on most P-series and S-series Lifebooks, but
41  * YMMV.
42  *
43  * The module parameter use_alt_lcd_levels switches between different ACPI
44  * brightness controls which are used by different Fujitsu laptops.  In most
45  * cases the correct method is automatically detected. "use_alt_lcd_levels=1"
46  * is applicable for a Fujitsu Lifebook S6410 if autodetection fails.
47  *
48  */
49 
50 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
51 
52 #include <linux/module.h>
53 #include <linux/kernel.h>
54 #include <linux/init.h>
55 #include <linux/acpi.h>
56 #include <linux/dmi.h>
57 #include <linux/backlight.h>
58 #include <linux/fb.h>
59 #include <linux/input.h>
60 #include <linux/input/sparse-keymap.h>
61 #include <linux/kfifo.h>
62 #include <linux/leds.h>
63 #include <linux/platform_device.h>
64 #include <linux/slab.h>
65 #include <acpi/video.h>
66 
67 #define FUJITSU_DRIVER_VERSION "0.6.0"
68 
69 #define FUJITSU_LCD_N_LEVELS 8
70 
71 #define ACPI_FUJITSU_CLASS		"fujitsu"
72 #define ACPI_FUJITSU_BL_HID		"FUJ02B1"
73 #define ACPI_FUJITSU_BL_DRIVER_NAME	"Fujitsu laptop FUJ02B1 ACPI brightness driver"
74 #define ACPI_FUJITSU_BL_DEVICE_NAME	"Fujitsu FUJ02B1"
75 #define ACPI_FUJITSU_LAPTOP_HID		"FUJ02E3"
76 #define ACPI_FUJITSU_LAPTOP_DRIVER_NAME	"Fujitsu laptop FUJ02E3 ACPI hotkeys driver"
77 #define ACPI_FUJITSU_LAPTOP_DEVICE_NAME	"Fujitsu FUJ02E3"
78 
79 #define ACPI_FUJITSU_NOTIFY_CODE1     0x80
80 
81 /* FUNC interface - command values */
82 #define FUNC_FLAGS	0x1000
83 #define FUNC_LEDS	0x1001
84 #define FUNC_BUTTONS	0x1002
85 #define FUNC_BACKLIGHT  0x1004
86 
87 /* FUNC interface - responses */
88 #define UNSUPPORTED_CMD 0x80000000
89 
90 /* FUNC interface - status flags */
91 #define FLAG_RFKILL	0x020
92 #define FLAG_LID	0x100
93 #define FLAG_DOCK	0x200
94 
95 /* FUNC interface - LED control */
96 #define FUNC_LED_OFF	0x1
97 #define FUNC_LED_ON	0x30001
98 #define KEYBOARD_LAMPS	0x100
99 #define LOGOLAMP_POWERON 0x2000
100 #define LOGOLAMP_ALWAYS  0x4000
101 #define RADIO_LED_ON	0x20
102 #define ECO_LED	0x10000
103 #define ECO_LED_ON	0x80000
104 
105 /* Hotkey details */
106 #define KEY1_CODE	0x410	/* codes for the keys in the GIRB register */
107 #define KEY2_CODE	0x411
108 #define KEY3_CODE	0x412
109 #define KEY4_CODE	0x413
110 #define KEY5_CODE	0x420
111 
112 #define MAX_HOTKEY_RINGBUFFER_SIZE 100
113 #define RINGBUFFERSIZE 40
114 
115 /* Debugging */
116 #define FUJLAPTOP_DBG_ERROR	  0x0001
117 #define FUJLAPTOP_DBG_WARN	  0x0002
118 #define FUJLAPTOP_DBG_INFO	  0x0004
119 #define FUJLAPTOP_DBG_TRACE	  0x0008
120 
121 #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
122 #define vdbg_printk(a_dbg_level, format, arg...) \
123 	do { if (dbg_level & a_dbg_level) \
124 		printk(KERN_DEBUG pr_fmt("%s: " format), __func__, ## arg); \
125 	} while (0)
126 #else
127 #define vdbg_printk(a_dbg_level, format, arg...) \
128 	do { } while (0)
129 #endif
130 
131 /* Device controlling the backlight and associated keys */
132 struct fujitsu_bl {
133 	acpi_handle acpi_handle;
134 	struct input_dev *input;
135 	char phys[32];
136 	struct backlight_device *bl_device;
137 	unsigned int max_brightness;
138 	unsigned int brightness_level;
139 };
140 
141 static struct fujitsu_bl *fujitsu_bl;
142 static int use_alt_lcd_levels = -1;
143 static bool disable_brightness_adjust;
144 
145 /* Device used to access hotkeys and other features on the laptop */
146 struct fujitsu_laptop {
147 	acpi_handle acpi_handle;
148 	struct acpi_device *dev;
149 	struct input_dev *input;
150 	char phys[32];
151 	struct platform_device *pf_device;
152 	struct kfifo fifo;
153 	spinlock_t fifo_lock;
154 	int flags_supported;
155 	int flags_state;
156 };
157 
158 static struct fujitsu_laptop *fujitsu_laptop;
159 
160 #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
161 static u32 dbg_level = 0x03;
162 #endif
163 
164 /* Fujitsu ACPI interface function */
165 
166 static int call_fext_func(int func, int op, int feature, int state)
167 {
168 	union acpi_object params[4] = {
169 		{ .integer.type = ACPI_TYPE_INTEGER, .integer.value = func },
170 		{ .integer.type = ACPI_TYPE_INTEGER, .integer.value = op },
171 		{ .integer.type = ACPI_TYPE_INTEGER, .integer.value = feature },
172 		{ .integer.type = ACPI_TYPE_INTEGER, .integer.value = state }
173 	};
174 	struct acpi_object_list arg_list = { 4, params };
175 	unsigned long long value;
176 	acpi_status status;
177 
178 	status = acpi_evaluate_integer(fujitsu_laptop->acpi_handle, "FUNC",
179 				       &arg_list, &value);
180 	if (ACPI_FAILURE(status)) {
181 		vdbg_printk(FUJLAPTOP_DBG_ERROR, "Failed to evaluate FUNC\n");
182 		return -ENODEV;
183 	}
184 
185 	vdbg_printk(FUJLAPTOP_DBG_TRACE, "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) returned 0x%x\n",
186 		    func, op, feature, state, (int)value);
187 	return value;
188 }
189 
190 /* Hardware access for LCD brightness control */
191 
192 static int set_lcd_level(int level)
193 {
194 	acpi_status status;
195 	char *method;
196 
197 	switch (use_alt_lcd_levels) {
198 	case -1:
199 		if (acpi_has_method(fujitsu_bl->acpi_handle, "SBL2"))
200 			method = "SBL2";
201 		else
202 			method = "SBLL";
203 		break;
204 	case 1:
205 		method = "SBL2";
206 		break;
207 	default:
208 		method = "SBLL";
209 		break;
210 	}
211 
212 	vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via %s [%d]\n",
213 		    method, level);
214 
215 	if (level < 0 || level >= fujitsu_bl->max_brightness)
216 		return -EINVAL;
217 
218 	status = acpi_execute_simple_method(fujitsu_bl->acpi_handle, method,
219 					    level);
220 	if (ACPI_FAILURE(status)) {
221 		vdbg_printk(FUJLAPTOP_DBG_ERROR, "Failed to evaluate %s\n",
222 			    method);
223 		return -ENODEV;
224 	}
225 
226 	fujitsu_bl->brightness_level = level;
227 
228 	return 0;
229 }
230 
231 static int get_lcd_level(void)
232 {
233 	unsigned long long state = 0;
234 	acpi_status status = AE_OK;
235 
236 	vdbg_printk(FUJLAPTOP_DBG_TRACE, "get lcd level via GBLL\n");
237 
238 	status = acpi_evaluate_integer(fujitsu_bl->acpi_handle, "GBLL", NULL,
239 				       &state);
240 	if (ACPI_FAILURE(status))
241 		return 0;
242 
243 	fujitsu_bl->brightness_level = state & 0x0fffffff;
244 
245 	return fujitsu_bl->brightness_level;
246 }
247 
248 static int get_max_brightness(void)
249 {
250 	unsigned long long state = 0;
251 	acpi_status status = AE_OK;
252 
253 	vdbg_printk(FUJLAPTOP_DBG_TRACE, "get max lcd level via RBLL\n");
254 
255 	status = acpi_evaluate_integer(fujitsu_bl->acpi_handle, "RBLL", NULL,
256 				       &state);
257 	if (ACPI_FAILURE(status))
258 		return -1;
259 
260 	fujitsu_bl->max_brightness = state;
261 
262 	return fujitsu_bl->max_brightness;
263 }
264 
265 /* Backlight device stuff */
266 
267 static int bl_get_brightness(struct backlight_device *b)
268 {
269 	return b->props.power == FB_BLANK_POWERDOWN ? 0 : get_lcd_level();
270 }
271 
272 static int bl_update_status(struct backlight_device *b)
273 {
274 	if (b->props.power == FB_BLANK_POWERDOWN)
275 		call_fext_func(FUNC_BACKLIGHT, 0x1, 0x4, 0x3);
276 	else
277 		call_fext_func(FUNC_BACKLIGHT, 0x1, 0x4, 0x0);
278 
279 	return set_lcd_level(b->props.brightness);
280 }
281 
282 static const struct backlight_ops fujitsu_bl_ops = {
283 	.get_brightness = bl_get_brightness,
284 	.update_status = bl_update_status,
285 };
286 
287 static ssize_t lid_show(struct device *dev, struct device_attribute *attr,
288 			char *buf)
289 {
290 	if (!(fujitsu_laptop->flags_supported & FLAG_LID))
291 		return sprintf(buf, "unknown\n");
292 	if (fujitsu_laptop->flags_state & FLAG_LID)
293 		return sprintf(buf, "open\n");
294 	else
295 		return sprintf(buf, "closed\n");
296 }
297 
298 static ssize_t dock_show(struct device *dev, struct device_attribute *attr,
299 			 char *buf)
300 {
301 	if (!(fujitsu_laptop->flags_supported & FLAG_DOCK))
302 		return sprintf(buf, "unknown\n");
303 	if (fujitsu_laptop->flags_state & FLAG_DOCK)
304 		return sprintf(buf, "docked\n");
305 	else
306 		return sprintf(buf, "undocked\n");
307 }
308 
309 static ssize_t radios_show(struct device *dev, struct device_attribute *attr,
310 			   char *buf)
311 {
312 	if (!(fujitsu_laptop->flags_supported & FLAG_RFKILL))
313 		return sprintf(buf, "unknown\n");
314 	if (fujitsu_laptop->flags_state & FLAG_RFKILL)
315 		return sprintf(buf, "on\n");
316 	else
317 		return sprintf(buf, "killed\n");
318 }
319 
320 static DEVICE_ATTR_RO(lid);
321 static DEVICE_ATTR_RO(dock);
322 static DEVICE_ATTR_RO(radios);
323 
324 static struct attribute *fujitsu_pf_attributes[] = {
325 	&dev_attr_lid.attr,
326 	&dev_attr_dock.attr,
327 	&dev_attr_radios.attr,
328 	NULL
329 };
330 
331 static struct attribute_group fujitsu_pf_attribute_group = {
332 	.attrs = fujitsu_pf_attributes
333 };
334 
335 static struct platform_driver fujitsu_pf_driver = {
336 	.driver = {
337 		   .name = "fujitsu-laptop",
338 		   }
339 };
340 
341 /* ACPI device for LCD brightness control */
342 
343 static const struct key_entry keymap_backlight[] = {
344 	{ KE_KEY, true, { KEY_BRIGHTNESSUP } },
345 	{ KE_KEY, false, { KEY_BRIGHTNESSDOWN } },
346 	{ KE_END, 0 }
347 };
348 
349 static int acpi_fujitsu_bl_input_setup(struct acpi_device *device)
350 {
351 	struct fujitsu_bl *fujitsu_bl = acpi_driver_data(device);
352 	int ret;
353 
354 	fujitsu_bl->input = devm_input_allocate_device(&device->dev);
355 	if (!fujitsu_bl->input)
356 		return -ENOMEM;
357 
358 	snprintf(fujitsu_bl->phys, sizeof(fujitsu_bl->phys),
359 		 "%s/video/input0", acpi_device_hid(device));
360 
361 	fujitsu_bl->input->name = acpi_device_name(device);
362 	fujitsu_bl->input->phys = fujitsu_bl->phys;
363 	fujitsu_bl->input->id.bustype = BUS_HOST;
364 	fujitsu_bl->input->id.product = 0x06;
365 
366 	ret = sparse_keymap_setup(fujitsu_bl->input, keymap_backlight, NULL);
367 	if (ret)
368 		return ret;
369 
370 	return input_register_device(fujitsu_bl->input);
371 }
372 
373 static int fujitsu_backlight_register(struct acpi_device *device)
374 {
375 	const struct backlight_properties props = {
376 		.brightness = fujitsu_bl->brightness_level,
377 		.max_brightness = fujitsu_bl->max_brightness - 1,
378 		.type = BACKLIGHT_PLATFORM
379 	};
380 	struct backlight_device *bd;
381 
382 	bd = devm_backlight_device_register(&device->dev, "fujitsu-laptop",
383 					    &device->dev, NULL,
384 					    &fujitsu_bl_ops, &props);
385 	if (IS_ERR(bd))
386 		return PTR_ERR(bd);
387 
388 	fujitsu_bl->bl_device = bd;
389 
390 	return 0;
391 }
392 
393 static int acpi_fujitsu_bl_add(struct acpi_device *device)
394 {
395 	int state = 0;
396 	int error;
397 
398 	if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
399 		return -ENODEV;
400 
401 	if (!device)
402 		return -EINVAL;
403 
404 	fujitsu_bl->acpi_handle = device->handle;
405 	sprintf(acpi_device_name(device), "%s", ACPI_FUJITSU_BL_DEVICE_NAME);
406 	sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS);
407 	device->driver_data = fujitsu_bl;
408 
409 	error = acpi_fujitsu_bl_input_setup(device);
410 	if (error)
411 		return error;
412 
413 	error = acpi_bus_update_power(fujitsu_bl->acpi_handle, &state);
414 	if (error) {
415 		pr_err("Error reading power state\n");
416 		return error;
417 	}
418 
419 	pr_info("ACPI: %s [%s] (%s)\n",
420 	       acpi_device_name(device), acpi_device_bid(device),
421 	       !device->power.state ? "on" : "off");
422 
423 	if (acpi_has_method(device->handle, METHOD_NAME__INI)) {
424 		vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n");
425 		if (ACPI_FAILURE
426 		    (acpi_evaluate_object
427 		     (device->handle, METHOD_NAME__INI, NULL, NULL)))
428 			pr_err("_INI Method failed\n");
429 	}
430 
431 	if (get_max_brightness() <= 0)
432 		fujitsu_bl->max_brightness = FUJITSU_LCD_N_LEVELS;
433 	get_lcd_level();
434 
435 	error = fujitsu_backlight_register(device);
436 	if (error)
437 		return error;
438 
439 	return 0;
440 }
441 
442 /* Brightness notify */
443 
444 static void acpi_fujitsu_bl_notify(struct acpi_device *device, u32 event)
445 {
446 	struct input_dev *input;
447 	int oldb, newb;
448 
449 	input = fujitsu_bl->input;
450 
451 	if (event != ACPI_FUJITSU_NOTIFY_CODE1) {
452 		vdbg_printk(FUJLAPTOP_DBG_WARN,
453 			    "unsupported event [0x%x]\n", event);
454 		sparse_keymap_report_event(input, -1, 1, true);
455 		return;
456 	}
457 
458 	oldb = fujitsu_bl->brightness_level;
459 	get_lcd_level();
460 	newb = fujitsu_bl->brightness_level;
461 
462 	vdbg_printk(FUJLAPTOP_DBG_TRACE, "brightness button event [%i -> %i]\n",
463 		    oldb, newb);
464 
465 	if (oldb == newb)
466 		return;
467 
468 	if (!disable_brightness_adjust)
469 		set_lcd_level(newb);
470 
471 	sparse_keymap_report_event(input, oldb < newb, 1, true);
472 }
473 
474 /* ACPI device for hotkey handling */
475 
476 static const struct key_entry keymap_default[] = {
477 	{ KE_KEY, KEY1_CODE, { KEY_PROG1 } },
478 	{ KE_KEY, KEY2_CODE, { KEY_PROG2 } },
479 	{ KE_KEY, KEY3_CODE, { KEY_PROG3 } },
480 	{ KE_KEY, KEY4_CODE, { KEY_PROG4 } },
481 	{ KE_KEY, KEY5_CODE, { KEY_RFKILL } },
482 	{ KE_KEY, BIT(26),   { KEY_TOUCHPAD_TOGGLE } },
483 	{ KE_END, 0 }
484 };
485 
486 static const struct key_entry keymap_s64x0[] = {
487 	{ KE_KEY, KEY1_CODE, { KEY_SCREENLOCK } },	/* "Lock" */
488 	{ KE_KEY, KEY2_CODE, { KEY_HELP } },		/* "Mobility Center */
489 	{ KE_KEY, KEY3_CODE, { KEY_PROG3 } },
490 	{ KE_KEY, KEY4_CODE, { KEY_PROG4 } },
491 	{ KE_END, 0 }
492 };
493 
494 static const struct key_entry keymap_p8010[] = {
495 	{ KE_KEY, KEY1_CODE, { KEY_HELP } },		/* "Support" */
496 	{ KE_KEY, KEY2_CODE, { KEY_PROG2 } },
497 	{ KE_KEY, KEY3_CODE, { KEY_SWITCHVIDEOMODE } },	/* "Presentation" */
498 	{ KE_KEY, KEY4_CODE, { KEY_WWW } },		/* "WWW" */
499 	{ KE_END, 0 }
500 };
501 
502 static const struct key_entry *keymap = keymap_default;
503 
504 static int fujitsu_laptop_dmi_keymap_override(const struct dmi_system_id *id)
505 {
506 	pr_info("Identified laptop model '%s'\n", id->ident);
507 	keymap = id->driver_data;
508 	return 1;
509 }
510 
511 static const struct dmi_system_id fujitsu_laptop_dmi_table[] = {
512 	{
513 		.callback = fujitsu_laptop_dmi_keymap_override,
514 		.ident = "Fujitsu Siemens S6410",
515 		.matches = {
516 			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
517 			DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6410"),
518 		},
519 		.driver_data = (void *)keymap_s64x0
520 	},
521 	{
522 		.callback = fujitsu_laptop_dmi_keymap_override,
523 		.ident = "Fujitsu Siemens S6420",
524 		.matches = {
525 			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
526 			DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6420"),
527 		},
528 		.driver_data = (void *)keymap_s64x0
529 	},
530 	{
531 		.callback = fujitsu_laptop_dmi_keymap_override,
532 		.ident = "Fujitsu LifeBook P8010",
533 		.matches = {
534 			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
535 			DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook P8010"),
536 		},
537 		.driver_data = (void *)keymap_p8010
538 	},
539 	{}
540 };
541 
542 static int acpi_fujitsu_laptop_input_setup(struct acpi_device *device)
543 {
544 	struct fujitsu_laptop *fujitsu_laptop = acpi_driver_data(device);
545 	int ret;
546 
547 	fujitsu_laptop->input = devm_input_allocate_device(&device->dev);
548 	if (!fujitsu_laptop->input)
549 		return -ENOMEM;
550 
551 	snprintf(fujitsu_laptop->phys, sizeof(fujitsu_laptop->phys),
552 		 "%s/video/input0", acpi_device_hid(device));
553 
554 	fujitsu_laptop->input->name = acpi_device_name(device);
555 	fujitsu_laptop->input->phys = fujitsu_laptop->phys;
556 	fujitsu_laptop->input->id.bustype = BUS_HOST;
557 	fujitsu_laptop->input->id.product = 0x06;
558 
559 	dmi_check_system(fujitsu_laptop_dmi_table);
560 	ret = sparse_keymap_setup(fujitsu_laptop->input, keymap, NULL);
561 	if (ret)
562 		return ret;
563 
564 	return input_register_device(fujitsu_laptop->input);
565 }
566 
567 static int fujitsu_laptop_platform_add(void)
568 {
569 	int ret;
570 
571 	fujitsu_laptop->pf_device = platform_device_alloc("fujitsu-laptop", -1);
572 	if (!fujitsu_laptop->pf_device)
573 		return -ENOMEM;
574 
575 	ret = platform_device_add(fujitsu_laptop->pf_device);
576 	if (ret)
577 		goto err_put_platform_device;
578 
579 	ret = sysfs_create_group(&fujitsu_laptop->pf_device->dev.kobj,
580 				 &fujitsu_pf_attribute_group);
581 	if (ret)
582 		goto err_del_platform_device;
583 
584 	return 0;
585 
586 err_del_platform_device:
587 	platform_device_del(fujitsu_laptop->pf_device);
588 err_put_platform_device:
589 	platform_device_put(fujitsu_laptop->pf_device);
590 
591 	return ret;
592 }
593 
594 static void fujitsu_laptop_platform_remove(void)
595 {
596 	sysfs_remove_group(&fujitsu_laptop->pf_device->dev.kobj,
597 			   &fujitsu_pf_attribute_group);
598 	platform_device_unregister(fujitsu_laptop->pf_device);
599 }
600 
601 static int logolamp_set(struct led_classdev *cdev,
602 			enum led_brightness brightness)
603 {
604 	int poweron = FUNC_LED_ON, always = FUNC_LED_ON;
605 	int ret;
606 
607 	if (brightness < LED_HALF)
608 		poweron = FUNC_LED_OFF;
609 
610 	if (brightness < LED_FULL)
611 		always = FUNC_LED_OFF;
612 
613 	ret = call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, poweron);
614 	if (ret < 0)
615 		return ret;
616 
617 	return call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, always);
618 }
619 
620 static enum led_brightness logolamp_get(struct led_classdev *cdev)
621 {
622 	int ret;
623 
624 	ret = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0);
625 	if (ret == FUNC_LED_ON)
626 		return LED_FULL;
627 
628 	ret = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0);
629 	if (ret == FUNC_LED_ON)
630 		return LED_HALF;
631 
632 	return LED_OFF;
633 }
634 
635 static struct led_classdev logolamp_led = {
636 	.name = "fujitsu::logolamp",
637 	.brightness_set_blocking = logolamp_set,
638 	.brightness_get = logolamp_get
639 };
640 
641 static int kblamps_set(struct led_classdev *cdev,
642 		       enum led_brightness brightness)
643 {
644 	if (brightness >= LED_FULL)
645 		return call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS,
646 				      FUNC_LED_ON);
647 	else
648 		return call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS,
649 				      FUNC_LED_OFF);
650 }
651 
652 static enum led_brightness kblamps_get(struct led_classdev *cdev)
653 {
654 	enum led_brightness brightness = LED_OFF;
655 
656 	if (call_fext_func(FUNC_LEDS, 0x2, KEYBOARD_LAMPS, 0x0) == FUNC_LED_ON)
657 		brightness = LED_FULL;
658 
659 	return brightness;
660 }
661 
662 static struct led_classdev kblamps_led = {
663 	.name = "fujitsu::kblamps",
664 	.brightness_set_blocking = kblamps_set,
665 	.brightness_get = kblamps_get
666 };
667 
668 static int radio_led_set(struct led_classdev *cdev,
669 			 enum led_brightness brightness)
670 {
671 	if (brightness >= LED_FULL)
672 		return call_fext_func(FUNC_FLAGS, 0x5, RADIO_LED_ON,
673 				      RADIO_LED_ON);
674 	else
675 		return call_fext_func(FUNC_FLAGS, 0x5, RADIO_LED_ON, 0x0);
676 }
677 
678 static enum led_brightness radio_led_get(struct led_classdev *cdev)
679 {
680 	enum led_brightness brightness = LED_OFF;
681 
682 	if (call_fext_func(FUNC_FLAGS, 0x4, 0x0, 0x0) & RADIO_LED_ON)
683 		brightness = LED_FULL;
684 
685 	return brightness;
686 }
687 
688 static struct led_classdev radio_led = {
689 	.name = "fujitsu::radio_led",
690 	.brightness_set_blocking = radio_led_set,
691 	.brightness_get = radio_led_get,
692 	.default_trigger = "rfkill-any"
693 };
694 
695 static int eco_led_set(struct led_classdev *cdev,
696 		       enum led_brightness brightness)
697 {
698 	int curr;
699 
700 	curr = call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0);
701 	if (brightness >= LED_FULL)
702 		return call_fext_func(FUNC_LEDS, 0x1, ECO_LED,
703 				      curr | ECO_LED_ON);
704 	else
705 		return call_fext_func(FUNC_LEDS, 0x1, ECO_LED,
706 				      curr & ~ECO_LED_ON);
707 }
708 
709 static enum led_brightness eco_led_get(struct led_classdev *cdev)
710 {
711 	enum led_brightness brightness = LED_OFF;
712 
713 	if (call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0) & ECO_LED_ON)
714 		brightness = LED_FULL;
715 
716 	return brightness;
717 }
718 
719 static struct led_classdev eco_led = {
720 	.name = "fujitsu::eco_led",
721 	.brightness_set_blocking = eco_led_set,
722 	.brightness_get = eco_led_get
723 };
724 
725 static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
726 {
727 	int result;
728 
729 	if (call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) {
730 		result = devm_led_classdev_register(&device->dev,
731 						    &logolamp_led);
732 		if (result)
733 			return result;
734 	}
735 
736 	if ((call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & KEYBOARD_LAMPS) &&
737 	    (call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0) == 0x0)) {
738 		result = devm_led_classdev_register(&device->dev, &kblamps_led);
739 		if (result)
740 			return result;
741 	}
742 
743 	/*
744 	 * BTNI bit 24 seems to indicate the presence of a radio toggle
745 	 * button in place of a slide switch, and all such machines appear
746 	 * to also have an RF LED.  Therefore use bit 24 as an indicator
747 	 * that an RF LED is present.
748 	 */
749 	if (call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0) & BIT(24)) {
750 		result = devm_led_classdev_register(&device->dev, &radio_led);
751 		if (result)
752 			return result;
753 	}
754 
755 	/* Support for eco led is not always signaled in bit corresponding
756 	 * to the bit used to control the led. According to the DSDT table,
757 	 * bit 14 seems to indicate presence of said led as well.
758 	 * Confirm by testing the status.
759 	 */
760 	if ((call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & BIT(14)) &&
761 	    (call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0) != UNSUPPORTED_CMD)) {
762 		result = devm_led_classdev_register(&device->dev, &eco_led);
763 		if (result)
764 			return result;
765 	}
766 
767 	return 0;
768 }
769 
770 static int acpi_fujitsu_laptop_add(struct acpi_device *device)
771 {
772 	int state = 0;
773 	int error;
774 	int i;
775 
776 	if (!device)
777 		return -EINVAL;
778 
779 	fujitsu_laptop->acpi_handle = device->handle;
780 	sprintf(acpi_device_name(device), "%s",
781 		ACPI_FUJITSU_LAPTOP_DEVICE_NAME);
782 	sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS);
783 	device->driver_data = fujitsu_laptop;
784 
785 	/* kfifo */
786 	spin_lock_init(&fujitsu_laptop->fifo_lock);
787 	error = kfifo_alloc(&fujitsu_laptop->fifo, RINGBUFFERSIZE * sizeof(int),
788 			GFP_KERNEL);
789 	if (error) {
790 		pr_err("kfifo_alloc failed\n");
791 		goto err_stop;
792 	}
793 
794 	error = acpi_fujitsu_laptop_input_setup(device);
795 	if (error)
796 		goto err_free_fifo;
797 
798 	error = acpi_bus_update_power(fujitsu_laptop->acpi_handle, &state);
799 	if (error) {
800 		pr_err("Error reading power state\n");
801 		goto err_free_fifo;
802 	}
803 
804 	pr_info("ACPI: %s [%s] (%s)\n",
805 		acpi_device_name(device), acpi_device_bid(device),
806 		!device->power.state ? "on" : "off");
807 
808 	fujitsu_laptop->dev = device;
809 
810 	if (acpi_has_method(device->handle, METHOD_NAME__INI)) {
811 		vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n");
812 		if (ACPI_FAILURE
813 		    (acpi_evaluate_object
814 		     (device->handle, METHOD_NAME__INI, NULL, NULL)))
815 			pr_err("_INI Method failed\n");
816 	}
817 
818 	i = 0;
819 	while (call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0) != 0
820 		&& (i++) < MAX_HOTKEY_RINGBUFFER_SIZE)
821 		; /* No action, result is discarded */
822 	vdbg_printk(FUJLAPTOP_DBG_INFO, "Discarded %i ringbuffer entries\n", i);
823 
824 	fujitsu_laptop->flags_supported =
825 		call_fext_func(FUNC_FLAGS, 0x0, 0x0, 0x0);
826 
827 	/* Make sure our bitmask of supported functions is cleared if the
828 	   RFKILL function block is not implemented, like on the S7020. */
829 	if (fujitsu_laptop->flags_supported == UNSUPPORTED_CMD)
830 		fujitsu_laptop->flags_supported = 0;
831 
832 	if (fujitsu_laptop->flags_supported)
833 		fujitsu_laptop->flags_state =
834 			call_fext_func(FUNC_FLAGS, 0x4, 0x0, 0x0);
835 
836 	/* Suspect this is a keymap of the application panel, print it */
837 	pr_info("BTNI: [0x%x]\n", call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0));
838 
839 	/* Sync backlight power status */
840 	if (fujitsu_bl->bl_device &&
841 	    acpi_video_get_backlight_type() == acpi_backlight_vendor) {
842 		if (call_fext_func(FUNC_BACKLIGHT, 0x2, 0x4, 0x0) == 3)
843 			fujitsu_bl->bl_device->props.power = FB_BLANK_POWERDOWN;
844 		else
845 			fujitsu_bl->bl_device->props.power = FB_BLANK_UNBLANK;
846 	}
847 
848 	error = acpi_fujitsu_laptop_leds_register(device);
849 	if (error)
850 		goto err_free_fifo;
851 
852 	error = fujitsu_laptop_platform_add();
853 	if (error)
854 		goto err_free_fifo;
855 
856 	return 0;
857 
858 err_free_fifo:
859 	kfifo_free(&fujitsu_laptop->fifo);
860 err_stop:
861 	return error;
862 }
863 
864 static int acpi_fujitsu_laptop_remove(struct acpi_device *device)
865 {
866 	struct fujitsu_laptop *fujitsu_laptop = acpi_driver_data(device);
867 
868 	fujitsu_laptop_platform_remove();
869 
870 	kfifo_free(&fujitsu_laptop->fifo);
871 
872 	return 0;
873 }
874 
875 static void acpi_fujitsu_laptop_press(int scancode)
876 {
877 	struct input_dev *input = fujitsu_laptop->input;
878 	int status;
879 
880 	status = kfifo_in_locked(&fujitsu_laptop->fifo,
881 				 (unsigned char *)&scancode, sizeof(scancode),
882 				 &fujitsu_laptop->fifo_lock);
883 	if (status != sizeof(scancode)) {
884 		vdbg_printk(FUJLAPTOP_DBG_WARN,
885 			    "Could not push scancode [0x%x]\n", scancode);
886 		return;
887 	}
888 	sparse_keymap_report_event(input, scancode, 1, false);
889 	vdbg_printk(FUJLAPTOP_DBG_TRACE,
890 		    "Push scancode into ringbuffer [0x%x]\n", scancode);
891 }
892 
893 static void acpi_fujitsu_laptop_release(void)
894 {
895 	struct input_dev *input = fujitsu_laptop->input;
896 	int scancode, status;
897 
898 	while (true) {
899 		status = kfifo_out_locked(&fujitsu_laptop->fifo,
900 					  (unsigned char *)&scancode,
901 					  sizeof(scancode),
902 					  &fujitsu_laptop->fifo_lock);
903 		if (status != sizeof(scancode))
904 			return;
905 		sparse_keymap_report_event(input, scancode, 0, false);
906 		vdbg_printk(FUJLAPTOP_DBG_TRACE,
907 			    "Pop scancode from ringbuffer [0x%x]\n", scancode);
908 	}
909 }
910 
911 static void acpi_fujitsu_laptop_notify(struct acpi_device *device, u32 event)
912 {
913 	struct input_dev *input;
914 	int scancode, i = 0;
915 	unsigned int irb;
916 
917 	input = fujitsu_laptop->input;
918 
919 	if (event != ACPI_FUJITSU_NOTIFY_CODE1) {
920 		vdbg_printk(FUJLAPTOP_DBG_WARN,
921 			    "Unsupported event [0x%x]\n", event);
922 		sparse_keymap_report_event(input, -1, 1, true);
923 		return;
924 	}
925 
926 	if (fujitsu_laptop->flags_supported)
927 		fujitsu_laptop->flags_state =
928 			call_fext_func(FUNC_FLAGS, 0x4, 0x0, 0x0);
929 
930 	while ((irb = call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0 &&
931 	       i++ < MAX_HOTKEY_RINGBUFFER_SIZE) {
932 		scancode = irb & 0x4ff;
933 		if (sparse_keymap_entry_from_scancode(input, scancode))
934 			acpi_fujitsu_laptop_press(scancode);
935 		else if (scancode == 0)
936 			acpi_fujitsu_laptop_release();
937 		else
938 			vdbg_printk(FUJLAPTOP_DBG_WARN,
939 				    "Unknown GIRB result [%x]\n", irb);
940 	}
941 
942 	/* On some models (first seen on the Skylake-based Lifebook
943 	 * E736/E746/E756), the touchpad toggle hotkey (Fn+F4) is
944 	 * handled in software; its state is queried using FUNC_FLAGS
945 	 */
946 	if ((fujitsu_laptop->flags_supported & BIT(26)) &&
947 	    (call_fext_func(FUNC_FLAGS, 0x1, 0x0, 0x0) & BIT(26)))
948 		sparse_keymap_report_event(input, BIT(26), 1, true);
949 }
950 
951 /* Initialization */
952 
953 static const struct acpi_device_id fujitsu_bl_device_ids[] = {
954 	{ACPI_FUJITSU_BL_HID, 0},
955 	{"", 0},
956 };
957 
958 static struct acpi_driver acpi_fujitsu_bl_driver = {
959 	.name = ACPI_FUJITSU_BL_DRIVER_NAME,
960 	.class = ACPI_FUJITSU_CLASS,
961 	.ids = fujitsu_bl_device_ids,
962 	.ops = {
963 		.add = acpi_fujitsu_bl_add,
964 		.notify = acpi_fujitsu_bl_notify,
965 		},
966 };
967 
968 static const struct acpi_device_id fujitsu_laptop_device_ids[] = {
969 	{ACPI_FUJITSU_LAPTOP_HID, 0},
970 	{"", 0},
971 };
972 
973 static struct acpi_driver acpi_fujitsu_laptop_driver = {
974 	.name = ACPI_FUJITSU_LAPTOP_DRIVER_NAME,
975 	.class = ACPI_FUJITSU_CLASS,
976 	.ids = fujitsu_laptop_device_ids,
977 	.ops = {
978 		.add = acpi_fujitsu_laptop_add,
979 		.remove = acpi_fujitsu_laptop_remove,
980 		.notify = acpi_fujitsu_laptop_notify,
981 		},
982 };
983 
984 static const struct acpi_device_id fujitsu_ids[] __used = {
985 	{ACPI_FUJITSU_BL_HID, 0},
986 	{ACPI_FUJITSU_LAPTOP_HID, 0},
987 	{"", 0}
988 };
989 MODULE_DEVICE_TABLE(acpi, fujitsu_ids);
990 
991 static int __init fujitsu_init(void)
992 {
993 	int ret;
994 
995 	if (acpi_disabled)
996 		return -ENODEV;
997 
998 	fujitsu_bl = kzalloc(sizeof(struct fujitsu_bl), GFP_KERNEL);
999 	if (!fujitsu_bl)
1000 		return -ENOMEM;
1001 
1002 	ret = acpi_bus_register_driver(&acpi_fujitsu_bl_driver);
1003 	if (ret)
1004 		goto err_free_fujitsu_bl;
1005 
1006 	/* Register platform stuff */
1007 
1008 	ret = platform_driver_register(&fujitsu_pf_driver);
1009 	if (ret)
1010 		goto err_unregister_acpi;
1011 
1012 	/* Register laptop driver */
1013 
1014 	fujitsu_laptop = kzalloc(sizeof(struct fujitsu_laptop), GFP_KERNEL);
1015 	if (!fujitsu_laptop) {
1016 		ret = -ENOMEM;
1017 		goto err_unregister_platform_driver;
1018 	}
1019 
1020 	ret = acpi_bus_register_driver(&acpi_fujitsu_laptop_driver);
1021 	if (ret)
1022 		goto err_free_fujitsu_laptop;
1023 
1024 	pr_info("driver " FUJITSU_DRIVER_VERSION " successfully loaded\n");
1025 
1026 	return 0;
1027 
1028 err_free_fujitsu_laptop:
1029 	kfree(fujitsu_laptop);
1030 err_unregister_platform_driver:
1031 	platform_driver_unregister(&fujitsu_pf_driver);
1032 err_unregister_acpi:
1033 	acpi_bus_unregister_driver(&acpi_fujitsu_bl_driver);
1034 err_free_fujitsu_bl:
1035 	kfree(fujitsu_bl);
1036 
1037 	return ret;
1038 }
1039 
1040 static void __exit fujitsu_cleanup(void)
1041 {
1042 	acpi_bus_unregister_driver(&acpi_fujitsu_laptop_driver);
1043 
1044 	kfree(fujitsu_laptop);
1045 
1046 	platform_driver_unregister(&fujitsu_pf_driver);
1047 
1048 	acpi_bus_unregister_driver(&acpi_fujitsu_bl_driver);
1049 
1050 	kfree(fujitsu_bl);
1051 
1052 	pr_info("driver unloaded\n");
1053 }
1054 
1055 module_init(fujitsu_init);
1056 module_exit(fujitsu_cleanup);
1057 
1058 module_param(use_alt_lcd_levels, int, 0644);
1059 MODULE_PARM_DESC(use_alt_lcd_levels, "Interface used for setting LCD brightness level (-1 = auto, 0 = force SBLL, 1 = force SBL2)");
1060 module_param(disable_brightness_adjust, bool, 0644);
1061 MODULE_PARM_DESC(disable_brightness_adjust, "Disable LCD brightness adjustment");
1062 #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
1063 module_param_named(debug, dbg_level, uint, 0644);
1064 MODULE_PARM_DESC(debug, "Sets debug level bit-mask");
1065 #endif
1066 
1067 MODULE_AUTHOR("Jonathan Woithe, Peter Gruber, Tony Vroon");
1068 MODULE_DESCRIPTION("Fujitsu laptop extras support");
1069 MODULE_VERSION(FUJITSU_DRIVER_VERSION);
1070 MODULE_LICENSE("GPL");
1071