aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/misc/fujitsu-laptop.c305
1 files changed, 272 insertions, 33 deletions
diff --git a/drivers/misc/fujitsu-laptop.c b/drivers/misc/fujitsu-laptop.c
index a7dd3e9fb79d..9c407ab9ba2b 100644
--- a/drivers/misc/fujitsu-laptop.c
+++ b/drivers/misc/fujitsu-laptop.c
@@ -3,6 +3,7 @@
3/* 3/*
4 Copyright (C) 2007,2008 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 Copyright (C) 2008 Peter Gruber <nokos@gmx.net>
6 Copyright (C) 2008 Tony Vroon <tony@linx.net>
6 Based on earlier work: 7 Based on earlier work:
7 Copyright (C) 2003 Shane Spencer <shane@bogomip.com> 8 Copyright (C) 2003 Shane Spencer <shane@bogomip.com>
8 Adrian Yee <brewt-fujitsu@brewt.org> 9 Adrian Yee <brewt-fujitsu@brewt.org>
@@ -65,8 +66,11 @@
65#include <linux/kfifo.h> 66#include <linux/kfifo.h>
66#include <linux/video_output.h> 67#include <linux/video_output.h>
67#include <linux/platform_device.h> 68#include <linux/platform_device.h>
69#ifdef CONFIG_LEDS_CLASS
70#include <linux/leds.h>
71#endif
68 72
69#define FUJITSU_DRIVER_VERSION "0.4.3" 73#define FUJITSU_DRIVER_VERSION "0.5.0"
70 74
71#define FUJITSU_LCD_N_LEVELS 8 75#define FUJITSU_LCD_N_LEVELS 8
72 76
@@ -83,6 +87,24 @@
83#define ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS 0x86 87#define ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS 0x86
84#define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS 0x87 88#define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS 0x87
85 89
90/* FUNC interface - command values */
91#define FUNC_RFKILL 0x1000
92#define FUNC_LEDS 0x1001
93#define FUNC_BUTTONS 0x1002
94#define FUNC_BACKLIGHT 0x1004
95
96/* FUNC interface - responses */
97#define UNSUPPORTED_CMD 0x80000000
98
99#ifdef CONFIG_LEDS_CLASS
100/* FUNC interface - LED control */
101#define FUNC_LED_OFF 0x1
102#define FUNC_LED_ON 0x30001
103#define KEYBOARD_LAMPS 0x100
104#define LOGOLAMP_POWERON 0x2000
105#define LOGOLAMP_ALWAYS 0x4000
106#endif
107
86/* Hotkey details */ 108/* Hotkey details */
87#define KEY1_CODE 0x410 /* codes for the keys in the GIRB register */ 109#define KEY1_CODE 0x410 /* codes for the keys in the GIRB register */
88#define KEY2_CODE 0x411 110#define KEY2_CODE 0x411
@@ -145,8 +167,9 @@ struct fujitsu_hotkey_t {
145 struct platform_device *pf_device; 167 struct platform_device *pf_device;
146 struct kfifo *fifo; 168 struct kfifo *fifo;
147 spinlock_t fifo_lock; 169 spinlock_t fifo_lock;
148 170 int rfkill_state;
149 unsigned int irb; /* info about the pressed buttons */ 171 int logolamp_registered;
172 int kblamps_registered;
150}; 173};
151 174
152static struct fujitsu_hotkey_t *fujitsu_hotkey; 175static struct fujitsu_hotkey_t *fujitsu_hotkey;
@@ -154,12 +177,139 @@ static struct fujitsu_hotkey_t *fujitsu_hotkey;
154static void acpi_fujitsu_hotkey_notify(acpi_handle handle, u32 event, 177static void acpi_fujitsu_hotkey_notify(acpi_handle handle, u32 event,
155 void *data); 178 void *data);
156 179
180#ifdef CONFIG_LEDS_CLASS
181static enum led_brightness logolamp_get(struct led_classdev *cdev);
182static void logolamp_set(struct led_classdev *cdev,
183 enum led_brightness brightness);
184
185struct led_classdev logolamp_led = {
186 .name = "fujitsu::logolamp",
187 .brightness_get = logolamp_get,
188 .brightness_set = logolamp_set
189};
190
191static enum led_brightness kblamps_get(struct led_classdev *cdev);
192static void kblamps_set(struct led_classdev *cdev,
193 enum led_brightness brightness);
194
195struct led_classdev kblamps_led = {
196 .name = "fujitsu::kblamps",
197 .brightness_get = kblamps_get,
198 .brightness_set = kblamps_set
199};
200#endif
201
157#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG 202#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
158static u32 dbg_level = 0x03; 203static u32 dbg_level = 0x03;
159#endif 204#endif
160 205
161static void acpi_fujitsu_notify(acpi_handle handle, u32 event, void *data); 206static void acpi_fujitsu_notify(acpi_handle handle, u32 event, void *data);
162 207
208/* Fujitsu ACPI interface function */
209
210static int call_fext_func(int cmd, int arg0, int arg1, int arg2)
211{
212 acpi_status status = AE_OK;
213 union acpi_object params[4] = {
214 { .type = ACPI_TYPE_INTEGER },
215 { .type = ACPI_TYPE_INTEGER },
216 { .type = ACPI_TYPE_INTEGER },
217 { .type = ACPI_TYPE_INTEGER }
218 };
219 struct acpi_object_list arg_list = { 4, &params[0] };
220 struct acpi_buffer output;
221 union acpi_object out_obj;
222 acpi_handle handle = NULL;
223
224 status = acpi_get_handle(fujitsu_hotkey->acpi_handle, "FUNC", &handle);
225 if (ACPI_FAILURE(status)) {
226 vdbg_printk(FUJLAPTOP_DBG_ERROR,
227 "FUNC interface is not present\n");
228 return -ENODEV;
229 }
230
231 params[0].integer.value = cmd;
232 params[1].integer.value = arg0;
233 params[2].integer.value = arg1;
234 params[3].integer.value = arg2;
235
236 output.length = sizeof(out_obj);
237 output.pointer = &out_obj;
238
239 status = acpi_evaluate_object(handle, NULL, &arg_list, &output);
240 if (ACPI_FAILURE(status)) {
241 vdbg_printk(FUJLAPTOP_DBG_WARN,
242 "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) call failed\n",
243 cmd, arg0, arg1, arg2);
244 return -ENODEV;
245 }
246
247 if (out_obj.type != ACPI_TYPE_INTEGER) {
248 vdbg_printk(FUJLAPTOP_DBG_WARN,
249 "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) did not "
250 "return an integer\n",
251 cmd, arg0, arg1, arg2);
252 return -ENODEV;
253 }
254
255 vdbg_printk(FUJLAPTOP_DBG_TRACE,
256 "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) returned 0x%x\n",
257 cmd, arg0, arg1, arg2, (int)out_obj.integer.value);
258 return out_obj.integer.value;
259}
260
261#ifdef CONFIG_LEDS_CLASS
262/* LED class callbacks */
263
264static void logolamp_set(struct led_classdev *cdev,
265 enum led_brightness brightness)
266{
267 if (brightness >= LED_FULL) {
268 call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_ON);
269 call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, FUNC_LED_ON);
270 } else if (brightness >= LED_HALF) {
271 call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_ON);
272 call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, FUNC_LED_OFF);
273 } else {
274 call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_OFF);
275 }
276}
277
278static void kblamps_set(struct led_classdev *cdev,
279 enum led_brightness brightness)
280{
281 if (brightness >= LED_FULL)
282 call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS, FUNC_LED_ON);
283 else
284 call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS, FUNC_LED_OFF);
285}
286
287static enum led_brightness logolamp_get(struct led_classdev *cdev)
288{
289 enum led_brightness brightness = LED_OFF;
290 int poweron, always;
291
292 poweron = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0);
293 if (poweron == FUNC_LED_ON) {
294 brightness = LED_HALF;
295 always = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0);
296 if (always == FUNC_LED_ON)
297 brightness = LED_FULL;
298 }
299 return brightness;
300}
301
302static enum led_brightness kblamps_get(struct led_classdev *cdev)
303{
304 enum led_brightness brightness = LED_OFF;
305
306 if (call_fext_func(FUNC_LEDS, 0x2, KEYBOARD_LAMPS, 0x0) == FUNC_LED_ON)
307 brightness = LED_FULL;
308
309 return brightness;
310}
311#endif
312
163/* Hardware access for LCD brightness control */ 313/* Hardware access for LCD brightness control */
164 314
165static int set_lcd_level(int level) 315static int set_lcd_level(int level)
@@ -297,10 +447,25 @@ static int bl_get_brightness(struct backlight_device *b)
297 447
298static int bl_update_status(struct backlight_device *b) 448static int bl_update_status(struct backlight_device *b)
299{ 449{
450 int ret;
451 if (b->props.power == 4)
452 ret = call_fext_func(FUNC_BACKLIGHT, 0x1, 0x4, 0x3);
453 else
454 ret = call_fext_func(FUNC_BACKLIGHT, 0x1, 0x4, 0x0);
455 if (ret != 0)
456 vdbg_printk(FUJLAPTOP_DBG_ERROR,
457 "Unable to adjust backlight power, error code %i\n",
458 ret);
459
300 if (use_alt_lcd_levels) 460 if (use_alt_lcd_levels)
301 return set_lcd_level_alt(b->props.brightness); 461 ret = set_lcd_level_alt(b->props.brightness);
302 else 462 else
303 return set_lcd_level(b->props.brightness); 463 ret = set_lcd_level(b->props.brightness);
464 if (ret != 0)
465 vdbg_printk(FUJLAPTOP_DBG_ERROR,
466 "Unable to adjust LCD brightness, error code %i\n",
467 ret);
468 return ret;
304} 469}
305 470
306static struct backlight_ops fujitsubl_ops = { 471static struct backlight_ops fujitsubl_ops = {
@@ -382,42 +547,64 @@ static ssize_t store_lcd_level(struct device *dev,
382 return count; 547 return count;
383} 548}
384 549
385/* Hardware access for hotkey device */ 550static ssize_t
386 551ignore_store(struct device *dev,
387static int get_irb(void) 552 struct device_attribute *attr, const char *buf, size_t count)
388{ 553{
389 unsigned long long state = 0; 554 return count;
390 acpi_status status = AE_OK; 555}
391
392 vdbg_printk(FUJLAPTOP_DBG_TRACE, "Get irb\n");
393
394 status =
395 acpi_evaluate_integer(fujitsu_hotkey->acpi_handle, "GIRB", NULL,
396 &state);
397 if (status < 0)
398 return status;
399 556
400 fujitsu_hotkey->irb = state; 557static ssize_t
558show_lid_state(struct device *dev,
559 struct device_attribute *attr, char *buf)
560{
561 if (fujitsu_hotkey->rfkill_state == UNSUPPORTED_CMD)
562 return sprintf(buf, "unknown\n");
563 if (fujitsu_hotkey->rfkill_state & 0x100)
564 return sprintf(buf, "open\n");
565 else
566 return sprintf(buf, "closed\n");
567}
401 568
402 return fujitsu_hotkey->irb; 569static ssize_t
570show_dock_state(struct device *dev,
571 struct device_attribute *attr, char *buf)
572{
573 if (fujitsu_hotkey->rfkill_state == UNSUPPORTED_CMD)
574 return sprintf(buf, "unknown\n");
575 if (fujitsu_hotkey->rfkill_state & 0x200)
576 return sprintf(buf, "docked\n");
577 else
578 return sprintf(buf, "undocked\n");
403} 579}
404 580
405static ssize_t 581static ssize_t
406ignore_store(struct device *dev, 582show_radios_state(struct device *dev,
407 struct device_attribute *attr, const char *buf, size_t count) 583 struct device_attribute *attr, char *buf)
408{ 584{
409 return count; 585 if (fujitsu_hotkey->rfkill_state == UNSUPPORTED_CMD)
586 return sprintf(buf, "unknown\n");
587 if (fujitsu_hotkey->rfkill_state & 0x20)
588 return sprintf(buf, "on\n");
589 else
590 return sprintf(buf, "killed\n");
410} 591}
411 592
412static DEVICE_ATTR(max_brightness, 0444, show_max_brightness, ignore_store); 593static DEVICE_ATTR(max_brightness, 0444, show_max_brightness, ignore_store);
413static DEVICE_ATTR(brightness_changed, 0444, show_brightness_changed, 594static DEVICE_ATTR(brightness_changed, 0444, show_brightness_changed,
414 ignore_store); 595 ignore_store);
415static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level); 596static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level);
597static DEVICE_ATTR(lid, 0444, show_lid_state, ignore_store);
598static DEVICE_ATTR(dock, 0444, show_dock_state, ignore_store);
599static DEVICE_ATTR(radios, 0444, show_radios_state, ignore_store);
416 600
417static struct attribute *fujitsupf_attributes[] = { 601static struct attribute *fujitsupf_attributes[] = {
418 &dev_attr_brightness_changed.attr, 602 &dev_attr_brightness_changed.attr,
419 &dev_attr_max_brightness.attr, 603 &dev_attr_max_brightness.attr,
420 &dev_attr_lcd_level.attr, 604 &dev_attr_lcd_level.attr,
605 &dev_attr_lid.attr,
606 &dev_attr_dock.attr,
607 &dev_attr_radios.attr,
421 NULL 608 NULL
422}; 609};
423 610
@@ -771,7 +958,8 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device)
771 input->id.bustype = BUS_HOST; 958 input->id.bustype = BUS_HOST;
772 input->id.product = 0x06; 959 input->id.product = 0x06;
773 input->dev.parent = &device->dev; 960 input->dev.parent = &device->dev;
774 input->evbit[0] = BIT(EV_KEY); 961
962 set_bit(EV_KEY, input->evbit);
775 set_bit(fujitsu->keycode1, input->keybit); 963 set_bit(fujitsu->keycode1, input->keybit);
776 set_bit(fujitsu->keycode2, input->keybit); 964 set_bit(fujitsu->keycode2, input->keybit);
777 set_bit(fujitsu->keycode3, input->keybit); 965 set_bit(fujitsu->keycode3, input->keybit);
@@ -803,10 +991,44 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device)
803 printk(KERN_ERR "_INI Method failed\n"); 991 printk(KERN_ERR "_INI Method failed\n");
804 } 992 }
805 993
806 i = 0; /* Discard hotkey ringbuffer */ 994 i = 0;
807 while (get_irb() != 0 && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) ; 995 while (call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0) != 0
996 && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE)
997 ; /* No action, result is discarded */
808 vdbg_printk(FUJLAPTOP_DBG_INFO, "Discarded %i ringbuffer entries\n", i); 998 vdbg_printk(FUJLAPTOP_DBG_INFO, "Discarded %i ringbuffer entries\n", i);
809 999
1000 fujitsu_hotkey->rfkill_state =
1001 call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0);
1002
1003 /* Suspect this is a keymap of the application panel, print it */
1004 printk(KERN_INFO "fujitsu-laptop: BTNI: [0x%x]\n",
1005 call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0));
1006
1007 #ifdef CONFIG_LEDS_CLASS
1008 if (call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) {
1009 result = led_classdev_register(&fujitsu->pf_device->dev,
1010 &logolamp_led);
1011 if (result == 0) {
1012 fujitsu_hotkey->logolamp_registered = 1;
1013 } else {
1014 printk(KERN_ERR "fujitsu-laptop: Could not register "
1015 "LED handler for logo lamp, error %i\n", result);
1016 }
1017 }
1018
1019 if ((call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & KEYBOARD_LAMPS) &&
1020 (call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0) == 0x0)) {
1021 result = led_classdev_register(&fujitsu->pf_device->dev,
1022 &kblamps_led);
1023 if (result == 0) {
1024 fujitsu_hotkey->kblamps_registered = 1;
1025 } else {
1026 printk(KERN_ERR "fujitsu-laptop: Could not register "
1027 "LED handler for keyboard lamps, error %i\n", result);
1028 }
1029 }
1030 #endif
1031
810 return result; 1032 return result;
811 1033
812end: 1034end:
@@ -852,16 +1074,15 @@ static void acpi_fujitsu_hotkey_notify(acpi_handle handle, u32 event,
852 1074
853 input = fujitsu_hotkey->input; 1075 input = fujitsu_hotkey->input;
854 1076
855 vdbg_printk(FUJLAPTOP_DBG_TRACE, "Hotkey event\n"); 1077 fujitsu_hotkey->rfkill_state =
1078 call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0);
856 1079
857 switch (event) { 1080 switch (event) {
858 case ACPI_FUJITSU_NOTIFY_CODE1: 1081 case ACPI_FUJITSU_NOTIFY_CODE1:
859 i = 0; 1082 i = 0;
860 while ((irb = get_irb()) != 0 1083 while ((irb =
861 && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) { 1084 call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0
862 vdbg_printk(FUJLAPTOP_DBG_TRACE, "GIRB result [%x]\n", 1085 && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) {
863 irb);
864
865 switch (irb & 0x4ff) { 1086 switch (irb & 0x4ff) {
866 case KEY1_CODE: 1087 case KEY1_CODE:
867 keycode = fujitsu->keycode1; 1088 keycode = fujitsu->keycode1;
@@ -1035,6 +1256,15 @@ static int __init fujitsu_init(void)
1035 goto fail_hotkey1; 1256 goto fail_hotkey1;
1036 } 1257 }
1037 1258
1259 /* Sync backlight power status (needs FUJ02E3 device, hence deferred) */
1260
1261 if (!acpi_video_backlight_support()) {
1262 if (call_fext_func(FUNC_BACKLIGHT, 0x2, 0x4, 0x0) == 3)
1263 fujitsu->bl_device->props.power = 4;
1264 else
1265 fujitsu->bl_device->props.power = 0;
1266 }
1267
1038 printk(KERN_INFO "fujitsu-laptop: driver " FUJITSU_DRIVER_VERSION 1268 printk(KERN_INFO "fujitsu-laptop: driver " FUJITSU_DRIVER_VERSION
1039 " successfully loaded.\n"); 1269 " successfully loaded.\n");
1040 1270
@@ -1074,6 +1304,14 @@ fail_acpi:
1074 1304
1075static void __exit fujitsu_cleanup(void) 1305static void __exit fujitsu_cleanup(void)
1076{ 1306{
1307 #ifdef CONFIG_LEDS_CLASS
1308 if (fujitsu_hotkey->logolamp_registered != 0)
1309 led_classdev_unregister(&logolamp_led);
1310
1311 if (fujitsu_hotkey->kblamps_registered != 0)
1312 led_classdev_unregister(&kblamps_led);
1313 #endif
1314
1077 sysfs_remove_group(&fujitsu->pf_device->dev.kobj, 1315 sysfs_remove_group(&fujitsu->pf_device->dev.kobj,
1078 &fujitsupf_attribute_group); 1316 &fujitsupf_attribute_group);
1079 platform_device_unregister(fujitsu->pf_device); 1317 platform_device_unregister(fujitsu->pf_device);
@@ -1108,12 +1346,13 @@ module_param_named(debug, dbg_level, uint, 0644);
1108MODULE_PARM_DESC(debug, "Sets debug level bit-mask"); 1346MODULE_PARM_DESC(debug, "Sets debug level bit-mask");
1109#endif 1347#endif
1110 1348
1111MODULE_AUTHOR("Jonathan Woithe, Peter Gruber"); 1349MODULE_AUTHOR("Jonathan Woithe, Peter Gruber, Tony Vroon");
1112MODULE_DESCRIPTION("Fujitsu laptop extras support"); 1350MODULE_DESCRIPTION("Fujitsu laptop extras support");
1113MODULE_VERSION(FUJITSU_DRIVER_VERSION); 1351MODULE_VERSION(FUJITSU_DRIVER_VERSION);
1114MODULE_LICENSE("GPL"); 1352MODULE_LICENSE("GPL");
1115 1353
1116MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1D3:*:cvrS6410:*"); 1354MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1D3:*:cvrS6410:*");
1355MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1E6:*:cvrS6420:*");
1117MODULE_ALIAS("dmi:*:svnFUJITSU:*:pvr:rvnFUJITSU:rnFJNB19C:*:cvrS7020:*"); 1356MODULE_ALIAS("dmi:*:svnFUJITSU:*:pvr:rvnFUJITSU:rnFJNB19C:*:cvrS7020:*");
1118 1357
1119static struct pnp_device_id pnp_ids[] = { 1358static struct pnp_device_id pnp_ids[] = {