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