Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.0-rc1 864 lines 21 kB view raw
1/* 2 * Alienware AlienFX control 3 * 4 * Copyright (C) 2014 Dell Inc <mario_limonciello@dell.com> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 */ 17 18#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 19 20#include <linux/acpi.h> 21#include <linux/module.h> 22#include <linux/platform_device.h> 23#include <linux/dmi.h> 24#include <linux/leds.h> 25 26#define LEGACY_CONTROL_GUID "A90597CE-A997-11DA-B012-B622A1EF5492" 27#define LEGACY_POWER_CONTROL_GUID "A80593CE-A997-11DA-B012-B622A1EF5492" 28#define WMAX_CONTROL_GUID "A70591CE-A997-11DA-B012-B622A1EF5492" 29 30#define WMAX_METHOD_HDMI_SOURCE 0x1 31#define WMAX_METHOD_HDMI_STATUS 0x2 32#define WMAX_METHOD_BRIGHTNESS 0x3 33#define WMAX_METHOD_ZONE_CONTROL 0x4 34#define WMAX_METHOD_HDMI_CABLE 0x5 35#define WMAX_METHOD_AMPLIFIER_CABLE 0x6 36#define WMAX_METHOD_DEEP_SLEEP_CONTROL 0x0B 37#define WMAX_METHOD_DEEP_SLEEP_STATUS 0x0C 38 39MODULE_AUTHOR("Mario Limonciello <mario_limonciello@dell.com>"); 40MODULE_DESCRIPTION("Alienware special feature control"); 41MODULE_LICENSE("GPL"); 42MODULE_ALIAS("wmi:" LEGACY_CONTROL_GUID); 43MODULE_ALIAS("wmi:" WMAX_CONTROL_GUID); 44 45enum INTERFACE_FLAGS { 46 LEGACY, 47 WMAX, 48}; 49 50enum LEGACY_CONTROL_STATES { 51 LEGACY_RUNNING = 1, 52 LEGACY_BOOTING = 0, 53 LEGACY_SUSPEND = 3, 54}; 55 56enum WMAX_CONTROL_STATES { 57 WMAX_RUNNING = 0xFF, 58 WMAX_BOOTING = 0, 59 WMAX_SUSPEND = 3, 60}; 61 62struct quirk_entry { 63 u8 num_zones; 64 u8 hdmi_mux; 65 u8 amplifier; 66 u8 deepslp; 67}; 68 69static struct quirk_entry *quirks; 70 71 72static struct quirk_entry quirk_inspiron5675 = { 73 .num_zones = 2, 74 .hdmi_mux = 0, 75 .amplifier = 0, 76 .deepslp = 0, 77}; 78 79static struct quirk_entry quirk_unknown = { 80 .num_zones = 2, 81 .hdmi_mux = 0, 82 .amplifier = 0, 83 .deepslp = 0, 84}; 85 86static struct quirk_entry quirk_x51_r1_r2 = { 87 .num_zones = 3, 88 .hdmi_mux = 0, 89 .amplifier = 0, 90 .deepslp = 0, 91}; 92 93static struct quirk_entry quirk_x51_r3 = { 94 .num_zones = 4, 95 .hdmi_mux = 0, 96 .amplifier = 1, 97 .deepslp = 0, 98}; 99 100static struct quirk_entry quirk_asm100 = { 101 .num_zones = 2, 102 .hdmi_mux = 1, 103 .amplifier = 0, 104 .deepslp = 0, 105}; 106 107static struct quirk_entry quirk_asm200 = { 108 .num_zones = 2, 109 .hdmi_mux = 1, 110 .amplifier = 0, 111 .deepslp = 1, 112}; 113 114static struct quirk_entry quirk_asm201 = { 115 .num_zones = 2, 116 .hdmi_mux = 1, 117 .amplifier = 1, 118 .deepslp = 1, 119}; 120 121static int __init dmi_matched(const struct dmi_system_id *dmi) 122{ 123 quirks = dmi->driver_data; 124 return 1; 125} 126 127static const struct dmi_system_id alienware_quirks[] __initconst = { 128 { 129 .callback = dmi_matched, 130 .ident = "Alienware X51 R3", 131 .matches = { 132 DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), 133 DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"), 134 }, 135 .driver_data = &quirk_x51_r3, 136 }, 137 { 138 .callback = dmi_matched, 139 .ident = "Alienware X51 R2", 140 .matches = { 141 DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), 142 DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"), 143 }, 144 .driver_data = &quirk_x51_r1_r2, 145 }, 146 { 147 .callback = dmi_matched, 148 .ident = "Alienware X51 R1", 149 .matches = { 150 DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), 151 DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"), 152 }, 153 .driver_data = &quirk_x51_r1_r2, 154 }, 155 { 156 .callback = dmi_matched, 157 .ident = "Alienware ASM100", 158 .matches = { 159 DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), 160 DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"), 161 }, 162 .driver_data = &quirk_asm100, 163 }, 164 { 165 .callback = dmi_matched, 166 .ident = "Alienware ASM200", 167 .matches = { 168 DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), 169 DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"), 170 }, 171 .driver_data = &quirk_asm200, 172 }, 173 { 174 .callback = dmi_matched, 175 .ident = "Alienware ASM201", 176 .matches = { 177 DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), 178 DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"), 179 }, 180 .driver_data = &quirk_asm201, 181 }, 182 { 183 .callback = dmi_matched, 184 .ident = "Dell Inc. Inspiron 5675", 185 .matches = { 186 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 187 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"), 188 }, 189 .driver_data = &quirk_inspiron5675, 190 }, 191 {} 192}; 193 194struct color_platform { 195 u8 blue; 196 u8 green; 197 u8 red; 198} __packed; 199 200struct platform_zone { 201 u8 location; 202 struct device_attribute *attr; 203 struct color_platform colors; 204}; 205 206struct wmax_brightness_args { 207 u32 led_mask; 208 u32 percentage; 209}; 210 211struct wmax_basic_args { 212 u8 arg; 213}; 214 215struct legacy_led_args { 216 struct color_platform colors; 217 u8 brightness; 218 u8 state; 219} __packed; 220 221struct wmax_led_args { 222 u32 led_mask; 223 struct color_platform colors; 224 u8 state; 225} __packed; 226 227static struct platform_device *platform_device; 228static struct device_attribute *zone_dev_attrs; 229static struct attribute **zone_attrs; 230static struct platform_zone *zone_data; 231 232static struct platform_driver platform_driver = { 233 .driver = { 234 .name = "alienware-wmi", 235 } 236}; 237 238static struct attribute_group zone_attribute_group = { 239 .name = "rgb_zones", 240}; 241 242static u8 interface; 243static u8 lighting_control_state; 244static u8 global_brightness; 245 246/* 247 * Helpers used for zone control 248 */ 249static int parse_rgb(const char *buf, struct platform_zone *zone) 250{ 251 long unsigned int rgb; 252 int ret; 253 union color_union { 254 struct color_platform cp; 255 int package; 256 } repackager; 257 258 ret = kstrtoul(buf, 16, &rgb); 259 if (ret) 260 return ret; 261 262 /* RGB triplet notation is 24-bit hexadecimal */ 263 if (rgb > 0xFFFFFF) 264 return -EINVAL; 265 266 repackager.package = rgb & 0x0f0f0f0f; 267 pr_debug("alienware-wmi: r: %d g:%d b: %d\n", 268 repackager.cp.red, repackager.cp.green, repackager.cp.blue); 269 zone->colors = repackager.cp; 270 return 0; 271} 272 273static struct platform_zone *match_zone(struct device_attribute *attr) 274{ 275 u8 zone; 276 277 for (zone = 0; zone < quirks->num_zones; zone++) { 278 if ((struct device_attribute *)zone_data[zone].attr == attr) { 279 pr_debug("alienware-wmi: matched zone location: %d\n", 280 zone_data[zone].location); 281 return &zone_data[zone]; 282 } 283 } 284 return NULL; 285} 286 287/* 288 * Individual RGB zone control 289 */ 290static int alienware_update_led(struct platform_zone *zone) 291{ 292 int method_id; 293 acpi_status status; 294 char *guid; 295 struct acpi_buffer input; 296 struct legacy_led_args legacy_args; 297 struct wmax_led_args wmax_basic_args; 298 if (interface == WMAX) { 299 wmax_basic_args.led_mask = 1 << zone->location; 300 wmax_basic_args.colors = zone->colors; 301 wmax_basic_args.state = lighting_control_state; 302 guid = WMAX_CONTROL_GUID; 303 method_id = WMAX_METHOD_ZONE_CONTROL; 304 305 input.length = (acpi_size) sizeof(wmax_basic_args); 306 input.pointer = &wmax_basic_args; 307 } else { 308 legacy_args.colors = zone->colors; 309 legacy_args.brightness = global_brightness; 310 legacy_args.state = 0; 311 if (lighting_control_state == LEGACY_BOOTING || 312 lighting_control_state == LEGACY_SUSPEND) { 313 guid = LEGACY_POWER_CONTROL_GUID; 314 legacy_args.state = lighting_control_state; 315 } else 316 guid = LEGACY_CONTROL_GUID; 317 method_id = zone->location + 1; 318 319 input.length = (acpi_size) sizeof(legacy_args); 320 input.pointer = &legacy_args; 321 } 322 pr_debug("alienware-wmi: guid %s method %d\n", guid, method_id); 323 324 status = wmi_evaluate_method(guid, 0, method_id, &input, NULL); 325 if (ACPI_FAILURE(status)) 326 pr_err("alienware-wmi: zone set failure: %u\n", status); 327 return ACPI_FAILURE(status); 328} 329 330static ssize_t zone_show(struct device *dev, struct device_attribute *attr, 331 char *buf) 332{ 333 struct platform_zone *target_zone; 334 target_zone = match_zone(attr); 335 if (target_zone == NULL) 336 return sprintf(buf, "red: -1, green: -1, blue: -1\n"); 337 return sprintf(buf, "red: %d, green: %d, blue: %d\n", 338 target_zone->colors.red, 339 target_zone->colors.green, target_zone->colors.blue); 340 341} 342 343static ssize_t zone_set(struct device *dev, struct device_attribute *attr, 344 const char *buf, size_t count) 345{ 346 struct platform_zone *target_zone; 347 int ret; 348 target_zone = match_zone(attr); 349 if (target_zone == NULL) { 350 pr_err("alienware-wmi: invalid target zone\n"); 351 return 1; 352 } 353 ret = parse_rgb(buf, target_zone); 354 if (ret) 355 return ret; 356 ret = alienware_update_led(target_zone); 357 return ret ? ret : count; 358} 359 360/* 361 * LED Brightness (Global) 362 */ 363static int wmax_brightness(int brightness) 364{ 365 acpi_status status; 366 struct acpi_buffer input; 367 struct wmax_brightness_args args = { 368 .led_mask = 0xFF, 369 .percentage = brightness, 370 }; 371 input.length = (acpi_size) sizeof(args); 372 input.pointer = &args; 373 status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0, 374 WMAX_METHOD_BRIGHTNESS, &input, NULL); 375 if (ACPI_FAILURE(status)) 376 pr_err("alienware-wmi: brightness set failure: %u\n", status); 377 return ACPI_FAILURE(status); 378} 379 380static void global_led_set(struct led_classdev *led_cdev, 381 enum led_brightness brightness) 382{ 383 int ret; 384 global_brightness = brightness; 385 if (interface == WMAX) 386 ret = wmax_brightness(brightness); 387 else 388 ret = alienware_update_led(&zone_data[0]); 389 if (ret) 390 pr_err("LED brightness update failed\n"); 391} 392 393static enum led_brightness global_led_get(struct led_classdev *led_cdev) 394{ 395 return global_brightness; 396} 397 398static struct led_classdev global_led = { 399 .brightness_set = global_led_set, 400 .brightness_get = global_led_get, 401 .name = "alienware::global_brightness", 402}; 403 404/* 405 * Lighting control state device attribute (Global) 406 */ 407static ssize_t show_control_state(struct device *dev, 408 struct device_attribute *attr, char *buf) 409{ 410 if (lighting_control_state == LEGACY_BOOTING) 411 return scnprintf(buf, PAGE_SIZE, "[booting] running suspend\n"); 412 else if (lighting_control_state == LEGACY_SUSPEND) 413 return scnprintf(buf, PAGE_SIZE, "booting running [suspend]\n"); 414 return scnprintf(buf, PAGE_SIZE, "booting [running] suspend\n"); 415} 416 417static ssize_t store_control_state(struct device *dev, 418 struct device_attribute *attr, 419 const char *buf, size_t count) 420{ 421 long unsigned int val; 422 if (strcmp(buf, "booting\n") == 0) 423 val = LEGACY_BOOTING; 424 else if (strcmp(buf, "suspend\n") == 0) 425 val = LEGACY_SUSPEND; 426 else if (interface == LEGACY) 427 val = LEGACY_RUNNING; 428 else 429 val = WMAX_RUNNING; 430 lighting_control_state = val; 431 pr_debug("alienware-wmi: updated control state to %d\n", 432 lighting_control_state); 433 return count; 434} 435 436static DEVICE_ATTR(lighting_control_state, 0644, show_control_state, 437 store_control_state); 438 439static int alienware_zone_init(struct platform_device *dev) 440{ 441 u8 zone; 442 char buffer[10]; 443 char *name; 444 445 if (interface == WMAX) { 446 lighting_control_state = WMAX_RUNNING; 447 } else if (interface == LEGACY) { 448 lighting_control_state = LEGACY_RUNNING; 449 } 450 global_led.max_brightness = 0x0F; 451 global_brightness = global_led.max_brightness; 452 453 /* 454 * - zone_dev_attrs num_zones + 1 is for individual zones and then 455 * null terminated 456 * - zone_attrs num_zones + 2 is for all attrs in zone_dev_attrs + 457 * the lighting control + null terminated 458 * - zone_data num_zones is for the distinct zones 459 */ 460 zone_dev_attrs = 461 kcalloc(quirks->num_zones + 1, sizeof(struct device_attribute), 462 GFP_KERNEL); 463 if (!zone_dev_attrs) 464 return -ENOMEM; 465 466 zone_attrs = 467 kcalloc(quirks->num_zones + 2, sizeof(struct attribute *), 468 GFP_KERNEL); 469 if (!zone_attrs) 470 return -ENOMEM; 471 472 zone_data = 473 kcalloc(quirks->num_zones, sizeof(struct platform_zone), 474 GFP_KERNEL); 475 if (!zone_data) 476 return -ENOMEM; 477 478 for (zone = 0; zone < quirks->num_zones; zone++) { 479 sprintf(buffer, "zone%02hhX", zone); 480 name = kstrdup(buffer, GFP_KERNEL); 481 if (name == NULL) 482 return 1; 483 sysfs_attr_init(&zone_dev_attrs[zone].attr); 484 zone_dev_attrs[zone].attr.name = name; 485 zone_dev_attrs[zone].attr.mode = 0644; 486 zone_dev_attrs[zone].show = zone_show; 487 zone_dev_attrs[zone].store = zone_set; 488 zone_data[zone].location = zone; 489 zone_attrs[zone] = &zone_dev_attrs[zone].attr; 490 zone_data[zone].attr = &zone_dev_attrs[zone]; 491 } 492 zone_attrs[quirks->num_zones] = &dev_attr_lighting_control_state.attr; 493 zone_attribute_group.attrs = zone_attrs; 494 495 led_classdev_register(&dev->dev, &global_led); 496 497 return sysfs_create_group(&dev->dev.kobj, &zone_attribute_group); 498} 499 500static void alienware_zone_exit(struct platform_device *dev) 501{ 502 u8 zone; 503 504 sysfs_remove_group(&dev->dev.kobj, &zone_attribute_group); 505 led_classdev_unregister(&global_led); 506 if (zone_dev_attrs) { 507 for (zone = 0; zone < quirks->num_zones; zone++) 508 kfree(zone_dev_attrs[zone].attr.name); 509 } 510 kfree(zone_dev_attrs); 511 kfree(zone_data); 512 kfree(zone_attrs); 513} 514 515static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args, 516 u32 command, int *out_data) 517{ 518 acpi_status status; 519 union acpi_object *obj; 520 struct acpi_buffer input; 521 struct acpi_buffer output; 522 523 input.length = (acpi_size) sizeof(*in_args); 524 input.pointer = in_args; 525 if (out_data != NULL) { 526 output.length = ACPI_ALLOCATE_BUFFER; 527 output.pointer = NULL; 528 status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0, 529 command, &input, &output); 530 } else 531 status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0, 532 command, &input, NULL); 533 534 if (ACPI_SUCCESS(status) && out_data != NULL) { 535 obj = (union acpi_object *)output.pointer; 536 if (obj && obj->type == ACPI_TYPE_INTEGER) 537 *out_data = (u32) obj->integer.value; 538 } 539 kfree(output.pointer); 540 return status; 541 542} 543 544/* 545 * The HDMI mux sysfs node indicates the status of the HDMI input mux. 546 * It can toggle between standard system GPU output and HDMI input. 547 */ 548static ssize_t show_hdmi_cable(struct device *dev, 549 struct device_attribute *attr, char *buf) 550{ 551 acpi_status status; 552 u32 out_data; 553 struct wmax_basic_args in_args = { 554 .arg = 0, 555 }; 556 status = 557 alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE, 558 (u32 *) &out_data); 559 if (ACPI_SUCCESS(status)) { 560 if (out_data == 0) 561 return scnprintf(buf, PAGE_SIZE, 562 "[unconnected] connected unknown\n"); 563 else if (out_data == 1) 564 return scnprintf(buf, PAGE_SIZE, 565 "unconnected [connected] unknown\n"); 566 } 567 pr_err("alienware-wmi: unknown HDMI cable status: %d\n", status); 568 return scnprintf(buf, PAGE_SIZE, "unconnected connected [unknown]\n"); 569} 570 571static ssize_t show_hdmi_source(struct device *dev, 572 struct device_attribute *attr, char *buf) 573{ 574 acpi_status status; 575 u32 out_data; 576 struct wmax_basic_args in_args = { 577 .arg = 0, 578 }; 579 status = 580 alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS, 581 (u32 *) &out_data); 582 583 if (ACPI_SUCCESS(status)) { 584 if (out_data == 1) 585 return scnprintf(buf, PAGE_SIZE, 586 "[input] gpu unknown\n"); 587 else if (out_data == 2) 588 return scnprintf(buf, PAGE_SIZE, 589 "input [gpu] unknown\n"); 590 } 591 pr_err("alienware-wmi: unknown HDMI source status: %d\n", out_data); 592 return scnprintf(buf, PAGE_SIZE, "input gpu [unknown]\n"); 593} 594 595static ssize_t toggle_hdmi_source(struct device *dev, 596 struct device_attribute *attr, 597 const char *buf, size_t count) 598{ 599 acpi_status status; 600 struct wmax_basic_args args; 601 if (strcmp(buf, "gpu\n") == 0) 602 args.arg = 1; 603 else if (strcmp(buf, "input\n") == 0) 604 args.arg = 2; 605 else 606 args.arg = 3; 607 pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf); 608 609 status = alienware_wmax_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL); 610 611 if (ACPI_FAILURE(status)) 612 pr_err("alienware-wmi: HDMI toggle failed: results: %u\n", 613 status); 614 return count; 615} 616 617static DEVICE_ATTR(cable, S_IRUGO, show_hdmi_cable, NULL); 618static DEVICE_ATTR(source, S_IRUGO | S_IWUSR, show_hdmi_source, 619 toggle_hdmi_source); 620 621static struct attribute *hdmi_attrs[] = { 622 &dev_attr_cable.attr, 623 &dev_attr_source.attr, 624 NULL, 625}; 626 627static const struct attribute_group hdmi_attribute_group = { 628 .name = "hdmi", 629 .attrs = hdmi_attrs, 630}; 631 632static void remove_hdmi(struct platform_device *dev) 633{ 634 if (quirks->hdmi_mux > 0) 635 sysfs_remove_group(&dev->dev.kobj, &hdmi_attribute_group); 636} 637 638static int create_hdmi(struct platform_device *dev) 639{ 640 int ret; 641 642 ret = sysfs_create_group(&dev->dev.kobj, &hdmi_attribute_group); 643 if (ret) 644 remove_hdmi(dev); 645 return ret; 646} 647 648/* 649 * Alienware GFX amplifier support 650 * - Currently supports reading cable status 651 * - Leaving expansion room to possibly support dock/undock events later 652 */ 653static ssize_t show_amplifier_status(struct device *dev, 654 struct device_attribute *attr, char *buf) 655{ 656 acpi_status status; 657 u32 out_data; 658 struct wmax_basic_args in_args = { 659 .arg = 0, 660 }; 661 status = 662 alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE, 663 (u32 *) &out_data); 664 if (ACPI_SUCCESS(status)) { 665 if (out_data == 0) 666 return scnprintf(buf, PAGE_SIZE, 667 "[unconnected] connected unknown\n"); 668 else if (out_data == 1) 669 return scnprintf(buf, PAGE_SIZE, 670 "unconnected [connected] unknown\n"); 671 } 672 pr_err("alienware-wmi: unknown amplifier cable status: %d\n", status); 673 return scnprintf(buf, PAGE_SIZE, "unconnected connected [unknown]\n"); 674} 675 676static DEVICE_ATTR(status, S_IRUGO, show_amplifier_status, NULL); 677 678static struct attribute *amplifier_attrs[] = { 679 &dev_attr_status.attr, 680 NULL, 681}; 682 683static const struct attribute_group amplifier_attribute_group = { 684 .name = "amplifier", 685 .attrs = amplifier_attrs, 686}; 687 688static void remove_amplifier(struct platform_device *dev) 689{ 690 if (quirks->amplifier > 0) 691 sysfs_remove_group(&dev->dev.kobj, &amplifier_attribute_group); 692} 693 694static int create_amplifier(struct platform_device *dev) 695{ 696 int ret; 697 698 ret = sysfs_create_group(&dev->dev.kobj, &amplifier_attribute_group); 699 if (ret) 700 remove_amplifier(dev); 701 return ret; 702} 703 704/* 705 * Deep Sleep Control support 706 * - Modifies BIOS setting for deep sleep control allowing extra wakeup events 707 */ 708static ssize_t show_deepsleep_status(struct device *dev, 709 struct device_attribute *attr, char *buf) 710{ 711 acpi_status status; 712 u32 out_data; 713 struct wmax_basic_args in_args = { 714 .arg = 0, 715 }; 716 status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS, 717 (u32 *) &out_data); 718 if (ACPI_SUCCESS(status)) { 719 if (out_data == 0) 720 return scnprintf(buf, PAGE_SIZE, 721 "[disabled] s5 s5_s4\n"); 722 else if (out_data == 1) 723 return scnprintf(buf, PAGE_SIZE, 724 "disabled [s5] s5_s4\n"); 725 else if (out_data == 2) 726 return scnprintf(buf, PAGE_SIZE, 727 "disabled s5 [s5_s4]\n"); 728 } 729 pr_err("alienware-wmi: unknown deep sleep status: %d\n", status); 730 return scnprintf(buf, PAGE_SIZE, "disabled s5 s5_s4 [unknown]\n"); 731} 732 733static ssize_t toggle_deepsleep(struct device *dev, 734 struct device_attribute *attr, 735 const char *buf, size_t count) 736{ 737 acpi_status status; 738 struct wmax_basic_args args; 739 740 if (strcmp(buf, "disabled\n") == 0) 741 args.arg = 0; 742 else if (strcmp(buf, "s5\n") == 0) 743 args.arg = 1; 744 else 745 args.arg = 2; 746 pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf); 747 748 status = alienware_wmax_command(&args, WMAX_METHOD_DEEP_SLEEP_CONTROL, 749 NULL); 750 751 if (ACPI_FAILURE(status)) 752 pr_err("alienware-wmi: deep sleep control failed: results: %u\n", 753 status); 754 return count; 755} 756 757static DEVICE_ATTR(deepsleep, S_IRUGO | S_IWUSR, show_deepsleep_status, toggle_deepsleep); 758 759static struct attribute *deepsleep_attrs[] = { 760 &dev_attr_deepsleep.attr, 761 NULL, 762}; 763 764static const struct attribute_group deepsleep_attribute_group = { 765 .name = "deepsleep", 766 .attrs = deepsleep_attrs, 767}; 768 769static void remove_deepsleep(struct platform_device *dev) 770{ 771 if (quirks->deepslp > 0) 772 sysfs_remove_group(&dev->dev.kobj, &deepsleep_attribute_group); 773} 774 775static int create_deepsleep(struct platform_device *dev) 776{ 777 int ret; 778 779 ret = sysfs_create_group(&dev->dev.kobj, &deepsleep_attribute_group); 780 if (ret) 781 remove_deepsleep(dev); 782 return ret; 783} 784 785static int __init alienware_wmi_init(void) 786{ 787 int ret; 788 789 if (wmi_has_guid(LEGACY_CONTROL_GUID)) 790 interface = LEGACY; 791 else if (wmi_has_guid(WMAX_CONTROL_GUID)) 792 interface = WMAX; 793 else { 794 pr_warn("alienware-wmi: No known WMI GUID found\n"); 795 return -ENODEV; 796 } 797 798 dmi_check_system(alienware_quirks); 799 if (quirks == NULL) 800 quirks = &quirk_unknown; 801 802 ret = platform_driver_register(&platform_driver); 803 if (ret) 804 goto fail_platform_driver; 805 platform_device = platform_device_alloc("alienware-wmi", -1); 806 if (!platform_device) { 807 ret = -ENOMEM; 808 goto fail_platform_device1; 809 } 810 ret = platform_device_add(platform_device); 811 if (ret) 812 goto fail_platform_device2; 813 814 if (quirks->hdmi_mux > 0) { 815 ret = create_hdmi(platform_device); 816 if (ret) 817 goto fail_prep_hdmi; 818 } 819 820 if (quirks->amplifier > 0) { 821 ret = create_amplifier(platform_device); 822 if (ret) 823 goto fail_prep_amplifier; 824 } 825 826 if (quirks->deepslp > 0) { 827 ret = create_deepsleep(platform_device); 828 if (ret) 829 goto fail_prep_deepsleep; 830 } 831 832 ret = alienware_zone_init(platform_device); 833 if (ret) 834 goto fail_prep_zones; 835 836 return 0; 837 838fail_prep_zones: 839 alienware_zone_exit(platform_device); 840fail_prep_deepsleep: 841fail_prep_amplifier: 842fail_prep_hdmi: 843 platform_device_del(platform_device); 844fail_platform_device2: 845 platform_device_put(platform_device); 846fail_platform_device1: 847 platform_driver_unregister(&platform_driver); 848fail_platform_driver: 849 return ret; 850} 851 852module_init(alienware_wmi_init); 853 854static void __exit alienware_wmi_exit(void) 855{ 856 if (platform_device) { 857 alienware_zone_exit(platform_device); 858 remove_hdmi(platform_device); 859 platform_device_unregister(platform_device); 860 platform_driver_unregister(&platform_driver); 861 } 862} 863 864module_exit(alienware_wmi_exit);