1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Alienware AlienFX control
8 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10 #include <linux/acpi.h>
11 #include <linux/bitfield.h>
12 #include <linux/bits.h>
13 #include <linux/module.h>
14 #include <linux/platform_device.h>
15 #include <linux/platform_profile.h>
16 #include <linux/dmi.h>
17 #include <linux/leds.h>
19 #define LEGACY_CONTROL_GUID "A90597CE-A997-11DA-B012-B622A1EF5492"
20 #define LEGACY_POWER_CONTROL_GUID "A80593CE-A997-11DA-B012-B622A1EF5492"
21 #define WMAX_CONTROL_GUID "A70591CE-A997-11DA-B012-B622A1EF5492"
23 #define WMAX_METHOD_HDMI_SOURCE 0x1
24 #define WMAX_METHOD_HDMI_STATUS 0x2
25 #define WMAX_METHOD_BRIGHTNESS 0x3
26 #define WMAX_METHOD_ZONE_CONTROL 0x4
27 #define WMAX_METHOD_HDMI_CABLE 0x5
28 #define WMAX_METHOD_AMPLIFIER_CABLE 0x6
29 #define WMAX_METHOD_DEEP_SLEEP_CONTROL 0x0B
30 #define WMAX_METHOD_DEEP_SLEEP_STATUS 0x0C
31 #define WMAX_METHOD_THERMAL_INFORMATION 0x14
32 #define WMAX_METHOD_THERMAL_CONTROL 0x15
33 #define WMAX_METHOD_GAME_SHIFT_STATUS 0x25
35 #define WMAX_THERMAL_MODE_GMODE 0xAB
37 #define WMAX_FAILURE_CODE 0xFFFFFFFF
40 MODULE_DESCRIPTION("Alienware special feature control");
41 MODULE_LICENSE("GPL");
42 MODULE_ALIAS("wmi:" LEGACY_CONTROL_GUID);
43 MODULE_ALIAS("wmi:" WMAX_CONTROL_GUID);
45 static bool force_platform_profile;
46 module_param_unsafe(force_platform_profile, bool, 0);
47 MODULE_PARM_DESC(force_platform_profile, "Forces auto-detecting thermal profiles without checking if WMI thermal backend is available");
49 static bool force_gmode;
50 module_param_unsafe(force_gmode, bool, 0);
51 MODULE_PARM_DESC(force_gmode, "Forces G-Mode when performance profile is selected");
53 enum INTERFACE_FLAGS {
58 enum LEGACY_CONTROL_STATES {
64 enum WMAX_CONTROL_STATES {
70 enum WMAX_THERMAL_INFORMATION_OPERATIONS {
71 WMAX_OPERATION_SYS_DESCRIPTION = 0x02,
72 WMAX_OPERATION_LIST_IDS = 0x03,
73 WMAX_OPERATION_CURRENT_PROFILE = 0x0B,
76 enum WMAX_THERMAL_CONTROL_OPERATIONS {
77 WMAX_OPERATION_ACTIVATE_PROFILE = 0x01,
80 enum WMAX_GAME_SHIFT_STATUS_OPERATIONS {
81 WMAX_OPERATION_TOGGLE_GAME_SHIFT = 0x01,
82 WMAX_OPERATION_GET_GAME_SHIFT_STATUS = 0x02,
85 enum WMAX_THERMAL_TABLES {
86 WMAX_THERMAL_TABLE_BASIC = 0x90,
87 WMAX_THERMAL_TABLE_USTT = 0xA0,
90 enum wmax_thermal_mode {
91 THERMAL_MODE_USTT_BALANCED,
92 THERMAL_MODE_USTT_BALANCED_PERFORMANCE,
93 THERMAL_MODE_USTT_COOL,
94 THERMAL_MODE_USTT_QUIET,
95 THERMAL_MODE_USTT_PERFORMANCE,
96 THERMAL_MODE_USTT_LOW_POWER,
97 THERMAL_MODE_BASIC_QUIET,
98 THERMAL_MODE_BASIC_BALANCED,
99 THERMAL_MODE_BASIC_BALANCED_PERFORMANCE,
100 THERMAL_MODE_BASIC_PERFORMANCE,
104 static const enum platform_profile_option wmax_mode_to_platform_profile[THERMAL_MODE_LAST] = {
105 [THERMAL_MODE_USTT_BALANCED] = PLATFORM_PROFILE_BALANCED,
106 [THERMAL_MODE_USTT_BALANCED_PERFORMANCE] = PLATFORM_PROFILE_BALANCED_PERFORMANCE,
107 [THERMAL_MODE_USTT_COOL] = PLATFORM_PROFILE_COOL,
108 [THERMAL_MODE_USTT_QUIET] = PLATFORM_PROFILE_QUIET,
109 [THERMAL_MODE_USTT_PERFORMANCE] = PLATFORM_PROFILE_PERFORMANCE,
110 [THERMAL_MODE_USTT_LOW_POWER] = PLATFORM_PROFILE_LOW_POWER,
111 [THERMAL_MODE_BASIC_QUIET] = PLATFORM_PROFILE_QUIET,
112 [THERMAL_MODE_BASIC_BALANCED] = PLATFORM_PROFILE_BALANCED,
113 [THERMAL_MODE_BASIC_BALANCED_PERFORMANCE] = PLATFORM_PROFILE_BALANCED_PERFORMANCE,
114 [THERMAL_MODE_BASIC_PERFORMANCE] = PLATFORM_PROFILE_PERFORMANCE,
126 static struct quirk_entry *quirks;
129 static struct quirk_entry quirk_inspiron5675 = {
138 static struct quirk_entry quirk_unknown = {
147 static struct quirk_entry quirk_x51_r1_r2 = {
156 static struct quirk_entry quirk_x51_r3 = {
165 static struct quirk_entry quirk_asm100 = {
174 static struct quirk_entry quirk_asm200 = {
183 static struct quirk_entry quirk_asm201 = {
192 static struct quirk_entry quirk_g_series = {
201 static struct quirk_entry quirk_x_series = {
210 static int __init dmi_matched(const struct dmi_system_id *dmi)
212 quirks = dmi->driver_data;
216 static const struct dmi_system_id alienware_quirks[] __initconst = {
218 .callback = dmi_matched,
219 .ident = "Alienware ASM100",
221 DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
222 DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"),
224 .driver_data = &quirk_asm100,
227 .callback = dmi_matched,
228 .ident = "Alienware ASM200",
230 DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
231 DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"),
233 .driver_data = &quirk_asm200,
236 .callback = dmi_matched,
237 .ident = "Alienware ASM201",
239 DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
240 DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"),
242 .driver_data = &quirk_asm201,
245 .callback = dmi_matched,
246 .ident = "Alienware m16 R1 AMD",
248 DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
249 DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m16 R1 AMD"),
251 .driver_data = &quirk_x_series,
254 .callback = dmi_matched,
255 .ident = "Alienware m17 R5",
257 DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
258 DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m17 R5 AMD"),
260 .driver_data = &quirk_x_series,
263 .callback = dmi_matched,
264 .ident = "Alienware m18 R2",
266 DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
267 DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m18 R2"),
269 .driver_data = &quirk_x_series,
272 .callback = dmi_matched,
273 .ident = "Alienware x15 R1",
275 DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
276 DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R1"),
278 .driver_data = &quirk_x_series,
281 .callback = dmi_matched,
282 .ident = "Alienware x17 R2",
284 DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
285 DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x17 R2"),
287 .driver_data = &quirk_x_series,
290 .callback = dmi_matched,
291 .ident = "Alienware X51 R1",
293 DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
294 DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"),
296 .driver_data = &quirk_x51_r1_r2,
299 .callback = dmi_matched,
300 .ident = "Alienware X51 R2",
302 DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
303 DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"),
305 .driver_data = &quirk_x51_r1_r2,
308 .callback = dmi_matched,
309 .ident = "Alienware X51 R3",
311 DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
312 DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"),
314 .driver_data = &quirk_x51_r3,
317 .callback = dmi_matched,
318 .ident = "Dell Inc. G15 5510",
320 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
321 DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5510"),
323 .driver_data = &quirk_g_series,
326 .callback = dmi_matched,
327 .ident = "Dell Inc. G15 5511",
329 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
330 DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5511"),
332 .driver_data = &quirk_g_series,
335 .callback = dmi_matched,
336 .ident = "Dell Inc. G15 5515",
338 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
339 DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5515"),
341 .driver_data = &quirk_g_series,
344 .callback = dmi_matched,
345 .ident = "Dell Inc. G3 3500",
347 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
348 DMI_MATCH(DMI_PRODUCT_NAME, "G3 3500"),
350 .driver_data = &quirk_g_series,
353 .callback = dmi_matched,
354 .ident = "Dell Inc. G3 3590",
356 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
357 DMI_MATCH(DMI_PRODUCT_NAME, "G3 3590"),
359 .driver_data = &quirk_g_series,
362 .callback = dmi_matched,
363 .ident = "Dell Inc. G5 5500",
365 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
366 DMI_MATCH(DMI_PRODUCT_NAME, "G5 5500"),
368 .driver_data = &quirk_g_series,
371 .callback = dmi_matched,
372 .ident = "Dell Inc. Inspiron 5675",
374 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
375 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"),
377 .driver_data = &quirk_inspiron5675,
382 struct color_platform {
388 struct wmax_brightness_args {
393 struct wmax_basic_args {
397 struct legacy_led_args {
398 struct color_platform colors;
403 struct wmax_led_args {
405 struct color_platform colors;
409 struct wmax_u32_args {
416 static struct platform_device *platform_device;
417 static struct color_platform colors[4];
418 static enum wmax_thermal_mode supported_thermal_profiles[PLATFORM_PROFILE_LAST];
421 static u8 lighting_control_state;
422 static u8 global_brightness;
425 * Helpers used for zone control
427 static int parse_rgb(const char *buf, struct color_platform *colors)
429 long unsigned int rgb;
432 struct color_platform cp;
436 ret = kstrtoul(buf, 16, &rgb);
440 /* RGB triplet notation is 24-bit hexadecimal */
444 repackager.package = rgb & 0x0f0f0f0f;
445 pr_debug("alienware-wmi: r: %d g:%d b: %d\n",
446 repackager.cp.red, repackager.cp.green, repackager.cp.blue);
447 *colors = repackager.cp;
452 * Individual RGB zone control
454 static int alienware_update_led(u8 location)
459 struct acpi_buffer input;
460 struct legacy_led_args legacy_args;
461 struct wmax_led_args wmax_basic_args;
462 if (interface == WMAX) {
463 wmax_basic_args.led_mask = 1 << location;
464 wmax_basic_args.colors = colors[location];
465 wmax_basic_args.state = lighting_control_state;
466 guid = WMAX_CONTROL_GUID;
467 method_id = WMAX_METHOD_ZONE_CONTROL;
469 input.length = sizeof(wmax_basic_args);
470 input.pointer = &wmax_basic_args;
472 legacy_args.colors = colors[location];
473 legacy_args.brightness = global_brightness;
474 legacy_args.state = 0;
475 if (lighting_control_state == LEGACY_BOOTING ||
476 lighting_control_state == LEGACY_SUSPEND) {
477 guid = LEGACY_POWER_CONTROL_GUID;
478 legacy_args.state = lighting_control_state;
480 guid = LEGACY_CONTROL_GUID;
481 method_id = location + 1;
483 input.length = sizeof(legacy_args);
484 input.pointer = &legacy_args;
486 pr_debug("alienware-wmi: guid %s method %d\n", guid, method_id);
488 status = wmi_evaluate_method(guid, 0, method_id, &input, NULL);
489 if (ACPI_FAILURE(status))
490 pr_err("alienware-wmi: zone set failure: %u\n", status);
491 return ACPI_FAILURE(status);
494 static ssize_t zone_show(struct device *dev, struct device_attribute *attr,
495 char *buf, u8 location)
497 return sprintf(buf, "red: %d, green: %d, blue: %d\n",
498 colors[location].red, colors[location].green,
499 colors[location].blue);
503 static ssize_t zone_store(struct device *dev, struct device_attribute *attr,
504 const char *buf, size_t count, u8 location)
508 ret = parse_rgb(buf, &colors[location]);
512 ret = alienware_update_led(location);
514 return ret ? ret : count;
517 static ssize_t zone00_show(struct device *dev, struct device_attribute *attr,
520 return zone_show(dev, attr, buf, 0);
523 static ssize_t zone00_store(struct device *dev, struct device_attribute *attr,
524 const char *buf, size_t count)
526 return zone_store(dev, attr, buf, count, 0);
529 static DEVICE_ATTR_RW(zone00);
531 static ssize_t zone01_show(struct device *dev, struct device_attribute *attr,
534 return zone_show(dev, attr, buf, 1);
537 static ssize_t zone01_store(struct device *dev, struct device_attribute *attr,
538 const char *buf, size_t count)
540 return zone_store(dev, attr, buf, count, 1);
543 static DEVICE_ATTR_RW(zone01);
545 static ssize_t zone02_show(struct device *dev, struct device_attribute *attr,
548 return zone_show(dev, attr, buf, 2);
551 static ssize_t zone02_store(struct device *dev, struct device_attribute *attr,
552 const char *buf, size_t count)
554 return zone_store(dev, attr, buf, count, 2);
557 static DEVICE_ATTR_RW(zone02);
559 static ssize_t zone03_show(struct device *dev, struct device_attribute *attr,
562 return zone_show(dev, attr, buf, 3);
565 static ssize_t zone03_store(struct device *dev, struct device_attribute *attr,
566 const char *buf, size_t count)
568 return zone_store(dev, attr, buf, count, 3);
571 static DEVICE_ATTR_RW(zone03);
574 * Lighting control state device attribute (Global)
576 static ssize_t lighting_control_state_show(struct device *dev,
577 struct device_attribute *attr,
580 if (lighting_control_state == LEGACY_BOOTING)
581 return sysfs_emit(buf, "[booting] running suspend\n");
582 else if (lighting_control_state == LEGACY_SUSPEND)
583 return sysfs_emit(buf, "booting running [suspend]\n");
585 return sysfs_emit(buf, "booting [running] suspend\n");
588 static ssize_t lighting_control_state_store(struct device *dev,
589 struct device_attribute *attr,
590 const char *buf, size_t count)
594 if (strcmp(buf, "booting\n") == 0)
595 val = LEGACY_BOOTING;
596 else if (strcmp(buf, "suspend\n") == 0)
597 val = LEGACY_SUSPEND;
598 else if (interface == LEGACY)
599 val = LEGACY_RUNNING;
603 lighting_control_state = val;
604 pr_debug("alienware-wmi: updated control state to %d\n",
605 lighting_control_state);
610 static DEVICE_ATTR_RW(lighting_control_state);
612 static umode_t zone_attr_visible(struct kobject *kobj,
613 struct attribute *attr, int n)
615 if (n < quirks->num_zones + 1)
621 static bool zone_group_visible(struct kobject *kobj)
623 return quirks->num_zones > 0;
625 DEFINE_SYSFS_GROUP_VISIBLE(zone);
627 static struct attribute *zone_attrs[] = {
628 &dev_attr_lighting_control_state.attr,
629 &dev_attr_zone00.attr,
630 &dev_attr_zone01.attr,
631 &dev_attr_zone02.attr,
632 &dev_attr_zone03.attr,
636 static struct attribute_group zone_attribute_group = {
638 .is_visible = SYSFS_GROUP_VISIBLE(zone),
643 * LED Brightness (Global)
645 static int wmax_brightness(int brightness)
648 struct acpi_buffer input;
649 struct wmax_brightness_args args = {
651 .percentage = brightness,
653 input.length = sizeof(args);
654 input.pointer = &args;
655 status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0,
656 WMAX_METHOD_BRIGHTNESS, &input, NULL);
657 if (ACPI_FAILURE(status))
658 pr_err("alienware-wmi: brightness set failure: %u\n", status);
659 return ACPI_FAILURE(status);
662 static void global_led_set(struct led_classdev *led_cdev,
663 enum led_brightness brightness)
666 global_brightness = brightness;
667 if (interface == WMAX)
668 ret = wmax_brightness(brightness);
670 ret = alienware_update_led(0);
672 pr_err("LED brightness update failed\n");
675 static enum led_brightness global_led_get(struct led_classdev *led_cdev)
677 return global_brightness;
680 static struct led_classdev global_led = {
681 .brightness_set = global_led_set,
682 .brightness_get = global_led_get,
683 .name = "alienware::global_brightness",
686 static int alienware_zone_init(struct platform_device *dev)
688 if (interface == WMAX) {
689 lighting_control_state = WMAX_RUNNING;
690 } else if (interface == LEGACY) {
691 lighting_control_state = LEGACY_RUNNING;
693 global_led.max_brightness = 0x0F;
694 global_brightness = global_led.max_brightness;
696 return led_classdev_register(&dev->dev, &global_led);
699 static void alienware_zone_exit(struct platform_device *dev)
701 if (!quirks->num_zones)
704 led_classdev_unregister(&global_led);
707 static acpi_status alienware_wmax_command(void *in_args, size_t in_size,
708 u32 command, u32 *out_data)
711 union acpi_object *obj;
712 struct acpi_buffer input;
713 struct acpi_buffer output;
715 input.length = in_size;
716 input.pointer = in_args;
718 output.length = ACPI_ALLOCATE_BUFFER;
719 output.pointer = NULL;
720 status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0,
721 command, &input, &output);
722 if (ACPI_SUCCESS(status)) {
723 obj = (union acpi_object *)output.pointer;
724 if (obj && obj->type == ACPI_TYPE_INTEGER)
725 *out_data = (u32)obj->integer.value;
727 kfree(output.pointer);
729 status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0,
730 command, &input, NULL);
736 * The HDMI mux sysfs node indicates the status of the HDMI input mux.
737 * It can toggle between standard system GPU output and HDMI input.
739 static ssize_t cable_show(struct device *dev, struct device_attribute *attr,
742 struct wmax_basic_args in_args = {
749 alienware_wmax_command(&in_args, sizeof(in_args),
750 WMAX_METHOD_HDMI_CABLE, &out_data);
751 if (ACPI_SUCCESS(status)) {
753 return sysfs_emit(buf, "[unconnected] connected unknown\n");
754 else if (out_data == 1)
755 return sysfs_emit(buf, "unconnected [connected] unknown\n");
757 pr_err("alienware-wmi: unknown HDMI cable status: %d\n", status);
758 return sysfs_emit(buf, "unconnected connected [unknown]\n");
761 static ssize_t source_show(struct device *dev, struct device_attribute *attr,
764 struct wmax_basic_args in_args = {
771 alienware_wmax_command(&in_args, sizeof(in_args),
772 WMAX_METHOD_HDMI_STATUS, &out_data);
774 if (ACPI_SUCCESS(status)) {
776 return sysfs_emit(buf, "[input] gpu unknown\n");
777 else if (out_data == 2)
778 return sysfs_emit(buf, "input [gpu] unknown\n");
780 pr_err("alienware-wmi: unknown HDMI source status: %u\n", status);
781 return sysfs_emit(buf, "input gpu [unknown]\n");
784 static ssize_t source_store(struct device *dev, struct device_attribute *attr,
785 const char *buf, size_t count)
787 struct wmax_basic_args args;
790 if (strcmp(buf, "gpu\n") == 0)
792 else if (strcmp(buf, "input\n") == 0)
796 pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf);
798 status = alienware_wmax_command(&args, sizeof(args),
799 WMAX_METHOD_HDMI_SOURCE, NULL);
801 if (ACPI_FAILURE(status))
802 pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
807 static DEVICE_ATTR_RO(cable);
808 static DEVICE_ATTR_RW(source);
810 static bool hdmi_group_visible(struct kobject *kobj)
812 return quirks->hdmi_mux;
814 DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(hdmi);
816 static struct attribute *hdmi_attrs[] = {
817 &dev_attr_cable.attr,
818 &dev_attr_source.attr,
822 static const struct attribute_group hdmi_attribute_group = {
824 .is_visible = SYSFS_GROUP_VISIBLE(hdmi),
829 * Alienware GFX amplifier support
830 * - Currently supports reading cable status
831 * - Leaving expansion room to possibly support dock/undock events later
833 static ssize_t status_show(struct device *dev, struct device_attribute *attr,
836 struct wmax_basic_args in_args = {
843 alienware_wmax_command(&in_args, sizeof(in_args),
844 WMAX_METHOD_AMPLIFIER_CABLE, &out_data);
845 if (ACPI_SUCCESS(status)) {
847 return sysfs_emit(buf, "[unconnected] connected unknown\n");
848 else if (out_data == 1)
849 return sysfs_emit(buf, "unconnected [connected] unknown\n");
851 pr_err("alienware-wmi: unknown amplifier cable status: %d\n", status);
852 return sysfs_emit(buf, "unconnected connected [unknown]\n");
855 static DEVICE_ATTR_RO(status);
857 static bool amplifier_group_visible(struct kobject *kobj)
859 return quirks->amplifier;
861 DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(amplifier);
863 static struct attribute *amplifier_attrs[] = {
864 &dev_attr_status.attr,
868 static const struct attribute_group amplifier_attribute_group = {
870 .is_visible = SYSFS_GROUP_VISIBLE(amplifier),
871 .attrs = amplifier_attrs,
875 * Deep Sleep Control support
876 * - Modifies BIOS setting for deep sleep control allowing extra wakeup events
878 static ssize_t deepsleep_show(struct device *dev, struct device_attribute *attr,
881 struct wmax_basic_args in_args = {
887 status = alienware_wmax_command(&in_args, sizeof(in_args),
888 WMAX_METHOD_DEEP_SLEEP_STATUS, &out_data);
889 if (ACPI_SUCCESS(status)) {
891 return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
892 else if (out_data == 1)
893 return sysfs_emit(buf, "disabled [s5] s5_s4\n");
894 else if (out_data == 2)
895 return sysfs_emit(buf, "disabled s5 [s5_s4]\n");
897 pr_err("alienware-wmi: unknown deep sleep status: %d\n", status);
898 return sysfs_emit(buf, "disabled s5 s5_s4 [unknown]\n");
901 static ssize_t deepsleep_store(struct device *dev, struct device_attribute *attr,
902 const char *buf, size_t count)
904 struct wmax_basic_args args;
907 if (strcmp(buf, "disabled\n") == 0)
909 else if (strcmp(buf, "s5\n") == 0)
913 pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf);
915 status = alienware_wmax_command(&args, sizeof(args),
916 WMAX_METHOD_DEEP_SLEEP_CONTROL, NULL);
918 if (ACPI_FAILURE(status))
919 pr_err("alienware-wmi: deep sleep control failed: results: %u\n",
924 static DEVICE_ATTR_RW(deepsleep);
926 static bool deepsleep_group_visible(struct kobject *kobj)
928 return quirks->deepslp;
930 DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(deepsleep);
932 static struct attribute *deepsleep_attrs[] = {
933 &dev_attr_deepsleep.attr,
937 static const struct attribute_group deepsleep_attribute_group = {
939 .is_visible = SYSFS_GROUP_VISIBLE(deepsleep),
940 .attrs = deepsleep_attrs,
944 * Thermal Profile control
945 * - Provides thermal profile control through the Platform Profile API
947 #define WMAX_THERMAL_TABLE_MASK GENMASK(7, 4)
948 #define WMAX_THERMAL_MODE_MASK GENMASK(3, 0)
949 #define WMAX_SENSOR_ID_MASK BIT(8)
951 static bool is_wmax_thermal_code(u32 code)
953 if (code & WMAX_SENSOR_ID_MASK)
956 if ((code & WMAX_THERMAL_MODE_MASK) >= THERMAL_MODE_LAST)
959 if ((code & WMAX_THERMAL_TABLE_MASK) == WMAX_THERMAL_TABLE_BASIC &&
960 (code & WMAX_THERMAL_MODE_MASK) >= THERMAL_MODE_BASIC_QUIET)
963 if ((code & WMAX_THERMAL_TABLE_MASK) == WMAX_THERMAL_TABLE_USTT &&
964 (code & WMAX_THERMAL_MODE_MASK) <= THERMAL_MODE_USTT_LOW_POWER)
970 static int wmax_thermal_information(u8 operation, u8 arg, u32 *out_data)
972 struct wmax_u32_args in_args = {
973 .operation = operation,
980 status = alienware_wmax_command(&in_args, sizeof(in_args),
981 WMAX_METHOD_THERMAL_INFORMATION,
984 if (ACPI_FAILURE(status))
987 if (*out_data == WMAX_FAILURE_CODE)
993 static int wmax_thermal_control(u8 profile)
995 struct wmax_u32_args in_args = {
996 .operation = WMAX_OPERATION_ACTIVATE_PROFILE,
1004 status = alienware_wmax_command(&in_args, sizeof(in_args),
1005 WMAX_METHOD_THERMAL_CONTROL,
1008 if (ACPI_FAILURE(status))
1011 if (out_data == WMAX_FAILURE_CODE)
1017 static int wmax_game_shift_status(u8 operation, u32 *out_data)
1019 struct wmax_u32_args in_args = {
1020 .operation = operation,
1027 status = alienware_wmax_command(&in_args, sizeof(in_args),
1028 WMAX_METHOD_GAME_SHIFT_STATUS,
1031 if (ACPI_FAILURE(status))
1034 if (*out_data == WMAX_FAILURE_CODE)
1040 static int thermal_profile_get(struct device *dev,
1041 enum platform_profile_option *profile)
1046 ret = wmax_thermal_information(WMAX_OPERATION_CURRENT_PROFILE,
1052 if (out_data == WMAX_THERMAL_MODE_GMODE) {
1053 *profile = PLATFORM_PROFILE_PERFORMANCE;
1057 if (!is_wmax_thermal_code(out_data))
1060 out_data &= WMAX_THERMAL_MODE_MASK;
1061 *profile = wmax_mode_to_platform_profile[out_data];
1066 static int thermal_profile_set(struct device *dev,
1067 enum platform_profile_option profile)
1069 if (quirks->gmode) {
1073 ret = wmax_game_shift_status(WMAX_OPERATION_GET_GAME_SHIFT_STATUS,
1079 if ((profile == PLATFORM_PROFILE_PERFORMANCE && !gmode_status) ||
1080 (profile != PLATFORM_PROFILE_PERFORMANCE && gmode_status)) {
1081 ret = wmax_game_shift_status(WMAX_OPERATION_TOGGLE_GAME_SHIFT,
1089 return wmax_thermal_control(supported_thermal_profiles[profile]);
1092 static int thermal_profile_probe(void *drvdata, unsigned long *choices)
1094 enum platform_profile_option profile;
1095 enum wmax_thermal_mode mode;
1101 ret = wmax_thermal_information(WMAX_OPERATION_SYS_DESCRIPTION,
1102 0, (u32 *) &sys_desc);
1106 first_mode = sys_desc[0] + sys_desc[1];
1108 for (u32 i = 0; i < sys_desc[3]; i++) {
1109 ret = wmax_thermal_information(WMAX_OPERATION_LIST_IDS,
1110 i + first_mode, &out_data);
1115 if (ret == -EBADRQC)
1118 if (!is_wmax_thermal_code(out_data))
1121 mode = out_data & WMAX_THERMAL_MODE_MASK;
1122 profile = wmax_mode_to_platform_profile[mode];
1123 supported_thermal_profiles[profile] = out_data;
1125 set_bit(profile, choices);
1128 if (bitmap_empty(choices, PLATFORM_PROFILE_LAST))
1131 if (quirks->gmode) {
1132 supported_thermal_profiles[PLATFORM_PROFILE_PERFORMANCE] =
1133 WMAX_THERMAL_MODE_GMODE;
1135 set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
1141 static const struct platform_profile_ops awcc_platform_profile_ops = {
1142 .probe = thermal_profile_probe,
1143 .profile_get = thermal_profile_get,
1144 .profile_set = thermal_profile_set,
1147 static int create_thermal_profile(struct platform_device *platform_device)
1149 struct device *ppdev;
1151 ppdev = devm_platform_profile_register(&platform_device->dev, "alienware-wmi",
1152 NULL, &awcc_platform_profile_ops);
1154 return PTR_ERR_OR_ZERO(ppdev);
1160 static const struct attribute_group *alienfx_groups[] = {
1161 &zone_attribute_group,
1162 &hdmi_attribute_group,
1163 &lifier_attribute_group,
1164 &deepsleep_attribute_group,
1168 static struct platform_driver platform_driver = {
1170 .name = "alienware-wmi",
1171 .dev_groups = alienfx_groups,
1175 static int __init alienware_wmi_init(void)
1179 if (wmi_has_guid(LEGACY_CONTROL_GUID))
1181 else if (wmi_has_guid(WMAX_CONTROL_GUID))
1184 pr_warn("alienware-wmi: No known WMI GUID found\n");
1188 dmi_check_system(alienware_quirks);
1190 quirks = &quirk_unknown;
1192 if (force_platform_profile)
1193 quirks->thermal = true;
1196 if (quirks->thermal)
1197 quirks->gmode = true;
1199 pr_warn("force_gmode requires platform profile support\n");
1202 ret = platform_driver_register(&platform_driver);
1204 goto fail_platform_driver;
1205 platform_device = platform_device_alloc("alienware-wmi", PLATFORM_DEVID_NONE);
1206 if (!platform_device) {
1208 goto fail_platform_device1;
1210 ret = platform_device_add(platform_device);
1212 goto fail_platform_device2;
1214 if (quirks->thermal) {
1215 ret = create_thermal_profile(platform_device);
1217 goto fail_prep_thermal_profile;
1220 if (quirks->num_zones > 0) {
1221 ret = alienware_zone_init(platform_device);
1223 goto fail_prep_zones;
1229 alienware_zone_exit(platform_device);
1230 fail_prep_thermal_profile:
1231 platform_device_del(platform_device);
1232 fail_platform_device2:
1233 platform_device_put(platform_device);
1234 fail_platform_device1:
1235 platform_driver_unregister(&platform_driver);
1236 fail_platform_driver:
1240 module_init(alienware_wmi_init);
1242 static void __exit alienware_wmi_exit(void)
1244 alienware_zone_exit(platform_device);
1245 platform_device_unregister(platform_device);
1246 platform_driver_unregister(&platform_driver);
1249 module_exit(alienware_wmi_exit);