platform/x86: Rename alienware-wmi.c
authorKurt Borja <kuurtb@gmail.com>
Fri, 7 Feb 2025 15:46:05 +0000 (10:46 -0500)
committerIlpo Järvinen <ilpo.jarvinen@linux.intel.com>
Mon, 10 Feb 2025 10:56:25 +0000 (12:56 +0200)
Rename alienware-wmi to support upcoming split.

Reviewed-by: Armin Wolf <W_Armin@gmx.de>
Reviewed-by: Mario Limonciello <mario.limonciello@amd.com>
Signed-off-by: Kurt Borja <kuurtb@gmail.com>
Link: https://lore.kernel.org/r/20250207154610.13675-10-kuurtb@gmail.com
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
drivers/platform/x86/dell/Makefile
drivers/platform/x86/dell/alienware-wmi-base.c [new file with mode: 0644]
drivers/platform/x86/dell/alienware-wmi.c [deleted file]

index bb3cbd470a4637e1d564be3bdc51ecf739fc33f5..f8aec8502c2f395776f7723ea51ef49fedbb419f 100644 (file)
@@ -5,6 +5,7 @@
 #
 
 obj-$(CONFIG_ALIENWARE_WMI)            += alienware-wmi.o
+alienware-wmi-objs                     := alienware-wmi-base.o
 obj-$(CONFIG_DCDBAS)                   += dcdbas.o
 obj-$(CONFIG_DELL_LAPTOP)              += dell-laptop.o
 obj-$(CONFIG_DELL_RBTN)                        += dell-rbtn.o
diff --git a/drivers/platform/x86/dell/alienware-wmi-base.c b/drivers/platform/x86/dell/alienware-wmi-base.c
new file mode 100644 (file)
index 0000000..c575b82
--- /dev/null
@@ -0,0 +1,1384 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Alienware AlienFX control
+ *
+ * Copyright (C) 2014 Dell Inc <Dell.Client.Kernel@dell.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/acpi.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/platform_profile.h>
+#include <linux/dmi.h>
+#include <linux/leds.h>
+#include <linux/wmi.h>
+
+#define LEGACY_CONTROL_GUID            "A90597CE-A997-11DA-B012-B622A1EF5492"
+#define LEGACY_POWER_CONTROL_GUID      "A80593CE-A997-11DA-B012-B622A1EF5492"
+#define WMAX_CONTROL_GUID              "A70591CE-A997-11DA-B012-B622A1EF5492"
+
+#define WMAX_METHOD_HDMI_SOURCE                0x1
+#define WMAX_METHOD_HDMI_STATUS                0x2
+#define WMAX_METHOD_BRIGHTNESS         0x3
+#define WMAX_METHOD_ZONE_CONTROL       0x4
+#define WMAX_METHOD_HDMI_CABLE         0x5
+#define WMAX_METHOD_AMPLIFIER_CABLE    0x6
+#define WMAX_METHOD_DEEP_SLEEP_CONTROL 0x0B
+#define WMAX_METHOD_DEEP_SLEEP_STATUS  0x0C
+#define WMAX_METHOD_THERMAL_INFORMATION        0x14
+#define WMAX_METHOD_THERMAL_CONTROL    0x15
+#define WMAX_METHOD_GAME_SHIFT_STATUS  0x25
+
+#define WMAX_THERMAL_MODE_GMODE                0xAB
+
+#define WMAX_FAILURE_CODE              0xFFFFFFFF
+
+MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
+MODULE_DESCRIPTION("Alienware special feature control");
+MODULE_LICENSE("GPL");
+
+static bool force_platform_profile;
+module_param_unsafe(force_platform_profile, bool, 0);
+MODULE_PARM_DESC(force_platform_profile, "Forces auto-detecting thermal profiles without checking if WMI thermal backend is available");
+
+static bool force_gmode;
+module_param_unsafe(force_gmode, bool, 0);
+MODULE_PARM_DESC(force_gmode, "Forces G-Mode when performance profile is selected");
+
+enum INTERFACE_FLAGS {
+       LEGACY,
+       WMAX,
+};
+
+enum LEGACY_CONTROL_STATES {
+       LEGACY_RUNNING = 1,
+       LEGACY_BOOTING = 0,
+       LEGACY_SUSPEND = 3,
+};
+
+enum WMAX_CONTROL_STATES {
+       WMAX_RUNNING = 0xFF,
+       WMAX_BOOTING = 0,
+       WMAX_SUSPEND = 3,
+};
+
+enum WMAX_THERMAL_INFORMATION_OPERATIONS {
+       WMAX_OPERATION_SYS_DESCRIPTION          = 0x02,
+       WMAX_OPERATION_LIST_IDS                 = 0x03,
+       WMAX_OPERATION_CURRENT_PROFILE          = 0x0B,
+};
+
+enum WMAX_THERMAL_CONTROL_OPERATIONS {
+       WMAX_OPERATION_ACTIVATE_PROFILE         = 0x01,
+};
+
+enum WMAX_GAME_SHIFT_STATUS_OPERATIONS {
+       WMAX_OPERATION_TOGGLE_GAME_SHIFT        = 0x01,
+       WMAX_OPERATION_GET_GAME_SHIFT_STATUS    = 0x02,
+};
+
+enum WMAX_THERMAL_TABLES {
+       WMAX_THERMAL_TABLE_BASIC                = 0x90,
+       WMAX_THERMAL_TABLE_USTT                 = 0xA0,
+};
+
+enum wmax_thermal_mode {
+       THERMAL_MODE_USTT_BALANCED,
+       THERMAL_MODE_USTT_BALANCED_PERFORMANCE,
+       THERMAL_MODE_USTT_COOL,
+       THERMAL_MODE_USTT_QUIET,
+       THERMAL_MODE_USTT_PERFORMANCE,
+       THERMAL_MODE_USTT_LOW_POWER,
+       THERMAL_MODE_BASIC_QUIET,
+       THERMAL_MODE_BASIC_BALANCED,
+       THERMAL_MODE_BASIC_BALANCED_PERFORMANCE,
+       THERMAL_MODE_BASIC_PERFORMANCE,
+       THERMAL_MODE_LAST,
+};
+
+static const enum platform_profile_option wmax_mode_to_platform_profile[THERMAL_MODE_LAST] = {
+       [THERMAL_MODE_USTT_BALANCED]                    = PLATFORM_PROFILE_BALANCED,
+       [THERMAL_MODE_USTT_BALANCED_PERFORMANCE]        = PLATFORM_PROFILE_BALANCED_PERFORMANCE,
+       [THERMAL_MODE_USTT_COOL]                        = PLATFORM_PROFILE_COOL,
+       [THERMAL_MODE_USTT_QUIET]                       = PLATFORM_PROFILE_QUIET,
+       [THERMAL_MODE_USTT_PERFORMANCE]                 = PLATFORM_PROFILE_PERFORMANCE,
+       [THERMAL_MODE_USTT_LOW_POWER]                   = PLATFORM_PROFILE_LOW_POWER,
+       [THERMAL_MODE_BASIC_QUIET]                      = PLATFORM_PROFILE_QUIET,
+       [THERMAL_MODE_BASIC_BALANCED]                   = PLATFORM_PROFILE_BALANCED,
+       [THERMAL_MODE_BASIC_BALANCED_PERFORMANCE]       = PLATFORM_PROFILE_BALANCED_PERFORMANCE,
+       [THERMAL_MODE_BASIC_PERFORMANCE]                = PLATFORM_PROFILE_PERFORMANCE,
+};
+
+struct alienfx_quirks {
+       u8 num_zones;
+       bool hdmi_mux;
+       bool amplifier;
+       bool deepslp;
+};
+
+static struct alienfx_quirks *alienfx;
+
+
+static struct alienfx_quirks quirk_inspiron5675 = {
+       .num_zones = 2,
+       .hdmi_mux = false,
+       .amplifier = false,
+       .deepslp = false,
+};
+
+static struct alienfx_quirks quirk_unknown = {
+       .num_zones = 2,
+       .hdmi_mux = false,
+       .amplifier = false,
+       .deepslp = false,
+};
+
+static struct alienfx_quirks quirk_x51_r1_r2 = {
+       .num_zones = 3,
+       .hdmi_mux = false,
+       .amplifier = false,
+       .deepslp = false,
+};
+
+static struct alienfx_quirks quirk_x51_r3 = {
+       .num_zones = 4,
+       .hdmi_mux = false,
+       .amplifier = true,
+       .deepslp = false,
+};
+
+static struct alienfx_quirks quirk_asm100 = {
+       .num_zones = 2,
+       .hdmi_mux = true,
+       .amplifier = false,
+       .deepslp = false,
+};
+
+static struct alienfx_quirks quirk_asm200 = {
+       .num_zones = 2,
+       .hdmi_mux = true,
+       .amplifier = false,
+       .deepslp = true,
+};
+
+static struct alienfx_quirks quirk_asm201 = {
+       .num_zones = 2,
+       .hdmi_mux = true,
+       .amplifier = true,
+       .deepslp = true,
+};
+
+static int __init dmi_matched(const struct dmi_system_id *dmi)
+{
+       alienfx = dmi->driver_data;
+       return 1;
+}
+
+static const struct dmi_system_id alienware_quirks[] __initconst = {
+       {
+               .callback = dmi_matched,
+               .ident = "Alienware ASM100",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"),
+               },
+               .driver_data = &quirk_asm100,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "Alienware ASM200",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"),
+               },
+               .driver_data = &quirk_asm200,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "Alienware ASM201",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"),
+               },
+               .driver_data = &quirk_asm201,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "Alienware X51 R1",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"),
+               },
+               .driver_data = &quirk_x51_r1_r2,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "Alienware X51 R2",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"),
+               },
+               .driver_data = &quirk_x51_r1_r2,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "Alienware X51 R3",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"),
+               },
+               .driver_data = &quirk_x51_r3,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "Dell Inc. Inspiron 5675",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"),
+               },
+               .driver_data = &quirk_inspiron5675,
+       },
+       {}
+};
+
+struct color_platform {
+       u8 blue;
+       u8 green;
+       u8 red;
+} __packed;
+
+struct wmax_brightness_args {
+       u32 led_mask;
+       u32 percentage;
+};
+
+struct wmax_basic_args {
+       u8 arg;
+};
+
+struct legacy_led_args {
+       struct color_platform colors;
+       u8 brightness;
+       u8 state;
+} __packed;
+
+struct wmax_led_args {
+       u32 led_mask;
+       struct color_platform colors;
+       u8 state;
+} __packed;
+
+struct wmax_u32_args {
+       u8 operation;
+       u8 arg1;
+       u8 arg2;
+       u8 arg3;
+};
+
+struct awcc_priv {
+       struct wmi_device *wdev;
+       struct device *ppdev;
+       enum wmax_thermal_mode supported_thermal_profiles[PLATFORM_PROFILE_LAST];
+};
+
+struct alienfx_priv {
+       struct platform_device *pdev;
+       struct led_classdev global_led;
+       struct color_platform colors[4];
+       u8 global_brightness;
+       u8 lighting_control_state;
+};
+
+struct alienfx_ops {
+       int (*upd_led)(struct alienfx_priv *priv, struct wmi_device *wdev,
+                      u8 location);
+       int (*upd_brightness)(struct alienfx_priv *priv, struct wmi_device *wdev,
+                             u8 brightness);
+};
+
+struct alienfx_platdata {
+       struct wmi_device *wdev;
+       struct alienfx_ops ops;
+};
+
+static u8 interface;
+
+struct awcc_quirks {
+       bool pprof;
+       bool gmode;
+};
+
+static struct awcc_quirks g_series_quirks = {
+       .pprof = true,
+       .gmode = true,
+};
+
+static struct awcc_quirks generic_quirks = {
+       .pprof = true,
+       .gmode = false,
+};
+
+static struct awcc_quirks empty_quirks;
+
+static const struct dmi_system_id awcc_dmi_table[] __initconst = {
+       {
+               .ident = "Alienware m16 R1 AMD",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m16 R1 AMD"),
+               },
+               .driver_data = &generic_quirks,
+       },
+       {
+               .ident = "Alienware m17 R5",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m17 R5 AMD"),
+               },
+               .driver_data = &generic_quirks,
+       },
+       {
+               .ident = "Alienware m18 R2",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m18 R2"),
+               },
+               .driver_data = &generic_quirks,
+       },
+       {
+               .ident = "Alienware x15 R1",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R1"),
+               },
+               .driver_data = &generic_quirks,
+       },
+       {
+               .ident = "Alienware x17 R2",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x17 R2"),
+               },
+               .driver_data = &generic_quirks,
+       },
+       {
+               .ident = "Dell Inc. G15 5510",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5510"),
+               },
+               .driver_data = &g_series_quirks,
+       },
+       {
+               .ident = "Dell Inc. G15 5511",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5511"),
+               },
+               .driver_data = &g_series_quirks,
+       },
+       {
+               .ident = "Dell Inc. G15 5515",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5515"),
+               },
+               .driver_data = &g_series_quirks,
+       },
+       {
+               .ident = "Dell Inc. G3 3500",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "G3 3500"),
+               },
+               .driver_data = &g_series_quirks,
+       },
+       {
+               .ident = "Dell Inc. G3 3590",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "G3 3590"),
+               },
+               .driver_data = &g_series_quirks,
+       },
+       {
+               .ident = "Dell Inc. G5 5500",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "G5 5500"),
+               },
+               .driver_data = &g_series_quirks,
+       },
+};
+
+static struct awcc_quirks *awcc;
+
+static int alienware_wmi_command(struct wmi_device *wdev, u32 method_id,
+                                void *in_args, size_t in_size, u32 *out_data)
+{
+       struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
+       struct acpi_buffer in = {in_size, in_args};
+       acpi_status ret;
+
+       ret = wmidev_evaluate_method(wdev, 0, method_id, &in, out_data ? &out : NULL);
+       if (ACPI_FAILURE(ret))
+               return -EIO;
+
+       union acpi_object *obj __free(kfree) = out.pointer;
+
+       if (out_data) {
+               if (obj && obj->type == ACPI_TYPE_INTEGER)
+                       *out_data = (u32)obj->integer.value;
+               else
+                       return -ENOMSG;
+       }
+
+       return 0;
+}
+
+/*
+ * Helpers used for zone control
+ */
+static int parse_rgb(const char *buf, struct color_platform *colors)
+{
+       long unsigned int rgb;
+       int ret;
+       union color_union {
+               struct color_platform cp;
+               int package;
+       } repackager;
+
+       ret = kstrtoul(buf, 16, &rgb);
+       if (ret)
+               return ret;
+
+       /* RGB triplet notation is 24-bit hexadecimal */
+       if (rgb > 0xFFFFFF)
+               return -EINVAL;
+
+       repackager.package = rgb & 0x0f0f0f0f;
+       pr_debug("alienware-wmi: r: %d g:%d b: %d\n",
+                repackager.cp.red, repackager.cp.green, repackager.cp.blue);
+       *colors = repackager.cp;
+       return 0;
+}
+
+/*
+ * Individual RGB zone control
+ */
+static ssize_t zone_show(struct device *dev, struct device_attribute *attr,
+                        char *buf, u8 location)
+{
+       struct alienfx_priv *priv = dev_get_drvdata(dev);
+       struct color_platform *colors = &priv->colors[location];
+
+       return sprintf(buf, "red: %d, green: %d, blue: %d\n",
+                      colors->red, colors->green, colors->blue);
+
+}
+
+static ssize_t zone_store(struct device *dev, struct device_attribute *attr,
+                         const char *buf, size_t count, u8 location)
+{
+       struct alienfx_priv *priv = dev_get_drvdata(dev);
+       struct color_platform *colors = &priv->colors[location];
+       struct alienfx_platdata *pdata = dev_get_platdata(dev);
+       int ret;
+
+       ret = parse_rgb(buf, colors);
+       if (ret)
+               return ret;
+
+       ret = pdata->ops.upd_led(priv, pdata->wdev, location);
+
+       return ret ? ret : count;
+}
+
+static ssize_t zone00_show(struct device *dev, struct device_attribute *attr,
+                          char *buf)
+{
+       return zone_show(dev, attr, buf, 0);
+}
+
+static ssize_t zone00_store(struct device *dev, struct device_attribute *attr,
+                           const char *buf, size_t count)
+{
+       return zone_store(dev, attr, buf, count, 0);
+}
+
+static DEVICE_ATTR_RW(zone00);
+
+static ssize_t zone01_show(struct device *dev, struct device_attribute *attr,
+                          char *buf)
+{
+       return zone_show(dev, attr, buf, 1);
+}
+
+static ssize_t zone01_store(struct device *dev, struct device_attribute *attr,
+                           const char *buf, size_t count)
+{
+       return zone_store(dev, attr, buf, count, 1);
+}
+
+static DEVICE_ATTR_RW(zone01);
+
+static ssize_t zone02_show(struct device *dev, struct device_attribute *attr,
+                          char *buf)
+{
+       return zone_show(dev, attr, buf, 2);
+}
+
+static ssize_t zone02_store(struct device *dev, struct device_attribute *attr,
+                           const char *buf, size_t count)
+{
+       return zone_store(dev, attr, buf, count, 2);
+}
+
+static DEVICE_ATTR_RW(zone02);
+
+static ssize_t zone03_show(struct device *dev, struct device_attribute *attr,
+                          char *buf)
+{
+       return zone_show(dev, attr, buf, 3);
+}
+
+static ssize_t zone03_store(struct device *dev, struct device_attribute *attr,
+                           const char *buf, size_t count)
+{
+       return zone_store(dev, attr, buf, count, 3);
+}
+
+static DEVICE_ATTR_RW(zone03);
+
+/*
+ * Lighting control state device attribute (Global)
+ */
+static ssize_t lighting_control_state_show(struct device *dev,
+                                          struct device_attribute *attr,
+                                          char *buf)
+{
+       struct alienfx_priv *priv = dev_get_drvdata(dev);
+
+       if (priv->lighting_control_state == LEGACY_BOOTING)
+               return sysfs_emit(buf, "[booting] running suspend\n");
+       else if (priv->lighting_control_state == LEGACY_SUSPEND)
+               return sysfs_emit(buf, "booting running [suspend]\n");
+
+       return sysfs_emit(buf, "booting [running] suspend\n");
+}
+
+static ssize_t lighting_control_state_store(struct device *dev,
+                                           struct device_attribute *attr,
+                                           const char *buf, size_t count)
+{
+       struct alienfx_priv *priv = dev_get_drvdata(dev);
+       u8 val;
+
+       if (strcmp(buf, "booting\n") == 0)
+               val = LEGACY_BOOTING;
+       else if (strcmp(buf, "suspend\n") == 0)
+               val = LEGACY_SUSPEND;
+       else if (interface == LEGACY)
+               val = LEGACY_RUNNING;
+       else
+               val = WMAX_RUNNING;
+
+       priv->lighting_control_state = val;
+       pr_debug("alienware-wmi: updated control state to %d\n",
+                priv->lighting_control_state);
+
+       return count;
+}
+
+static DEVICE_ATTR_RW(lighting_control_state);
+
+static umode_t zone_attr_visible(struct kobject *kobj,
+                                struct attribute *attr, int n)
+{
+       if (n < alienfx->num_zones + 1)
+               return attr->mode;
+
+       return 0;
+}
+
+static bool zone_group_visible(struct kobject *kobj)
+{
+       return alienfx->num_zones > 0;
+}
+DEFINE_SYSFS_GROUP_VISIBLE(zone);
+
+static struct attribute *zone_attrs[] = {
+       &dev_attr_lighting_control_state.attr,
+       &dev_attr_zone00.attr,
+       &dev_attr_zone01.attr,
+       &dev_attr_zone02.attr,
+       &dev_attr_zone03.attr,
+       NULL
+};
+
+static struct attribute_group zone_attribute_group = {
+       .name = "rgb_zones",
+       .is_visible = SYSFS_GROUP_VISIBLE(zone),
+       .attrs = zone_attrs,
+};
+
+/*
+ * LED Brightness (Global)
+ */
+static void global_led_set(struct led_classdev *led_cdev,
+                          enum led_brightness brightness)
+{
+       struct alienfx_priv *priv = container_of(led_cdev, struct alienfx_priv,
+                                                global_led);
+       struct alienfx_platdata *pdata = dev_get_platdata(&priv->pdev->dev);
+       int ret;
+
+       priv->global_brightness = brightness;
+
+       ret = pdata->ops.upd_brightness(priv, pdata->wdev, brightness);
+       if (ret)
+               pr_err("LED brightness update failed\n");
+}
+
+static enum led_brightness global_led_get(struct led_classdev *led_cdev)
+{
+       struct alienfx_priv *priv = container_of(led_cdev, struct alienfx_priv,
+                                                global_led);
+
+       return priv->global_brightness;
+}
+
+/*
+ *     The HDMI mux sysfs node indicates the status of the HDMI input mux.
+ *     It can toggle between standard system GPU output and HDMI input.
+ */
+static ssize_t cable_show(struct device *dev, struct device_attribute *attr,
+                         char *buf)
+{
+       struct alienfx_platdata *pdata = dev_get_platdata(dev);
+       struct wmax_basic_args in_args = {
+               .arg = 0,
+       };
+       u32 out_data;
+       int ret;
+
+       ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_HDMI_CABLE,
+                                   &in_args, sizeof(in_args), &out_data);
+       if (!ret) {
+               if (out_data == 0)
+                       return sysfs_emit(buf, "[unconnected] connected unknown\n");
+               else if (out_data == 1)
+                       return sysfs_emit(buf, "unconnected [connected] unknown\n");
+       }
+
+       pr_err("alienware-wmi: unknown HDMI cable status: %d\n", ret);
+       return sysfs_emit(buf, "unconnected connected [unknown]\n");
+}
+
+static ssize_t source_show(struct device *dev, struct device_attribute *attr,
+                          char *buf)
+{
+       struct alienfx_platdata *pdata = dev_get_platdata(dev);
+       struct wmax_basic_args in_args = {
+               .arg = 0,
+       };
+       u32 out_data;
+       int ret;
+
+       ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_HDMI_STATUS,
+                                   &in_args, sizeof(in_args), &out_data);
+       if (!ret) {
+               if (out_data == 1)
+                       return sysfs_emit(buf, "[input] gpu unknown\n");
+               else if (out_data == 2)
+                       return sysfs_emit(buf, "input [gpu] unknown\n");
+       }
+
+       pr_err("alienware-wmi: unknown HDMI source status: %u\n", ret);
+       return sysfs_emit(buf, "input gpu [unknown]\n");
+}
+
+static ssize_t source_store(struct device *dev, struct device_attribute *attr,
+                           const char *buf, size_t count)
+{
+       struct alienfx_platdata *pdata = dev_get_platdata(dev);
+       struct wmax_basic_args args;
+       int ret;
+
+       if (strcmp(buf, "gpu\n") == 0)
+               args.arg = 1;
+       else if (strcmp(buf, "input\n") == 0)
+               args.arg = 2;
+       else
+               args.arg = 3;
+       pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf);
+
+       ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_HDMI_SOURCE, &args,
+                                   sizeof(args), NULL);
+       if (ret < 0)
+               pr_err("alienware-wmi: HDMI toggle failed: results: %u\n", ret);
+
+       return count;
+}
+
+static DEVICE_ATTR_RO(cable);
+static DEVICE_ATTR_RW(source);
+
+static bool hdmi_group_visible(struct kobject *kobj)
+{
+       return interface == WMAX && alienfx->hdmi_mux;
+}
+DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(hdmi);
+
+static struct attribute *hdmi_attrs[] = {
+       &dev_attr_cable.attr,
+       &dev_attr_source.attr,
+       NULL,
+};
+
+static const struct attribute_group hdmi_attribute_group = {
+       .name = "hdmi",
+       .is_visible = SYSFS_GROUP_VISIBLE(hdmi),
+       .attrs = hdmi_attrs,
+};
+
+/*
+ * Alienware GFX amplifier support
+ * - Currently supports reading cable status
+ * - Leaving expansion room to possibly support dock/undock events later
+ */
+static ssize_t status_show(struct device *dev, struct device_attribute *attr,
+                          char *buf)
+{
+       struct alienfx_platdata *pdata = dev_get_platdata(dev);
+       struct wmax_basic_args in_args = {
+               .arg = 0,
+       };
+       u32 out_data;
+       int ret;
+
+       ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_AMPLIFIER_CABLE,
+                                   &in_args, sizeof(in_args), &out_data);
+       if (!ret) {
+               if (out_data == 0)
+                       return sysfs_emit(buf, "[unconnected] connected unknown\n");
+               else if (out_data == 1)
+                       return sysfs_emit(buf, "unconnected [connected] unknown\n");
+       }
+
+       pr_err("alienware-wmi: unknown amplifier cable status: %d\n", ret);
+       return sysfs_emit(buf, "unconnected connected [unknown]\n");
+}
+
+static DEVICE_ATTR_RO(status);
+
+static bool amplifier_group_visible(struct kobject *kobj)
+{
+       return interface == WMAX && alienfx->amplifier;
+}
+DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(amplifier);
+
+static struct attribute *amplifier_attrs[] = {
+       &dev_attr_status.attr,
+       NULL,
+};
+
+static const struct attribute_group amplifier_attribute_group = {
+       .name = "amplifier",
+       .is_visible = SYSFS_GROUP_VISIBLE(amplifier),
+       .attrs = amplifier_attrs,
+};
+
+/*
+ * Deep Sleep Control support
+ * - Modifies BIOS setting for deep sleep control allowing extra wakeup events
+ */
+static ssize_t deepsleep_show(struct device *dev, struct device_attribute *attr,
+                             char *buf)
+{
+       struct alienfx_platdata *pdata = dev_get_platdata(dev);
+       struct wmax_basic_args in_args = {
+               .arg = 0,
+       };
+       u32 out_data;
+       int ret;
+
+       ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_DEEP_SLEEP_STATUS,
+                                   &in_args, sizeof(in_args), &out_data);
+       if (!ret) {
+               if (out_data == 0)
+                       return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
+               else if (out_data == 1)
+                       return sysfs_emit(buf, "disabled [s5] s5_s4\n");
+               else if (out_data == 2)
+                       return sysfs_emit(buf, "disabled s5 [s5_s4]\n");
+       }
+
+       pr_err("alienware-wmi: unknown deep sleep status: %d\n", ret);
+       return sysfs_emit(buf, "disabled s5 s5_s4 [unknown]\n");
+}
+
+static ssize_t deepsleep_store(struct device *dev, struct device_attribute *attr,
+                              const char *buf, size_t count)
+{
+       struct alienfx_platdata *pdata = dev_get_platdata(dev);
+       struct wmax_basic_args args;
+       int ret;
+
+       if (strcmp(buf, "disabled\n") == 0)
+               args.arg = 0;
+       else if (strcmp(buf, "s5\n") == 0)
+               args.arg = 1;
+       else
+               args.arg = 2;
+       pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf);
+
+       ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_DEEP_SLEEP_CONTROL,
+                                   &args, sizeof(args), NULL);
+       if (!ret)
+               pr_err("alienware-wmi: deep sleep control failed: results: %u\n", ret);
+
+       return count;
+}
+
+static DEVICE_ATTR_RW(deepsleep);
+
+static bool deepsleep_group_visible(struct kobject *kobj)
+{
+       return interface == WMAX && alienfx->deepslp;
+}
+DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(deepsleep);
+
+static struct attribute *deepsleep_attrs[] = {
+       &dev_attr_deepsleep.attr,
+       NULL,
+};
+
+static const struct attribute_group deepsleep_attribute_group = {
+       .name = "deepsleep",
+       .is_visible = SYSFS_GROUP_VISIBLE(deepsleep),
+       .attrs = deepsleep_attrs,
+};
+
+/*
+ * Thermal Profile control
+ *  - Provides thermal profile control through the Platform Profile API
+ */
+#define WMAX_THERMAL_TABLE_MASK                GENMASK(7, 4)
+#define WMAX_THERMAL_MODE_MASK         GENMASK(3, 0)
+#define WMAX_SENSOR_ID_MASK            BIT(8)
+
+static bool is_wmax_thermal_code(u32 code)
+{
+       if (code & WMAX_SENSOR_ID_MASK)
+               return false;
+
+       if ((code & WMAX_THERMAL_MODE_MASK) >= THERMAL_MODE_LAST)
+               return false;
+
+       if ((code & WMAX_THERMAL_TABLE_MASK) == WMAX_THERMAL_TABLE_BASIC &&
+           (code & WMAX_THERMAL_MODE_MASK) >= THERMAL_MODE_BASIC_QUIET)
+               return true;
+
+       if ((code & WMAX_THERMAL_TABLE_MASK) == WMAX_THERMAL_TABLE_USTT &&
+           (code & WMAX_THERMAL_MODE_MASK) <= THERMAL_MODE_USTT_LOW_POWER)
+               return true;
+
+       return false;
+}
+
+static int wmax_thermal_information(struct wmi_device *wdev, u8 operation,
+                                   u8 arg, u32 *out_data)
+{
+       struct wmax_u32_args in_args = {
+               .operation = operation,
+               .arg1 = arg,
+               .arg2 = 0,
+               .arg3 = 0,
+       };
+       int ret;
+
+       ret = alienware_wmi_command(wdev, WMAX_METHOD_THERMAL_INFORMATION,
+                                   &in_args, sizeof(in_args), out_data);
+       if (ret < 0)
+               return ret;
+
+       if (*out_data == WMAX_FAILURE_CODE)
+               return -EBADRQC;
+
+       return 0;
+}
+
+static int wmax_thermal_control(struct wmi_device *wdev, u8 profile)
+{
+       struct wmax_u32_args in_args = {
+               .operation = WMAX_OPERATION_ACTIVATE_PROFILE,
+               .arg1 = profile,
+               .arg2 = 0,
+               .arg3 = 0,
+       };
+       u32 out_data;
+       int ret;
+
+       ret = alienware_wmi_command(wdev, WMAX_METHOD_THERMAL_CONTROL,
+                                   &in_args, sizeof(in_args), &out_data);
+       if (ret)
+               return ret;
+
+       if (out_data == WMAX_FAILURE_CODE)
+               return -EBADRQC;
+
+       return 0;
+}
+
+static int wmax_game_shift_status(struct wmi_device *wdev, u8 operation,
+                                 u32 *out_data)
+{
+       struct wmax_u32_args in_args = {
+               .operation = operation,
+               .arg1 = 0,
+               .arg2 = 0,
+               .arg3 = 0,
+       };
+       int ret;
+
+       ret = alienware_wmi_command(wdev, WMAX_METHOD_GAME_SHIFT_STATUS,
+                                   &in_args, sizeof(in_args), out_data);
+       if (ret < 0)
+               return ret;
+
+       if (*out_data == WMAX_FAILURE_CODE)
+               return -EOPNOTSUPP;
+
+       return 0;
+}
+
+static int thermal_profile_get(struct device *dev,
+                              enum platform_profile_option *profile)
+{
+       struct awcc_priv *priv = dev_get_drvdata(dev);
+       u32 out_data;
+       int ret;
+
+       ret = wmax_thermal_information(priv->wdev, WMAX_OPERATION_CURRENT_PROFILE,
+                                      0, &out_data);
+
+       if (ret < 0)
+               return ret;
+
+       if (out_data == WMAX_THERMAL_MODE_GMODE) {
+               *profile = PLATFORM_PROFILE_PERFORMANCE;
+               return 0;
+       }
+
+       if (!is_wmax_thermal_code(out_data))
+               return -ENODATA;
+
+       out_data &= WMAX_THERMAL_MODE_MASK;
+       *profile = wmax_mode_to_platform_profile[out_data];
+
+       return 0;
+}
+
+static int thermal_profile_set(struct device *dev,
+                              enum platform_profile_option profile)
+{
+       struct awcc_priv *priv = dev_get_drvdata(dev);
+
+       if (awcc->gmode) {
+               u32 gmode_status;
+               int ret;
+
+               ret = wmax_game_shift_status(priv->wdev,
+                                            WMAX_OPERATION_GET_GAME_SHIFT_STATUS,
+                                            &gmode_status);
+
+               if (ret < 0)
+                       return ret;
+
+               if ((profile == PLATFORM_PROFILE_PERFORMANCE && !gmode_status) ||
+                   (profile != PLATFORM_PROFILE_PERFORMANCE && gmode_status)) {
+                       ret = wmax_game_shift_status(priv->wdev,
+                                                    WMAX_OPERATION_TOGGLE_GAME_SHIFT,
+                                                    &gmode_status);
+
+                       if (ret < 0)
+                               return ret;
+               }
+       }
+
+       return wmax_thermal_control(priv->wdev,
+                                   priv->supported_thermal_profiles[profile]);
+}
+
+static int thermal_profile_probe(void *drvdata, unsigned long *choices)
+{
+       enum platform_profile_option profile;
+       struct awcc_priv *priv = drvdata;
+       enum wmax_thermal_mode mode;
+       u8 sys_desc[4];
+       u32 first_mode;
+       u32 out_data;
+       int ret;
+
+       ret = wmax_thermal_information(priv->wdev, WMAX_OPERATION_SYS_DESCRIPTION,
+                                      0, (u32 *) &sys_desc);
+       if (ret < 0)
+               return ret;
+
+       first_mode = sys_desc[0] + sys_desc[1];
+
+       for (u32 i = 0; i < sys_desc[3]; i++) {
+               ret = wmax_thermal_information(priv->wdev, WMAX_OPERATION_LIST_IDS,
+                                              i + first_mode, &out_data);
+
+               if (ret == -EIO)
+                       return ret;
+
+               if (ret == -EBADRQC)
+                       break;
+
+               if (!is_wmax_thermal_code(out_data))
+                       continue;
+
+               mode = out_data & WMAX_THERMAL_MODE_MASK;
+               profile = wmax_mode_to_platform_profile[mode];
+               priv->supported_thermal_profiles[profile] = out_data;
+
+               set_bit(profile, choices);
+       }
+
+       if (bitmap_empty(choices, PLATFORM_PROFILE_LAST))
+               return -ENODEV;
+
+       if (awcc->gmode) {
+               priv->supported_thermal_profiles[PLATFORM_PROFILE_PERFORMANCE] =
+                       WMAX_THERMAL_MODE_GMODE;
+
+               set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
+       }
+
+       return 0;
+}
+
+static const struct platform_profile_ops awcc_platform_profile_ops = {
+       .probe = thermal_profile_probe,
+       .profile_get = thermal_profile_get,
+       .profile_set = thermal_profile_set,
+};
+
+static int awcc_platform_profile_init(struct wmi_device *wdev)
+{
+       struct awcc_priv *priv = dev_get_drvdata(&wdev->dev);
+
+       priv->ppdev = devm_platform_profile_register(&wdev->dev, "alienware-wmi",
+                                                    priv, &awcc_platform_profile_ops);
+
+       return PTR_ERR_OR_ZERO(priv->ppdev);
+}
+
+static int alienware_awcc_setup(struct wmi_device *wdev)
+{
+       struct awcc_priv *priv;
+       int ret;
+
+       priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->wdev = wdev;
+       dev_set_drvdata(&wdev->dev, priv);
+
+       if (awcc->pprof) {
+               ret = awcc_platform_profile_init(wdev);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+/*
+ * Platform Driver
+ */
+static int alienfx_probe(struct platform_device *pdev)
+{
+       struct alienfx_priv *priv;
+
+       priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       if (interface == WMAX)
+               priv->lighting_control_state = WMAX_RUNNING;
+       else
+               priv->lighting_control_state = LEGACY_RUNNING;
+
+       priv->pdev = pdev;
+       priv->global_led.name = "alienware::global_brightness";
+       priv->global_led.brightness_set = global_led_set;
+       priv->global_led.brightness_get = global_led_get;
+       priv->global_led.max_brightness = 0x0F;
+       priv->global_brightness = priv->global_led.max_brightness;
+       platform_set_drvdata(pdev, priv);
+
+       return devm_led_classdev_register(&pdev->dev, &priv->global_led);
+}
+
+static const struct attribute_group *alienfx_groups[] = {
+       &zone_attribute_group,
+       &hdmi_attribute_group,
+       &amplifier_attribute_group,
+       &deepsleep_attribute_group,
+       NULL
+};
+
+static struct platform_driver platform_driver = {
+       .driver = {
+               .name = "alienware-wmi",
+               .dev_groups = alienfx_groups,
+       },
+       .probe = alienfx_probe,
+};
+
+static void alienware_alienfx_remove(void *data)
+{
+       struct platform_device *pdev = data;
+
+       platform_device_unregister(pdev);
+}
+
+static int alienware_alienfx_setup(struct alienfx_platdata *pdata)
+{
+       struct device *dev = &pdata->wdev->dev;
+       struct platform_device *pdev;
+       int ret;
+
+       pdev = platform_device_register_data(NULL, "alienware-wmi",
+                                            PLATFORM_DEVID_NONE, pdata,
+                                            sizeof(*pdata));
+       if (IS_ERR(pdev))
+               return PTR_ERR(pdev);
+
+       dev_set_drvdata(dev, pdev);
+       ret = devm_add_action_or_reset(dev, alienware_alienfx_remove, pdev);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+/*
+ * Legacy WMI driver
+ */
+static int legacy_wmi_update_led(struct alienfx_priv *priv,
+                                struct wmi_device *wdev, u8 location)
+{
+       struct legacy_led_args legacy_args = {
+               .colors = priv->colors[location],
+               .brightness = priv->global_brightness,
+               .state = 0,
+       };
+       struct acpi_buffer input;
+       acpi_status status;
+
+       if (legacy_args.state != LEGACY_RUNNING) {
+               legacy_args.state = priv->lighting_control_state;
+
+               input.length = sizeof(legacy_args);
+               input.pointer = &legacy_args;
+
+               status = wmi_evaluate_method(LEGACY_POWER_CONTROL_GUID, 0,
+                                            location + 1, &input, NULL);
+               if (ACPI_FAILURE(status))
+                       return -EIO;
+
+               return 0;
+       }
+
+       return alienware_wmi_command(wdev, location + 1, &legacy_args,
+                                    sizeof(legacy_args), NULL);
+}
+
+static int legacy_wmi_update_brightness(struct alienfx_priv *priv,
+                                       struct wmi_device *wdev, u8 brightness)
+{
+       return legacy_wmi_update_led(priv, wdev, 0);
+}
+
+static int legacy_wmi_probe(struct wmi_device *wdev, const void *context)
+{
+       struct alienfx_platdata pdata = {
+               .wdev = wdev,
+               .ops = {
+                       .upd_led = legacy_wmi_update_led,
+                       .upd_brightness = legacy_wmi_update_brightness,
+               },
+       };
+
+       return alienware_alienfx_setup(&pdata);
+}
+
+static const struct wmi_device_id alienware_legacy_device_id_table[] = {
+       { LEGACY_CONTROL_GUID, NULL },
+       { },
+};
+MODULE_DEVICE_TABLE(wmi, alienware_legacy_device_id_table);
+
+static struct wmi_driver alienware_legacy_wmi_driver = {
+       .driver = {
+               .name = "alienware-wmi-alienfx",
+               .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+       },
+       .id_table = alienware_legacy_device_id_table,
+       .probe = legacy_wmi_probe,
+       .no_singleton = true,
+};
+
+static int __init alienware_legacy_wmi_init(void)
+{
+       return wmi_driver_register(&alienware_legacy_wmi_driver);
+}
+
+static void __exit alienware_legacy_wmi_exit(void)
+{
+       wmi_driver_unregister(&alienware_legacy_wmi_driver);
+}
+
+/*
+ * WMAX WMI driver
+ */
+static int wmax_wmi_update_led(struct alienfx_priv *priv,
+                              struct wmi_device *wdev, u8 location)
+{
+       struct wmax_led_args in_args = {
+               .led_mask = 1 << location,
+               .colors = priv->colors[location],
+               .state = priv->lighting_control_state,
+       };
+
+       return alienware_wmi_command(wdev, WMAX_METHOD_ZONE_CONTROL, &in_args,
+                                    sizeof(in_args), NULL);
+}
+
+static int wmax_wmi_update_brightness(struct alienfx_priv *priv,
+                                     struct wmi_device *wdev, u8 brightness)
+{
+       struct wmax_brightness_args in_args = {
+               .led_mask = 0xFF,
+               .percentage = brightness,
+       };
+
+       return alienware_wmi_command(wdev, WMAX_METHOD_BRIGHTNESS, &in_args,
+                                    sizeof(in_args), NULL);
+}
+
+static int wmax_wmi_probe(struct wmi_device *wdev, const void *context)
+{
+       struct alienfx_platdata pdata = {
+               .wdev = wdev,
+               .ops = {
+                       .upd_led = wmax_wmi_update_led,
+                       .upd_brightness = wmax_wmi_update_brightness,
+               },
+       };
+       int ret;
+
+       if (awcc)
+               ret = alienware_awcc_setup(wdev);
+       else
+               ret = alienware_alienfx_setup(&pdata);
+
+       return ret;
+}
+
+static const struct wmi_device_id alienware_wmax_device_id_table[] = {
+       { WMAX_CONTROL_GUID, NULL },
+       { },
+};
+MODULE_DEVICE_TABLE(wmi, alienware_wmax_device_id_table);
+
+static struct wmi_driver alienware_wmax_wmi_driver = {
+       .driver = {
+               .name = "alienware-wmi-wmax",
+               .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+       },
+       .id_table = alienware_wmax_device_id_table,
+       .probe = wmax_wmi_probe,
+       .no_singleton = true,
+};
+
+static int __init alienware_wmax_wmi_init(void)
+{
+       const struct dmi_system_id *id;
+
+       id = dmi_first_match(awcc_dmi_table);
+       if (id)
+               awcc = id->driver_data;
+
+       if (force_platform_profile) {
+               if (!awcc)
+                       awcc = &empty_quirks;
+
+               awcc->pprof = true;
+       }
+
+       if (force_gmode) {
+               if (awcc)
+                       awcc->gmode = true;
+               else
+                       pr_warn("force_gmode requires platform profile support\n");
+       }
+
+       return wmi_driver_register(&alienware_wmax_wmi_driver);
+}
+
+static void __exit alienware_wmax_wmi_exit(void)
+{
+       wmi_driver_unregister(&alienware_wmax_wmi_driver);
+}
+
+static int __init alienware_wmi_init(void)
+{
+       int ret;
+
+       dmi_check_system(alienware_quirks);
+       if (!alienfx)
+               alienfx = &quirk_unknown;
+
+       ret = platform_driver_register(&platform_driver);
+       if (ret < 0)
+               return ret;
+
+       if (wmi_has_guid(WMAX_CONTROL_GUID)) {
+               interface = WMAX;
+               ret = alienware_wmax_wmi_init();
+       } else {
+               interface = LEGACY;
+               ret = alienware_legacy_wmi_init();
+       }
+
+       if (ret < 0)
+               platform_driver_unregister(&platform_driver);
+
+       return ret;
+}
+
+module_init(alienware_wmi_init);
+
+static void __exit alienware_wmi_exit(void)
+{
+       if (interface == WMAX)
+               alienware_wmax_wmi_exit();
+       else
+               alienware_legacy_wmi_exit();
+
+       platform_driver_unregister(&platform_driver);
+}
+
+module_exit(alienware_wmi_exit);
diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
deleted file mode 100644 (file)
index c575b82..0000000
+++ /dev/null
@@ -1,1384 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Alienware AlienFX control
- *
- * Copyright (C) 2014 Dell Inc <Dell.Client.Kernel@dell.com>
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/acpi.h>
-#include <linux/bitfield.h>
-#include <linux/bits.h>
-#include <linux/cleanup.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/platform_profile.h>
-#include <linux/dmi.h>
-#include <linux/leds.h>
-#include <linux/wmi.h>
-
-#define LEGACY_CONTROL_GUID            "A90597CE-A997-11DA-B012-B622A1EF5492"
-#define LEGACY_POWER_CONTROL_GUID      "A80593CE-A997-11DA-B012-B622A1EF5492"
-#define WMAX_CONTROL_GUID              "A70591CE-A997-11DA-B012-B622A1EF5492"
-
-#define WMAX_METHOD_HDMI_SOURCE                0x1
-#define WMAX_METHOD_HDMI_STATUS                0x2
-#define WMAX_METHOD_BRIGHTNESS         0x3
-#define WMAX_METHOD_ZONE_CONTROL       0x4
-#define WMAX_METHOD_HDMI_CABLE         0x5
-#define WMAX_METHOD_AMPLIFIER_CABLE    0x6
-#define WMAX_METHOD_DEEP_SLEEP_CONTROL 0x0B
-#define WMAX_METHOD_DEEP_SLEEP_STATUS  0x0C
-#define WMAX_METHOD_THERMAL_INFORMATION        0x14
-#define WMAX_METHOD_THERMAL_CONTROL    0x15
-#define WMAX_METHOD_GAME_SHIFT_STATUS  0x25
-
-#define WMAX_THERMAL_MODE_GMODE                0xAB
-
-#define WMAX_FAILURE_CODE              0xFFFFFFFF
-
-MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
-MODULE_DESCRIPTION("Alienware special feature control");
-MODULE_LICENSE("GPL");
-
-static bool force_platform_profile;
-module_param_unsafe(force_platform_profile, bool, 0);
-MODULE_PARM_DESC(force_platform_profile, "Forces auto-detecting thermal profiles without checking if WMI thermal backend is available");
-
-static bool force_gmode;
-module_param_unsafe(force_gmode, bool, 0);
-MODULE_PARM_DESC(force_gmode, "Forces G-Mode when performance profile is selected");
-
-enum INTERFACE_FLAGS {
-       LEGACY,
-       WMAX,
-};
-
-enum LEGACY_CONTROL_STATES {
-       LEGACY_RUNNING = 1,
-       LEGACY_BOOTING = 0,
-       LEGACY_SUSPEND = 3,
-};
-
-enum WMAX_CONTROL_STATES {
-       WMAX_RUNNING = 0xFF,
-       WMAX_BOOTING = 0,
-       WMAX_SUSPEND = 3,
-};
-
-enum WMAX_THERMAL_INFORMATION_OPERATIONS {
-       WMAX_OPERATION_SYS_DESCRIPTION          = 0x02,
-       WMAX_OPERATION_LIST_IDS                 = 0x03,
-       WMAX_OPERATION_CURRENT_PROFILE          = 0x0B,
-};
-
-enum WMAX_THERMAL_CONTROL_OPERATIONS {
-       WMAX_OPERATION_ACTIVATE_PROFILE         = 0x01,
-};
-
-enum WMAX_GAME_SHIFT_STATUS_OPERATIONS {
-       WMAX_OPERATION_TOGGLE_GAME_SHIFT        = 0x01,
-       WMAX_OPERATION_GET_GAME_SHIFT_STATUS    = 0x02,
-};
-
-enum WMAX_THERMAL_TABLES {
-       WMAX_THERMAL_TABLE_BASIC                = 0x90,
-       WMAX_THERMAL_TABLE_USTT                 = 0xA0,
-};
-
-enum wmax_thermal_mode {
-       THERMAL_MODE_USTT_BALANCED,
-       THERMAL_MODE_USTT_BALANCED_PERFORMANCE,
-       THERMAL_MODE_USTT_COOL,
-       THERMAL_MODE_USTT_QUIET,
-       THERMAL_MODE_USTT_PERFORMANCE,
-       THERMAL_MODE_USTT_LOW_POWER,
-       THERMAL_MODE_BASIC_QUIET,
-       THERMAL_MODE_BASIC_BALANCED,
-       THERMAL_MODE_BASIC_BALANCED_PERFORMANCE,
-       THERMAL_MODE_BASIC_PERFORMANCE,
-       THERMAL_MODE_LAST,
-};
-
-static const enum platform_profile_option wmax_mode_to_platform_profile[THERMAL_MODE_LAST] = {
-       [THERMAL_MODE_USTT_BALANCED]                    = PLATFORM_PROFILE_BALANCED,
-       [THERMAL_MODE_USTT_BALANCED_PERFORMANCE]        = PLATFORM_PROFILE_BALANCED_PERFORMANCE,
-       [THERMAL_MODE_USTT_COOL]                        = PLATFORM_PROFILE_COOL,
-       [THERMAL_MODE_USTT_QUIET]                       = PLATFORM_PROFILE_QUIET,
-       [THERMAL_MODE_USTT_PERFORMANCE]                 = PLATFORM_PROFILE_PERFORMANCE,
-       [THERMAL_MODE_USTT_LOW_POWER]                   = PLATFORM_PROFILE_LOW_POWER,
-       [THERMAL_MODE_BASIC_QUIET]                      = PLATFORM_PROFILE_QUIET,
-       [THERMAL_MODE_BASIC_BALANCED]                   = PLATFORM_PROFILE_BALANCED,
-       [THERMAL_MODE_BASIC_BALANCED_PERFORMANCE]       = PLATFORM_PROFILE_BALANCED_PERFORMANCE,
-       [THERMAL_MODE_BASIC_PERFORMANCE]                = PLATFORM_PROFILE_PERFORMANCE,
-};
-
-struct alienfx_quirks {
-       u8 num_zones;
-       bool hdmi_mux;
-       bool amplifier;
-       bool deepslp;
-};
-
-static struct alienfx_quirks *alienfx;
-
-
-static struct alienfx_quirks quirk_inspiron5675 = {
-       .num_zones = 2,
-       .hdmi_mux = false,
-       .amplifier = false,
-       .deepslp = false,
-};
-
-static struct alienfx_quirks quirk_unknown = {
-       .num_zones = 2,
-       .hdmi_mux = false,
-       .amplifier = false,
-       .deepslp = false,
-};
-
-static struct alienfx_quirks quirk_x51_r1_r2 = {
-       .num_zones = 3,
-       .hdmi_mux = false,
-       .amplifier = false,
-       .deepslp = false,
-};
-
-static struct alienfx_quirks quirk_x51_r3 = {
-       .num_zones = 4,
-       .hdmi_mux = false,
-       .amplifier = true,
-       .deepslp = false,
-};
-
-static struct alienfx_quirks quirk_asm100 = {
-       .num_zones = 2,
-       .hdmi_mux = true,
-       .amplifier = false,
-       .deepslp = false,
-};
-
-static struct alienfx_quirks quirk_asm200 = {
-       .num_zones = 2,
-       .hdmi_mux = true,
-       .amplifier = false,
-       .deepslp = true,
-};
-
-static struct alienfx_quirks quirk_asm201 = {
-       .num_zones = 2,
-       .hdmi_mux = true,
-       .amplifier = true,
-       .deepslp = true,
-};
-
-static int __init dmi_matched(const struct dmi_system_id *dmi)
-{
-       alienfx = dmi->driver_data;
-       return 1;
-}
-
-static const struct dmi_system_id alienware_quirks[] __initconst = {
-       {
-               .callback = dmi_matched,
-               .ident = "Alienware ASM100",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"),
-               },
-               .driver_data = &quirk_asm100,
-       },
-       {
-               .callback = dmi_matched,
-               .ident = "Alienware ASM200",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"),
-               },
-               .driver_data = &quirk_asm200,
-       },
-       {
-               .callback = dmi_matched,
-               .ident = "Alienware ASM201",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"),
-               },
-               .driver_data = &quirk_asm201,
-       },
-       {
-               .callback = dmi_matched,
-               .ident = "Alienware X51 R1",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"),
-               },
-               .driver_data = &quirk_x51_r1_r2,
-       },
-       {
-               .callback = dmi_matched,
-               .ident = "Alienware X51 R2",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"),
-               },
-               .driver_data = &quirk_x51_r1_r2,
-       },
-       {
-               .callback = dmi_matched,
-               .ident = "Alienware X51 R3",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"),
-               },
-               .driver_data = &quirk_x51_r3,
-       },
-       {
-               .callback = dmi_matched,
-               .ident = "Dell Inc. Inspiron 5675",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"),
-               },
-               .driver_data = &quirk_inspiron5675,
-       },
-       {}
-};
-
-struct color_platform {
-       u8 blue;
-       u8 green;
-       u8 red;
-} __packed;
-
-struct wmax_brightness_args {
-       u32 led_mask;
-       u32 percentage;
-};
-
-struct wmax_basic_args {
-       u8 arg;
-};
-
-struct legacy_led_args {
-       struct color_platform colors;
-       u8 brightness;
-       u8 state;
-} __packed;
-
-struct wmax_led_args {
-       u32 led_mask;
-       struct color_platform colors;
-       u8 state;
-} __packed;
-
-struct wmax_u32_args {
-       u8 operation;
-       u8 arg1;
-       u8 arg2;
-       u8 arg3;
-};
-
-struct awcc_priv {
-       struct wmi_device *wdev;
-       struct device *ppdev;
-       enum wmax_thermal_mode supported_thermal_profiles[PLATFORM_PROFILE_LAST];
-};
-
-struct alienfx_priv {
-       struct platform_device *pdev;
-       struct led_classdev global_led;
-       struct color_platform colors[4];
-       u8 global_brightness;
-       u8 lighting_control_state;
-};
-
-struct alienfx_ops {
-       int (*upd_led)(struct alienfx_priv *priv, struct wmi_device *wdev,
-                      u8 location);
-       int (*upd_brightness)(struct alienfx_priv *priv, struct wmi_device *wdev,
-                             u8 brightness);
-};
-
-struct alienfx_platdata {
-       struct wmi_device *wdev;
-       struct alienfx_ops ops;
-};
-
-static u8 interface;
-
-struct awcc_quirks {
-       bool pprof;
-       bool gmode;
-};
-
-static struct awcc_quirks g_series_quirks = {
-       .pprof = true,
-       .gmode = true,
-};
-
-static struct awcc_quirks generic_quirks = {
-       .pprof = true,
-       .gmode = false,
-};
-
-static struct awcc_quirks empty_quirks;
-
-static const struct dmi_system_id awcc_dmi_table[] __initconst = {
-       {
-               .ident = "Alienware m16 R1 AMD",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m16 R1 AMD"),
-               },
-               .driver_data = &generic_quirks,
-       },
-       {
-               .ident = "Alienware m17 R5",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m17 R5 AMD"),
-               },
-               .driver_data = &generic_quirks,
-       },
-       {
-               .ident = "Alienware m18 R2",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m18 R2"),
-               },
-               .driver_data = &generic_quirks,
-       },
-       {
-               .ident = "Alienware x15 R1",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R1"),
-               },
-               .driver_data = &generic_quirks,
-       },
-       {
-               .ident = "Alienware x17 R2",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x17 R2"),
-               },
-               .driver_data = &generic_quirks,
-       },
-       {
-               .ident = "Dell Inc. G15 5510",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5510"),
-               },
-               .driver_data = &g_series_quirks,
-       },
-       {
-               .ident = "Dell Inc. G15 5511",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5511"),
-               },
-               .driver_data = &g_series_quirks,
-       },
-       {
-               .ident = "Dell Inc. G15 5515",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5515"),
-               },
-               .driver_data = &g_series_quirks,
-       },
-       {
-               .ident = "Dell Inc. G3 3500",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "G3 3500"),
-               },
-               .driver_data = &g_series_quirks,
-       },
-       {
-               .ident = "Dell Inc. G3 3590",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "G3 3590"),
-               },
-               .driver_data = &g_series_quirks,
-       },
-       {
-               .ident = "Dell Inc. G5 5500",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "G5 5500"),
-               },
-               .driver_data = &g_series_quirks,
-       },
-};
-
-static struct awcc_quirks *awcc;
-
-static int alienware_wmi_command(struct wmi_device *wdev, u32 method_id,
-                                void *in_args, size_t in_size, u32 *out_data)
-{
-       struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
-       struct acpi_buffer in = {in_size, in_args};
-       acpi_status ret;
-
-       ret = wmidev_evaluate_method(wdev, 0, method_id, &in, out_data ? &out : NULL);
-       if (ACPI_FAILURE(ret))
-               return -EIO;
-
-       union acpi_object *obj __free(kfree) = out.pointer;
-
-       if (out_data) {
-               if (obj && obj->type == ACPI_TYPE_INTEGER)
-                       *out_data = (u32)obj->integer.value;
-               else
-                       return -ENOMSG;
-       }
-
-       return 0;
-}
-
-/*
- * Helpers used for zone control
- */
-static int parse_rgb(const char *buf, struct color_platform *colors)
-{
-       long unsigned int rgb;
-       int ret;
-       union color_union {
-               struct color_platform cp;
-               int package;
-       } repackager;
-
-       ret = kstrtoul(buf, 16, &rgb);
-       if (ret)
-               return ret;
-
-       /* RGB triplet notation is 24-bit hexadecimal */
-       if (rgb > 0xFFFFFF)
-               return -EINVAL;
-
-       repackager.package = rgb & 0x0f0f0f0f;
-       pr_debug("alienware-wmi: r: %d g:%d b: %d\n",
-                repackager.cp.red, repackager.cp.green, repackager.cp.blue);
-       *colors = repackager.cp;
-       return 0;
-}
-
-/*
- * Individual RGB zone control
- */
-static ssize_t zone_show(struct device *dev, struct device_attribute *attr,
-                        char *buf, u8 location)
-{
-       struct alienfx_priv *priv = dev_get_drvdata(dev);
-       struct color_platform *colors = &priv->colors[location];
-
-       return sprintf(buf, "red: %d, green: %d, blue: %d\n",
-                      colors->red, colors->green, colors->blue);
-
-}
-
-static ssize_t zone_store(struct device *dev, struct device_attribute *attr,
-                         const char *buf, size_t count, u8 location)
-{
-       struct alienfx_priv *priv = dev_get_drvdata(dev);
-       struct color_platform *colors = &priv->colors[location];
-       struct alienfx_platdata *pdata = dev_get_platdata(dev);
-       int ret;
-
-       ret = parse_rgb(buf, colors);
-       if (ret)
-               return ret;
-
-       ret = pdata->ops.upd_led(priv, pdata->wdev, location);
-
-       return ret ? ret : count;
-}
-
-static ssize_t zone00_show(struct device *dev, struct device_attribute *attr,
-                          char *buf)
-{
-       return zone_show(dev, attr, buf, 0);
-}
-
-static ssize_t zone00_store(struct device *dev, struct device_attribute *attr,
-                           const char *buf, size_t count)
-{
-       return zone_store(dev, attr, buf, count, 0);
-}
-
-static DEVICE_ATTR_RW(zone00);
-
-static ssize_t zone01_show(struct device *dev, struct device_attribute *attr,
-                          char *buf)
-{
-       return zone_show(dev, attr, buf, 1);
-}
-
-static ssize_t zone01_store(struct device *dev, struct device_attribute *attr,
-                           const char *buf, size_t count)
-{
-       return zone_store(dev, attr, buf, count, 1);
-}
-
-static DEVICE_ATTR_RW(zone01);
-
-static ssize_t zone02_show(struct device *dev, struct device_attribute *attr,
-                          char *buf)
-{
-       return zone_show(dev, attr, buf, 2);
-}
-
-static ssize_t zone02_store(struct device *dev, struct device_attribute *attr,
-                           const char *buf, size_t count)
-{
-       return zone_store(dev, attr, buf, count, 2);
-}
-
-static DEVICE_ATTR_RW(zone02);
-
-static ssize_t zone03_show(struct device *dev, struct device_attribute *attr,
-                          char *buf)
-{
-       return zone_show(dev, attr, buf, 3);
-}
-
-static ssize_t zone03_store(struct device *dev, struct device_attribute *attr,
-                           const char *buf, size_t count)
-{
-       return zone_store(dev, attr, buf, count, 3);
-}
-
-static DEVICE_ATTR_RW(zone03);
-
-/*
- * Lighting control state device attribute (Global)
- */
-static ssize_t lighting_control_state_show(struct device *dev,
-                                          struct device_attribute *attr,
-                                          char *buf)
-{
-       struct alienfx_priv *priv = dev_get_drvdata(dev);
-
-       if (priv->lighting_control_state == LEGACY_BOOTING)
-               return sysfs_emit(buf, "[booting] running suspend\n");
-       else if (priv->lighting_control_state == LEGACY_SUSPEND)
-               return sysfs_emit(buf, "booting running [suspend]\n");
-
-       return sysfs_emit(buf, "booting [running] suspend\n");
-}
-
-static ssize_t lighting_control_state_store(struct device *dev,
-                                           struct device_attribute *attr,
-                                           const char *buf, size_t count)
-{
-       struct alienfx_priv *priv = dev_get_drvdata(dev);
-       u8 val;
-
-       if (strcmp(buf, "booting\n") == 0)
-               val = LEGACY_BOOTING;
-       else if (strcmp(buf, "suspend\n") == 0)
-               val = LEGACY_SUSPEND;
-       else if (interface == LEGACY)
-               val = LEGACY_RUNNING;
-       else
-               val = WMAX_RUNNING;
-
-       priv->lighting_control_state = val;
-       pr_debug("alienware-wmi: updated control state to %d\n",
-                priv->lighting_control_state);
-
-       return count;
-}
-
-static DEVICE_ATTR_RW(lighting_control_state);
-
-static umode_t zone_attr_visible(struct kobject *kobj,
-                                struct attribute *attr, int n)
-{
-       if (n < alienfx->num_zones + 1)
-               return attr->mode;
-
-       return 0;
-}
-
-static bool zone_group_visible(struct kobject *kobj)
-{
-       return alienfx->num_zones > 0;
-}
-DEFINE_SYSFS_GROUP_VISIBLE(zone);
-
-static struct attribute *zone_attrs[] = {
-       &dev_attr_lighting_control_state.attr,
-       &dev_attr_zone00.attr,
-       &dev_attr_zone01.attr,
-       &dev_attr_zone02.attr,
-       &dev_attr_zone03.attr,
-       NULL
-};
-
-static struct attribute_group zone_attribute_group = {
-       .name = "rgb_zones",
-       .is_visible = SYSFS_GROUP_VISIBLE(zone),
-       .attrs = zone_attrs,
-};
-
-/*
- * LED Brightness (Global)
- */
-static void global_led_set(struct led_classdev *led_cdev,
-                          enum led_brightness brightness)
-{
-       struct alienfx_priv *priv = container_of(led_cdev, struct alienfx_priv,
-                                                global_led);
-       struct alienfx_platdata *pdata = dev_get_platdata(&priv->pdev->dev);
-       int ret;
-
-       priv->global_brightness = brightness;
-
-       ret = pdata->ops.upd_brightness(priv, pdata->wdev, brightness);
-       if (ret)
-               pr_err("LED brightness update failed\n");
-}
-
-static enum led_brightness global_led_get(struct led_classdev *led_cdev)
-{
-       struct alienfx_priv *priv = container_of(led_cdev, struct alienfx_priv,
-                                                global_led);
-
-       return priv->global_brightness;
-}
-
-/*
- *     The HDMI mux sysfs node indicates the status of the HDMI input mux.
- *     It can toggle between standard system GPU output and HDMI input.
- */
-static ssize_t cable_show(struct device *dev, struct device_attribute *attr,
-                         char *buf)
-{
-       struct alienfx_platdata *pdata = dev_get_platdata(dev);
-       struct wmax_basic_args in_args = {
-               .arg = 0,
-       };
-       u32 out_data;
-       int ret;
-
-       ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_HDMI_CABLE,
-                                   &in_args, sizeof(in_args), &out_data);
-       if (!ret) {
-               if (out_data == 0)
-                       return sysfs_emit(buf, "[unconnected] connected unknown\n");
-               else if (out_data == 1)
-                       return sysfs_emit(buf, "unconnected [connected] unknown\n");
-       }
-
-       pr_err("alienware-wmi: unknown HDMI cable status: %d\n", ret);
-       return sysfs_emit(buf, "unconnected connected [unknown]\n");
-}
-
-static ssize_t source_show(struct device *dev, struct device_attribute *attr,
-                          char *buf)
-{
-       struct alienfx_platdata *pdata = dev_get_platdata(dev);
-       struct wmax_basic_args in_args = {
-               .arg = 0,
-       };
-       u32 out_data;
-       int ret;
-
-       ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_HDMI_STATUS,
-                                   &in_args, sizeof(in_args), &out_data);
-       if (!ret) {
-               if (out_data == 1)
-                       return sysfs_emit(buf, "[input] gpu unknown\n");
-               else if (out_data == 2)
-                       return sysfs_emit(buf, "input [gpu] unknown\n");
-       }
-
-       pr_err("alienware-wmi: unknown HDMI source status: %u\n", ret);
-       return sysfs_emit(buf, "input gpu [unknown]\n");
-}
-
-static ssize_t source_store(struct device *dev, struct device_attribute *attr,
-                           const char *buf, size_t count)
-{
-       struct alienfx_platdata *pdata = dev_get_platdata(dev);
-       struct wmax_basic_args args;
-       int ret;
-
-       if (strcmp(buf, "gpu\n") == 0)
-               args.arg = 1;
-       else if (strcmp(buf, "input\n") == 0)
-               args.arg = 2;
-       else
-               args.arg = 3;
-       pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf);
-
-       ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_HDMI_SOURCE, &args,
-                                   sizeof(args), NULL);
-       if (ret < 0)
-               pr_err("alienware-wmi: HDMI toggle failed: results: %u\n", ret);
-
-       return count;
-}
-
-static DEVICE_ATTR_RO(cable);
-static DEVICE_ATTR_RW(source);
-
-static bool hdmi_group_visible(struct kobject *kobj)
-{
-       return interface == WMAX && alienfx->hdmi_mux;
-}
-DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(hdmi);
-
-static struct attribute *hdmi_attrs[] = {
-       &dev_attr_cable.attr,
-       &dev_attr_source.attr,
-       NULL,
-};
-
-static const struct attribute_group hdmi_attribute_group = {
-       .name = "hdmi",
-       .is_visible = SYSFS_GROUP_VISIBLE(hdmi),
-       .attrs = hdmi_attrs,
-};
-
-/*
- * Alienware GFX amplifier support
- * - Currently supports reading cable status
- * - Leaving expansion room to possibly support dock/undock events later
- */
-static ssize_t status_show(struct device *dev, struct device_attribute *attr,
-                          char *buf)
-{
-       struct alienfx_platdata *pdata = dev_get_platdata(dev);
-       struct wmax_basic_args in_args = {
-               .arg = 0,
-       };
-       u32 out_data;
-       int ret;
-
-       ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_AMPLIFIER_CABLE,
-                                   &in_args, sizeof(in_args), &out_data);
-       if (!ret) {
-               if (out_data == 0)
-                       return sysfs_emit(buf, "[unconnected] connected unknown\n");
-               else if (out_data == 1)
-                       return sysfs_emit(buf, "unconnected [connected] unknown\n");
-       }
-
-       pr_err("alienware-wmi: unknown amplifier cable status: %d\n", ret);
-       return sysfs_emit(buf, "unconnected connected [unknown]\n");
-}
-
-static DEVICE_ATTR_RO(status);
-
-static bool amplifier_group_visible(struct kobject *kobj)
-{
-       return interface == WMAX && alienfx->amplifier;
-}
-DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(amplifier);
-
-static struct attribute *amplifier_attrs[] = {
-       &dev_attr_status.attr,
-       NULL,
-};
-
-static const struct attribute_group amplifier_attribute_group = {
-       .name = "amplifier",
-       .is_visible = SYSFS_GROUP_VISIBLE(amplifier),
-       .attrs = amplifier_attrs,
-};
-
-/*
- * Deep Sleep Control support
- * - Modifies BIOS setting for deep sleep control allowing extra wakeup events
- */
-static ssize_t deepsleep_show(struct device *dev, struct device_attribute *attr,
-                             char *buf)
-{
-       struct alienfx_platdata *pdata = dev_get_platdata(dev);
-       struct wmax_basic_args in_args = {
-               .arg = 0,
-       };
-       u32 out_data;
-       int ret;
-
-       ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_DEEP_SLEEP_STATUS,
-                                   &in_args, sizeof(in_args), &out_data);
-       if (!ret) {
-               if (out_data == 0)
-                       return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
-               else if (out_data == 1)
-                       return sysfs_emit(buf, "disabled [s5] s5_s4\n");
-               else if (out_data == 2)
-                       return sysfs_emit(buf, "disabled s5 [s5_s4]\n");
-       }
-
-       pr_err("alienware-wmi: unknown deep sleep status: %d\n", ret);
-       return sysfs_emit(buf, "disabled s5 s5_s4 [unknown]\n");
-}
-
-static ssize_t deepsleep_store(struct device *dev, struct device_attribute *attr,
-                              const char *buf, size_t count)
-{
-       struct alienfx_platdata *pdata = dev_get_platdata(dev);
-       struct wmax_basic_args args;
-       int ret;
-
-       if (strcmp(buf, "disabled\n") == 0)
-               args.arg = 0;
-       else if (strcmp(buf, "s5\n") == 0)
-               args.arg = 1;
-       else
-               args.arg = 2;
-       pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf);
-
-       ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_DEEP_SLEEP_CONTROL,
-                                   &args, sizeof(args), NULL);
-       if (!ret)
-               pr_err("alienware-wmi: deep sleep control failed: results: %u\n", ret);
-
-       return count;
-}
-
-static DEVICE_ATTR_RW(deepsleep);
-
-static bool deepsleep_group_visible(struct kobject *kobj)
-{
-       return interface == WMAX && alienfx->deepslp;
-}
-DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(deepsleep);
-
-static struct attribute *deepsleep_attrs[] = {
-       &dev_attr_deepsleep.attr,
-       NULL,
-};
-
-static const struct attribute_group deepsleep_attribute_group = {
-       .name = "deepsleep",
-       .is_visible = SYSFS_GROUP_VISIBLE(deepsleep),
-       .attrs = deepsleep_attrs,
-};
-
-/*
- * Thermal Profile control
- *  - Provides thermal profile control through the Platform Profile API
- */
-#define WMAX_THERMAL_TABLE_MASK                GENMASK(7, 4)
-#define WMAX_THERMAL_MODE_MASK         GENMASK(3, 0)
-#define WMAX_SENSOR_ID_MASK            BIT(8)
-
-static bool is_wmax_thermal_code(u32 code)
-{
-       if (code & WMAX_SENSOR_ID_MASK)
-               return false;
-
-       if ((code & WMAX_THERMAL_MODE_MASK) >= THERMAL_MODE_LAST)
-               return false;
-
-       if ((code & WMAX_THERMAL_TABLE_MASK) == WMAX_THERMAL_TABLE_BASIC &&
-           (code & WMAX_THERMAL_MODE_MASK) >= THERMAL_MODE_BASIC_QUIET)
-               return true;
-
-       if ((code & WMAX_THERMAL_TABLE_MASK) == WMAX_THERMAL_TABLE_USTT &&
-           (code & WMAX_THERMAL_MODE_MASK) <= THERMAL_MODE_USTT_LOW_POWER)
-               return true;
-
-       return false;
-}
-
-static int wmax_thermal_information(struct wmi_device *wdev, u8 operation,
-                                   u8 arg, u32 *out_data)
-{
-       struct wmax_u32_args in_args = {
-               .operation = operation,
-               .arg1 = arg,
-               .arg2 = 0,
-               .arg3 = 0,
-       };
-       int ret;
-
-       ret = alienware_wmi_command(wdev, WMAX_METHOD_THERMAL_INFORMATION,
-                                   &in_args, sizeof(in_args), out_data);
-       if (ret < 0)
-               return ret;
-
-       if (*out_data == WMAX_FAILURE_CODE)
-               return -EBADRQC;
-
-       return 0;
-}
-
-static int wmax_thermal_control(struct wmi_device *wdev, u8 profile)
-{
-       struct wmax_u32_args in_args = {
-               .operation = WMAX_OPERATION_ACTIVATE_PROFILE,
-               .arg1 = profile,
-               .arg2 = 0,
-               .arg3 = 0,
-       };
-       u32 out_data;
-       int ret;
-
-       ret = alienware_wmi_command(wdev, WMAX_METHOD_THERMAL_CONTROL,
-                                   &in_args, sizeof(in_args), &out_data);
-       if (ret)
-               return ret;
-
-       if (out_data == WMAX_FAILURE_CODE)
-               return -EBADRQC;
-
-       return 0;
-}
-
-static int wmax_game_shift_status(struct wmi_device *wdev, u8 operation,
-                                 u32 *out_data)
-{
-       struct wmax_u32_args in_args = {
-               .operation = operation,
-               .arg1 = 0,
-               .arg2 = 0,
-               .arg3 = 0,
-       };
-       int ret;
-
-       ret = alienware_wmi_command(wdev, WMAX_METHOD_GAME_SHIFT_STATUS,
-                                   &in_args, sizeof(in_args), out_data);
-       if (ret < 0)
-               return ret;
-
-       if (*out_data == WMAX_FAILURE_CODE)
-               return -EOPNOTSUPP;
-
-       return 0;
-}
-
-static int thermal_profile_get(struct device *dev,
-                              enum platform_profile_option *profile)
-{
-       struct awcc_priv *priv = dev_get_drvdata(dev);
-       u32 out_data;
-       int ret;
-
-       ret = wmax_thermal_information(priv->wdev, WMAX_OPERATION_CURRENT_PROFILE,
-                                      0, &out_data);
-
-       if (ret < 0)
-               return ret;
-
-       if (out_data == WMAX_THERMAL_MODE_GMODE) {
-               *profile = PLATFORM_PROFILE_PERFORMANCE;
-               return 0;
-       }
-
-       if (!is_wmax_thermal_code(out_data))
-               return -ENODATA;
-
-       out_data &= WMAX_THERMAL_MODE_MASK;
-       *profile = wmax_mode_to_platform_profile[out_data];
-
-       return 0;
-}
-
-static int thermal_profile_set(struct device *dev,
-                              enum platform_profile_option profile)
-{
-       struct awcc_priv *priv = dev_get_drvdata(dev);
-
-       if (awcc->gmode) {
-               u32 gmode_status;
-               int ret;
-
-               ret = wmax_game_shift_status(priv->wdev,
-                                            WMAX_OPERATION_GET_GAME_SHIFT_STATUS,
-                                            &gmode_status);
-
-               if (ret < 0)
-                       return ret;
-
-               if ((profile == PLATFORM_PROFILE_PERFORMANCE && !gmode_status) ||
-                   (profile != PLATFORM_PROFILE_PERFORMANCE && gmode_status)) {
-                       ret = wmax_game_shift_status(priv->wdev,
-                                                    WMAX_OPERATION_TOGGLE_GAME_SHIFT,
-                                                    &gmode_status);
-
-                       if (ret < 0)
-                               return ret;
-               }
-       }
-
-       return wmax_thermal_control(priv->wdev,
-                                   priv->supported_thermal_profiles[profile]);
-}
-
-static int thermal_profile_probe(void *drvdata, unsigned long *choices)
-{
-       enum platform_profile_option profile;
-       struct awcc_priv *priv = drvdata;
-       enum wmax_thermal_mode mode;
-       u8 sys_desc[4];
-       u32 first_mode;
-       u32 out_data;
-       int ret;
-
-       ret = wmax_thermal_information(priv->wdev, WMAX_OPERATION_SYS_DESCRIPTION,
-                                      0, (u32 *) &sys_desc);
-       if (ret < 0)
-               return ret;
-
-       first_mode = sys_desc[0] + sys_desc[1];
-
-       for (u32 i = 0; i < sys_desc[3]; i++) {
-               ret = wmax_thermal_information(priv->wdev, WMAX_OPERATION_LIST_IDS,
-                                              i + first_mode, &out_data);
-
-               if (ret == -EIO)
-                       return ret;
-
-               if (ret == -EBADRQC)
-                       break;
-
-               if (!is_wmax_thermal_code(out_data))
-                       continue;
-
-               mode = out_data & WMAX_THERMAL_MODE_MASK;
-               profile = wmax_mode_to_platform_profile[mode];
-               priv->supported_thermal_profiles[profile] = out_data;
-
-               set_bit(profile, choices);
-       }
-
-       if (bitmap_empty(choices, PLATFORM_PROFILE_LAST))
-               return -ENODEV;
-
-       if (awcc->gmode) {
-               priv->supported_thermal_profiles[PLATFORM_PROFILE_PERFORMANCE] =
-                       WMAX_THERMAL_MODE_GMODE;
-
-               set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
-       }
-
-       return 0;
-}
-
-static const struct platform_profile_ops awcc_platform_profile_ops = {
-       .probe = thermal_profile_probe,
-       .profile_get = thermal_profile_get,
-       .profile_set = thermal_profile_set,
-};
-
-static int awcc_platform_profile_init(struct wmi_device *wdev)
-{
-       struct awcc_priv *priv = dev_get_drvdata(&wdev->dev);
-
-       priv->ppdev = devm_platform_profile_register(&wdev->dev, "alienware-wmi",
-                                                    priv, &awcc_platform_profile_ops);
-
-       return PTR_ERR_OR_ZERO(priv->ppdev);
-}
-
-static int alienware_awcc_setup(struct wmi_device *wdev)
-{
-       struct awcc_priv *priv;
-       int ret;
-
-       priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
-       if (!priv)
-               return -ENOMEM;
-
-       priv->wdev = wdev;
-       dev_set_drvdata(&wdev->dev, priv);
-
-       if (awcc->pprof) {
-               ret = awcc_platform_profile_init(wdev);
-               if (ret)
-                       return ret;
-       }
-
-       return 0;
-}
-
-/*
- * Platform Driver
- */
-static int alienfx_probe(struct platform_device *pdev)
-{
-       struct alienfx_priv *priv;
-
-       priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
-       if (!priv)
-               return -ENOMEM;
-
-       if (interface == WMAX)
-               priv->lighting_control_state = WMAX_RUNNING;
-       else
-               priv->lighting_control_state = LEGACY_RUNNING;
-
-       priv->pdev = pdev;
-       priv->global_led.name = "alienware::global_brightness";
-       priv->global_led.brightness_set = global_led_set;
-       priv->global_led.brightness_get = global_led_get;
-       priv->global_led.max_brightness = 0x0F;
-       priv->global_brightness = priv->global_led.max_brightness;
-       platform_set_drvdata(pdev, priv);
-
-       return devm_led_classdev_register(&pdev->dev, &priv->global_led);
-}
-
-static const struct attribute_group *alienfx_groups[] = {
-       &zone_attribute_group,
-       &hdmi_attribute_group,
-       &amplifier_attribute_group,
-       &deepsleep_attribute_group,
-       NULL
-};
-
-static struct platform_driver platform_driver = {
-       .driver = {
-               .name = "alienware-wmi",
-               .dev_groups = alienfx_groups,
-       },
-       .probe = alienfx_probe,
-};
-
-static void alienware_alienfx_remove(void *data)
-{
-       struct platform_device *pdev = data;
-
-       platform_device_unregister(pdev);
-}
-
-static int alienware_alienfx_setup(struct alienfx_platdata *pdata)
-{
-       struct device *dev = &pdata->wdev->dev;
-       struct platform_device *pdev;
-       int ret;
-
-       pdev = platform_device_register_data(NULL, "alienware-wmi",
-                                            PLATFORM_DEVID_NONE, pdata,
-                                            sizeof(*pdata));
-       if (IS_ERR(pdev))
-               return PTR_ERR(pdev);
-
-       dev_set_drvdata(dev, pdev);
-       ret = devm_add_action_or_reset(dev, alienware_alienfx_remove, pdev);
-       if (ret)
-               return ret;
-
-       return 0;
-}
-
-/*
- * Legacy WMI driver
- */
-static int legacy_wmi_update_led(struct alienfx_priv *priv,
-                                struct wmi_device *wdev, u8 location)
-{
-       struct legacy_led_args legacy_args = {
-               .colors = priv->colors[location],
-               .brightness = priv->global_brightness,
-               .state = 0,
-       };
-       struct acpi_buffer input;
-       acpi_status status;
-
-       if (legacy_args.state != LEGACY_RUNNING) {
-               legacy_args.state = priv->lighting_control_state;
-
-               input.length = sizeof(legacy_args);
-               input.pointer = &legacy_args;
-
-               status = wmi_evaluate_method(LEGACY_POWER_CONTROL_GUID, 0,
-                                            location + 1, &input, NULL);
-               if (ACPI_FAILURE(status))
-                       return -EIO;
-
-               return 0;
-       }
-
-       return alienware_wmi_command(wdev, location + 1, &legacy_args,
-                                    sizeof(legacy_args), NULL);
-}
-
-static int legacy_wmi_update_brightness(struct alienfx_priv *priv,
-                                       struct wmi_device *wdev, u8 brightness)
-{
-       return legacy_wmi_update_led(priv, wdev, 0);
-}
-
-static int legacy_wmi_probe(struct wmi_device *wdev, const void *context)
-{
-       struct alienfx_platdata pdata = {
-               .wdev = wdev,
-               .ops = {
-                       .upd_led = legacy_wmi_update_led,
-                       .upd_brightness = legacy_wmi_update_brightness,
-               },
-       };
-
-       return alienware_alienfx_setup(&pdata);
-}
-
-static const struct wmi_device_id alienware_legacy_device_id_table[] = {
-       { LEGACY_CONTROL_GUID, NULL },
-       { },
-};
-MODULE_DEVICE_TABLE(wmi, alienware_legacy_device_id_table);
-
-static struct wmi_driver alienware_legacy_wmi_driver = {
-       .driver = {
-               .name = "alienware-wmi-alienfx",
-               .probe_type = PROBE_PREFER_ASYNCHRONOUS,
-       },
-       .id_table = alienware_legacy_device_id_table,
-       .probe = legacy_wmi_probe,
-       .no_singleton = true,
-};
-
-static int __init alienware_legacy_wmi_init(void)
-{
-       return wmi_driver_register(&alienware_legacy_wmi_driver);
-}
-
-static void __exit alienware_legacy_wmi_exit(void)
-{
-       wmi_driver_unregister(&alienware_legacy_wmi_driver);
-}
-
-/*
- * WMAX WMI driver
- */
-static int wmax_wmi_update_led(struct alienfx_priv *priv,
-                              struct wmi_device *wdev, u8 location)
-{
-       struct wmax_led_args in_args = {
-               .led_mask = 1 << location,
-               .colors = priv->colors[location],
-               .state = priv->lighting_control_state,
-       };
-
-       return alienware_wmi_command(wdev, WMAX_METHOD_ZONE_CONTROL, &in_args,
-                                    sizeof(in_args), NULL);
-}
-
-static int wmax_wmi_update_brightness(struct alienfx_priv *priv,
-                                     struct wmi_device *wdev, u8 brightness)
-{
-       struct wmax_brightness_args in_args = {
-               .led_mask = 0xFF,
-               .percentage = brightness,
-       };
-
-       return alienware_wmi_command(wdev, WMAX_METHOD_BRIGHTNESS, &in_args,
-                                    sizeof(in_args), NULL);
-}
-
-static int wmax_wmi_probe(struct wmi_device *wdev, const void *context)
-{
-       struct alienfx_platdata pdata = {
-               .wdev = wdev,
-               .ops = {
-                       .upd_led = wmax_wmi_update_led,
-                       .upd_brightness = wmax_wmi_update_brightness,
-               },
-       };
-       int ret;
-
-       if (awcc)
-               ret = alienware_awcc_setup(wdev);
-       else
-               ret = alienware_alienfx_setup(&pdata);
-
-       return ret;
-}
-
-static const struct wmi_device_id alienware_wmax_device_id_table[] = {
-       { WMAX_CONTROL_GUID, NULL },
-       { },
-};
-MODULE_DEVICE_TABLE(wmi, alienware_wmax_device_id_table);
-
-static struct wmi_driver alienware_wmax_wmi_driver = {
-       .driver = {
-               .name = "alienware-wmi-wmax",
-               .probe_type = PROBE_PREFER_ASYNCHRONOUS,
-       },
-       .id_table = alienware_wmax_device_id_table,
-       .probe = wmax_wmi_probe,
-       .no_singleton = true,
-};
-
-static int __init alienware_wmax_wmi_init(void)
-{
-       const struct dmi_system_id *id;
-
-       id = dmi_first_match(awcc_dmi_table);
-       if (id)
-               awcc = id->driver_data;
-
-       if (force_platform_profile) {
-               if (!awcc)
-                       awcc = &empty_quirks;
-
-               awcc->pprof = true;
-       }
-
-       if (force_gmode) {
-               if (awcc)
-                       awcc->gmode = true;
-               else
-                       pr_warn("force_gmode requires platform profile support\n");
-       }
-
-       return wmi_driver_register(&alienware_wmax_wmi_driver);
-}
-
-static void __exit alienware_wmax_wmi_exit(void)
-{
-       wmi_driver_unregister(&alienware_wmax_wmi_driver);
-}
-
-static int __init alienware_wmi_init(void)
-{
-       int ret;
-
-       dmi_check_system(alienware_quirks);
-       if (!alienfx)
-               alienfx = &quirk_unknown;
-
-       ret = platform_driver_register(&platform_driver);
-       if (ret < 0)
-               return ret;
-
-       if (wmi_has_guid(WMAX_CONTROL_GUID)) {
-               interface = WMAX;
-               ret = alienware_wmax_wmi_init();
-       } else {
-               interface = LEGACY;
-               ret = alienware_legacy_wmi_init();
-       }
-
-       if (ret < 0)
-               platform_driver_unregister(&platform_driver);
-
-       return ret;
-}
-
-module_init(alienware_wmi_init);
-
-static void __exit alienware_wmi_exit(void)
-{
-       if (interface == WMAX)
-               alienware_wmax_wmi_exit();
-       else
-               alienware_legacy_wmi_exit();
-
-       platform_driver_unregister(&platform_driver);
-}
-
-module_exit(alienware_wmi_exit);