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