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