aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/fujitsu-laptop.c
diff options
context:
space:
mode:
authorJonathan Woithe <jwoithe@physics.adelaide.edu.au>2008-06-10 20:44:56 -0400
committerAndi Kleen <andi@basil.nowhere.org>2008-07-16 17:27:01 -0400
commit20b937343e55c16e37b1a4ad2176760b4a11002c (patch)
treefe79bea945e9fd2207c8c1d787019f68eb19efb2 /drivers/misc/fujitsu-laptop.c
parent706546d02384b64e083bd9130c56eaa599c66038 (diff)
Fujitsu-laptop update
Add additional capabilities to the Fujitsu-laptop driver. * Brightness hotkey actions are sent to userspace. This can be disabled using a module parameter if it causes issues with models which handle these keys transparently in the BIOS. * Actions of additional hotkeys found on some Fujitsu models (eg: the suspend key and the dedicated "power on passphrase" keys) are broadcast to userspace. * An alternative brightness control method used by some Fujitsu models (for example, the S6410) is now supported, enabling software brightness controls on models using this method. * DMI-based module aliases are configured for the S6410 and S7020. * The current LCD brightness after booting should now be reflected in the standard backlight interface sysfs file (previously it was always set to 0). The platform brightness sysfs interface has always been fine. Thanks go to Peter Gruber who provided a significant portion of this code and tested various iterations of the patch on his S6410. Signed-off-by: Peter Gruber <nokos@gmx.net> Signed-off-by: Jonathan Woithe <jwoithe@physics.adelaide.edu.au> Signed-off-by: Len Brown <len.brown@intel.com> Signed-off-by: Andi Kleen <ak@linux.intel.com>
Diffstat (limited to 'drivers/misc/fujitsu-laptop.c')
-rw-r--r--drivers/misc/fujitsu-laptop.c825
1 files changed, 774 insertions, 51 deletions
diff --git a/drivers/misc/fujitsu-laptop.c b/drivers/misc/fujitsu-laptop.c
index 6d14e8fe1537..7a1ef6c262de 100644
--- a/drivers/misc/fujitsu-laptop.c
+++ b/drivers/misc/fujitsu-laptop.c
@@ -1,12 +1,14 @@
1/*-*-linux-c-*-*/ 1/*-*-linux-c-*-*/
2 2
3/* 3/*
4 Copyright (C) 2007 Jonathan Woithe <jwoithe@physics.adelaide.edu.au> 4 Copyright (C) 2007,2008 Jonathan Woithe <jwoithe@physics.adelaide.edu.au>
5 Copyright (C) 2008 Peter Gruber <nokos@gmx.net>
5 Based on earlier work: 6 Based on earlier work:
6 Copyright (C) 2003 Shane Spencer <shane@bogomip.com> 7 Copyright (C) 2003 Shane Spencer <shane@bogomip.com>
7 Adrian Yee <brewt-fujitsu@brewt.org> 8 Adrian Yee <brewt-fujitsu@brewt.org>
8 9
9 Templated from msi-laptop.c which is copyright by its respective authors. 10 Templated from msi-laptop.c and thinkpad_acpi.c which is copyright
11 by its respective authors.
10 12
11 This program is free software; you can redistribute it and/or modify 13 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by 14 it under the terms of the GNU General Public License as published by
@@ -39,8 +41,17 @@
39 * registers itself in the Linux backlight control subsystem and is 41 * registers itself in the Linux backlight control subsystem and is
40 * available to userspace under /sys/class/backlight/fujitsu-laptop/. 42 * available to userspace under /sys/class/backlight/fujitsu-laptop/.
41 * 43 *
42 * This driver has been tested on a Fujitsu Lifebook S7020. It should 44 * Hotkeys present on certain Fujitsu laptops (eg: the S6xxx series) are
43 * work on most P-series and S-series Lifebooks, but YMMV. 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 *
44 */ 55 */
45 56
46#include <linux/module.h> 57#include <linux/module.h>
@@ -49,30 +60,105 @@
49#include <linux/acpi.h> 60#include <linux/acpi.h>
50#include <linux/dmi.h> 61#include <linux/dmi.h>
51#include <linux/backlight.h> 62#include <linux/backlight.h>
63#include <linux/input.h>
64#include <linux/kfifo.h>
65#include <linux/video_output.h>
52#include <linux/platform_device.h> 66#include <linux/platform_device.h>
53 67
54#define FUJITSU_DRIVER_VERSION "0.3" 68#define FUJITSU_DRIVER_VERSION "0.4.2"
55 69
56#define FUJITSU_LCD_N_LEVELS 8 70#define FUJITSU_LCD_N_LEVELS 8
57 71
58#define ACPI_FUJITSU_CLASS "fujitsu" 72#define ACPI_FUJITSU_CLASS "fujitsu"
59#define ACPI_FUJITSU_HID "FUJ02B1" 73#define ACPI_FUJITSU_HID "FUJ02B1"
60#define ACPI_FUJITSU_DRIVER_NAME "Fujitsu laptop FUJ02B1 ACPI extras driver" 74#define ACPI_FUJITSU_DRIVER_NAME "Fujitsu laptop FUJ02B1 ACPI brightness driver"
61#define ACPI_FUJITSU_DEVICE_NAME "Fujitsu FUJ02B1" 75#define ACPI_FUJITSU_DEVICE_NAME "Fujitsu FUJ02B1"
62 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 */
63struct fujitsu_t { 119struct fujitsu_t {
64 acpi_handle acpi_handle; 120 acpi_handle acpi_handle;
121 struct acpi_device *dev;
122 struct input_dev *input;
123 char phys[32];
65 struct backlight_device *bl_device; 124 struct backlight_device *bl_device;
66 struct platform_device *pf_device; 125 struct platform_device *pf_device;
67 126
68 unsigned long fuj02b1_state; 127 unsigned int max_brightness;
69 unsigned int brightness_changed; 128 unsigned int brightness_changed;
70 unsigned int brightness_level; 129 unsigned int brightness_level;
71}; 130};
72 131
73static struct fujitsu_t *fujitsu; 132static struct fujitsu_t *fujitsu;
133static int use_alt_lcd_levels = -1;
134static int disable_brightness_keys = -1;
135static int disable_brightness_adjust = -1;
136
137/* Device used to access other hotkeys on the laptop */
138struct 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};
74 149
75/* Hardware access */ 150static struct fujitsu_hotkey_t *fujitsu_hotkey;
151
152static void acpi_fujitsu_hotkey_notify(acpi_handle handle, u32 event,
153 void *data);
154
155#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
156static u32 dbg_level = 0x03;
157#endif
158
159static void acpi_fujitsu_notify(acpi_handle handle, u32 event, void *data);
160
161/* Hardware access for LCD brightness control */
76 162
77static int set_lcd_level(int level) 163static int set_lcd_level(int level)
78{ 164{
@@ -81,7 +167,10 @@ static int set_lcd_level(int level)
81 struct acpi_object_list arg_list = { 1, &arg0 }; 167 struct acpi_object_list arg_list = { 1, &arg0 };
82 acpi_handle handle = NULL; 168 acpi_handle handle = NULL;
83 169
84 if (level < 0 || level >= FUJITSU_LCD_N_LEVELS) 170 vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via SBLL [%d]\n",
171 level);
172
173 if (level < 0 || level >= fujitsu->max_brightness)
85 return -EINVAL; 174 return -EINVAL;
86 175
87 if (!fujitsu) 176 if (!fujitsu)
@@ -89,7 +178,38 @@ static int set_lcd_level(int level)
89 178
90 status = acpi_get_handle(fujitsu->acpi_handle, "SBLL", &handle); 179 status = acpi_get_handle(fujitsu->acpi_handle, "SBLL", &handle);
91 if (ACPI_FAILURE(status)) { 180 if (ACPI_FAILURE(status)) {
92 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "SBLL not present\n")); 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
194static 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");
93 return -ENODEV; 213 return -ENODEV;
94 } 214 }
95 215
@@ -107,13 +227,52 @@ static int get_lcd_level(void)
107 unsigned long state = 0; 227 unsigned long state = 0;
108 acpi_status status = AE_OK; 228 acpi_status status = AE_OK;
109 229
110 // Get the Brightness 230 vdbg_printk(FUJLAPTOP_DBG_TRACE, "get lcd level via GBLL\n");
231
111 status = 232 status =
112 acpi_evaluate_integer(fujitsu->acpi_handle, "GBLL", NULL, &state); 233 acpi_evaluate_integer(fujitsu->acpi_handle, "GBLL", NULL, &state);
113 if (status < 0) 234 if (status < 0)
114 return status; 235 return status;
115 236
116 fujitsu->fuj02b1_state = state; 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
247static 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
264static 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
117 fujitsu->brightness_level = state & 0x0fffffff; 276 fujitsu->brightness_level = state & 0x0fffffff;
118 277
119 if (state & 0x80000000) 278 if (state & 0x80000000)
@@ -128,12 +287,18 @@ static int get_lcd_level(void)
128 287
129static int bl_get_brightness(struct backlight_device *b) 288static int bl_get_brightness(struct backlight_device *b)
130{ 289{
131 return get_lcd_level(); 290 if (use_alt_lcd_levels)
291 return get_lcd_level_alt();
292 else
293 return get_lcd_level();
132} 294}
133 295
134static int bl_update_status(struct backlight_device *b) 296static int bl_update_status(struct backlight_device *b)
135{ 297{
136 return set_lcd_level(b->props.brightness); 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);
137} 302}
138 303
139static struct backlight_ops fujitsubl_ops = { 304static struct backlight_ops fujitsubl_ops = {
@@ -141,7 +306,35 @@ static struct backlight_ops fujitsubl_ops = {
141 .update_status = bl_update_status, 306 .update_status = bl_update_status,
142}; 307};
143 308
144/* Platform device */ 309/* Platform LCD brightness device */
310
311static ssize_t
312show_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
325static ssize_t
326show_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}
145 338
146static ssize_t show_lcd_level(struct device *dev, 339static ssize_t show_lcd_level(struct device *dev,
147 struct device_attribute *attr, char *buf) 340 struct device_attribute *attr, char *buf)
@@ -149,7 +342,10 @@ static ssize_t show_lcd_level(struct device *dev,
149 342
150 int ret; 343 int ret;
151 344
152 ret = get_lcd_level(); 345 if (use_alt_lcd_levels)
346 ret = get_lcd_level_alt();
347 else
348 ret = get_lcd_level();
153 if (ret < 0) 349 if (ret < 0)
154 return ret; 350 return ret;
155 351
@@ -164,19 +360,61 @@ static ssize_t store_lcd_level(struct device *dev,
164 int level, ret; 360 int level, ret;
165 361
166 if (sscanf(buf, "%i", &level) != 1 362 if (sscanf(buf, "%i", &level) != 1
167 || (level < 0 || level >= FUJITSU_LCD_N_LEVELS)) 363 || (level < 0 || level >= fujitsu->max_brightness))
168 return -EINVAL; 364 return -EINVAL;
169 365
170 ret = set_lcd_level(level); 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();
171 if (ret < 0) 377 if (ret < 0)
172 return ret; 378 return ret;
173 379
174 return count; 380 return count;
175} 381}
176 382
383/* Hardware access for hotkey device */
384
385static 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
403static ssize_t
404ignore_store(struct device *dev,
405 struct device_attribute *attr, const char *buf, size_t count)
406{
407 return count;
408}
409
410static DEVICE_ATTR(max_brightness, 0444, show_max_brightness, ignore_store);
411static DEVICE_ATTR(brightness_changed, 0444, show_brightness_changed,
412 ignore_store);
177static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level); 413static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level);
178 414
179static struct attribute *fujitsupf_attributes[] = { 415static struct attribute *fujitsupf_attributes[] = {
416 &dev_attr_brightness_changed.attr,
417 &dev_attr_max_brightness.attr,
180 &dev_attr_lcd_level.attr, 418 &dev_attr_lcd_level.attr,
181 NULL 419 NULL
182}; 420};
@@ -192,14 +430,52 @@ static struct platform_driver fujitsupf_driver = {
192 } 430 }
193}; 431};
194 432
195/* ACPI device */ 433static 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
458static 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 */
196 470
197static int acpi_fujitsu_add(struct acpi_device *device) 471static int acpi_fujitsu_add(struct acpi_device *device)
198{ 472{
473 acpi_status status;
474 acpi_handle handle;
199 int result = 0; 475 int result = 0;
200 int state = 0; 476 int state = 0;
201 477 struct input_dev *input;
202 ACPI_FUNCTION_TRACE("acpi_fujitsu_add"); 478 int error;
203 479
204 if (!device) 480 if (!device)
205 return -EINVAL; 481 return -EINVAL;
@@ -209,10 +485,42 @@ static int acpi_fujitsu_add(struct acpi_device *device)
209 sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS); 485 sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS);
210 acpi_driver_data(device) = fujitsu; 486 acpi_driver_data(device) = fujitsu;
211 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
212 result = acpi_bus_get_power(fujitsu->acpi_handle, &state); 521 result = acpi_bus_get_power(fujitsu->acpi_handle, &state);
213 if (result) { 522 if (result) {
214 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 523 printk(KERN_ERR "Error reading power state\n");
215 "Error reading power state\n"));
216 goto end; 524 goto end;
217 } 525 }
218 526
@@ -220,22 +528,373 @@ static int acpi_fujitsu_add(struct acpi_device *device)
220 acpi_device_name(device), acpi_device_bid(device), 528 acpi_device_name(device), acpi_device_bid(device),
221 !device->power.state ? "on" : "off"); 529 !device->power.state ? "on" : "off");
222 530
223 end: 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
561end:
562err_free_input_dev:
563 input_free_device(input);
564err_uninstall_notify:
565 acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
566 acpi_fujitsu_notify);
567err_stop:
224 568
225 return result; 569 return result;
226} 570}
227 571
228static int acpi_fujitsu_remove(struct acpi_device *device, int type) 572static int acpi_fujitsu_remove(struct acpi_device *device, int type)
229{ 573{
230 ACPI_FUNCTION_TRACE("acpi_fujitsu_remove"); 574 acpi_status status;
575 struct fujitsu_t *fujitsu = NULL;
231 576
232 if (!device || !acpi_driver_data(device)) 577 if (!device || !acpi_driver_data(device))
233 return -EINVAL; 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
234 fujitsu->acpi_handle = NULL; 589 fujitsu->acpi_handle = NULL;
235 590
236 return 0; 591 return 0;
237} 592}
238 593
594/* Brightness notify */
595
596static 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
682static 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
775end:
776err_free_input_dev:
777 input_free_device(input);
778err_uninstall_notify:
779 acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
780 acpi_fujitsu_hotkey_notify);
781 kfifo_free(fujitsu_hotkey->fifo);
782err_stop:
783
784 return result;
785}
786
787static 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
808static 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
239static const struct acpi_device_id fujitsu_device_ids[] = { 898static const struct acpi_device_id fujitsu_device_ids[] = {
240 {ACPI_FUJITSU_HID, 0}, 899 {ACPI_FUJITSU_HID, 0},
241 {"", 0}, 900 {"", 0},
@@ -251,11 +910,24 @@ static struct acpi_driver acpi_fujitsu_driver = {
251 }, 910 },
252}; 911};
253 912
254/* Initialization */ 913static const struct acpi_device_id fujitsu_hotkey_device_ids[] = {
914 {ACPI_FUJITSU_HOTKEY_HID, 0},
915 {"", 0},
916};
917
918static 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};
255 927
256static int __init fujitsu_init(void) 928static int __init fujitsu_init(void)
257{ 929{
258 int ret, result; 930 int ret, result, max_brightness;
259 931
260 if (acpi_disabled) 932 if (acpi_disabled)
261 return -ENODEV; 933 return -ENODEV;
@@ -271,19 +943,6 @@ static int __init fujitsu_init(void)
271 goto fail_acpi; 943 goto fail_acpi;
272 } 944 }
273 945
274 /* Register backlight stuff */
275
276 fujitsu->bl_device =
277 backlight_device_register("fujitsu-laptop", NULL, NULL,
278 &fujitsubl_ops);
279 if (IS_ERR(fujitsu->bl_device))
280 return PTR_ERR(fujitsu->bl_device);
281
282 fujitsu->bl_device->props.max_brightness = FUJITSU_LCD_N_LEVELS - 1;
283 ret = platform_driver_register(&fujitsupf_driver);
284 if (ret)
285 goto fail_backlight;
286
287 /* Register platform stuff */ 946 /* Register platform stuff */
288 947
289 fujitsu->pf_device = platform_device_alloc("fujitsu-laptop", -1); 948 fujitsu->pf_device = platform_device_alloc("fujitsu-laptop", -1);
@@ -302,28 +961,68 @@ static int __init fujitsu_init(void)
302 if (ret) 961 if (ret)
303 goto fail_platform_device2; 962 goto fail_platform_device2;
304 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
305 printk(KERN_INFO "fujitsu-laptop: driver " FUJITSU_DRIVER_VERSION 996 printk(KERN_INFO "fujitsu-laptop: driver " FUJITSU_DRIVER_VERSION
306 " successfully loaded.\n"); 997 " successfully loaded.\n");
307 998
308 return 0; 999 return 0;
309 1000
310 fail_platform_device2: 1001fail_hotkey1:
311 1002
312 platform_device_del(fujitsu->pf_device); 1003 kfree(fujitsu_hotkey);
313
314 fail_platform_device1:
315
316 platform_device_put(fujitsu->pf_device);
317 1004
318 fail_platform_driver: 1005fail_hotkey:
319 1006
320 platform_driver_unregister(&fujitsupf_driver); 1007 platform_driver_unregister(&fujitsupf_driver);
321 1008
322 fail_backlight: 1009fail_backlight:
323 1010
324 backlight_device_unregister(fujitsu->bl_device); 1011 backlight_device_unregister(fujitsu->bl_device);
325 1012
326 fail_acpi: 1013fail_platform_device2:
1014
1015 platform_device_del(fujitsu->pf_device);
1016
1017fail_platform_device1:
1018
1019 platform_device_put(fujitsu->pf_device);
1020
1021fail_platform_driver:
1022
1023 acpi_bus_unregister_driver(&acpi_fujitsu_driver);
1024
1025fail_acpi:
327 1026
328 kfree(fujitsu); 1027 kfree(fujitsu);
329 1028
@@ -342,19 +1041,43 @@ static void __exit fujitsu_cleanup(void)
342 1041
343 kfree(fujitsu); 1042 kfree(fujitsu);
344 1043
1044 acpi_bus_unregister_driver(&acpi_fujitsu_hotkey_driver);
1045
1046 kfree(fujitsu_hotkey);
1047
345 printk(KERN_INFO "fujitsu-laptop: driver unloaded.\n"); 1048 printk(KERN_INFO "fujitsu-laptop: driver unloaded.\n");
346} 1049}
347 1050
348module_init(fujitsu_init); 1051module_init(fujitsu_init);
349module_exit(fujitsu_cleanup); 1052module_exit(fujitsu_cleanup);
350 1053
351MODULE_AUTHOR("Jonathan Woithe"); 1054module_param(use_alt_lcd_levels, uint, 0644);
1055MODULE_PARM_DESC(use_alt_lcd_levels,
1056 "Use alternative interface for lcd_levels (needed for Lifebook s6410).");
1057module_param(disable_brightness_keys, uint, 0644);
1058MODULE_PARM_DESC(disable_brightness_keys,
1059 "Disable brightness keys (eg. if they are already handled by the generic ACPI_VIDEO device).");
1060module_param(disable_brightness_adjust, uint, 0644);
1061MODULE_PARM_DESC(disable_brightness_adjust, "Disable brightness adjustment .");
1062#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
1063module_param_named(debug, dbg_level, uint, 0644);
1064MODULE_PARM_DESC(debug, "Sets debug level bit-mask");
1065#endif
1066
1067MODULE_AUTHOR("Jonathan Woithe, Peter Gruber");
352MODULE_DESCRIPTION("Fujitsu laptop extras support"); 1068MODULE_DESCRIPTION("Fujitsu laptop extras support");
353MODULE_VERSION(FUJITSU_DRIVER_VERSION); 1069MODULE_VERSION(FUJITSU_DRIVER_VERSION);
354MODULE_LICENSE("GPL"); 1070MODULE_LICENSE("GPL");
355 1071
1072MODULE_ALIAS
1073 ("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1D3:*:cvrS6410:*");
1074MODULE_ALIAS
1075 ("dmi:*:svnFUJITSU:*:pvr:rvnFUJITSU:rnFJNB19C:*:cvrS7020:*");
1076
356static struct pnp_device_id pnp_ids[] = { 1077static struct pnp_device_id pnp_ids[] = {
357 { .id = "FUJ02bf" }, 1078 { .id = "FUJ02bf" },
1079 { .id = "FUJ02B1" },
1080 { .id = "FUJ02E3" },
358 { .id = "" } 1081 { .id = "" }
359}; 1082};
360MODULE_DEVICE_TABLE(pnp, pnp_ids); 1083MODULE_DEVICE_TABLE(pnp, pnp_ids);