root/drivers/platform/x86/alienware-wmi.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. dmi_matched
  2. parse_rgb
  3. match_zone
  4. alienware_update_led
  5. zone_show
  6. zone_set
  7. wmax_brightness
  8. global_led_set
  9. global_led_get
  10. show_control_state
  11. store_control_state
  12. alienware_zone_init
  13. alienware_zone_exit
  14. alienware_wmax_command
  15. show_hdmi_cable
  16. show_hdmi_source
  17. toggle_hdmi_source
  18. remove_hdmi
  19. create_hdmi
  20. show_amplifier_status
  21. remove_amplifier
  22. create_amplifier
  23. show_deepsleep_status
  24. toggle_deepsleep
  25. remove_deepsleep
  26. create_deepsleep
  27. alienware_wmi_init
  28. alienware_wmi_exit

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

/* [<][>][^][v][top][bottom][index][help] */