Merge branch 'hwmon-for-linus' of git://jdelvare.pck.nerim.net/jdelvare-2.6
[linux-2.6] / 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 };
468
469 /* ACPI device for LCD brightness control */
470
471 static int acpi_fujitsu_add(struct acpi_device *device)
472 {
473         acpi_status status;
474         acpi_handle handle;
475         int result = 0;
476         int state = 0;
477         struct input_dev *input;
478         int error;
479
480         if (!device)
481                 return -EINVAL;
482
483         fujitsu->acpi_handle = device->handle;
484         sprintf(acpi_device_name(device), "%s", ACPI_FUJITSU_DEVICE_NAME);
485         sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS);
486         acpi_driver_data(device) = fujitsu;
487
488         status = acpi_install_notify_handler(device->handle,
489                                              ACPI_DEVICE_NOTIFY,
490                                              acpi_fujitsu_notify, fujitsu);
491
492         if (ACPI_FAILURE(status)) {
493                 printk(KERN_ERR "Error installing notify handler\n");
494                 error = -ENODEV;
495                 goto err_stop;
496         }
497
498         fujitsu->input = input = input_allocate_device();
499         if (!input) {
500                 error = -ENOMEM;
501                 goto err_uninstall_notify;
502         }
503
504         snprintf(fujitsu->phys, sizeof(fujitsu->phys),
505                  "%s/video/input0", acpi_device_hid(device));
506
507         input->name = acpi_device_name(device);
508         input->phys = fujitsu->phys;
509         input->id.bustype = BUS_HOST;
510         input->id.product = 0x06;
511         input->dev.parent = &device->dev;
512         input->evbit[0] = BIT(EV_KEY);
513         set_bit(KEY_BRIGHTNESSUP, input->keybit);
514         set_bit(KEY_BRIGHTNESSDOWN, input->keybit);
515         set_bit(KEY_UNKNOWN, input->keybit);
516
517         error = input_register_device(input);
518         if (error)
519                 goto err_free_input_dev;
520
521         result = acpi_bus_get_power(fujitsu->acpi_handle, &state);
522         if (result) {
523                 printk(KERN_ERR "Error reading power state\n");
524                 goto end;
525         }
526
527         printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
528                acpi_device_name(device), acpi_device_bid(device),
529                !device->power.state ? "on" : "off");
530
531         fujitsu->dev = device;
532
533         if (ACPI_SUCCESS
534             (acpi_get_handle(device->handle, METHOD_NAME__INI, &handle))) {
535                 vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n");
536                 if (ACPI_FAILURE
537                     (acpi_evaluate_object
538                      (device->handle, METHOD_NAME__INI, NULL, NULL)))
539                         printk(KERN_ERR "_INI Method failed\n");
540         }
541
542         /* do config (detect defaults) */
543         dmi_check_system(fujitsu_dmi_table);
544         use_alt_lcd_levels = use_alt_lcd_levels == 1 ? 1 : 0;
545         disable_brightness_keys = disable_brightness_keys == 1 ? 1 : 0;
546         disable_brightness_adjust = disable_brightness_adjust == 1 ? 1 : 0;
547         vdbg_printk(FUJLAPTOP_DBG_INFO,
548                     "config: [alt interface: %d], [key disable: %d], [adjust disable: %d]\n",
549                     use_alt_lcd_levels, disable_brightness_keys,
550                     disable_brightness_adjust);
551
552         if (get_max_brightness() <= 0)
553                 fujitsu->max_brightness = FUJITSU_LCD_N_LEVELS;
554         if (use_alt_lcd_levels)
555                 get_lcd_level_alt();
556         else
557                 get_lcd_level();
558
559         return result;
560
561 end:
562 err_free_input_dev:
563         input_free_device(input);
564 err_uninstall_notify:
565         acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
566                                    acpi_fujitsu_notify);
567 err_stop:
568
569         return result;
570 }
571
572 static int acpi_fujitsu_remove(struct acpi_device *device, int type)
573 {
574         acpi_status status;
575         struct fujitsu_t *fujitsu = NULL;
576
577         if (!device || !acpi_driver_data(device))
578                 return -EINVAL;
579
580         fujitsu = acpi_driver_data(device);
581
582         status = acpi_remove_notify_handler(fujitsu->acpi_handle,
583                                             ACPI_DEVICE_NOTIFY,
584                                             acpi_fujitsu_notify);
585
586         if (!device || !acpi_driver_data(device))
587                 return -EINVAL;
588
589         fujitsu->acpi_handle = NULL;
590
591         return 0;
592 }
593
594 /* Brightness notify */
595
596 static void acpi_fujitsu_notify(acpi_handle handle, u32 event, void *data)
597 {
598         struct input_dev *input;
599         int keycode;
600         int oldb, newb;
601
602         input = fujitsu->input;
603
604         switch (event) {
605         case ACPI_FUJITSU_NOTIFY_CODE1:
606                 keycode = 0;
607                 oldb = fujitsu->brightness_level;
608                 get_lcd_level();  /* the alt version always yields changed */
609                 newb = fujitsu->brightness_level;
610
611                 vdbg_printk(FUJLAPTOP_DBG_TRACE,
612                             "brightness button event [%i -> %i (%i)]\n",
613                             oldb, newb, fujitsu->brightness_changed);
614
615                 if (oldb == newb && fujitsu->brightness_changed) {
616                         keycode = 0;
617                         if (disable_brightness_keys != 1) {
618                                 if (oldb == 0) {
619                                         acpi_bus_generate_proc_event(fujitsu->
620                                                 dev,
621                                                 ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS,
622                                                 0);
623                                         keycode = KEY_BRIGHTNESSDOWN;
624                                 } else if (oldb ==
625                                            (fujitsu->max_brightness) - 1) {
626                                         acpi_bus_generate_proc_event(fujitsu->
627                                                 dev,
628                                                 ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS,
629                                                 0);
630                                         keycode = KEY_BRIGHTNESSUP;
631                                 }
632                         }
633                 } else if (oldb < newb) {
634                         if (disable_brightness_adjust != 1) {
635                                 if (use_alt_lcd_levels)
636                                         set_lcd_level_alt(newb);
637                                 else
638                                         set_lcd_level(newb);
639                         }
640                         if (disable_brightness_keys != 1) {
641                                 acpi_bus_generate_proc_event(fujitsu->dev,
642                                         ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS,
643                                         0);
644                                 keycode = KEY_BRIGHTNESSUP;
645                         }
646                 } else if (oldb > newb) {
647                         if (disable_brightness_adjust != 1) {
648                                 if (use_alt_lcd_levels)
649                                         set_lcd_level_alt(newb);
650                                 else
651                                         set_lcd_level(newb);
652                         }
653                         if (disable_brightness_keys != 1) {
654                                 acpi_bus_generate_proc_event(fujitsu->dev,
655                                         ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS,
656                                         0);
657                                 keycode = KEY_BRIGHTNESSDOWN;
658                         }
659                 } else {
660                         keycode = KEY_UNKNOWN;
661                 }
662                 break;
663         default:
664                 keycode = KEY_UNKNOWN;
665                 vdbg_printk(FUJLAPTOP_DBG_WARN,
666                             "unsupported event [0x%x]\n", event);
667                 break;
668         }
669
670         if (keycode != 0) {
671                 input_report_key(input, keycode, 1);
672                 input_sync(input);
673                 input_report_key(input, keycode, 0);
674                 input_sync(input);
675         }
676
677         return;
678 }
679
680 /* ACPI device for hotkey handling */
681
682 static int acpi_fujitsu_hotkey_add(struct acpi_device *device)
683 {
684         acpi_status status;
685         acpi_handle handle;
686         int result = 0;
687         int state = 0;
688         struct input_dev *input;
689         int error;
690         int i;
691
692         if (!device)
693                 return -EINVAL;
694
695         fujitsu_hotkey->acpi_handle = device->handle;
696         sprintf(acpi_device_name(device), "%s",
697                 ACPI_FUJITSU_HOTKEY_DEVICE_NAME);
698         sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS);
699         acpi_driver_data(device) = fujitsu_hotkey;
700
701         status = acpi_install_notify_handler(device->handle,
702                                              ACPI_DEVICE_NOTIFY,
703                                              acpi_fujitsu_hotkey_notify,
704                                              fujitsu_hotkey);
705
706         if (ACPI_FAILURE(status)) {
707                 printk(KERN_ERR "Error installing notify handler\n");
708                 error = -ENODEV;
709                 goto err_stop;
710         }
711
712         /* kfifo */
713         spin_lock_init(&fujitsu_hotkey->fifo_lock);
714         fujitsu_hotkey->fifo =
715             kfifo_alloc(RINGBUFFERSIZE * sizeof(int), GFP_KERNEL,
716                         &fujitsu_hotkey->fifo_lock);
717         if (IS_ERR(fujitsu_hotkey->fifo)) {
718                 printk(KERN_ERR "kfifo_alloc failed\n");
719                 error = PTR_ERR(fujitsu_hotkey->fifo);
720                 goto err_stop;
721         }
722
723         fujitsu_hotkey->input = input = input_allocate_device();
724         if (!input) {
725                 error = -ENOMEM;
726                 goto err_uninstall_notify;
727         }
728
729         snprintf(fujitsu_hotkey->phys, sizeof(fujitsu_hotkey->phys),
730                  "%s/video/input0", acpi_device_hid(device));
731
732         input->name = acpi_device_name(device);
733         input->phys = fujitsu_hotkey->phys;
734         input->id.bustype = BUS_HOST;
735         input->id.product = 0x06;
736         input->dev.parent = &device->dev;
737         input->evbit[0] = BIT(EV_KEY);
738         set_bit(KEY_SCREENLOCK, input->keybit);
739         set_bit(KEY_MEDIA, input->keybit);
740         set_bit(KEY_EMAIL, input->keybit);
741         set_bit(KEY_SUSPEND, input->keybit);
742         set_bit(KEY_UNKNOWN, input->keybit);
743
744         error = input_register_device(input);
745         if (error)
746                 goto err_free_input_dev;
747
748         result = acpi_bus_get_power(fujitsu_hotkey->acpi_handle, &state);
749         if (result) {
750                 printk(KERN_ERR "Error reading power state\n");
751                 goto end;
752         }
753
754         printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
755                acpi_device_name(device), acpi_device_bid(device),
756                !device->power.state ? "on" : "off");
757
758         fujitsu_hotkey->dev = device;
759
760         if (ACPI_SUCCESS
761             (acpi_get_handle(device->handle, METHOD_NAME__INI, &handle))) {
762                 vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n");
763                 if (ACPI_FAILURE
764                     (acpi_evaluate_object
765                      (device->handle, METHOD_NAME__INI, NULL, NULL)))
766                         printk(KERN_ERR "_INI Method failed\n");
767         }
768
769         i = 0;                  /* Discard hotkey ringbuffer */
770         while (get_irb() != 0 && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) ;
771         vdbg_printk(FUJLAPTOP_DBG_INFO, "Discarded %i ringbuffer entries\n", i);
772
773         return result;
774
775 end:
776 err_free_input_dev:
777         input_free_device(input);
778 err_uninstall_notify:
779         acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
780                                    acpi_fujitsu_hotkey_notify);
781         kfifo_free(fujitsu_hotkey->fifo);
782 err_stop:
783
784         return result;
785 }
786
787 static int acpi_fujitsu_hotkey_remove(struct acpi_device *device, int type)
788 {
789         acpi_status status;
790         struct fujitsu_hotkey_t *fujitsu_hotkey = NULL;
791
792         if (!device || !acpi_driver_data(device))
793                 return -EINVAL;
794
795         fujitsu_hotkey = acpi_driver_data(device);
796
797         status = acpi_remove_notify_handler(fujitsu_hotkey->acpi_handle,
798                                             ACPI_DEVICE_NOTIFY,
799                                             acpi_fujitsu_hotkey_notify);
800
801         fujitsu_hotkey->acpi_handle = NULL;
802
803         kfifo_free(fujitsu_hotkey->fifo);
804
805         return 0;
806 }
807
808 static void acpi_fujitsu_hotkey_notify(acpi_handle handle, u32 event,
809                                        void *data)
810 {
811         struct input_dev *input;
812         int keycode, keycode_r;
813         unsigned int irb = 1;
814         int i, status;
815
816         input = fujitsu_hotkey->input;
817
818         vdbg_printk(FUJLAPTOP_DBG_TRACE, "Hotkey event\n");
819
820         switch (event) {
821         case ACPI_FUJITSU_NOTIFY_CODE1:
822                 i = 0;
823                 while ((irb = get_irb()) != 0
824                        && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) {
825                         vdbg_printk(FUJLAPTOP_DBG_TRACE, "GIRB result [%x]\n",
826                                     irb);
827
828                         switch (irb & 0x4ff) {
829                         case LOCK_KEY:
830                                 keycode = KEY_SCREENLOCK;
831                                 break;
832                         case DISPLAY_KEY:
833                                 keycode = KEY_MEDIA;
834                                 break;
835                         case ENERGY_KEY:
836                                 keycode = KEY_EMAIL;
837                                 break;
838                         case REST_KEY:
839                                 keycode = KEY_SUSPEND;
840                                 break;
841                         case 0:
842                                 keycode = 0;
843                                 break;
844                         default:
845                                 vdbg_printk(FUJLAPTOP_DBG_WARN,
846                                         "Unknown GIRB result [%x]\n", irb);
847                                 keycode = -1;
848                                 break;
849                         }
850                         if (keycode > 0) {
851                                 vdbg_printk(FUJLAPTOP_DBG_TRACE,
852                                         "Push keycode into ringbuffer [%d]\n",
853                                         keycode);
854                                 status = kfifo_put(fujitsu_hotkey->fifo,
855                                                 (unsigned char *)&keycode,
856                                                 sizeof(keycode));
857                                 if (status != sizeof(keycode)) {
858                                         vdbg_printk(FUJLAPTOP_DBG_WARN,
859                                                 "Could not push keycode [0x%x]\n",
860                                                 keycode);
861                                 } else {
862                                         input_report_key(input, keycode, 1);
863                                         input_sync(input);
864                                 }
865                         } else if (keycode == 0) {
866                                 while ((status =
867                                         kfifo_get
868                                         (fujitsu_hotkey->fifo, (unsigned char *)
869                                          &keycode_r,
870                                          sizeof
871                                          (keycode_r))) == sizeof(keycode_r)) {
872                                         input_report_key(input, keycode_r, 0);
873                                         input_sync(input);
874                                         vdbg_printk(FUJLAPTOP_DBG_TRACE,
875                                                     "Pop keycode from ringbuffer [%d]\n",
876                                                     keycode_r);
877                                 }
878                         }
879                 }
880
881                 break;
882         default:
883                 keycode = KEY_UNKNOWN;
884                 vdbg_printk(FUJLAPTOP_DBG_WARN,
885                             "Unsupported event [0x%x]\n", event);
886                 input_report_key(input, keycode, 1);
887                 input_sync(input);
888                 input_report_key(input, keycode, 0);
889                 input_sync(input);
890                 break;
891         }
892
893         return;
894 }
895
896 /* Initialization */
897
898 static const struct acpi_device_id fujitsu_device_ids[] = {
899         {ACPI_FUJITSU_HID, 0},
900         {"", 0},
901 };
902
903 static struct acpi_driver acpi_fujitsu_driver = {
904         .name = ACPI_FUJITSU_DRIVER_NAME,
905         .class = ACPI_FUJITSU_CLASS,
906         .ids = fujitsu_device_ids,
907         .ops = {
908                 .add = acpi_fujitsu_add,
909                 .remove = acpi_fujitsu_remove,
910                 },
911 };
912
913 static const struct acpi_device_id fujitsu_hotkey_device_ids[] = {
914         {ACPI_FUJITSU_HOTKEY_HID, 0},
915         {"", 0},
916 };
917
918 static struct acpi_driver acpi_fujitsu_hotkey_driver = {
919         .name = ACPI_FUJITSU_HOTKEY_DRIVER_NAME,
920         .class = ACPI_FUJITSU_CLASS,
921         .ids = fujitsu_hotkey_device_ids,
922         .ops = {
923                 .add = acpi_fujitsu_hotkey_add,
924                 .remove = acpi_fujitsu_hotkey_remove,
925                 },
926 };
927
928 static int __init fujitsu_init(void)
929 {
930         int ret, result, max_brightness;
931
932         if (acpi_disabled)
933                 return -ENODEV;
934
935         fujitsu = kmalloc(sizeof(struct fujitsu_t), GFP_KERNEL);
936         if (!fujitsu)
937                 return -ENOMEM;
938         memset(fujitsu, 0, sizeof(struct fujitsu_t));
939
940         result = acpi_bus_register_driver(&acpi_fujitsu_driver);
941         if (result < 0) {
942                 ret = -ENODEV;
943                 goto fail_acpi;
944         }
945
946         /* Register platform stuff */
947
948         fujitsu->pf_device = platform_device_alloc("fujitsu-laptop", -1);
949         if (!fujitsu->pf_device) {
950                 ret = -ENOMEM;
951                 goto fail_platform_driver;
952         }
953
954         ret = platform_device_add(fujitsu->pf_device);
955         if (ret)
956                 goto fail_platform_device1;
957
958         ret =
959             sysfs_create_group(&fujitsu->pf_device->dev.kobj,
960                                &fujitsupf_attribute_group);
961         if (ret)
962                 goto fail_platform_device2;
963
964         /* Register backlight stuff */
965
966         fujitsu->bl_device =
967             backlight_device_register("fujitsu-laptop", NULL, NULL,
968                                       &fujitsubl_ops);
969         if (IS_ERR(fujitsu->bl_device))
970                 return PTR_ERR(fujitsu->bl_device);
971
972         max_brightness = fujitsu->max_brightness;
973
974         fujitsu->bl_device->props.max_brightness = max_brightness - 1;
975         fujitsu->bl_device->props.brightness = fujitsu->brightness_level;
976
977         ret = platform_driver_register(&fujitsupf_driver);
978         if (ret)
979                 goto fail_backlight;
980
981         /* Register hotkey driver */
982
983         fujitsu_hotkey = kmalloc(sizeof(struct fujitsu_hotkey_t), GFP_KERNEL);
984         if (!fujitsu_hotkey) {
985                 ret = -ENOMEM;
986                 goto fail_hotkey;
987         }
988         memset(fujitsu_hotkey, 0, sizeof(struct fujitsu_hotkey_t));
989
990         result = acpi_bus_register_driver(&acpi_fujitsu_hotkey_driver);
991         if (result < 0) {
992                 ret = -ENODEV;
993                 goto fail_hotkey1;
994         }
995
996         printk(KERN_INFO "fujitsu-laptop: driver " FUJITSU_DRIVER_VERSION
997                " successfully loaded.\n");
998
999         return 0;
1000
1001 fail_hotkey1:
1002
1003         kfree(fujitsu_hotkey);
1004
1005 fail_hotkey:
1006
1007         platform_driver_unregister(&fujitsupf_driver);
1008
1009 fail_backlight:
1010
1011         backlight_device_unregister(fujitsu->bl_device);
1012
1013 fail_platform_device2:
1014
1015         platform_device_del(fujitsu->pf_device);
1016
1017 fail_platform_device1:
1018
1019         platform_device_put(fujitsu->pf_device);
1020
1021 fail_platform_driver:
1022
1023         acpi_bus_unregister_driver(&acpi_fujitsu_driver);
1024
1025 fail_acpi:
1026
1027         kfree(fujitsu);
1028
1029         return ret;
1030 }
1031
1032 static void __exit fujitsu_cleanup(void)
1033 {
1034         sysfs_remove_group(&fujitsu->pf_device->dev.kobj,
1035                            &fujitsupf_attribute_group);
1036         platform_device_unregister(fujitsu->pf_device);
1037         platform_driver_unregister(&fujitsupf_driver);
1038         backlight_device_unregister(fujitsu->bl_device);
1039
1040         acpi_bus_unregister_driver(&acpi_fujitsu_driver);
1041
1042         kfree(fujitsu);
1043
1044         acpi_bus_unregister_driver(&acpi_fujitsu_hotkey_driver);
1045
1046         kfree(fujitsu_hotkey);
1047
1048         printk(KERN_INFO "fujitsu-laptop: driver unloaded.\n");
1049 }
1050
1051 module_init(fujitsu_init);
1052 module_exit(fujitsu_cleanup);
1053
1054 module_param(use_alt_lcd_levels, uint, 0644);
1055 MODULE_PARM_DESC(use_alt_lcd_levels,
1056                  "Use alternative interface for lcd_levels (needed for Lifebook s6410).");
1057 module_param(disable_brightness_keys, uint, 0644);
1058 MODULE_PARM_DESC(disable_brightness_keys,
1059                  "Disable brightness keys (eg. if they are already handled by the generic ACPI_VIDEO device).");
1060 module_param(disable_brightness_adjust, uint, 0644);
1061 MODULE_PARM_DESC(disable_brightness_adjust, "Disable brightness adjustment .");
1062 #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
1063 module_param_named(debug, dbg_level, uint, 0644);
1064 MODULE_PARM_DESC(debug, "Sets debug level bit-mask");
1065 #endif
1066
1067 MODULE_AUTHOR("Jonathan Woithe, Peter Gruber");
1068 MODULE_DESCRIPTION("Fujitsu laptop extras support");
1069 MODULE_VERSION(FUJITSU_DRIVER_VERSION);
1070 MODULE_LICENSE("GPL");
1071
1072 MODULE_ALIAS
1073     ("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1D3:*:cvrS6410:*");
1074 MODULE_ALIAS
1075     ("dmi:*:svnFUJITSU:*:pvr:rvnFUJITSU:rnFJNB19C:*:cvrS7020:*");
1076
1077 static struct pnp_device_id pnp_ids[] = {
1078         { .id = "FUJ02bf" },
1079         { .id = "FUJ02B1" },
1080         { .id = "FUJ02E3" },
1081         { .id = "" }
1082 };
1083 MODULE_DEVICE_TABLE(pnp, pnp_ids);