1 /*-*-linux-c-*-*/ 2 3 /* 4 Copyright (C) 2007,2008 Jonathan Woithe <jwoithe@just42.net> 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 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 60 61 #include <linux/module.h> 62 #include <linux/kernel.h> 63 #include <linux/init.h> 64 #include <linux/acpi.h> 65 #include <linux/dmi.h> 66 #include <linux/backlight.h> 67 #include <linux/fb.h> 68 #include <linux/input.h> 69 #include <linux/kfifo.h> 70 #include <linux/platform_device.h> 71 #include <linux/slab.h> 72 #if IS_ENABLED(CONFIG_LEDS_CLASS) 73 #include <linux/leds.h> 74 #endif 75 #include <acpi/video.h> 76 77 #define FUJITSU_DRIVER_VERSION "0.6.0" 78 79 #define FUJITSU_LCD_N_LEVELS 8 80 81 #define ACPI_FUJITSU_CLASS "fujitsu" 82 #define ACPI_FUJITSU_BL_HID "FUJ02B1" 83 #define ACPI_FUJITSU_BL_DRIVER_NAME "Fujitsu laptop FUJ02B1 ACPI brightness driver" 84 #define ACPI_FUJITSU_BL_DEVICE_NAME "Fujitsu FUJ02B1" 85 #define ACPI_FUJITSU_LAPTOP_HID "FUJ02E3" 86 #define ACPI_FUJITSU_LAPTOP_DRIVER_NAME "Fujitsu laptop FUJ02E3 ACPI hotkeys driver" 87 #define ACPI_FUJITSU_LAPTOP_DEVICE_NAME "Fujitsu FUJ02E3" 88 89 #define ACPI_FUJITSU_NOTIFY_CODE1 0x80 90 91 /* FUNC interface - command values */ 92 #define FUNC_FLAGS 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 /* FUNC interface - status flags */ 101 #define FLAG_RFKILL 0x020 102 #define FLAG_LID 0x100 103 #define FLAG_DOCK 0x200 104 105 #if IS_ENABLED(CONFIG_LEDS_CLASS) 106 /* FUNC interface - LED control */ 107 #define FUNC_LED_OFF 0x1 108 #define FUNC_LED_ON 0x30001 109 #define KEYBOARD_LAMPS 0x100 110 #define LOGOLAMP_POWERON 0x2000 111 #define LOGOLAMP_ALWAYS 0x4000 112 #define RADIO_LED_ON 0x20 113 #define ECO_LED 0x10000 114 #define ECO_LED_ON 0x80000 115 #endif 116 117 /* Hotkey details */ 118 #define KEY1_CODE 0x410 /* codes for the keys in the GIRB register */ 119 #define KEY2_CODE 0x411 120 #define KEY3_CODE 0x412 121 #define KEY4_CODE 0x413 122 #define KEY5_CODE 0x420 123 124 #define MAX_HOTKEY_RINGBUFFER_SIZE 100 125 #define RINGBUFFERSIZE 40 126 127 /* Debugging */ 128 #define FUJLAPTOP_DBG_ERROR 0x0001 129 #define FUJLAPTOP_DBG_WARN 0x0002 130 #define FUJLAPTOP_DBG_INFO 0x0004 131 #define FUJLAPTOP_DBG_TRACE 0x0008 132 133 #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG 134 #define vdbg_printk(a_dbg_level, format, arg...) \ 135 do { if (dbg_level & a_dbg_level) \ 136 printk(KERN_DEBUG pr_fmt("%s: " format), __func__, ## arg); \ 137 } while (0) 138 #else 139 #define vdbg_printk(a_dbg_level, format, arg...) \ 140 do { } while (0) 141 #endif 142 143 /* Device controlling the backlight and associated keys */ 144 struct fujitsu_bl { 145 acpi_handle acpi_handle; 146 struct acpi_device *dev; 147 struct input_dev *input; 148 char phys[32]; 149 struct backlight_device *bl_device; 150 struct platform_device *pf_device; 151 int keycode1, keycode2, keycode3, keycode4, keycode5; 152 153 unsigned int max_brightness; 154 unsigned int brightness_changed; 155 unsigned int brightness_level; 156 }; 157 158 static struct fujitsu_bl *fujitsu_bl; 159 static int use_alt_lcd_levels = -1; 160 static int disable_brightness_adjust = -1; 161 162 /* Device used to access hotkeys and other features on the laptop */ 163 struct fujitsu_laptop { 164 acpi_handle acpi_handle; 165 struct acpi_device *dev; 166 struct input_dev *input; 167 char phys[32]; 168 struct platform_device *pf_device; 169 struct kfifo fifo; 170 spinlock_t fifo_lock; 171 int flags_supported; 172 int flags_state; 173 int logolamp_registered; 174 int kblamps_registered; 175 int radio_led_registered; 176 int eco_led_registered; 177 }; 178 179 static struct fujitsu_laptop *fujitsu_laptop; 180 181 #if IS_ENABLED(CONFIG_LEDS_CLASS) 182 static enum led_brightness logolamp_get(struct led_classdev *cdev); 183 static int logolamp_set(struct led_classdev *cdev, 184 enum led_brightness brightness); 185 186 static struct led_classdev logolamp_led = { 187 .name = "fujitsu::logolamp", 188 .brightness_get = logolamp_get, 189 .brightness_set_blocking = logolamp_set 190 }; 191 192 static enum led_brightness kblamps_get(struct led_classdev *cdev); 193 static int kblamps_set(struct led_classdev *cdev, 194 enum led_brightness brightness); 195 196 static struct led_classdev kblamps_led = { 197 .name = "fujitsu::kblamps", 198 .brightness_get = kblamps_get, 199 .brightness_set_blocking = kblamps_set 200 }; 201 202 static enum led_brightness radio_led_get(struct led_classdev *cdev); 203 static int radio_led_set(struct led_classdev *cdev, 204 enum led_brightness brightness); 205 206 static struct led_classdev radio_led = { 207 .name = "fujitsu::radio_led", 208 .default_trigger = "rfkill-any", 209 .brightness_get = radio_led_get, 210 .brightness_set_blocking = radio_led_set 211 }; 212 213 static enum led_brightness eco_led_get(struct led_classdev *cdev); 214 static int eco_led_set(struct led_classdev *cdev, 215 enum led_brightness brightness); 216 217 static struct led_classdev eco_led = { 218 .name = "fujitsu::eco_led", 219 .brightness_get = eco_led_get, 220 .brightness_set_blocking = eco_led_set 221 }; 222 #endif 223 224 #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG 225 static u32 dbg_level = 0x03; 226 #endif 227 228 /* Fujitsu ACPI interface function */ 229 230 static int call_fext_func(int cmd, int arg0, int arg1, int arg2) 231 { 232 acpi_status status = AE_OK; 233 union acpi_object params[4] = { 234 { .type = ACPI_TYPE_INTEGER }, 235 { .type = ACPI_TYPE_INTEGER }, 236 { .type = ACPI_TYPE_INTEGER }, 237 { .type = ACPI_TYPE_INTEGER } 238 }; 239 struct acpi_object_list arg_list = { 4, ¶ms[0] }; 240 unsigned long long value; 241 acpi_handle handle = NULL; 242 243 status = acpi_get_handle(fujitsu_laptop->acpi_handle, "FUNC", &handle); 244 if (ACPI_FAILURE(status)) { 245 vdbg_printk(FUJLAPTOP_DBG_ERROR, 246 "FUNC interface is not present\n"); 247 return -ENODEV; 248 } 249 250 params[0].integer.value = cmd; 251 params[1].integer.value = arg0; 252 params[2].integer.value = arg1; 253 params[3].integer.value = arg2; 254 255 status = acpi_evaluate_integer(handle, NULL, &arg_list, &value); 256 if (ACPI_FAILURE(status)) { 257 vdbg_printk(FUJLAPTOP_DBG_WARN, 258 "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) call failed\n", 259 cmd, arg0, arg1, arg2); 260 return -ENODEV; 261 } 262 263 vdbg_printk(FUJLAPTOP_DBG_TRACE, 264 "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) returned 0x%x\n", 265 cmd, arg0, arg1, arg2, (int)value); 266 return value; 267 } 268 269 #if IS_ENABLED(CONFIG_LEDS_CLASS) 270 /* LED class callbacks */ 271 272 static int logolamp_set(struct led_classdev *cdev, 273 enum led_brightness brightness) 274 { 275 int poweron = FUNC_LED_ON, always = FUNC_LED_ON; 276 int ret; 277 278 if (brightness < LED_HALF) 279 poweron = FUNC_LED_OFF; 280 281 if (brightness < LED_FULL) 282 always = FUNC_LED_OFF; 283 284 ret = call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, poweron); 285 if (ret < 0) 286 return ret; 287 288 return call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, always); 289 } 290 291 static int kblamps_set(struct led_classdev *cdev, 292 enum led_brightness brightness) 293 { 294 if (brightness >= LED_FULL) 295 return call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS, FUNC_LED_ON); 296 else 297 return call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS, FUNC_LED_OFF); 298 } 299 300 static int radio_led_set(struct led_classdev *cdev, 301 enum led_brightness brightness) 302 { 303 if (brightness >= LED_FULL) 304 return call_fext_func(FUNC_FLAGS, 0x5, RADIO_LED_ON, RADIO_LED_ON); 305 else 306 return call_fext_func(FUNC_FLAGS, 0x5, RADIO_LED_ON, 0x0); 307 } 308 309 static int eco_led_set(struct led_classdev *cdev, 310 enum led_brightness brightness) 311 { 312 int curr; 313 314 curr = call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0); 315 if (brightness >= LED_FULL) 316 return call_fext_func(FUNC_LEDS, 0x1, ECO_LED, curr | ECO_LED_ON); 317 else 318 return call_fext_func(FUNC_LEDS, 0x1, ECO_LED, curr & ~ECO_LED_ON); 319 } 320 321 static enum led_brightness logolamp_get(struct led_classdev *cdev) 322 { 323 int ret; 324 325 ret = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0); 326 if (ret == FUNC_LED_ON) 327 return LED_FULL; 328 329 ret = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0); 330 if (ret == FUNC_LED_ON) 331 return LED_HALF; 332 333 return LED_OFF; 334 } 335 336 static enum led_brightness kblamps_get(struct led_classdev *cdev) 337 { 338 enum led_brightness brightness = LED_OFF; 339 340 if (call_fext_func(FUNC_LEDS, 0x2, KEYBOARD_LAMPS, 0x0) == FUNC_LED_ON) 341 brightness = LED_FULL; 342 343 return brightness; 344 } 345 346 static enum led_brightness radio_led_get(struct led_classdev *cdev) 347 { 348 enum led_brightness brightness = LED_OFF; 349 350 if (call_fext_func(FUNC_FLAGS, 0x4, 0x0, 0x0) & RADIO_LED_ON) 351 brightness = LED_FULL; 352 353 return brightness; 354 } 355 356 static enum led_brightness eco_led_get(struct led_classdev *cdev) 357 { 358 enum led_brightness brightness = LED_OFF; 359 360 if (call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0) & ECO_LED_ON) 361 brightness = LED_FULL; 362 363 return brightness; 364 } 365 #endif 366 367 /* Hardware access for LCD brightness control */ 368 369 static int set_lcd_level(int level) 370 { 371 acpi_status status = AE_OK; 372 acpi_handle handle = NULL; 373 374 vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via SBLL [%d]\n", 375 level); 376 377 if (level < 0 || level >= fujitsu_bl->max_brightness) 378 return -EINVAL; 379 380 status = acpi_get_handle(fujitsu_bl->acpi_handle, "SBLL", &handle); 381 if (ACPI_FAILURE(status)) { 382 vdbg_printk(FUJLAPTOP_DBG_ERROR, "SBLL not present\n"); 383 return -ENODEV; 384 } 385 386 387 status = acpi_execute_simple_method(handle, NULL, level); 388 if (ACPI_FAILURE(status)) 389 return -ENODEV; 390 391 return 0; 392 } 393 394 static int set_lcd_level_alt(int level) 395 { 396 acpi_status status = AE_OK; 397 acpi_handle handle = NULL; 398 399 vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via SBL2 [%d]\n", 400 level); 401 402 if (level < 0 || level >= fujitsu_bl->max_brightness) 403 return -EINVAL; 404 405 status = acpi_get_handle(fujitsu_bl->acpi_handle, "SBL2", &handle); 406 if (ACPI_FAILURE(status)) { 407 vdbg_printk(FUJLAPTOP_DBG_ERROR, "SBL2 not present\n"); 408 return -ENODEV; 409 } 410 411 status = acpi_execute_simple_method(handle, NULL, level); 412 if (ACPI_FAILURE(status)) 413 return -ENODEV; 414 415 return 0; 416 } 417 418 static int get_lcd_level(void) 419 { 420 unsigned long long state = 0; 421 acpi_status status = AE_OK; 422 423 vdbg_printk(FUJLAPTOP_DBG_TRACE, "get lcd level via GBLL\n"); 424 425 status = acpi_evaluate_integer(fujitsu_bl->acpi_handle, "GBLL", NULL, 426 &state); 427 if (ACPI_FAILURE(status)) 428 return 0; 429 430 fujitsu_bl->brightness_level = state & 0x0fffffff; 431 432 if (state & 0x80000000) 433 fujitsu_bl->brightness_changed = 1; 434 else 435 fujitsu_bl->brightness_changed = 0; 436 437 return fujitsu_bl->brightness_level; 438 } 439 440 static int get_max_brightness(void) 441 { 442 unsigned long long state = 0; 443 acpi_status status = AE_OK; 444 445 vdbg_printk(FUJLAPTOP_DBG_TRACE, "get max lcd level via RBLL\n"); 446 447 status = acpi_evaluate_integer(fujitsu_bl->acpi_handle, "RBLL", NULL, 448 &state); 449 if (ACPI_FAILURE(status)) 450 return -1; 451 452 fujitsu_bl->max_brightness = state; 453 454 return fujitsu_bl->max_brightness; 455 } 456 457 /* Backlight device stuff */ 458 459 static int bl_get_brightness(struct backlight_device *b) 460 { 461 return get_lcd_level(); 462 } 463 464 static int bl_update_status(struct backlight_device *b) 465 { 466 int ret; 467 if (b->props.power == FB_BLANK_POWERDOWN) 468 ret = call_fext_func(FUNC_BACKLIGHT, 0x1, 0x4, 0x3); 469 else 470 ret = call_fext_func(FUNC_BACKLIGHT, 0x1, 0x4, 0x0); 471 if (ret != 0) 472 vdbg_printk(FUJLAPTOP_DBG_ERROR, 473 "Unable to adjust backlight power, error code %i\n", 474 ret); 475 476 if (use_alt_lcd_levels) 477 ret = set_lcd_level_alt(b->props.brightness); 478 else 479 ret = set_lcd_level(b->props.brightness); 480 if (ret != 0) 481 vdbg_printk(FUJLAPTOP_DBG_ERROR, 482 "Unable to adjust LCD brightness, error code %i\n", 483 ret); 484 return ret; 485 } 486 487 static const struct backlight_ops fujitsu_bl_ops = { 488 .get_brightness = bl_get_brightness, 489 .update_status = bl_update_status, 490 }; 491 492 /* Platform LCD brightness device */ 493 494 static ssize_t 495 show_max_brightness(struct device *dev, 496 struct device_attribute *attr, char *buf) 497 { 498 499 int ret; 500 501 ret = get_max_brightness(); 502 if (ret < 0) 503 return ret; 504 505 return sprintf(buf, "%i\n", ret); 506 } 507 508 static ssize_t 509 show_brightness_changed(struct device *dev, 510 struct device_attribute *attr, char *buf) 511 { 512 513 int ret; 514 515 ret = fujitsu_bl->brightness_changed; 516 if (ret < 0) 517 return ret; 518 519 return sprintf(buf, "%i\n", ret); 520 } 521 522 static ssize_t show_lcd_level(struct device *dev, 523 struct device_attribute *attr, char *buf) 524 { 525 526 int ret; 527 528 ret = get_lcd_level(); 529 if (ret < 0) 530 return ret; 531 532 return sprintf(buf, "%i\n", ret); 533 } 534 535 static ssize_t store_lcd_level(struct device *dev, 536 struct device_attribute *attr, const char *buf, 537 size_t count) 538 { 539 540 int level, ret; 541 542 if (sscanf(buf, "%i", &level) != 1 543 || (level < 0 || level >= fujitsu_bl->max_brightness)) 544 return -EINVAL; 545 546 if (use_alt_lcd_levels) 547 ret = set_lcd_level_alt(level); 548 else 549 ret = set_lcd_level(level); 550 if (ret < 0) 551 return ret; 552 553 ret = get_lcd_level(); 554 if (ret < 0) 555 return ret; 556 557 return count; 558 } 559 560 static ssize_t 561 ignore_store(struct device *dev, 562 struct device_attribute *attr, const char *buf, size_t count) 563 { 564 return count; 565 } 566 567 static ssize_t 568 show_lid_state(struct device *dev, 569 struct device_attribute *attr, char *buf) 570 { 571 if (!(fujitsu_laptop->flags_supported & FLAG_LID)) 572 return sprintf(buf, "unknown\n"); 573 if (fujitsu_laptop->flags_state & FLAG_LID) 574 return sprintf(buf, "open\n"); 575 else 576 return sprintf(buf, "closed\n"); 577 } 578 579 static ssize_t 580 show_dock_state(struct device *dev, 581 struct device_attribute *attr, char *buf) 582 { 583 if (!(fujitsu_laptop->flags_supported & FLAG_DOCK)) 584 return sprintf(buf, "unknown\n"); 585 if (fujitsu_laptop->flags_state & FLAG_DOCK) 586 return sprintf(buf, "docked\n"); 587 else 588 return sprintf(buf, "undocked\n"); 589 } 590 591 static ssize_t 592 show_radios_state(struct device *dev, 593 struct device_attribute *attr, char *buf) 594 { 595 if (!(fujitsu_laptop->flags_supported & FLAG_RFKILL)) 596 return sprintf(buf, "unknown\n"); 597 if (fujitsu_laptop->flags_state & FLAG_RFKILL) 598 return sprintf(buf, "on\n"); 599 else 600 return sprintf(buf, "killed\n"); 601 } 602 603 static DEVICE_ATTR(max_brightness, 0444, show_max_brightness, ignore_store); 604 static DEVICE_ATTR(brightness_changed, 0444, show_brightness_changed, 605 ignore_store); 606 static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level); 607 static DEVICE_ATTR(lid, 0444, show_lid_state, ignore_store); 608 static DEVICE_ATTR(dock, 0444, show_dock_state, ignore_store); 609 static DEVICE_ATTR(radios, 0444, show_radios_state, ignore_store); 610 611 static struct attribute *fujitsu_pf_attributes[] = { 612 &dev_attr_brightness_changed.attr, 613 &dev_attr_max_brightness.attr, 614 &dev_attr_lcd_level.attr, 615 &dev_attr_lid.attr, 616 &dev_attr_dock.attr, 617 &dev_attr_radios.attr, 618 NULL 619 }; 620 621 static struct attribute_group fujitsu_pf_attribute_group = { 622 .attrs = fujitsu_pf_attributes 623 }; 624 625 static struct platform_driver fujitsu_pf_driver = { 626 .driver = { 627 .name = "fujitsu-laptop", 628 } 629 }; 630 631 static void __init dmi_check_cb_common(const struct dmi_system_id *id) 632 { 633 pr_info("Identified laptop model '%s'\n", id->ident); 634 } 635 636 static int __init dmi_check_cb_s6410(const struct dmi_system_id *id) 637 { 638 dmi_check_cb_common(id); 639 fujitsu_bl->keycode1 = KEY_SCREENLOCK; /* "Lock" */ 640 fujitsu_bl->keycode2 = KEY_HELP; /* "Mobility Center" */ 641 return 1; 642 } 643 644 static int __init dmi_check_cb_s6420(const struct dmi_system_id *id) 645 { 646 dmi_check_cb_common(id); 647 fujitsu_bl->keycode1 = KEY_SCREENLOCK; /* "Lock" */ 648 fujitsu_bl->keycode2 = KEY_HELP; /* "Mobility Center" */ 649 return 1; 650 } 651 652 static int __init dmi_check_cb_p8010(const struct dmi_system_id *id) 653 { 654 dmi_check_cb_common(id); 655 fujitsu_bl->keycode1 = KEY_HELP; /* "Support" */ 656 fujitsu_bl->keycode3 = KEY_SWITCHVIDEOMODE; /* "Presentation" */ 657 fujitsu_bl->keycode4 = KEY_WWW; /* "Internet" */ 658 return 1; 659 } 660 661 static const struct dmi_system_id fujitsu_dmi_table[] __initconst = { 662 { 663 .ident = "Fujitsu Siemens S6410", 664 .matches = { 665 DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), 666 DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6410"), 667 }, 668 .callback = dmi_check_cb_s6410}, 669 { 670 .ident = "Fujitsu Siemens S6420", 671 .matches = { 672 DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), 673 DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6420"), 674 }, 675 .callback = dmi_check_cb_s6420}, 676 { 677 .ident = "Fujitsu LifeBook P8010", 678 .matches = { 679 DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), 680 DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook P8010"), 681 }, 682 .callback = dmi_check_cb_p8010}, 683 {} 684 }; 685 686 /* ACPI device for LCD brightness control */ 687 688 static int acpi_fujitsu_bl_add(struct acpi_device *device) 689 { 690 int state = 0; 691 struct input_dev *input; 692 int error; 693 694 if (!device) 695 return -EINVAL; 696 697 fujitsu_bl->acpi_handle = device->handle; 698 sprintf(acpi_device_name(device), "%s", ACPI_FUJITSU_BL_DEVICE_NAME); 699 sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS); 700 device->driver_data = fujitsu_bl; 701 702 fujitsu_bl->input = input = input_allocate_device(); 703 if (!input) { 704 error = -ENOMEM; 705 goto err_stop; 706 } 707 708 snprintf(fujitsu_bl->phys, sizeof(fujitsu_bl->phys), 709 "%s/video/input0", acpi_device_hid(device)); 710 711 input->name = acpi_device_name(device); 712 input->phys = fujitsu_bl->phys; 713 input->id.bustype = BUS_HOST; 714 input->id.product = 0x06; 715 input->dev.parent = &device->dev; 716 input->evbit[0] = BIT(EV_KEY); 717 set_bit(KEY_BRIGHTNESSUP, input->keybit); 718 set_bit(KEY_BRIGHTNESSDOWN, input->keybit); 719 set_bit(KEY_UNKNOWN, input->keybit); 720 721 error = input_register_device(input); 722 if (error) 723 goto err_free_input_dev; 724 725 error = acpi_bus_update_power(fujitsu_bl->acpi_handle, &state); 726 if (error) { 727 pr_err("Error reading power state\n"); 728 goto err_unregister_input_dev; 729 } 730 731 pr_info("ACPI: %s [%s] (%s)\n", 732 acpi_device_name(device), acpi_device_bid(device), 733 !device->power.state ? "on" : "off"); 734 735 fujitsu_bl->dev = device; 736 737 if (acpi_has_method(device->handle, METHOD_NAME__INI)) { 738 vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n"); 739 if (ACPI_FAILURE 740 (acpi_evaluate_object 741 (device->handle, METHOD_NAME__INI, NULL, NULL))) 742 pr_err("_INI Method failed\n"); 743 } 744 745 if (use_alt_lcd_levels == -1) { 746 if (acpi_has_method(NULL, "\\_SB.PCI0.LPCB.FJEX.SBL2")) 747 use_alt_lcd_levels = 1; 748 else 749 use_alt_lcd_levels = 0; 750 vdbg_printk(FUJLAPTOP_DBG_TRACE, "auto-detected usealt as %i\n", 751 use_alt_lcd_levels); 752 } 753 754 /* do config (detect defaults) */ 755 use_alt_lcd_levels = use_alt_lcd_levels == 1 ? 1 : 0; 756 disable_brightness_adjust = disable_brightness_adjust == 1 ? 1 : 0; 757 vdbg_printk(FUJLAPTOP_DBG_INFO, 758 "config: [alt interface: %d], [adjust disable: %d]\n", 759 use_alt_lcd_levels, disable_brightness_adjust); 760 761 if (get_max_brightness() <= 0) 762 fujitsu_bl->max_brightness = FUJITSU_LCD_N_LEVELS; 763 get_lcd_level(); 764 765 return 0; 766 767 err_unregister_input_dev: 768 input_unregister_device(input); 769 input = NULL; 770 err_free_input_dev: 771 input_free_device(input); 772 err_stop: 773 return error; 774 } 775 776 static int acpi_fujitsu_bl_remove(struct acpi_device *device) 777 { 778 struct fujitsu_bl *fujitsu_bl = acpi_driver_data(device); 779 struct input_dev *input = fujitsu_bl->input; 780 781 input_unregister_device(input); 782 783 fujitsu_bl->acpi_handle = NULL; 784 785 return 0; 786 } 787 788 /* Brightness notify */ 789 790 static void acpi_fujitsu_bl_notify(struct acpi_device *device, u32 event) 791 { 792 struct input_dev *input; 793 int keycode; 794 int oldb, newb; 795 796 input = fujitsu_bl->input; 797 798 switch (event) { 799 case ACPI_FUJITSU_NOTIFY_CODE1: 800 keycode = 0; 801 oldb = fujitsu_bl->brightness_level; 802 get_lcd_level(); 803 newb = fujitsu_bl->brightness_level; 804 805 vdbg_printk(FUJLAPTOP_DBG_TRACE, 806 "brightness button event [%i -> %i (%i)]\n", 807 oldb, newb, fujitsu_bl->brightness_changed); 808 809 if (oldb < newb) { 810 if (disable_brightness_adjust != 1) { 811 if (use_alt_lcd_levels) 812 set_lcd_level_alt(newb); 813 else 814 set_lcd_level(newb); 815 } 816 keycode = KEY_BRIGHTNESSUP; 817 } else if (oldb > newb) { 818 if (disable_brightness_adjust != 1) { 819 if (use_alt_lcd_levels) 820 set_lcd_level_alt(newb); 821 else 822 set_lcd_level(newb); 823 } 824 keycode = KEY_BRIGHTNESSDOWN; 825 } 826 break; 827 default: 828 keycode = KEY_UNKNOWN; 829 vdbg_printk(FUJLAPTOP_DBG_WARN, 830 "unsupported event [0x%x]\n", event); 831 break; 832 } 833 834 if (keycode != 0) { 835 input_report_key(input, keycode, 1); 836 input_sync(input); 837 input_report_key(input, keycode, 0); 838 input_sync(input); 839 } 840 } 841 842 /* ACPI device for hotkey handling */ 843 844 static int acpi_fujitsu_laptop_add(struct acpi_device *device) 845 { 846 int result = 0; 847 int state = 0; 848 struct input_dev *input; 849 int error; 850 int i; 851 852 if (!device) 853 return -EINVAL; 854 855 fujitsu_laptop->acpi_handle = device->handle; 856 sprintf(acpi_device_name(device), "%s", 857 ACPI_FUJITSU_LAPTOP_DEVICE_NAME); 858 sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS); 859 device->driver_data = fujitsu_laptop; 860 861 /* kfifo */ 862 spin_lock_init(&fujitsu_laptop->fifo_lock); 863 error = kfifo_alloc(&fujitsu_laptop->fifo, RINGBUFFERSIZE * sizeof(int), 864 GFP_KERNEL); 865 if (error) { 866 pr_err("kfifo_alloc failed\n"); 867 goto err_stop; 868 } 869 870 fujitsu_laptop->input = input = input_allocate_device(); 871 if (!input) { 872 error = -ENOMEM; 873 goto err_free_fifo; 874 } 875 876 snprintf(fujitsu_laptop->phys, sizeof(fujitsu_laptop->phys), 877 "%s/video/input0", acpi_device_hid(device)); 878 879 input->name = acpi_device_name(device); 880 input->phys = fujitsu_laptop->phys; 881 input->id.bustype = BUS_HOST; 882 input->id.product = 0x06; 883 input->dev.parent = &device->dev; 884 885 set_bit(EV_KEY, input->evbit); 886 set_bit(fujitsu_bl->keycode1, input->keybit); 887 set_bit(fujitsu_bl->keycode2, input->keybit); 888 set_bit(fujitsu_bl->keycode3, input->keybit); 889 set_bit(fujitsu_bl->keycode4, input->keybit); 890 set_bit(fujitsu_bl->keycode5, input->keybit); 891 set_bit(KEY_TOUCHPAD_TOGGLE, input->keybit); 892 set_bit(KEY_UNKNOWN, input->keybit); 893 894 error = input_register_device(input); 895 if (error) 896 goto err_free_input_dev; 897 898 error = acpi_bus_update_power(fujitsu_laptop->acpi_handle, &state); 899 if (error) { 900 pr_err("Error reading power state\n"); 901 goto err_unregister_input_dev; 902 } 903 904 pr_info("ACPI: %s [%s] (%s)\n", 905 acpi_device_name(device), acpi_device_bid(device), 906 !device->power.state ? "on" : "off"); 907 908 fujitsu_laptop->dev = device; 909 910 if (acpi_has_method(device->handle, METHOD_NAME__INI)) { 911 vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n"); 912 if (ACPI_FAILURE 913 (acpi_evaluate_object 914 (device->handle, METHOD_NAME__INI, NULL, NULL))) 915 pr_err("_INI Method failed\n"); 916 } 917 918 i = 0; 919 while (call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0) != 0 920 && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) 921 ; /* No action, result is discarded */ 922 vdbg_printk(FUJLAPTOP_DBG_INFO, "Discarded %i ringbuffer entries\n", i); 923 924 fujitsu_laptop->flags_supported = 925 call_fext_func(FUNC_FLAGS, 0x0, 0x0, 0x0); 926 927 /* Make sure our bitmask of supported functions is cleared if the 928 RFKILL function block is not implemented, like on the S7020. */ 929 if (fujitsu_laptop->flags_supported == UNSUPPORTED_CMD) 930 fujitsu_laptop->flags_supported = 0; 931 932 if (fujitsu_laptop->flags_supported) 933 fujitsu_laptop->flags_state = 934 call_fext_func(FUNC_FLAGS, 0x4, 0x0, 0x0); 935 936 /* Suspect this is a keymap of the application panel, print it */ 937 pr_info("BTNI: [0x%x]\n", call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0)); 938 939 #if IS_ENABLED(CONFIG_LEDS_CLASS) 940 if (call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) { 941 result = led_classdev_register(&fujitsu_bl->pf_device->dev, 942 &logolamp_led); 943 if (result == 0) { 944 fujitsu_laptop->logolamp_registered = 1; 945 } else { 946 pr_err("Could not register LED handler for logo lamp, error %i\n", 947 result); 948 } 949 } 950 951 if ((call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & KEYBOARD_LAMPS) && 952 (call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0) == 0x0)) { 953 result = led_classdev_register(&fujitsu_bl->pf_device->dev, 954 &kblamps_led); 955 if (result == 0) { 956 fujitsu_laptop->kblamps_registered = 1; 957 } else { 958 pr_err("Could not register LED handler for keyboard lamps, error %i\n", 959 result); 960 } 961 } 962 963 /* 964 * BTNI bit 24 seems to indicate the presence of a radio toggle 965 * button in place of a slide switch, and all such machines appear 966 * to also have an RF LED. Therefore use bit 24 as an indicator 967 * that an RF LED is present. 968 */ 969 if (call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0) & BIT(24)) { 970 result = led_classdev_register(&fujitsu_bl->pf_device->dev, 971 &radio_led); 972 if (result == 0) { 973 fujitsu_laptop->radio_led_registered = 1; 974 } else { 975 pr_err("Could not register LED handler for radio LED, error %i\n", 976 result); 977 } 978 } 979 980 /* Support for eco led is not always signaled in bit corresponding 981 * to the bit used to control the led. According to the DSDT table, 982 * bit 14 seems to indicate presence of said led as well. 983 * Confirm by testing the status. 984 */ 985 if ((call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & BIT(14)) && 986 (call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0) != UNSUPPORTED_CMD)) { 987 result = led_classdev_register(&fujitsu_bl->pf_device->dev, 988 &eco_led); 989 if (result == 0) { 990 fujitsu_laptop->eco_led_registered = 1; 991 } else { 992 pr_err("Could not register LED handler for eco LED, error %i\n", 993 result); 994 } 995 } 996 #endif 997 998 return result; 999 1000 err_unregister_input_dev: 1001 input_unregister_device(input); 1002 input = NULL; 1003 err_free_input_dev: 1004 input_free_device(input); 1005 err_free_fifo: 1006 kfifo_free(&fujitsu_laptop->fifo); 1007 err_stop: 1008 return error; 1009 } 1010 1011 static int acpi_fujitsu_laptop_remove(struct acpi_device *device) 1012 { 1013 struct fujitsu_laptop *fujitsu_laptop = acpi_driver_data(device); 1014 struct input_dev *input = fujitsu_laptop->input; 1015 1016 #if IS_ENABLED(CONFIG_LEDS_CLASS) 1017 if (fujitsu_laptop->logolamp_registered) 1018 led_classdev_unregister(&logolamp_led); 1019 1020 if (fujitsu_laptop->kblamps_registered) 1021 led_classdev_unregister(&kblamps_led); 1022 1023 if (fujitsu_laptop->radio_led_registered) 1024 led_classdev_unregister(&radio_led); 1025 1026 if (fujitsu_laptop->eco_led_registered) 1027 led_classdev_unregister(&eco_led); 1028 #endif 1029 1030 input_unregister_device(input); 1031 1032 kfifo_free(&fujitsu_laptop->fifo); 1033 1034 fujitsu_laptop->acpi_handle = NULL; 1035 1036 return 0; 1037 } 1038 1039 static void acpi_fujitsu_laptop_press(int keycode) 1040 { 1041 struct input_dev *input = fujitsu_laptop->input; 1042 int status; 1043 1044 status = kfifo_in_locked(&fujitsu_laptop->fifo, 1045 (unsigned char *)&keycode, sizeof(keycode), 1046 &fujitsu_laptop->fifo_lock); 1047 if (status != sizeof(keycode)) { 1048 vdbg_printk(FUJLAPTOP_DBG_WARN, 1049 "Could not push keycode [0x%x]\n", keycode); 1050 return; 1051 } 1052 input_report_key(input, keycode, 1); 1053 input_sync(input); 1054 vdbg_printk(FUJLAPTOP_DBG_TRACE, 1055 "Push keycode into ringbuffer [%d]\n", keycode); 1056 } 1057 1058 static void acpi_fujitsu_laptop_release(void) 1059 { 1060 struct input_dev *input = fujitsu_laptop->input; 1061 int keycode, status; 1062 1063 while (true) { 1064 status = kfifo_out_locked(&fujitsu_laptop->fifo, 1065 (unsigned char *)&keycode, 1066 sizeof(keycode), 1067 &fujitsu_laptop->fifo_lock); 1068 if (status != sizeof(keycode)) 1069 return; 1070 input_report_key(input, keycode, 0); 1071 input_sync(input); 1072 vdbg_printk(FUJLAPTOP_DBG_TRACE, 1073 "Pop keycode from ringbuffer [%d]\n", keycode); 1074 } 1075 } 1076 1077 static void acpi_fujitsu_laptop_notify(struct acpi_device *device, u32 event) 1078 { 1079 struct input_dev *input; 1080 int keycode; 1081 unsigned int irb = 1; 1082 int i; 1083 1084 input = fujitsu_laptop->input; 1085 1086 if (event != ACPI_FUJITSU_NOTIFY_CODE1) { 1087 keycode = KEY_UNKNOWN; 1088 vdbg_printk(FUJLAPTOP_DBG_WARN, 1089 "Unsupported event [0x%x]\n", event); 1090 input_report_key(input, keycode, 1); 1091 input_sync(input); 1092 input_report_key(input, keycode, 0); 1093 input_sync(input); 1094 return; 1095 } 1096 1097 if (fujitsu_laptop->flags_supported) 1098 fujitsu_laptop->flags_state = 1099 call_fext_func(FUNC_FLAGS, 0x4, 0x0, 0x0); 1100 1101 i = 0; 1102 while ((irb = 1103 call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0 1104 && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) { 1105 switch (irb & 0x4ff) { 1106 case KEY1_CODE: 1107 keycode = fujitsu_bl->keycode1; 1108 break; 1109 case KEY2_CODE: 1110 keycode = fujitsu_bl->keycode2; 1111 break; 1112 case KEY3_CODE: 1113 keycode = fujitsu_bl->keycode3; 1114 break; 1115 case KEY4_CODE: 1116 keycode = fujitsu_bl->keycode4; 1117 break; 1118 case KEY5_CODE: 1119 keycode = fujitsu_bl->keycode5; 1120 break; 1121 case 0: 1122 keycode = 0; 1123 break; 1124 default: 1125 vdbg_printk(FUJLAPTOP_DBG_WARN, 1126 "Unknown GIRB result [%x]\n", irb); 1127 keycode = -1; 1128 break; 1129 } 1130 1131 if (keycode > 0) 1132 acpi_fujitsu_laptop_press(keycode); 1133 else if (keycode == 0) 1134 acpi_fujitsu_laptop_release(); 1135 } 1136 1137 /* On some models (first seen on the Skylake-based Lifebook 1138 * E736/E746/E756), the touchpad toggle hotkey (Fn+F4) is 1139 * handled in software; its state is queried using FUNC_FLAGS 1140 */ 1141 if ((fujitsu_laptop->flags_supported & BIT(26)) && 1142 (call_fext_func(FUNC_FLAGS, 0x1, 0x0, 0x0) & BIT(26))) { 1143 keycode = KEY_TOUCHPAD_TOGGLE; 1144 input_report_key(input, keycode, 1); 1145 input_sync(input); 1146 input_report_key(input, keycode, 0); 1147 input_sync(input); 1148 } 1149 1150 } 1151 1152 /* Initialization */ 1153 1154 static const struct acpi_device_id fujitsu_bl_device_ids[] = { 1155 {ACPI_FUJITSU_BL_HID, 0}, 1156 {"", 0}, 1157 }; 1158 1159 static struct acpi_driver acpi_fujitsu_bl_driver = { 1160 .name = ACPI_FUJITSU_BL_DRIVER_NAME, 1161 .class = ACPI_FUJITSU_CLASS, 1162 .ids = fujitsu_bl_device_ids, 1163 .ops = { 1164 .add = acpi_fujitsu_bl_add, 1165 .remove = acpi_fujitsu_bl_remove, 1166 .notify = acpi_fujitsu_bl_notify, 1167 }, 1168 }; 1169 1170 static const struct acpi_device_id fujitsu_laptop_device_ids[] = { 1171 {ACPI_FUJITSU_LAPTOP_HID, 0}, 1172 {"", 0}, 1173 }; 1174 1175 static struct acpi_driver acpi_fujitsu_laptop_driver = { 1176 .name = ACPI_FUJITSU_LAPTOP_DRIVER_NAME, 1177 .class = ACPI_FUJITSU_CLASS, 1178 .ids = fujitsu_laptop_device_ids, 1179 .ops = { 1180 .add = acpi_fujitsu_laptop_add, 1181 .remove = acpi_fujitsu_laptop_remove, 1182 .notify = acpi_fujitsu_laptop_notify, 1183 }, 1184 }; 1185 1186 static const struct acpi_device_id fujitsu_ids[] __used = { 1187 {ACPI_FUJITSU_BL_HID, 0}, 1188 {ACPI_FUJITSU_LAPTOP_HID, 0}, 1189 {"", 0} 1190 }; 1191 MODULE_DEVICE_TABLE(acpi, fujitsu_ids); 1192 1193 static int __init fujitsu_init(void) 1194 { 1195 int ret, max_brightness; 1196 1197 if (acpi_disabled) 1198 return -ENODEV; 1199 1200 fujitsu_bl = kzalloc(sizeof(struct fujitsu_bl), GFP_KERNEL); 1201 if (!fujitsu_bl) 1202 return -ENOMEM; 1203 fujitsu_bl->keycode1 = KEY_PROG1; 1204 fujitsu_bl->keycode2 = KEY_PROG2; 1205 fujitsu_bl->keycode3 = KEY_PROG3; 1206 fujitsu_bl->keycode4 = KEY_PROG4; 1207 fujitsu_bl->keycode5 = KEY_RFKILL; 1208 dmi_check_system(fujitsu_dmi_table); 1209 1210 ret = acpi_bus_register_driver(&acpi_fujitsu_bl_driver); 1211 if (ret) 1212 goto fail_acpi; 1213 1214 /* Register platform stuff */ 1215 1216 fujitsu_bl->pf_device = platform_device_alloc("fujitsu-laptop", -1); 1217 if (!fujitsu_bl->pf_device) { 1218 ret = -ENOMEM; 1219 goto fail_platform_driver; 1220 } 1221 1222 ret = platform_device_add(fujitsu_bl->pf_device); 1223 if (ret) 1224 goto fail_platform_device1; 1225 1226 ret = 1227 sysfs_create_group(&fujitsu_bl->pf_device->dev.kobj, 1228 &fujitsu_pf_attribute_group); 1229 if (ret) 1230 goto fail_platform_device2; 1231 1232 /* Register backlight stuff */ 1233 1234 if (acpi_video_get_backlight_type() == acpi_backlight_vendor) { 1235 struct backlight_properties props; 1236 1237 memset(&props, 0, sizeof(struct backlight_properties)); 1238 max_brightness = fujitsu_bl->max_brightness; 1239 props.type = BACKLIGHT_PLATFORM; 1240 props.max_brightness = max_brightness - 1; 1241 fujitsu_bl->bl_device = backlight_device_register("fujitsu-laptop", 1242 NULL, NULL, 1243 &fujitsu_bl_ops, 1244 &props); 1245 if (IS_ERR(fujitsu_bl->bl_device)) { 1246 ret = PTR_ERR(fujitsu_bl->bl_device); 1247 fujitsu_bl->bl_device = NULL; 1248 goto fail_sysfs_group; 1249 } 1250 fujitsu_bl->bl_device->props.brightness = fujitsu_bl->brightness_level; 1251 } 1252 1253 ret = platform_driver_register(&fujitsu_pf_driver); 1254 if (ret) 1255 goto fail_backlight; 1256 1257 /* Register laptop driver */ 1258 1259 fujitsu_laptop = kzalloc(sizeof(struct fujitsu_laptop), GFP_KERNEL); 1260 if (!fujitsu_laptop) { 1261 ret = -ENOMEM; 1262 goto fail_laptop; 1263 } 1264 1265 ret = acpi_bus_register_driver(&acpi_fujitsu_laptop_driver); 1266 if (ret) 1267 goto fail_laptop1; 1268 1269 /* Sync backlight power status (needs FUJ02E3 device, hence deferred) */ 1270 if (acpi_video_get_backlight_type() == acpi_backlight_vendor) { 1271 if (call_fext_func(FUNC_BACKLIGHT, 0x2, 0x4, 0x0) == 3) 1272 fujitsu_bl->bl_device->props.power = FB_BLANK_POWERDOWN; 1273 else 1274 fujitsu_bl->bl_device->props.power = FB_BLANK_UNBLANK; 1275 } 1276 1277 pr_info("driver " FUJITSU_DRIVER_VERSION " successfully loaded\n"); 1278 1279 return 0; 1280 1281 fail_laptop1: 1282 kfree(fujitsu_laptop); 1283 fail_laptop: 1284 platform_driver_unregister(&fujitsu_pf_driver); 1285 fail_backlight: 1286 backlight_device_unregister(fujitsu_bl->bl_device); 1287 fail_sysfs_group: 1288 sysfs_remove_group(&fujitsu_bl->pf_device->dev.kobj, 1289 &fujitsu_pf_attribute_group); 1290 fail_platform_device2: 1291 platform_device_del(fujitsu_bl->pf_device); 1292 fail_platform_device1: 1293 platform_device_put(fujitsu_bl->pf_device); 1294 fail_platform_driver: 1295 acpi_bus_unregister_driver(&acpi_fujitsu_bl_driver); 1296 fail_acpi: 1297 kfree(fujitsu_bl); 1298 1299 return ret; 1300 } 1301 1302 static void __exit fujitsu_cleanup(void) 1303 { 1304 acpi_bus_unregister_driver(&acpi_fujitsu_laptop_driver); 1305 1306 kfree(fujitsu_laptop); 1307 1308 platform_driver_unregister(&fujitsu_pf_driver); 1309 1310 backlight_device_unregister(fujitsu_bl->bl_device); 1311 1312 sysfs_remove_group(&fujitsu_bl->pf_device->dev.kobj, 1313 &fujitsu_pf_attribute_group); 1314 1315 platform_device_unregister(fujitsu_bl->pf_device); 1316 1317 acpi_bus_unregister_driver(&acpi_fujitsu_bl_driver); 1318 1319 kfree(fujitsu_bl); 1320 1321 pr_info("driver unloaded\n"); 1322 } 1323 1324 module_init(fujitsu_init); 1325 module_exit(fujitsu_cleanup); 1326 1327 module_param(use_alt_lcd_levels, uint, 0644); 1328 MODULE_PARM_DESC(use_alt_lcd_levels, 1329 "Use alternative interface for lcd_levels (needed for Lifebook s6410)."); 1330 module_param(disable_brightness_adjust, uint, 0644); 1331 MODULE_PARM_DESC(disable_brightness_adjust, "Disable brightness adjustment ."); 1332 #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG 1333 module_param_named(debug, dbg_level, uint, 0644); 1334 MODULE_PARM_DESC(debug, "Sets debug level bit-mask"); 1335 #endif 1336 1337 MODULE_AUTHOR("Jonathan Woithe, Peter Gruber, Tony Vroon"); 1338 MODULE_DESCRIPTION("Fujitsu laptop extras support"); 1339 MODULE_VERSION(FUJITSU_DRIVER_VERSION); 1340 MODULE_LICENSE("GPL"); 1341