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 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 four_kbd_backlight_levels; 360 bool enable_kbd_backlight; 361 bool lid_handling; 362 }; 363 364 static struct samsung_quirks samsung_unknown = {}; 365 366 static struct samsung_quirks samsung_np740u3e = { 367 .four_kbd_backlight_levels = true, 368 .enable_kbd_backlight = true, 369 }; 370 371 static struct samsung_quirks samsung_lid_handling = { 372 .lid_handling = true, 373 }; 374 375 static bool force; 376 module_param(force, bool, 0); 377 MODULE_PARM_DESC(force, 378 "Disable the DMI check and forces the driver to be loaded"); 379 380 static bool debug; 381 module_param(debug, bool, 0644); 382 MODULE_PARM_DESC(debug, "Debug enabled or not"); 383 384 static int sabi_command(struct samsung_laptop *samsung, u16 command, 385 struct sabi_data *in, 386 struct sabi_data *out) 387 { 388 const struct sabi_config *config = samsung->config; 389 int ret = 0; 390 u16 port = readw(samsung->sabi + config->header_offsets.port); 391 u8 complete, iface_data; 392 393 mutex_lock(&samsung->sabi_mutex); 394 395 if (debug) { 396 if (in) 397 pr_info("SABI command:0x%04x " 398 "data:{0x%08x, 0x%08x, 0x%04x, 0x%02x}", 399 command, in->d0, in->d1, in->d2, in->d3); 400 else 401 pr_info("SABI command:0x%04x", command); 402 } 403 404 /* enable memory to be able to write to it */ 405 outb(readb(samsung->sabi + config->header_offsets.en_mem), port); 406 407 /* write out the command */ 408 writew(config->main_function, samsung->sabi_iface + SABI_IFACE_MAIN); 409 writew(command, samsung->sabi_iface + SABI_IFACE_SUB); 410 writeb(0, samsung->sabi_iface + SABI_IFACE_COMPLETE); 411 if (in) { 412 writel(in->d0, samsung->sabi_iface + SABI_IFACE_DATA); 413 writel(in->d1, samsung->sabi_iface + SABI_IFACE_DATA + 4); 414 writew(in->d2, samsung->sabi_iface + SABI_IFACE_DATA + 8); 415 writeb(in->d3, samsung->sabi_iface + SABI_IFACE_DATA + 10); 416 } 417 outb(readb(samsung->sabi + config->header_offsets.iface_func), port); 418 419 /* write protect memory to make it safe */ 420 outb(readb(samsung->sabi + config->header_offsets.re_mem), port); 421 422 /* see if the command actually succeeded */ 423 complete = readb(samsung->sabi_iface + SABI_IFACE_COMPLETE); 424 iface_data = readb(samsung->sabi_iface + SABI_IFACE_DATA); 425 426 /* iface_data = 0xFF happens when a command is not known 427 * so we only add a warning in debug mode since we will 428 * probably issue some unknown command at startup to find 429 * out which features are supported */ 430 if (complete != 0xaa || (iface_data == 0xff && debug)) 431 pr_warn("SABI command 0x%04x failed with" 432 " completion flag 0x%02x and interface data 0x%02x", 433 command, complete, iface_data); 434 435 if (complete != 0xaa || iface_data == 0xff) { 436 ret = -EINVAL; 437 goto exit; 438 } 439 440 if (out) { 441 out->d0 = readl(samsung->sabi_iface + SABI_IFACE_DATA); 442 out->d1 = readl(samsung->sabi_iface + SABI_IFACE_DATA + 4); 443 out->d2 = readw(samsung->sabi_iface + SABI_IFACE_DATA + 2); 444 out->d3 = readb(samsung->sabi_iface + SABI_IFACE_DATA + 1); 445 } 446 447 if (debug && out) { 448 pr_info("SABI return data:{0x%08x, 0x%08x, 0x%04x, 0x%02x}", 449 out->d0, out->d1, out->d2, out->d3); 450 } 451 452 exit: 453 mutex_unlock(&samsung->sabi_mutex); 454 return ret; 455 } 456 457 /* simple wrappers usable with most commands */ 458 static int sabi_set_commandb(struct samsung_laptop *samsung, 459 u16 command, u8 data) 460 { 461 struct sabi_data in = { { { .d0 = 0, .d1 = 0, .d2 = 0, .d3 = 0 } } }; 462 463 in.data[0] = data; 464 return sabi_command(samsung, command, &in, NULL); 465 } 466 467 static int read_brightness(struct samsung_laptop *samsung) 468 { 469 const struct sabi_config *config = samsung->config; 470 const struct sabi_commands *commands = &samsung->config->commands; 471 struct sabi_data sretval; 472 int user_brightness = 0; 473 int retval; 474 475 retval = sabi_command(samsung, commands->get_brightness, 476 NULL, &sretval); 477 if (retval) 478 return retval; 479 480 user_brightness = sretval.data[0]; 481 if (user_brightness > config->min_brightness) 482 user_brightness -= config->min_brightness; 483 else 484 user_brightness = 0; 485 486 return user_brightness; 487 } 488 489 static void set_brightness(struct samsung_laptop *samsung, u8 user_brightness) 490 { 491 const struct sabi_config *config = samsung->config; 492 const struct sabi_commands *commands = &samsung->config->commands; 493 u8 user_level = user_brightness + config->min_brightness; 494 495 if (samsung->has_stepping_quirk && user_level != 0) { 496 /* 497 * short circuit if the specified level is what's already set 498 * to prevent the screen from flickering needlessly 499 */ 500 if (user_brightness == read_brightness(samsung)) 501 return; 502 503 sabi_set_commandb(samsung, commands->set_brightness, 0); 504 } 505 506 sabi_set_commandb(samsung, commands->set_brightness, user_level); 507 } 508 509 static int get_brightness(struct backlight_device *bd) 510 { 511 struct samsung_laptop *samsung = bl_get_data(bd); 512 513 return read_brightness(samsung); 514 } 515 516 static void check_for_stepping_quirk(struct samsung_laptop *samsung) 517 { 518 int initial_level; 519 int check_level; 520 int orig_level = read_brightness(samsung); 521 522 /* 523 * Some laptops exhibit the strange behaviour of stepping toward 524 * (rather than setting) the brightness except when changing to/from 525 * brightness level 0. This behaviour is checked for here and worked 526 * around in set_brightness. 527 */ 528 529 if (orig_level == 0) 530 set_brightness(samsung, 1); 531 532 initial_level = read_brightness(samsung); 533 534 if (initial_level <= 2) 535 check_level = initial_level + 2; 536 else 537 check_level = initial_level - 2; 538 539 samsung->has_stepping_quirk = false; 540 set_brightness(samsung, check_level); 541 542 if (read_brightness(samsung) != check_level) { 543 samsung->has_stepping_quirk = true; 544 pr_info("enabled workaround for brightness stepping quirk\n"); 545 } 546 547 set_brightness(samsung, orig_level); 548 } 549 550 static int update_status(struct backlight_device *bd) 551 { 552 struct samsung_laptop *samsung = bl_get_data(bd); 553 const struct sabi_commands *commands = &samsung->config->commands; 554 555 set_brightness(samsung, bd->props.brightness); 556 557 if (bd->props.power == FB_BLANK_UNBLANK) 558 sabi_set_commandb(samsung, commands->set_backlight, 1); 559 else 560 sabi_set_commandb(samsung, commands->set_backlight, 0); 561 562 return 0; 563 } 564 565 static const struct backlight_ops backlight_ops = { 566 .get_brightness = get_brightness, 567 .update_status = update_status, 568 }; 569 570 static int seclinux_rfkill_set(void *data, bool blocked) 571 { 572 struct samsung_rfkill *srfkill = data; 573 struct samsung_laptop *samsung = srfkill->samsung; 574 const struct sabi_commands *commands = &samsung->config->commands; 575 576 return sabi_set_commandb(samsung, commands->set_wireless_button, 577 !blocked); 578 } 579 580 static const struct rfkill_ops seclinux_rfkill_ops = { 581 .set_block = seclinux_rfkill_set, 582 }; 583 584 static int swsmi_wireless_status(struct samsung_laptop *samsung, 585 struct sabi_data *data) 586 { 587 const struct sabi_commands *commands = &samsung->config->commands; 588 589 return sabi_command(samsung, commands->get_wireless_status, 590 NULL, data); 591 } 592 593 static int swsmi_rfkill_set(void *priv, bool blocked) 594 { 595 struct samsung_rfkill *srfkill = priv; 596 struct samsung_laptop *samsung = srfkill->samsung; 597 const struct sabi_commands *commands = &samsung->config->commands; 598 struct sabi_data data; 599 int ret, i; 600 601 ret = swsmi_wireless_status(samsung, &data); 602 if (ret) 603 return ret; 604 605 /* Don't set the state for non-present devices */ 606 for (i = 0; i < 4; i++) 607 if (data.data[i] == 0x02) 608 data.data[1] = 0; 609 610 if (srfkill->type == RFKILL_TYPE_WLAN) 611 data.data[WL_STATUS_WLAN] = !blocked; 612 else if (srfkill->type == RFKILL_TYPE_BLUETOOTH) 613 data.data[WL_STATUS_BT] = !blocked; 614 615 return sabi_command(samsung, commands->set_wireless_status, 616 &data, &data); 617 } 618 619 static void swsmi_rfkill_query(struct rfkill *rfkill, void *priv) 620 { 621 struct samsung_rfkill *srfkill = priv; 622 struct samsung_laptop *samsung = srfkill->samsung; 623 struct sabi_data data; 624 int ret; 625 626 ret = swsmi_wireless_status(samsung, &data); 627 if (ret) 628 return ; 629 630 if (srfkill->type == RFKILL_TYPE_WLAN) 631 ret = data.data[WL_STATUS_WLAN]; 632 else if (srfkill->type == RFKILL_TYPE_BLUETOOTH) 633 ret = data.data[WL_STATUS_BT]; 634 else 635 return ; 636 637 rfkill_set_sw_state(rfkill, !ret); 638 } 639 640 static const struct rfkill_ops swsmi_rfkill_ops = { 641 .set_block = swsmi_rfkill_set, 642 .query = swsmi_rfkill_query, 643 }; 644 645 static ssize_t get_performance_level(struct device *dev, 646 struct device_attribute *attr, char *buf) 647 { 648 struct samsung_laptop *samsung = dev_get_drvdata(dev); 649 const struct sabi_config *config = samsung->config; 650 const struct sabi_commands *commands = &config->commands; 651 struct sabi_data sretval; 652 int retval; 653 int i; 654 655 /* Read the state */ 656 retval = sabi_command(samsung, commands->get_performance_level, 657 NULL, &sretval); 658 if (retval) 659 return retval; 660 661 /* The logic is backwards, yeah, lots of fun... */ 662 for (i = 0; config->performance_levels[i].name; ++i) { 663 if (sretval.data[0] == config->performance_levels[i].value) 664 return sprintf(buf, "%s\n", config->performance_levels[i].name); 665 } 666 return sprintf(buf, "%s\n", "unknown"); 667 } 668 669 static ssize_t set_performance_level(struct device *dev, 670 struct device_attribute *attr, const char *buf, 671 size_t count) 672 { 673 struct samsung_laptop *samsung = dev_get_drvdata(dev); 674 const struct sabi_config *config = samsung->config; 675 const struct sabi_commands *commands = &config->commands; 676 int i; 677 678 if (count < 1) 679 return count; 680 681 for (i = 0; config->performance_levels[i].name; ++i) { 682 const struct sabi_performance_level *level = 683 &config->performance_levels[i]; 684 if (!strncasecmp(level->name, buf, strlen(level->name))) { 685 sabi_set_commandb(samsung, 686 commands->set_performance_level, 687 level->value); 688 break; 689 } 690 } 691 692 if (!config->performance_levels[i].name) 693 return -EINVAL; 694 695 return count; 696 } 697 698 static DEVICE_ATTR(performance_level, 0644, 699 get_performance_level, set_performance_level); 700 701 static int read_battery_life_extender(struct samsung_laptop *samsung) 702 { 703 const struct sabi_commands *commands = &samsung->config->commands; 704 struct sabi_data data; 705 int retval; 706 707 if (commands->get_battery_life_extender == 0xFFFF) 708 return -ENODEV; 709 710 memset(&data, 0, sizeof(data)); 711 data.data[0] = 0x80; 712 retval = sabi_command(samsung, commands->get_battery_life_extender, 713 &data, &data); 714 715 if (retval) 716 return retval; 717 718 if (data.data[0] != 0 && data.data[0] != 1) 719 return -ENODEV; 720 721 return data.data[0]; 722 } 723 724 static int write_battery_life_extender(struct samsung_laptop *samsung, 725 int enabled) 726 { 727 const struct sabi_commands *commands = &samsung->config->commands; 728 struct sabi_data data; 729 730 memset(&data, 0, sizeof(data)); 731 data.data[0] = 0x80 | enabled; 732 return sabi_command(samsung, commands->set_battery_life_extender, 733 &data, NULL); 734 } 735 736 static ssize_t get_battery_life_extender(struct device *dev, 737 struct device_attribute *attr, 738 char *buf) 739 { 740 struct samsung_laptop *samsung = dev_get_drvdata(dev); 741 int ret; 742 743 ret = read_battery_life_extender(samsung); 744 if (ret < 0) 745 return ret; 746 747 return sprintf(buf, "%d\n", ret); 748 } 749 750 static ssize_t set_battery_life_extender(struct device *dev, 751 struct device_attribute *attr, 752 const char *buf, size_t count) 753 { 754 struct samsung_laptop *samsung = dev_get_drvdata(dev); 755 int ret, value; 756 757 if (!count || kstrtoint(buf, 0, &value) != 0) 758 return -EINVAL; 759 760 ret = write_battery_life_extender(samsung, !!value); 761 if (ret < 0) 762 return ret; 763 764 return count; 765 } 766 767 static DEVICE_ATTR(battery_life_extender, 0644, 768 get_battery_life_extender, set_battery_life_extender); 769 770 static int read_usb_charge(struct samsung_laptop *samsung) 771 { 772 const struct sabi_commands *commands = &samsung->config->commands; 773 struct sabi_data data; 774 int retval; 775 776 if (commands->get_usb_charge == 0xFFFF) 777 return -ENODEV; 778 779 memset(&data, 0, sizeof(data)); 780 data.data[0] = 0x80; 781 retval = sabi_command(samsung, commands->get_usb_charge, 782 &data, &data); 783 784 if (retval) 785 return retval; 786 787 if (data.data[0] != 0 && data.data[0] != 1) 788 return -ENODEV; 789 790 return data.data[0]; 791 } 792 793 static int write_usb_charge(struct samsung_laptop *samsung, 794 int enabled) 795 { 796 const struct sabi_commands *commands = &samsung->config->commands; 797 struct sabi_data data; 798 799 memset(&data, 0, sizeof(data)); 800 data.data[0] = 0x80 | enabled; 801 return sabi_command(samsung, commands->set_usb_charge, 802 &data, NULL); 803 } 804 805 static ssize_t get_usb_charge(struct device *dev, 806 struct device_attribute *attr, 807 char *buf) 808 { 809 struct samsung_laptop *samsung = dev_get_drvdata(dev); 810 int ret; 811 812 ret = read_usb_charge(samsung); 813 if (ret < 0) 814 return ret; 815 816 return sprintf(buf, "%d\n", ret); 817 } 818 819 static ssize_t set_usb_charge(struct device *dev, 820 struct device_attribute *attr, 821 const char *buf, size_t count) 822 { 823 struct samsung_laptop *samsung = dev_get_drvdata(dev); 824 int ret, value; 825 826 if (!count || kstrtoint(buf, 0, &value) != 0) 827 return -EINVAL; 828 829 ret = write_usb_charge(samsung, !!value); 830 if (ret < 0) 831 return ret; 832 833 return count; 834 } 835 836 static DEVICE_ATTR(usb_charge, 0644, 837 get_usb_charge, set_usb_charge); 838 839 static int read_lid_handling(struct samsung_laptop *samsung) 840 { 841 const struct sabi_commands *commands = &samsung->config->commands; 842 struct sabi_data data; 843 int retval; 844 845 if (commands->get_lid_handling == 0xFFFF) 846 return -ENODEV; 847 848 memset(&data, 0, sizeof(data)); 849 retval = sabi_command(samsung, commands->get_lid_handling, 850 &data, &data); 851 852 if (retval) 853 return retval; 854 855 return data.data[0] & 0x1; 856 } 857 858 static int write_lid_handling(struct samsung_laptop *samsung, 859 int enabled) 860 { 861 const struct sabi_commands *commands = &samsung->config->commands; 862 struct sabi_data data; 863 864 memset(&data, 0, sizeof(data)); 865 data.data[0] = 0x80 | enabled; 866 return sabi_command(samsung, commands->set_lid_handling, 867 &data, NULL); 868 } 869 870 static ssize_t get_lid_handling(struct device *dev, 871 struct device_attribute *attr, 872 char *buf) 873 { 874 struct samsung_laptop *samsung = dev_get_drvdata(dev); 875 int ret; 876 877 ret = read_lid_handling(samsung); 878 if (ret < 0) 879 return ret; 880 881 return sprintf(buf, "%d\n", ret); 882 } 883 884 static ssize_t set_lid_handling(struct device *dev, 885 struct device_attribute *attr, 886 const char *buf, size_t count) 887 { 888 struct samsung_laptop *samsung = dev_get_drvdata(dev); 889 int ret, value; 890 891 if (!count || kstrtoint(buf, 0, &value) != 0) 892 return -EINVAL; 893 894 ret = write_lid_handling(samsung, !!value); 895 if (ret < 0) 896 return ret; 897 898 return count; 899 } 900 901 static DEVICE_ATTR(lid_handling, 0644, 902 get_lid_handling, set_lid_handling); 903 904 static struct attribute *platform_attributes[] = { 905 &dev_attr_performance_level.attr, 906 &dev_attr_battery_life_extender.attr, 907 &dev_attr_usb_charge.attr, 908 &dev_attr_lid_handling.attr, 909 NULL 910 }; 911 912 static int find_signature(void __iomem *memcheck, const char *testStr) 913 { 914 int i = 0; 915 int loca; 916 917 for (loca = 0; loca < 0xffff; loca++) { 918 char temp = readb(memcheck + loca); 919 920 if (temp == testStr[i]) { 921 if (i == strlen(testStr)-1) 922 break; 923 ++i; 924 } else { 925 i = 0; 926 } 927 } 928 return loca; 929 } 930 931 static void samsung_rfkill_exit(struct samsung_laptop *samsung) 932 { 933 if (samsung->wlan.rfkill) { 934 rfkill_unregister(samsung->wlan.rfkill); 935 rfkill_destroy(samsung->wlan.rfkill); 936 samsung->wlan.rfkill = NULL; 937 } 938 if (samsung->bluetooth.rfkill) { 939 rfkill_unregister(samsung->bluetooth.rfkill); 940 rfkill_destroy(samsung->bluetooth.rfkill); 941 samsung->bluetooth.rfkill = NULL; 942 } 943 } 944 945 static int samsung_new_rfkill(struct samsung_laptop *samsung, 946 struct samsung_rfkill *arfkill, 947 const char *name, enum rfkill_type type, 948 const struct rfkill_ops *ops, 949 int blocked) 950 { 951 struct rfkill **rfkill = &arfkill->rfkill; 952 int ret; 953 954 arfkill->type = type; 955 arfkill->samsung = samsung; 956 957 *rfkill = rfkill_alloc(name, &samsung->platform_device->dev, 958 type, ops, arfkill); 959 960 if (!*rfkill) 961 return -EINVAL; 962 963 if (blocked != -1) 964 rfkill_init_sw_state(*rfkill, blocked); 965 966 ret = rfkill_register(*rfkill); 967 if (ret) { 968 rfkill_destroy(*rfkill); 969 *rfkill = NULL; 970 return ret; 971 } 972 return 0; 973 } 974 975 static int __init samsung_rfkill_init_seclinux(struct samsung_laptop *samsung) 976 { 977 return samsung_new_rfkill(samsung, &samsung->wlan, "samsung-wlan", 978 RFKILL_TYPE_WLAN, &seclinux_rfkill_ops, -1); 979 } 980 981 static int __init samsung_rfkill_init_swsmi(struct samsung_laptop *samsung) 982 { 983 struct sabi_data data; 984 int ret; 985 986 ret = swsmi_wireless_status(samsung, &data); 987 if (ret) { 988 /* Some swsmi laptops use the old seclinux way to control 989 * wireless devices */ 990 if (ret == -EINVAL) 991 ret = samsung_rfkill_init_seclinux(samsung); 992 return ret; 993 } 994 995 /* 0x02 seems to mean that the device is no present/available */ 996 997 if (data.data[WL_STATUS_WLAN] != 0x02) 998 ret = samsung_new_rfkill(samsung, &samsung->wlan, 999 "samsung-wlan", 1000 RFKILL_TYPE_WLAN, 1001 &swsmi_rfkill_ops, 1002 !data.data[WL_STATUS_WLAN]); 1003 if (ret) 1004 goto exit; 1005 1006 if (data.data[WL_STATUS_BT] != 0x02) 1007 ret = samsung_new_rfkill(samsung, &samsung->bluetooth, 1008 "samsung-bluetooth", 1009 RFKILL_TYPE_BLUETOOTH, 1010 &swsmi_rfkill_ops, 1011 !data.data[WL_STATUS_BT]); 1012 if (ret) 1013 goto exit; 1014 1015 exit: 1016 if (ret) 1017 samsung_rfkill_exit(samsung); 1018 1019 return ret; 1020 } 1021 1022 static int __init samsung_rfkill_init(struct samsung_laptop *samsung) 1023 { 1024 if (samsung->config->sabi_version == 2) 1025 return samsung_rfkill_init_seclinux(samsung); 1026 if (samsung->config->sabi_version == 3) 1027 return samsung_rfkill_init_swsmi(samsung); 1028 return 0; 1029 } 1030 1031 static void samsung_lid_handling_exit(struct samsung_laptop *samsung) 1032 { 1033 if (samsung->quirks->lid_handling) 1034 write_lid_handling(samsung, 0); 1035 } 1036 1037 static int __init samsung_lid_handling_init(struct samsung_laptop *samsung) 1038 { 1039 int retval = 0; 1040 1041 if (samsung->quirks->lid_handling) 1042 retval = write_lid_handling(samsung, 1); 1043 1044 return retval; 1045 } 1046 1047 static int kbd_backlight_enable(struct samsung_laptop *samsung) 1048 { 1049 const struct sabi_commands *commands = &samsung->config->commands; 1050 struct sabi_data data; 1051 int retval; 1052 1053 if (commands->kbd_backlight == 0xFFFF) 1054 return -ENODEV; 1055 1056 memset(&data, 0, sizeof(data)); 1057 data.d0 = 0xaabb; 1058 retval = sabi_command(samsung, commands->kbd_backlight, 1059 &data, &data); 1060 1061 if (retval) 1062 return retval; 1063 1064 if (data.d0 != 0xccdd) 1065 return -ENODEV; 1066 return 0; 1067 } 1068 1069 static int kbd_backlight_read(struct samsung_laptop *samsung) 1070 { 1071 const struct sabi_commands *commands = &samsung->config->commands; 1072 struct sabi_data data; 1073 int retval; 1074 1075 memset(&data, 0, sizeof(data)); 1076 data.data[0] = 0x81; 1077 retval = sabi_command(samsung, commands->kbd_backlight, 1078 &data, &data); 1079 1080 if (retval) 1081 return retval; 1082 1083 return data.data[0]; 1084 } 1085 1086 static int kbd_backlight_write(struct samsung_laptop *samsung, int brightness) 1087 { 1088 const struct sabi_commands *commands = &samsung->config->commands; 1089 struct sabi_data data; 1090 1091 memset(&data, 0, sizeof(data)); 1092 data.d0 = 0x82 | ((brightness & 0xFF) << 8); 1093 return sabi_command(samsung, commands->kbd_backlight, 1094 &data, NULL); 1095 } 1096 1097 static void kbd_led_update(struct work_struct *work) 1098 { 1099 struct samsung_laptop *samsung; 1100 1101 samsung = container_of(work, struct samsung_laptop, kbd_led_work); 1102 kbd_backlight_write(samsung, samsung->kbd_led_wk); 1103 } 1104 1105 static void kbd_led_set(struct led_classdev *led_cdev, 1106 enum led_brightness value) 1107 { 1108 struct samsung_laptop *samsung; 1109 1110 samsung = container_of(led_cdev, struct samsung_laptop, kbd_led); 1111 1112 if (value > samsung->kbd_led.max_brightness) 1113 value = samsung->kbd_led.max_brightness; 1114 1115 samsung->kbd_led_wk = value; 1116 queue_work(samsung->led_workqueue, &samsung->kbd_led_work); 1117 } 1118 1119 static enum led_brightness kbd_led_get(struct led_classdev *led_cdev) 1120 { 1121 struct samsung_laptop *samsung; 1122 1123 samsung = container_of(led_cdev, struct samsung_laptop, kbd_led); 1124 return kbd_backlight_read(samsung); 1125 } 1126 1127 static void samsung_leds_exit(struct samsung_laptop *samsung) 1128 { 1129 led_classdev_unregister(&samsung->kbd_led); 1130 if (samsung->led_workqueue) 1131 destroy_workqueue(samsung->led_workqueue); 1132 } 1133 1134 static int __init samsung_leds_init(struct samsung_laptop *samsung) 1135 { 1136 int ret = 0; 1137 1138 samsung->led_workqueue = create_singlethread_workqueue("led_workqueue"); 1139 if (!samsung->led_workqueue) 1140 return -ENOMEM; 1141 1142 if (kbd_backlight_enable(samsung) >= 0) { 1143 INIT_WORK(&samsung->kbd_led_work, kbd_led_update); 1144 1145 samsung->kbd_led.name = "samsung::kbd_backlight"; 1146 samsung->kbd_led.brightness_set = kbd_led_set; 1147 samsung->kbd_led.brightness_get = kbd_led_get; 1148 samsung->kbd_led.max_brightness = 8; 1149 if (samsung->quirks->four_kbd_backlight_levels) 1150 samsung->kbd_led.max_brightness = 4; 1151 1152 ret = led_classdev_register(&samsung->platform_device->dev, 1153 &samsung->kbd_led); 1154 } 1155 1156 if (ret) 1157 samsung_leds_exit(samsung); 1158 1159 return ret; 1160 } 1161 1162 static void samsung_backlight_exit(struct samsung_laptop *samsung) 1163 { 1164 if (samsung->backlight_device) { 1165 backlight_device_unregister(samsung->backlight_device); 1166 samsung->backlight_device = NULL; 1167 } 1168 } 1169 1170 static int __init samsung_backlight_init(struct samsung_laptop *samsung) 1171 { 1172 struct backlight_device *bd; 1173 struct backlight_properties props; 1174 1175 if (!samsung->handle_backlight) 1176 return 0; 1177 1178 memset(&props, 0, sizeof(struct backlight_properties)); 1179 props.type = BACKLIGHT_PLATFORM; 1180 props.max_brightness = samsung->config->max_brightness - 1181 samsung->config->min_brightness; 1182 1183 bd = backlight_device_register("samsung", 1184 &samsung->platform_device->dev, 1185 samsung, &backlight_ops, 1186 &props); 1187 if (IS_ERR(bd)) 1188 return PTR_ERR(bd); 1189 1190 samsung->backlight_device = bd; 1191 samsung->backlight_device->props.brightness = read_brightness(samsung); 1192 samsung->backlight_device->props.power = FB_BLANK_UNBLANK; 1193 backlight_update_status(samsung->backlight_device); 1194 1195 return 0; 1196 } 1197 1198 static umode_t samsung_sysfs_is_visible(struct kobject *kobj, 1199 struct attribute *attr, int idx) 1200 { 1201 struct device *dev = kobj_to_dev(kobj); 1202 struct samsung_laptop *samsung = dev_get_drvdata(dev); 1203 bool ok = true; 1204 1205 if (attr == &dev_attr_performance_level.attr) 1206 ok = !!samsung->config->performance_levels[0].name; 1207 if (attr == &dev_attr_battery_life_extender.attr) 1208 ok = !!(read_battery_life_extender(samsung) >= 0); 1209 if (attr == &dev_attr_usb_charge.attr) 1210 ok = !!(read_usb_charge(samsung) >= 0); 1211 if (attr == &dev_attr_lid_handling.attr) 1212 ok = !!(read_lid_handling(samsung) >= 0); 1213 1214 return ok ? attr->mode : 0; 1215 } 1216 1217 static const struct attribute_group platform_attribute_group = { 1218 .is_visible = samsung_sysfs_is_visible, 1219 .attrs = platform_attributes 1220 }; 1221 1222 static void samsung_sysfs_exit(struct samsung_laptop *samsung) 1223 { 1224 struct platform_device *device = samsung->platform_device; 1225 1226 sysfs_remove_group(&device->dev.kobj, &platform_attribute_group); 1227 } 1228 1229 static int __init samsung_sysfs_init(struct samsung_laptop *samsung) 1230 { 1231 struct platform_device *device = samsung->platform_device; 1232 1233 return sysfs_create_group(&device->dev.kobj, &platform_attribute_group); 1234 1235 } 1236 1237 static int samsung_laptop_call_show(struct seq_file *m, void *data) 1238 { 1239 struct samsung_laptop *samsung = m->private; 1240 struct sabi_data *sdata = &samsung->debug.data; 1241 int ret; 1242 1243 seq_printf(m, "SABI 0x%04x {0x%08x, 0x%08x, 0x%04x, 0x%02x}\n", 1244 samsung->debug.command, 1245 sdata->d0, sdata->d1, sdata->d2, sdata->d3); 1246 1247 ret = sabi_command(samsung, samsung->debug.command, sdata, sdata); 1248 1249 if (ret) { 1250 seq_printf(m, "SABI command 0x%04x failed\n", 1251 samsung->debug.command); 1252 return ret; 1253 } 1254 1255 seq_printf(m, "SABI {0x%08x, 0x%08x, 0x%04x, 0x%02x}\n", 1256 sdata->d0, sdata->d1, sdata->d2, sdata->d3); 1257 return 0; 1258 } 1259 DEFINE_SHOW_ATTRIBUTE(samsung_laptop_call); 1260 1261 static void samsung_debugfs_exit(struct samsung_laptop *samsung) 1262 { 1263 debugfs_remove_recursive(samsung->debug.root); 1264 } 1265 1266 static void samsung_debugfs_init(struct samsung_laptop *samsung) 1267 { 1268 struct dentry *root; 1269 1270 root = debugfs_create_dir("samsung-laptop", NULL); 1271 samsung->debug.root = root; 1272 1273 samsung->debug.f0000_wrapper.data = samsung->f0000_segment; 1274 samsung->debug.f0000_wrapper.size = 0xffff; 1275 1276 samsung->debug.data_wrapper.data = &samsung->debug.data; 1277 samsung->debug.data_wrapper.size = sizeof(samsung->debug.data); 1278 1279 samsung->debug.sdiag_wrapper.data = samsung->sdiag; 1280 samsung->debug.sdiag_wrapper.size = strlen(samsung->sdiag); 1281 1282 debugfs_create_u16("command", 0644, root, &samsung->debug.command); 1283 debugfs_create_u32("d0", 0644, root, &samsung->debug.data.d0); 1284 debugfs_create_u32("d1", 0644, root, &samsung->debug.data.d1); 1285 debugfs_create_u16("d2", 0644, root, &samsung->debug.data.d2); 1286 debugfs_create_u8("d3", 0644, root, &samsung->debug.data.d3); 1287 debugfs_create_blob("data", 0444, root, &samsung->debug.data_wrapper); 1288 debugfs_create_blob("f0000_segment", 0400, root, 1289 &samsung->debug.f0000_wrapper); 1290 debugfs_create_file("call", 0444, root, samsung, 1291 &samsung_laptop_call_fops); 1292 debugfs_create_blob("sdiag", 0444, root, &samsung->debug.sdiag_wrapper); 1293 } 1294 1295 static void samsung_sabi_exit(struct samsung_laptop *samsung) 1296 { 1297 const struct sabi_config *config = samsung->config; 1298 1299 /* Turn off "Linux" mode in the BIOS */ 1300 if (config && config->commands.set_linux != 0xff) 1301 sabi_set_commandb(samsung, config->commands.set_linux, 0x80); 1302 1303 if (samsung->sabi_iface) { 1304 iounmap(samsung->sabi_iface); 1305 samsung->sabi_iface = NULL; 1306 } 1307 if (samsung->f0000_segment) { 1308 iounmap(samsung->f0000_segment); 1309 samsung->f0000_segment = NULL; 1310 } 1311 1312 samsung->config = NULL; 1313 } 1314 1315 static __init void samsung_sabi_infos(struct samsung_laptop *samsung, int loca, 1316 unsigned int ifaceP) 1317 { 1318 const struct sabi_config *config = samsung->config; 1319 1320 printk(KERN_DEBUG "This computer supports SABI==%x\n", 1321 loca + 0xf0000 - 6); 1322 1323 printk(KERN_DEBUG "SABI header:\n"); 1324 printk(KERN_DEBUG " SMI Port Number = 0x%04x\n", 1325 readw(samsung->sabi + config->header_offsets.port)); 1326 printk(KERN_DEBUG " SMI Interface Function = 0x%02x\n", 1327 readb(samsung->sabi + config->header_offsets.iface_func)); 1328 printk(KERN_DEBUG " SMI enable memory buffer = 0x%02x\n", 1329 readb(samsung->sabi + config->header_offsets.en_mem)); 1330 printk(KERN_DEBUG " SMI restore memory buffer = 0x%02x\n", 1331 readb(samsung->sabi + config->header_offsets.re_mem)); 1332 printk(KERN_DEBUG " SABI data offset = 0x%04x\n", 1333 readw(samsung->sabi + config->header_offsets.data_offset)); 1334 printk(KERN_DEBUG " SABI data segment = 0x%04x\n", 1335 readw(samsung->sabi + config->header_offsets.data_segment)); 1336 1337 printk(KERN_DEBUG " SABI pointer = 0x%08x\n", ifaceP); 1338 } 1339 1340 static void __init samsung_sabi_diag(struct samsung_laptop *samsung) 1341 { 1342 int loca = find_signature(samsung->f0000_segment, "SDiaG@"); 1343 int i; 1344 1345 if (loca == 0xffff) 1346 return ; 1347 1348 /* Example: 1349 * Ident: @SDiaG@686XX-N90X3A/966-SEC-07HL-S90X3A 1350 * 1351 * Product name: 90X3A 1352 * BIOS Version: 07HL 1353 */ 1354 loca += 1; 1355 for (i = 0; loca < 0xffff && i < sizeof(samsung->sdiag) - 1; loca++) { 1356 char temp = readb(samsung->f0000_segment + loca); 1357 1358 if (isalnum(temp) || temp == '/' || temp == '-') 1359 samsung->sdiag[i++] = temp; 1360 else 1361 break ; 1362 } 1363 1364 if (debug && samsung->sdiag[0]) 1365 pr_info("sdiag: %s", samsung->sdiag); 1366 } 1367 1368 static int __init samsung_sabi_init(struct samsung_laptop *samsung) 1369 { 1370 const struct sabi_config *config = NULL; 1371 const struct sabi_commands *commands; 1372 unsigned int ifaceP; 1373 int loca = 0xffff; 1374 int ret = 0; 1375 int i; 1376 1377 samsung->f0000_segment = ioremap(0xf0000, 0xffff); 1378 if (!samsung->f0000_segment) { 1379 if (debug || force) 1380 pr_err("Can't map the segment at 0xf0000\n"); 1381 ret = -EINVAL; 1382 goto exit; 1383 } 1384 1385 samsung_sabi_diag(samsung); 1386 1387 /* Try to find one of the signatures in memory to find the header */ 1388 for (i = 0; sabi_configs[i].test_string != NULL; ++i) { 1389 samsung->config = &sabi_configs[i]; 1390 loca = find_signature(samsung->f0000_segment, 1391 samsung->config->test_string); 1392 if (loca != 0xffff) 1393 break; 1394 } 1395 1396 if (loca == 0xffff) { 1397 if (debug || force) 1398 pr_err("This computer does not support SABI\n"); 1399 ret = -ENODEV; 1400 goto exit; 1401 } 1402 1403 config = samsung->config; 1404 commands = &config->commands; 1405 1406 /* point to the SMI port Number */ 1407 loca += 1; 1408 samsung->sabi = (samsung->f0000_segment + loca); 1409 1410 /* Get a pointer to the SABI Interface */ 1411 ifaceP = (readw(samsung->sabi + config->header_offsets.data_segment) & 0x0ffff) << 4; 1412 ifaceP += readw(samsung->sabi + config->header_offsets.data_offset) & 0x0ffff; 1413 1414 if (debug) 1415 samsung_sabi_infos(samsung, loca, ifaceP); 1416 1417 samsung->sabi_iface = ioremap(ifaceP, 16); 1418 if (!samsung->sabi_iface) { 1419 pr_err("Can't remap %x\n", ifaceP); 1420 ret = -EINVAL; 1421 goto exit; 1422 } 1423 1424 /* Turn on "Linux" mode in the BIOS */ 1425 if (commands->set_linux != 0xff) { 1426 int retval = sabi_set_commandb(samsung, 1427 commands->set_linux, 0x81); 1428 if (retval) { 1429 pr_warn("Linux mode was not set!\n"); 1430 ret = -ENODEV; 1431 goto exit; 1432 } 1433 } 1434 1435 /* Check for stepping quirk */ 1436 if (samsung->handle_backlight) 1437 check_for_stepping_quirk(samsung); 1438 1439 pr_info("detected SABI interface: %s\n", 1440 samsung->config->test_string); 1441 1442 exit: 1443 if (ret) 1444 samsung_sabi_exit(samsung); 1445 1446 return ret; 1447 } 1448 1449 static void samsung_platform_exit(struct samsung_laptop *samsung) 1450 { 1451 if (samsung->platform_device) { 1452 platform_device_unregister(samsung->platform_device); 1453 samsung->platform_device = NULL; 1454 } 1455 } 1456 1457 static int samsung_pm_notification(struct notifier_block *nb, 1458 unsigned long val, void *ptr) 1459 { 1460 struct samsung_laptop *samsung; 1461 1462 samsung = container_of(nb, struct samsung_laptop, pm_nb); 1463 if (val == PM_POST_HIBERNATION && 1464 samsung->quirks->enable_kbd_backlight) 1465 kbd_backlight_enable(samsung); 1466 1467 if (val == PM_POST_HIBERNATION && samsung->quirks->lid_handling) 1468 write_lid_handling(samsung, 1); 1469 1470 return 0; 1471 } 1472 1473 static int __init samsung_platform_init(struct samsung_laptop *samsung) 1474 { 1475 struct platform_device *pdev; 1476 1477 pdev = platform_device_register_simple("samsung", PLATFORM_DEVID_NONE, NULL, 0); 1478 if (IS_ERR(pdev)) 1479 return PTR_ERR(pdev); 1480 1481 samsung->platform_device = pdev; 1482 platform_set_drvdata(samsung->platform_device, samsung); 1483 return 0; 1484 } 1485 1486 static struct samsung_quirks *quirks; 1487 1488 static int __init samsung_dmi_matched(const struct dmi_system_id *d) 1489 { 1490 quirks = d->driver_data; 1491 return 0; 1492 } 1493 1494 static const struct dmi_system_id samsung_dmi_table[] __initconst = { 1495 { 1496 .matches = { 1497 DMI_MATCH(DMI_SYS_VENDOR, 1498 "SAMSUNG ELECTRONICS CO., LTD."), 1499 DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */ 1500 }, 1501 }, 1502 { 1503 .matches = { 1504 DMI_MATCH(DMI_SYS_VENDOR, 1505 "SAMSUNG ELECTRONICS CO., LTD."), 1506 DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /* Laptop */ 1507 }, 1508 }, 1509 { 1510 .matches = { 1511 DMI_MATCH(DMI_SYS_VENDOR, 1512 "SAMSUNG ELECTRONICS CO., LTD."), 1513 DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */ 1514 }, 1515 }, 1516 { 1517 .matches = { 1518 DMI_MATCH(DMI_SYS_VENDOR, 1519 "SAMSUNG ELECTRONICS CO., LTD."), 1520 DMI_MATCH(DMI_CHASSIS_TYPE, "14"), /* Sub-Notebook */ 1521 }, 1522 }, 1523 /* DMI ids for laptops with bad Chassis Type */ 1524 { 1525 .ident = "R40/R41", 1526 .matches = { 1527 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1528 DMI_MATCH(DMI_PRODUCT_NAME, "R40/R41"), 1529 DMI_MATCH(DMI_BOARD_NAME, "R40/R41"), 1530 }, 1531 }, 1532 /* Specific DMI ids for laptop with quirks */ 1533 { 1534 .callback = samsung_dmi_matched, 1535 .ident = "730U3E/740U3E", 1536 .matches = { 1537 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1538 DMI_MATCH(DMI_PRODUCT_NAME, "730U3E/740U3E"), 1539 }, 1540 .driver_data = &samsung_np740u3e, 1541 }, 1542 { 1543 .callback = samsung_dmi_matched, 1544 .ident = "300V3Z/300V4Z/300V5Z", 1545 .matches = { 1546 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1547 DMI_MATCH(DMI_PRODUCT_NAME, "300V3Z/300V4Z/300V5Z"), 1548 }, 1549 .driver_data = &samsung_lid_handling, 1550 }, 1551 { }, 1552 }; 1553 MODULE_DEVICE_TABLE(dmi, samsung_dmi_table); 1554 1555 static struct platform_device *samsung_platform_device; 1556 1557 static int __init samsung_init(void) 1558 { 1559 struct samsung_laptop *samsung; 1560 int ret; 1561 1562 if (efi_enabled(EFI_BOOT)) 1563 return -ENODEV; 1564 1565 quirks = &samsung_unknown; 1566 if (!force && !dmi_check_system(samsung_dmi_table)) 1567 return -ENODEV; 1568 1569 samsung = kzalloc(sizeof(*samsung), GFP_KERNEL); 1570 if (!samsung) 1571 return -ENOMEM; 1572 1573 mutex_init(&samsung->sabi_mutex); 1574 samsung->handle_backlight = true; 1575 samsung->quirks = quirks; 1576 1577 if (acpi_video_get_backlight_type() != acpi_backlight_vendor) 1578 samsung->handle_backlight = false; 1579 1580 ret = samsung_platform_init(samsung); 1581 if (ret) 1582 goto error_platform; 1583 1584 ret = samsung_sabi_init(samsung); 1585 if (ret) 1586 goto error_sabi; 1587 1588 ret = samsung_sysfs_init(samsung); 1589 if (ret) 1590 goto error_sysfs; 1591 1592 ret = samsung_backlight_init(samsung); 1593 if (ret) 1594 goto error_backlight; 1595 1596 ret = samsung_rfkill_init(samsung); 1597 if (ret) 1598 goto error_rfkill; 1599 1600 ret = samsung_leds_init(samsung); 1601 if (ret) 1602 goto error_leds; 1603 1604 ret = samsung_lid_handling_init(samsung); 1605 if (ret) 1606 goto error_lid_handling; 1607 1608 samsung_debugfs_init(samsung); 1609 1610 samsung->pm_nb.notifier_call = samsung_pm_notification; 1611 register_pm_notifier(&samsung->pm_nb); 1612 1613 samsung_platform_device = samsung->platform_device; 1614 return ret; 1615 1616 error_lid_handling: 1617 samsung_leds_exit(samsung); 1618 error_leds: 1619 samsung_rfkill_exit(samsung); 1620 error_rfkill: 1621 samsung_backlight_exit(samsung); 1622 error_backlight: 1623 samsung_sysfs_exit(samsung); 1624 error_sysfs: 1625 samsung_sabi_exit(samsung); 1626 error_sabi: 1627 samsung_platform_exit(samsung); 1628 error_platform: 1629 kfree(samsung); 1630 return ret; 1631 } 1632 1633 static void __exit samsung_exit(void) 1634 { 1635 struct samsung_laptop *samsung; 1636 1637 samsung = platform_get_drvdata(samsung_platform_device); 1638 unregister_pm_notifier(&samsung->pm_nb); 1639 1640 samsung_debugfs_exit(samsung); 1641 samsung_lid_handling_exit(samsung); 1642 samsung_leds_exit(samsung); 1643 samsung_rfkill_exit(samsung); 1644 samsung_backlight_exit(samsung); 1645 samsung_sysfs_exit(samsung); 1646 samsung_sabi_exit(samsung); 1647 samsung_platform_exit(samsung); 1648 1649 kfree(samsung); 1650 samsung_platform_device = NULL; 1651 } 1652 1653 module_init(samsung_init); 1654 module_exit(samsung_exit); 1655 1656 MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@suse.de>"); 1657 MODULE_DESCRIPTION("Samsung Backlight driver"); 1658 MODULE_LICENSE("GPL"); 1659