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