1 /* 2 * Samsung Laptop driver 3 * 4 * Copyright (C) 2009,2011 Greg Kroah-Hartman (gregkh@suse.de) 5 * Copyright (C) 2009,2011 Novell Inc. 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License version 2 as published by 9 * the Free Software Foundation. 10 * 11 */ 12 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 13 14 #include <linux/kernel.h> 15 #include <linux/init.h> 16 #include <linux/module.h> 17 #include <linux/delay.h> 18 #include <linux/pci.h> 19 #include <linux/backlight.h> 20 #include <linux/leds.h> 21 #include <linux/fb.h> 22 #include <linux/dmi.h> 23 #include <linux/platform_device.h> 24 #include <linux/rfkill.h> 25 #include <linux/acpi.h> 26 #include <linux/seq_file.h> 27 #include <linux/debugfs.h> 28 #include <linux/ctype.h> 29 #include <linux/efi.h> 30 #include <linux/suspend.h> 31 #include <acpi/video.h> 32 33 /* 34 * This driver is needed because a number of Samsung laptops do not hook 35 * their control settings through ACPI. So we have to poke around in the 36 * BIOS to do things like brightness values, and "special" key controls. 37 */ 38 39 /* 40 * We have 0 - 8 as valid brightness levels. The specs say that level 0 should 41 * be reserved by the BIOS (which really doesn't make much sense), we tell 42 * userspace that the value is 0 - 7 and then just tell the hardware 1 - 8 43 */ 44 #define MAX_BRIGHT 0x07 45 46 47 #define SABI_IFACE_MAIN 0x00 48 #define SABI_IFACE_SUB 0x02 49 #define SABI_IFACE_COMPLETE 0x04 50 #define SABI_IFACE_DATA 0x05 51 52 #define WL_STATUS_WLAN 0x0 53 #define WL_STATUS_BT 0x2 54 55 /* Structure get/set data using sabi */ 56 struct sabi_data { 57 union { 58 struct { 59 u32 d0; 60 u32 d1; 61 u16 d2; 62 u8 d3; 63 }; 64 u8 data[11]; 65 }; 66 }; 67 68 struct sabi_header_offsets { 69 u8 port; 70 u8 re_mem; 71 u8 iface_func; 72 u8 en_mem; 73 u8 data_offset; 74 u8 data_segment; 75 }; 76 77 struct sabi_commands { 78 /* 79 * Brightness is 0 - 8, as described above. 80 * Value 0 is for the BIOS to use 81 */ 82 u16 get_brightness; 83 u16 set_brightness; 84 85 /* 86 * first byte: 87 * 0x00 - wireless is off 88 * 0x01 - wireless is on 89 * second byte: 90 * 0x02 - 3G is off 91 * 0x03 - 3G is on 92 * TODO, verify 3G is correct, that doesn't seem right... 93 */ 94 u16 get_wireless_button; 95 u16 set_wireless_button; 96 97 /* 0 is off, 1 is on */ 98 u16 get_backlight; 99 u16 set_backlight; 100 101 /* 102 * 0x80 or 0x00 - no action 103 * 0x81 - recovery key pressed 104 */ 105 u16 get_recovery_mode; 106 u16 set_recovery_mode; 107 108 /* 109 * on seclinux: 0 is low, 1 is high, 110 * on swsmi: 0 is normal, 1 is silent, 2 is turbo 111 */ 112 u16 get_performance_level; 113 u16 set_performance_level; 114 115 /* 0x80 is off, 0x81 is on */ 116 u16 get_battery_life_extender; 117 u16 set_battery_life_extender; 118 119 /* 0x80 is off, 0x81 is on */ 120 u16 get_usb_charge; 121 u16 set_usb_charge; 122 123 /* the first byte is for bluetooth and the third one is for wlan */ 124 u16 get_wireless_status; 125 u16 set_wireless_status; 126 127 /* 0x80 is off, 0x81 is on */ 128 u16 get_lid_handling; 129 u16 set_lid_handling; 130 131 /* 0x81 to read, (0x82 | level << 8) to set, 0xaabb to enable */ 132 u16 kbd_backlight; 133 134 /* 135 * Tell the BIOS that Linux is running on this machine. 136 * 81 is on, 80 is off 137 */ 138 u16 set_linux; 139 }; 140 141 struct sabi_performance_level { 142 const char *name; 143 u16 value; 144 }; 145 146 struct sabi_config { 147 int sabi_version; 148 const char *test_string; 149 u16 main_function; 150 const struct sabi_header_offsets header_offsets; 151 const struct sabi_commands commands; 152 const struct sabi_performance_level performance_levels[4]; 153 u8 min_brightness; 154 u8 max_brightness; 155 }; 156 157 static const struct sabi_config sabi_configs[] = { 158 { 159 /* I don't know if it is really 2, but it it is 160 * less than 3 anyway */ 161 .sabi_version = 2, 162 163 .test_string = "SECLINUX", 164 165 .main_function = 0x4c49, 166 167 .header_offsets = { 168 .port = 0x00, 169 .re_mem = 0x02, 170 .iface_func = 0x03, 171 .en_mem = 0x04, 172 .data_offset = 0x05, 173 .data_segment = 0x07, 174 }, 175 176 .commands = { 177 .get_brightness = 0x00, 178 .set_brightness = 0x01, 179 180 .get_wireless_button = 0x02, 181 .set_wireless_button = 0x03, 182 183 .get_backlight = 0x04, 184 .set_backlight = 0x05, 185 186 .get_recovery_mode = 0x06, 187 .set_recovery_mode = 0x07, 188 189 .get_performance_level = 0x08, 190 .set_performance_level = 0x09, 191 192 .get_battery_life_extender = 0xFFFF, 193 .set_battery_life_extender = 0xFFFF, 194 195 .get_usb_charge = 0xFFFF, 196 .set_usb_charge = 0xFFFF, 197 198 .get_wireless_status = 0xFFFF, 199 .set_wireless_status = 0xFFFF, 200 201 .get_lid_handling = 0xFFFF, 202 .set_lid_handling = 0xFFFF, 203 204 .kbd_backlight = 0xFFFF, 205 206 .set_linux = 0x0a, 207 }, 208 209 .performance_levels = { 210 { 211 .name = "silent", 212 .value = 0, 213 }, 214 { 215 .name = "normal", 216 .value = 1, 217 }, 218 { }, 219 }, 220 .min_brightness = 1, 221 .max_brightness = 8, 222 }, 223 { 224 .sabi_version = 3, 225 226 .test_string = "SwSmi@", 227 228 .main_function = 0x5843, 229 230 .header_offsets = { 231 .port = 0x00, 232 .re_mem = 0x04, 233 .iface_func = 0x02, 234 .en_mem = 0x03, 235 .data_offset = 0x05, 236 .data_segment = 0x07, 237 }, 238 239 .commands = { 240 .get_brightness = 0x10, 241 .set_brightness = 0x11, 242 243 .get_wireless_button = 0x12, 244 .set_wireless_button = 0x13, 245 246 .get_backlight = 0x2d, 247 .set_backlight = 0x2e, 248 249 .get_recovery_mode = 0xff, 250 .set_recovery_mode = 0xff, 251 252 .get_performance_level = 0x31, 253 .set_performance_level = 0x32, 254 255 .get_battery_life_extender = 0x65, 256 .set_battery_life_extender = 0x66, 257 258 .get_usb_charge = 0x67, 259 .set_usb_charge = 0x68, 260 261 .get_wireless_status = 0x69, 262 .set_wireless_status = 0x6a, 263 264 .get_lid_handling = 0x6d, 265 .set_lid_handling = 0x6e, 266 267 .kbd_backlight = 0x78, 268 269 .set_linux = 0xff, 270 }, 271 272 .performance_levels = { 273 { 274 .name = "normal", 275 .value = 0, 276 }, 277 { 278 .name = "silent", 279 .value = 1, 280 }, 281 { 282 .name = "overclock", 283 .value = 2, 284 }, 285 { }, 286 }, 287 .min_brightness = 0, 288 .max_brightness = 8, 289 }, 290 { }, 291 }; 292 293 /* 294 * samsung-laptop/ - debugfs root directory 295 * f0000_segment - dump f0000 segment 296 * command - current command 297 * data - current data 298 * d0, d1, d2, d3 - data fields 299 * call - call SABI using command and data 300 * 301 * This allow to call arbitrary sabi commands wihout 302 * modifying the driver at all. 303 * For example, setting the keyboard backlight brightness to 5 304 * 305 * echo 0x78 > command 306 * echo 0x0582 > d0 307 * echo 0 > d1 308 * echo 0 > d2 309 * echo 0 > d3 310 * cat call 311 */ 312 313 struct samsung_laptop_debug { 314 struct dentry *root; 315 struct sabi_data data; 316 u16 command; 317 318 struct debugfs_blob_wrapper f0000_wrapper; 319 struct debugfs_blob_wrapper data_wrapper; 320 struct debugfs_blob_wrapper sdiag_wrapper; 321 }; 322 323 struct samsung_laptop; 324 325 struct samsung_rfkill { 326 struct samsung_laptop *samsung; 327 struct rfkill *rfkill; 328 enum rfkill_type type; 329 }; 330 331 struct samsung_laptop { 332 const struct sabi_config *config; 333 334 void __iomem *sabi; 335 void __iomem *sabi_iface; 336 void __iomem *f0000_segment; 337 338 struct mutex sabi_mutex; 339 340 struct platform_device *platform_device; 341 struct backlight_device *backlight_device; 342 343 struct samsung_rfkill wlan; 344 struct samsung_rfkill bluetooth; 345 346 struct led_classdev kbd_led; 347 int kbd_led_wk; 348 struct workqueue_struct *led_workqueue; 349 struct work_struct kbd_led_work; 350 351 struct samsung_laptop_debug debug; 352 struct samsung_quirks *quirks; 353 354 struct notifier_block pm_nb; 355 356 bool handle_backlight; 357 bool has_stepping_quirk; 358 359 char sdiag[64]; 360 }; 361 362 struct samsung_quirks { 363 bool broken_acpi_video; 364 bool four_kbd_backlight_levels; 365 bool enable_kbd_backlight; 366 bool use_native_backlight; 367 bool lid_handling; 368 }; 369 370 static struct samsung_quirks samsung_unknown = {}; 371 372 static struct samsung_quirks samsung_broken_acpi_video = { 373 .broken_acpi_video = true, 374 }; 375 376 static struct samsung_quirks samsung_use_native_backlight = { 377 .use_native_backlight = true, 378 }; 379 380 static struct samsung_quirks samsung_np740u3e = { 381 .four_kbd_backlight_levels = true, 382 .enable_kbd_backlight = true, 383 }; 384 385 static struct samsung_quirks samsung_lid_handling = { 386 .lid_handling = true, 387 }; 388 389 static bool force; 390 module_param(force, bool, 0); 391 MODULE_PARM_DESC(force, 392 "Disable the DMI check and forces the driver to be loaded"); 393 394 static bool debug; 395 module_param(debug, bool, S_IRUGO | S_IWUSR); 396 MODULE_PARM_DESC(debug, "Debug enabled or not"); 397 398 static int sabi_command(struct samsung_laptop *samsung, u16 command, 399 struct sabi_data *in, 400 struct sabi_data *out) 401 { 402 const struct sabi_config *config = samsung->config; 403 int ret = 0; 404 u16 port = readw(samsung->sabi + config->header_offsets.port); 405 u8 complete, iface_data; 406 407 mutex_lock(&samsung->sabi_mutex); 408 409 if (debug) { 410 if (in) 411 pr_info("SABI command:0x%04x " 412 "data:{0x%08x, 0x%08x, 0x%04x, 0x%02x}", 413 command, in->d0, in->d1, in->d2, in->d3); 414 else 415 pr_info("SABI command:0x%04x", command); 416 } 417 418 /* enable memory to be able to write to it */ 419 outb(readb(samsung->sabi + config->header_offsets.en_mem), port); 420 421 /* write out the command */ 422 writew(config->main_function, samsung->sabi_iface + SABI_IFACE_MAIN); 423 writew(command, samsung->sabi_iface + SABI_IFACE_SUB); 424 writeb(0, samsung->sabi_iface + SABI_IFACE_COMPLETE); 425 if (in) { 426 writel(in->d0, samsung->sabi_iface + SABI_IFACE_DATA); 427 writel(in->d1, samsung->sabi_iface + SABI_IFACE_DATA + 4); 428 writew(in->d2, samsung->sabi_iface + SABI_IFACE_DATA + 8); 429 writeb(in->d3, samsung->sabi_iface + SABI_IFACE_DATA + 10); 430 } 431 outb(readb(samsung->sabi + config->header_offsets.iface_func), port); 432 433 /* write protect memory to make it safe */ 434 outb(readb(samsung->sabi + config->header_offsets.re_mem), port); 435 436 /* see if the command actually succeeded */ 437 complete = readb(samsung->sabi_iface + SABI_IFACE_COMPLETE); 438 iface_data = readb(samsung->sabi_iface + SABI_IFACE_DATA); 439 440 /* iface_data = 0xFF happens when a command is not known 441 * so we only add a warning in debug mode since we will 442 * probably issue some unknown command at startup to find 443 * out which features are supported */ 444 if (complete != 0xaa || (iface_data == 0xff && debug)) 445 pr_warn("SABI command 0x%04x failed with" 446 " completion flag 0x%02x and interface data 0x%02x", 447 command, complete, iface_data); 448 449 if (complete != 0xaa || iface_data == 0xff) { 450 ret = -EINVAL; 451 goto exit; 452 } 453 454 if (out) { 455 out->d0 = readl(samsung->sabi_iface + SABI_IFACE_DATA); 456 out->d1 = readl(samsung->sabi_iface + SABI_IFACE_DATA + 4); 457 out->d2 = readw(samsung->sabi_iface + SABI_IFACE_DATA + 2); 458 out->d3 = readb(samsung->sabi_iface + SABI_IFACE_DATA + 1); 459 } 460 461 if (debug && out) { 462 pr_info("SABI return data:{0x%08x, 0x%08x, 0x%04x, 0x%02x}", 463 out->d0, out->d1, out->d2, out->d3); 464 } 465 466 exit: 467 mutex_unlock(&samsung->sabi_mutex); 468 return ret; 469 } 470 471 /* simple wrappers usable with most commands */ 472 static int sabi_set_commandb(struct samsung_laptop *samsung, 473 u16 command, u8 data) 474 { 475 struct sabi_data in = { { { .d0 = 0, .d1 = 0, .d2 = 0, .d3 = 0 } } }; 476 477 in.data[0] = data; 478 return sabi_command(samsung, command, &in, NULL); 479 } 480 481 static int read_brightness(struct samsung_laptop *samsung) 482 { 483 const struct sabi_config *config = samsung->config; 484 const struct sabi_commands *commands = &samsung->config->commands; 485 struct sabi_data sretval; 486 int user_brightness = 0; 487 int retval; 488 489 retval = sabi_command(samsung, commands->get_brightness, 490 NULL, &sretval); 491 if (retval) 492 return retval; 493 494 user_brightness = sretval.data[0]; 495 if (user_brightness > config->min_brightness) 496 user_brightness -= config->min_brightness; 497 else 498 user_brightness = 0; 499 500 return user_brightness; 501 } 502 503 static void set_brightness(struct samsung_laptop *samsung, u8 user_brightness) 504 { 505 const struct sabi_config *config = samsung->config; 506 const struct sabi_commands *commands = &samsung->config->commands; 507 u8 user_level = user_brightness + config->min_brightness; 508 509 if (samsung->has_stepping_quirk && user_level != 0) { 510 /* 511 * short circuit if the specified level is what's already set 512 * to prevent the screen from flickering needlessly 513 */ 514 if (user_brightness == read_brightness(samsung)) 515 return; 516 517 sabi_set_commandb(samsung, commands->set_brightness, 0); 518 } 519 520 sabi_set_commandb(samsung, commands->set_brightness, user_level); 521 } 522 523 static int get_brightness(struct backlight_device *bd) 524 { 525 struct samsung_laptop *samsung = bl_get_data(bd); 526 527 return read_brightness(samsung); 528 } 529 530 static void check_for_stepping_quirk(struct samsung_laptop *samsung) 531 { 532 int initial_level; 533 int check_level; 534 int orig_level = read_brightness(samsung); 535 536 /* 537 * Some laptops exhibit the strange behaviour of stepping toward 538 * (rather than setting) the brightness except when changing to/from 539 * brightness level 0. This behaviour is checked for here and worked 540 * around in set_brightness. 541 */ 542 543 if (orig_level == 0) 544 set_brightness(samsung, 1); 545 546 initial_level = read_brightness(samsung); 547 548 if (initial_level <= 2) 549 check_level = initial_level + 2; 550 else 551 check_level = initial_level - 2; 552 553 samsung->has_stepping_quirk = false; 554 set_brightness(samsung, check_level); 555 556 if (read_brightness(samsung) != check_level) { 557 samsung->has_stepping_quirk = true; 558 pr_info("enabled workaround for brightness stepping quirk\n"); 559 } 560 561 set_brightness(samsung, orig_level); 562 } 563 564 static int update_status(struct backlight_device *bd) 565 { 566 struct samsung_laptop *samsung = bl_get_data(bd); 567 const struct sabi_commands *commands = &samsung->config->commands; 568 569 set_brightness(samsung, bd->props.brightness); 570 571 if (bd->props.power == FB_BLANK_UNBLANK) 572 sabi_set_commandb(samsung, commands->set_backlight, 1); 573 else 574 sabi_set_commandb(samsung, commands->set_backlight, 0); 575 576 return 0; 577 } 578 579 static const struct backlight_ops backlight_ops = { 580 .get_brightness = get_brightness, 581 .update_status = update_status, 582 }; 583 584 static int seclinux_rfkill_set(void *data, bool blocked) 585 { 586 struct samsung_rfkill *srfkill = data; 587 struct samsung_laptop *samsung = srfkill->samsung; 588 const struct sabi_commands *commands = &samsung->config->commands; 589 590 return sabi_set_commandb(samsung, commands->set_wireless_button, 591 !blocked); 592 } 593 594 static const struct rfkill_ops seclinux_rfkill_ops = { 595 .set_block = seclinux_rfkill_set, 596 }; 597 598 static int swsmi_wireless_status(struct samsung_laptop *samsung, 599 struct sabi_data *data) 600 { 601 const struct sabi_commands *commands = &samsung->config->commands; 602 603 return sabi_command(samsung, commands->get_wireless_status, 604 NULL, data); 605 } 606 607 static int swsmi_rfkill_set(void *priv, bool blocked) 608 { 609 struct samsung_rfkill *srfkill = priv; 610 struct samsung_laptop *samsung = srfkill->samsung; 611 const struct sabi_commands *commands = &samsung->config->commands; 612 struct sabi_data data; 613 int ret, i; 614 615 ret = swsmi_wireless_status(samsung, &data); 616 if (ret) 617 return ret; 618 619 /* Don't set the state for non-present devices */ 620 for (i = 0; i < 4; i++) 621 if (data.data[i] == 0x02) 622 data.data[1] = 0; 623 624 if (srfkill->type == RFKILL_TYPE_WLAN) 625 data.data[WL_STATUS_WLAN] = !blocked; 626 else if (srfkill->type == RFKILL_TYPE_BLUETOOTH) 627 data.data[WL_STATUS_BT] = !blocked; 628 629 return sabi_command(samsung, commands->set_wireless_status, 630 &data, &data); 631 } 632 633 static void swsmi_rfkill_query(struct rfkill *rfkill, void *priv) 634 { 635 struct samsung_rfkill *srfkill = priv; 636 struct samsung_laptop *samsung = srfkill->samsung; 637 struct sabi_data data; 638 int ret; 639 640 ret = swsmi_wireless_status(samsung, &data); 641 if (ret) 642 return ; 643 644 if (srfkill->type == RFKILL_TYPE_WLAN) 645 ret = data.data[WL_STATUS_WLAN]; 646 else if (srfkill->type == RFKILL_TYPE_BLUETOOTH) 647 ret = data.data[WL_STATUS_BT]; 648 else 649 return ; 650 651 rfkill_set_sw_state(rfkill, !ret); 652 } 653 654 static const struct rfkill_ops swsmi_rfkill_ops = { 655 .set_block = swsmi_rfkill_set, 656 .query = swsmi_rfkill_query, 657 }; 658 659 static ssize_t get_performance_level(struct device *dev, 660 struct device_attribute *attr, char *buf) 661 { 662 struct samsung_laptop *samsung = dev_get_drvdata(dev); 663 const struct sabi_config *config = samsung->config; 664 const struct sabi_commands *commands = &config->commands; 665 struct sabi_data sretval; 666 int retval; 667 int i; 668 669 /* Read the state */ 670 retval = sabi_command(samsung, commands->get_performance_level, 671 NULL, &sretval); 672 if (retval) 673 return retval; 674 675 /* The logic is backwards, yeah, lots of fun... */ 676 for (i = 0; config->performance_levels[i].name; ++i) { 677 if (sretval.data[0] == config->performance_levels[i].value) 678 return sprintf(buf, "%s\n", config->performance_levels[i].name); 679 } 680 return sprintf(buf, "%s\n", "unknown"); 681 } 682 683 static ssize_t set_performance_level(struct device *dev, 684 struct device_attribute *attr, const char *buf, 685 size_t count) 686 { 687 struct samsung_laptop *samsung = dev_get_drvdata(dev); 688 const struct sabi_config *config = samsung->config; 689 const struct sabi_commands *commands = &config->commands; 690 int i; 691 692 if (count < 1) 693 return count; 694 695 for (i = 0; config->performance_levels[i].name; ++i) { 696 const struct sabi_performance_level *level = 697 &config->performance_levels[i]; 698 if (!strncasecmp(level->name, buf, strlen(level->name))) { 699 sabi_set_commandb(samsung, 700 commands->set_performance_level, 701 level->value); 702 break; 703 } 704 } 705 706 if (!config->performance_levels[i].name) 707 return -EINVAL; 708 709 return count; 710 } 711 712 static DEVICE_ATTR(performance_level, S_IWUSR | S_IRUGO, 713 get_performance_level, set_performance_level); 714 715 static int read_battery_life_extender(struct samsung_laptop *samsung) 716 { 717 const struct sabi_commands *commands = &samsung->config->commands; 718 struct sabi_data data; 719 int retval; 720 721 if (commands->get_battery_life_extender == 0xFFFF) 722 return -ENODEV; 723 724 memset(&data, 0, sizeof(data)); 725 data.data[0] = 0x80; 726 retval = sabi_command(samsung, commands->get_battery_life_extender, 727 &data, &data); 728 729 if (retval) 730 return retval; 731 732 if (data.data[0] != 0 && data.data[0] != 1) 733 return -ENODEV; 734 735 return data.data[0]; 736 } 737 738 static int write_battery_life_extender(struct samsung_laptop *samsung, 739 int enabled) 740 { 741 const struct sabi_commands *commands = &samsung->config->commands; 742 struct sabi_data data; 743 744 memset(&data, 0, sizeof(data)); 745 data.data[0] = 0x80 | enabled; 746 return sabi_command(samsung, commands->set_battery_life_extender, 747 &data, NULL); 748 } 749 750 static ssize_t get_battery_life_extender(struct device *dev, 751 struct device_attribute *attr, 752 char *buf) 753 { 754 struct samsung_laptop *samsung = dev_get_drvdata(dev); 755 int ret; 756 757 ret = read_battery_life_extender(samsung); 758 if (ret < 0) 759 return ret; 760 761 return sprintf(buf, "%d\n", ret); 762 } 763 764 static ssize_t set_battery_life_extender(struct device *dev, 765 struct device_attribute *attr, 766 const char *buf, size_t count) 767 { 768 struct samsung_laptop *samsung = dev_get_drvdata(dev); 769 int ret, value; 770 771 if (!count || kstrtoint(buf, 0, &value) != 0) 772 return -EINVAL; 773 774 ret = write_battery_life_extender(samsung, !!value); 775 if (ret < 0) 776 return ret; 777 778 return count; 779 } 780 781 static DEVICE_ATTR(battery_life_extender, S_IWUSR | S_IRUGO, 782 get_battery_life_extender, set_battery_life_extender); 783 784 static int read_usb_charge(struct samsung_laptop *samsung) 785 { 786 const struct sabi_commands *commands = &samsung->config->commands; 787 struct sabi_data data; 788 int retval; 789 790 if (commands->get_usb_charge == 0xFFFF) 791 return -ENODEV; 792 793 memset(&data, 0, sizeof(data)); 794 data.data[0] = 0x80; 795 retval = sabi_command(samsung, commands->get_usb_charge, 796 &data, &data); 797 798 if (retval) 799 return retval; 800 801 if (data.data[0] != 0 && data.data[0] != 1) 802 return -ENODEV; 803 804 return data.data[0]; 805 } 806 807 static int write_usb_charge(struct samsung_laptop *samsung, 808 int enabled) 809 { 810 const struct sabi_commands *commands = &samsung->config->commands; 811 struct sabi_data data; 812 813 memset(&data, 0, sizeof(data)); 814 data.data[0] = 0x80 | enabled; 815 return sabi_command(samsung, commands->set_usb_charge, 816 &data, NULL); 817 } 818 819 static ssize_t get_usb_charge(struct device *dev, 820 struct device_attribute *attr, 821 char *buf) 822 { 823 struct samsung_laptop *samsung = dev_get_drvdata(dev); 824 int ret; 825 826 ret = read_usb_charge(samsung); 827 if (ret < 0) 828 return ret; 829 830 return sprintf(buf, "%d\n", ret); 831 } 832 833 static ssize_t set_usb_charge(struct device *dev, 834 struct device_attribute *attr, 835 const char *buf, size_t count) 836 { 837 struct samsung_laptop *samsung = dev_get_drvdata(dev); 838 int ret, value; 839 840 if (!count || kstrtoint(buf, 0, &value) != 0) 841 return -EINVAL; 842 843 ret = write_usb_charge(samsung, !!value); 844 if (ret < 0) 845 return ret; 846 847 return count; 848 } 849 850 static DEVICE_ATTR(usb_charge, S_IWUSR | S_IRUGO, 851 get_usb_charge, set_usb_charge); 852 853 static int read_lid_handling(struct samsung_laptop *samsung) 854 { 855 const struct sabi_commands *commands = &samsung->config->commands; 856 struct sabi_data data; 857 int retval; 858 859 if (commands->get_lid_handling == 0xFFFF) 860 return -ENODEV; 861 862 memset(&data, 0, sizeof(data)); 863 retval = sabi_command(samsung, commands->get_lid_handling, 864 &data, &data); 865 866 if (retval) 867 return retval; 868 869 return data.data[0] & 0x1; 870 } 871 872 static int write_lid_handling(struct samsung_laptop *samsung, 873 int enabled) 874 { 875 const struct sabi_commands *commands = &samsung->config->commands; 876 struct sabi_data data; 877 878 memset(&data, 0, sizeof(data)); 879 data.data[0] = 0x80 | enabled; 880 return sabi_command(samsung, commands->set_lid_handling, 881 &data, NULL); 882 } 883 884 static ssize_t get_lid_handling(struct device *dev, 885 struct device_attribute *attr, 886 char *buf) 887 { 888 struct samsung_laptop *samsung = dev_get_drvdata(dev); 889 int ret; 890 891 ret = read_lid_handling(samsung); 892 if (ret < 0) 893 return ret; 894 895 return sprintf(buf, "%d\n", ret); 896 } 897 898 static ssize_t set_lid_handling(struct device *dev, 899 struct device_attribute *attr, 900 const char *buf, size_t count) 901 { 902 struct samsung_laptop *samsung = dev_get_drvdata(dev); 903 int ret, value; 904 905 if (!count || kstrtoint(buf, 0, &value) != 0) 906 return -EINVAL; 907 908 ret = write_lid_handling(samsung, !!value); 909 if (ret < 0) 910 return ret; 911 912 return count; 913 } 914 915 static DEVICE_ATTR(lid_handling, S_IWUSR | S_IRUGO, 916 get_lid_handling, set_lid_handling); 917 918 static struct attribute *platform_attributes[] = { 919 &dev_attr_performance_level.attr, 920 &dev_attr_battery_life_extender.attr, 921 &dev_attr_usb_charge.attr, 922 &dev_attr_lid_handling.attr, 923 NULL 924 }; 925 926 static int find_signature(void __iomem *memcheck, const char *testStr) 927 { 928 int i = 0; 929 int loca; 930 931 for (loca = 0; loca < 0xffff; loca++) { 932 char temp = readb(memcheck + loca); 933 934 if (temp == testStr[i]) { 935 if (i == strlen(testStr)-1) 936 break; 937 ++i; 938 } else { 939 i = 0; 940 } 941 } 942 return loca; 943 } 944 945 static void samsung_rfkill_exit(struct samsung_laptop *samsung) 946 { 947 if (samsung->wlan.rfkill) { 948 rfkill_unregister(samsung->wlan.rfkill); 949 rfkill_destroy(samsung->wlan.rfkill); 950 samsung->wlan.rfkill = NULL; 951 } 952 if (samsung->bluetooth.rfkill) { 953 rfkill_unregister(samsung->bluetooth.rfkill); 954 rfkill_destroy(samsung->bluetooth.rfkill); 955 samsung->bluetooth.rfkill = NULL; 956 } 957 } 958 959 static int samsung_new_rfkill(struct samsung_laptop *samsung, 960 struct samsung_rfkill *arfkill, 961 const char *name, enum rfkill_type type, 962 const struct rfkill_ops *ops, 963 int blocked) 964 { 965 struct rfkill **rfkill = &arfkill->rfkill; 966 int ret; 967 968 arfkill->type = type; 969 arfkill->samsung = samsung; 970 971 *rfkill = rfkill_alloc(name, &samsung->platform_device->dev, 972 type, ops, arfkill); 973 974 if (!*rfkill) 975 return -EINVAL; 976 977 if (blocked != -1) 978 rfkill_init_sw_state(*rfkill, blocked); 979 980 ret = rfkill_register(*rfkill); 981 if (ret) { 982 rfkill_destroy(*rfkill); 983 *rfkill = NULL; 984 return ret; 985 } 986 return 0; 987 } 988 989 static int __init samsung_rfkill_init_seclinux(struct samsung_laptop *samsung) 990 { 991 return samsung_new_rfkill(samsung, &samsung->wlan, "samsung-wlan", 992 RFKILL_TYPE_WLAN, &seclinux_rfkill_ops, -1); 993 } 994 995 static int __init samsung_rfkill_init_swsmi(struct samsung_laptop *samsung) 996 { 997 struct sabi_data data; 998 int ret; 999 1000 ret = swsmi_wireless_status(samsung, &data); 1001 if (ret) { 1002 /* Some swsmi laptops use the old seclinux way to control 1003 * wireless devices */ 1004 if (ret == -EINVAL) 1005 ret = samsung_rfkill_init_seclinux(samsung); 1006 return ret; 1007 } 1008 1009 /* 0x02 seems to mean that the device is no present/available */ 1010 1011 if (data.data[WL_STATUS_WLAN] != 0x02) 1012 ret = samsung_new_rfkill(samsung, &samsung->wlan, 1013 "samsung-wlan", 1014 RFKILL_TYPE_WLAN, 1015 &swsmi_rfkill_ops, 1016 !data.data[WL_STATUS_WLAN]); 1017 if (ret) 1018 goto exit; 1019 1020 if (data.data[WL_STATUS_BT] != 0x02) 1021 ret = samsung_new_rfkill(samsung, &samsung->bluetooth, 1022 "samsung-bluetooth", 1023 RFKILL_TYPE_BLUETOOTH, 1024 &swsmi_rfkill_ops, 1025 !data.data[WL_STATUS_BT]); 1026 if (ret) 1027 goto exit; 1028 1029 exit: 1030 if (ret) 1031 samsung_rfkill_exit(samsung); 1032 1033 return ret; 1034 } 1035 1036 static int __init samsung_rfkill_init(struct samsung_laptop *samsung) 1037 { 1038 if (samsung->config->sabi_version == 2) 1039 return samsung_rfkill_init_seclinux(samsung); 1040 if (samsung->config->sabi_version == 3) 1041 return samsung_rfkill_init_swsmi(samsung); 1042 return 0; 1043 } 1044 1045 static void samsung_lid_handling_exit(struct samsung_laptop *samsung) 1046 { 1047 if (samsung->quirks->lid_handling) 1048 write_lid_handling(samsung, 0); 1049 } 1050 1051 static int __init samsung_lid_handling_init(struct samsung_laptop *samsung) 1052 { 1053 int retval = 0; 1054 1055 if (samsung->quirks->lid_handling) 1056 retval = write_lid_handling(samsung, 1); 1057 1058 return retval; 1059 } 1060 1061 static int kbd_backlight_enable(struct samsung_laptop *samsung) 1062 { 1063 const struct sabi_commands *commands = &samsung->config->commands; 1064 struct sabi_data data; 1065 int retval; 1066 1067 if (commands->kbd_backlight == 0xFFFF) 1068 return -ENODEV; 1069 1070 memset(&data, 0, sizeof(data)); 1071 data.d0 = 0xaabb; 1072 retval = sabi_command(samsung, commands->kbd_backlight, 1073 &data, &data); 1074 1075 if (retval) 1076 return retval; 1077 1078 if (data.d0 != 0xccdd) 1079 return -ENODEV; 1080 return 0; 1081 } 1082 1083 static int kbd_backlight_read(struct samsung_laptop *samsung) 1084 { 1085 const struct sabi_commands *commands = &samsung->config->commands; 1086 struct sabi_data data; 1087 int retval; 1088 1089 memset(&data, 0, sizeof(data)); 1090 data.data[0] = 0x81; 1091 retval = sabi_command(samsung, commands->kbd_backlight, 1092 &data, &data); 1093 1094 if (retval) 1095 return retval; 1096 1097 return data.data[0]; 1098 } 1099 1100 static int kbd_backlight_write(struct samsung_laptop *samsung, int brightness) 1101 { 1102 const struct sabi_commands *commands = &samsung->config->commands; 1103 struct sabi_data data; 1104 1105 memset(&data, 0, sizeof(data)); 1106 data.d0 = 0x82 | ((brightness & 0xFF) << 8); 1107 return sabi_command(samsung, commands->kbd_backlight, 1108 &data, NULL); 1109 } 1110 1111 static void kbd_led_update(struct work_struct *work) 1112 { 1113 struct samsung_laptop *samsung; 1114 1115 samsung = container_of(work, struct samsung_laptop, kbd_led_work); 1116 kbd_backlight_write(samsung, samsung->kbd_led_wk); 1117 } 1118 1119 static void kbd_led_set(struct led_classdev *led_cdev, 1120 enum led_brightness value) 1121 { 1122 struct samsung_laptop *samsung; 1123 1124 samsung = container_of(led_cdev, struct samsung_laptop, kbd_led); 1125 1126 if (value > samsung->kbd_led.max_brightness) 1127 value = samsung->kbd_led.max_brightness; 1128 else if (value < 0) 1129 value = 0; 1130 1131 samsung->kbd_led_wk = value; 1132 queue_work(samsung->led_workqueue, &samsung->kbd_led_work); 1133 } 1134 1135 static enum led_brightness kbd_led_get(struct led_classdev *led_cdev) 1136 { 1137 struct samsung_laptop *samsung; 1138 1139 samsung = container_of(led_cdev, struct samsung_laptop, kbd_led); 1140 return kbd_backlight_read(samsung); 1141 } 1142 1143 static void samsung_leds_exit(struct samsung_laptop *samsung) 1144 { 1145 if (!IS_ERR_OR_NULL(samsung->kbd_led.dev)) 1146 led_classdev_unregister(&samsung->kbd_led); 1147 if (samsung->led_workqueue) 1148 destroy_workqueue(samsung->led_workqueue); 1149 } 1150 1151 static int __init samsung_leds_init(struct samsung_laptop *samsung) 1152 { 1153 int ret = 0; 1154 1155 samsung->led_workqueue = create_singlethread_workqueue("led_workqueue"); 1156 if (!samsung->led_workqueue) 1157 return -ENOMEM; 1158 1159 if (kbd_backlight_enable(samsung) >= 0) { 1160 INIT_WORK(&samsung->kbd_led_work, kbd_led_update); 1161 1162 samsung->kbd_led.name = "samsung::kbd_backlight"; 1163 samsung->kbd_led.brightness_set = kbd_led_set; 1164 samsung->kbd_led.brightness_get = kbd_led_get; 1165 samsung->kbd_led.max_brightness = 8; 1166 if (samsung->quirks->four_kbd_backlight_levels) 1167 samsung->kbd_led.max_brightness = 4; 1168 1169 ret = led_classdev_register(&samsung->platform_device->dev, 1170 &samsung->kbd_led); 1171 } 1172 1173 if (ret) 1174 samsung_leds_exit(samsung); 1175 1176 return ret; 1177 } 1178 1179 static void samsung_backlight_exit(struct samsung_laptop *samsung) 1180 { 1181 if (samsung->backlight_device) { 1182 backlight_device_unregister(samsung->backlight_device); 1183 samsung->backlight_device = NULL; 1184 } 1185 } 1186 1187 static int __init samsung_backlight_init(struct samsung_laptop *samsung) 1188 { 1189 struct backlight_device *bd; 1190 struct backlight_properties props; 1191 1192 if (!samsung->handle_backlight) 1193 return 0; 1194 1195 memset(&props, 0, sizeof(struct backlight_properties)); 1196 props.type = BACKLIGHT_PLATFORM; 1197 props.max_brightness = samsung->config->max_brightness - 1198 samsung->config->min_brightness; 1199 1200 bd = backlight_device_register("samsung", 1201 &samsung->platform_device->dev, 1202 samsung, &backlight_ops, 1203 &props); 1204 if (IS_ERR(bd)) 1205 return PTR_ERR(bd); 1206 1207 samsung->backlight_device = bd; 1208 samsung->backlight_device->props.brightness = read_brightness(samsung); 1209 samsung->backlight_device->props.power = FB_BLANK_UNBLANK; 1210 backlight_update_status(samsung->backlight_device); 1211 1212 return 0; 1213 } 1214 1215 static umode_t samsung_sysfs_is_visible(struct kobject *kobj, 1216 struct attribute *attr, int idx) 1217 { 1218 struct device *dev = container_of(kobj, struct device, kobj); 1219 struct samsung_laptop *samsung = dev_get_drvdata(dev); 1220 bool ok = true; 1221 1222 if (attr == &dev_attr_performance_level.attr) 1223 ok = !!samsung->config->performance_levels[0].name; 1224 if (attr == &dev_attr_battery_life_extender.attr) 1225 ok = !!(read_battery_life_extender(samsung) >= 0); 1226 if (attr == &dev_attr_usb_charge.attr) 1227 ok = !!(read_usb_charge(samsung) >= 0); 1228 if (attr == &dev_attr_lid_handling.attr) 1229 ok = !!(read_lid_handling(samsung) >= 0); 1230 1231 return ok ? attr->mode : 0; 1232 } 1233 1234 static const struct attribute_group platform_attribute_group = { 1235 .is_visible = samsung_sysfs_is_visible, 1236 .attrs = platform_attributes 1237 }; 1238 1239 static void samsung_sysfs_exit(struct samsung_laptop *samsung) 1240 { 1241 struct platform_device *device = samsung->platform_device; 1242 1243 sysfs_remove_group(&device->dev.kobj, &platform_attribute_group); 1244 } 1245 1246 static int __init samsung_sysfs_init(struct samsung_laptop *samsung) 1247 { 1248 struct platform_device *device = samsung->platform_device; 1249 1250 return sysfs_create_group(&device->dev.kobj, &platform_attribute_group); 1251 1252 } 1253 1254 static int samsung_laptop_call_show(struct seq_file *m, void *data) 1255 { 1256 struct samsung_laptop *samsung = m->private; 1257 struct sabi_data *sdata = &samsung->debug.data; 1258 int ret; 1259 1260 seq_printf(m, "SABI 0x%04x {0x%08x, 0x%08x, 0x%04x, 0x%02x}\n", 1261 samsung->debug.command, 1262 sdata->d0, sdata->d1, sdata->d2, sdata->d3); 1263 1264 ret = sabi_command(samsung, samsung->debug.command, sdata, sdata); 1265 1266 if (ret) { 1267 seq_printf(m, "SABI command 0x%04x failed\n", 1268 samsung->debug.command); 1269 return ret; 1270 } 1271 1272 seq_printf(m, "SABI {0x%08x, 0x%08x, 0x%04x, 0x%02x}\n", 1273 sdata->d0, sdata->d1, sdata->d2, sdata->d3); 1274 return 0; 1275 } 1276 DEFINE_SHOW_ATTRIBUTE(samsung_laptop_call); 1277 1278 static void samsung_debugfs_exit(struct samsung_laptop *samsung) 1279 { 1280 debugfs_remove_recursive(samsung->debug.root); 1281 } 1282 1283 static int samsung_debugfs_init(struct samsung_laptop *samsung) 1284 { 1285 struct dentry *dent; 1286 1287 samsung->debug.root = debugfs_create_dir("samsung-laptop", NULL); 1288 if (!samsung->debug.root) { 1289 pr_err("failed to create debugfs directory"); 1290 goto error_debugfs; 1291 } 1292 1293 samsung->debug.f0000_wrapper.data = samsung->f0000_segment; 1294 samsung->debug.f0000_wrapper.size = 0xffff; 1295 1296 samsung->debug.data_wrapper.data = &samsung->debug.data; 1297 samsung->debug.data_wrapper.size = sizeof(samsung->debug.data); 1298 1299 samsung->debug.sdiag_wrapper.data = samsung->sdiag; 1300 samsung->debug.sdiag_wrapper.size = strlen(samsung->sdiag); 1301 1302 dent = debugfs_create_u16("command", S_IRUGO | S_IWUSR, 1303 samsung->debug.root, &samsung->debug.command); 1304 if (!dent) 1305 goto error_debugfs; 1306 1307 dent = debugfs_create_u32("d0", S_IRUGO | S_IWUSR, samsung->debug.root, 1308 &samsung->debug.data.d0); 1309 if (!dent) 1310 goto error_debugfs; 1311 1312 dent = debugfs_create_u32("d1", S_IRUGO | S_IWUSR, samsung->debug.root, 1313 &samsung->debug.data.d1); 1314 if (!dent) 1315 goto error_debugfs; 1316 1317 dent = debugfs_create_u16("d2", S_IRUGO | S_IWUSR, samsung->debug.root, 1318 &samsung->debug.data.d2); 1319 if (!dent) 1320 goto error_debugfs; 1321 1322 dent = debugfs_create_u8("d3", S_IRUGO | S_IWUSR, samsung->debug.root, 1323 &samsung->debug.data.d3); 1324 if (!dent) 1325 goto error_debugfs; 1326 1327 dent = debugfs_create_blob("data", S_IRUGO | S_IWUSR, 1328 samsung->debug.root, 1329 &samsung->debug.data_wrapper); 1330 if (!dent) 1331 goto error_debugfs; 1332 1333 dent = debugfs_create_blob("f0000_segment", S_IRUSR | S_IWUSR, 1334 samsung->debug.root, 1335 &samsung->debug.f0000_wrapper); 1336 if (!dent) 1337 goto error_debugfs; 1338 1339 dent = debugfs_create_file("call", S_IFREG | S_IRUGO, 1340 samsung->debug.root, samsung, 1341 &samsung_laptop_call_fops); 1342 if (!dent) 1343 goto error_debugfs; 1344 1345 dent = debugfs_create_blob("sdiag", S_IRUGO | S_IWUSR, 1346 samsung->debug.root, 1347 &samsung->debug.sdiag_wrapper); 1348 if (!dent) 1349 goto error_debugfs; 1350 1351 return 0; 1352 1353 error_debugfs: 1354 samsung_debugfs_exit(samsung); 1355 return -ENOMEM; 1356 } 1357 1358 static void samsung_sabi_exit(struct samsung_laptop *samsung) 1359 { 1360 const struct sabi_config *config = samsung->config; 1361 1362 /* Turn off "Linux" mode in the BIOS */ 1363 if (config && config->commands.set_linux != 0xff) 1364 sabi_set_commandb(samsung, config->commands.set_linux, 0x80); 1365 1366 if (samsung->sabi_iface) { 1367 iounmap(samsung->sabi_iface); 1368 samsung->sabi_iface = NULL; 1369 } 1370 if (samsung->f0000_segment) { 1371 iounmap(samsung->f0000_segment); 1372 samsung->f0000_segment = NULL; 1373 } 1374 1375 samsung->config = NULL; 1376 } 1377 1378 static __init void samsung_sabi_infos(struct samsung_laptop *samsung, int loca, 1379 unsigned int ifaceP) 1380 { 1381 const struct sabi_config *config = samsung->config; 1382 1383 printk(KERN_DEBUG "This computer supports SABI==%x\n", 1384 loca + 0xf0000 - 6); 1385 1386 printk(KERN_DEBUG "SABI header:\n"); 1387 printk(KERN_DEBUG " SMI Port Number = 0x%04x\n", 1388 readw(samsung->sabi + config->header_offsets.port)); 1389 printk(KERN_DEBUG " SMI Interface Function = 0x%02x\n", 1390 readb(samsung->sabi + config->header_offsets.iface_func)); 1391 printk(KERN_DEBUG " SMI enable memory buffer = 0x%02x\n", 1392 readb(samsung->sabi + config->header_offsets.en_mem)); 1393 printk(KERN_DEBUG " SMI restore memory buffer = 0x%02x\n", 1394 readb(samsung->sabi + config->header_offsets.re_mem)); 1395 printk(KERN_DEBUG " SABI data offset = 0x%04x\n", 1396 readw(samsung->sabi + config->header_offsets.data_offset)); 1397 printk(KERN_DEBUG " SABI data segment = 0x%04x\n", 1398 readw(samsung->sabi + config->header_offsets.data_segment)); 1399 1400 printk(KERN_DEBUG " SABI pointer = 0x%08x\n", ifaceP); 1401 } 1402 1403 static void __init samsung_sabi_diag(struct samsung_laptop *samsung) 1404 { 1405 int loca = find_signature(samsung->f0000_segment, "SDiaG@"); 1406 int i; 1407 1408 if (loca == 0xffff) 1409 return ; 1410 1411 /* Example: 1412 * Ident: @SDiaG@686XX-N90X3A/966-SEC-07HL-S90X3A 1413 * 1414 * Product name: 90X3A 1415 * BIOS Version: 07HL 1416 */ 1417 loca += 1; 1418 for (i = 0; loca < 0xffff && i < sizeof(samsung->sdiag) - 1; loca++) { 1419 char temp = readb(samsung->f0000_segment + loca); 1420 1421 if (isalnum(temp) || temp == '/' || temp == '-') 1422 samsung->sdiag[i++] = temp; 1423 else 1424 break ; 1425 } 1426 1427 if (debug && samsung->sdiag[0]) 1428 pr_info("sdiag: %s", samsung->sdiag); 1429 } 1430 1431 static int __init samsung_sabi_init(struct samsung_laptop *samsung) 1432 { 1433 const struct sabi_config *config = NULL; 1434 const struct sabi_commands *commands; 1435 unsigned int ifaceP; 1436 int loca = 0xffff; 1437 int ret = 0; 1438 int i; 1439 1440 samsung->f0000_segment = ioremap_nocache(0xf0000, 0xffff); 1441 if (!samsung->f0000_segment) { 1442 if (debug || force) 1443 pr_err("Can't map the segment at 0xf0000\n"); 1444 ret = -EINVAL; 1445 goto exit; 1446 } 1447 1448 samsung_sabi_diag(samsung); 1449 1450 /* Try to find one of the signatures in memory to find the header */ 1451 for (i = 0; sabi_configs[i].test_string != NULL; ++i) { 1452 samsung->config = &sabi_configs[i]; 1453 loca = find_signature(samsung->f0000_segment, 1454 samsung->config->test_string); 1455 if (loca != 0xffff) 1456 break; 1457 } 1458 1459 if (loca == 0xffff) { 1460 if (debug || force) 1461 pr_err("This computer does not support SABI\n"); 1462 ret = -ENODEV; 1463 goto exit; 1464 } 1465 1466 config = samsung->config; 1467 commands = &config->commands; 1468 1469 /* point to the SMI port Number */ 1470 loca += 1; 1471 samsung->sabi = (samsung->f0000_segment + loca); 1472 1473 /* Get a pointer to the SABI Interface */ 1474 ifaceP = (readw(samsung->sabi + config->header_offsets.data_segment) & 0x0ffff) << 4; 1475 ifaceP += readw(samsung->sabi + config->header_offsets.data_offset) & 0x0ffff; 1476 1477 if (debug) 1478 samsung_sabi_infos(samsung, loca, ifaceP); 1479 1480 samsung->sabi_iface = ioremap_nocache(ifaceP, 16); 1481 if (!samsung->sabi_iface) { 1482 pr_err("Can't remap %x\n", ifaceP); 1483 ret = -EINVAL; 1484 goto exit; 1485 } 1486 1487 /* Turn on "Linux" mode in the BIOS */ 1488 if (commands->set_linux != 0xff) { 1489 int retval = sabi_set_commandb(samsung, 1490 commands->set_linux, 0x81); 1491 if (retval) { 1492 pr_warn("Linux mode was not set!\n"); 1493 ret = -ENODEV; 1494 goto exit; 1495 } 1496 } 1497 1498 /* Check for stepping quirk */ 1499 if (samsung->handle_backlight) 1500 check_for_stepping_quirk(samsung); 1501 1502 pr_info("detected SABI interface: %s\n", 1503 samsung->config->test_string); 1504 1505 exit: 1506 if (ret) 1507 samsung_sabi_exit(samsung); 1508 1509 return ret; 1510 } 1511 1512 static void samsung_platform_exit(struct samsung_laptop *samsung) 1513 { 1514 if (samsung->platform_device) { 1515 platform_device_unregister(samsung->platform_device); 1516 samsung->platform_device = NULL; 1517 } 1518 } 1519 1520 static int samsung_pm_notification(struct notifier_block *nb, 1521 unsigned long val, void *ptr) 1522 { 1523 struct samsung_laptop *samsung; 1524 1525 samsung = container_of(nb, struct samsung_laptop, pm_nb); 1526 if (val == PM_POST_HIBERNATION && 1527 samsung->quirks->enable_kbd_backlight) 1528 kbd_backlight_enable(samsung); 1529 1530 if (val == PM_POST_HIBERNATION && samsung->quirks->lid_handling) 1531 write_lid_handling(samsung, 1); 1532 1533 return 0; 1534 } 1535 1536 static int __init samsung_platform_init(struct samsung_laptop *samsung) 1537 { 1538 struct platform_device *pdev; 1539 1540 pdev = platform_device_register_simple("samsung", -1, NULL, 0); 1541 if (IS_ERR(pdev)) 1542 return PTR_ERR(pdev); 1543 1544 samsung->platform_device = pdev; 1545 platform_set_drvdata(samsung->platform_device, samsung); 1546 return 0; 1547 } 1548 1549 static struct samsung_quirks *quirks; 1550 1551 static int __init samsung_dmi_matched(const struct dmi_system_id *d) 1552 { 1553 quirks = d->driver_data; 1554 return 0; 1555 } 1556 1557 static const struct dmi_system_id samsung_dmi_table[] __initconst = { 1558 { 1559 .matches = { 1560 DMI_MATCH(DMI_SYS_VENDOR, 1561 "SAMSUNG ELECTRONICS CO., LTD."), 1562 DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */ 1563 }, 1564 }, 1565 { 1566 .matches = { 1567 DMI_MATCH(DMI_SYS_VENDOR, 1568 "SAMSUNG ELECTRONICS CO., LTD."), 1569 DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /* Laptop */ 1570 }, 1571 }, 1572 { 1573 .matches = { 1574 DMI_MATCH(DMI_SYS_VENDOR, 1575 "SAMSUNG ELECTRONICS CO., LTD."), 1576 DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */ 1577 }, 1578 }, 1579 { 1580 .matches = { 1581 DMI_MATCH(DMI_SYS_VENDOR, 1582 "SAMSUNG ELECTRONICS CO., LTD."), 1583 DMI_MATCH(DMI_CHASSIS_TYPE, "14"), /* Sub-Notebook */ 1584 }, 1585 }, 1586 /* DMI ids for laptops with bad Chassis Type */ 1587 { 1588 .ident = "R40/R41", 1589 .matches = { 1590 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1591 DMI_MATCH(DMI_PRODUCT_NAME, "R40/R41"), 1592 DMI_MATCH(DMI_BOARD_NAME, "R40/R41"), 1593 }, 1594 }, 1595 /* Specific DMI ids for laptop with quirks */ 1596 { 1597 .callback = samsung_dmi_matched, 1598 .ident = "N150P", 1599 .matches = { 1600 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1601 DMI_MATCH(DMI_PRODUCT_NAME, "N150P"), 1602 DMI_MATCH(DMI_BOARD_NAME, "N150P"), 1603 }, 1604 .driver_data = &samsung_use_native_backlight, 1605 }, 1606 { 1607 .callback = samsung_dmi_matched, 1608 .ident = "N145P/N250P/N260P", 1609 .matches = { 1610 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1611 DMI_MATCH(DMI_PRODUCT_NAME, "N145P/N250P/N260P"), 1612 DMI_MATCH(DMI_BOARD_NAME, "N145P/N250P/N260P"), 1613 }, 1614 .driver_data = &samsung_use_native_backlight, 1615 }, 1616 { 1617 .callback = samsung_dmi_matched, 1618 .ident = "N150/N210/N220", 1619 .matches = { 1620 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1621 DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220"), 1622 DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220"), 1623 }, 1624 .driver_data = &samsung_broken_acpi_video, 1625 }, 1626 { 1627 .callback = samsung_dmi_matched, 1628 .ident = "NF110/NF210/NF310", 1629 .matches = { 1630 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1631 DMI_MATCH(DMI_PRODUCT_NAME, "NF110/NF210/NF310"), 1632 DMI_MATCH(DMI_BOARD_NAME, "NF110/NF210/NF310"), 1633 }, 1634 .driver_data = &samsung_broken_acpi_video, 1635 }, 1636 { 1637 .callback = samsung_dmi_matched, 1638 .ident = "X360", 1639 .matches = { 1640 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1641 DMI_MATCH(DMI_PRODUCT_NAME, "X360"), 1642 DMI_MATCH(DMI_BOARD_NAME, "X360"), 1643 }, 1644 .driver_data = &samsung_broken_acpi_video, 1645 }, 1646 { 1647 .callback = samsung_dmi_matched, 1648 .ident = "N250P", 1649 .matches = { 1650 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1651 DMI_MATCH(DMI_PRODUCT_NAME, "N250P"), 1652 DMI_MATCH(DMI_BOARD_NAME, "N250P"), 1653 }, 1654 .driver_data = &samsung_use_native_backlight, 1655 }, 1656 { 1657 .callback = samsung_dmi_matched, 1658 .ident = "NC210", 1659 .matches = { 1660 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1661 DMI_MATCH(DMI_PRODUCT_NAME, "NC210/NC110"), 1662 DMI_MATCH(DMI_BOARD_NAME, "NC210/NC110"), 1663 }, 1664 .driver_data = &samsung_broken_acpi_video, 1665 }, 1666 { 1667 .callback = samsung_dmi_matched, 1668 .ident = "730U3E/740U3E", 1669 .matches = { 1670 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1671 DMI_MATCH(DMI_PRODUCT_NAME, "730U3E/740U3E"), 1672 }, 1673 .driver_data = &samsung_np740u3e, 1674 }, 1675 { 1676 .callback = samsung_dmi_matched, 1677 .ident = "300V3Z/300V4Z/300V5Z", 1678 .matches = { 1679 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1680 DMI_MATCH(DMI_PRODUCT_NAME, "300V3Z/300V4Z/300V5Z"), 1681 }, 1682 .driver_data = &samsung_lid_handling, 1683 }, 1684 { }, 1685 }; 1686 MODULE_DEVICE_TABLE(dmi, samsung_dmi_table); 1687 1688 static struct platform_device *samsung_platform_device; 1689 1690 static int __init samsung_init(void) 1691 { 1692 struct samsung_laptop *samsung; 1693 int ret; 1694 1695 if (efi_enabled(EFI_BOOT)) 1696 return -ENODEV; 1697 1698 quirks = &samsung_unknown; 1699 if (!force && !dmi_check_system(samsung_dmi_table)) 1700 return -ENODEV; 1701 1702 samsung = kzalloc(sizeof(*samsung), GFP_KERNEL); 1703 if (!samsung) 1704 return -ENOMEM; 1705 1706 mutex_init(&samsung->sabi_mutex); 1707 samsung->handle_backlight = true; 1708 samsung->quirks = quirks; 1709 1710 #ifdef CONFIG_ACPI 1711 if (samsung->quirks->broken_acpi_video) 1712 acpi_video_set_dmi_backlight_type(acpi_backlight_vendor); 1713 if (samsung->quirks->use_native_backlight) 1714 acpi_video_set_dmi_backlight_type(acpi_backlight_native); 1715 1716 if (acpi_video_get_backlight_type() != acpi_backlight_vendor) 1717 samsung->handle_backlight = false; 1718 #endif 1719 1720 ret = samsung_platform_init(samsung); 1721 if (ret) 1722 goto error_platform; 1723 1724 ret = samsung_sabi_init(samsung); 1725 if (ret) 1726 goto error_sabi; 1727 1728 ret = samsung_sysfs_init(samsung); 1729 if (ret) 1730 goto error_sysfs; 1731 1732 ret = samsung_backlight_init(samsung); 1733 if (ret) 1734 goto error_backlight; 1735 1736 ret = samsung_rfkill_init(samsung); 1737 if (ret) 1738 goto error_rfkill; 1739 1740 ret = samsung_leds_init(samsung); 1741 if (ret) 1742 goto error_leds; 1743 1744 ret = samsung_lid_handling_init(samsung); 1745 if (ret) 1746 goto error_lid_handling; 1747 1748 ret = samsung_debugfs_init(samsung); 1749 if (ret) 1750 goto error_debugfs; 1751 1752 samsung->pm_nb.notifier_call = samsung_pm_notification; 1753 register_pm_notifier(&samsung->pm_nb); 1754 1755 samsung_platform_device = samsung->platform_device; 1756 return ret; 1757 1758 error_debugfs: 1759 samsung_lid_handling_exit(samsung); 1760 error_lid_handling: 1761 samsung_leds_exit(samsung); 1762 error_leds: 1763 samsung_rfkill_exit(samsung); 1764 error_rfkill: 1765 samsung_backlight_exit(samsung); 1766 error_backlight: 1767 samsung_sysfs_exit(samsung); 1768 error_sysfs: 1769 samsung_sabi_exit(samsung); 1770 error_sabi: 1771 samsung_platform_exit(samsung); 1772 error_platform: 1773 kfree(samsung); 1774 return ret; 1775 } 1776 1777 static void __exit samsung_exit(void) 1778 { 1779 struct samsung_laptop *samsung; 1780 1781 samsung = platform_get_drvdata(samsung_platform_device); 1782 unregister_pm_notifier(&samsung->pm_nb); 1783 1784 samsung_debugfs_exit(samsung); 1785 samsung_lid_handling_exit(samsung); 1786 samsung_leds_exit(samsung); 1787 samsung_rfkill_exit(samsung); 1788 samsung_backlight_exit(samsung); 1789 samsung_sysfs_exit(samsung); 1790 samsung_sabi_exit(samsung); 1791 samsung_platform_exit(samsung); 1792 1793 kfree(samsung); 1794 samsung_platform_device = NULL; 1795 } 1796 1797 module_init(samsung_init); 1798 module_exit(samsung_exit); 1799 1800 MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@suse.de>"); 1801 MODULE_DESCRIPTION("Samsung Backlight driver"); 1802 MODULE_LICENSE("GPL"); 1803