blob: 68e338c6a876b396329f6f62ba8c5e35377aefdc [file] [log] [blame]
Jonathan Woithed0482532007-08-29 15:58:19 +09301/*-*-linux-c-*-*/
2
3/*
Jonathan Woithe409a3e92012-03-27 13:01:01 +10304 Copyright (C) 2007,2008 Jonathan Woithe <jwoithe@just42.net>
Jonathan Woithe20b93732008-06-11 10:14:56 +09305 Copyright (C) 2008 Peter Gruber <nokos@gmx.net>
Tony Vroon3a407082008-12-31 18:19:59 +00006 Copyright (C) 2008 Tony Vroon <tony@linx.net>
Jonathan Woithed0482532007-08-29 15:58:19 +09307 Based on earlier work:
8 Copyright (C) 2003 Shane Spencer <shane@bogomip.com>
9 Adrian Yee <brewt-fujitsu@brewt.org>
10
Jonathan Woithe20b93732008-06-11 10:14:56 +093011 Templated from msi-laptop.c and thinkpad_acpi.c which is copyright
12 by its respective authors.
Jonathan Woithed0482532007-08-29 15:58:19 +093013
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 *
Michał Kępień78b26022017-03-14 11:26:27 +010035 * This driver implements a vendor-specific backlight control interface for
36 * Fujitsu laptops and provides support for hotkeys present on certain Fujitsu
37 * laptops.
Jonathan Woithe20b93732008-06-11 10:14:56 +093038 *
Jonathan Woithe0e6a66e2008-10-09 13:44:40 +093039 * 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.
Jonathan Woithe20b93732008-06-11 10:14:56 +093042 *
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 *
Jonathan Woithed0482532007-08-29 15:58:19 +093048 */
49
Joe Perches77bad7c2011-03-29 15:21:39 -070050#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
51
Jonathan Woithed0482532007-08-29 15:58:19 +093052#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>
Michael Karchere8549e22015-01-18 20:28:46 +010058#include <linux/fb.h>
Jonathan Woithe20b93732008-06-11 10:14:56 +093059#include <linux/input.h>
60#include <linux/kfifo.h>
Jonathan Woithed0482532007-08-29 15:58:19 +093061#include <linux/platform_device.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090062#include <linux/slab.h>
Javier Martinez Canillas575b2452016-04-26 18:28:17 -040063#if IS_ENABLED(CONFIG_LEDS_CLASS)
Tony Vroon3a407082008-12-31 18:19:59 +000064#include <linux/leds.h>
65#endif
Hans de Goede413226f2015-06-16 16:28:03 +020066#include <acpi/video.h>
Jonathan Woithed0482532007-08-29 15:58:19 +093067
Jonathan Woithe84a6ce22009-07-31 18:16:59 +093068#define FUJITSU_DRIVER_VERSION "0.6.0"
Jonathan Woithed0482532007-08-29 15:58:19 +093069
70#define FUJITSU_LCD_N_LEVELS 8
71
Alan Jenkins9fc5cf62017-02-08 14:46:24 +010072#define ACPI_FUJITSU_CLASS "fujitsu"
73#define ACPI_FUJITSU_BL_HID "FUJ02B1"
74#define ACPI_FUJITSU_BL_DRIVER_NAME "Fujitsu laptop FUJ02B1 ACPI brightness driver"
75#define ACPI_FUJITSU_BL_DEVICE_NAME "Fujitsu FUJ02B1"
Alan Jenkins6942eab2017-02-08 14:46:25 +010076#define ACPI_FUJITSU_LAPTOP_HID "FUJ02E3"
77#define ACPI_FUJITSU_LAPTOP_DRIVER_NAME "Fujitsu laptop FUJ02E3 ACPI hotkeys driver"
78#define ACPI_FUJITSU_LAPTOP_DEVICE_NAME "Fujitsu FUJ02E3"
Jonathan Woithed0482532007-08-29 15:58:19 +093079
Jonathan Woithe20b93732008-06-11 10:14:56 +093080#define ACPI_FUJITSU_NOTIFY_CODE1 0x80
81
Tony Vroon3a407082008-12-31 18:19:59 +000082/* FUNC interface - command values */
Alan Jenkins8ef27bd2017-02-08 14:46:27 +010083#define FUNC_FLAGS 0x1000
Tony Vroon3a407082008-12-31 18:19:59 +000084#define FUNC_LEDS 0x1001
85#define FUNC_BUTTONS 0x1002
86#define FUNC_BACKLIGHT 0x1004
87
88/* FUNC interface - responses */
89#define UNSUPPORTED_CMD 0x80000000
90
Alan Jenkinsd3dd4482017-02-08 14:46:28 +010091/* FUNC interface - status flags */
92#define FLAG_RFKILL 0x020
93#define FLAG_LID 0x100
94#define FLAG_DOCK 0x200
95
Javier Martinez Canillas575b2452016-04-26 18:28:17 -040096#if IS_ENABLED(CONFIG_LEDS_CLASS)
Tony Vroon3a407082008-12-31 18:19:59 +000097/* FUNC interface - LED control */
98#define FUNC_LED_OFF 0x1
99#define FUNC_LED_ON 0x30001
100#define KEYBOARD_LAMPS 0x100
101#define LOGOLAMP_POWERON 0x2000
102#define LOGOLAMP_ALWAYS 0x4000
Michał Kępień4f625682016-04-12 22:06:34 +0930103#define RADIO_LED_ON 0x20
Matej Gromad6b88f62016-06-21 10:09:21 +0200104#define ECO_LED 0x10000
105#define ECO_LED_ON 0x80000
Tony Vroon3a407082008-12-31 18:19:59 +0000106#endif
107
Jonathan Woithe20b93732008-06-11 10:14:56 +0930108/* Hotkey details */
Jonathan Woithe0e6a66e2008-10-09 13:44:40 +0930109#define KEY1_CODE 0x410 /* codes for the keys in the GIRB register */
110#define KEY2_CODE 0x411
111#define KEY3_CODE 0x412
112#define KEY4_CODE 0x413
Michał Kępieńb5df36c2016-02-24 14:23:32 +0100113#define KEY5_CODE 0x420
Jonathan Woithe20b93732008-06-11 10:14:56 +0930114
115#define MAX_HOTKEY_RINGBUFFER_SIZE 100
116#define RINGBUFFERSIZE 40
117
118/* Debugging */
Jonathan Woithe20b93732008-06-11 10:14:56 +0930119#define FUJLAPTOP_DBG_ERROR 0x0001
120#define FUJLAPTOP_DBG_WARN 0x0002
121#define FUJLAPTOP_DBG_INFO 0x0004
122#define FUJLAPTOP_DBG_TRACE 0x0008
123
Jean Delvarec4960cf2014-06-16 11:55:13 +0200124#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
125#define vdbg_printk(a_dbg_level, format, arg...) \
Jonathan Woithe20b93732008-06-11 10:14:56 +0930126 do { if (dbg_level & a_dbg_level) \
Michał Kępień98020a42016-06-23 12:02:47 +0200127 printk(KERN_DEBUG pr_fmt("%s: " format), __func__, ## arg); \
Jonathan Woithe20b93732008-06-11 10:14:56 +0930128 } while (0)
Jonathan Woithe20b93732008-06-11 10:14:56 +0930129#else
Jean Delvarec4960cf2014-06-16 11:55:13 +0200130#define vdbg_printk(a_dbg_level, format, arg...) \
131 do { } while (0)
Jonathan Woithe20b93732008-06-11 10:14:56 +0930132#endif
133
134/* Device controlling the backlight and associated keys */
Alan Jenkins9fc5cf62017-02-08 14:46:24 +0100135struct fujitsu_bl {
Jonathan Woithed0482532007-08-29 15:58:19 +0930136 acpi_handle acpi_handle;
Jonathan Woithe20b93732008-06-11 10:14:56 +0930137 struct acpi_device *dev;
138 struct input_dev *input;
139 char phys[32];
Jonathan Woithed0482532007-08-29 15:58:19 +0930140 struct backlight_device *bl_device;
Michał Kępieńb5df36c2016-02-24 14:23:32 +0100141 int keycode1, keycode2, keycode3, keycode4, keycode5;
Jonathan Woithed0482532007-08-29 15:58:19 +0930142
Jonathan Woithe20b93732008-06-11 10:14:56 +0930143 unsigned int max_brightness;
Jonathan Woithed0482532007-08-29 15:58:19 +0930144 unsigned int brightness_changed;
145 unsigned int brightness_level;
146};
147
Alan Jenkins9fc5cf62017-02-08 14:46:24 +0100148static struct fujitsu_bl *fujitsu_bl;
Jonathan Woithe20b93732008-06-11 10:14:56 +0930149static int use_alt_lcd_levels = -1;
Jonathan Woithe20b93732008-06-11 10:14:56 +0930150static int disable_brightness_adjust = -1;
Jonathan Woithed0482532007-08-29 15:58:19 +0930151
Alan Jenkins6942eab2017-02-08 14:46:25 +0100152/* Device used to access hotkeys and other features on the laptop */
153struct fujitsu_laptop {
Jonathan Woithe20b93732008-06-11 10:14:56 +0930154 acpi_handle acpi_handle;
155 struct acpi_device *dev;
156 struct input_dev *input;
157 char phys[32];
158 struct platform_device *pf_device;
Stefani Seibold45465482009-12-21 14:37:26 -0800159 struct kfifo fifo;
Jonathan Woithe20b93732008-06-11 10:14:56 +0930160 spinlock_t fifo_lock;
Alan Jenkins8ef27bd2017-02-08 14:46:27 +0100161 int flags_supported;
162 int flags_state;
Tony Vroon3a407082008-12-31 18:19:59 +0000163 int logolamp_registered;
164 int kblamps_registered;
Michał Kępień4f625682016-04-12 22:06:34 +0930165 int radio_led_registered;
Matej Gromad6b88f62016-06-21 10:09:21 +0200166 int eco_led_registered;
Jonathan Woithe20b93732008-06-11 10:14:56 +0930167};
168
Alan Jenkins6942eab2017-02-08 14:46:25 +0100169static struct fujitsu_laptop *fujitsu_laptop;
Jonathan Woithe20b93732008-06-11 10:14:56 +0930170
Javier Martinez Canillas575b2452016-04-26 18:28:17 -0400171#if IS_ENABLED(CONFIG_LEDS_CLASS)
Tony Vroon3a407082008-12-31 18:19:59 +0000172static enum led_brightness logolamp_get(struct led_classdev *cdev);
Micha? K?pie?a608a9d2016-12-23 10:00:08 +0100173static int logolamp_set(struct led_classdev *cdev,
Tony Vroon3a407082008-12-31 18:19:59 +0000174 enum led_brightness brightness);
175
Axel Lin67af7112010-07-20 15:19:45 -0700176static struct led_classdev logolamp_led = {
Tony Vroon3a407082008-12-31 18:19:59 +0000177 .name = "fujitsu::logolamp",
178 .brightness_get = logolamp_get,
Micha? K?pie?a608a9d2016-12-23 10:00:08 +0100179 .brightness_set_blocking = logolamp_set
Tony Vroon3a407082008-12-31 18:19:59 +0000180};
181
182static enum led_brightness kblamps_get(struct led_classdev *cdev);
Micha? K?pie?a608a9d2016-12-23 10:00:08 +0100183static int kblamps_set(struct led_classdev *cdev,
Tony Vroon3a407082008-12-31 18:19:59 +0000184 enum led_brightness brightness);
185
Axel Lin67af7112010-07-20 15:19:45 -0700186static struct led_classdev kblamps_led = {
Tony Vroon3a407082008-12-31 18:19:59 +0000187 .name = "fujitsu::kblamps",
188 .brightness_get = kblamps_get,
Micha? K?pie?a608a9d2016-12-23 10:00:08 +0100189 .brightness_set_blocking = kblamps_set
Tony Vroon3a407082008-12-31 18:19:59 +0000190};
Michał Kępień4f625682016-04-12 22:06:34 +0930191
192static enum led_brightness radio_led_get(struct led_classdev *cdev);
Micha? K?pie?a608a9d2016-12-23 10:00:08 +0100193static int radio_led_set(struct led_classdev *cdev,
Michał Kępień4f625682016-04-12 22:06:34 +0930194 enum led_brightness brightness);
195
196static struct led_classdev radio_led = {
197 .name = "fujitsu::radio_led",
Micha? K?pie?5f25b002016-12-16 15:46:03 +0100198 .default_trigger = "rfkill-any",
Michał Kępień4f625682016-04-12 22:06:34 +0930199 .brightness_get = radio_led_get,
Micha? K?pie?a608a9d2016-12-23 10:00:08 +0100200 .brightness_set_blocking = radio_led_set
Michał Kępień4f625682016-04-12 22:06:34 +0930201};
Matej Gromad6b88f62016-06-21 10:09:21 +0200202
203static enum led_brightness eco_led_get(struct led_classdev *cdev);
Micha? K?pie?a608a9d2016-12-23 10:00:08 +0100204static int eco_led_set(struct led_classdev *cdev,
Matej Gromad6b88f62016-06-21 10:09:21 +0200205 enum led_brightness brightness);
206
207static struct led_classdev eco_led = {
208 .name = "fujitsu::eco_led",
Matej Gromad6b88f62016-06-21 10:09:21 +0200209 .brightness_get = eco_led_get,
Micha? K?pie?a608a9d2016-12-23 10:00:08 +0100210 .brightness_set_blocking = eco_led_set
Matej Gromad6b88f62016-06-21 10:09:21 +0200211};
Tony Vroon3a407082008-12-31 18:19:59 +0000212#endif
213
Jonathan Woithe20b93732008-06-11 10:14:56 +0930214#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
215static u32 dbg_level = 0x03;
216#endif
217
Tony Vroon3a407082008-12-31 18:19:59 +0000218/* Fujitsu ACPI interface function */
219
220static int call_fext_func(int cmd, int arg0, int arg1, int arg2)
221{
222 acpi_status status = AE_OK;
223 union acpi_object params[4] = {
224 { .type = ACPI_TYPE_INTEGER },
225 { .type = ACPI_TYPE_INTEGER },
226 { .type = ACPI_TYPE_INTEGER },
227 { .type = ACPI_TYPE_INTEGER }
228 };
229 struct acpi_object_list arg_list = { 4, &params[0] };
Zhang Rui29c29a92013-09-03 08:32:12 +0800230 unsigned long long value;
Tony Vroon3a407082008-12-31 18:19:59 +0000231 acpi_handle handle = NULL;
232
Alan Jenkins6942eab2017-02-08 14:46:25 +0100233 status = acpi_get_handle(fujitsu_laptop->acpi_handle, "FUNC", &handle);
Tony Vroon3a407082008-12-31 18:19:59 +0000234 if (ACPI_FAILURE(status)) {
235 vdbg_printk(FUJLAPTOP_DBG_ERROR,
236 "FUNC interface is not present\n");
237 return -ENODEV;
238 }
239
240 params[0].integer.value = cmd;
241 params[1].integer.value = arg0;
242 params[2].integer.value = arg1;
243 params[3].integer.value = arg2;
244
Zhang Rui29c29a92013-09-03 08:32:12 +0800245 status = acpi_evaluate_integer(handle, NULL, &arg_list, &value);
Tony Vroon3a407082008-12-31 18:19:59 +0000246 if (ACPI_FAILURE(status)) {
247 vdbg_printk(FUJLAPTOP_DBG_WARN,
248 "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) call failed\n",
249 cmd, arg0, arg1, arg2);
250 return -ENODEV;
251 }
252
Tony Vroon3a407082008-12-31 18:19:59 +0000253 vdbg_printk(FUJLAPTOP_DBG_TRACE,
254 "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) returned 0x%x\n",
Zhang Rui29c29a92013-09-03 08:32:12 +0800255 cmd, arg0, arg1, arg2, (int)value);
256 return value;
Tony Vroon3a407082008-12-31 18:19:59 +0000257}
258
Javier Martinez Canillas575b2452016-04-26 18:28:17 -0400259#if IS_ENABLED(CONFIG_LEDS_CLASS)
Tony Vroon3a407082008-12-31 18:19:59 +0000260/* LED class callbacks */
261
Micha? K?pie?a608a9d2016-12-23 10:00:08 +0100262static int logolamp_set(struct led_classdev *cdev,
Tony Vroon3a407082008-12-31 18:19:59 +0000263 enum led_brightness brightness)
264{
Michał Kępieńdcb50b32017-01-09 14:14:16 +0100265 int poweron = FUNC_LED_ON, always = FUNC_LED_ON;
266 int ret;
267
268 if (brightness < LED_HALF)
269 poweron = FUNC_LED_OFF;
270
271 if (brightness < LED_FULL)
272 always = FUNC_LED_OFF;
273
274 ret = call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, poweron);
275 if (ret < 0)
276 return ret;
277
278 return call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, always);
Tony Vroon3a407082008-12-31 18:19:59 +0000279}
280
Micha? K?pie?a608a9d2016-12-23 10:00:08 +0100281static int kblamps_set(struct led_classdev *cdev,
Tony Vroon3a407082008-12-31 18:19:59 +0000282 enum led_brightness brightness)
283{
284 if (brightness >= LED_FULL)
Micha? K?pie?a608a9d2016-12-23 10:00:08 +0100285 return call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS, FUNC_LED_ON);
Tony Vroon3a407082008-12-31 18:19:59 +0000286 else
Micha? K?pie?a608a9d2016-12-23 10:00:08 +0100287 return call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS, FUNC_LED_OFF);
Tony Vroon3a407082008-12-31 18:19:59 +0000288}
289
Micha? K?pie?a608a9d2016-12-23 10:00:08 +0100290static int radio_led_set(struct led_classdev *cdev,
Michał Kępień4f625682016-04-12 22:06:34 +0930291 enum led_brightness brightness)
292{
293 if (brightness >= LED_FULL)
Alan Jenkins8ef27bd2017-02-08 14:46:27 +0100294 return call_fext_func(FUNC_FLAGS, 0x5, RADIO_LED_ON, RADIO_LED_ON);
Michał Kępień4f625682016-04-12 22:06:34 +0930295 else
Alan Jenkins8ef27bd2017-02-08 14:46:27 +0100296 return call_fext_func(FUNC_FLAGS, 0x5, RADIO_LED_ON, 0x0);
Michał Kępień4f625682016-04-12 22:06:34 +0930297}
298
Micha? K?pie?a608a9d2016-12-23 10:00:08 +0100299static int eco_led_set(struct led_classdev *cdev,
Matej Gromad6b88f62016-06-21 10:09:21 +0200300 enum led_brightness brightness)
301{
302 int curr;
303
304 curr = call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0);
Matej Groma69678932016-07-04 12:04:12 +0200305 if (brightness >= LED_FULL)
Micha? K?pie?a608a9d2016-12-23 10:00:08 +0100306 return call_fext_func(FUNC_LEDS, 0x1, ECO_LED, curr | ECO_LED_ON);
Matej Gromad6b88f62016-06-21 10:09:21 +0200307 else
Micha? K?pie?a608a9d2016-12-23 10:00:08 +0100308 return call_fext_func(FUNC_LEDS, 0x1, ECO_LED, curr & ~ECO_LED_ON);
Matej Gromad6b88f62016-06-21 10:09:21 +0200309}
310
Tony Vroon3a407082008-12-31 18:19:59 +0000311static enum led_brightness logolamp_get(struct led_classdev *cdev)
312{
Michał Kępień5c461e82017-01-09 14:14:17 +0100313 int ret;
Tony Vroon3a407082008-12-31 18:19:59 +0000314
Michał Kępień5c461e82017-01-09 14:14:17 +0100315 ret = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0);
316 if (ret == FUNC_LED_ON)
317 return LED_FULL;
318
319 ret = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0);
320 if (ret == FUNC_LED_ON)
321 return LED_HALF;
322
323 return LED_OFF;
Tony Vroon3a407082008-12-31 18:19:59 +0000324}
325
326static enum led_brightness kblamps_get(struct led_classdev *cdev)
327{
328 enum led_brightness brightness = LED_OFF;
329
330 if (call_fext_func(FUNC_LEDS, 0x2, KEYBOARD_LAMPS, 0x0) == FUNC_LED_ON)
331 brightness = LED_FULL;
332
333 return brightness;
334}
Michał Kępień4f625682016-04-12 22:06:34 +0930335
336static enum led_brightness radio_led_get(struct led_classdev *cdev)
337{
338 enum led_brightness brightness = LED_OFF;
339
Alan Jenkins8ef27bd2017-02-08 14:46:27 +0100340 if (call_fext_func(FUNC_FLAGS, 0x4, 0x0, 0x0) & RADIO_LED_ON)
Michał Kępień4f625682016-04-12 22:06:34 +0930341 brightness = LED_FULL;
342
343 return brightness;
344}
Matej Gromad6b88f62016-06-21 10:09:21 +0200345
346static enum led_brightness eco_led_get(struct led_classdev *cdev)
347{
348 enum led_brightness brightness = LED_OFF;
349
350 if (call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0) & ECO_LED_ON)
Matej Groma69678932016-07-04 12:04:12 +0200351 brightness = LED_FULL;
Matej Gromad6b88f62016-06-21 10:09:21 +0200352
353 return brightness;
354}
Tony Vroon3a407082008-12-31 18:19:59 +0000355#endif
356
Jonathan Woithe20b93732008-06-11 10:14:56 +0930357/* Hardware access for LCD brightness control */
Jonathan Woithed0482532007-08-29 15:58:19 +0930358
359static int set_lcd_level(int level)
360{
361 acpi_status status = AE_OK;
Jonathan Woithed0482532007-08-29 15:58:19 +0930362 acpi_handle handle = NULL;
363
Jonathan Woithe20b93732008-06-11 10:14:56 +0930364 vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via SBLL [%d]\n",
365 level);
366
Alan Jenkins9fc5cf62017-02-08 14:46:24 +0100367 if (level < 0 || level >= fujitsu_bl->max_brightness)
Jonathan Woithed0482532007-08-29 15:58:19 +0930368 return -EINVAL;
369
Alan Jenkins9fc5cf62017-02-08 14:46:24 +0100370 status = acpi_get_handle(fujitsu_bl->acpi_handle, "SBLL", &handle);
Jonathan Woithed0482532007-08-29 15:58:19 +0930371 if (ACPI_FAILURE(status)) {
Jonathan Woithe20b93732008-06-11 10:14:56 +0930372 vdbg_printk(FUJLAPTOP_DBG_ERROR, "SBLL not present\n");
373 return -ENODEV;
374 }
375
Jonathan Woithe20b93732008-06-11 10:14:56 +0930376
Zhang Rui6c7fe47a2013-09-03 08:31:52 +0800377 status = acpi_execute_simple_method(handle, NULL, level);
Jonathan Woithe20b93732008-06-11 10:14:56 +0930378 if (ACPI_FAILURE(status))
379 return -ENODEV;
380
381 return 0;
382}
383
384static int set_lcd_level_alt(int level)
385{
386 acpi_status status = AE_OK;
Jonathan Woithe20b93732008-06-11 10:14:56 +0930387 acpi_handle handle = NULL;
388
389 vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via SBL2 [%d]\n",
390 level);
391
Alan Jenkins9fc5cf62017-02-08 14:46:24 +0100392 if (level < 0 || level >= fujitsu_bl->max_brightness)
Jonathan Woithe20b93732008-06-11 10:14:56 +0930393 return -EINVAL;
394
Alan Jenkins9fc5cf62017-02-08 14:46:24 +0100395 status = acpi_get_handle(fujitsu_bl->acpi_handle, "SBL2", &handle);
Jonathan Woithe20b93732008-06-11 10:14:56 +0930396 if (ACPI_FAILURE(status)) {
397 vdbg_printk(FUJLAPTOP_DBG_ERROR, "SBL2 not present\n");
Jonathan Woithed0482532007-08-29 15:58:19 +0930398 return -ENODEV;
399 }
400
Zhang Rui6c7fe47a2013-09-03 08:31:52 +0800401 status = acpi_execute_simple_method(handle, NULL, level);
Jonathan Woithed0482532007-08-29 15:58:19 +0930402 if (ACPI_FAILURE(status))
403 return -ENODEV;
404
405 return 0;
406}
407
408static int get_lcd_level(void)
409{
Matthew Wilcox27663c52008-10-10 02:22:59 -0400410 unsigned long long state = 0;
Jonathan Woithed0482532007-08-29 15:58:19 +0930411 acpi_status status = AE_OK;
412
Jonathan Woithe20b93732008-06-11 10:14:56 +0930413 vdbg_printk(FUJLAPTOP_DBG_TRACE, "get lcd level via GBLL\n");
414
Alan Jenkins9fc5cf62017-02-08 14:46:24 +0100415 status = acpi_evaluate_integer(fujitsu_bl->acpi_handle, "GBLL", NULL,
416 &state);
Jonathan Woithe3b1c37c2009-12-23 09:19:42 +1030417 if (ACPI_FAILURE(status))
418 return 0;
Jonathan Woithed0482532007-08-29 15:58:19 +0930419
Alan Jenkins9fc5cf62017-02-08 14:46:24 +0100420 fujitsu_bl->brightness_level = state & 0x0fffffff;
Jonathan Woithe20b93732008-06-11 10:14:56 +0930421
422 if (state & 0x80000000)
Alan Jenkins9fc5cf62017-02-08 14:46:24 +0100423 fujitsu_bl->brightness_changed = 1;
Jonathan Woithe20b93732008-06-11 10:14:56 +0930424 else
Alan Jenkins9fc5cf62017-02-08 14:46:24 +0100425 fujitsu_bl->brightness_changed = 0;
Jonathan Woithe20b93732008-06-11 10:14:56 +0930426
Alan Jenkins9fc5cf62017-02-08 14:46:24 +0100427 return fujitsu_bl->brightness_level;
Jonathan Woithe20b93732008-06-11 10:14:56 +0930428}
429
430static int get_max_brightness(void)
431{
Matthew Wilcox27663c52008-10-10 02:22:59 -0400432 unsigned long long state = 0;
Jonathan Woithe20b93732008-06-11 10:14:56 +0930433 acpi_status status = AE_OK;
434
435 vdbg_printk(FUJLAPTOP_DBG_TRACE, "get max lcd level via RBLL\n");
436
Alan Jenkins9fc5cf62017-02-08 14:46:24 +0100437 status = acpi_evaluate_integer(fujitsu_bl->acpi_handle, "RBLL", NULL,
438 &state);
Jonathan Woithe3b1c37c2009-12-23 09:19:42 +1030439 if (ACPI_FAILURE(status))
440 return -1;
Jonathan Woithe20b93732008-06-11 10:14:56 +0930441
Alan Jenkins9fc5cf62017-02-08 14:46:24 +0100442 fujitsu_bl->max_brightness = state;
Jonathan Woithe20b93732008-06-11 10:14:56 +0930443
Alan Jenkins9fc5cf62017-02-08 14:46:24 +0100444 return fujitsu_bl->max_brightness;
Jonathan Woithe20b93732008-06-11 10:14:56 +0930445}
446
Jonathan Woithed0482532007-08-29 15:58:19 +0930447/* Backlight device stuff */
448
449static int bl_get_brightness(struct backlight_device *b)
450{
Tony Vroonf87a1a52009-01-07 10:11:24 +0000451 return get_lcd_level();
Jonathan Woithed0482532007-08-29 15:58:19 +0930452}
453
454static int bl_update_status(struct backlight_device *b)
455{
Tony Vroon3a407082008-12-31 18:19:59 +0000456 int ret;
Michael Karchere8549e22015-01-18 20:28:46 +0100457 if (b->props.power == FB_BLANK_POWERDOWN)
Tony Vroon3a407082008-12-31 18:19:59 +0000458 ret = call_fext_func(FUNC_BACKLIGHT, 0x1, 0x4, 0x3);
Jonathan Woithe20b93732008-06-11 10:14:56 +0930459 else
Tony Vroon3a407082008-12-31 18:19:59 +0000460 ret = call_fext_func(FUNC_BACKLIGHT, 0x1, 0x4, 0x0);
461 if (ret != 0)
462 vdbg_printk(FUJLAPTOP_DBG_ERROR,
463 "Unable to adjust backlight power, error code %i\n",
464 ret);
465
466 if (use_alt_lcd_levels)
467 ret = set_lcd_level_alt(b->props.brightness);
468 else
469 ret = set_lcd_level(b->props.brightness);
470 if (ret != 0)
471 vdbg_printk(FUJLAPTOP_DBG_ERROR,
472 "Unable to adjust LCD brightness, error code %i\n",
473 ret);
474 return ret;
Jonathan Woithed0482532007-08-29 15:58:19 +0930475}
476
Alan Jenkins9fc5cf62017-02-08 14:46:24 +0100477static const struct backlight_ops fujitsu_bl_ops = {
Jonathan Woithed0482532007-08-29 15:58:19 +0930478 .get_brightness = bl_get_brightness,
479 .update_status = bl_update_status,
480};
481
Michał Kępieńb0c4b9c2017-03-14 11:26:28 +0100482static ssize_t lid_show(struct device *dev, struct device_attribute *attr,
483 char *buf)
Tony Vroon3a407082008-12-31 18:19:59 +0000484{
Alan Jenkinsd3dd4482017-02-08 14:46:28 +0100485 if (!(fujitsu_laptop->flags_supported & FLAG_LID))
Tony Vroon3a407082008-12-31 18:19:59 +0000486 return sprintf(buf, "unknown\n");
Alan Jenkinsd3dd4482017-02-08 14:46:28 +0100487 if (fujitsu_laptop->flags_state & FLAG_LID)
Tony Vroon3a407082008-12-31 18:19:59 +0000488 return sprintf(buf, "open\n");
489 else
490 return sprintf(buf, "closed\n");
491}
492
Michał Kępieńb0c4b9c2017-03-14 11:26:28 +0100493static ssize_t dock_show(struct device *dev, struct device_attribute *attr,
494 char *buf)
Tony Vroon3a407082008-12-31 18:19:59 +0000495{
Alan Jenkinsd3dd4482017-02-08 14:46:28 +0100496 if (!(fujitsu_laptop->flags_supported & FLAG_DOCK))
Tony Vroon3a407082008-12-31 18:19:59 +0000497 return sprintf(buf, "unknown\n");
Alan Jenkinsd3dd4482017-02-08 14:46:28 +0100498 if (fujitsu_laptop->flags_state & FLAG_DOCK)
Tony Vroon3a407082008-12-31 18:19:59 +0000499 return sprintf(buf, "docked\n");
500 else
501 return sprintf(buf, "undocked\n");
502}
503
Michał Kępieńb0c4b9c2017-03-14 11:26:28 +0100504static ssize_t radios_show(struct device *dev, struct device_attribute *attr,
505 char *buf)
Tony Vroon3a407082008-12-31 18:19:59 +0000506{
Alan Jenkinsd3dd4482017-02-08 14:46:28 +0100507 if (!(fujitsu_laptop->flags_supported & FLAG_RFKILL))
Tony Vroon3a407082008-12-31 18:19:59 +0000508 return sprintf(buf, "unknown\n");
Alan Jenkinsd3dd4482017-02-08 14:46:28 +0100509 if (fujitsu_laptop->flags_state & FLAG_RFKILL)
Tony Vroon3a407082008-12-31 18:19:59 +0000510 return sprintf(buf, "on\n");
511 else
512 return sprintf(buf, "killed\n");
513}
514
Michał Kępieńb0c4b9c2017-03-14 11:26:28 +0100515static DEVICE_ATTR_RO(lid);
516static DEVICE_ATTR_RO(dock);
517static DEVICE_ATTR_RO(radios);
Jonathan Woithed0482532007-08-29 15:58:19 +0930518
Alan Jenkins16506022017-02-08 14:46:26 +0100519static struct attribute *fujitsu_pf_attributes[] = {
Tony Vroon3a407082008-12-31 18:19:59 +0000520 &dev_attr_lid.attr,
521 &dev_attr_dock.attr,
522 &dev_attr_radios.attr,
Jonathan Woithed0482532007-08-29 15:58:19 +0930523 NULL
524};
525
Alan Jenkins16506022017-02-08 14:46:26 +0100526static struct attribute_group fujitsu_pf_attribute_group = {
527 .attrs = fujitsu_pf_attributes
Jonathan Woithed0482532007-08-29 15:58:19 +0930528};
529
Alan Jenkins16506022017-02-08 14:46:26 +0100530static struct platform_driver fujitsu_pf_driver = {
Jonathan Woithed0482532007-08-29 15:58:19 +0930531 .driver = {
532 .name = "fujitsu-laptop",
Jonathan Woithed0482532007-08-29 15:58:19 +0930533 }
534};
535
Mathias Krausefbe9b792014-07-16 19:43:11 +0200536static void __init dmi_check_cb_common(const struct dmi_system_id *id)
Jonathan Woithe20b93732008-06-11 10:14:56 +0930537{
Joe Perches77bad7c2011-03-29 15:21:39 -0700538 pr_info("Identified laptop model '%s'\n", id->ident);
Jonathan Woithe0e6a66e2008-10-09 13:44:40 +0930539}
540
Mathias Krausefbe9b792014-07-16 19:43:11 +0200541static int __init dmi_check_cb_s6410(const struct dmi_system_id *id)
Jonathan Woithe0e6a66e2008-10-09 13:44:40 +0930542{
543 dmi_check_cb_common(id);
Alan Jenkins9fc5cf62017-02-08 14:46:24 +0100544 fujitsu_bl->keycode1 = KEY_SCREENLOCK; /* "Lock" */
545 fujitsu_bl->keycode2 = KEY_HELP; /* "Mobility Center" */
Axel Lin80183a42010-07-20 15:19:40 -0700546 return 1;
Jonathan Woithe0e6a66e2008-10-09 13:44:40 +0930547}
548
Mathias Krausefbe9b792014-07-16 19:43:11 +0200549static int __init dmi_check_cb_s6420(const struct dmi_system_id *id)
Tony Vroon56960b52008-11-09 04:20:05 +0000550{
551 dmi_check_cb_common(id);
Alan Jenkins9fc5cf62017-02-08 14:46:24 +0100552 fujitsu_bl->keycode1 = KEY_SCREENLOCK; /* "Lock" */
553 fujitsu_bl->keycode2 = KEY_HELP; /* "Mobility Center" */
Axel Lin80183a42010-07-20 15:19:40 -0700554 return 1;
Tony Vroon56960b52008-11-09 04:20:05 +0000555}
556
Mathias Krausefbe9b792014-07-16 19:43:11 +0200557static int __init dmi_check_cb_p8010(const struct dmi_system_id *id)
Jonathan Woithe0e6a66e2008-10-09 13:44:40 +0930558{
559 dmi_check_cb_common(id);
Alan Jenkins9fc5cf62017-02-08 14:46:24 +0100560 fujitsu_bl->keycode1 = KEY_HELP; /* "Support" */
561 fujitsu_bl->keycode3 = KEY_SWITCHVIDEOMODE; /* "Presentation" */
562 fujitsu_bl->keycode4 = KEY_WWW; /* "Internet" */
Axel Lin80183a42010-07-20 15:19:40 -0700563 return 1;
Jonathan Woithe20b93732008-06-11 10:14:56 +0930564}
565
Mathias Krausefbe9b792014-07-16 19:43:11 +0200566static const struct dmi_system_id fujitsu_dmi_table[] __initconst = {
Jonathan Woithe20b93732008-06-11 10:14:56 +0930567 {
Jonathan Woithe0e6a66e2008-10-09 13:44:40 +0930568 .ident = "Fujitsu Siemens S6410",
Jonathan Woithe20b93732008-06-11 10:14:56 +0930569 .matches = {
570 DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
571 DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6410"),
572 },
573 .callback = dmi_check_cb_s6410},
Jonathan Woithed8196a92008-08-29 11:06:21 +0930574 {
Tony Vroon56960b52008-11-09 04:20:05 +0000575 .ident = "Fujitsu Siemens S6420",
576 .matches = {
577 DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
578 DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6420"),
579 },
580 .callback = dmi_check_cb_s6420},
581 {
Jonathan Woithe0e6a66e2008-10-09 13:44:40 +0930582 .ident = "Fujitsu LifeBook P8010",
Jonathan Woithed8196a92008-08-29 11:06:21 +0930583 .matches = {
584 DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
585 DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook P8010"),
Jonathan Woithe0e6a66e2008-10-09 13:44:40 +0930586 },
587 .callback = dmi_check_cb_p8010},
Jonathan Woithe20b93732008-06-11 10:14:56 +0930588 {}
589};
590
591/* ACPI device for LCD brightness control */
Jonathan Woithed0482532007-08-29 15:58:19 +0930592
Michał Kępień7d134e42017-03-20 10:32:17 +0100593static int acpi_fujitsu_bl_input_setup(struct acpi_device *device)
594{
595 struct fujitsu_bl *fujitsu_bl = acpi_driver_data(device);
Michał Kępień7d134e42017-03-20 10:32:17 +0100596
Michał Kępieńf8a399d2017-03-20 10:32:18 +0100597 fujitsu_bl->input = devm_input_allocate_device(&device->dev);
598 if (!fujitsu_bl->input)
Michał Kępień7d134e42017-03-20 10:32:17 +0100599 return -ENOMEM;
600
601 snprintf(fujitsu_bl->phys, sizeof(fujitsu_bl->phys),
602 "%s/video/input0", acpi_device_hid(device));
603
Michał Kępieńf8a399d2017-03-20 10:32:18 +0100604 fujitsu_bl->input->name = acpi_device_name(device);
605 fujitsu_bl->input->phys = fujitsu_bl->phys;
606 fujitsu_bl->input->id.bustype = BUS_HOST;
607 fujitsu_bl->input->id.product = 0x06;
608 fujitsu_bl->input->evbit[0] = BIT(EV_KEY);
609 set_bit(KEY_BRIGHTNESSUP, fujitsu_bl->input->keybit);
610 set_bit(KEY_BRIGHTNESSDOWN, fujitsu_bl->input->keybit);
611 set_bit(KEY_UNKNOWN, fujitsu_bl->input->keybit);
Michał Kępień7d134e42017-03-20 10:32:17 +0100612
Michał Kępieńf8a399d2017-03-20 10:32:18 +0100613 return input_register_device(fujitsu_bl->input);
Michał Kępień7d134e42017-03-20 10:32:17 +0100614}
615
Michał Kępieńb8d69c12017-03-10 11:50:33 +0100616static int fujitsu_backlight_register(void)
617{
618 struct backlight_properties props = {
619 .brightness = fujitsu_bl->brightness_level,
620 .max_brightness = fujitsu_bl->max_brightness - 1,
621 .type = BACKLIGHT_PLATFORM
622 };
623 struct backlight_device *bd;
624
625 bd = backlight_device_register("fujitsu-laptop", NULL, NULL,
626 &fujitsu_bl_ops, &props);
627 if (IS_ERR(bd))
628 return PTR_ERR(bd);
629
630 fujitsu_bl->bl_device = bd;
631
632 return 0;
633}
634
Alan Jenkins9fc5cf62017-02-08 14:46:24 +0100635static int acpi_fujitsu_bl_add(struct acpi_device *device)
Jonathan Woithed0482532007-08-29 15:58:19 +0930636{
Jonathan Woithed0482532007-08-29 15:58:19 +0930637 int state = 0;
Jonathan Woithe20b93732008-06-11 10:14:56 +0930638 int error;
Jonathan Woithed0482532007-08-29 15:58:19 +0930639
640 if (!device)
641 return -EINVAL;
642
Alan Jenkins9fc5cf62017-02-08 14:46:24 +0100643 fujitsu_bl->acpi_handle = device->handle;
644 sprintf(acpi_device_name(device), "%s", ACPI_FUJITSU_BL_DEVICE_NAME);
Jonathan Woithed0482532007-08-29 15:58:19 +0930645 sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS);
Alan Jenkins9fc5cf62017-02-08 14:46:24 +0100646 device->driver_data = fujitsu_bl;
Jonathan Woithed0482532007-08-29 15:58:19 +0930647
Michał Kępień7d134e42017-03-20 10:32:17 +0100648 error = acpi_fujitsu_bl_input_setup(device);
Jonathan Woithe20b93732008-06-11 10:14:56 +0930649 if (error)
Michał Kępieńf8a399d2017-03-20 10:32:18 +0100650 return error;
Jonathan Woithe20b93732008-06-11 10:14:56 +0930651
Alan Jenkins9fc5cf62017-02-08 14:46:24 +0100652 error = acpi_bus_update_power(fujitsu_bl->acpi_handle, &state);
Julia Lawallb30bb892013-12-29 23:47:36 +0100653 if (error) {
Joe Perches77bad7c2011-03-29 15:21:39 -0700654 pr_err("Error reading power state\n");
Michał Kępieńf8a399d2017-03-20 10:32:18 +0100655 return error;
Jonathan Woithed0482532007-08-29 15:58:19 +0930656 }
657
Joe Perches77bad7c2011-03-29 15:21:39 -0700658 pr_info("ACPI: %s [%s] (%s)\n",
Jonathan Woithed0482532007-08-29 15:58:19 +0930659 acpi_device_name(device), acpi_device_bid(device),
660 !device->power.state ? "on" : "off");
661
Alan Jenkins9fc5cf62017-02-08 14:46:24 +0100662 fujitsu_bl->dev = device;
Jonathan Woithe20b93732008-06-11 10:14:56 +0930663
Zhang Ruidd13b9a2013-09-03 08:32:03 +0800664 if (acpi_has_method(device->handle, METHOD_NAME__INI)) {
Jonathan Woithe20b93732008-06-11 10:14:56 +0930665 vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n");
666 if (ACPI_FAILURE
667 (acpi_evaluate_object
668 (device->handle, METHOD_NAME__INI, NULL, NULL)))
Joe Perches77bad7c2011-03-29 15:21:39 -0700669 pr_err("_INI Method failed\n");
Jonathan Woithe20b93732008-06-11 10:14:56 +0930670 }
671
Alan Jenkins5296a732017-02-08 14:46:32 +0100672 if (use_alt_lcd_levels == -1) {
673 if (acpi_has_method(NULL, "\\_SB.PCI0.LPCB.FJEX.SBL2"))
674 use_alt_lcd_levels = 1;
675 else
676 use_alt_lcd_levels = 0;
677 vdbg_printk(FUJLAPTOP_DBG_TRACE, "auto-detected usealt as %i\n",
678 use_alt_lcd_levels);
679 }
680
Jonathan Woithe20b93732008-06-11 10:14:56 +0930681 /* do config (detect defaults) */
Jonathan Woithe20b93732008-06-11 10:14:56 +0930682 use_alt_lcd_levels = use_alt_lcd_levels == 1 ? 1 : 0;
Jonathan Woithe20b93732008-06-11 10:14:56 +0930683 disable_brightness_adjust = disable_brightness_adjust == 1 ? 1 : 0;
684 vdbg_printk(FUJLAPTOP_DBG_INFO,
Tony Vroonf87a1a52009-01-07 10:11:24 +0000685 "config: [alt interface: %d], [adjust disable: %d]\n",
686 use_alt_lcd_levels, disable_brightness_adjust);
Jonathan Woithe20b93732008-06-11 10:14:56 +0930687
688 if (get_max_brightness() <= 0)
Alan Jenkins9fc5cf62017-02-08 14:46:24 +0100689 fujitsu_bl->max_brightness = FUJITSU_LCD_N_LEVELS;
Tony Vroonf87a1a52009-01-07 10:11:24 +0000690 get_lcd_level();
Jonathan Woithe20b93732008-06-11 10:14:56 +0930691
Michał Kępieńaea31372017-03-10 11:50:35 +0100692 if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
693 error = fujitsu_backlight_register();
694 if (error)
Michał Kępieńf8a399d2017-03-20 10:32:18 +0100695 return error;
Michał Kępieńaea31372017-03-10 11:50:35 +0100696 }
697
Julia Lawallb30bb892013-12-29 23:47:36 +0100698 return 0;
Jonathan Woithed0482532007-08-29 15:58:19 +0930699}
700
Alan Jenkins9fc5cf62017-02-08 14:46:24 +0100701static int acpi_fujitsu_bl_remove(struct acpi_device *device)
Jonathan Woithed0482532007-08-29 15:58:19 +0930702{
Alan Jenkins9fc5cf62017-02-08 14:46:24 +0100703 struct fujitsu_bl *fujitsu_bl = acpi_driver_data(device);
Jonathan Woithed0482532007-08-29 15:58:19 +0930704
Michał Kępieńaea31372017-03-10 11:50:35 +0100705 backlight_device_unregister(fujitsu_bl->bl_device);
Jonathan Woithe20b93732008-06-11 10:14:56 +0930706
Alan Jenkins9fc5cf62017-02-08 14:46:24 +0100707 fujitsu_bl->acpi_handle = NULL;
Jonathan Woithed0482532007-08-29 15:58:19 +0930708
709 return 0;
710}
711
Jonathan Woithe20b93732008-06-11 10:14:56 +0930712/* Brightness notify */
713
Alan Jenkins9fc5cf62017-02-08 14:46:24 +0100714static void acpi_fujitsu_bl_notify(struct acpi_device *device, u32 event)
Jonathan Woithe20b93732008-06-11 10:14:56 +0930715{
716 struct input_dev *input;
Michał Kępieńd2aa3ae2017-03-01 07:42:54 +0100717 int oldb, newb, keycode;
Jonathan Woithe20b93732008-06-11 10:14:56 +0930718
Alan Jenkins9fc5cf62017-02-08 14:46:24 +0100719 input = fujitsu_bl->input;
Jonathan Woithe20b93732008-06-11 10:14:56 +0930720
Michał Kępień5efc8002017-03-01 07:42:53 +0100721 if (event != ACPI_FUJITSU_NOTIFY_CODE1) {
Jonathan Woithe20b93732008-06-11 10:14:56 +0930722 keycode = KEY_UNKNOWN;
723 vdbg_printk(FUJLAPTOP_DBG_WARN,
724 "unsupported event [0x%x]\n", event);
Michał Kępień5efc8002017-03-01 07:42:53 +0100725 input_report_key(input, keycode, 1);
726 input_sync(input);
727 input_report_key(input, keycode, 0);
728 input_sync(input);
729 return;
730 }
731
Michał Kępień5efc8002017-03-01 07:42:53 +0100732 oldb = fujitsu_bl->brightness_level;
733 get_lcd_level();
734 newb = fujitsu_bl->brightness_level;
735
736 vdbg_printk(FUJLAPTOP_DBG_TRACE,
737 "brightness button event [%i -> %i (%i)]\n",
738 oldb, newb, fujitsu_bl->brightness_changed);
739
Michał Kępieńd2aa3ae2017-03-01 07:42:54 +0100740 if (oldb == newb)
741 return;
742
743 if (disable_brightness_adjust != 1) {
744 if (use_alt_lcd_levels)
745 set_lcd_level_alt(newb);
746 else
747 set_lcd_level(newb);
Jonathan Woithe20b93732008-06-11 10:14:56 +0930748 }
749
Michał Kępieńd2aa3ae2017-03-01 07:42:54 +0100750 keycode = oldb < newb ? KEY_BRIGHTNESSUP : KEY_BRIGHTNESSDOWN;
751
752 input_report_key(input, keycode, 1);
753 input_sync(input);
754 input_report_key(input, keycode, 0);
755 input_sync(input);
Jonathan Woithe20b93732008-06-11 10:14:56 +0930756}
757
758/* ACPI device for hotkey handling */
759
Michał Kępieńd811b512017-03-14 11:26:29 +0100760static int fujitsu_laptop_platform_add(void)
761{
762 int ret;
763
Michał Kępień979800e2017-03-14 11:26:31 +0100764 fujitsu_laptop->pf_device = platform_device_alloc("fujitsu-laptop", -1);
765 if (!fujitsu_laptop->pf_device)
Michał Kępieńd811b512017-03-14 11:26:29 +0100766 return -ENOMEM;
767
Michał Kępień979800e2017-03-14 11:26:31 +0100768 ret = platform_device_add(fujitsu_laptop->pf_device);
Michał Kępieńd811b512017-03-14 11:26:29 +0100769 if (ret)
770 goto err_put_platform_device;
771
Michał Kępień979800e2017-03-14 11:26:31 +0100772 ret = sysfs_create_group(&fujitsu_laptop->pf_device->dev.kobj,
Michał Kępieńd811b512017-03-14 11:26:29 +0100773 &fujitsu_pf_attribute_group);
774 if (ret)
775 goto err_del_platform_device;
776
777 return 0;
778
779err_del_platform_device:
Michał Kępień979800e2017-03-14 11:26:31 +0100780 platform_device_del(fujitsu_laptop->pf_device);
Michał Kępieńd811b512017-03-14 11:26:29 +0100781err_put_platform_device:
Michał Kępień979800e2017-03-14 11:26:31 +0100782 platform_device_put(fujitsu_laptop->pf_device);
Michał Kępieńd811b512017-03-14 11:26:29 +0100783
784 return ret;
785}
786
787static void fujitsu_laptop_platform_remove(void)
788{
Michał Kępień979800e2017-03-14 11:26:31 +0100789 sysfs_remove_group(&fujitsu_laptop->pf_device->dev.kobj,
Michał Kępieńd811b512017-03-14 11:26:29 +0100790 &fujitsu_pf_attribute_group);
Michał Kępień979800e2017-03-14 11:26:31 +0100791 platform_device_unregister(fujitsu_laptop->pf_device);
Michał Kępieńd811b512017-03-14 11:26:29 +0100792}
793
Alan Jenkins6942eab2017-02-08 14:46:25 +0100794static int acpi_fujitsu_laptop_add(struct acpi_device *device)
Jonathan Woithe20b93732008-06-11 10:14:56 +0930795{
Jonathan Woithe20b93732008-06-11 10:14:56 +0930796 int result = 0;
797 int state = 0;
798 struct input_dev *input;
799 int error;
800 int i;
801
802 if (!device)
803 return -EINVAL;
804
Alan Jenkins6942eab2017-02-08 14:46:25 +0100805 fujitsu_laptop->acpi_handle = device->handle;
Jonathan Woithe20b93732008-06-11 10:14:56 +0930806 sprintf(acpi_device_name(device), "%s",
Alan Jenkins6942eab2017-02-08 14:46:25 +0100807 ACPI_FUJITSU_LAPTOP_DEVICE_NAME);
Jonathan Woithe20b93732008-06-11 10:14:56 +0930808 sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS);
Alan Jenkins6942eab2017-02-08 14:46:25 +0100809 device->driver_data = fujitsu_laptop;
Jonathan Woithe20b93732008-06-11 10:14:56 +0930810
Jonathan Woithe20b93732008-06-11 10:14:56 +0930811 /* kfifo */
Alan Jenkins6942eab2017-02-08 14:46:25 +0100812 spin_lock_init(&fujitsu_laptop->fifo_lock);
813 error = kfifo_alloc(&fujitsu_laptop->fifo, RINGBUFFERSIZE * sizeof(int),
Stefani Seiboldc1e13f22009-12-21 14:37:27 -0800814 GFP_KERNEL);
Stefani Seibold45465482009-12-21 14:37:26 -0800815 if (error) {
Joe Perches77bad7c2011-03-29 15:21:39 -0700816 pr_err("kfifo_alloc failed\n");
Jonathan Woithe20b93732008-06-11 10:14:56 +0930817 goto err_stop;
818 }
819
Alan Jenkins6942eab2017-02-08 14:46:25 +0100820 fujitsu_laptop->input = input = input_allocate_device();
Jonathan Woithe20b93732008-06-11 10:14:56 +0930821 if (!input) {
822 error = -ENOMEM;
Bjorn Helgaasb4ec0272009-04-07 15:37:22 +0000823 goto err_free_fifo;
Jonathan Woithe20b93732008-06-11 10:14:56 +0930824 }
825
Alan Jenkins6942eab2017-02-08 14:46:25 +0100826 snprintf(fujitsu_laptop->phys, sizeof(fujitsu_laptop->phys),
Jonathan Woithe20b93732008-06-11 10:14:56 +0930827 "%s/video/input0", acpi_device_hid(device));
828
829 input->name = acpi_device_name(device);
Alan Jenkins6942eab2017-02-08 14:46:25 +0100830 input->phys = fujitsu_laptop->phys;
Jonathan Woithe20b93732008-06-11 10:14:56 +0930831 input->id.bustype = BUS_HOST;
832 input->id.product = 0x06;
833 input->dev.parent = &device->dev;
Tony Vroon3a407082008-12-31 18:19:59 +0000834
835 set_bit(EV_KEY, input->evbit);
Alan Jenkins9fc5cf62017-02-08 14:46:24 +0100836 set_bit(fujitsu_bl->keycode1, input->keybit);
837 set_bit(fujitsu_bl->keycode2, input->keybit);
838 set_bit(fujitsu_bl->keycode3, input->keybit);
839 set_bit(fujitsu_bl->keycode4, input->keybit);
840 set_bit(fujitsu_bl->keycode5, input->keybit);
Michał Kępień1879e692016-06-28 09:25:50 +0200841 set_bit(KEY_TOUCHPAD_TOGGLE, input->keybit);
Jonathan Woithe20b93732008-06-11 10:14:56 +0930842 set_bit(KEY_UNKNOWN, input->keybit);
843
844 error = input_register_device(input);
845 if (error)
846 goto err_free_input_dev;
847
Alan Jenkins6942eab2017-02-08 14:46:25 +0100848 error = acpi_bus_update_power(fujitsu_laptop->acpi_handle, &state);
Julia Lawallb30bb892013-12-29 23:47:36 +0100849 if (error) {
Joe Perches77bad7c2011-03-29 15:21:39 -0700850 pr_err("Error reading power state\n");
Bartlomiej Zolnierkiewicz72afeea2009-07-31 18:16:02 +0930851 goto err_unregister_input_dev;
Jonathan Woithe20b93732008-06-11 10:14:56 +0930852 }
853
Joe Perches77bad7c2011-03-29 15:21:39 -0700854 pr_info("ACPI: %s [%s] (%s)\n",
855 acpi_device_name(device), acpi_device_bid(device),
856 !device->power.state ? "on" : "off");
Jonathan Woithe20b93732008-06-11 10:14:56 +0930857
Alan Jenkins6942eab2017-02-08 14:46:25 +0100858 fujitsu_laptop->dev = device;
Jonathan Woithe20b93732008-06-11 10:14:56 +0930859
Zhang Ruidd13b9a2013-09-03 08:32:03 +0800860 if (acpi_has_method(device->handle, METHOD_NAME__INI)) {
Jonathan Woithe20b93732008-06-11 10:14:56 +0930861 vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n");
862 if (ACPI_FAILURE
863 (acpi_evaluate_object
864 (device->handle, METHOD_NAME__INI, NULL, NULL)))
Joe Perches77bad7c2011-03-29 15:21:39 -0700865 pr_err("_INI Method failed\n");
Jonathan Woithe20b93732008-06-11 10:14:56 +0930866 }
867
Tony Vroon3a407082008-12-31 18:19:59 +0000868 i = 0;
869 while (call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0) != 0
870 && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE)
871 ; /* No action, result is discarded */
Jonathan Woithe20b93732008-06-11 10:14:56 +0930872 vdbg_printk(FUJLAPTOP_DBG_INFO, "Discarded %i ringbuffer entries\n", i);
873
Alan Jenkins8ef27bd2017-02-08 14:46:27 +0100874 fujitsu_laptop->flags_supported =
875 call_fext_func(FUNC_FLAGS, 0x0, 0x0, 0x0);
Tony Vroon4898c2b2009-02-02 11:11:10 +0000876
877 /* Make sure our bitmask of supported functions is cleared if the
878 RFKILL function block is not implemented, like on the S7020. */
Alan Jenkins8ef27bd2017-02-08 14:46:27 +0100879 if (fujitsu_laptop->flags_supported == UNSUPPORTED_CMD)
880 fujitsu_laptop->flags_supported = 0;
Tony Vroon4898c2b2009-02-02 11:11:10 +0000881
Alan Jenkins8ef27bd2017-02-08 14:46:27 +0100882 if (fujitsu_laptop->flags_supported)
883 fujitsu_laptop->flags_state =
884 call_fext_func(FUNC_FLAGS, 0x4, 0x0, 0x0);
Tony Vroon3a407082008-12-31 18:19:59 +0000885
886 /* Suspect this is a keymap of the application panel, print it */
Joe Perches77bad7c2011-03-29 15:21:39 -0700887 pr_info("BTNI: [0x%x]\n", call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0));
Tony Vroon3a407082008-12-31 18:19:59 +0000888
Michał Kępień1877e262017-03-10 11:50:34 +0100889 /* Sync backlight power status */
Michał Kępieńaea31372017-03-10 11:50:35 +0100890 if (fujitsu_bl->bl_device &&
891 acpi_video_get_backlight_type() == acpi_backlight_vendor) {
Michał Kępień1877e262017-03-10 11:50:34 +0100892 if (call_fext_func(FUNC_BACKLIGHT, 0x2, 0x4, 0x0) == 3)
893 fujitsu_bl->bl_device->props.power = FB_BLANK_POWERDOWN;
894 else
895 fujitsu_bl->bl_device->props.power = FB_BLANK_UNBLANK;
896 }
897
Michał Kępieńc33f4c02017-03-14 11:26:30 +0100898 error = fujitsu_laptop_platform_add();
899 if (error)
900 goto err_unregister_input_dev;
901
Javier Martinez Canillas575b2452016-04-26 18:28:17 -0400902#if IS_ENABLED(CONFIG_LEDS_CLASS)
Tony Vroon3a407082008-12-31 18:19:59 +0000903 if (call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) {
Michał Kępień979800e2017-03-14 11:26:31 +0100904 result = led_classdev_register(&fujitsu_laptop->pf_device->dev,
Tony Vroon3a407082008-12-31 18:19:59 +0000905 &logolamp_led);
906 if (result == 0) {
Alan Jenkins6942eab2017-02-08 14:46:25 +0100907 fujitsu_laptop->logolamp_registered = 1;
Tony Vroon3a407082008-12-31 18:19:59 +0000908 } else {
Joe Perches77bad7c2011-03-29 15:21:39 -0700909 pr_err("Could not register LED handler for logo lamp, error %i\n",
910 result);
Tony Vroon3a407082008-12-31 18:19:59 +0000911 }
912 }
913
914 if ((call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & KEYBOARD_LAMPS) &&
915 (call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0) == 0x0)) {
Michał Kępień979800e2017-03-14 11:26:31 +0100916 result = led_classdev_register(&fujitsu_laptop->pf_device->dev,
Tony Vroon3a407082008-12-31 18:19:59 +0000917 &kblamps_led);
918 if (result == 0) {
Alan Jenkins6942eab2017-02-08 14:46:25 +0100919 fujitsu_laptop->kblamps_registered = 1;
Tony Vroon3a407082008-12-31 18:19:59 +0000920 } else {
Joe Perches77bad7c2011-03-29 15:21:39 -0700921 pr_err("Could not register LED handler for keyboard lamps, error %i\n",
922 result);
Tony Vroon3a407082008-12-31 18:19:59 +0000923 }
924 }
Michał Kępień4f625682016-04-12 22:06:34 +0930925
926 /*
927 * BTNI bit 24 seems to indicate the presence of a radio toggle
928 * button in place of a slide switch, and all such machines appear
929 * to also have an RF LED. Therefore use bit 24 as an indicator
930 * that an RF LED is present.
931 */
932 if (call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0) & BIT(24)) {
Michał Kępień979800e2017-03-14 11:26:31 +0100933 result = led_classdev_register(&fujitsu_laptop->pf_device->dev,
Michał Kępień4f625682016-04-12 22:06:34 +0930934 &radio_led);
935 if (result == 0) {
Alan Jenkins6942eab2017-02-08 14:46:25 +0100936 fujitsu_laptop->radio_led_registered = 1;
Michał Kępień4f625682016-04-12 22:06:34 +0930937 } else {
938 pr_err("Could not register LED handler for radio LED, error %i\n",
939 result);
940 }
941 }
Matej Gromad6b88f62016-06-21 10:09:21 +0200942
943 /* Support for eco led is not always signaled in bit corresponding
944 * to the bit used to control the led. According to the DSDT table,
945 * bit 14 seems to indicate presence of said led as well.
946 * Confirm by testing the status.
947 */
948 if ((call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & BIT(14)) &&
949 (call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0) != UNSUPPORTED_CMD)) {
Michał Kępień979800e2017-03-14 11:26:31 +0100950 result = led_classdev_register(&fujitsu_laptop->pf_device->dev,
Matej Gromad6b88f62016-06-21 10:09:21 +0200951 &eco_led);
952 if (result == 0) {
Alan Jenkins6942eab2017-02-08 14:46:25 +0100953 fujitsu_laptop->eco_led_registered = 1;
Matej Gromad6b88f62016-06-21 10:09:21 +0200954 } else {
955 pr_err("Could not register LED handler for eco LED, error %i\n",
956 result);
957 }
958 }
Bartlomiej Zolnierkiewicz72afeea2009-07-31 18:16:02 +0930959#endif
Tony Vroon3a407082008-12-31 18:19:59 +0000960
Jonathan Woithe20b93732008-06-11 10:14:56 +0930961 return result;
962
Bartlomiej Zolnierkiewicz72afeea2009-07-31 18:16:02 +0930963err_unregister_input_dev:
964 input_unregister_device(input);
Axel Lin8e4e2ef2010-07-20 15:19:34 -0700965 input = NULL;
Jonathan Woithe20b93732008-06-11 10:14:56 +0930966err_free_input_dev:
967 input_free_device(input);
Bjorn Helgaasb4ec0272009-04-07 15:37:22 +0000968err_free_fifo:
Alan Jenkins6942eab2017-02-08 14:46:25 +0100969 kfifo_free(&fujitsu_laptop->fifo);
Jonathan Woithe20b93732008-06-11 10:14:56 +0930970err_stop:
Julia Lawallb30bb892013-12-29 23:47:36 +0100971 return error;
Jonathan Woithe20b93732008-06-11 10:14:56 +0930972}
973
Alan Jenkins6942eab2017-02-08 14:46:25 +0100974static int acpi_fujitsu_laptop_remove(struct acpi_device *device)
Jonathan Woithe20b93732008-06-11 10:14:56 +0930975{
Alan Jenkins6942eab2017-02-08 14:46:25 +0100976 struct fujitsu_laptop *fujitsu_laptop = acpi_driver_data(device);
977 struct input_dev *input = fujitsu_laptop->input;
Jonathan Woithe20b93732008-06-11 10:14:56 +0930978
Javier Martinez Canillas575b2452016-04-26 18:28:17 -0400979#if IS_ENABLED(CONFIG_LEDS_CLASS)
Alan Jenkins6942eab2017-02-08 14:46:25 +0100980 if (fujitsu_laptop->logolamp_registered)
Bartlomiej Zolnierkiewicz72afeea2009-07-31 18:16:02 +0930981 led_classdev_unregister(&logolamp_led);
Jonathan Woithe20b93732008-06-11 10:14:56 +0930982
Alan Jenkins6942eab2017-02-08 14:46:25 +0100983 if (fujitsu_laptop->kblamps_registered)
Bartlomiej Zolnierkiewicz72afeea2009-07-31 18:16:02 +0930984 led_classdev_unregister(&kblamps_led);
Michał Kępień4f625682016-04-12 22:06:34 +0930985
Alan Jenkins6942eab2017-02-08 14:46:25 +0100986 if (fujitsu_laptop->radio_led_registered)
Michał Kępień4f625682016-04-12 22:06:34 +0930987 led_classdev_unregister(&radio_led);
Matej Gromad6b88f62016-06-21 10:09:21 +0200988
Alan Jenkins6942eab2017-02-08 14:46:25 +0100989 if (fujitsu_laptop->eco_led_registered)
Matej Gromad6b88f62016-06-21 10:09:21 +0200990 led_classdev_unregister(&eco_led);
Bartlomiej Zolnierkiewicz72afeea2009-07-31 18:16:02 +0930991#endif
Jonathan Woithe20b93732008-06-11 10:14:56 +0930992
Michał Kępieńc33f4c02017-03-14 11:26:30 +0100993 fujitsu_laptop_platform_remove();
994
Bartlomiej Zolnierkiewicz72afeea2009-07-31 18:16:02 +0930995 input_unregister_device(input);
996
Alan Jenkins6942eab2017-02-08 14:46:25 +0100997 kfifo_free(&fujitsu_laptop->fifo);
Jonathan Woithe20b93732008-06-11 10:14:56 +0930998
Alan Jenkins6942eab2017-02-08 14:46:25 +0100999 fujitsu_laptop->acpi_handle = NULL;
Bartlomiej Zolnierkiewicz72afeea2009-07-31 18:16:02 +09301000
Jonathan Woithe20b93732008-06-11 10:14:56 +09301001 return 0;
1002}
1003
Alan Jenkins6942eab2017-02-08 14:46:25 +01001004static void acpi_fujitsu_laptop_press(int keycode)
Michał Kępień2451d192017-01-11 09:59:31 +01001005{
Alan Jenkins6942eab2017-02-08 14:46:25 +01001006 struct input_dev *input = fujitsu_laptop->input;
Michał Kępień2451d192017-01-11 09:59:31 +01001007 int status;
1008
Alan Jenkins6942eab2017-02-08 14:46:25 +01001009 status = kfifo_in_locked(&fujitsu_laptop->fifo,
Michał Kępień2451d192017-01-11 09:59:31 +01001010 (unsigned char *)&keycode, sizeof(keycode),
Alan Jenkins6942eab2017-02-08 14:46:25 +01001011 &fujitsu_laptop->fifo_lock);
Michał Kępień2451d192017-01-11 09:59:31 +01001012 if (status != sizeof(keycode)) {
1013 vdbg_printk(FUJLAPTOP_DBG_WARN,
1014 "Could not push keycode [0x%x]\n", keycode);
Michał Kępieńa28c7e92017-01-11 09:59:33 +01001015 return;
Michał Kępień2451d192017-01-11 09:59:31 +01001016 }
Michał Kępieńa28c7e92017-01-11 09:59:33 +01001017 input_report_key(input, keycode, 1);
1018 input_sync(input);
1019 vdbg_printk(FUJLAPTOP_DBG_TRACE,
1020 "Push keycode into ringbuffer [%d]\n", keycode);
Michał Kępień2451d192017-01-11 09:59:31 +01001021}
1022
Alan Jenkins6942eab2017-02-08 14:46:25 +01001023static void acpi_fujitsu_laptop_release(void)
Michał Kępień2451d192017-01-11 09:59:31 +01001024{
Alan Jenkins6942eab2017-02-08 14:46:25 +01001025 struct input_dev *input = fujitsu_laptop->input;
Michał Kępień2451d192017-01-11 09:59:31 +01001026 int keycode, status;
1027
Michał Kępień29544f02017-01-11 09:59:32 +01001028 while (true) {
Alan Jenkins6942eab2017-02-08 14:46:25 +01001029 status = kfifo_out_locked(&fujitsu_laptop->fifo,
Michał Kępień2451d192017-01-11 09:59:31 +01001030 (unsigned char *)&keycode,
1031 sizeof(keycode),
Alan Jenkins6942eab2017-02-08 14:46:25 +01001032 &fujitsu_laptop->fifo_lock);
Michał Kępień29544f02017-01-11 09:59:32 +01001033 if (status != sizeof(keycode))
1034 return;
Michał Kępień2451d192017-01-11 09:59:31 +01001035 input_report_key(input, keycode, 0);
1036 input_sync(input);
1037 vdbg_printk(FUJLAPTOP_DBG_TRACE,
1038 "Pop keycode from ringbuffer [%d]\n", keycode);
1039 }
1040}
1041
Alan Jenkins6942eab2017-02-08 14:46:25 +01001042static void acpi_fujitsu_laptop_notify(struct acpi_device *device, u32 event)
Jonathan Woithe20b93732008-06-11 10:14:56 +09301043{
1044 struct input_dev *input;
Michał Kępień2451d192017-01-11 09:59:31 +01001045 int keycode;
Jonathan Woithe20b93732008-06-11 10:14:56 +09301046 unsigned int irb = 1;
Michał Kępień2451d192017-01-11 09:59:31 +01001047 int i;
Jonathan Woithe20b93732008-06-11 10:14:56 +09301048
Alan Jenkins6942eab2017-02-08 14:46:25 +01001049 input = fujitsu_laptop->input;
Jonathan Woithe20b93732008-06-11 10:14:56 +09301050
Michał Kępieńeb357cb2017-01-11 09:59:30 +01001051 if (event != ACPI_FUJITSU_NOTIFY_CODE1) {
Jonathan Woithe20b93732008-06-11 10:14:56 +09301052 keycode = KEY_UNKNOWN;
1053 vdbg_printk(FUJLAPTOP_DBG_WARN,
1054 "Unsupported event [0x%x]\n", event);
1055 input_report_key(input, keycode, 1);
1056 input_sync(input);
1057 input_report_key(input, keycode, 0);
1058 input_sync(input);
Michał Kępieńeb357cb2017-01-11 09:59:30 +01001059 return;
Jonathan Woithe20b93732008-06-11 10:14:56 +09301060 }
Michał Kępieńeb357cb2017-01-11 09:59:30 +01001061
Alan Jenkins8ef27bd2017-02-08 14:46:27 +01001062 if (fujitsu_laptop->flags_supported)
1063 fujitsu_laptop->flags_state =
1064 call_fext_func(FUNC_FLAGS, 0x4, 0x0, 0x0);
Michał Kępieńeb357cb2017-01-11 09:59:30 +01001065
1066 i = 0;
1067 while ((irb =
1068 call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0
1069 && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) {
1070 switch (irb & 0x4ff) {
1071 case KEY1_CODE:
Alan Jenkins9fc5cf62017-02-08 14:46:24 +01001072 keycode = fujitsu_bl->keycode1;
Michał Kępieńeb357cb2017-01-11 09:59:30 +01001073 break;
1074 case KEY2_CODE:
Alan Jenkins9fc5cf62017-02-08 14:46:24 +01001075 keycode = fujitsu_bl->keycode2;
Michał Kępieńeb357cb2017-01-11 09:59:30 +01001076 break;
1077 case KEY3_CODE:
Alan Jenkins9fc5cf62017-02-08 14:46:24 +01001078 keycode = fujitsu_bl->keycode3;
Michał Kępieńeb357cb2017-01-11 09:59:30 +01001079 break;
1080 case KEY4_CODE:
Alan Jenkins9fc5cf62017-02-08 14:46:24 +01001081 keycode = fujitsu_bl->keycode4;
Michał Kępieńeb357cb2017-01-11 09:59:30 +01001082 break;
1083 case KEY5_CODE:
Alan Jenkins9fc5cf62017-02-08 14:46:24 +01001084 keycode = fujitsu_bl->keycode5;
Michał Kępieńeb357cb2017-01-11 09:59:30 +01001085 break;
1086 case 0:
1087 keycode = 0;
1088 break;
1089 default:
1090 vdbg_printk(FUJLAPTOP_DBG_WARN,
1091 "Unknown GIRB result [%x]\n", irb);
1092 keycode = -1;
1093 break;
1094 }
Michał Kępień2451d192017-01-11 09:59:31 +01001095
1096 if (keycode > 0)
Alan Jenkins6942eab2017-02-08 14:46:25 +01001097 acpi_fujitsu_laptop_press(keycode);
Michał Kępień2451d192017-01-11 09:59:31 +01001098 else if (keycode == 0)
Alan Jenkins6942eab2017-02-08 14:46:25 +01001099 acpi_fujitsu_laptop_release();
Michał Kępieńeb357cb2017-01-11 09:59:30 +01001100 }
1101
1102 /* On some models (first seen on the Skylake-based Lifebook
1103 * E736/E746/E756), the touchpad toggle hotkey (Fn+F4) is
Alan Jenkins8ef27bd2017-02-08 14:46:27 +01001104 * handled in software; its state is queried using FUNC_FLAGS
Michał Kępieńeb357cb2017-01-11 09:59:30 +01001105 */
Alan Jenkins8ef27bd2017-02-08 14:46:27 +01001106 if ((fujitsu_laptop->flags_supported & BIT(26)) &&
1107 (call_fext_func(FUNC_FLAGS, 0x1, 0x0, 0x0) & BIT(26))) {
Michał Kępieńeb357cb2017-01-11 09:59:30 +01001108 keycode = KEY_TOUCHPAD_TOGGLE;
1109 input_report_key(input, keycode, 1);
1110 input_sync(input);
1111 input_report_key(input, keycode, 0);
1112 input_sync(input);
1113 }
1114
Jonathan Woithe20b93732008-06-11 10:14:56 +09301115}
1116
1117/* Initialization */
1118
Alan Jenkins9fc5cf62017-02-08 14:46:24 +01001119static const struct acpi_device_id fujitsu_bl_device_ids[] = {
1120 {ACPI_FUJITSU_BL_HID, 0},
Jonathan Woithed0482532007-08-29 15:58:19 +09301121 {"", 0},
1122};
1123
Alan Jenkins9fc5cf62017-02-08 14:46:24 +01001124static struct acpi_driver acpi_fujitsu_bl_driver = {
1125 .name = ACPI_FUJITSU_BL_DRIVER_NAME,
Jonathan Woithed0482532007-08-29 15:58:19 +09301126 .class = ACPI_FUJITSU_CLASS,
Alan Jenkins9fc5cf62017-02-08 14:46:24 +01001127 .ids = fujitsu_bl_device_ids,
Jonathan Woithed0482532007-08-29 15:58:19 +09301128 .ops = {
Alan Jenkins9fc5cf62017-02-08 14:46:24 +01001129 .add = acpi_fujitsu_bl_add,
1130 .remove = acpi_fujitsu_bl_remove,
1131 .notify = acpi_fujitsu_bl_notify,
Jonathan Woithed0482532007-08-29 15:58:19 +09301132 },
1133};
1134
Alan Jenkins6942eab2017-02-08 14:46:25 +01001135static const struct acpi_device_id fujitsu_laptop_device_ids[] = {
1136 {ACPI_FUJITSU_LAPTOP_HID, 0},
Jonathan Woithe20b93732008-06-11 10:14:56 +09301137 {"", 0},
1138};
1139
Alan Jenkins6942eab2017-02-08 14:46:25 +01001140static struct acpi_driver acpi_fujitsu_laptop_driver = {
1141 .name = ACPI_FUJITSU_LAPTOP_DRIVER_NAME,
Jonathan Woithe20b93732008-06-11 10:14:56 +09301142 .class = ACPI_FUJITSU_CLASS,
Alan Jenkins6942eab2017-02-08 14:46:25 +01001143 .ids = fujitsu_laptop_device_ids,
Jonathan Woithe20b93732008-06-11 10:14:56 +09301144 .ops = {
Alan Jenkins6942eab2017-02-08 14:46:25 +01001145 .add = acpi_fujitsu_laptop_add,
1146 .remove = acpi_fujitsu_laptop_remove,
1147 .notify = acpi_fujitsu_laptop_notify,
Jonathan Woithe20b93732008-06-11 10:14:56 +09301148 },
1149};
Jonathan Woithed0482532007-08-29 15:58:19 +09301150
Zhang Rui49901412014-09-09 00:21:59 +02001151static const struct acpi_device_id fujitsu_ids[] __used = {
Alan Jenkins9fc5cf62017-02-08 14:46:24 +01001152 {ACPI_FUJITSU_BL_HID, 0},
Alan Jenkins6942eab2017-02-08 14:46:25 +01001153 {ACPI_FUJITSU_LAPTOP_HID, 0},
Zhang Rui49901412014-09-09 00:21:59 +02001154 {"", 0}
1155};
1156MODULE_DEVICE_TABLE(acpi, fujitsu_ids);
1157
Jonathan Woithed0482532007-08-29 15:58:19 +09301158static int __init fujitsu_init(void)
1159{
Michał Kępieńb8d69c12017-03-10 11:50:33 +01001160 int ret;
Jonathan Woithed0482532007-08-29 15:58:19 +09301161
1162 if (acpi_disabled)
1163 return -ENODEV;
1164
Alan Jenkins9fc5cf62017-02-08 14:46:24 +01001165 fujitsu_bl = kzalloc(sizeof(struct fujitsu_bl), GFP_KERNEL);
1166 if (!fujitsu_bl)
Jonathan Woithed0482532007-08-29 15:58:19 +09301167 return -ENOMEM;
Alan Jenkins9fc5cf62017-02-08 14:46:24 +01001168 fujitsu_bl->keycode1 = KEY_PROG1;
1169 fujitsu_bl->keycode2 = KEY_PROG2;
1170 fujitsu_bl->keycode3 = KEY_PROG3;
1171 fujitsu_bl->keycode4 = KEY_PROG4;
1172 fujitsu_bl->keycode5 = KEY_RFKILL;
Jonathan Woithe0e6a66e2008-10-09 13:44:40 +09301173 dmi_check_system(fujitsu_dmi_table);
Jonathan Woithed0482532007-08-29 15:58:19 +09301174
Alan Jenkinsc1d1e8a2017-02-08 14:46:30 +01001175 ret = acpi_bus_register_driver(&acpi_fujitsu_bl_driver);
1176 if (ret)
Michał Kępieńc2cddd42017-03-10 11:50:36 +01001177 goto err_free_fujitsu_bl;
Jonathan Woithed0482532007-08-29 15:58:19 +09301178
Jonathan Woithed0482532007-08-29 15:58:19 +09301179 /* Register platform stuff */
1180
Alan Jenkins16506022017-02-08 14:46:26 +01001181 ret = platform_driver_register(&fujitsu_pf_driver);
Jonathan Woithe20b93732008-06-11 10:14:56 +09301182 if (ret)
Michał Kępieńc33f4c02017-03-14 11:26:30 +01001183 goto err_unregister_acpi;
Jonathan Woithe20b93732008-06-11 10:14:56 +09301184
Alan Jenkins6942eab2017-02-08 14:46:25 +01001185 /* Register laptop driver */
Jonathan Woithe20b93732008-06-11 10:14:56 +09301186
Alan Jenkins6942eab2017-02-08 14:46:25 +01001187 fujitsu_laptop = kzalloc(sizeof(struct fujitsu_laptop), GFP_KERNEL);
1188 if (!fujitsu_laptop) {
Jonathan Woithe20b93732008-06-11 10:14:56 +09301189 ret = -ENOMEM;
Michał Kępieńc2cddd42017-03-10 11:50:36 +01001190 goto err_unregister_platform_driver;
Jonathan Woithe20b93732008-06-11 10:14:56 +09301191 }
Jonathan Woithe20b93732008-06-11 10:14:56 +09301192
Alan Jenkinsc1d1e8a2017-02-08 14:46:30 +01001193 ret = acpi_bus_register_driver(&acpi_fujitsu_laptop_driver);
1194 if (ret)
Michał Kępieńc2cddd42017-03-10 11:50:36 +01001195 goto err_free_fujitsu_laptop;
Jonathan Woithe20b93732008-06-11 10:14:56 +09301196
Joe Perches77bad7c2011-03-29 15:21:39 -07001197 pr_info("driver " FUJITSU_DRIVER_VERSION " successfully loaded\n");
Jonathan Woithed0482532007-08-29 15:58:19 +09301198
1199 return 0;
1200
Michał Kępieńc2cddd42017-03-10 11:50:36 +01001201err_free_fujitsu_laptop:
Alan Jenkins6942eab2017-02-08 14:46:25 +01001202 kfree(fujitsu_laptop);
Michał Kępieńc2cddd42017-03-10 11:50:36 +01001203err_unregister_platform_driver:
Alan Jenkins16506022017-02-08 14:46:26 +01001204 platform_driver_unregister(&fujitsu_pf_driver);
Michał Kępieńc2cddd42017-03-10 11:50:36 +01001205err_unregister_acpi:
Alan Jenkins9fc5cf62017-02-08 14:46:24 +01001206 acpi_bus_unregister_driver(&acpi_fujitsu_bl_driver);
Michał Kępieńc2cddd42017-03-10 11:50:36 +01001207err_free_fujitsu_bl:
Alan Jenkins9fc5cf62017-02-08 14:46:24 +01001208 kfree(fujitsu_bl);
Jonathan Woithed0482532007-08-29 15:58:19 +09301209
1210 return ret;
1211}
1212
1213static void __exit fujitsu_cleanup(void)
1214{
Alan Jenkins6942eab2017-02-08 14:46:25 +01001215 acpi_bus_unregister_driver(&acpi_fujitsu_laptop_driver);
Tony Vroon3a407082008-12-31 18:19:59 +00001216
Alan Jenkins6942eab2017-02-08 14:46:25 +01001217 kfree(fujitsu_laptop);
Bartlomiej Zolnierkiewicz72afeea2009-07-31 18:16:02 +09301218
Alan Jenkins16506022017-02-08 14:46:26 +01001219 platform_driver_unregister(&fujitsu_pf_driver);
Bartlomiej Zolnierkiewicz72afeea2009-07-31 18:16:02 +09301220
Alan Jenkins9fc5cf62017-02-08 14:46:24 +01001221 acpi_bus_unregister_driver(&acpi_fujitsu_bl_driver);
Jonathan Woithed0482532007-08-29 15:58:19 +09301222
Alan Jenkins9fc5cf62017-02-08 14:46:24 +01001223 kfree(fujitsu_bl);
Jonathan Woithed0482532007-08-29 15:58:19 +09301224
Joe Perches77bad7c2011-03-29 15:21:39 -07001225 pr_info("driver unloaded\n");
Jonathan Woithed0482532007-08-29 15:58:19 +09301226}
1227
1228module_init(fujitsu_init);
1229module_exit(fujitsu_cleanup);
1230
Jonathan Woithe20b93732008-06-11 10:14:56 +09301231module_param(use_alt_lcd_levels, uint, 0644);
1232MODULE_PARM_DESC(use_alt_lcd_levels,
1233 "Use alternative interface for lcd_levels (needed for Lifebook s6410).");
Jonathan Woithe20b93732008-06-11 10:14:56 +09301234module_param(disable_brightness_adjust, uint, 0644);
1235MODULE_PARM_DESC(disable_brightness_adjust, "Disable brightness adjustment .");
1236#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
1237module_param_named(debug, dbg_level, uint, 0644);
1238MODULE_PARM_DESC(debug, "Sets debug level bit-mask");
1239#endif
1240
Tony Vroon3a407082008-12-31 18:19:59 +00001241MODULE_AUTHOR("Jonathan Woithe, Peter Gruber, Tony Vroon");
Jonathan Woithed0482532007-08-29 15:58:19 +09301242MODULE_DESCRIPTION("Fujitsu laptop extras support");
1243MODULE_VERSION(FUJITSU_DRIVER_VERSION);
1244MODULE_LICENSE("GPL");