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