lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20241205004730.2187107-2-kuurtb@gmail.com>
Date: Wed,  4 Dec 2024 21:47:31 -0300
From: Kurt Borja <kuurtb@...il.com>
To: kuurtb@...il.com
Cc: Dell.Client.Kernel@...l.com,
	hdegoede@...hat.com,
	ilpo.jarvinen@...ux.intel.com,
	linux-kernel@...r.kernel.org,
	mario.limonciello@....com,
	platform-driver-x86@...r.kernel.org,
	w_armin@....de
Subject: [RFC PATCH 20/21] platform-x86: Split the alienware-wmi module

Split the alienware-wmi module into three files, which correspond to the
different interfaces this module exposed. The new structure roughly
works like this:

 - alienware-wmi-base.c: Manages DMI quirks and will initialize the
   preferred WMI driver
 - alienware-wmi-alienfx.c: Manages the sysfs interface exposed by the
   "alienware-wmi" platform device
 - alienware-wmi-awcc.c: Manages thermal control methods present on new
   Alienware and Dell devices

These three parts already worked completely independently, so the split
is done seamlessly by copying and pasting.

Signed-off-by: Kurt Borja <kuurtb@...il.com>
---
 drivers/platform/x86/dell/Makefile            |   2 +
 .../platform/x86/dell/alienware-wmi-alienfx.c | 531 ++++++++++++
 .../platform/x86/dell/alienware-wmi-awcc.c    | 282 +++++++
 .../platform/x86/dell/alienware-wmi-base.c    | 794 ------------------
 4 files changed, 815 insertions(+), 794 deletions(-)
 create mode 100644 drivers/platform/x86/dell/alienware-wmi-alienfx.c
 create mode 100644 drivers/platform/x86/dell/alienware-wmi-awcc.c

diff --git a/drivers/platform/x86/dell/Makefile b/drivers/platform/x86/dell/Makefile
index 5e7496aeb070..54a592fd6ae6 100644
--- a/drivers/platform/x86/dell/Makefile
+++ b/drivers/platform/x86/dell/Makefile
@@ -6,6 +6,8 @@
 
 obj-$(CONFIG_ALIENWARE_WMI)		+= alienware-wmi.o
 alienware-wmi-objs			:= alienware-wmi-base.o
+alienware-wmi-y				+= alienware-wmi-alienfx.o
+alienware-wmi-y				+= alienware-wmi-awcc.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-alienfx.c b/drivers/platform/x86/dell/alienware-wmi-alienfx.c
new file mode 100644
index 000000000000..d0291d1ba9bb
--- /dev/null
+++ b/drivers/platform/x86/dell/alienware-wmi-alienfx.c
@@ -0,0 +1,531 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Alienware AlienFX control
+ *
+ * Copyright (C) 2014 Dell Inc <Dell.Client.Kernel@...l.com>
+ */
+
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/wmi.h>
+#include "alienware-wmi.h"
+
+struct wmax_basic_args {
+	u8 arg;
+};
+
+/*
+ * 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;
+	struct color_platform *colors;
+
+	priv = dev_get_drvdata(dev);
+	colors = &priv->colors[location];
+
+	return sprintf(buf, "red: %d, green: %d, blue: %d\n",
+		       colors->red, colors->green, colors->blue);
+
+}
+
+static ssize_t zone_set(struct device *dev, struct device_attribute *attr,
+			const char *buf, size_t count, u8 location)
+{
+	struct alienfx_priv *priv;
+	struct alienfx_platdata *pdata;
+	struct color_platform *colors;
+	int ret;
+
+	priv = dev_get_drvdata(dev);
+	pdata = dev_get_platdata(dev);
+
+	colors = &priv->colors[location];
+	ret = parse_rgb(buf, colors);
+	if (ret)
+		return ret;
+
+	ret = pdata->ops.upd_led(priv, pdata->wdev, location);
+
+	return ret ? ret : count;
+}
+
+#define ALIENWARE_ZONE_SHOW_FUNC(_num)					\
+	static ssize_t zone0##_num##_show(struct device *dev,		\
+					struct device_attribute *attr,	\
+					char *buf)			\
+	{								\
+		return zone_show(dev, attr, buf, _num);			\
+	}
+
+#define ALIENWARE_ZONE_STORE_FUNC(_num)					\
+	static ssize_t zone0##_num##_store(struct device *dev,		\
+					struct device_attribute *attr,	\
+					const char *buf, size_t count)	\
+	{								\
+		return zone_set(dev, attr, buf, count, _num);		\
+	}
+
+#define ALIENWARE_ZONE_ATTR(_num)					\
+	ALIENWARE_ZONE_SHOW_FUNC(_num)					\
+	ALIENWARE_ZONE_STORE_FUNC(_num)					\
+	static DEVICE_ATTR_RW(zone0##_num)
+
+ALIENWARE_ZONE_ATTR(0);
+ALIENWARE_ZONE_ATTR(1);
+ALIENWARE_ZONE_ATTR(2);
+ALIENWARE_ZONE_ATTR(3);
+
+/*
+ * 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;
+
+	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;
+	struct alienfx_platdata *pdata;
+	u8 val;
+
+	priv = dev_get_drvdata(dev);
+	pdata = dev_get_platdata(dev);
+
+	if (strcmp(buf, "booting\n") == 0)
+		val = LEGACY_BOOTING;
+	else if (strcmp(buf, "suspend\n") == 0)
+		val = LEGACY_SUSPEND;
+	else
+		val = pdata->running_code;
+
+	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)
+{
+	struct device *dev;
+	struct alienfx_platdata *pdata;
+
+	dev = container_of(kobj, struct device, kobj);
+	pdata = dev_get_platdata(dev);
+
+	return n < pdata->num_zones + 1 ? 0644 : 0;
+}
+
+static bool zone_group_visible(struct kobject *kobj)
+{
+	struct device *dev;
+	struct alienfx_platdata *pdata;
+
+	dev = container_of(kobj, struct device, kobj);
+	pdata = dev_get_platdata(dev);
+
+	return pdata->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;
+	struct alienfx_platdata *pdata;
+	int ret;
+
+	priv = container_of(led_cdev, struct alienfx_priv, global_led);
+	pdata = dev_get_platdata(&priv->pdev->dev);
+
+	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;
+
+	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 show_hdmi_cable(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	struct alienfx_platdata *pdata;
+	acpi_status status;
+	u32 out_data;
+	struct wmax_basic_args in_args = {
+		.arg = 0,
+	};
+
+	pdata = dev_get_platdata(dev);
+
+	status = alienware_wmi_command(pdata->wdev, WMAX_METHOD_HDMI_CABLE,
+				       &in_args, sizeof(in_args), &out_data);
+
+	if (ACPI_SUCCESS(status)) {
+		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", status);
+	return sysfs_emit(buf, "unconnected connected [unknown]\n");
+}
+
+static ssize_t show_hdmi_source(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct alienfx_platdata *pdata;
+	acpi_status status;
+	u32 out_data;
+	struct wmax_basic_args in_args = {
+		.arg = 0,
+	};
+
+	pdata = dev_get_platdata(dev);
+
+	status = alienware_wmi_command(pdata->wdev, WMAX_METHOD_HDMI_STATUS,
+				       &in_args, sizeof(in_args), &out_data);
+
+	if (ACPI_SUCCESS(status)) {
+		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", status);
+	return sysfs_emit(buf, "input gpu [unknown]\n");
+}
+
+static ssize_t toggle_hdmi_source(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t count)
+{
+	struct alienfx_platdata *pdata;
+	acpi_status status;
+	struct wmax_basic_args args;
+
+	pdata = dev_get_platdata(dev);
+
+	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);
+
+	status = alienware_wmi_command(pdata->wdev, WMAX_METHOD_HDMI_SOURCE,
+				       &args, sizeof(args), NULL);
+
+	if (ACPI_FAILURE(status))
+		pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
+		       status);
+	return count;
+}
+
+static DEVICE_ATTR(cable, S_IRUGO, show_hdmi_cable, NULL);
+static DEVICE_ATTR(source, S_IRUGO | S_IWUSR, show_hdmi_source,
+		   toggle_hdmi_source);
+
+static bool hdmi_group_visible(struct kobject *kobj)
+{
+	struct device *dev;
+	struct alienfx_platdata *pdata;
+
+	dev = container_of(kobj, struct device, kobj);
+	pdata = dev_get_platdata(dev);
+
+	return pdata->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 show_amplifier_status(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	struct alienfx_platdata *pdata;
+	acpi_status status;
+	u32 out_data;
+	struct wmax_basic_args in_args = {
+		.arg = 0,
+	};
+
+	pdata = dev_get_platdata(dev);
+
+	status = alienware_wmi_command(pdata->wdev, WMAX_METHOD_AMPLIFIER_CABLE,
+				       &in_args, sizeof(in_args), &out_data);
+	if (ACPI_SUCCESS(status)) {
+		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", status);
+	return sysfs_emit(buf, "unconnected connected [unknown]\n");
+}
+
+static DEVICE_ATTR(status, S_IRUGO, show_amplifier_status, NULL);
+
+static bool amplifier_group_visible(struct kobject *kobj)
+{
+	struct device *dev;
+	struct alienfx_platdata *pdata;
+
+	dev = container_of(kobj, struct device, kobj);
+	pdata = dev_get_platdata(dev);
+
+	return pdata->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 show_deepsleep_status(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	struct alienfx_platdata *pdata;
+	acpi_status status;
+	u32 out_data;
+	struct wmax_basic_args in_args = {
+		.arg = 0,
+	};
+
+	pdata = dev_get_platdata(dev);
+
+	status = alienware_wmi_command(pdata->wdev, WMAX_METHOD_DEEP_SLEEP_STATUS,
+				       &in_args, sizeof(in_args), &out_data);
+	if (ACPI_SUCCESS(status)) {
+		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", status);
+	return sysfs_emit(buf, "disabled s5 s5_s4 [unknown]\n");
+}
+
+static ssize_t toggle_deepsleep(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct alienfx_platdata *pdata;
+	acpi_status status;
+	struct wmax_basic_args args;
+
+	pdata = dev_get_platdata(dev);
+
+	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);
+
+	status = alienware_wmi_command(pdata->wdev, WMAX_METHOD_DEEP_SLEEP_CONTROL,
+				       &args, sizeof(args), NULL);
+
+	if (ACPI_FAILURE(status))
+		pr_err("alienware-wmi: deep sleep control failed: results: %u\n",
+			status);
+	return count;
+}
+
+static DEVICE_ATTR(deepsleep, S_IRUGO | S_IWUSR, show_deepsleep_status, toggle_deepsleep);
+
+static bool deepsleep_group_visible(struct kobject *kobj)
+{
+	struct device *dev;
+	struct alienfx_platdata *pdata;
+
+	dev = container_of(kobj, struct device, kobj);
+	pdata = dev_get_platdata(dev);
+
+	return pdata->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,
+};
+
+/*
+ * Platform Driver
+ */
+static int alienfx_probe(struct platform_device *pdev)
+{
+	struct alienfx_priv *priv;
+	struct alienfx_platdata *pdata;
+	struct led_classdev *leds;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	platform_set_drvdata(pdev, priv);
+
+	priv->pdev = pdev;
+
+	pdata = dev_get_platdata(&pdev->dev);
+
+	priv->lighting_control_state = pdata->running_code;
+
+	leds = &priv->global_led;
+	leds->name = "alienware::global_brightness";
+	leds->brightness_set = global_led_set;
+	leds->brightness_get = global_led_get;
+	leds->max_brightness = 0x0F;
+
+	priv->global_brightness = priv->global_led.max_brightness;
+
+	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,
+};
+
+int alienfx_wmi_init(struct alienfx_platdata *pdata)
+{
+	struct platform_device *pdev;
+
+	pdev = platform_create_bundle(&platform_driver, alienfx_probe, NULL, 0,
+				      pdata, sizeof(*pdata));
+
+	dev_set_drvdata(&pdata->wdev->dev, pdev);
+
+	return PTR_ERR_OR_ZERO(pdev);
+}
+
+void alienfx_wmi_exit(struct wmi_device *wdev)
+{
+	struct platform_device *pdev;
+
+	pdev = dev_get_drvdata(&wdev->dev);
+
+	platform_device_unregister(pdev);
+	platform_driver_unregister(&platform_driver);
+}
diff --git a/drivers/platform/x86/dell/alienware-wmi-awcc.c b/drivers/platform/x86/dell/alienware-wmi-awcc.c
new file mode 100644
index 000000000000..115ef8436ae3
--- /dev/null
+++ b/drivers/platform/x86/dell/alienware-wmi-awcc.c
@@ -0,0 +1,282 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Alienware AlienFX control
+ *
+ * Copyright (C) 2024 Kurt Borja <kuurtb@...il.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/platform_device.h>
+#include <linux/platform_profile.h>
+#include <linux/wmi.h>
+#include "alienware-wmi.h"
+
+#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
+#define WMAX_THERMAL_TABLE_MASK		GENMASK(7, 4)
+#define WMAX_THERMAL_MODE_MASK		GENMASK(3, 0)
+#define WMAX_SENSOR_ID_MASK		BIT(8)
+
+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,
+};
+
+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 wmax_u32_args {
+	u8 operation;
+	u8 arg1;
+	u8 arg2;
+	u8 arg3;
+};
+
+/*
+ * Thermal Profile control
+ *  - Provides thermal profile control through the Platform Profile API
+ */
+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)
+{
+	acpi_status status;
+	struct wmax_u32_args in_args = {
+		.operation = operation,
+		.arg1 = arg,
+		.arg2 = 0,
+		.arg3 = 0,
+	};
+
+	status = alienware_wmi_command(wdev, WMAX_METHOD_THERMAL_INFORMATION,
+				       &in_args, sizeof(in_args), out_data);
+
+	if (ACPI_FAILURE(status))
+		return -EIO;
+
+	if (*out_data == WMAX_FAILURE_CODE)
+		return -EBADRQC;
+
+	return 0;
+}
+
+static int wmax_thermal_control(struct wmi_device *wdev, u8 profile)
+{
+	acpi_status status;
+	struct wmax_u32_args in_args = {
+		.operation = WMAX_OPERATION_ACTIVATE_PROFILE,
+		.arg1 = profile,
+		.arg2 = 0,
+		.arg3 = 0,
+	};
+	u32 out_data;
+
+	status = alienware_wmi_command(wdev, WMAX_METHOD_THERMAL_CONTROL,
+				       &in_args, sizeof(in_args), &out_data);
+
+	if (ACPI_FAILURE(status))
+		return -EIO;
+
+	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)
+{
+	acpi_status status;
+	struct wmax_u32_args in_args = {
+		.operation = operation,
+		.arg1 = 0,
+		.arg2 = 0,
+		.arg3 = 0,
+	};
+
+	status = alienware_wmi_command(wdev, WMAX_METHOD_GAME_SHIFT_STATUS,
+				       &in_args, sizeof(in_args), out_data);
+
+	if (ACPI_FAILURE(status))
+		return -EIO;
+
+	if (*out_data == WMAX_FAILURE_CODE)
+		return -EOPNOTSUPP;
+
+	return 0;
+}
+
+static int thermal_profile_get(struct platform_profile_handler *pprof,
+			       enum platform_profile_option *profile)
+{
+	struct awcc_priv *priv;
+	u32 out_data;
+	int ret;
+
+	priv = container_of(pprof, struct awcc_priv, pp_handler);
+
+	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 platform_profile_handler *pprof,
+			       enum platform_profile_option profile)
+{
+	struct awcc_priv *priv;
+
+	priv = container_of(pprof, struct awcc_priv, pp_handler);
+
+	if (priv->has_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]);
+}
+
+int create_thermal_profile(struct wmi_device *wdev, bool has_gmode)
+{
+	struct awcc_priv *priv;
+	u32 out_data;
+	u8 sys_desc[4];
+	u32 first_mode;
+	enum wmax_thermal_mode mode;
+	enum platform_profile_option profile;
+	int ret;
+
+	priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
+	dev_set_drvdata(&wdev->dev, priv);
+
+	priv->wdev = wdev;
+
+	ret = wmax_thermal_information(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(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, priv->pp_handler.choices);
+	}
+
+	if (bitmap_empty(priv->pp_handler.choices, PLATFORM_PROFILE_LAST))
+		return -ENODEV;
+
+	if (has_gmode) {
+		priv->has_gmode = true;
+		priv->supported_thermal_profiles[PLATFORM_PROFILE_PERFORMANCE] =
+			WMAX_THERMAL_MODE_GMODE;
+
+		set_bit(PLATFORM_PROFILE_PERFORMANCE, priv->pp_handler.choices);
+	}
+
+	priv->pp_handler.profile_get = thermal_profile_get;
+	priv->pp_handler.profile_set = thermal_profile_set;
+
+	return platform_profile_register(&priv->pp_handler);
+}
+
+void remove_thermal_profile(void)
+{
+	platform_profile_remove();
+}
diff --git a/drivers/platform/x86/dell/alienware-wmi-base.c b/drivers/platform/x86/dell/alienware-wmi-base.c
index 4165eb0d0bf5..7cc6bb3dc0d7 100644
--- a/drivers/platform/x86/dell/alienware-wmi-base.c
+++ b/drivers/platform/x86/dell/alienware-wmi-base.c
@@ -8,24 +8,12 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/acpi.h>
-#include <linux/bitfield.h>
-#include <linux/bits.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>
 #include "alienware-wmi.h"
 
-#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@...look.com>");
 MODULE_DESCRIPTION("Alienware special feature control");
 MODULE_LICENSE("GPL");
@@ -43,39 +31,6 @@ enum INTERFACE_FLAGS {
 	WMAX,
 };
 
-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,
-};
-
-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 quirk_entry {
 	u8 num_zones;
 	u8 hdmi_mux;
@@ -332,17 +287,6 @@ static const struct dmi_system_id alienware_quirks[] __initconst = {
 	{}
 };
 
-struct wmax_basic_args {
-	u8 arg;
-};
-
-struct wmax_u32_args {
-	u8 operation;
-	u8 arg1;
-	u8 arg2;
-	u8 arg3;
-};
-
 static u8 interface;
 static struct wmi_driver *preferred_wmi_driver;
 
@@ -372,744 +316,6 @@ acpi_status alienware_wmi_command(struct wmi_device *wdev, u32 method_id,
 	return ret;
 }
 
-/*
- * 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;
-	struct color_platform *colors;
-
-	priv = dev_get_drvdata(dev);
-	colors = &priv->colors[location];
-
-	return sprintf(buf, "red: %d, green: %d, blue: %d\n",
-		       colors->red, colors->green, colors->blue);
-
-}
-
-static ssize_t zone_set(struct device *dev, struct device_attribute *attr,
-			const char *buf, size_t count, u8 location)
-{
-	struct alienfx_priv *priv;
-	struct alienfx_platdata *pdata;
-	struct color_platform *colors;
-	int ret;
-
-	priv = dev_get_drvdata(dev);
-	pdata = dev_get_platdata(dev);
-
-	colors = &priv->colors[location];
-	ret = parse_rgb(buf, colors);
-	if (ret)
-		return ret;
-
-	ret = pdata->ops.upd_led(priv, pdata->wdev, location);
-
-	return ret ? ret : count;
-}
-
-#define ALIENWARE_ZONE_SHOW_FUNC(_num)					\
-	static ssize_t zone0##_num##_show(struct device *dev,		\
-					struct device_attribute *attr,	\
-					char *buf)			\
-	{								\
-		return zone_show(dev, attr, buf, _num);			\
-	}
-
-#define ALIENWARE_ZONE_STORE_FUNC(_num)					\
-	static ssize_t zone0##_num##_store(struct device *dev,		\
-					struct device_attribute *attr,	\
-					const char *buf, size_t count)	\
-	{								\
-		return zone_set(dev, attr, buf, count, _num);		\
-	}
-
-#define ALIENWARE_ZONE_ATTR(_num)					\
-	ALIENWARE_ZONE_SHOW_FUNC(_num)					\
-	ALIENWARE_ZONE_STORE_FUNC(_num)					\
-	static DEVICE_ATTR_RW(zone0##_num)
-
-ALIENWARE_ZONE_ATTR(0);
-ALIENWARE_ZONE_ATTR(1);
-ALIENWARE_ZONE_ATTR(2);
-ALIENWARE_ZONE_ATTR(3);
-
-/*
- * 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;
-
-	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;
-	struct alienfx_platdata *pdata;
-	u8 val;
-
-	priv = dev_get_drvdata(dev);
-	pdata = dev_get_platdata(dev);
-
-	if (strcmp(buf, "booting\n") == 0)
-		val = LEGACY_BOOTING;
-	else if (strcmp(buf, "suspend\n") == 0)
-		val = LEGACY_SUSPEND;
-	else
-		val = pdata->running_code;
-
-	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)
-{
-	struct device *dev;
-	struct alienfx_platdata *pdata;
-
-	dev = container_of(kobj, struct device, kobj);
-	pdata = dev_get_platdata(dev);
-
-	return n < pdata->num_zones + 1 ? 0644 : 0;
-}
-
-static bool zone_group_visible(struct kobject *kobj)
-{
-	struct device *dev;
-	struct alienfx_platdata *pdata;
-
-	dev = container_of(kobj, struct device, kobj);
-	pdata = dev_get_platdata(dev);
-
-	return pdata->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;
-	struct alienfx_platdata *pdata;
-	int ret;
-
-	priv = container_of(led_cdev, struct alienfx_priv, global_led);
-	pdata = dev_get_platdata(&priv->pdev->dev);
-
-	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;
-
-	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 show_hdmi_cable(struct device *dev,
-			       struct device_attribute *attr, char *buf)
-{
-	struct alienfx_platdata *pdata;
-	acpi_status status;
-	u32 out_data;
-	struct wmax_basic_args in_args = {
-		.arg = 0,
-	};
-
-	pdata = dev_get_platdata(dev);
-
-	status = alienware_wmi_command(pdata->wdev, WMAX_METHOD_HDMI_CABLE,
-				       &in_args, sizeof(in_args), &out_data);
-
-	if (ACPI_SUCCESS(status)) {
-		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", status);
-	return sysfs_emit(buf, "unconnected connected [unknown]\n");
-}
-
-static ssize_t show_hdmi_source(struct device *dev,
-				struct device_attribute *attr, char *buf)
-{
-	struct alienfx_platdata *pdata;
-	acpi_status status;
-	u32 out_data;
-	struct wmax_basic_args in_args = {
-		.arg = 0,
-	};
-
-	pdata = dev_get_platdata(dev);
-
-	status = alienware_wmi_command(pdata->wdev, WMAX_METHOD_HDMI_STATUS,
-				       &in_args, sizeof(in_args), &out_data);
-
-	if (ACPI_SUCCESS(status)) {
-		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", status);
-	return sysfs_emit(buf, "input gpu [unknown]\n");
-}
-
-static ssize_t toggle_hdmi_source(struct device *dev,
-				  struct device_attribute *attr,
-				  const char *buf, size_t count)
-{
-	struct alienfx_platdata *pdata;
-	acpi_status status;
-	struct wmax_basic_args args;
-
-	pdata = dev_get_platdata(dev);
-
-	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);
-
-	status = alienware_wmi_command(pdata->wdev, WMAX_METHOD_HDMI_SOURCE,
-				       &args, sizeof(args), NULL);
-
-	if (ACPI_FAILURE(status))
-		pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
-		       status);
-	return count;
-}
-
-static DEVICE_ATTR(cable, S_IRUGO, show_hdmi_cable, NULL);
-static DEVICE_ATTR(source, S_IRUGO | S_IWUSR, show_hdmi_source,
-		   toggle_hdmi_source);
-
-static bool hdmi_group_visible(struct kobject *kobj)
-{
-	struct device *dev;
-	struct alienfx_platdata *pdata;
-
-	dev = container_of(kobj, struct device, kobj);
-	pdata = dev_get_platdata(dev);
-
-	return pdata->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 show_amplifier_status(struct device *dev,
-				     struct device_attribute *attr, char *buf)
-{
-	struct alienfx_platdata *pdata;
-	acpi_status status;
-	u32 out_data;
-	struct wmax_basic_args in_args = {
-		.arg = 0,
-	};
-
-	pdata = dev_get_platdata(dev);
-
-	status = alienware_wmi_command(pdata->wdev, WMAX_METHOD_AMPLIFIER_CABLE,
-				       &in_args, sizeof(in_args), &out_data);
-	if (ACPI_SUCCESS(status)) {
-		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", status);
-	return sysfs_emit(buf, "unconnected connected [unknown]\n");
-}
-
-static DEVICE_ATTR(status, S_IRUGO, show_amplifier_status, NULL);
-
-static bool amplifier_group_visible(struct kobject *kobj)
-{
-	struct device *dev;
-	struct alienfx_platdata *pdata;
-
-	dev = container_of(kobj, struct device, kobj);
-	pdata = dev_get_platdata(dev);
-
-	return pdata->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 show_deepsleep_status(struct device *dev,
-				     struct device_attribute *attr, char *buf)
-{
-	struct alienfx_platdata *pdata;
-	acpi_status status;
-	u32 out_data;
-	struct wmax_basic_args in_args = {
-		.arg = 0,
-	};
-
-	pdata = dev_get_platdata(dev);
-
-	status = alienware_wmi_command(pdata->wdev, WMAX_METHOD_DEEP_SLEEP_STATUS,
-				       &in_args, sizeof(in_args), &out_data);
-	if (ACPI_SUCCESS(status)) {
-		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", status);
-	return sysfs_emit(buf, "disabled s5 s5_s4 [unknown]\n");
-}
-
-static ssize_t toggle_deepsleep(struct device *dev,
-				struct device_attribute *attr,
-				const char *buf, size_t count)
-{
-	struct alienfx_platdata *pdata;
-	acpi_status status;
-	struct wmax_basic_args args;
-
-	pdata = dev_get_platdata(dev);
-
-	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);
-
-	status = alienware_wmi_command(pdata->wdev, WMAX_METHOD_DEEP_SLEEP_CONTROL,
-				       &args, sizeof(args), NULL);
-
-	if (ACPI_FAILURE(status))
-		pr_err("alienware-wmi: deep sleep control failed: results: %u\n",
-			status);
-	return count;
-}
-
-static DEVICE_ATTR(deepsleep, S_IRUGO | S_IWUSR, show_deepsleep_status, toggle_deepsleep);
-
-static bool deepsleep_group_visible(struct kobject *kobj)
-{
-	struct device *dev;
-	struct alienfx_platdata *pdata;
-
-	dev = container_of(kobj, struct device, kobj);
-	pdata = dev_get_platdata(dev);
-
-	return pdata->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)
-{
-	acpi_status status;
-	struct wmax_u32_args in_args = {
-		.operation = operation,
-		.arg1 = arg,
-		.arg2 = 0,
-		.arg3 = 0,
-	};
-
-	status = alienware_wmi_command(wdev, WMAX_METHOD_THERMAL_INFORMATION,
-				       &in_args, sizeof(in_args), out_data);
-
-	if (ACPI_FAILURE(status))
-		return -EIO;
-
-	if (*out_data == WMAX_FAILURE_CODE)
-		return -EBADRQC;
-
-	return 0;
-}
-
-static int wmax_thermal_control(struct wmi_device *wdev, u8 profile)
-{
-	acpi_status status;
-	struct wmax_u32_args in_args = {
-		.operation = WMAX_OPERATION_ACTIVATE_PROFILE,
-		.arg1 = profile,
-		.arg2 = 0,
-		.arg3 = 0,
-	};
-	u32 out_data;
-
-	status = alienware_wmi_command(wdev, WMAX_METHOD_THERMAL_CONTROL,
-				       &in_args, sizeof(in_args), &out_data);
-
-	if (ACPI_FAILURE(status))
-		return -EIO;
-
-	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)
-{
-	acpi_status status;
-	struct wmax_u32_args in_args = {
-		.operation = operation,
-		.arg1 = 0,
-		.arg2 = 0,
-		.arg3 = 0,
-	};
-
-	status = alienware_wmi_command(wdev, WMAX_METHOD_GAME_SHIFT_STATUS,
-				       &in_args, sizeof(in_args), out_data);
-
-	if (ACPI_FAILURE(status))
-		return -EIO;
-
-	if (*out_data == WMAX_FAILURE_CODE)
-		return -EOPNOTSUPP;
-
-	return 0;
-}
-
-static int thermal_profile_get(struct platform_profile_handler *pprof,
-			       enum platform_profile_option *profile)
-{
-	struct awcc_priv *priv;
-	u32 out_data;
-	int ret;
-
-	priv = container_of(pprof, struct awcc_priv, pp_handler);
-
-	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 platform_profile_handler *pprof,
-			       enum platform_profile_option profile)
-{
-	struct awcc_priv *priv;
-
-	priv = container_of(pprof, struct awcc_priv, pp_handler);
-
-	if (priv->has_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]);
-}
-
-int create_thermal_profile(struct wmi_device *wdev, bool has_gmode)
-{
-	struct awcc_priv *priv;
-	u32 out_data;
-	u8 sys_desc[4];
-	u32 first_mode;
-	enum wmax_thermal_mode mode;
-	enum platform_profile_option profile;
-	int ret;
-
-	priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
-	dev_set_drvdata(&wdev->dev, priv);
-
-	priv->wdev = wdev;
-
-	ret = wmax_thermal_information(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(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, priv->pp_handler.choices);
-	}
-
-	if (bitmap_empty(priv->pp_handler.choices, PLATFORM_PROFILE_LAST))
-		return -ENODEV;
-
-	if (has_gmode) {
-		priv->has_gmode = true;
-		priv->supported_thermal_profiles[PLATFORM_PROFILE_PERFORMANCE] =
-			WMAX_THERMAL_MODE_GMODE;
-
-		set_bit(PLATFORM_PROFILE_PERFORMANCE, priv->pp_handler.choices);
-	}
-
-	priv->pp_handler.profile_get = thermal_profile_get;
-	priv->pp_handler.profile_set = thermal_profile_set;
-
-	return platform_profile_register(&priv->pp_handler);
-}
-
-void remove_thermal_profile(void)
-{
-	platform_profile_remove();
-}
-
-/*
- * Platform Driver
- */
-static int alienfx_probe(struct platform_device *pdev)
-{
-	struct alienfx_priv *priv;
-	struct led_classdev *leds;
-
-	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
-	platform_set_drvdata(pdev, priv);
-
-	priv->pdev = pdev;
-
-	if (interface == WMAX)
-		priv->lighting_control_state = WMAX_RUNNING;
-	else if (interface == LEGACY)
-		priv->lighting_control_state = LEGACY_RUNNING;
-
-	leds = &priv->global_led;
-	leds->name = "alienware::global_brightness";
-	leds->brightness_set = global_led_set;
-	leds->brightness_get = global_led_get;
-	leds->max_brightness = 0x0F;
-
-	priv->global_brightness = priv->global_led.max_brightness;
-
-	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,
-};
-
-int alienfx_wmi_init(struct alienfx_platdata *pdata)
-{
-	struct platform_device *pdev;
-
-	pdev = platform_create_bundle(&platform_driver, alienfx_probe, NULL, 0,
-				      pdata, sizeof(*pdata));
-
-	dev_set_drvdata(&pdata->wdev->dev, pdev);
-
-	return PTR_ERR_OR_ZERO(pdev);
-}
-
-void alienfx_wmi_exit(struct wmi_device *wdev)
-{
-	struct platform_device *pdev;
-
-	pdev = dev_get_drvdata(&wdev->dev);
-
-	platform_device_unregister(pdev);
-	platform_driver_unregister(&platform_driver);
-}
-
 /*
  * Legacy WMI device
  */
-- 
2.47.1


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ