Version:  2.0.40 2.2.26 2.4.37 3.9 3.10 3.11 3.12 3.13 3.14 3.15 3.16 3.17 3.18 3.19 4.0 4.1 4.2 4.3 4.4 4.5 4.6

Linux/drivers/platform/x86/fujitsu-laptop.c

  1 /*-*-linux-c-*-*/
  2 
  3 /*
  4   Copyright (C) 2007,2008 Jonathan Woithe <jwoithe@just42.net>
  5   Copyright (C) 2008 Peter Gruber <nokos@gmx.net>
  6   Copyright (C) 2008 Tony Vroon <tony@linx.net>
  7   Based on earlier work:
  8     Copyright (C) 2003 Shane Spencer <shane@bogomip.com>
  9     Adrian Yee <brewt-fujitsu@brewt.org>
 10 
 11   Templated from msi-laptop.c and thinkpad_acpi.c which is copyright
 12   by its respective authors.
 13 
 14   This program is free software; you can redistribute it and/or modify
 15   it under the terms of the GNU General Public License as published by
 16   the Free Software Foundation; either version 2 of the License, or
 17   (at your option) any later version.
 18 
 19   This program is distributed in the hope that it will be useful, but
 20   WITHOUT ANY WARRANTY; without even the implied warranty of
 21   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 22   General Public License for more details.
 23 
 24   You should have received a copy of the GNU General Public License
 25   along with this program; if not, write to the Free Software
 26   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 27   02110-1301, USA.
 28  */
 29 
 30 /*
 31  * fujitsu-laptop.c - Fujitsu laptop support, providing access to additional
 32  * features made available on a range of Fujitsu laptops including the
 33  * P2xxx/P5xxx/S6xxx/S7xxx series.
 34  *
 35  * This driver exports a few files in /sys/devices/platform/fujitsu-laptop/;
 36  * others may be added at a later date.
 37  *
 38  *   lcd_level - Screen brightness: contains a single integer in the
 39  *   range 0..7. (rw)
 40  *
 41  * In addition to these platform device attributes the driver
 42  * registers itself in the Linux backlight control subsystem and is
 43  * available to userspace under /sys/class/backlight/fujitsu-laptop/.
 44  *
 45  * Hotkeys present on certain Fujitsu laptops (eg: the S6xxx series) are
 46  * also supported by this driver.
 47  *
 48  * This driver has been tested on a Fujitsu Lifebook S6410, S7020 and
 49  * P8010.  It should work on most P-series and S-series Lifebooks, but
 50  * YMMV.
 51  *
 52  * The module parameter use_alt_lcd_levels switches between different ACPI
 53  * brightness controls which are used by different Fujitsu laptops.  In most
 54  * cases the correct method is automatically detected. "use_alt_lcd_levels=1"
 55  * is applicable for a Fujitsu Lifebook S6410 if autodetection fails.
 56  *
 57  */
 58 
 59 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 60 
 61 #include <linux/module.h>
 62 #include <linux/kernel.h>
 63 #include <linux/init.h>
 64 #include <linux/acpi.h>
 65 #include <linux/dmi.h>
 66 #include <linux/backlight.h>
 67 #include <linux/fb.h>
 68 #include <linux/input.h>
 69 #include <linux/kfifo.h>
 70 #include <linux/platform_device.h>
 71 #include <linux/slab.h>
 72 #if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
 73 #include <linux/leds.h>
 74 #endif
 75 #include <acpi/video.h>
 76 
 77 #define FUJITSU_DRIVER_VERSION "0.6.0"
 78 
 79 #define FUJITSU_LCD_N_LEVELS 8
 80 
 81 #define ACPI_FUJITSU_CLASS              "fujitsu"
 82 #define ACPI_FUJITSU_HID                "FUJ02B1"
 83 #define ACPI_FUJITSU_DRIVER_NAME        "Fujitsu laptop FUJ02B1 ACPI brightness driver"
 84 #define ACPI_FUJITSU_DEVICE_NAME        "Fujitsu FUJ02B1"
 85 #define ACPI_FUJITSU_HOTKEY_HID         "FUJ02E3"
 86 #define ACPI_FUJITSU_HOTKEY_DRIVER_NAME "Fujitsu laptop FUJ02E3 ACPI hotkeys driver"
 87 #define ACPI_FUJITSU_HOTKEY_DEVICE_NAME "Fujitsu FUJ02E3"
 88 
 89 #define ACPI_FUJITSU_NOTIFY_CODE1     0x80
 90 
 91 #define ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS     0x86
 92 #define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS     0x87
 93 
 94 /* FUNC interface - command values */
 95 #define FUNC_RFKILL     0x1000
 96 #define FUNC_LEDS       0x1001
 97 #define FUNC_BUTTONS    0x1002
 98 #define FUNC_BACKLIGHT  0x1004
 99 
100 /* FUNC interface - responses */
101 #define UNSUPPORTED_CMD 0x80000000
102 
103 #if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
104 /* FUNC interface - LED control */
105 #define FUNC_LED_OFF    0x1
106 #define FUNC_LED_ON     0x30001
107 #define KEYBOARD_LAMPS  0x100
108 #define LOGOLAMP_POWERON 0x2000
109 #define LOGOLAMP_ALWAYS  0x4000
110 #endif
111 
112 /* Hotkey details */
113 #define KEY1_CODE       0x410   /* codes for the keys in the GIRB register */
114 #define KEY2_CODE       0x411
115 #define KEY3_CODE       0x412
116 #define KEY4_CODE       0x413
117 #define KEY5_CODE       0x420
118 
119 #define MAX_HOTKEY_RINGBUFFER_SIZE 100
120 #define RINGBUFFERSIZE 40
121 
122 /* Debugging */
123 #define FUJLAPTOP_LOG      ACPI_FUJITSU_HID ": "
124 #define FUJLAPTOP_ERR      KERN_ERR FUJLAPTOP_LOG
125 #define FUJLAPTOP_NOTICE   KERN_NOTICE FUJLAPTOP_LOG
126 #define FUJLAPTOP_INFO     KERN_INFO FUJLAPTOP_LOG
127 #define FUJLAPTOP_DEBUG    KERN_DEBUG FUJLAPTOP_LOG
128 
129 #define FUJLAPTOP_DBG_ALL         0xffff
130 #define FUJLAPTOP_DBG_ERROR       0x0001
131 #define FUJLAPTOP_DBG_WARN        0x0002
132 #define FUJLAPTOP_DBG_INFO        0x0004
133 #define FUJLAPTOP_DBG_TRACE       0x0008
134 
135 #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
136 #define vdbg_printk(a_dbg_level, format, arg...) \
137         do { if (dbg_level & a_dbg_level) \
138                 printk(FUJLAPTOP_DEBUG "%s: " format, __func__ , ## arg); \
139         } while (0)
140 #else
141 #define vdbg_printk(a_dbg_level, format, arg...) \
142         do { } while (0)
143 #endif
144 
145 /* Device controlling the backlight and associated keys */
146 struct fujitsu_t {
147         acpi_handle acpi_handle;
148         struct acpi_device *dev;
149         struct input_dev *input;
150         char phys[32];
151         struct backlight_device *bl_device;
152         struct platform_device *pf_device;
153         int keycode1, keycode2, keycode3, keycode4, keycode5;
154 
155         unsigned int max_brightness;
156         unsigned int brightness_changed;
157         unsigned int brightness_level;
158 };
159 
160 static struct fujitsu_t *fujitsu;
161 static int use_alt_lcd_levels = -1;
162 static int disable_brightness_adjust = -1;
163 
164 /* Device used to access other hotkeys on the laptop */
165 struct fujitsu_hotkey_t {
166         acpi_handle acpi_handle;
167         struct acpi_device *dev;
168         struct input_dev *input;
169         char phys[32];
170         struct platform_device *pf_device;
171         struct kfifo fifo;
172         spinlock_t fifo_lock;
173         int rfkill_supported;
174         int rfkill_state;
175         int logolamp_registered;
176         int kblamps_registered;
177 };
178 
179 static struct fujitsu_hotkey_t *fujitsu_hotkey;
180 
181 static void acpi_fujitsu_hotkey_notify(struct acpi_device *device, u32 event);
182 
183 #if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
184 static enum led_brightness logolamp_get(struct led_classdev *cdev);
185 static void logolamp_set(struct led_classdev *cdev,
186                                enum led_brightness brightness);
187 
188 static struct led_classdev logolamp_led = {
189  .name = "fujitsu::logolamp",
190  .brightness_get = logolamp_get,
191  .brightness_set = logolamp_set
192 };
193 
194 static enum led_brightness kblamps_get(struct led_classdev *cdev);
195 static void kblamps_set(struct led_classdev *cdev,
196                                enum led_brightness brightness);
197 
198 static struct led_classdev kblamps_led = {
199  .name = "fujitsu::kblamps",
200  .brightness_get = kblamps_get,
201  .brightness_set = kblamps_set
202 };
203 #endif
204 
205 #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
206 static u32 dbg_level = 0x03;
207 #endif
208 
209 static void acpi_fujitsu_notify(struct acpi_device *device, u32 event);
210 
211 /* Fujitsu ACPI interface function */
212 
213 static int call_fext_func(int cmd, int arg0, int arg1, int arg2)
214 {
215         acpi_status status = AE_OK;
216         union acpi_object params[4] = {
217         { .type = ACPI_TYPE_INTEGER },
218         { .type = ACPI_TYPE_INTEGER },
219         { .type = ACPI_TYPE_INTEGER },
220         { .type = ACPI_TYPE_INTEGER }
221         };
222         struct acpi_object_list arg_list = { 4, &params[0] };
223         unsigned long long value;
224         acpi_handle handle = NULL;
225 
226         status = acpi_get_handle(fujitsu_hotkey->acpi_handle, "FUNC", &handle);
227         if (ACPI_FAILURE(status)) {
228                 vdbg_printk(FUJLAPTOP_DBG_ERROR,
229                                 "FUNC interface is not present\n");
230                 return -ENODEV;
231         }
232 
233         params[0].integer.value = cmd;
234         params[1].integer.value = arg0;
235         params[2].integer.value = arg1;
236         params[3].integer.value = arg2;
237 
238         status = acpi_evaluate_integer(handle, NULL, &arg_list, &value);
239         if (ACPI_FAILURE(status)) {
240                 vdbg_printk(FUJLAPTOP_DBG_WARN,
241                         "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) call failed\n",
242                                 cmd, arg0, arg1, arg2);
243                 return -ENODEV;
244         }
245 
246         vdbg_printk(FUJLAPTOP_DBG_TRACE,
247                 "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) returned 0x%x\n",
248                         cmd, arg0, arg1, arg2, (int)value);
249         return value;
250 }
251 
252 #if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
253 /* LED class callbacks */
254 
255 static void logolamp_set(struct led_classdev *cdev,
256                                enum led_brightness brightness)
257 {
258         if (brightness >= LED_FULL) {
259                 call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_ON);
260                 call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, FUNC_LED_ON);
261         } else if (brightness >= LED_HALF) {
262                 call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_ON);
263                 call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, FUNC_LED_OFF);
264         } else {
265                 call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_OFF);
266         }
267 }
268 
269 static void kblamps_set(struct led_classdev *cdev,
270                                enum led_brightness brightness)
271 {
272         if (brightness >= LED_FULL)
273                 call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS, FUNC_LED_ON);
274         else
275                 call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS, FUNC_LED_OFF);
276 }
277 
278 static enum led_brightness logolamp_get(struct led_classdev *cdev)
279 {
280         enum led_brightness brightness = LED_OFF;
281         int poweron, always;
282 
283         poweron = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0);
284         if (poweron == FUNC_LED_ON) {
285                 brightness = LED_HALF;
286                 always = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0);
287                 if (always == FUNC_LED_ON)
288                         brightness = LED_FULL;
289         }
290         return brightness;
291 }
292 
293 static enum led_brightness kblamps_get(struct led_classdev *cdev)
294 {
295         enum led_brightness brightness = LED_OFF;
296 
297         if (call_fext_func(FUNC_LEDS, 0x2, KEYBOARD_LAMPS, 0x0) == FUNC_LED_ON)
298                 brightness = LED_FULL;
299 
300         return brightness;
301 }
302 #endif
303 
304 /* Hardware access for LCD brightness control */
305 
306 static int set_lcd_level(int level)
307 {
308         acpi_status status = AE_OK;
309         acpi_handle handle = NULL;
310 
311         vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via SBLL [%d]\n",
312                     level);
313 
314         if (level < 0 || level >= fujitsu->max_brightness)
315                 return -EINVAL;
316 
317         status = acpi_get_handle(fujitsu->acpi_handle, "SBLL", &handle);
318         if (ACPI_FAILURE(status)) {
319                 vdbg_printk(FUJLAPTOP_DBG_ERROR, "SBLL not present\n");
320                 return -ENODEV;
321         }
322 
323 
324         status = acpi_execute_simple_method(handle, NULL, level);
325         if (ACPI_FAILURE(status))
326                 return -ENODEV;
327 
328         return 0;
329 }
330 
331 static int set_lcd_level_alt(int level)
332 {
333         acpi_status status = AE_OK;
334         acpi_handle handle = NULL;
335 
336         vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via SBL2 [%d]\n",
337                     level);
338 
339         if (level < 0 || level >= fujitsu->max_brightness)
340                 return -EINVAL;
341 
342         status = acpi_get_handle(fujitsu->acpi_handle, "SBL2", &handle);
343         if (ACPI_FAILURE(status)) {
344                 vdbg_printk(FUJLAPTOP_DBG_ERROR, "SBL2 not present\n");
345                 return -ENODEV;
346         }
347 
348         status = acpi_execute_simple_method(handle, NULL, level);
349         if (ACPI_FAILURE(status))
350                 return -ENODEV;
351 
352         return 0;
353 }
354 
355 static int get_lcd_level(void)
356 {
357         unsigned long long state = 0;
358         acpi_status status = AE_OK;
359 
360         vdbg_printk(FUJLAPTOP_DBG_TRACE, "get lcd level via GBLL\n");
361 
362         status =
363             acpi_evaluate_integer(fujitsu->acpi_handle, "GBLL", NULL, &state);
364         if (ACPI_FAILURE(status))
365                 return 0;
366 
367         fujitsu->brightness_level = state & 0x0fffffff;
368 
369         if (state & 0x80000000)
370                 fujitsu->brightness_changed = 1;
371         else
372                 fujitsu->brightness_changed = 0;
373 
374         return fujitsu->brightness_level;
375 }
376 
377 static int get_max_brightness(void)
378 {
379         unsigned long long state = 0;
380         acpi_status status = AE_OK;
381 
382         vdbg_printk(FUJLAPTOP_DBG_TRACE, "get max lcd level via RBLL\n");
383 
384         status =
385             acpi_evaluate_integer(fujitsu->acpi_handle, "RBLL", NULL, &state);
386         if (ACPI_FAILURE(status))
387                 return -1;
388 
389         fujitsu->max_brightness = state;
390 
391         return fujitsu->max_brightness;
392 }
393 
394 /* Backlight device stuff */
395 
396 static int bl_get_brightness(struct backlight_device *b)
397 {
398         return get_lcd_level();
399 }
400 
401 static int bl_update_status(struct backlight_device *b)
402 {
403         int ret;
404         if (b->props.power == FB_BLANK_POWERDOWN)
405                 ret = call_fext_func(FUNC_BACKLIGHT, 0x1, 0x4, 0x3);
406         else
407                 ret = call_fext_func(FUNC_BACKLIGHT, 0x1, 0x4, 0x0);
408         if (ret != 0)
409                 vdbg_printk(FUJLAPTOP_DBG_ERROR,
410                         "Unable to adjust backlight power, error code %i\n",
411                         ret);
412 
413         if (use_alt_lcd_levels)
414                 ret = set_lcd_level_alt(b->props.brightness);
415         else
416                 ret = set_lcd_level(b->props.brightness);
417         if (ret != 0)
418                 vdbg_printk(FUJLAPTOP_DBG_ERROR,
419                         "Unable to adjust LCD brightness, error code %i\n",
420                         ret);
421         return ret;
422 }
423 
424 static const struct backlight_ops fujitsubl_ops = {
425         .get_brightness = bl_get_brightness,
426         .update_status = bl_update_status,
427 };
428 
429 /* Platform LCD brightness device */
430 
431 static ssize_t
432 show_max_brightness(struct device *dev,
433                     struct device_attribute *attr, char *buf)
434 {
435 
436         int ret;
437 
438         ret = get_max_brightness();
439         if (ret < 0)
440                 return ret;
441 
442         return sprintf(buf, "%i\n", ret);
443 }
444 
445 static ssize_t
446 show_brightness_changed(struct device *dev,
447                         struct device_attribute *attr, char *buf)
448 {
449 
450         int ret;
451 
452         ret = fujitsu->brightness_changed;
453         if (ret < 0)
454                 return ret;
455 
456         return sprintf(buf, "%i\n", ret);
457 }
458 
459 static ssize_t show_lcd_level(struct device *dev,
460                               struct device_attribute *attr, char *buf)
461 {
462 
463         int ret;
464 
465         ret = get_lcd_level();
466         if (ret < 0)
467                 return ret;
468 
469         return sprintf(buf, "%i\n", ret);
470 }
471 
472 static ssize_t store_lcd_level(struct device *dev,
473                                struct device_attribute *attr, const char *buf,
474                                size_t count)
475 {
476 
477         int level, ret;
478 
479         if (sscanf(buf, "%i", &level) != 1
480             || (level < 0 || level >= fujitsu->max_brightness))
481                 return -EINVAL;
482 
483         if (use_alt_lcd_levels)
484                 ret = set_lcd_level_alt(level);
485         else
486                 ret = set_lcd_level(level);
487         if (ret < 0)
488                 return ret;
489 
490         ret = get_lcd_level();
491         if (ret < 0)
492                 return ret;
493 
494         return count;
495 }
496 
497 static ssize_t
498 ignore_store(struct device *dev,
499              struct device_attribute *attr, const char *buf, size_t count)
500 {
501         return count;
502 }
503 
504 static ssize_t
505 show_lid_state(struct device *dev,
506                         struct device_attribute *attr, char *buf)
507 {
508         if (!(fujitsu_hotkey->rfkill_supported & 0x100))
509                 return sprintf(buf, "unknown\n");
510         if (fujitsu_hotkey->rfkill_state & 0x100)
511                 return sprintf(buf, "open\n");
512         else
513                 return sprintf(buf, "closed\n");
514 }
515 
516 static ssize_t
517 show_dock_state(struct device *dev,
518                         struct device_attribute *attr, char *buf)
519 {
520         if (!(fujitsu_hotkey->rfkill_supported & 0x200))
521                 return sprintf(buf, "unknown\n");
522         if (fujitsu_hotkey->rfkill_state & 0x200)
523                 return sprintf(buf, "docked\n");
524         else
525                 return sprintf(buf, "undocked\n");
526 }
527 
528 static ssize_t
529 show_radios_state(struct device *dev,
530                         struct device_attribute *attr, char *buf)
531 {
532         if (!(fujitsu_hotkey->rfkill_supported & 0x20))
533                 return sprintf(buf, "unknown\n");
534         if (fujitsu_hotkey->rfkill_state & 0x20)
535                 return sprintf(buf, "on\n");
536         else
537                 return sprintf(buf, "killed\n");
538 }
539 
540 static DEVICE_ATTR(max_brightness, 0444, show_max_brightness, ignore_store);
541 static DEVICE_ATTR(brightness_changed, 0444, show_brightness_changed,
542                    ignore_store);
543 static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level);
544 static DEVICE_ATTR(lid, 0444, show_lid_state, ignore_store);
545 static DEVICE_ATTR(dock, 0444, show_dock_state, ignore_store);
546 static DEVICE_ATTR(radios, 0444, show_radios_state, ignore_store);
547 
548 static struct attribute *fujitsupf_attributes[] = {
549         &dev_attr_brightness_changed.attr,
550         &dev_attr_max_brightness.attr,
551         &dev_attr_lcd_level.attr,
552         &dev_attr_lid.attr,
553         &dev_attr_dock.attr,
554         &dev_attr_radios.attr,
555         NULL
556 };
557 
558 static struct attribute_group fujitsupf_attribute_group = {
559         .attrs = fujitsupf_attributes
560 };
561 
562 static struct platform_driver fujitsupf_driver = {
563         .driver = {
564                    .name = "fujitsu-laptop",
565                    }
566 };
567 
568 static void __init dmi_check_cb_common(const struct dmi_system_id *id)
569 {
570         pr_info("Identified laptop model '%s'\n", id->ident);
571         if (use_alt_lcd_levels == -1) {
572                 if (acpi_has_method(NULL,
573                                 "\\_SB.PCI0.LPCB.FJEX.SBL2"))
574                         use_alt_lcd_levels = 1;
575                 else
576                         use_alt_lcd_levels = 0;
577                 vdbg_printk(FUJLAPTOP_DBG_TRACE, "auto-detected usealt as "
578                         "%i\n", use_alt_lcd_levels);
579         }
580 }
581 
582 static int __init dmi_check_cb_s6410(const struct dmi_system_id *id)
583 {
584         dmi_check_cb_common(id);
585         fujitsu->keycode1 = KEY_SCREENLOCK;     /* "Lock" */
586         fujitsu->keycode2 = KEY_HELP;   /* "Mobility Center" */
587         return 1;
588 }
589 
590 static int __init dmi_check_cb_s6420(const struct dmi_system_id *id)
591 {
592         dmi_check_cb_common(id);
593         fujitsu->keycode1 = KEY_SCREENLOCK;     /* "Lock" */
594         fujitsu->keycode2 = KEY_HELP;   /* "Mobility Center" */
595         return 1;
596 }
597 
598 static int __init dmi_check_cb_p8010(const struct dmi_system_id *id)
599 {
600         dmi_check_cb_common(id);
601         fujitsu->keycode1 = KEY_HELP;   /* "Support" */
602         fujitsu->keycode3 = KEY_SWITCHVIDEOMODE;        /* "Presentation" */
603         fujitsu->keycode4 = KEY_WWW;    /* "Internet" */
604         return 1;
605 }
606 
607 static const struct dmi_system_id fujitsu_dmi_table[] __initconst = {
608         {
609          .ident = "Fujitsu Siemens S6410",
610          .matches = {
611                      DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
612                      DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6410"),
613                      },
614          .callback = dmi_check_cb_s6410},
615         {
616          .ident = "Fujitsu Siemens S6420",
617          .matches = {
618                      DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
619                      DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6420"),
620                      },
621          .callback = dmi_check_cb_s6420},
622         {
623          .ident = "Fujitsu LifeBook P8010",
624          .matches = {
625                      DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
626                      DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook P8010"),
627                      },
628          .callback = dmi_check_cb_p8010},
629         {}
630 };
631 
632 /* ACPI device for LCD brightness control */
633 
634 static int acpi_fujitsu_add(struct acpi_device *device)
635 {
636         int state = 0;
637         struct input_dev *input;
638         int error;
639 
640         if (!device)
641                 return -EINVAL;
642 
643         fujitsu->acpi_handle = device->handle;
644         sprintf(acpi_device_name(device), "%s", ACPI_FUJITSU_DEVICE_NAME);
645         sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS);
646         device->driver_data = fujitsu;
647 
648         fujitsu->input = input = input_allocate_device();
649         if (!input) {
650                 error = -ENOMEM;
651                 goto err_stop;
652         }
653 
654         snprintf(fujitsu->phys, sizeof(fujitsu->phys),
655                  "%s/video/input0", acpi_device_hid(device));
656 
657         input->name = acpi_device_name(device);
658         input->phys = fujitsu->phys;
659         input->id.bustype = BUS_HOST;
660         input->id.product = 0x06;
661         input->dev.parent = &device->dev;
662         input->evbit[0] = BIT(EV_KEY);
663         set_bit(KEY_BRIGHTNESSUP, input->keybit);
664         set_bit(KEY_BRIGHTNESSDOWN, input->keybit);
665         set_bit(KEY_UNKNOWN, input->keybit);
666 
667         error = input_register_device(input);
668         if (error)
669                 goto err_free_input_dev;
670 
671         error = acpi_bus_update_power(fujitsu->acpi_handle, &state);
672         if (error) {
673                 pr_err("Error reading power state\n");
674                 goto err_unregister_input_dev;
675         }
676 
677         pr_info("ACPI: %s [%s] (%s)\n",
678                acpi_device_name(device), acpi_device_bid(device),
679                !device->power.state ? "on" : "off");
680 
681         fujitsu->dev = device;
682 
683         if (acpi_has_method(device->handle, METHOD_NAME__INI)) {
684                 vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n");
685                 if (ACPI_FAILURE
686                     (acpi_evaluate_object
687                      (device->handle, METHOD_NAME__INI, NULL, NULL)))
688                         pr_err("_INI Method failed\n");
689         }
690 
691         /* do config (detect defaults) */
692         use_alt_lcd_levels = use_alt_lcd_levels == 1 ? 1 : 0;
693         disable_brightness_adjust = disable_brightness_adjust == 1 ? 1 : 0;
694         vdbg_printk(FUJLAPTOP_DBG_INFO,
695                     "config: [alt interface: %d], [adjust disable: %d]\n",
696                     use_alt_lcd_levels, disable_brightness_adjust);
697 
698         if (get_max_brightness() <= 0)
699                 fujitsu->max_brightness = FUJITSU_LCD_N_LEVELS;
700         get_lcd_level();
701 
702         return 0;
703 
704 err_unregister_input_dev:
705         input_unregister_device(input);
706         input = NULL;
707 err_free_input_dev:
708         input_free_device(input);
709 err_stop:
710         return error;
711 }
712 
713 static int acpi_fujitsu_remove(struct acpi_device *device)
714 {
715         struct fujitsu_t *fujitsu = acpi_driver_data(device);
716         struct input_dev *input = fujitsu->input;
717 
718         input_unregister_device(input);
719 
720         fujitsu->acpi_handle = NULL;
721 
722         return 0;
723 }
724 
725 /* Brightness notify */
726 
727 static void acpi_fujitsu_notify(struct acpi_device *device, u32 event)
728 {
729         struct input_dev *input;
730         int keycode;
731         int oldb, newb;
732 
733         input = fujitsu->input;
734 
735         switch (event) {
736         case ACPI_FUJITSU_NOTIFY_CODE1:
737                 keycode = 0;
738                 oldb = fujitsu->brightness_level;
739                 get_lcd_level();
740                 newb = fujitsu->brightness_level;
741 
742                 vdbg_printk(FUJLAPTOP_DBG_TRACE,
743                             "brightness button event [%i -> %i (%i)]\n",
744                             oldb, newb, fujitsu->brightness_changed);
745 
746                 if (oldb < newb) {
747                         if (disable_brightness_adjust != 1) {
748                                 if (use_alt_lcd_levels)
749                                         set_lcd_level_alt(newb);
750                                 else
751                                         set_lcd_level(newb);
752                         }
753                         keycode = KEY_BRIGHTNESSUP;
754                 } else if (oldb > newb) {
755                         if (disable_brightness_adjust != 1) {
756                                 if (use_alt_lcd_levels)
757                                         set_lcd_level_alt(newb);
758                                 else
759                                         set_lcd_level(newb);
760                         }
761                         keycode = KEY_BRIGHTNESSDOWN;
762                 }
763                 break;
764         default:
765                 keycode = KEY_UNKNOWN;
766                 vdbg_printk(FUJLAPTOP_DBG_WARN,
767                             "unsupported event [0x%x]\n", event);
768                 break;
769         }
770 
771         if (keycode != 0) {
772                 input_report_key(input, keycode, 1);
773                 input_sync(input);
774                 input_report_key(input, keycode, 0);
775                 input_sync(input);
776         }
777 }
778 
779 /* ACPI device for hotkey handling */
780 
781 static int acpi_fujitsu_hotkey_add(struct acpi_device *device)
782 {
783         int result = 0;
784         int state = 0;
785         struct input_dev *input;
786         int error;
787         int i;
788 
789         if (!device)
790                 return -EINVAL;
791 
792         fujitsu_hotkey->acpi_handle = device->handle;
793         sprintf(acpi_device_name(device), "%s",
794                 ACPI_FUJITSU_HOTKEY_DEVICE_NAME);
795         sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS);
796         device->driver_data = fujitsu_hotkey;
797 
798         /* kfifo */
799         spin_lock_init(&fujitsu_hotkey->fifo_lock);
800         error = kfifo_alloc(&fujitsu_hotkey->fifo, RINGBUFFERSIZE * sizeof(int),
801                         GFP_KERNEL);
802         if (error) {
803                 pr_err("kfifo_alloc failed\n");
804                 goto err_stop;
805         }
806 
807         fujitsu_hotkey->input = input = input_allocate_device();
808         if (!input) {
809                 error = -ENOMEM;
810                 goto err_free_fifo;
811         }
812 
813         snprintf(fujitsu_hotkey->phys, sizeof(fujitsu_hotkey->phys),
814                  "%s/video/input0", acpi_device_hid(device));
815 
816         input->name = acpi_device_name(device);
817         input->phys = fujitsu_hotkey->phys;
818         input->id.bustype = BUS_HOST;
819         input->id.product = 0x06;
820         input->dev.parent = &device->dev;
821 
822         set_bit(EV_KEY, input->evbit);
823         set_bit(fujitsu->keycode1, input->keybit);
824         set_bit(fujitsu->keycode2, input->keybit);
825         set_bit(fujitsu->keycode3, input->keybit);
826         set_bit(fujitsu->keycode4, input->keybit);
827         set_bit(fujitsu->keycode5, input->keybit);
828         set_bit(KEY_UNKNOWN, input->keybit);
829 
830         error = input_register_device(input);
831         if (error)
832                 goto err_free_input_dev;
833 
834         error = acpi_bus_update_power(fujitsu_hotkey->acpi_handle, &state);
835         if (error) {
836                 pr_err("Error reading power state\n");
837                 goto err_unregister_input_dev;
838         }
839 
840         pr_info("ACPI: %s [%s] (%s)\n",
841                 acpi_device_name(device), acpi_device_bid(device),
842                 !device->power.state ? "on" : "off");
843 
844         fujitsu_hotkey->dev = device;
845 
846         if (acpi_has_method(device->handle, METHOD_NAME__INI)) {
847                 vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n");
848                 if (ACPI_FAILURE
849                     (acpi_evaluate_object
850                      (device->handle, METHOD_NAME__INI, NULL, NULL)))
851                         pr_err("_INI Method failed\n");
852         }
853 
854         i = 0;
855         while (call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0) != 0
856                 && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE)
857                 ; /* No action, result is discarded */
858         vdbg_printk(FUJLAPTOP_DBG_INFO, "Discarded %i ringbuffer entries\n", i);
859 
860         fujitsu_hotkey->rfkill_supported =
861                 call_fext_func(FUNC_RFKILL, 0x0, 0x0, 0x0);
862 
863         /* Make sure our bitmask of supported functions is cleared if the
864            RFKILL function block is not implemented, like on the S7020. */
865         if (fujitsu_hotkey->rfkill_supported == UNSUPPORTED_CMD)
866                 fujitsu_hotkey->rfkill_supported = 0;
867 
868         if (fujitsu_hotkey->rfkill_supported)
869                 fujitsu_hotkey->rfkill_state =
870                         call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0);
871 
872         /* Suspect this is a keymap of the application panel, print it */
873         pr_info("BTNI: [0x%x]\n", call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0));
874 
875 #if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
876         if (call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) {
877                 result = led_classdev_register(&fujitsu->pf_device->dev,
878                                                 &logolamp_led);
879                 if (result == 0) {
880                         fujitsu_hotkey->logolamp_registered = 1;
881                 } else {
882                         pr_err("Could not register LED handler for logo lamp, error %i\n",
883                                result);
884                 }
885         }
886 
887         if ((call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & KEYBOARD_LAMPS) &&
888            (call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0) == 0x0)) {
889                 result = led_classdev_register(&fujitsu->pf_device->dev,
890                                                 &kblamps_led);
891                 if (result == 0) {
892                         fujitsu_hotkey->kblamps_registered = 1;
893                 } else {
894                         pr_err("Could not register LED handler for keyboard lamps, error %i\n",
895                                result);
896                 }
897         }
898 #endif
899 
900         return result;
901 
902 err_unregister_input_dev:
903         input_unregister_device(input);
904         input = NULL;
905 err_free_input_dev:
906         input_free_device(input);
907 err_free_fifo:
908         kfifo_free(&fujitsu_hotkey->fifo);
909 err_stop:
910         return error;
911 }
912 
913 static int acpi_fujitsu_hotkey_remove(struct acpi_device *device)
914 {
915         struct fujitsu_hotkey_t *fujitsu_hotkey = acpi_driver_data(device);
916         struct input_dev *input = fujitsu_hotkey->input;
917 
918 #if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
919         if (fujitsu_hotkey->logolamp_registered)
920                 led_classdev_unregister(&logolamp_led);
921 
922         if (fujitsu_hotkey->kblamps_registered)
923                 led_classdev_unregister(&kblamps_led);
924 #endif
925 
926         input_unregister_device(input);
927 
928         kfifo_free(&fujitsu_hotkey->fifo);
929 
930         fujitsu_hotkey->acpi_handle = NULL;
931 
932         return 0;
933 }
934 
935 static void acpi_fujitsu_hotkey_notify(struct acpi_device *device, u32 event)
936 {
937         struct input_dev *input;
938         int keycode, keycode_r;
939         unsigned int irb = 1;
940         int i, status;
941 
942         input = fujitsu_hotkey->input;
943 
944         if (fujitsu_hotkey->rfkill_supported)
945                 fujitsu_hotkey->rfkill_state =
946                         call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0);
947 
948         switch (event) {
949         case ACPI_FUJITSU_NOTIFY_CODE1:
950                 i = 0;
951                 while ((irb =
952                         call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0
953                                 && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) {
954                         switch (irb & 0x4ff) {
955                         case KEY1_CODE:
956                                 keycode = fujitsu->keycode1;
957                                 break;
958                         case KEY2_CODE:
959                                 keycode = fujitsu->keycode2;
960                                 break;
961                         case KEY3_CODE:
962                                 keycode = fujitsu->keycode3;
963                                 break;
964                         case KEY4_CODE:
965                                 keycode = fujitsu->keycode4;
966                                 break;
967                         case KEY5_CODE:
968                                 keycode = fujitsu->keycode5;
969                                 break;
970                         case 0:
971                                 keycode = 0;
972                                 break;
973                         default:
974                                 vdbg_printk(FUJLAPTOP_DBG_WARN,
975                                             "Unknown GIRB result [%x]\n", irb);
976                                 keycode = -1;
977                                 break;
978                         }
979                         if (keycode > 0) {
980                                 vdbg_printk(FUJLAPTOP_DBG_TRACE,
981                                         "Push keycode into ringbuffer [%d]\n",
982                                         keycode);
983                                 status = kfifo_in_locked(&fujitsu_hotkey->fifo,
984                                                    (unsigned char *)&keycode,
985                                                    sizeof(keycode),
986                                                    &fujitsu_hotkey->fifo_lock);
987                                 if (status != sizeof(keycode)) {
988                                         vdbg_printk(FUJLAPTOP_DBG_WARN,
989                                             "Could not push keycode [0x%x]\n",
990                                             keycode);
991                                 } else {
992                                         input_report_key(input, keycode, 1);
993                                         input_sync(input);
994                                 }
995                         } else if (keycode == 0) {
996                                 while ((status =
997                                         kfifo_out_locked(
998                                          &fujitsu_hotkey->fifo,
999                                          (unsigned char *) &keycode_r,
1000                                          sizeof(keycode_r),
1001                                          &fujitsu_hotkey->fifo_lock))
1002                                          == sizeof(keycode_r)) {
1003                                         input_report_key(input, keycode_r, 0);
1004                                         input_sync(input);
1005                                         vdbg_printk(FUJLAPTOP_DBG_TRACE,
1006                                           "Pop keycode from ringbuffer [%d]\n",
1007                                           keycode_r);
1008                                 }
1009                         }
1010                 }
1011 
1012                 break;
1013         default:
1014                 keycode = KEY_UNKNOWN;
1015                 vdbg_printk(FUJLAPTOP_DBG_WARN,
1016                             "Unsupported event [0x%x]\n", event);
1017                 input_report_key(input, keycode, 1);
1018                 input_sync(input);
1019                 input_report_key(input, keycode, 0);
1020                 input_sync(input);
1021                 break;
1022         }
1023 }
1024 
1025 /* Initialization */
1026 
1027 static const struct acpi_device_id fujitsu_device_ids[] = {
1028         {ACPI_FUJITSU_HID, 0},
1029         {"", 0},
1030 };
1031 
1032 static struct acpi_driver acpi_fujitsu_driver = {
1033         .name = ACPI_FUJITSU_DRIVER_NAME,
1034         .class = ACPI_FUJITSU_CLASS,
1035         .ids = fujitsu_device_ids,
1036         .ops = {
1037                 .add = acpi_fujitsu_add,
1038                 .remove = acpi_fujitsu_remove,
1039                 .notify = acpi_fujitsu_notify,
1040                 },
1041 };
1042 
1043 static const struct acpi_device_id fujitsu_hotkey_device_ids[] = {
1044         {ACPI_FUJITSU_HOTKEY_HID, 0},
1045         {"", 0},
1046 };
1047 
1048 static struct acpi_driver acpi_fujitsu_hotkey_driver = {
1049         .name = ACPI_FUJITSU_HOTKEY_DRIVER_NAME,
1050         .class = ACPI_FUJITSU_CLASS,
1051         .ids = fujitsu_hotkey_device_ids,
1052         .ops = {
1053                 .add = acpi_fujitsu_hotkey_add,
1054                 .remove = acpi_fujitsu_hotkey_remove,
1055                 .notify = acpi_fujitsu_hotkey_notify,
1056                 },
1057 };
1058 
1059 static const struct acpi_device_id fujitsu_ids[] __used = {
1060         {ACPI_FUJITSU_HID, 0},
1061         {ACPI_FUJITSU_HOTKEY_HID, 0},
1062         {"", 0}
1063 };
1064 MODULE_DEVICE_TABLE(acpi, fujitsu_ids);
1065 
1066 static int __init fujitsu_init(void)
1067 {
1068         int ret, result, max_brightness;
1069 
1070         if (acpi_disabled)
1071                 return -ENODEV;
1072 
1073         fujitsu = kzalloc(sizeof(struct fujitsu_t), GFP_KERNEL);
1074         if (!fujitsu)
1075                 return -ENOMEM;
1076         fujitsu->keycode1 = KEY_PROG1;
1077         fujitsu->keycode2 = KEY_PROG2;
1078         fujitsu->keycode3 = KEY_PROG3;
1079         fujitsu->keycode4 = KEY_PROG4;
1080         fujitsu->keycode5 = KEY_RFKILL;
1081         dmi_check_system(fujitsu_dmi_table);
1082 
1083         result = acpi_bus_register_driver(&acpi_fujitsu_driver);
1084         if (result < 0) {
1085                 ret = -ENODEV;
1086                 goto fail_acpi;
1087         }
1088 
1089         /* Register platform stuff */
1090 
1091         fujitsu->pf_device = platform_device_alloc("fujitsu-laptop", -1);
1092         if (!fujitsu->pf_device) {
1093                 ret = -ENOMEM;
1094                 goto fail_platform_driver;
1095         }
1096 
1097         ret = platform_device_add(fujitsu->pf_device);
1098         if (ret)
1099                 goto fail_platform_device1;
1100 
1101         ret =
1102             sysfs_create_group(&fujitsu->pf_device->dev.kobj,
1103                                &fujitsupf_attribute_group);
1104         if (ret)
1105                 goto fail_platform_device2;
1106 
1107         /* Register backlight stuff */
1108 
1109         if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
1110                 struct backlight_properties props;
1111 
1112                 memset(&props, 0, sizeof(struct backlight_properties));
1113                 max_brightness = fujitsu->max_brightness;
1114                 props.type = BACKLIGHT_PLATFORM;
1115                 props.max_brightness = max_brightness - 1;
1116                 fujitsu->bl_device = backlight_device_register("fujitsu-laptop",
1117                                                                NULL, NULL,
1118                                                                &fujitsubl_ops,
1119                                                                &props);
1120                 if (IS_ERR(fujitsu->bl_device)) {
1121                         ret = PTR_ERR(fujitsu->bl_device);
1122                         fujitsu->bl_device = NULL;
1123                         goto fail_sysfs_group;
1124                 }
1125                 fujitsu->bl_device->props.brightness = fujitsu->brightness_level;
1126         }
1127 
1128         ret = platform_driver_register(&fujitsupf_driver);
1129         if (ret)
1130                 goto fail_backlight;
1131 
1132         /* Register hotkey driver */
1133 
1134         fujitsu_hotkey = kzalloc(sizeof(struct fujitsu_hotkey_t), GFP_KERNEL);
1135         if (!fujitsu_hotkey) {
1136                 ret = -ENOMEM;
1137                 goto fail_hotkey;
1138         }
1139 
1140         result = acpi_bus_register_driver(&acpi_fujitsu_hotkey_driver);
1141         if (result < 0) {
1142                 ret = -ENODEV;
1143                 goto fail_hotkey1;
1144         }
1145 
1146         /* Sync backlight power status (needs FUJ02E3 device, hence deferred) */
1147         if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
1148                 if (call_fext_func(FUNC_BACKLIGHT, 0x2, 0x4, 0x0) == 3)
1149                         fujitsu->bl_device->props.power = FB_BLANK_POWERDOWN;
1150                 else
1151                         fujitsu->bl_device->props.power = FB_BLANK_UNBLANK;
1152         }
1153 
1154         pr_info("driver " FUJITSU_DRIVER_VERSION " successfully loaded\n");
1155 
1156         return 0;
1157 
1158 fail_hotkey1:
1159         kfree(fujitsu_hotkey);
1160 fail_hotkey:
1161         platform_driver_unregister(&fujitsupf_driver);
1162 fail_backlight:
1163         backlight_device_unregister(fujitsu->bl_device);
1164 fail_sysfs_group:
1165         sysfs_remove_group(&fujitsu->pf_device->dev.kobj,
1166                            &fujitsupf_attribute_group);
1167 fail_platform_device2:
1168         platform_device_del(fujitsu->pf_device);
1169 fail_platform_device1:
1170         platform_device_put(fujitsu->pf_device);
1171 fail_platform_driver:
1172         acpi_bus_unregister_driver(&acpi_fujitsu_driver);
1173 fail_acpi:
1174         kfree(fujitsu);
1175 
1176         return ret;
1177 }
1178 
1179 static void __exit fujitsu_cleanup(void)
1180 {
1181         acpi_bus_unregister_driver(&acpi_fujitsu_hotkey_driver);
1182 
1183         kfree(fujitsu_hotkey);
1184 
1185         platform_driver_unregister(&fujitsupf_driver);
1186 
1187         backlight_device_unregister(fujitsu->bl_device);
1188 
1189         sysfs_remove_group(&fujitsu->pf_device->dev.kobj,
1190                            &fujitsupf_attribute_group);
1191 
1192         platform_device_unregister(fujitsu->pf_device);
1193 
1194         acpi_bus_unregister_driver(&acpi_fujitsu_driver);
1195 
1196         kfree(fujitsu);
1197 
1198         pr_info("driver unloaded\n");
1199 }
1200 
1201 module_init(fujitsu_init);
1202 module_exit(fujitsu_cleanup);
1203 
1204 module_param(use_alt_lcd_levels, uint, 0644);
1205 MODULE_PARM_DESC(use_alt_lcd_levels,
1206                  "Use alternative interface for lcd_levels (needed for Lifebook s6410).");
1207 module_param(disable_brightness_adjust, uint, 0644);
1208 MODULE_PARM_DESC(disable_brightness_adjust, "Disable brightness adjustment .");
1209 #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
1210 module_param_named(debug, dbg_level, uint, 0644);
1211 MODULE_PARM_DESC(debug, "Sets debug level bit-mask");
1212 #endif
1213 
1214 MODULE_AUTHOR("Jonathan Woithe, Peter Gruber, Tony Vroon");
1215 MODULE_DESCRIPTION("Fujitsu laptop extras support");
1216 MODULE_VERSION(FUJITSU_DRIVER_VERSION);
1217 MODULE_LICENSE("GPL");
1218 
1219 MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1D3:*:cvrS6410:*");
1220 MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1E6:*:cvrS6420:*");
1221 MODULE_ALIAS("dmi:*:svnFUJITSU:*:pvr:rvnFUJITSU:rnFJNB19C:*:cvrS7020:*");
1222 

This page was automatically generated by LXR 0.3.1 (source).  •  Linux is a registered trademark of Linus Torvalds  •  Contact us