All of lore.kernel.org
 help / color / mirror / Atom feed
From: Kurt Borja <kuurtb@gmail.com>
To: kuurtb@gmail.com
Cc: Dell.Client.Kernel@dell.com, hdegoede@redhat.com,
	ilpo.jarvinen@linux.intel.com, linux-kernel@vger.kernel.org,
	mario.limonciello@amd.com, platform-driver-x86@vger.kernel.org,
	w_armin@gmx.de
Subject: [RFC PATCH 20/21] platform-x86: Split the alienware-wmi module
Date: Wed,  4 Dec 2024 21:47:31 -0300	[thread overview]
Message-ID: <20241205004730.2187107-2-kuurtb@gmail.com> (raw)
In-Reply-To: <20241205002733.2183537-3-kuurtb@gmail.com>

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@gmail.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@dell.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@gmail.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@outlook.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


  parent reply	other threads:[~2024-12-05  0:47 UTC|newest]

Thread overview: 41+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-12-05  0:27 [RFC PATCH 00/21] alienware-wmi driver rework Kurt Borja
2024-12-05  0:38 ` [RFC PATCH 01/21] alienware-wmi: Modify parse_rgb() signature Kurt Borja
2024-12-05  0:38 ` [RFC PATCH 02/21] alienware-wmi: Move Lighting Control State Kurt Borja
2024-12-05  0:39 ` [RFC PATCH 03/21] alienware-wmi: Remove unnecessary check at module exit Kurt Borja
2024-12-05  0:39 ` [RFC PATCH 04/21] alienware-wmi: Improve sysfs groups creation Kurt Borja
2024-12-05  0:40 ` [RFC PATCH 05/21] alienware-wmi: Refactor rgb-zones sysfs group creation Kurt Borja
2024-12-05 10:17   ` Ilpo Järvinen
2024-12-05 12:48     ` Kurt Borja
2024-12-05 13:18       ` Ilpo Järvinen
2024-12-05 13:34         ` Kurt Borja
2024-12-05  0:40 ` [RFC PATCH 06/21] alienware-wmi: Add state container and alienfx_probe() Kurt Borja
2024-12-05  0:40 ` [RFC PATCH 07/21] alienware-wmi: Migrate to state container pattern Kurt Borja
2024-12-05  0:41 ` [RFC PATCH 08/21] alienware-wmi: Add WMI Drivers Kurt Borja
2024-12-05  0:41 ` [RFC PATCH 09/21] alienware-wmi: Initialize WMI drivers Kurt Borja
2024-12-05  0:42 ` [RFC PATCH 10/21] alienware-wmi: Add alienfx OPs to platdata Kurt Borja
2024-12-05 11:05   ` Ilpo Järvinen
2024-12-05 12:50     ` Kurt Borja
2024-12-05  0:43 ` [RFC PATCH 11/21] alienware-wmi: Refactor LED control methods Kurt Borja
2024-12-05  0:43 ` [RFC PATCH 12/21] alienware-wmi: Refactor hdmi, amplifier, deepslp Kurt Borja
2024-12-05  0:44 ` [RFC PATCH 13/21] alienware-wmi: Add a state container for AWCC Kurt Borja
2024-12-05  0:44 ` [RFC PATCH 14/21] alienware-wmi: Migrate thermal methods to wmidev Kurt Borja
2024-12-05  0:44 ` [RFC PATCH 15/21] alienware-wmi: Refactor sysfs visibility methods Kurt Borja
2024-12-05  0:45 ` [RFC PATCH 16/21] alienware-wmi: Make running control state part of platdata Kurt Borja
2024-12-05 11:32   ` Ilpo Järvinen
2024-12-05 13:10     ` Kurt Borja
2024-12-05 14:06       ` Ilpo Järvinen
2024-12-07  2:10         ` Kurt Borja
2024-12-05  0:46 ` [RFC PATCH 17/21] alienware-wmi: Drop thermal methods dependency on quirks Kurt Borja
2024-12-05 11:14   ` Ilpo Järvinen
2024-12-05 12:56     ` Kurt Borja
2024-12-05  0:46 ` [RFC PATCH 18/21] platform-x86: Add header file for alienware-wmi Kurt Borja
2024-12-05  7:49   ` kernel test robot
2024-12-05  0:47 ` [RFC PATCH 19/21] platform-x86: Rename alienare-wmi Kurt Borja
2024-12-05 11:16   ` Ilpo Järvinen
2024-12-05 12:57     ` Kurt Borja
2024-12-05  0:47 ` Kurt Borja [this message]
2024-12-05  0:48 ` [RFC PATCH 21/21] platform-x86: Add config entries to alienware-wmi Kurt Borja
2024-12-06 23:26 ` [RFC PATCH 00/21] alienware-wmi driver rework Armin Wolf
2024-12-07  1:59   ` Kurt Borja
2024-12-07  3:20     ` Armin Wolf
2024-12-07  3:47       ` Kurt Borja

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20241205004730.2187107-2-kuurtb@gmail.com \
    --to=kuurtb@gmail.com \
    --cc=Dell.Client.Kernel@dell.com \
    --cc=hdegoede@redhat.com \
    --cc=ilpo.jarvinen@linux.intel.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mario.limonciello@amd.com \
    --cc=platform-driver-x86@vger.kernel.org \
    --cc=w_armin@gmx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.