1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
51
52#include <linux/module.h>
53#include <linux/kernel.h>
54#include <linux/init.h>
55#include <linux/acpi.h>
56#include <linux/dmi.h>
57#include <linux/backlight.h>
58#include <linux/fb.h>
59#include <linux/input.h>
60#include <linux/input/sparse-keymap.h>
61#include <linux/kfifo.h>
62#include <linux/leds.h>
63#include <linux/platform_device.h>
64#include <linux/slab.h>
65#include <acpi/video.h>
66
67#define FUJITSU_DRIVER_VERSION "0.6.0"
68
69#define FUJITSU_LCD_N_LEVELS 8
70
71#define ACPI_FUJITSU_CLASS "fujitsu"
72#define ACPI_FUJITSU_BL_HID "FUJ02B1"
73#define ACPI_FUJITSU_BL_DRIVER_NAME "Fujitsu laptop FUJ02B1 ACPI brightness driver"
74#define ACPI_FUJITSU_BL_DEVICE_NAME "Fujitsu FUJ02B1"
75#define ACPI_FUJITSU_LAPTOP_HID "FUJ02E3"
76#define ACPI_FUJITSU_LAPTOP_DRIVER_NAME "Fujitsu laptop FUJ02E3 ACPI hotkeys driver"
77#define ACPI_FUJITSU_LAPTOP_DEVICE_NAME "Fujitsu FUJ02E3"
78
79#define ACPI_FUJITSU_NOTIFY_CODE1 0x80
80
81
82#define FUNC_FLAGS 0x1000
83#define FUNC_LEDS 0x1001
84#define FUNC_BUTTONS 0x1002
85#define FUNC_BACKLIGHT 0x1004
86
87
88#define UNSUPPORTED_CMD 0x80000000
89
90
91#define FLAG_RFKILL 0x020
92#define FLAG_LID 0x100
93#define FLAG_DOCK 0x200
94
95
96#define FUNC_LED_OFF 0x1
97#define FUNC_LED_ON 0x30001
98#define KEYBOARD_LAMPS 0x100
99#define LOGOLAMP_POWERON 0x2000
100#define LOGOLAMP_ALWAYS 0x4000
101#define RADIO_LED_ON 0x20
102#define ECO_LED 0x10000
103#define ECO_LED_ON 0x80000
104
105
106#define KEY1_CODE 0x410
107#define KEY2_CODE 0x411
108#define KEY3_CODE 0x412
109#define KEY4_CODE 0x413
110#define KEY5_CODE 0x420
111
112#define MAX_HOTKEY_RINGBUFFER_SIZE 100
113#define RINGBUFFERSIZE 40
114
115
116struct fujitsu_bl {
117 struct input_dev *input;
118 char phys[32];
119 struct backlight_device *bl_device;
120 unsigned int max_brightness;
121 unsigned int brightness_level;
122};
123
124static struct fujitsu_bl *fujitsu_bl;
125static int use_alt_lcd_levels = -1;
126static bool disable_brightness_adjust;
127
128
129struct fujitsu_laptop {
130 struct input_dev *input;
131 char phys[32];
132 struct platform_device *pf_device;
133 struct kfifo fifo;
134 spinlock_t fifo_lock;
135 int flags_supported;
136 int flags_state;
137};
138
139static struct acpi_device *fext;
140
141
142
143static int call_fext_func(struct acpi_device *device,
144 int func, int op, int feature, int state)
145{
146 union acpi_object params[4] = {
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 }
151 };
152 struct acpi_object_list arg_list = { 4, params };
153 unsigned long long value;
154 acpi_status status;
155
156 status = acpi_evaluate_integer(device->handle, "FUNC", &arg_list,
157 &value);
158 if (ACPI_FAILURE(status)) {
159 acpi_handle_err(device->handle, "Failed to evaluate FUNC\n");
160 return -ENODEV;
161 }
162
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);
166 return value;
167}
168
169
170
171static int set_lcd_level(struct acpi_device *device, int level)
172{
173 struct fujitsu_bl *priv = acpi_driver_data(device);
174 acpi_status status;
175 char *method;
176
177 switch (use_alt_lcd_levels) {
178 case -1:
179 if (acpi_has_method(device->handle, "SBL2"))
180 method = "SBL2";
181 else
182 method = "SBLL";
183 break;
184 case 1:
185 method = "SBL2";
186 break;
187 default:
188 method = "SBLL";
189 break;
190 }
191
192 acpi_handle_debug(device->handle, "set lcd level via %s [%d]\n", method,
193 level);
194
195 if (level < 0 || level >= priv->max_brightness)
196 return -EINVAL;
197
198 status = acpi_execute_simple_method(device->handle, method, level);
199 if (ACPI_FAILURE(status)) {
200 acpi_handle_err(device->handle, "Failed to evaluate %s\n",
201 method);
202 return -ENODEV;
203 }
204
205 priv->brightness_level = level;
206
207 return 0;
208}
209
210static int get_lcd_level(struct acpi_device *device)
211{
212 struct fujitsu_bl *priv = acpi_driver_data(device);
213 unsigned long long state = 0;
214 acpi_status status = AE_OK;
215
216 acpi_handle_debug(device->handle, "get lcd level via GBLL\n");
217
218 status = acpi_evaluate_integer(device->handle, "GBLL", NULL, &state);
219 if (ACPI_FAILURE(status))
220 return 0;
221
222 priv->brightness_level = state & 0x0fffffff;
223
224 return priv->brightness_level;
225}
226
227static int get_max_brightness(struct acpi_device *device)
228{
229 struct fujitsu_bl *priv = acpi_driver_data(device);
230 unsigned long long state = 0;
231 acpi_status status = AE_OK;
232
233 acpi_handle_debug(device->handle, "get max lcd level via RBLL\n");
234
235 status = acpi_evaluate_integer(device->handle, "RBLL", NULL, &state);
236 if (ACPI_FAILURE(status))
237 return -1;
238
239 priv->max_brightness = state;
240
241 return priv->max_brightness;
242}
243
244
245
246static int bl_get_brightness(struct backlight_device *b)
247{
248 struct acpi_device *device = bl_get_data(b);
249
250 return b->props.power == FB_BLANK_POWERDOWN ? 0 : get_lcd_level(device);
251}
252
253static int bl_update_status(struct backlight_device *b)
254{
255 struct acpi_device *device = bl_get_data(b);
256
257 if (b->props.power == FB_BLANK_POWERDOWN)
258 call_fext_func(fext, FUNC_BACKLIGHT, 0x1, 0x4, 0x3);
259 else
260 call_fext_func(fext, FUNC_BACKLIGHT, 0x1, 0x4, 0x0);
261
262 return set_lcd_level(device, b->props.brightness);
263}
264
265static const struct backlight_ops fujitsu_bl_ops = {
266 .get_brightness = bl_get_brightness,
267 .update_status = bl_update_status,
268};
269
270static ssize_t lid_show(struct device *dev, struct device_attribute *attr,
271 char *buf)
272{
273 struct fujitsu_laptop *priv = dev_get_drvdata(dev);
274
275 if (!(priv->flags_supported & FLAG_LID))
276 return sprintf(buf, "unknown\n");
277 if (priv->flags_state & FLAG_LID)
278 return sprintf(buf, "open\n");
279 else
280 return sprintf(buf, "closed\n");
281}
282
283static ssize_t dock_show(struct device *dev, struct device_attribute *attr,
284 char *buf)
285{
286 struct fujitsu_laptop *priv = dev_get_drvdata(dev);
287
288 if (!(priv->flags_supported & FLAG_DOCK))
289 return sprintf(buf, "unknown\n");
290 if (priv->flags_state & FLAG_DOCK)
291 return sprintf(buf, "docked\n");
292 else
293 return sprintf(buf, "undocked\n");
294}
295
296static ssize_t radios_show(struct device *dev, struct device_attribute *attr,
297 char *buf)
298{
299 struct fujitsu_laptop *priv = dev_get_drvdata(dev);
300
301 if (!(priv->flags_supported & FLAG_RFKILL))
302 return sprintf(buf, "unknown\n");
303 if (priv->flags_state & FLAG_RFKILL)
304 return sprintf(buf, "on\n");
305 else
306 return sprintf(buf, "killed\n");
307}
308
309static DEVICE_ATTR_RO(lid);
310static DEVICE_ATTR_RO(dock);
311static DEVICE_ATTR_RO(radios);
312
313static struct attribute *fujitsu_pf_attributes[] = {
314 &dev_attr_lid.attr,
315 &dev_attr_dock.attr,
316 &dev_attr_radios.attr,
317 NULL
318};
319
320static const struct attribute_group fujitsu_pf_attribute_group = {
321 .attrs = fujitsu_pf_attributes
322};
323
324static struct platform_driver fujitsu_pf_driver = {
325 .driver = {
326 .name = "fujitsu-laptop",
327 }
328};
329
330
331
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
338static int acpi_fujitsu_bl_input_setup(struct acpi_device *device)
339{
340 struct fujitsu_bl *priv = acpi_driver_data(device);
341 int ret;
342
343 priv->input = devm_input_allocate_device(&device->dev);
344 if (!priv->input)
345 return -ENOMEM;
346
347 snprintf(priv->phys, sizeof(priv->phys), "%s/video/input0",
348 acpi_device_hid(device));
349
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;
354
355 ret = sparse_keymap_setup(priv->input, keymap_backlight, NULL);
356 if (ret)
357 return ret;
358
359 return input_register_device(priv->input);
360}
361
362static int fujitsu_backlight_register(struct acpi_device *device)
363{
364 struct fujitsu_bl *priv = acpi_driver_data(device);
365 const struct backlight_properties props = {
366 .brightness = priv->brightness_level,
367 .max_brightness = priv->max_brightness - 1,
368 .type = BACKLIGHT_PLATFORM
369 };
370 struct backlight_device *bd;
371
372 bd = devm_backlight_device_register(&device->dev, "fujitsu-laptop",
373 &device->dev, device,
374 &fujitsu_bl_ops, &props);
375 if (IS_ERR(bd))
376 return PTR_ERR(bd);
377
378 priv->bl_device = bd;
379
380 return 0;
381}
382
383static int acpi_fujitsu_bl_add(struct acpi_device *device)
384{
385 struct fujitsu_bl *priv;
386 int error;
387
388 if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
389 return -ENODEV;
390
391 priv = devm_kzalloc(&device->dev, sizeof(*priv), GFP_KERNEL);
392 if (!priv)
393 return -ENOMEM;
394
395 fujitsu_bl = priv;
396 strcpy(acpi_device_name(device), ACPI_FUJITSU_BL_DEVICE_NAME);
397 strcpy(acpi_device_class(device), ACPI_FUJITSU_CLASS);
398 device->driver_data = priv;
399
400 error = acpi_fujitsu_bl_input_setup(device);
401 if (error)
402 return error;
403
404 pr_info("ACPI: %s [%s]\n",
405 acpi_device_name(device), acpi_device_bid(device));
406
407 if (get_max_brightness(device) <= 0)
408 priv->max_brightness = FUJITSU_LCD_N_LEVELS;
409 get_lcd_level(device);
410
411 error = fujitsu_backlight_register(device);
412 if (error)
413 return error;
414
415 return 0;
416}
417
418
419
420static void acpi_fujitsu_bl_notify(struct acpi_device *device, u32 event)
421{
422 struct fujitsu_bl *priv = acpi_driver_data(device);
423 int oldb, newb;
424
425 if (event != ACPI_FUJITSU_NOTIFY_CODE1) {
426 acpi_handle_info(device->handle, "unsupported event [0x%x]\n",
427 event);
428 sparse_keymap_report_event(priv->input, -1, 1, true);
429 return;
430 }
431
432 oldb = priv->brightness_level;
433 get_lcd_level(device);
434 newb = priv->brightness_level;
435
436 acpi_handle_debug(device->handle,
437 "brightness button event [%i -> %i]\n", oldb, newb);
438
439 if (oldb == newb)
440 return;
441
442 if (!disable_brightness_adjust)
443 set_lcd_level(device, newb);
444
445 sparse_keymap_report_event(priv->input, oldb < newb, 1, true);
446}
447
448
449
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
460static const struct key_entry keymap_s64x0[] = {
461 { KE_KEY, KEY1_CODE, { KEY_SCREENLOCK } },
462 { KE_KEY, KEY2_CODE, { KEY_HELP } },
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 } },
470 { KE_KEY, KEY2_CODE, { KEY_PROG2 } },
471 { KE_KEY, KEY3_CODE, { KEY_SWITCHVIDEOMODE } },
472 { KE_KEY, KEY4_CODE, { KEY_WWW } },
473 { KE_END, 0 }
474};
475
476static const struct key_entry *keymap = keymap_default;
477
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
516static int acpi_fujitsu_laptop_input_setup(struct acpi_device *device)
517{
518 struct fujitsu_laptop *priv = acpi_driver_data(device);
519 int ret;
520
521 priv->input = devm_input_allocate_device(&device->dev);
522 if (!priv->input)
523 return -ENOMEM;
524
525 snprintf(priv->phys, sizeof(priv->phys), "%s/input0",
526 acpi_device_hid(device));
527
528 priv->input->name = acpi_device_name(device);
529 priv->input->phys = priv->phys;
530 priv->input->id.bustype = BUS_HOST;
531
532 dmi_check_system(fujitsu_laptop_dmi_table);
533 ret = sparse_keymap_setup(priv->input, keymap, NULL);
534 if (ret)
535 return ret;
536
537 return input_register_device(priv->input);
538}
539
540static int fujitsu_laptop_platform_add(struct acpi_device *device)
541{
542 struct fujitsu_laptop *priv = acpi_driver_data(device);
543 int ret;
544
545 priv->pf_device = platform_device_alloc("fujitsu-laptop", -1);
546 if (!priv->pf_device)
547 return -ENOMEM;
548
549 platform_set_drvdata(priv->pf_device, priv);
550
551 ret = platform_device_add(priv->pf_device);
552 if (ret)
553 goto err_put_platform_device;
554
555 ret = sysfs_create_group(&priv->pf_device->dev.kobj,
556 &fujitsu_pf_attribute_group);
557 if (ret)
558 goto err_del_platform_device;
559
560 return 0;
561
562err_del_platform_device:
563 platform_device_del(priv->pf_device);
564err_put_platform_device:
565 platform_device_put(priv->pf_device);
566
567 return ret;
568}
569
570static void fujitsu_laptop_platform_remove(struct acpi_device *device)
571{
572 struct fujitsu_laptop *priv = acpi_driver_data(device);
573
574 sysfs_remove_group(&priv->pf_device->dev.kobj,
575 &fujitsu_pf_attribute_group);
576 platform_device_unregister(priv->pf_device);
577}
578
579static int logolamp_set(struct led_classdev *cdev,
580 enum led_brightness brightness)
581{
582 struct acpi_device *device = to_acpi_device(cdev->dev->parent);
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
592 ret = call_fext_func(device, FUNC_LEDS, 0x1, LOGOLAMP_POWERON, poweron);
593 if (ret < 0)
594 return ret;
595
596 return call_fext_func(device, FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, always);
597}
598
599static enum led_brightness logolamp_get(struct led_classdev *cdev)
600{
601 struct acpi_device *device = to_acpi_device(cdev->dev->parent);
602 int ret;
603
604 ret = call_fext_func(device, FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0);
605 if (ret == FUNC_LED_ON)
606 return LED_FULL;
607
608 ret = call_fext_func(device, FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0);
609 if (ret == FUNC_LED_ON)
610 return LED_HALF;
611
612 return LED_OFF;
613}
614
615static int kblamps_set(struct led_classdev *cdev,
616 enum led_brightness brightness)
617{
618 struct acpi_device *device = to_acpi_device(cdev->dev->parent);
619
620 if (brightness >= LED_FULL)
621 return call_fext_func(device, FUNC_LEDS, 0x1, KEYBOARD_LAMPS,
622 FUNC_LED_ON);
623 else
624 return call_fext_func(device, FUNC_LEDS, 0x1, KEYBOARD_LAMPS,
625 FUNC_LED_OFF);
626}
627
628static enum led_brightness kblamps_get(struct led_classdev *cdev)
629{
630 struct acpi_device *device = to_acpi_device(cdev->dev->parent);
631 enum led_brightness brightness = LED_OFF;
632
633 if (call_fext_func(device,
634 FUNC_LEDS, 0x2, KEYBOARD_LAMPS, 0x0) == FUNC_LED_ON)
635 brightness = LED_FULL;
636
637 return brightness;
638}
639
640static int radio_led_set(struct led_classdev *cdev,
641 enum led_brightness brightness)
642{
643 struct acpi_device *device = to_acpi_device(cdev->dev->parent);
644
645 if (brightness >= LED_FULL)
646 return call_fext_func(device, FUNC_FLAGS, 0x5, RADIO_LED_ON,
647 RADIO_LED_ON);
648 else
649 return call_fext_func(device, FUNC_FLAGS, 0x5, RADIO_LED_ON,
650 0x0);
651}
652
653static enum led_brightness radio_led_get(struct led_classdev *cdev)
654{
655 struct acpi_device *device = to_acpi_device(cdev->dev->parent);
656 enum led_brightness brightness = LED_OFF;
657
658 if (call_fext_func(device, FUNC_FLAGS, 0x4, 0x0, 0x0) & RADIO_LED_ON)
659 brightness = LED_FULL;
660
661 return brightness;
662}
663
664static int eco_led_set(struct led_classdev *cdev,
665 enum led_brightness brightness)
666{
667 struct acpi_device *device = to_acpi_device(cdev->dev->parent);
668 int curr;
669
670 curr = call_fext_func(device, FUNC_LEDS, 0x2, ECO_LED, 0x0);
671 if (brightness >= LED_FULL)
672 return call_fext_func(device, FUNC_LEDS, 0x1, ECO_LED,
673 curr | ECO_LED_ON);
674 else
675 return call_fext_func(device, FUNC_LEDS, 0x1, ECO_LED,
676 curr & ~ECO_LED_ON);
677}
678
679static enum led_brightness eco_led_get(struct led_classdev *cdev)
680{
681 struct acpi_device *device = to_acpi_device(cdev->dev->parent);
682 enum led_brightness brightness = LED_OFF;
683
684 if (call_fext_func(device, FUNC_LEDS, 0x2, ECO_LED, 0x0) & ECO_LED_ON)
685 brightness = LED_FULL;
686
687 return brightness;
688}
689
690static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
691{
692 struct led_classdev *led;
693 int result;
694
695 if (call_fext_func(device,
696 FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) {
697 led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL);
698 if (!led)
699 return -ENOMEM;
700
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);
705 if (result)
706 return result;
707 }
708
709 if ((call_fext_func(device,
710 FUNC_LEDS, 0x0, 0x0, 0x0) & KEYBOARD_LAMPS) &&
711 (call_fext_func(device, FUNC_BUTTONS, 0x0, 0x0, 0x0) == 0x0)) {
712 led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL);
713 if (!led)
714 return -ENOMEM;
715
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);
720 if (result)
721 return result;
722 }
723
724
725
726
727
728
729
730 if (call_fext_func(device, FUNC_BUTTONS, 0x0, 0x0, 0x0) & BIT(24)) {
731 led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL);
732 if (!led)
733 return -ENOMEM;
734
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);
740 if (result)
741 return result;
742 }
743
744
745
746
747
748
749 if ((call_fext_func(device, FUNC_LEDS, 0x0, 0x0, 0x0) & BIT(14)) &&
750 (call_fext_func(device,
751 FUNC_LEDS, 0x2, ECO_LED, 0x0) != UNSUPPORTED_CMD)) {
752 led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL);
753 if (!led)
754 return -ENOMEM;
755
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);
760 if (result)
761 return result;
762 }
763
764 return 0;
765}
766
767static int acpi_fujitsu_laptop_add(struct acpi_device *device)
768{
769 struct fujitsu_laptop *priv;
770 int error;
771 int i;
772
773 priv = devm_kzalloc(&device->dev, sizeof(*priv), GFP_KERNEL);
774 if (!priv)
775 return -ENOMEM;
776
777 WARN_ONCE(fext, "More than one FUJ02E3 ACPI device was found. Driver may not work as intended.");
778 fext = device;
779
780 strcpy(acpi_device_name(device), ACPI_FUJITSU_LAPTOP_DEVICE_NAME);
781 strcpy(acpi_device_class(device), ACPI_FUJITSU_CLASS);
782 device->driver_data = priv;
783
784
785 spin_lock_init(&priv->fifo_lock);
786 error = kfifo_alloc(&priv->fifo, RINGBUFFERSIZE * sizeof(int),
787 GFP_KERNEL);
788 if (error) {
789 pr_err("kfifo_alloc failed\n");
790 goto err_stop;
791 }
792
793 error = acpi_fujitsu_laptop_input_setup(device);
794 if (error)
795 goto err_free_fifo;
796
797 pr_info("ACPI: %s [%s]\n",
798 acpi_device_name(device), acpi_device_bid(device));
799
800 i = 0;
801 while (call_fext_func(device, FUNC_BUTTONS, 0x1, 0x0, 0x0) != 0
802 && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE)
803 ;
804 acpi_handle_debug(device->handle, "Discarded %i ringbuffer entries\n",
805 i);
806
807 priv->flags_supported = call_fext_func(device, FUNC_FLAGS, 0x0, 0x0,
808 0x0);
809
810
811
812 if (priv->flags_supported == UNSUPPORTED_CMD)
813 priv->flags_supported = 0;
814
815 if (priv->flags_supported)
816 priv->flags_state = call_fext_func(device, FUNC_FLAGS, 0x4, 0x0,
817 0x0);
818
819
820 acpi_handle_info(device->handle, "BTNI: [0x%x]\n",
821 call_fext_func(device, FUNC_BUTTONS, 0x0, 0x0, 0x0));
822
823
824 if (fujitsu_bl && fujitsu_bl->bl_device &&
825 acpi_video_get_backlight_type() == acpi_backlight_vendor) {
826 if (call_fext_func(fext, FUNC_BACKLIGHT, 0x2, 0x4, 0x0) == 3)
827 fujitsu_bl->bl_device->props.power = FB_BLANK_POWERDOWN;
828 else
829 fujitsu_bl->bl_device->props.power = FB_BLANK_UNBLANK;
830 }
831
832 error = acpi_fujitsu_laptop_leds_register(device);
833 if (error)
834 goto err_free_fifo;
835
836 error = fujitsu_laptop_platform_add(device);
837 if (error)
838 goto err_free_fifo;
839
840 return 0;
841
842err_free_fifo:
843 kfifo_free(&priv->fifo);
844err_stop:
845 return error;
846}
847
848static int acpi_fujitsu_laptop_remove(struct acpi_device *device)
849{
850 struct fujitsu_laptop *priv = acpi_driver_data(device);
851
852 fujitsu_laptop_platform_remove(device);
853
854 kfifo_free(&priv->fifo);
855
856 return 0;
857}
858
859static void acpi_fujitsu_laptop_press(struct acpi_device *device, int scancode)
860{
861 struct fujitsu_laptop *priv = acpi_driver_data(device);
862 int status;
863
864 status = kfifo_in_locked(&priv->fifo, (unsigned char *)&scancode,
865 sizeof(scancode), &priv->fifo_lock);
866 if (status != sizeof(scancode)) {
867 dev_info(&priv->input->dev, "Could not push scancode [0x%x]\n",
868 scancode);
869 return;
870 }
871 sparse_keymap_report_event(priv->input, scancode, 1, false);
872 dev_dbg(&priv->input->dev, "Push scancode into ringbuffer [0x%x]\n",
873 scancode);
874}
875
876static void acpi_fujitsu_laptop_release(struct acpi_device *device)
877{
878 struct fujitsu_laptop *priv = acpi_driver_data(device);
879 int scancode, status;
880
881 while (true) {
882 status = kfifo_out_locked(&priv->fifo,
883 (unsigned char *)&scancode,
884 sizeof(scancode), &priv->fifo_lock);
885 if (status != sizeof(scancode))
886 return;
887 sparse_keymap_report_event(priv->input, scancode, 0, false);
888 dev_dbg(&priv->input->dev,
889 "Pop scancode from ringbuffer [0x%x]\n", scancode);
890 }
891}
892
893static void acpi_fujitsu_laptop_notify(struct acpi_device *device, u32 event)
894{
895 struct fujitsu_laptop *priv = acpi_driver_data(device);
896 int scancode, i = 0;
897 unsigned int irb;
898
899 if (event != ACPI_FUJITSU_NOTIFY_CODE1) {
900 acpi_handle_info(device->handle, "Unsupported event [0x%x]\n",
901 event);
902 sparse_keymap_report_event(priv->input, -1, 1, true);
903 return;
904 }
905
906 if (priv->flags_supported)
907 priv->flags_state = call_fext_func(device, FUNC_FLAGS, 0x4, 0x0,
908 0x0);
909
910 while ((irb = call_fext_func(device,
911 FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0 &&
912 i++ < MAX_HOTKEY_RINGBUFFER_SIZE) {
913 scancode = irb & 0x4ff;
914 if (sparse_keymap_entry_from_scancode(priv->input, scancode))
915 acpi_fujitsu_laptop_press(device, scancode);
916 else if (scancode == 0)
917 acpi_fujitsu_laptop_release(device);
918 else
919 acpi_handle_info(device->handle,
920 "Unknown GIRB result [%x]\n", irb);
921 }
922
923
924
925
926
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);
930}
931
932
933
934static const struct acpi_device_id fujitsu_bl_device_ids[] = {
935 {ACPI_FUJITSU_BL_HID, 0},
936 {"", 0},
937};
938
939static struct acpi_driver acpi_fujitsu_bl_driver = {
940 .name = ACPI_FUJITSU_BL_DRIVER_NAME,
941 .class = ACPI_FUJITSU_CLASS,
942 .ids = fujitsu_bl_device_ids,
943 .ops = {
944 .add = acpi_fujitsu_bl_add,
945 .notify = acpi_fujitsu_bl_notify,
946 },
947};
948
949static const struct acpi_device_id fujitsu_laptop_device_ids[] = {
950 {ACPI_FUJITSU_LAPTOP_HID, 0},
951 {"", 0},
952};
953
954static struct acpi_driver acpi_fujitsu_laptop_driver = {
955 .name = ACPI_FUJITSU_LAPTOP_DRIVER_NAME,
956 .class = ACPI_FUJITSU_CLASS,
957 .ids = fujitsu_laptop_device_ids,
958 .ops = {
959 .add = acpi_fujitsu_laptop_add,
960 .remove = acpi_fujitsu_laptop_remove,
961 .notify = acpi_fujitsu_laptop_notify,
962 },
963};
964
965static const struct acpi_device_id fujitsu_ids[] __used = {
966 {ACPI_FUJITSU_BL_HID, 0},
967 {ACPI_FUJITSU_LAPTOP_HID, 0},
968 {"", 0}
969};
970MODULE_DEVICE_TABLE(acpi, fujitsu_ids);
971
972static int __init fujitsu_init(void)
973{
974 int ret;
975
976 ret = acpi_bus_register_driver(&acpi_fujitsu_bl_driver);
977 if (ret)
978 return ret;
979
980
981
982 ret = platform_driver_register(&fujitsu_pf_driver);
983 if (ret)
984 goto err_unregister_acpi;
985
986
987
988 ret = acpi_bus_register_driver(&acpi_fujitsu_laptop_driver);
989 if (ret)
990 goto err_unregister_platform_driver;
991
992 pr_info("driver " FUJITSU_DRIVER_VERSION " successfully loaded\n");
993
994 return 0;
995
996err_unregister_platform_driver:
997 platform_driver_unregister(&fujitsu_pf_driver);
998err_unregister_acpi:
999 acpi_bus_unregister_driver(&acpi_fujitsu_bl_driver);
1000
1001 return ret;
1002}
1003
1004static void __exit fujitsu_cleanup(void)
1005{
1006 acpi_bus_unregister_driver(&acpi_fujitsu_laptop_driver);
1007
1008 platform_driver_unregister(&fujitsu_pf_driver);
1009
1010 acpi_bus_unregister_driver(&acpi_fujitsu_bl_driver);
1011
1012 pr_info("driver unloaded\n");
1013}
1014
1015module_init(fujitsu_init);
1016module_exit(fujitsu_cleanup);
1017
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)");
1020module_param(disable_brightness_adjust, bool, 0644);
1021MODULE_PARM_DESC(disable_brightness_adjust, "Disable LCD brightness adjustment");
1022
1023MODULE_AUTHOR("Jonathan Woithe, Peter Gruber, Tony Vroon");
1024MODULE_DESCRIPTION("Fujitsu laptop extras support");
1025MODULE_VERSION(FUJITSU_DRIVER_VERSION);
1026MODULE_LICENSE("GPL");
1027