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 #if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) 70 #include <linux/leds.h> 71 #endif 72 73 #define FUJITSU_DRIVER_VERSION "0.6.0" 74 75 #define FUJITSU_LCD_N_LEVELS 8 76 77 #define ACPI_FUJITSU_CLASS "fujitsu" 78 #define ACPI_FUJITSU_HID "FUJ02B1" 79 #define ACPI_FUJITSU_DRIVER_NAME "Fujitsu laptop FUJ02B1 ACPI brightness driver" 80 #define ACPI_FUJITSU_DEVICE_NAME "Fujitsu FUJ02B1" 81 #define ACPI_FUJITSU_HOTKEY_HID "FUJ02E3" 82 #define ACPI_FUJITSU_HOTKEY_DRIVER_NAME "Fujitsu laptop FUJ02E3 ACPI hotkeys driver" 83 #define ACPI_FUJITSU_HOTKEY_DEVICE_NAME "Fujitsu FUJ02E3" 84 85 #define ACPI_FUJITSU_NOTIFY_CODE1 0x80 86 87 #define ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS 0x86 88 #define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS 0x87 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 #if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) 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 108 /* Hotkey details */ 109 #define KEY1_CODE 0x410 /* codes for the keys in the GIRB register */ 110 #define KEY2_CODE 0x411 111 #define KEY3_CODE 0x412 112 #define KEY4_CODE 0x413 113 114 #define MAX_HOTKEY_RINGBUFFER_SIZE 100 115 #define RINGBUFFERSIZE 40 116 117 /* Debugging */ 118 #define FUJLAPTOP_LOG ACPI_FUJITSU_HID ": " 119 #define FUJLAPTOP_ERR KERN_ERR FUJLAPTOP_LOG 120 #define FUJLAPTOP_NOTICE KERN_NOTICE FUJLAPTOP_LOG 121 #define FUJLAPTOP_INFO KERN_INFO FUJLAPTOP_LOG 122 #define FUJLAPTOP_DEBUG KERN_DEBUG FUJLAPTOP_LOG 123 124 #define FUJLAPTOP_DBG_ALL 0xffff 125 #define FUJLAPTOP_DBG_ERROR 0x0001 126 #define FUJLAPTOP_DBG_WARN 0x0002 127 #define FUJLAPTOP_DBG_INFO 0x0004 128 #define FUJLAPTOP_DBG_TRACE 0x0008 129 130 #define dbg_printk(a_dbg_level, format, arg...) \ 131 do { if (dbg_level & a_dbg_level) \ 132 printk(FUJLAPTOP_DEBUG "%s: " format, __func__ , ## arg); \ 133 } while (0) 134 #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG 135 #define vdbg_printk(a_dbg_level, format, arg...) \ 136 dbg_printk(a_dbg_level, format, ## arg) 137 #else 138 #define vdbg_printk(a_dbg_level, format, arg...) 139 #endif 140 141 /* Device controlling the backlight and associated keys */ 142 struct fujitsu_t { 143 acpi_handle acpi_handle; 144 struct acpi_device *dev; 145 struct input_dev *input; 146 char phys[32]; 147 struct backlight_device *bl_device; 148 struct platform_device *pf_device; 149 int keycode1, keycode2, keycode3, keycode4; 150 151 unsigned int max_brightness; 152 unsigned int brightness_changed; 153 unsigned int brightness_level; 154 }; 155 156 static struct fujitsu_t *fujitsu; 157 static int use_alt_lcd_levels = -1; 158 static int disable_brightness_adjust = -1; 159 160 /* Device used to access other hotkeys on the laptop */ 161 struct fujitsu_hotkey_t { 162 acpi_handle acpi_handle; 163 struct acpi_device *dev; 164 struct input_dev *input; 165 char phys[32]; 166 struct platform_device *pf_device; 167 struct kfifo *fifo; 168 spinlock_t fifo_lock; 169 int rfkill_supported; 170 int rfkill_state; 171 int logolamp_registered; 172 int kblamps_registered; 173 }; 174 175 static struct fujitsu_hotkey_t *fujitsu_hotkey; 176 177 static void acpi_fujitsu_hotkey_notify(struct acpi_device *device, u32 event); 178 179 #if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) 180 static enum led_brightness logolamp_get(struct led_classdev *cdev); 181 static void logolamp_set(struct led_classdev *cdev, 182 enum led_brightness brightness); 183 184 struct led_classdev logolamp_led = { 185 .name = "fujitsu::logolamp", 186 .brightness_get = logolamp_get, 187 .brightness_set = logolamp_set 188 }; 189 190 static enum led_brightness kblamps_get(struct led_classdev *cdev); 191 static void kblamps_set(struct led_classdev *cdev, 192 enum led_brightness brightness); 193 194 struct led_classdev kblamps_led = { 195 .name = "fujitsu::kblamps", 196 .brightness_get = kblamps_get, 197 .brightness_set = kblamps_set 198 }; 199 #endif 200 201 #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG 202 static u32 dbg_level = 0x03; 203 #endif 204 205 static void acpi_fujitsu_notify(struct acpi_device *device, u32 event); 206 207 /* Fujitsu ACPI interface function */ 208 209 static int call_fext_func(int cmd, int arg0, int arg1, int arg2) 210 { 211 acpi_status status = AE_OK; 212 union acpi_object params[4] = { 213 { .type = ACPI_TYPE_INTEGER }, 214 { .type = ACPI_TYPE_INTEGER }, 215 { .type = ACPI_TYPE_INTEGER }, 216 { .type = ACPI_TYPE_INTEGER } 217 }; 218 struct acpi_object_list arg_list = { 4, ¶ms[0] }; 219 struct acpi_buffer output; 220 union acpi_object out_obj; 221 acpi_handle handle = NULL; 222 223 status = acpi_get_handle(fujitsu_hotkey->acpi_handle, "FUNC", &handle); 224 if (ACPI_FAILURE(status)) { 225 vdbg_printk(FUJLAPTOP_DBG_ERROR, 226 "FUNC interface is not present\n"); 227 return -ENODEV; 228 } 229 230 params[0].integer.value = cmd; 231 params[1].integer.value = arg0; 232 params[2].integer.value = arg1; 233 params[3].integer.value = arg2; 234 235 output.length = sizeof(out_obj); 236 output.pointer = &out_obj; 237 238 status = acpi_evaluate_object(handle, NULL, &arg_list, &output); 239 if (ACPI_FAILURE(status)) { 240 vdbg_printk(FUJLAPTOP_DBG_WARN, 241 "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) call failed\n", 242 cmd, arg0, arg1, arg2); 243 return -ENODEV; 244 } 245 246 if (out_obj.type != ACPI_TYPE_INTEGER) { 247 vdbg_printk(FUJLAPTOP_DBG_WARN, 248 "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) did not " 249 "return an integer\n", 250 cmd, arg0, arg1, arg2); 251 return -ENODEV; 252 } 253 254 vdbg_printk(FUJLAPTOP_DBG_TRACE, 255 "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) returned 0x%x\n", 256 cmd, arg0, arg1, arg2, (int)out_obj.integer.value); 257 return out_obj.integer.value; 258 } 259 260 #if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) 261 /* LED class callbacks */ 262 263 static void logolamp_set(struct led_classdev *cdev, 264 enum led_brightness brightness) 265 { 266 if (brightness >= LED_FULL) { 267 call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_ON); 268 call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, FUNC_LED_ON); 269 } else if (brightness >= LED_HALF) { 270 call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_ON); 271 call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, FUNC_LED_OFF); 272 } else { 273 call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_OFF); 274 } 275 } 276 277 static void kblamps_set(struct led_classdev *cdev, 278 enum led_brightness brightness) 279 { 280 if (brightness >= LED_FULL) 281 call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS, FUNC_LED_ON); 282 else 283 call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS, FUNC_LED_OFF); 284 } 285 286 static enum led_brightness logolamp_get(struct led_classdev *cdev) 287 { 288 enum led_brightness brightness = LED_OFF; 289 int poweron, always; 290 291 poweron = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0); 292 if (poweron == FUNC_LED_ON) { 293 brightness = LED_HALF; 294 always = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0); 295 if (always == FUNC_LED_ON) 296 brightness = LED_FULL; 297 } 298 return brightness; 299 } 300 301 static enum led_brightness kblamps_get(struct led_classdev *cdev) 302 { 303 enum led_brightness brightness = LED_OFF; 304 305 if (call_fext_func(FUNC_LEDS, 0x2, KEYBOARD_LAMPS, 0x0) == FUNC_LED_ON) 306 brightness = LED_FULL; 307 308 return brightness; 309 } 310 #endif 311 312 /* Hardware access for LCD brightness control */ 313 314 static int set_lcd_level(int level) 315 { 316 acpi_status status = AE_OK; 317 union acpi_object arg0 = { ACPI_TYPE_INTEGER }; 318 struct acpi_object_list arg_list = { 1, &arg0 }; 319 acpi_handle handle = NULL; 320 321 vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via SBLL [%d]\n", 322 level); 323 324 if (level < 0 || level >= fujitsu->max_brightness) 325 return -EINVAL; 326 327 status = acpi_get_handle(fujitsu->acpi_handle, "SBLL", &handle); 328 if (ACPI_FAILURE(status)) { 329 vdbg_printk(FUJLAPTOP_DBG_ERROR, "SBLL not present\n"); 330 return -ENODEV; 331 } 332 333 arg0.integer.value = level; 334 335 status = acpi_evaluate_object(handle, NULL, &arg_list, NULL); 336 if (ACPI_FAILURE(status)) 337 return -ENODEV; 338 339 return 0; 340 } 341 342 static int set_lcd_level_alt(int level) 343 { 344 acpi_status status = AE_OK; 345 union acpi_object arg0 = { ACPI_TYPE_INTEGER }; 346 struct acpi_object_list arg_list = { 1, &arg0 }; 347 acpi_handle handle = NULL; 348 349 vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via SBL2 [%d]\n", 350 level); 351 352 if (level < 0 || level >= fujitsu->max_brightness) 353 return -EINVAL; 354 355 status = acpi_get_handle(fujitsu->acpi_handle, "SBL2", &handle); 356 if (ACPI_FAILURE(status)) { 357 vdbg_printk(FUJLAPTOP_DBG_ERROR, "SBL2 not present\n"); 358 return -ENODEV; 359 } 360 361 arg0.integer.value = level; 362 363 status = acpi_evaluate_object(handle, NULL, &arg_list, NULL); 364 if (ACPI_FAILURE(status)) 365 return -ENODEV; 366 367 return 0; 368 } 369 370 static int get_lcd_level(void) 371 { 372 unsigned long long state = 0; 373 acpi_status status = AE_OK; 374 375 vdbg_printk(FUJLAPTOP_DBG_TRACE, "get lcd level via GBLL\n"); 376 377 status = 378 acpi_evaluate_integer(fujitsu->acpi_handle, "GBLL", NULL, &state); 379 if (status < 0) 380 return status; 381 382 fujitsu->brightness_level = state & 0x0fffffff; 383 384 if (state & 0x80000000) 385 fujitsu->brightness_changed = 1; 386 else 387 fujitsu->brightness_changed = 0; 388 389 return fujitsu->brightness_level; 390 } 391 392 static int get_max_brightness(void) 393 { 394 unsigned long long state = 0; 395 acpi_status status = AE_OK; 396 397 vdbg_printk(FUJLAPTOP_DBG_TRACE, "get max lcd level via RBLL\n"); 398 399 status = 400 acpi_evaluate_integer(fujitsu->acpi_handle, "RBLL", NULL, &state); 401 if (status < 0) 402 return status; 403 404 fujitsu->max_brightness = state; 405 406 return fujitsu->max_brightness; 407 } 408 409 /* Backlight device stuff */ 410 411 static int bl_get_brightness(struct backlight_device *b) 412 { 413 return get_lcd_level(); 414 } 415 416 static int bl_update_status(struct backlight_device *b) 417 { 418 int ret; 419 if (b->props.power == 4) 420 ret = call_fext_func(FUNC_BACKLIGHT, 0x1, 0x4, 0x3); 421 else 422 ret = call_fext_func(FUNC_BACKLIGHT, 0x1, 0x4, 0x0); 423 if (ret != 0) 424 vdbg_printk(FUJLAPTOP_DBG_ERROR, 425 "Unable to adjust backlight power, error code %i\n", 426 ret); 427 428 if (use_alt_lcd_levels) 429 ret = set_lcd_level_alt(b->props.brightness); 430 else 431 ret = set_lcd_level(b->props.brightness); 432 if (ret != 0) 433 vdbg_printk(FUJLAPTOP_DBG_ERROR, 434 "Unable to adjust LCD brightness, error code %i\n", 435 ret); 436 return ret; 437 } 438 439 static struct backlight_ops fujitsubl_ops = { 440 .get_brightness = bl_get_brightness, 441 .update_status = bl_update_status, 442 }; 443 444 /* Platform LCD brightness device */ 445 446 static ssize_t 447 show_max_brightness(struct device *dev, 448 struct device_attribute *attr, char *buf) 449 { 450 451 int ret; 452 453 ret = get_max_brightness(); 454 if (ret < 0) 455 return ret; 456 457 return sprintf(buf, "%i\n", ret); 458 } 459 460 static ssize_t 461 show_brightness_changed(struct device *dev, 462 struct device_attribute *attr, char *buf) 463 { 464 465 int ret; 466 467 ret = fujitsu->brightness_changed; 468 if (ret < 0) 469 return ret; 470 471 return sprintf(buf, "%i\n", ret); 472 } 473 474 static ssize_t show_lcd_level(struct device *dev, 475 struct device_attribute *attr, char *buf) 476 { 477 478 int ret; 479 480 ret = get_lcd_level(); 481 if (ret < 0) 482 return ret; 483 484 return sprintf(buf, "%i\n", ret); 485 } 486 487 static ssize_t store_lcd_level(struct device *dev, 488 struct device_attribute *attr, const char *buf, 489 size_t count) 490 { 491 492 int level, ret; 493 494 if (sscanf(buf, "%i", &level) != 1 495 || (level < 0 || level >= fujitsu->max_brightness)) 496 return -EINVAL; 497 498 if (use_alt_lcd_levels) 499 ret = set_lcd_level_alt(level); 500 else 501 ret = set_lcd_level(level); 502 if (ret < 0) 503 return ret; 504 505 ret = get_lcd_level(); 506 if (ret < 0) 507 return ret; 508 509 return count; 510 } 511 512 static ssize_t 513 ignore_store(struct device *dev, 514 struct device_attribute *attr, const char *buf, size_t count) 515 { 516 return count; 517 } 518 519 static ssize_t 520 show_lid_state(struct device *dev, 521 struct device_attribute *attr, char *buf) 522 { 523 if (!(fujitsu_hotkey->rfkill_supported & 0x100)) 524 return sprintf(buf, "unknown\n"); 525 if (fujitsu_hotkey->rfkill_state & 0x100) 526 return sprintf(buf, "open\n"); 527 else 528 return sprintf(buf, "closed\n"); 529 } 530 531 static ssize_t 532 show_dock_state(struct device *dev, 533 struct device_attribute *attr, char *buf) 534 { 535 if (!(fujitsu_hotkey->rfkill_supported & 0x200)) 536 return sprintf(buf, "unknown\n"); 537 if (fujitsu_hotkey->rfkill_state & 0x200) 538 return sprintf(buf, "docked\n"); 539 else 540 return sprintf(buf, "undocked\n"); 541 } 542 543 static ssize_t 544 show_radios_state(struct device *dev, 545 struct device_attribute *attr, char *buf) 546 { 547 if (!(fujitsu_hotkey->rfkill_supported & 0x20)) 548 return sprintf(buf, "unknown\n"); 549 if (fujitsu_hotkey->rfkill_state & 0x20) 550 return sprintf(buf, "on\n"); 551 else 552 return sprintf(buf, "killed\n"); 553 } 554 555 static DEVICE_ATTR(max_brightness, 0444, show_max_brightness, ignore_store); 556 static DEVICE_ATTR(brightness_changed, 0444, show_brightness_changed, 557 ignore_store); 558 static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level); 559 static DEVICE_ATTR(lid, 0444, show_lid_state, ignore_store); 560 static DEVICE_ATTR(dock, 0444, show_dock_state, ignore_store); 561 static DEVICE_ATTR(radios, 0444, show_radios_state, ignore_store); 562 563 static struct attribute *fujitsupf_attributes[] = { 564 &dev_attr_brightness_changed.attr, 565 &dev_attr_max_brightness.attr, 566 &dev_attr_lcd_level.attr, 567 &dev_attr_lid.attr, 568 &dev_attr_dock.attr, 569 &dev_attr_radios.attr, 570 NULL 571 }; 572 573 static struct attribute_group fujitsupf_attribute_group = { 574 .attrs = fujitsupf_attributes 575 }; 576 577 static struct platform_driver fujitsupf_driver = { 578 .driver = { 579 .name = "fujitsu-laptop", 580 .owner = THIS_MODULE, 581 } 582 }; 583 584 static void dmi_check_cb_common(const struct dmi_system_id *id) 585 { 586 acpi_handle handle; 587 printk(KERN_INFO "fujitsu-laptop: Identified laptop model '%s'.\n", 588 id->ident); 589 if (use_alt_lcd_levels == -1) { 590 if (ACPI_SUCCESS(acpi_get_handle(NULL, 591 "\\_SB.PCI0.LPCB.FJEX.SBL2", &handle))) 592 use_alt_lcd_levels = 1; 593 else 594 use_alt_lcd_levels = 0; 595 vdbg_printk(FUJLAPTOP_DBG_TRACE, "auto-detected usealt as " 596 "%i\n", use_alt_lcd_levels); 597 } 598 } 599 600 static int dmi_check_cb_s6410(const struct dmi_system_id *id) 601 { 602 dmi_check_cb_common(id); 603 fujitsu->keycode1 = KEY_SCREENLOCK; /* "Lock" */ 604 fujitsu->keycode2 = KEY_HELP; /* "Mobility Center" */ 605 return 0; 606 } 607 608 static int dmi_check_cb_s6420(const struct dmi_system_id *id) 609 { 610 dmi_check_cb_common(id); 611 fujitsu->keycode1 = KEY_SCREENLOCK; /* "Lock" */ 612 fujitsu->keycode2 = KEY_HELP; /* "Mobility Center" */ 613 return 0; 614 } 615 616 static int dmi_check_cb_p8010(const struct dmi_system_id *id) 617 { 618 dmi_check_cb_common(id); 619 fujitsu->keycode1 = KEY_HELP; /* "Support" */ 620 fujitsu->keycode3 = KEY_SWITCHVIDEOMODE; /* "Presentation" */ 621 fujitsu->keycode4 = KEY_WWW; /* "Internet" */ 622 return 0; 623 } 624 625 static struct dmi_system_id fujitsu_dmi_table[] = { 626 { 627 .ident = "Fujitsu Siemens S6410", 628 .matches = { 629 DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), 630 DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6410"), 631 }, 632 .callback = dmi_check_cb_s6410}, 633 { 634 .ident = "Fujitsu Siemens S6420", 635 .matches = { 636 DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), 637 DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6420"), 638 }, 639 .callback = dmi_check_cb_s6420}, 640 { 641 .ident = "Fujitsu LifeBook P8010", 642 .matches = { 643 DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), 644 DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook P8010"), 645 }, 646 .callback = dmi_check_cb_p8010}, 647 {} 648 }; 649 650 /* ACPI device for LCD brightness control */ 651 652 static int acpi_fujitsu_add(struct acpi_device *device) 653 { 654 acpi_handle handle; 655 int result = 0; 656 int state = 0; 657 struct input_dev *input; 658 int error; 659 660 if (!device) 661 return -EINVAL; 662 663 fujitsu->acpi_handle = device->handle; 664 sprintf(acpi_device_name(device), "%s", ACPI_FUJITSU_DEVICE_NAME); 665 sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS); 666 device->driver_data = fujitsu; 667 668 fujitsu->input = input = input_allocate_device(); 669 if (!input) { 670 error = -ENOMEM; 671 goto err_stop; 672 } 673 674 snprintf(fujitsu->phys, sizeof(fujitsu->phys), 675 "%s/video/input0", acpi_device_hid(device)); 676 677 input->name = acpi_device_name(device); 678 input->phys = fujitsu->phys; 679 input->id.bustype = BUS_HOST; 680 input->id.product = 0x06; 681 input->dev.parent = &device->dev; 682 input->evbit[0] = BIT(EV_KEY); 683 set_bit(KEY_BRIGHTNESSUP, input->keybit); 684 set_bit(KEY_BRIGHTNESSDOWN, input->keybit); 685 set_bit(KEY_UNKNOWN, input->keybit); 686 687 error = input_register_device(input); 688 if (error) 689 goto err_free_input_dev; 690 691 result = acpi_bus_get_power(fujitsu->acpi_handle, &state); 692 if (result) { 693 printk(KERN_ERR "Error reading power state\n"); 694 goto err_unregister_input_dev; 695 } 696 697 printk(KERN_INFO "ACPI: %s [%s] (%s)\n", 698 acpi_device_name(device), acpi_device_bid(device), 699 !device->power.state ? "on" : "off"); 700 701 fujitsu->dev = device; 702 703 if (ACPI_SUCCESS 704 (acpi_get_handle(device->handle, METHOD_NAME__INI, &handle))) { 705 vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n"); 706 if (ACPI_FAILURE 707 (acpi_evaluate_object 708 (device->handle, METHOD_NAME__INI, NULL, NULL))) 709 printk(KERN_ERR "_INI Method failed\n"); 710 } 711 712 /* do config (detect defaults) */ 713 use_alt_lcd_levels = use_alt_lcd_levels == 1 ? 1 : 0; 714 disable_brightness_adjust = disable_brightness_adjust == 1 ? 1 : 0; 715 vdbg_printk(FUJLAPTOP_DBG_INFO, 716 "config: [alt interface: %d], [adjust disable: %d]\n", 717 use_alt_lcd_levels, disable_brightness_adjust); 718 719 if (get_max_brightness() <= 0) 720 fujitsu->max_brightness = FUJITSU_LCD_N_LEVELS; 721 get_lcd_level(); 722 723 return result; 724 725 err_unregister_input_dev: 726 input_unregister_device(input); 727 err_free_input_dev: 728 input_free_device(input); 729 err_stop: 730 return result; 731 } 732 733 static int acpi_fujitsu_remove(struct acpi_device *device, int type) 734 { 735 struct fujitsu_t *fujitsu = acpi_driver_data(device); 736 struct input_dev *input = fujitsu->input; 737 738 input_unregister_device(input); 739 740 input_free_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 fujitsu_hotkey->fifo = 828 kfifo_alloc(RINGBUFFERSIZE * sizeof(int), GFP_KERNEL, 829 &fujitsu_hotkey->fifo_lock); 830 if (IS_ERR(fujitsu_hotkey->fifo)) { 831 printk(KERN_ERR "kfifo_alloc failed\n"); 832 error = PTR_ERR(fujitsu_hotkey->fifo); 833 goto err_stop; 834 } 835 836 fujitsu_hotkey->input = input = input_allocate_device(); 837 if (!input) { 838 error = -ENOMEM; 839 goto err_free_fifo; 840 } 841 842 snprintf(fujitsu_hotkey->phys, sizeof(fujitsu_hotkey->phys), 843 "%s/video/input0", acpi_device_hid(device)); 844 845 input->name = acpi_device_name(device); 846 input->phys = fujitsu_hotkey->phys; 847 input->id.bustype = BUS_HOST; 848 input->id.product = 0x06; 849 input->dev.parent = &device->dev; 850 851 set_bit(EV_KEY, input->evbit); 852 set_bit(fujitsu->keycode1, input->keybit); 853 set_bit(fujitsu->keycode2, input->keybit); 854 set_bit(fujitsu->keycode3, input->keybit); 855 set_bit(fujitsu->keycode4, input->keybit); 856 set_bit(KEY_UNKNOWN, input->keybit); 857 858 error = input_register_device(input); 859 if (error) 860 goto err_free_input_dev; 861 862 result = acpi_bus_get_power(fujitsu_hotkey->acpi_handle, &state); 863 if (result) { 864 printk(KERN_ERR "Error reading power state\n"); 865 goto err_unregister_input_dev; 866 } 867 868 printk(KERN_INFO "ACPI: %s [%s] (%s)\n", 869 acpi_device_name(device), acpi_device_bid(device), 870 !device->power.state ? "on" : "off"); 871 872 fujitsu_hotkey->dev = device; 873 874 if (ACPI_SUCCESS 875 (acpi_get_handle(device->handle, METHOD_NAME__INI, &handle))) { 876 vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n"); 877 if (ACPI_FAILURE 878 (acpi_evaluate_object 879 (device->handle, METHOD_NAME__INI, NULL, NULL))) 880 printk(KERN_ERR "_INI Method failed\n"); 881 } 882 883 i = 0; 884 while (call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0) != 0 885 && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) 886 ; /* No action, result is discarded */ 887 vdbg_printk(FUJLAPTOP_DBG_INFO, "Discarded %i ringbuffer entries\n", i); 888 889 fujitsu_hotkey->rfkill_supported = 890 call_fext_func(FUNC_RFKILL, 0x0, 0x0, 0x0); 891 892 /* Make sure our bitmask of supported functions is cleared if the 893 RFKILL function block is not implemented, like on the S7020. */ 894 if (fujitsu_hotkey->rfkill_supported == UNSUPPORTED_CMD) 895 fujitsu_hotkey->rfkill_supported = 0; 896 897 if (fujitsu_hotkey->rfkill_supported) 898 fujitsu_hotkey->rfkill_state = 899 call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0); 900 901 /* Suspect this is a keymap of the application panel, print it */ 902 printk(KERN_INFO "fujitsu-laptop: BTNI: [0x%x]\n", 903 call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0)); 904 905 #if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) 906 if (call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) { 907 result = led_classdev_register(&fujitsu->pf_device->dev, 908 &logolamp_led); 909 if (result == 0) { 910 fujitsu_hotkey->logolamp_registered = 1; 911 } else { 912 printk(KERN_ERR "fujitsu-laptop: Could not register " 913 "LED handler for logo lamp, error %i\n", result); 914 } 915 } 916 917 if ((call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & KEYBOARD_LAMPS) && 918 (call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0) == 0x0)) { 919 result = led_classdev_register(&fujitsu->pf_device->dev, 920 &kblamps_led); 921 if (result == 0) { 922 fujitsu_hotkey->kblamps_registered = 1; 923 } else { 924 printk(KERN_ERR "fujitsu-laptop: Could not register " 925 "LED handler for keyboard lamps, error %i\n", result); 926 } 927 } 928 #endif 929 930 return result; 931 932 err_unregister_input_dev: 933 input_unregister_device(input); 934 err_free_input_dev: 935 input_free_device(input); 936 err_free_fifo: 937 kfifo_free(fujitsu_hotkey->fifo); 938 err_stop: 939 return result; 940 } 941 942 static int acpi_fujitsu_hotkey_remove(struct acpi_device *device, int type) 943 { 944 struct fujitsu_hotkey_t *fujitsu_hotkey = acpi_driver_data(device); 945 struct input_dev *input = fujitsu_hotkey->input; 946 947 #ifdef CONFIG_LEDS_CLASS 948 if (fujitsu_hotkey->logolamp_registered) 949 led_classdev_unregister(&logolamp_led); 950 951 if (fujitsu_hotkey->kblamps_registered) 952 led_classdev_unregister(&kblamps_led); 953 #endif 954 955 input_unregister_device(input); 956 957 input_free_device(input); 958 959 kfifo_free(fujitsu_hotkey->fifo); 960 961 fujitsu_hotkey->acpi_handle = NULL; 962 963 return 0; 964 } 965 966 static void acpi_fujitsu_hotkey_notify(struct acpi_device *device, u32 event) 967 { 968 struct input_dev *input; 969 int keycode, keycode_r; 970 unsigned int irb = 1; 971 int i, status; 972 973 input = fujitsu_hotkey->input; 974 975 if (fujitsu_hotkey->rfkill_supported) 976 fujitsu_hotkey->rfkill_state = 977 call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0); 978 979 switch (event) { 980 case ACPI_FUJITSU_NOTIFY_CODE1: 981 i = 0; 982 while ((irb = 983 call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0 984 && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) { 985 switch (irb & 0x4ff) { 986 case KEY1_CODE: 987 keycode = fujitsu->keycode1; 988 break; 989 case KEY2_CODE: 990 keycode = fujitsu->keycode2; 991 break; 992 case KEY3_CODE: 993 keycode = fujitsu->keycode3; 994 break; 995 case KEY4_CODE: 996 keycode = fujitsu->keycode4; 997 break; 998 case 0: 999 keycode = 0; 1000 break; 1001 default: 1002 vdbg_printk(FUJLAPTOP_DBG_WARN, 1003 "Unknown GIRB result [%x]\n", irb); 1004 keycode = -1; 1005 break; 1006 } 1007 if (keycode > 0) { 1008 vdbg_printk(FUJLAPTOP_DBG_TRACE, 1009 "Push keycode into ringbuffer [%d]\n", 1010 keycode); 1011 status = kfifo_put(fujitsu_hotkey->fifo, 1012 (unsigned char *)&keycode, 1013 sizeof(keycode)); 1014 if (status != sizeof(keycode)) { 1015 vdbg_printk(FUJLAPTOP_DBG_WARN, 1016 "Could not push keycode [0x%x]\n", 1017 keycode); 1018 } else { 1019 input_report_key(input, keycode, 1); 1020 input_sync(input); 1021 } 1022 } else if (keycode == 0) { 1023 while ((status = 1024 kfifo_get 1025 (fujitsu_hotkey->fifo, (unsigned char *) 1026 &keycode_r, 1027 sizeof 1028 (keycode_r))) == sizeof(keycode_r)) { 1029 input_report_key(input, keycode_r, 0); 1030 input_sync(input); 1031 vdbg_printk(FUJLAPTOP_DBG_TRACE, 1032 "Pop keycode from ringbuffer [%d]\n", 1033 keycode_r); 1034 } 1035 } 1036 } 1037 1038 break; 1039 default: 1040 keycode = KEY_UNKNOWN; 1041 vdbg_printk(FUJLAPTOP_DBG_WARN, 1042 "Unsupported event [0x%x]\n", event); 1043 input_report_key(input, keycode, 1); 1044 input_sync(input); 1045 input_report_key(input, keycode, 0); 1046 input_sync(input); 1047 break; 1048 } 1049 } 1050 1051 /* Initialization */ 1052 1053 static const struct acpi_device_id fujitsu_device_ids[] = { 1054 {ACPI_FUJITSU_HID, 0}, 1055 {"", 0}, 1056 }; 1057 1058 static struct acpi_driver acpi_fujitsu_driver = { 1059 .name = ACPI_FUJITSU_DRIVER_NAME, 1060 .class = ACPI_FUJITSU_CLASS, 1061 .ids = fujitsu_device_ids, 1062 .ops = { 1063 .add = acpi_fujitsu_add, 1064 .remove = acpi_fujitsu_remove, 1065 .notify = acpi_fujitsu_notify, 1066 }, 1067 }; 1068 1069 static const struct acpi_device_id fujitsu_hotkey_device_ids[] = { 1070 {ACPI_FUJITSU_HOTKEY_HID, 0}, 1071 {"", 0}, 1072 }; 1073 1074 static struct acpi_driver acpi_fujitsu_hotkey_driver = { 1075 .name = ACPI_FUJITSU_HOTKEY_DRIVER_NAME, 1076 .class = ACPI_FUJITSU_CLASS, 1077 .ids = fujitsu_hotkey_device_ids, 1078 .ops = { 1079 .add = acpi_fujitsu_hotkey_add, 1080 .remove = acpi_fujitsu_hotkey_remove, 1081 .notify = acpi_fujitsu_hotkey_notify, 1082 }, 1083 }; 1084 1085 static int __init fujitsu_init(void) 1086 { 1087 int ret, result, max_brightness; 1088 1089 if (acpi_disabled) 1090 return -ENODEV; 1091 1092 fujitsu = kmalloc(sizeof(struct fujitsu_t), GFP_KERNEL); 1093 if (!fujitsu) 1094 return -ENOMEM; 1095 memset(fujitsu, 0, sizeof(struct fujitsu_t)); 1096 fujitsu->keycode1 = KEY_PROG1; 1097 fujitsu->keycode2 = KEY_PROG2; 1098 fujitsu->keycode3 = KEY_PROG3; 1099 fujitsu->keycode4 = KEY_PROG4; 1100 dmi_check_system(fujitsu_dmi_table); 1101 1102 result = acpi_bus_register_driver(&acpi_fujitsu_driver); 1103 if (result < 0) { 1104 ret = -ENODEV; 1105 goto fail_acpi; 1106 } 1107 1108 /* Register platform stuff */ 1109 1110 fujitsu->pf_device = platform_device_alloc("fujitsu-laptop", -1); 1111 if (!fujitsu->pf_device) { 1112 ret = -ENOMEM; 1113 goto fail_platform_driver; 1114 } 1115 1116 ret = platform_device_add(fujitsu->pf_device); 1117 if (ret) 1118 goto fail_platform_device1; 1119 1120 ret = 1121 sysfs_create_group(&fujitsu->pf_device->dev.kobj, 1122 &fujitsupf_attribute_group); 1123 if (ret) 1124 goto fail_platform_device2; 1125 1126 /* Register backlight stuff */ 1127 1128 if (!acpi_video_backlight_support()) { 1129 fujitsu->bl_device = 1130 backlight_device_register("fujitsu-laptop", NULL, NULL, 1131 &fujitsubl_ops); 1132 if (IS_ERR(fujitsu->bl_device)) { 1133 ret = PTR_ERR(fujitsu->bl_device); 1134 fujitsu->bl_device = NULL; 1135 goto fail_sysfs_group; 1136 } 1137 max_brightness = fujitsu->max_brightness; 1138 fujitsu->bl_device->props.max_brightness = max_brightness - 1; 1139 fujitsu->bl_device->props.brightness = fujitsu->brightness_level; 1140 } 1141 1142 ret = platform_driver_register(&fujitsupf_driver); 1143 if (ret) 1144 goto fail_backlight; 1145 1146 /* Register hotkey driver */ 1147 1148 fujitsu_hotkey = kmalloc(sizeof(struct fujitsu_hotkey_t), GFP_KERNEL); 1149 if (!fujitsu_hotkey) { 1150 ret = -ENOMEM; 1151 goto fail_hotkey; 1152 } 1153 memset(fujitsu_hotkey, 0, sizeof(struct fujitsu_hotkey_t)); 1154 1155 result = acpi_bus_register_driver(&acpi_fujitsu_hotkey_driver); 1156 if (result < 0) { 1157 ret = -ENODEV; 1158 goto fail_hotkey1; 1159 } 1160 1161 /* Sync backlight power status (needs FUJ02E3 device, hence deferred) */ 1162 1163 if (!acpi_video_backlight_support()) { 1164 if (call_fext_func(FUNC_BACKLIGHT, 0x2, 0x4, 0x0) == 3) 1165 fujitsu->bl_device->props.power = 4; 1166 else 1167 fujitsu->bl_device->props.power = 0; 1168 } 1169 1170 printk(KERN_INFO "fujitsu-laptop: driver " FUJITSU_DRIVER_VERSION 1171 " successfully loaded.\n"); 1172 1173 return 0; 1174 1175 fail_hotkey1: 1176 kfree(fujitsu_hotkey); 1177 fail_hotkey: 1178 platform_driver_unregister(&fujitsupf_driver); 1179 fail_backlight: 1180 if (fujitsu->bl_device) 1181 backlight_device_unregister(fujitsu->bl_device); 1182 fail_sysfs_group: 1183 sysfs_remove_group(&fujitsu->pf_device->dev.kobj, 1184 &fujitsupf_attribute_group); 1185 fail_platform_device2: 1186 platform_device_del(fujitsu->pf_device); 1187 fail_platform_device1: 1188 platform_device_put(fujitsu->pf_device); 1189 fail_platform_driver: 1190 acpi_bus_unregister_driver(&acpi_fujitsu_driver); 1191 fail_acpi: 1192 kfree(fujitsu); 1193 1194 return ret; 1195 } 1196 1197 static void __exit fujitsu_cleanup(void) 1198 { 1199 acpi_bus_unregister_driver(&acpi_fujitsu_hotkey_driver); 1200 1201 kfree(fujitsu_hotkey); 1202 1203 platform_driver_unregister(&fujitsupf_driver); 1204 1205 if (fujitsu->bl_device) 1206 backlight_device_unregister(fujitsu->bl_device); 1207 1208 sysfs_remove_group(&fujitsu->pf_device->dev.kobj, 1209 &fujitsupf_attribute_group); 1210 1211 platform_device_unregister(fujitsu->pf_device); 1212 1213 acpi_bus_unregister_driver(&acpi_fujitsu_driver); 1214 1215 kfree(fujitsu); 1216 1217 printk(KERN_INFO "fujitsu-laptop: driver unloaded.\n"); 1218 } 1219 1220 module_init(fujitsu_init); 1221 module_exit(fujitsu_cleanup); 1222 1223 module_param(use_alt_lcd_levels, uint, 0644); 1224 MODULE_PARM_DESC(use_alt_lcd_levels, 1225 "Use alternative interface for lcd_levels (needed for Lifebook s6410)."); 1226 module_param(disable_brightness_adjust, uint, 0644); 1227 MODULE_PARM_DESC(disable_brightness_adjust, "Disable brightness adjustment ."); 1228 #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG 1229 module_param_named(debug, dbg_level, uint, 0644); 1230 MODULE_PARM_DESC(debug, "Sets debug level bit-mask"); 1231 #endif 1232 1233 MODULE_AUTHOR("Jonathan Woithe, Peter Gruber, Tony Vroon"); 1234 MODULE_DESCRIPTION("Fujitsu laptop extras support"); 1235 MODULE_VERSION(FUJITSU_DRIVER_VERSION); 1236 MODULE_LICENSE("GPL"); 1237 1238 MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1D3:*:cvrS6410:*"); 1239 MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1E6:*:cvrS6420:*"); 1240 MODULE_ALIAS("dmi:*:svnFUJITSU:*:pvr:rvnFUJITSU:rnFJNB19C:*:cvrS7020:*"); 1241 1242 static struct pnp_device_id pnp_ids[] = { 1243 {.id = "FUJ02bf"}, 1244 {.id = "FUJ02B1"}, 1245 {.id = "FUJ02E3"}, 1246 {.id = ""} 1247 }; 1248 1249 MODULE_DEVICE_TABLE(pnp, pnp_ids); 1250