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