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