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 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, 0644); 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, 0644, 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, 0644, 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, 0644, 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, 0644, 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 led_classdev_unregister(&samsung->kbd_led); 1142 if (samsung->led_workqueue) 1143 destroy_workqueue(samsung->led_workqueue); 1144 } 1145 1146 static int __init samsung_leds_init(struct samsung_laptop *samsung) 1147 { 1148 int ret = 0; 1149 1150 samsung->led_workqueue = create_singlethread_workqueue("led_workqueue"); 1151 if (!samsung->led_workqueue) 1152 return -ENOMEM; 1153 1154 if (kbd_backlight_enable(samsung) >= 0) { 1155 INIT_WORK(&samsung->kbd_led_work, kbd_led_update); 1156 1157 samsung->kbd_led.name = "samsung::kbd_backlight"; 1158 samsung->kbd_led.brightness_set = kbd_led_set; 1159 samsung->kbd_led.brightness_get = kbd_led_get; 1160 samsung->kbd_led.max_brightness = 8; 1161 if (samsung->quirks->four_kbd_backlight_levels) 1162 samsung->kbd_led.max_brightness = 4; 1163 1164 ret = led_classdev_register(&samsung->platform_device->dev, 1165 &samsung->kbd_led); 1166 } 1167 1168 if (ret) 1169 samsung_leds_exit(samsung); 1170 1171 return ret; 1172 } 1173 1174 static void samsung_backlight_exit(struct samsung_laptop *samsung) 1175 { 1176 if (samsung->backlight_device) { 1177 backlight_device_unregister(samsung->backlight_device); 1178 samsung->backlight_device = NULL; 1179 } 1180 } 1181 1182 static int __init samsung_backlight_init(struct samsung_laptop *samsung) 1183 { 1184 struct backlight_device *bd; 1185 struct backlight_properties props; 1186 1187 if (!samsung->handle_backlight) 1188 return 0; 1189 1190 memset(&props, 0, sizeof(struct backlight_properties)); 1191 props.type = BACKLIGHT_PLATFORM; 1192 props.max_brightness = samsung->config->max_brightness - 1193 samsung->config->min_brightness; 1194 1195 bd = backlight_device_register("samsung", 1196 &samsung->platform_device->dev, 1197 samsung, &backlight_ops, 1198 &props); 1199 if (IS_ERR(bd)) 1200 return PTR_ERR(bd); 1201 1202 samsung->backlight_device = bd; 1203 samsung->backlight_device->props.brightness = read_brightness(samsung); 1204 samsung->backlight_device->props.power = FB_BLANK_UNBLANK; 1205 backlight_update_status(samsung->backlight_device); 1206 1207 return 0; 1208 } 1209 1210 static umode_t samsung_sysfs_is_visible(struct kobject *kobj, 1211 struct attribute *attr, int idx) 1212 { 1213 struct device *dev = container_of(kobj, struct device, kobj); 1214 struct samsung_laptop *samsung = dev_get_drvdata(dev); 1215 bool ok = true; 1216 1217 if (attr == &dev_attr_performance_level.attr) 1218 ok = !!samsung->config->performance_levels[0].name; 1219 if (attr == &dev_attr_battery_life_extender.attr) 1220 ok = !!(read_battery_life_extender(samsung) >= 0); 1221 if (attr == &dev_attr_usb_charge.attr) 1222 ok = !!(read_usb_charge(samsung) >= 0); 1223 if (attr == &dev_attr_lid_handling.attr) 1224 ok = !!(read_lid_handling(samsung) >= 0); 1225 1226 return ok ? attr->mode : 0; 1227 } 1228 1229 static const struct attribute_group platform_attribute_group = { 1230 .is_visible = samsung_sysfs_is_visible, 1231 .attrs = platform_attributes 1232 }; 1233 1234 static void samsung_sysfs_exit(struct samsung_laptop *samsung) 1235 { 1236 struct platform_device *device = samsung->platform_device; 1237 1238 sysfs_remove_group(&device->dev.kobj, &platform_attribute_group); 1239 } 1240 1241 static int __init samsung_sysfs_init(struct samsung_laptop *samsung) 1242 { 1243 struct platform_device *device = samsung->platform_device; 1244 1245 return sysfs_create_group(&device->dev.kobj, &platform_attribute_group); 1246 1247 } 1248 1249 static int samsung_laptop_call_show(struct seq_file *m, void *data) 1250 { 1251 struct samsung_laptop *samsung = m->private; 1252 struct sabi_data *sdata = &samsung->debug.data; 1253 int ret; 1254 1255 seq_printf(m, "SABI 0x%04x {0x%08x, 0x%08x, 0x%04x, 0x%02x}\n", 1256 samsung->debug.command, 1257 sdata->d0, sdata->d1, sdata->d2, sdata->d3); 1258 1259 ret = sabi_command(samsung, samsung->debug.command, sdata, sdata); 1260 1261 if (ret) { 1262 seq_printf(m, "SABI command 0x%04x failed\n", 1263 samsung->debug.command); 1264 return ret; 1265 } 1266 1267 seq_printf(m, "SABI {0x%08x, 0x%08x, 0x%04x, 0x%02x}\n", 1268 sdata->d0, sdata->d1, sdata->d2, sdata->d3); 1269 return 0; 1270 } 1271 DEFINE_SHOW_ATTRIBUTE(samsung_laptop_call); 1272 1273 static void samsung_debugfs_exit(struct samsung_laptop *samsung) 1274 { 1275 debugfs_remove_recursive(samsung->debug.root); 1276 } 1277 1278 static void samsung_debugfs_init(struct samsung_laptop *samsung) 1279 { 1280 struct dentry *root; 1281 1282 root = debugfs_create_dir("samsung-laptop", NULL); 1283 samsung->debug.root = root; 1284 1285 samsung->debug.f0000_wrapper.data = samsung->f0000_segment; 1286 samsung->debug.f0000_wrapper.size = 0xffff; 1287 1288 samsung->debug.data_wrapper.data = &samsung->debug.data; 1289 samsung->debug.data_wrapper.size = sizeof(samsung->debug.data); 1290 1291 samsung->debug.sdiag_wrapper.data = samsung->sdiag; 1292 samsung->debug.sdiag_wrapper.size = strlen(samsung->sdiag); 1293 1294 debugfs_create_u16("command", 0644, root, &samsung->debug.command); 1295 debugfs_create_u32("d0", 0644, root, &samsung->debug.data.d0); 1296 debugfs_create_u32("d1", 0644, root, &samsung->debug.data.d1); 1297 debugfs_create_u16("d2", 0644, root, &samsung->debug.data.d2); 1298 debugfs_create_u8("d3", 0644, root, &samsung->debug.data.d3); 1299 debugfs_create_blob("data", 0444, root, &samsung->debug.data_wrapper); 1300 debugfs_create_blob("f0000_segment", 0400, root, 1301 &samsung->debug.f0000_wrapper); 1302 debugfs_create_file("call", 0444, root, samsung, 1303 &samsung_laptop_call_fops); 1304 debugfs_create_blob("sdiag", 0444, root, &samsung->debug.sdiag_wrapper); 1305 } 1306 1307 static void samsung_sabi_exit(struct samsung_laptop *samsung) 1308 { 1309 const struct sabi_config *config = samsung->config; 1310 1311 /* Turn off "Linux" mode in the BIOS */ 1312 if (config && config->commands.set_linux != 0xff) 1313 sabi_set_commandb(samsung, config->commands.set_linux, 0x80); 1314 1315 if (samsung->sabi_iface) { 1316 iounmap(samsung->sabi_iface); 1317 samsung->sabi_iface = NULL; 1318 } 1319 if (samsung->f0000_segment) { 1320 iounmap(samsung->f0000_segment); 1321 samsung->f0000_segment = NULL; 1322 } 1323 1324 samsung->config = NULL; 1325 } 1326 1327 static __init void samsung_sabi_infos(struct samsung_laptop *samsung, int loca, 1328 unsigned int ifaceP) 1329 { 1330 const struct sabi_config *config = samsung->config; 1331 1332 printk(KERN_DEBUG "This computer supports SABI==%x\n", 1333 loca + 0xf0000 - 6); 1334 1335 printk(KERN_DEBUG "SABI header:\n"); 1336 printk(KERN_DEBUG " SMI Port Number = 0x%04x\n", 1337 readw(samsung->sabi + config->header_offsets.port)); 1338 printk(KERN_DEBUG " SMI Interface Function = 0x%02x\n", 1339 readb(samsung->sabi + config->header_offsets.iface_func)); 1340 printk(KERN_DEBUG " SMI enable memory buffer = 0x%02x\n", 1341 readb(samsung->sabi + config->header_offsets.en_mem)); 1342 printk(KERN_DEBUG " SMI restore memory buffer = 0x%02x\n", 1343 readb(samsung->sabi + config->header_offsets.re_mem)); 1344 printk(KERN_DEBUG " SABI data offset = 0x%04x\n", 1345 readw(samsung->sabi + config->header_offsets.data_offset)); 1346 printk(KERN_DEBUG " SABI data segment = 0x%04x\n", 1347 readw(samsung->sabi + config->header_offsets.data_segment)); 1348 1349 printk(KERN_DEBUG " SABI pointer = 0x%08x\n", ifaceP); 1350 } 1351 1352 static void __init samsung_sabi_diag(struct samsung_laptop *samsung) 1353 { 1354 int loca = find_signature(samsung->f0000_segment, "SDiaG@"); 1355 int i; 1356 1357 if (loca == 0xffff) 1358 return ; 1359 1360 /* Example: 1361 * Ident: @SDiaG@686XX-N90X3A/966-SEC-07HL-S90X3A 1362 * 1363 * Product name: 90X3A 1364 * BIOS Version: 07HL 1365 */ 1366 loca += 1; 1367 for (i = 0; loca < 0xffff && i < sizeof(samsung->sdiag) - 1; loca++) { 1368 char temp = readb(samsung->f0000_segment + loca); 1369 1370 if (isalnum(temp) || temp == '/' || temp == '-') 1371 samsung->sdiag[i++] = temp; 1372 else 1373 break ; 1374 } 1375 1376 if (debug && samsung->sdiag[0]) 1377 pr_info("sdiag: %s", samsung->sdiag); 1378 } 1379 1380 static int __init samsung_sabi_init(struct samsung_laptop *samsung) 1381 { 1382 const struct sabi_config *config = NULL; 1383 const struct sabi_commands *commands; 1384 unsigned int ifaceP; 1385 int loca = 0xffff; 1386 int ret = 0; 1387 int i; 1388 1389 samsung->f0000_segment = ioremap(0xf0000, 0xffff); 1390 if (!samsung->f0000_segment) { 1391 if (debug || force) 1392 pr_err("Can't map the segment at 0xf0000\n"); 1393 ret = -EINVAL; 1394 goto exit; 1395 } 1396 1397 samsung_sabi_diag(samsung); 1398 1399 /* Try to find one of the signatures in memory to find the header */ 1400 for (i = 0; sabi_configs[i].test_string != NULL; ++i) { 1401 samsung->config = &sabi_configs[i]; 1402 loca = find_signature(samsung->f0000_segment, 1403 samsung->config->test_string); 1404 if (loca != 0xffff) 1405 break; 1406 } 1407 1408 if (loca == 0xffff) { 1409 if (debug || force) 1410 pr_err("This computer does not support SABI\n"); 1411 ret = -ENODEV; 1412 goto exit; 1413 } 1414 1415 config = samsung->config; 1416 commands = &config->commands; 1417 1418 /* point to the SMI port Number */ 1419 loca += 1; 1420 samsung->sabi = (samsung->f0000_segment + loca); 1421 1422 /* Get a pointer to the SABI Interface */ 1423 ifaceP = (readw(samsung->sabi + config->header_offsets.data_segment) & 0x0ffff) << 4; 1424 ifaceP += readw(samsung->sabi + config->header_offsets.data_offset) & 0x0ffff; 1425 1426 if (debug) 1427 samsung_sabi_infos(samsung, loca, ifaceP); 1428 1429 samsung->sabi_iface = ioremap(ifaceP, 16); 1430 if (!samsung->sabi_iface) { 1431 pr_err("Can't remap %x\n", ifaceP); 1432 ret = -EINVAL; 1433 goto exit; 1434 } 1435 1436 /* Turn on "Linux" mode in the BIOS */ 1437 if (commands->set_linux != 0xff) { 1438 int retval = sabi_set_commandb(samsung, 1439 commands->set_linux, 0x81); 1440 if (retval) { 1441 pr_warn("Linux mode was not set!\n"); 1442 ret = -ENODEV; 1443 goto exit; 1444 } 1445 } 1446 1447 /* Check for stepping quirk */ 1448 if (samsung->handle_backlight) 1449 check_for_stepping_quirk(samsung); 1450 1451 pr_info("detected SABI interface: %s\n", 1452 samsung->config->test_string); 1453 1454 exit: 1455 if (ret) 1456 samsung_sabi_exit(samsung); 1457 1458 return ret; 1459 } 1460 1461 static void samsung_platform_exit(struct samsung_laptop *samsung) 1462 { 1463 if (samsung->platform_device) { 1464 platform_device_unregister(samsung->platform_device); 1465 samsung->platform_device = NULL; 1466 } 1467 } 1468 1469 static int samsung_pm_notification(struct notifier_block *nb, 1470 unsigned long val, void *ptr) 1471 { 1472 struct samsung_laptop *samsung; 1473 1474 samsung = container_of(nb, struct samsung_laptop, pm_nb); 1475 if (val == PM_POST_HIBERNATION && 1476 samsung->quirks->enable_kbd_backlight) 1477 kbd_backlight_enable(samsung); 1478 1479 if (val == PM_POST_HIBERNATION && samsung->quirks->lid_handling) 1480 write_lid_handling(samsung, 1); 1481 1482 return 0; 1483 } 1484 1485 static int __init samsung_platform_init(struct samsung_laptop *samsung) 1486 { 1487 struct platform_device *pdev; 1488 1489 pdev = platform_device_register_simple("samsung", -1, NULL, 0); 1490 if (IS_ERR(pdev)) 1491 return PTR_ERR(pdev); 1492 1493 samsung->platform_device = pdev; 1494 platform_set_drvdata(samsung->platform_device, samsung); 1495 return 0; 1496 } 1497 1498 static struct samsung_quirks *quirks; 1499 1500 static int __init samsung_dmi_matched(const struct dmi_system_id *d) 1501 { 1502 quirks = d->driver_data; 1503 return 0; 1504 } 1505 1506 static const struct dmi_system_id samsung_dmi_table[] __initconst = { 1507 { 1508 .matches = { 1509 DMI_MATCH(DMI_SYS_VENDOR, 1510 "SAMSUNG ELECTRONICS CO., LTD."), 1511 DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */ 1512 }, 1513 }, 1514 { 1515 .matches = { 1516 DMI_MATCH(DMI_SYS_VENDOR, 1517 "SAMSUNG ELECTRONICS CO., LTD."), 1518 DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /* Laptop */ 1519 }, 1520 }, 1521 { 1522 .matches = { 1523 DMI_MATCH(DMI_SYS_VENDOR, 1524 "SAMSUNG ELECTRONICS CO., LTD."), 1525 DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */ 1526 }, 1527 }, 1528 { 1529 .matches = { 1530 DMI_MATCH(DMI_SYS_VENDOR, 1531 "SAMSUNG ELECTRONICS CO., LTD."), 1532 DMI_MATCH(DMI_CHASSIS_TYPE, "14"), /* Sub-Notebook */ 1533 }, 1534 }, 1535 /* DMI ids for laptops with bad Chassis Type */ 1536 { 1537 .ident = "R40/R41", 1538 .matches = { 1539 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1540 DMI_MATCH(DMI_PRODUCT_NAME, "R40/R41"), 1541 DMI_MATCH(DMI_BOARD_NAME, "R40/R41"), 1542 }, 1543 }, 1544 /* Specific DMI ids for laptop with quirks */ 1545 { 1546 .callback = samsung_dmi_matched, 1547 .ident = "N150P", 1548 .matches = { 1549 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1550 DMI_MATCH(DMI_PRODUCT_NAME, "N150P"), 1551 DMI_MATCH(DMI_BOARD_NAME, "N150P"), 1552 }, 1553 .driver_data = &samsung_use_native_backlight, 1554 }, 1555 { 1556 .callback = samsung_dmi_matched, 1557 .ident = "N145P/N250P/N260P", 1558 .matches = { 1559 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1560 DMI_MATCH(DMI_PRODUCT_NAME, "N145P/N250P/N260P"), 1561 DMI_MATCH(DMI_BOARD_NAME, "N145P/N250P/N260P"), 1562 }, 1563 .driver_data = &samsung_use_native_backlight, 1564 }, 1565 { 1566 .callback = samsung_dmi_matched, 1567 .ident = "N150/N210/N220", 1568 .matches = { 1569 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1570 DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220"), 1571 DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220"), 1572 }, 1573 .driver_data = &samsung_broken_acpi_video, 1574 }, 1575 { 1576 .callback = samsung_dmi_matched, 1577 .ident = "NF110/NF210/NF310", 1578 .matches = { 1579 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1580 DMI_MATCH(DMI_PRODUCT_NAME, "NF110/NF210/NF310"), 1581 DMI_MATCH(DMI_BOARD_NAME, "NF110/NF210/NF310"), 1582 }, 1583 .driver_data = &samsung_broken_acpi_video, 1584 }, 1585 { 1586 .callback = samsung_dmi_matched, 1587 .ident = "X360", 1588 .matches = { 1589 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1590 DMI_MATCH(DMI_PRODUCT_NAME, "X360"), 1591 DMI_MATCH(DMI_BOARD_NAME, "X360"), 1592 }, 1593 .driver_data = &samsung_broken_acpi_video, 1594 }, 1595 { 1596 .callback = samsung_dmi_matched, 1597 .ident = "N250P", 1598 .matches = { 1599 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1600 DMI_MATCH(DMI_PRODUCT_NAME, "N250P"), 1601 DMI_MATCH(DMI_BOARD_NAME, "N250P"), 1602 }, 1603 .driver_data = &samsung_use_native_backlight, 1604 }, 1605 { 1606 .callback = samsung_dmi_matched, 1607 .ident = "NC210", 1608 .matches = { 1609 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1610 DMI_MATCH(DMI_PRODUCT_NAME, "NC210/NC110"), 1611 DMI_MATCH(DMI_BOARD_NAME, "NC210/NC110"), 1612 }, 1613 .driver_data = &samsung_broken_acpi_video, 1614 }, 1615 { 1616 .callback = samsung_dmi_matched, 1617 .ident = "730U3E/740U3E", 1618 .matches = { 1619 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1620 DMI_MATCH(DMI_PRODUCT_NAME, "730U3E/740U3E"), 1621 }, 1622 .driver_data = &samsung_np740u3e, 1623 }, 1624 { 1625 .callback = samsung_dmi_matched, 1626 .ident = "300V3Z/300V4Z/300V5Z", 1627 .matches = { 1628 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1629 DMI_MATCH(DMI_PRODUCT_NAME, "300V3Z/300V4Z/300V5Z"), 1630 }, 1631 .driver_data = &samsung_lid_handling, 1632 }, 1633 { }, 1634 }; 1635 MODULE_DEVICE_TABLE(dmi, samsung_dmi_table); 1636 1637 static struct platform_device *samsung_platform_device; 1638 1639 static int __init samsung_init(void) 1640 { 1641 struct samsung_laptop *samsung; 1642 int ret; 1643 1644 if (efi_enabled(EFI_BOOT)) 1645 return -ENODEV; 1646 1647 quirks = &samsung_unknown; 1648 if (!force && !dmi_check_system(samsung_dmi_table)) 1649 return -ENODEV; 1650 1651 samsung = kzalloc(sizeof(*samsung), GFP_KERNEL); 1652 if (!samsung) 1653 return -ENOMEM; 1654 1655 mutex_init(&samsung->sabi_mutex); 1656 samsung->handle_backlight = true; 1657 samsung->quirks = quirks; 1658 1659 #ifdef CONFIG_ACPI 1660 if (samsung->quirks->broken_acpi_video) 1661 acpi_video_set_dmi_backlight_type(acpi_backlight_vendor); 1662 if (samsung->quirks->use_native_backlight) 1663 acpi_video_set_dmi_backlight_type(acpi_backlight_native); 1664 1665 if (acpi_video_get_backlight_type() != acpi_backlight_vendor) 1666 samsung->handle_backlight = false; 1667 #endif 1668 1669 ret = samsung_platform_init(samsung); 1670 if (ret) 1671 goto error_platform; 1672 1673 ret = samsung_sabi_init(samsung); 1674 if (ret) 1675 goto error_sabi; 1676 1677 ret = samsung_sysfs_init(samsung); 1678 if (ret) 1679 goto error_sysfs; 1680 1681 ret = samsung_backlight_init(samsung); 1682 if (ret) 1683 goto error_backlight; 1684 1685 ret = samsung_rfkill_init(samsung); 1686 if (ret) 1687 goto error_rfkill; 1688 1689 ret = samsung_leds_init(samsung); 1690 if (ret) 1691 goto error_leds; 1692 1693 ret = samsung_lid_handling_init(samsung); 1694 if (ret) 1695 goto error_lid_handling; 1696 1697 samsung_debugfs_init(samsung); 1698 1699 samsung->pm_nb.notifier_call = samsung_pm_notification; 1700 register_pm_notifier(&samsung->pm_nb); 1701 1702 samsung_platform_device = samsung->platform_device; 1703 return ret; 1704 1705 error_lid_handling: 1706 samsung_leds_exit(samsung); 1707 error_leds: 1708 samsung_rfkill_exit(samsung); 1709 error_rfkill: 1710 samsung_backlight_exit(samsung); 1711 error_backlight: 1712 samsung_sysfs_exit(samsung); 1713 error_sysfs: 1714 samsung_sabi_exit(samsung); 1715 error_sabi: 1716 samsung_platform_exit(samsung); 1717 error_platform: 1718 kfree(samsung); 1719 return ret; 1720 } 1721 1722 static void __exit samsung_exit(void) 1723 { 1724 struct samsung_laptop *samsung; 1725 1726 samsung = platform_get_drvdata(samsung_platform_device); 1727 unregister_pm_notifier(&samsung->pm_nb); 1728 1729 samsung_debugfs_exit(samsung); 1730 samsung_lid_handling_exit(samsung); 1731 samsung_leds_exit(samsung); 1732 samsung_rfkill_exit(samsung); 1733 samsung_backlight_exit(samsung); 1734 samsung_sysfs_exit(samsung); 1735 samsung_sabi_exit(samsung); 1736 samsung_platform_exit(samsung); 1737 1738 kfree(samsung); 1739 samsung_platform_device = NULL; 1740 } 1741 1742 module_init(samsung_init); 1743 module_exit(samsung_exit); 1744 1745 MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@suse.de>"); 1746 MODULE_DESCRIPTION("Samsung Backlight driver"); 1747 MODULE_LICENSE("GPL"); 1748