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