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