]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - drivers/platform/x86/fujitsu-laptop.c
Merge tag 'scsi-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
[mirror_ubuntu-artful-kernel.git] / drivers / platform / x86 / fujitsu-laptop.c
CommitLineData
d0482533
JW
1/*-*-linux-c-*-*/
2
3/*
409a3e98 4 Copyright (C) 2007,2008 Jonathan Woithe <jwoithe@just42.net>
20b93734 5 Copyright (C) 2008 Peter Gruber <nokos@gmx.net>
3a407086 6 Copyright (C) 2008 Tony Vroon <tony@linx.net>
d0482533
JW
7 Based on earlier work:
8 Copyright (C) 2003 Shane Spencer <shane@bogomip.com>
9 Adrian Yee <brewt-fujitsu@brewt.org>
10
20b93734
JW
11 Templated from msi-laptop.c and thinkpad_acpi.c which is copyright
12 by its respective authors.
d0482533
JW
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 *
78b2602f
MK
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.
20b93734 38 *
0e6a66e9
JW
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.
20b93734
JW
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 *
d0482533
JW
48 */
49
77bad7c8
JP
50#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
51
d0482533
JW
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>
e8549e2c 58#include <linux/fb.h>
20b93734 59#include <linux/input.h>
f2252672 60#include <linux/input/sparse-keymap.h>
20b93734 61#include <linux/kfifo.h>
d89bcc83 62#include <linux/leds.h>
d0482533 63#include <linux/platform_device.h>
5a0e3ad6 64#include <linux/slab.h>
413226f7 65#include <acpi/video.h>
d0482533 66
84a6ce26 67#define FUJITSU_DRIVER_VERSION "0.6.0"
d0482533
JW
68
69#define FUJITSU_LCD_N_LEVELS 8
70
9fc5cf6e
AJ
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"
6942eabc
AJ
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"
20b93734
JW
78
79#define ACPI_FUJITSU_NOTIFY_CODE1 0x80
80
3a407086 81/* FUNC interface - command values */
8ef27bd3 82#define FUNC_FLAGS 0x1000
3a407086
TV
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
d3dd4480
AJ
90/* FUNC interface - status flags */
91#define FLAG_RFKILL 0x020
92#define FLAG_LID 0x100
93#define FLAG_DOCK 0x200
94
3a407086
TV
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
4f62568c 101#define RADIO_LED_ON 0x20
d6b88f64
MG
102#define ECO_LED 0x10000
103#define ECO_LED_ON 0x80000
3a407086 104
20b93734 105/* Hotkey details */
0e6a66e9
JW
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
b5df36cf 110#define KEY5_CODE 0x420
20b93734
JW
111
112#define MAX_HOTKEY_RINGBUFFER_SIZE 100
113#define RINGBUFFERSIZE 40
114
20b93734 115/* Device controlling the backlight and associated keys */
9fc5cf6e 116struct fujitsu_bl {
20b93734
JW
117 struct input_dev *input;
118 char phys[32];
d0482533 119 struct backlight_device *bl_device;
20b93734 120 unsigned int max_brightness;
d0482533
JW
121 unsigned int brightness_level;
122};
123
9fc5cf6e 124static struct fujitsu_bl *fujitsu_bl;
20b93734 125static int use_alt_lcd_levels = -1;
b4bb0cfd 126static bool disable_brightness_adjust;
20b93734 127
6942eabc
AJ
128/* Device used to access hotkeys and other features on the laptop */
129struct fujitsu_laptop {
20b93734
JW
130 struct input_dev *input;
131 char phys[32];
132 struct platform_device *pf_device;
45465487 133 struct kfifo fifo;
20b93734 134 spinlock_t fifo_lock;
8ef27bd3
AJ
135 int flags_supported;
136 int flags_state;
20b93734 137};
d0482533 138
ca0d9eab 139static struct acpi_device *fext;
20b93734 140
3a407086
TV
141/* Fujitsu ACPI interface function */
142
84631e0c
MK
143static int call_fext_func(struct acpi_device *device,
144 int func, int op, int feature, int state)
3a407086 145{
3a407086 146 union acpi_object params[4] = {
f68e492c
MK
147 { .integer.type = ACPI_TYPE_INTEGER, .integer.value = func },
148 { .integer.type = ACPI_TYPE_INTEGER, .integer.value = op },
149 { .integer.type = ACPI_TYPE_INTEGER, .integer.value = feature },
150 { .integer.type = ACPI_TYPE_INTEGER, .integer.value = state }
3a407086 151 };
b1066410 152 struct acpi_object_list arg_list = { 4, params };
29c29a9b 153 unsigned long long value;
b1066410 154 acpi_status status;
3a407086 155
84631e0c
MK
156 status = acpi_evaluate_integer(device->handle, "FUNC", &arg_list,
157 &value);
3a407086 158 if (ACPI_FAILURE(status)) {
eee77da1 159 acpi_handle_err(device->handle, "Failed to evaluate FUNC\n");
3a407086
TV
160 return -ENODEV;
161 }
162
eee77da1
MK
163 acpi_handle_debug(device->handle,
164 "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) returned 0x%x\n",
165 func, op, feature, state, (int)value);
29c29a9b 166 return value;
3a407086
TV
167}
168
20b93734 169/* Hardware access for LCD brightness control */
d0482533 170
f2db7c64 171static int set_lcd_level(struct acpi_device *device, int level)
d0482533 172{
f2db7c64 173 struct fujitsu_bl *priv = acpi_driver_data(device);
a8779c35 174 acpi_status status;
e32c50ba
MK
175 char *method;
176
177 switch (use_alt_lcd_levels) {
e06e4831 178 case -1:
f2db7c64 179 if (acpi_has_method(device->handle, "SBL2"))
e06e4831
MK
180 method = "SBL2";
181 else
182 method = "SBLL";
183 break;
e32c50ba
MK
184 case 1:
185 method = "SBL2";
186 break;
187 default:
188 method = "SBLL";
189 break;
20b93734
JW
190 }
191
eee77da1
MK
192 acpi_handle_debug(device->handle, "set lcd level via %s [%d]\n", method,
193 level);
20b93734 194
f2db7c64 195 if (level < 0 || level >= priv->max_brightness)
20b93734
JW
196 return -EINVAL;
197
f2db7c64 198 status = acpi_execute_simple_method(device->handle, method, level);
20b93734 199 if (ACPI_FAILURE(status)) {
eee77da1
MK
200 acpi_handle_err(device->handle, "Failed to evaluate %s\n",
201 method);
d0482533
JW
202 return -ENODEV;
203 }
204
f2db7c64 205 priv->brightness_level = level;
bd079a2c 206
d0482533
JW
207 return 0;
208}
209
f2db7c64 210static int get_lcd_level(struct acpi_device *device)
d0482533 211{
f2db7c64 212 struct fujitsu_bl *priv = acpi_driver_data(device);
27663c58 213 unsigned long long state = 0;
d0482533
JW
214 acpi_status status = AE_OK;
215
eee77da1 216 acpi_handle_debug(device->handle, "get lcd level via GBLL\n");
20b93734 217
f2db7c64 218 status = acpi_evaluate_integer(device->handle, "GBLL", NULL, &state);
3b1c37ca
JW
219 if (ACPI_FAILURE(status))
220 return 0;
d0482533 221
f2db7c64 222 priv->brightness_level = state & 0x0fffffff;
20b93734 223
f2db7c64 224 return priv->brightness_level;
20b93734
JW
225}
226
f2db7c64 227static int get_max_brightness(struct acpi_device *device)
20b93734 228{
f2db7c64 229 struct fujitsu_bl *priv = acpi_driver_data(device);
27663c58 230 unsigned long long state = 0;
20b93734
JW
231 acpi_status status = AE_OK;
232
eee77da1 233 acpi_handle_debug(device->handle, "get max lcd level via RBLL\n");
20b93734 234
f2db7c64 235 status = acpi_evaluate_integer(device->handle, "RBLL", NULL, &state);
3b1c37ca
JW
236 if (ACPI_FAILURE(status))
237 return -1;
20b93734 238
f2db7c64 239 priv->max_brightness = state;
20b93734 240
f2db7c64 241 return priv->max_brightness;
20b93734
JW
242}
243
d0482533
JW
244/* Backlight device stuff */
245
246static int bl_get_brightness(struct backlight_device *b)
247{
f2db7c64
MK
248 struct acpi_device *device = bl_get_data(b);
249
250 return b->props.power == FB_BLANK_POWERDOWN ? 0 : get_lcd_level(device);
d0482533
JW
251}
252
253static int bl_update_status(struct backlight_device *b)
254{
f2db7c64
MK
255 struct acpi_device *device = bl_get_data(b);
256
e8549e2c 257 if (b->props.power == FB_BLANK_POWERDOWN)
84631e0c 258 call_fext_func(fext, FUNC_BACKLIGHT, 0x1, 0x4, 0x3);
3a407086 259 else
84631e0c 260 call_fext_func(fext, FUNC_BACKLIGHT, 0x1, 0x4, 0x0);
3a407086 261
f2db7c64 262 return set_lcd_level(device, b->props.brightness);
d0482533
JW
263}
264
9fc5cf6e 265static const struct backlight_ops fujitsu_bl_ops = {
d0482533
JW
266 .get_brightness = bl_get_brightness,
267 .update_status = bl_update_status,
268};
269
b0c4b9c6
MK
270static ssize_t lid_show(struct device *dev, struct device_attribute *attr,
271 char *buf)
3a407086 272{
d659d11a
MK
273 struct fujitsu_laptop *priv = dev_get_drvdata(dev);
274
275 if (!(priv->flags_supported & FLAG_LID))
3a407086 276 return sprintf(buf, "unknown\n");
d659d11a 277 if (priv->flags_state & FLAG_LID)
3a407086
TV
278 return sprintf(buf, "open\n");
279 else
280 return sprintf(buf, "closed\n");
281}
20b93734 282
b0c4b9c6
MK
283static ssize_t dock_show(struct device *dev, struct device_attribute *attr,
284 char *buf)
3a407086 285{
d659d11a
MK
286 struct fujitsu_laptop *priv = dev_get_drvdata(dev);
287
288 if (!(priv->flags_supported & FLAG_DOCK))
3a407086 289 return sprintf(buf, "unknown\n");
d659d11a 290 if (priv->flags_state & FLAG_DOCK)
3a407086
TV
291 return sprintf(buf, "docked\n");
292 else
293 return sprintf(buf, "undocked\n");
20b93734
JW
294}
295
b0c4b9c6
MK
296static ssize_t radios_show(struct device *dev, struct device_attribute *attr,
297 char *buf)
20b93734 298{
d659d11a
MK
299 struct fujitsu_laptop *priv = dev_get_drvdata(dev);
300
301 if (!(priv->flags_supported & FLAG_RFKILL))
3a407086 302 return sprintf(buf, "unknown\n");
d659d11a 303 if (priv->flags_state & FLAG_RFKILL)
3a407086
TV
304 return sprintf(buf, "on\n");
305 else
306 return sprintf(buf, "killed\n");
20b93734
JW
307}
308
b0c4b9c6
MK
309static DEVICE_ATTR_RO(lid);
310static DEVICE_ATTR_RO(dock);
311static DEVICE_ATTR_RO(radios);
d0482533 312
16506026 313static struct attribute *fujitsu_pf_attributes[] = {
3a407086
TV
314 &dev_attr_lid.attr,
315 &dev_attr_dock.attr,
316 &dev_attr_radios.attr,
d0482533
JW
317 NULL
318};
319
ee56ff71 320static const struct attribute_group fujitsu_pf_attribute_group = {
16506026 321 .attrs = fujitsu_pf_attributes
d0482533
JW
322};
323
16506026 324static struct platform_driver fujitsu_pf_driver = {
d0482533
JW
325 .driver = {
326 .name = "fujitsu-laptop",
d0482533
JW
327 }
328};
329
20b93734 330/* ACPI device for LCD brightness control */
d0482533 331
f2252672
MK
332static const struct key_entry keymap_backlight[] = {
333 { KE_KEY, true, { KEY_BRIGHTNESSUP } },
334 { KE_KEY, false, { KEY_BRIGHTNESSDOWN } },
335 { KE_END, 0 }
336};
337
7d134e43
MK
338static int acpi_fujitsu_bl_input_setup(struct acpi_device *device)
339{
7ec3b54d 340 struct fujitsu_bl *priv = acpi_driver_data(device);
f2252672 341 int ret;
7d134e43 342
7ec3b54d
MK
343 priv->input = devm_input_allocate_device(&device->dev);
344 if (!priv->input)
7d134e43
MK
345 return -ENOMEM;
346
7ec3b54d
MK
347 snprintf(priv->phys, sizeof(priv->phys), "%s/video/input0",
348 acpi_device_hid(device));
7d134e43 349
7ec3b54d
MK
350 priv->input->name = acpi_device_name(device);
351 priv->input->phys = priv->phys;
352 priv->input->id.bustype = BUS_HOST;
353 priv->input->id.product = 0x06;
f2252672 354
7ec3b54d 355 ret = sparse_keymap_setup(priv->input, keymap_backlight, NULL);
f2252672
MK
356 if (ret)
357 return ret;
7d134e43 358
7ec3b54d 359 return input_register_device(priv->input);
7d134e43
MK
360}
361
a1aabd5f 362static int fujitsu_backlight_register(struct acpi_device *device)
b8d69c16 363{
f2db7c64 364 struct fujitsu_bl *priv = acpi_driver_data(device);
a1aabd5f 365 const struct backlight_properties props = {
f2db7c64
MK
366 .brightness = priv->brightness_level,
367 .max_brightness = priv->max_brightness - 1,
b8d69c16
MK
368 .type = BACKLIGHT_PLATFORM
369 };
370 struct backlight_device *bd;
371
a1aabd5f 372 bd = devm_backlight_device_register(&device->dev, "fujitsu-laptop",
f2db7c64 373 &device->dev, device,
a1aabd5f 374 &fujitsu_bl_ops, &props);
b8d69c16
MK
375 if (IS_ERR(bd))
376 return PTR_ERR(bd);
377
f2db7c64 378 priv->bl_device = bd;
b8d69c16
MK
379
380 return 0;
381}
382
9fc5cf6e 383static int acpi_fujitsu_bl_add(struct acpi_device *device)
d0482533 384{
679374e4 385 struct fujitsu_bl *priv;
20b93734 386 int error;
d0482533 387
07acf62a
MK
388 if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
389 return -ENODEV;
390
679374e4
MK
391 priv = devm_kzalloc(&device->dev, sizeof(*priv), GFP_KERNEL);
392 if (!priv)
393 return -ENOMEM;
394
395 fujitsu_bl = priv;
d6a298ae
MK
396 strcpy(acpi_device_name(device), ACPI_FUJITSU_BL_DEVICE_NAME);
397 strcpy(acpi_device_class(device), ACPI_FUJITSU_CLASS);
679374e4 398 device->driver_data = priv;
d0482533 399
7d134e43 400 error = acpi_fujitsu_bl_input_setup(device);
20b93734 401 if (error)
f8a399dc 402 return error;
20b93734 403
1c194626
MK
404 pr_info("ACPI: %s [%s]\n",
405 acpi_device_name(device), acpi_device_bid(device));
d0482533 406
f2db7c64
MK
407 if (get_max_brightness(device) <= 0)
408 priv->max_brightness = FUJITSU_LCD_N_LEVELS;
409 get_lcd_level(device);
20b93734 410
a1aabd5f 411 error = fujitsu_backlight_register(device);
07acf62a
MK
412 if (error)
413 return error;
aea3137c 414
b30bb89f 415 return 0;
d0482533
JW
416}
417
20b93734
JW
418/* Brightness notify */
419
9fc5cf6e 420static void acpi_fujitsu_bl_notify(struct acpi_device *device, u32 event)
20b93734 421{
f2db7c64 422 struct fujitsu_bl *priv = acpi_driver_data(device);
f2252672 423 int oldb, newb;
20b93734 424
5efc8004 425 if (event != ACPI_FUJITSU_NOTIFY_CODE1) {
eee77da1
MK
426 acpi_handle_info(device->handle, "unsupported event [0x%x]\n",
427 event);
f2db7c64 428 sparse_keymap_report_event(priv->input, -1, 1, true);
5efc8004
MK
429 return;
430 }
431
f2db7c64
MK
432 oldb = priv->brightness_level;
433 get_lcd_level(device);
434 newb = priv->brightness_level;
5efc8004 435
eee77da1
MK
436 acpi_handle_debug(device->handle,
437 "brightness button event [%i -> %i]\n", oldb, newb);
5efc8004 438
d2aa3ae8
MK
439 if (oldb == newb)
440 return;
20b93734 441
b4bb0cfd 442 if (!disable_brightness_adjust)
f2db7c64 443 set_lcd_level(device, newb);
d2aa3ae8 444
f2db7c64 445 sparse_keymap_report_event(priv->input, oldb < newb, 1, true);
20b93734
JW
446}
447
448/* ACPI device for hotkey handling */
449
527483a8
MK
450static const struct key_entry keymap_default[] = {
451 { KE_KEY, KEY1_CODE, { KEY_PROG1 } },
452 { KE_KEY, KEY2_CODE, { KEY_PROG2 } },
453 { KE_KEY, KEY3_CODE, { KEY_PROG3 } },
454 { KE_KEY, KEY4_CODE, { KEY_PROG4 } },
455 { KE_KEY, KEY5_CODE, { KEY_RFKILL } },
456 { KE_KEY, BIT(26), { KEY_TOUCHPAD_TOGGLE } },
457 { KE_END, 0 }
458};
459
f8c94ecd
MK
460static const struct key_entry keymap_s64x0[] = {
461 { KE_KEY, KEY1_CODE, { KEY_SCREENLOCK } }, /* "Lock" */
462 { KE_KEY, KEY2_CODE, { KEY_HELP } }, /* "Mobility Center */
463 { KE_KEY, KEY3_CODE, { KEY_PROG3 } },
464 { KE_KEY, KEY4_CODE, { KEY_PROG4 } },
465 { KE_END, 0 }
466};
467
468static const struct key_entry keymap_p8010[] = {
469 { KE_KEY, KEY1_CODE, { KEY_HELP } }, /* "Support" */
470 { KE_KEY, KEY2_CODE, { KEY_PROG2 } },
471 { KE_KEY, KEY3_CODE, { KEY_SWITCHVIDEOMODE } }, /* "Presentation" */
472 { KE_KEY, KEY4_CODE, { KEY_WWW } }, /* "WWW" */
473 { KE_END, 0 }
474};
475
527483a8
MK
476static const struct key_entry *keymap = keymap_default;
477
f8c94ecd
MK
478static int fujitsu_laptop_dmi_keymap_override(const struct dmi_system_id *id)
479{
480 pr_info("Identified laptop model '%s'\n", id->ident);
481 keymap = id->driver_data;
482 return 1;
483}
484
485static const struct dmi_system_id fujitsu_laptop_dmi_table[] = {
486 {
487 .callback = fujitsu_laptop_dmi_keymap_override,
488 .ident = "Fujitsu Siemens S6410",
489 .matches = {
490 DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
491 DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6410"),
492 },
493 .driver_data = (void *)keymap_s64x0
494 },
495 {
496 .callback = fujitsu_laptop_dmi_keymap_override,
497 .ident = "Fujitsu Siemens S6420",
498 .matches = {
499 DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
500 DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6420"),
501 },
502 .driver_data = (void *)keymap_s64x0
503 },
504 {
505 .callback = fujitsu_laptop_dmi_keymap_override,
506 .ident = "Fujitsu LifeBook P8010",
507 .matches = {
508 DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
509 DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook P8010"),
510 },
511 .driver_data = (void *)keymap_p8010
512 },
513 {}
514};
515
11182dbc
MK
516static int acpi_fujitsu_laptop_input_setup(struct acpi_device *device)
517{
7ec3b54d 518 struct fujitsu_laptop *priv = acpi_driver_data(device);
527483a8 519 int ret;
11182dbc 520
7ec3b54d
MK
521 priv->input = devm_input_allocate_device(&device->dev);
522 if (!priv->input)
11182dbc
MK
523 return -ENOMEM;
524
c1f51f1c 525 snprintf(priv->phys, sizeof(priv->phys), "%s/input0",
7ec3b54d 526 acpi_device_hid(device));
11182dbc 527
7ec3b54d
MK
528 priv->input->name = acpi_device_name(device);
529 priv->input->phys = priv->phys;
530 priv->input->id.bustype = BUS_HOST;
f66735f8 531
f8c94ecd 532 dmi_check_system(fujitsu_laptop_dmi_table);
7ec3b54d 533 ret = sparse_keymap_setup(priv->input, keymap, NULL);
527483a8
MK
534 if (ret)
535 return ret;
f66735f8 536
7ec3b54d 537 return input_register_device(priv->input);
11182dbc
MK
538}
539
d659d11a 540static int fujitsu_laptop_platform_add(struct acpi_device *device)
d811b511 541{
d659d11a 542 struct fujitsu_laptop *priv = acpi_driver_data(device);
d811b511
MK
543 int ret;
544
d659d11a
MK
545 priv->pf_device = platform_device_alloc("fujitsu-laptop", -1);
546 if (!priv->pf_device)
d811b511
MK
547 return -ENOMEM;
548
d659d11a
MK
549 platform_set_drvdata(priv->pf_device, priv);
550
551 ret = platform_device_add(priv->pf_device);
d811b511
MK
552 if (ret)
553 goto err_put_platform_device;
554
d659d11a 555 ret = sysfs_create_group(&priv->pf_device->dev.kobj,
d811b511
MK
556 &fujitsu_pf_attribute_group);
557 if (ret)
558 goto err_del_platform_device;
559
560 return 0;
561
562err_del_platform_device:
d659d11a 563 platform_device_del(priv->pf_device);
d811b511 564err_put_platform_device:
d659d11a 565 platform_device_put(priv->pf_device);
d811b511
MK
566
567 return ret;
568}
569
d659d11a 570static void fujitsu_laptop_platform_remove(struct acpi_device *device)
d811b511 571{
d659d11a
MK
572 struct fujitsu_laptop *priv = acpi_driver_data(device);
573
574 sysfs_remove_group(&priv->pf_device->dev.kobj,
d811b511 575 &fujitsu_pf_attribute_group);
d659d11a 576 platform_device_unregister(priv->pf_device);
d811b511
MK
577}
578
e33ca45c
MK
579static int logolamp_set(struct led_classdev *cdev,
580 enum led_brightness brightness)
581{
a823f8e7 582 struct acpi_device *device = to_acpi_device(cdev->dev->parent);
e33ca45c
MK
583 int poweron = FUNC_LED_ON, always = FUNC_LED_ON;
584 int ret;
585
586 if (brightness < LED_HALF)
587 poweron = FUNC_LED_OFF;
588
589 if (brightness < LED_FULL)
590 always = FUNC_LED_OFF;
591
a823f8e7 592 ret = call_fext_func(device, FUNC_LEDS, 0x1, LOGOLAMP_POWERON, poweron);
e33ca45c
MK
593 if (ret < 0)
594 return ret;
595
a823f8e7 596 return call_fext_func(device, FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, always);
e33ca45c
MK
597}
598
599static enum led_brightness logolamp_get(struct led_classdev *cdev)
600{
a823f8e7 601 struct acpi_device *device = to_acpi_device(cdev->dev->parent);
e33ca45c
MK
602 int ret;
603
a823f8e7 604 ret = call_fext_func(device, FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0);
e33ca45c
MK
605 if (ret == FUNC_LED_ON)
606 return LED_FULL;
607
a823f8e7 608 ret = call_fext_func(device, FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0);
e33ca45c
MK
609 if (ret == FUNC_LED_ON)
610 return LED_HALF;
611
612 return LED_OFF;
613}
614
e33ca45c
MK
615static int kblamps_set(struct led_classdev *cdev,
616 enum led_brightness brightness)
617{
a823f8e7
MK
618 struct acpi_device *device = to_acpi_device(cdev->dev->parent);
619
e33ca45c 620 if (brightness >= LED_FULL)
a823f8e7 621 return call_fext_func(device, FUNC_LEDS, 0x1, KEYBOARD_LAMPS,
e33ca45c
MK
622 FUNC_LED_ON);
623 else
a823f8e7 624 return call_fext_func(device, FUNC_LEDS, 0x1, KEYBOARD_LAMPS,
e33ca45c
MK
625 FUNC_LED_OFF);
626}
627
628static enum led_brightness kblamps_get(struct led_classdev *cdev)
629{
a823f8e7 630 struct acpi_device *device = to_acpi_device(cdev->dev->parent);
e33ca45c
MK
631 enum led_brightness brightness = LED_OFF;
632
a823f8e7 633 if (call_fext_func(device,
84631e0c 634 FUNC_LEDS, 0x2, KEYBOARD_LAMPS, 0x0) == FUNC_LED_ON)
e33ca45c
MK
635 brightness = LED_FULL;
636
637 return brightness;
638}
639
e33ca45c
MK
640static int radio_led_set(struct led_classdev *cdev,
641 enum led_brightness brightness)
642{
a823f8e7
MK
643 struct acpi_device *device = to_acpi_device(cdev->dev->parent);
644
e33ca45c 645 if (brightness >= LED_FULL)
a823f8e7 646 return call_fext_func(device, FUNC_FLAGS, 0x5, RADIO_LED_ON,
e33ca45c
MK
647 RADIO_LED_ON);
648 else
a823f8e7 649 return call_fext_func(device, FUNC_FLAGS, 0x5, RADIO_LED_ON,
84631e0c 650 0x0);
e33ca45c
MK
651}
652
653static enum led_brightness radio_led_get(struct led_classdev *cdev)
654{
a823f8e7 655 struct acpi_device *device = to_acpi_device(cdev->dev->parent);
e33ca45c
MK
656 enum led_brightness brightness = LED_OFF;
657
a823f8e7 658 if (call_fext_func(device, FUNC_FLAGS, 0x4, 0x0, 0x0) & RADIO_LED_ON)
e33ca45c
MK
659 brightness = LED_FULL;
660
661 return brightness;
662}
663
e33ca45c
MK
664static int eco_led_set(struct led_classdev *cdev,
665 enum led_brightness brightness)
666{
a823f8e7 667 struct acpi_device *device = to_acpi_device(cdev->dev->parent);
e33ca45c
MK
668 int curr;
669
a823f8e7 670 curr = call_fext_func(device, FUNC_LEDS, 0x2, ECO_LED, 0x0);
e33ca45c 671 if (brightness >= LED_FULL)
a823f8e7 672 return call_fext_func(device, FUNC_LEDS, 0x1, ECO_LED,
e33ca45c
MK
673 curr | ECO_LED_ON);
674 else
a823f8e7 675 return call_fext_func(device, FUNC_LEDS, 0x1, ECO_LED,
e33ca45c
MK
676 curr & ~ECO_LED_ON);
677}
678
679static enum led_brightness eco_led_get(struct led_classdev *cdev)
680{
a823f8e7 681 struct acpi_device *device = to_acpi_device(cdev->dev->parent);
e33ca45c
MK
682 enum led_brightness brightness = LED_OFF;
683
a823f8e7 684 if (call_fext_func(device, FUNC_LEDS, 0x2, ECO_LED, 0x0) & ECO_LED_ON)
e33ca45c
MK
685 brightness = LED_FULL;
686
687 return brightness;
688}
689
81f6821f 690static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
20b93734 691{
a823f8e7 692 struct led_classdev *led;
30943e14 693 int result;
7adb7b12 694
a823f8e7 695 if (call_fext_func(device,
84631e0c 696 FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) {
a823f8e7 697 led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL);
b77a5372
GS
698 if (!led)
699 return -ENOMEM;
700
a823f8e7
MK
701 led->name = "fujitsu::logolamp";
702 led->brightness_set_blocking = logolamp_set;
703 led->brightness_get = logolamp_get;
704 result = devm_led_classdev_register(&device->dev, led);
81f6821f 705 if (result)
30943e14 706 return result;
7adb7b12
MK
707 }
708
a823f8e7 709 if ((call_fext_func(device,
84631e0c 710 FUNC_LEDS, 0x0, 0x0, 0x0) & KEYBOARD_LAMPS) &&
a823f8e7
MK
711 (call_fext_func(device, FUNC_BUTTONS, 0x0, 0x0, 0x0) == 0x0)) {
712 led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL);
b77a5372
GS
713 if (!led)
714 return -ENOMEM;
715
a823f8e7
MK
716 led->name = "fujitsu::kblamps";
717 led->brightness_set_blocking = kblamps_set;
718 led->brightness_get = kblamps_get;
719 result = devm_led_classdev_register(&device->dev, led);
81f6821f 720 if (result)
30943e14 721 return result;
7adb7b12
MK
722 }
723
724 /*
725 * BTNI bit 24 seems to indicate the presence of a radio toggle
726 * button in place of a slide switch, and all such machines appear
727 * to also have an RF LED. Therefore use bit 24 as an indicator
728 * that an RF LED is present.
729 */
a823f8e7
MK
730 if (call_fext_func(device, FUNC_BUTTONS, 0x0, 0x0, 0x0) & BIT(24)) {
731 led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL);
b77a5372
GS
732 if (!led)
733 return -ENOMEM;
734
a823f8e7
MK
735 led->name = "fujitsu::radio_led";
736 led->brightness_set_blocking = radio_led_set;
737 led->brightness_get = radio_led_get;
738 led->default_trigger = "rfkill-any";
739 result = devm_led_classdev_register(&device->dev, led);
81f6821f 740 if (result)
30943e14 741 return result;
7adb7b12
MK
742 }
743
744 /* Support for eco led is not always signaled in bit corresponding
745 * to the bit used to control the led. According to the DSDT table,
746 * bit 14 seems to indicate presence of said led as well.
747 * Confirm by testing the status.
748 */
a823f8e7
MK
749 if ((call_fext_func(device, FUNC_LEDS, 0x0, 0x0, 0x0) & BIT(14)) &&
750 (call_fext_func(device,
84631e0c 751 FUNC_LEDS, 0x2, ECO_LED, 0x0) != UNSUPPORTED_CMD)) {
a823f8e7 752 led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL);
b77a5372
GS
753 if (!led)
754 return -ENOMEM;
755
a823f8e7
MK
756 led->name = "fujitsu::eco_led";
757 led->brightness_set_blocking = eco_led_set;
758 led->brightness_get = eco_led_get;
759 result = devm_led_classdev_register(&device->dev, led);
81f6821f 760 if (result)
30943e14 761 return result;
7adb7b12
MK
762 }
763
30943e14 764 return 0;
7adb7b12
MK
765}
766
767static int acpi_fujitsu_laptop_add(struct acpi_device *device)
768{
a4b176ea 769 struct fujitsu_laptop *priv;
20b93734
JW
770 int error;
771 int i;
772
a4b176ea
MK
773 priv = devm_kzalloc(&device->dev, sizeof(*priv), GFP_KERNEL);
774 if (!priv)
775 return -ENOMEM;
776
ca0d9eab
MK
777 WARN_ONCE(fext, "More than one FUJ02E3 ACPI device was found. Driver may not work as intended.");
778 fext = device;
779
d6a298ae
MK
780 strcpy(acpi_device_name(device), ACPI_FUJITSU_LAPTOP_DEVICE_NAME);
781 strcpy(acpi_device_class(device), ACPI_FUJITSU_CLASS);
a4b176ea 782 device->driver_data = priv;
20b93734 783
20b93734 784 /* kfifo */
d659d11a
MK
785 spin_lock_init(&priv->fifo_lock);
786 error = kfifo_alloc(&priv->fifo, RINGBUFFERSIZE * sizeof(int),
787 GFP_KERNEL);
45465487 788 if (error) {
77bad7c8 789 pr_err("kfifo_alloc failed\n");
20b93734
JW
790 goto err_stop;
791 }
792
11182dbc 793 error = acpi_fujitsu_laptop_input_setup(device);
20b93734 794 if (error)
11182dbc 795 goto err_free_fifo;
20b93734 796
1c194626
MK
797 pr_info("ACPI: %s [%s]\n",
798 acpi_device_name(device), acpi_device_bid(device));
20b93734 799
3a407086 800 i = 0;
d659d11a 801 while (call_fext_func(device, FUNC_BUTTONS, 0x1, 0x0, 0x0) != 0
3a407086
TV
802 && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE)
803 ; /* No action, result is discarded */
eee77da1
MK
804 acpi_handle_debug(device->handle, "Discarded %i ringbuffer entries\n",
805 i);
20b93734 806
d659d11a
MK
807 priv->flags_supported = call_fext_func(device, FUNC_FLAGS, 0x0, 0x0,
808 0x0);
4898c2b2
TV
809
810 /* Make sure our bitmask of supported functions is cleared if the
811 RFKILL function block is not implemented, like on the S7020. */
d659d11a
MK
812 if (priv->flags_supported == UNSUPPORTED_CMD)
813 priv->flags_supported = 0;
4898c2b2 814
d659d11a
MK
815 if (priv->flags_supported)
816 priv->flags_state = call_fext_func(device, FUNC_FLAGS, 0x4, 0x0,
817 0x0);
3a407086
TV
818
819 /* Suspect this is a keymap of the application panel, print it */
eee77da1
MK
820 acpi_handle_info(device->handle, "BTNI: [0x%x]\n",
821 call_fext_func(device, FUNC_BUTTONS, 0x0, 0x0, 0x0));
3a407086 822
1877e267 823 /* Sync backlight power status */
679374e4 824 if (fujitsu_bl && fujitsu_bl->bl_device &&
aea3137c 825 acpi_video_get_backlight_type() == acpi_backlight_vendor) {
84631e0c 826 if (call_fext_func(fext, FUNC_BACKLIGHT, 0x2, 0x4, 0x0) == 3)
1877e267
MK
827 fujitsu_bl->bl_device->props.power = FB_BLANK_POWERDOWN;
828 else
829 fujitsu_bl->bl_device->props.power = FB_BLANK_UNBLANK;
830 }
831
d1c7073b 832 error = acpi_fujitsu_laptop_leds_register(device);
c33f4c04 833 if (error)
f66735f8 834 goto err_free_fifo;
c33f4c04 835
d659d11a 836 error = fujitsu_laptop_platform_add(device);
7adb7b12 837 if (error)
d1c7073b 838 goto err_free_fifo;
3a407086 839
7adb7b12 840 return 0;
20b93734 841
b4ec0275 842err_free_fifo:
d659d11a 843 kfifo_free(&priv->fifo);
20b93734 844err_stop:
b30bb89f 845 return error;
20b93734
JW
846}
847
6942eabc 848static int acpi_fujitsu_laptop_remove(struct acpi_device *device)
20b93734 849{
7ec3b54d 850 struct fujitsu_laptop *priv = acpi_driver_data(device);
20b93734 851
d659d11a 852 fujitsu_laptop_platform_remove(device);
c33f4c04 853
7ec3b54d 854 kfifo_free(&priv->fifo);
20b93734
JW
855
856 return 0;
857}
858
d659d11a 859static void acpi_fujitsu_laptop_press(struct acpi_device *device, int scancode)
2451d19d 860{
d659d11a 861 struct fujitsu_laptop *priv = acpi_driver_data(device);
2451d19d
MK
862 int status;
863
d659d11a
MK
864 status = kfifo_in_locked(&priv->fifo, (unsigned char *)&scancode,
865 sizeof(scancode), &priv->fifo_lock);
527483a8 866 if (status != sizeof(scancode)) {
eee77da1
MK
867 dev_info(&priv->input->dev, "Could not push scancode [0x%x]\n",
868 scancode);
a28c7e93 869 return;
2451d19d 870 }
d659d11a 871 sparse_keymap_report_event(priv->input, scancode, 1, false);
eee77da1
MK
872 dev_dbg(&priv->input->dev, "Push scancode into ringbuffer [0x%x]\n",
873 scancode);
2451d19d
MK
874}
875
d659d11a 876static void acpi_fujitsu_laptop_release(struct acpi_device *device)
2451d19d 877{
d659d11a 878 struct fujitsu_laptop *priv = acpi_driver_data(device);
527483a8 879 int scancode, status;
2451d19d 880
29544f03 881 while (true) {
d659d11a 882 status = kfifo_out_locked(&priv->fifo,
527483a8 883 (unsigned char *)&scancode,
d659d11a 884 sizeof(scancode), &priv->fifo_lock);
527483a8 885 if (status != sizeof(scancode))
29544f03 886 return;
d659d11a 887 sparse_keymap_report_event(priv->input, scancode, 0, false);
eee77da1
MK
888 dev_dbg(&priv->input->dev,
889 "Pop scancode from ringbuffer [0x%x]\n", scancode);
2451d19d
MK
890 }
891}
892
6942eabc 893static void acpi_fujitsu_laptop_notify(struct acpi_device *device, u32 event)
20b93734 894{
d659d11a 895 struct fujitsu_laptop *priv = acpi_driver_data(device);
527483a8
MK
896 int scancode, i = 0;
897 unsigned int irb;
20b93734 898
eb357cba 899 if (event != ACPI_FUJITSU_NOTIFY_CODE1) {
eee77da1
MK
900 acpi_handle_info(device->handle, "Unsupported event [0x%x]\n",
901 event);
d659d11a 902 sparse_keymap_report_event(priv->input, -1, 1, true);
eb357cba
MK
903 return;
904 }
905
d659d11a
MK
906 if (priv->flags_supported)
907 priv->flags_state = call_fext_func(device, FUNC_FLAGS, 0x4, 0x0,
908 0x0);
20b93734 909
d659d11a 910 while ((irb = call_fext_func(device,
84631e0c 911 FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0 &&
527483a8
MK
912 i++ < MAX_HOTKEY_RINGBUFFER_SIZE) {
913 scancode = irb & 0x4ff;
d659d11a
MK
914 if (sparse_keymap_entry_from_scancode(priv->input, scancode))
915 acpi_fujitsu_laptop_press(device, scancode);
527483a8 916 else if (scancode == 0)
d659d11a 917 acpi_fujitsu_laptop_release(device);
527483a8 918 else
eee77da1
MK
919 acpi_handle_info(device->handle,
920 "Unknown GIRB result [%x]\n", irb);
eb357cba 921 }
20b93734 922
eb357cba
MK
923 /* On some models (first seen on the Skylake-based Lifebook
924 * E736/E746/E756), the touchpad toggle hotkey (Fn+F4) is
8ef27bd3 925 * handled in software; its state is queried using FUNC_FLAGS
eb357cba 926 */
d659d11a
MK
927 if ((priv->flags_supported & BIT(26)) &&
928 (call_fext_func(device, FUNC_FLAGS, 0x1, 0x0, 0x0) & BIT(26)))
929 sparse_keymap_report_event(priv->input, BIT(26), 1, true);
20b93734
JW
930}
931
932/* Initialization */
933
9fc5cf6e
AJ
934static const struct acpi_device_id fujitsu_bl_device_ids[] = {
935 {ACPI_FUJITSU_BL_HID, 0},
d0482533
JW
936 {"", 0},
937};
938
9fc5cf6e
AJ
939static struct acpi_driver acpi_fujitsu_bl_driver = {
940 .name = ACPI_FUJITSU_BL_DRIVER_NAME,
d0482533 941 .class = ACPI_FUJITSU_CLASS,
9fc5cf6e 942 .ids = fujitsu_bl_device_ids,
d0482533 943 .ops = {
9fc5cf6e 944 .add = acpi_fujitsu_bl_add,
9fc5cf6e 945 .notify = acpi_fujitsu_bl_notify,
d0482533
JW
946 },
947};
948
6942eabc
AJ
949static const struct acpi_device_id fujitsu_laptop_device_ids[] = {
950 {ACPI_FUJITSU_LAPTOP_HID, 0},
20b93734
JW
951 {"", 0},
952};
953
6942eabc
AJ
954static struct acpi_driver acpi_fujitsu_laptop_driver = {
955 .name = ACPI_FUJITSU_LAPTOP_DRIVER_NAME,
20b93734 956 .class = ACPI_FUJITSU_CLASS,
6942eabc 957 .ids = fujitsu_laptop_device_ids,
20b93734 958 .ops = {
6942eabc
AJ
959 .add = acpi_fujitsu_laptop_add,
960 .remove = acpi_fujitsu_laptop_remove,
961 .notify = acpi_fujitsu_laptop_notify,
20b93734
JW
962 },
963};
d0482533 964
49901414 965static const struct acpi_device_id fujitsu_ids[] __used = {
9fc5cf6e 966 {ACPI_FUJITSU_BL_HID, 0},
6942eabc 967 {ACPI_FUJITSU_LAPTOP_HID, 0},
49901414
ZR
968 {"", 0}
969};
970MODULE_DEVICE_TABLE(acpi, fujitsu_ids);
971
d0482533
JW
972static int __init fujitsu_init(void)
973{
b8d69c16 974 int ret;
d0482533 975
c1d1e8a0
AJ
976 ret = acpi_bus_register_driver(&acpi_fujitsu_bl_driver);
977 if (ret)
679374e4 978 return ret;
d0482533 979
d0482533
JW
980 /* Register platform stuff */
981
16506026 982 ret = platform_driver_register(&fujitsu_pf_driver);
20b93734 983 if (ret)
c33f4c04 984 goto err_unregister_acpi;
20b93734 985
6942eabc 986 /* Register laptop driver */
20b93734 987
c1d1e8a0
AJ
988 ret = acpi_bus_register_driver(&acpi_fujitsu_laptop_driver);
989 if (ret)
a4b176ea 990 goto err_unregister_platform_driver;
20b93734 991
77bad7c8 992 pr_info("driver " FUJITSU_DRIVER_VERSION " successfully loaded\n");
d0482533
JW
993
994 return 0;
995
c2cddd4f 996err_unregister_platform_driver:
16506026 997 platform_driver_unregister(&fujitsu_pf_driver);
c2cddd4f 998err_unregister_acpi:
9fc5cf6e 999 acpi_bus_unregister_driver(&acpi_fujitsu_bl_driver);
d0482533
JW
1000
1001 return ret;
1002}
1003
1004static void __exit fujitsu_cleanup(void)
1005{
6942eabc 1006 acpi_bus_unregister_driver(&acpi_fujitsu_laptop_driver);
3a407086 1007
16506026 1008 platform_driver_unregister(&fujitsu_pf_driver);
72afeeaf 1009
9fc5cf6e 1010 acpi_bus_unregister_driver(&acpi_fujitsu_bl_driver);
20b93734 1011
77bad7c8 1012 pr_info("driver unloaded\n");
d0482533
JW
1013}
1014
1015module_init(fujitsu_init);
1016module_exit(fujitsu_cleanup);
1017
e06e4831
MK
1018module_param(use_alt_lcd_levels, int, 0644);
1019MODULE_PARM_DESC(use_alt_lcd_levels, "Interface used for setting LCD brightness level (-1 = auto, 0 = force SBLL, 1 = force SBL2)");
b4bb0cfd
MK
1020module_param(disable_brightness_adjust, bool, 0644);
1021MODULE_PARM_DESC(disable_brightness_adjust, "Disable LCD brightness adjustment");
20b93734 1022
3a407086 1023MODULE_AUTHOR("Jonathan Woithe, Peter Gruber, Tony Vroon");
d0482533
JW
1024MODULE_DESCRIPTION("Fujitsu laptop extras support");
1025MODULE_VERSION(FUJITSU_DRIVER_VERSION);
1026MODULE_LICENSE("GPL");