[PATCH] Dell AWCC platform_profile support

Kurt Borja posted 1 patch 1 month, 3 weeks ago
There is a newer version of this series
drivers/platform/x86/dell/Kconfig         |   9 +
drivers/platform/x86/dell/Makefile        |   1 +
drivers/platform/x86/dell/dell-wmi-awcc.c | 204 ++++++++++++++++++++++
3 files changed, 214 insertions(+)
create mode 100644 drivers/platform/x86/dell/dell-wmi-awcc.c
[PATCH] Dell AWCC platform_profile support
Posted by Kurt Borja 1 month, 3 weeks ago
This patch adds platform_profile support for Dell devices which implement
User Selectable Thermal Tables (USTT) that are meant to be controlled by
Alienware Command Center (AWCC). These devices may include newer Alienware
M-Series, Alienware X-Series and Dell's G-Series. This patch, was tested
by me on an Alienware x15 R1.

It is suspected that Alienware Command Center manages thermal profiles
through the WMI interface, specifically through a device with identifier
\_SB_.AMW1.WMAX. This device was reverse engineered and the relevant
functionality is documented here [1]. This driver interacts with this
WMI device and thus is able to mimic AWCC's thermal profiles functionality
through the platform_profile API. In consequence the user would be able
to set and retrieve thermal profiles, which are just fan speed profiles.

This driver was heavily inspired on inspur_platform_profile, special
thanks.

Notes:
 - Performance (FullSpeed) profile is a special profile which has it's own
   entry in the Firmware Settings of the Alienware x15 R1. It also changes
   the color of the F1 key. I suspect this behavior would be replicated in
   other X-Series or M-Series laptops.
 - G-Mode is a profile documented on [1] which mimics the behavior of
   FullSpeed mode but it does not have an entry on the Firmware Settings of
   the Alienware x15 R1, this may correspond to the G-Mode functionality on
   G-Series laptops (activated by a special button) but I cannot test it. I
   did not include this code in the driver as G-Mode causes unexpected
   behavior on X-Series laptops.

Thanks for your time and patiente in advance.

Regards,

Kurt

[1] https://gist.github.com/kuu-rt/b22328ff2b454be505387e2a38c61ee4

Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
 drivers/platform/x86/dell/Kconfig         |   9 +
 drivers/platform/x86/dell/Makefile        |   1 +
 drivers/platform/x86/dell/dell-wmi-awcc.c | 204 ++++++++++++++++++++++
 3 files changed, 214 insertions(+)
 create mode 100644 drivers/platform/x86/dell/dell-wmi-awcc.c

diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig
index 68a49788a..20300ff98 100644
--- a/drivers/platform/x86/dell/Kconfig
+++ b/drivers/platform/x86/dell/Kconfig
@@ -27,6 +27,15 @@ config ALIENWARE_WMI
 	 zones on Alienware machines that don't contain a dedicated AlienFX
 	 USB MCU such as the X51 and X51-R2.
 
+config AWCC_PLATFORM_PROFILE
+	tristate "AWCC Platform Profile support"
+	depends on ACPI_WMI
+	select ACPI_PLATFORM_PROFILE
+	help
+	 This driver provides platform_profile support for selecting thermal
+	 profiles on Dell devices with User Selectable Thermal Tables,
+	 controlled by AWCC's WMI interface.
+
 config DCDBAS
 	tristate "Dell Systems Management Base Driver"
 	default m
diff --git a/drivers/platform/x86/dell/Makefile b/drivers/platform/x86/dell/Makefile
index 79d60f1bf..bfef99580 100644
--- a/drivers/platform/x86/dell/Makefile
+++ b/drivers/platform/x86/dell/Makefile
@@ -23,4 +23,5 @@ obj-$(CONFIG_DELL_WMI_AIO)		+= dell-wmi-aio.o
 obj-$(CONFIG_DELL_WMI_DESCRIPTOR)	+= dell-wmi-descriptor.o
 obj-$(CONFIG_DELL_WMI_DDV)		+= dell-wmi-ddv.o
 obj-$(CONFIG_DELL_WMI_LED)		+= dell-wmi-led.o
+obj-$(CONFIG_AWCC_PLATFORM_PROFILE)	+= dell-wmi-awcc.o
 obj-$(CONFIG_DELL_WMI_SYSMAN)		+= dell-wmi-sysman/
diff --git a/drivers/platform/x86/dell/dell-wmi-awcc.c b/drivers/platform/x86/dell/dell-wmi-awcc.c
new file mode 100644
index 000000000..0837d1bc6
--- /dev/null
+++ b/drivers/platform/x86/dell/dell-wmi-awcc.c
@@ -0,0 +1,204 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *  WMI driver for Dell's AWCC platform_profile
+ *
+ *  Copyright (c) Kurt Borja <kuurtb@gmail.com>
+ *
+ */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/platform_profile.h>
+#include <linux/wmi.h>
+
+#define PROF_TO_ARG(mode) ((mode << 8) | 1)
+
+#define DELL_AWCC_GUID "A70591CE-A997-11DA-B012-B622A1EF5492"
+
+enum awcc_wmi_method {
+	AWCC_WMI_THERMAL_INFORMATION = 0x14,
+	AWCC_WMI_THERMAL_CONTROL = 0x15,
+};
+
+enum awcc_tmp_profile {
+	AWCC_TMP_PROFILE_BALANCED = 0xA0,
+	AWCC_TMP_PROFILE_BALANCED_PERFORMANCE = 0xA1,
+	AWCC_TMP_PROFILE_COOL = 0xA2,
+	AWCC_TMP_PROFILE_QUIET = 0xA3,
+	AWCC_TMP_PROFILE_PERFORMANCE = 0xA4,
+	AWCC_TMP_PROFILE_LOW_POWER = 0xA5,
+};
+
+struct awcc_wmi_priv {
+	struct wmi_device *wdev;
+	struct platform_profile_handler handler;
+};
+
+static int awcc_wmi_query(struct wmi_device *wdev, enum awcc_wmi_method method,
+			  u32 arg, u32 *res)
+{
+	struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
+	const struct acpi_buffer in = { sizeof(arg), &arg };
+	union acpi_object *obj;
+	acpi_status status;
+	int ret = 0;
+
+	status = wmidev_evaluate_method(wdev, 0x0, method, &in, &out);
+
+	if (ACPI_FAILURE(status))
+		return -EIO;
+
+	obj = out.pointer;
+	if (!obj)
+		return -ENODATA;
+
+	if (obj->type != ACPI_TYPE_INTEGER) {
+		ret = -EINVAL;
+		goto out_free;
+	}
+
+	if (obj->integer.value <= U32_MAX)
+		*res = (u32)obj->integer.value;
+	else
+		ret = -ERANGE;
+
+out_free:
+	kfree(obj);
+
+	return ret;
+}
+
+static int awcc_platform_profile_get(struct platform_profile_handler *pprof,
+				     enum platform_profile_option *profile)
+{
+	struct awcc_wmi_priv *priv =
+		container_of(pprof, struct awcc_wmi_priv, handler);
+
+	u32 res;
+	int ret;
+
+	ret = awcc_wmi_query(priv->wdev, AWCC_WMI_THERMAL_INFORMATION, 0x0B,
+			     &res);
+
+	if (ret < 0)
+		return ret;
+
+	if (res < 0)
+		return -EBADRQC;
+
+	switch (res) {
+	case AWCC_TMP_PROFILE_LOW_POWER:
+		*profile = PLATFORM_PROFILE_LOW_POWER;
+		break;
+	case AWCC_TMP_PROFILE_QUIET:
+		*profile = PLATFORM_PROFILE_QUIET;
+		break;
+	case AWCC_TMP_PROFILE_BALANCED:
+		*profile = PLATFORM_PROFILE_BALANCED;
+		break;
+	case AWCC_TMP_PROFILE_BALANCED_PERFORMANCE:
+		*profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE;
+		break;
+	case AWCC_TMP_PROFILE_PERFORMANCE:
+		*profile = PLATFORM_PROFILE_PERFORMANCE;
+		break;
+	default:
+		return -ENODATA;
+	}
+
+	return 0;
+}
+
+static int awcc_platform_profile_set(struct platform_profile_handler *pprof,
+				     enum platform_profile_option profile)
+{
+	struct awcc_wmi_priv *priv =
+		container_of(pprof, struct awcc_wmi_priv, handler);
+
+	u32 arg;
+	u32 res;
+	int ret;
+
+	switch (profile) {
+	case PLATFORM_PROFILE_LOW_POWER:
+		arg = PROF_TO_ARG(AWCC_TMP_PROFILE_LOW_POWER);
+		break;
+	case PLATFORM_PROFILE_QUIET:
+		arg = PROF_TO_ARG(AWCC_TMP_PROFILE_QUIET);
+		break;
+	case PLATFORM_PROFILE_BALANCED:
+		arg = PROF_TO_ARG(AWCC_TMP_PROFILE_BALANCED);
+		break;
+	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
+		arg = PROF_TO_ARG(AWCC_TMP_PROFILE_BALANCED_PERFORMANCE);
+		break;
+	case PLATFORM_PROFILE_PERFORMANCE:
+		arg = PROF_TO_ARG(AWCC_TMP_PROFILE_PERFORMANCE);
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	ret = awcc_wmi_query(priv->wdev, AWCC_WMI_THERMAL_CONTROL, arg, &res);
+
+	if (ret < 0)
+		return ret;
+
+	if (res < 0)
+		return -EBADRQC;
+
+	return 0;
+}
+
+static int awcc_wmi_probe(struct wmi_device *wdev, const void *context)
+{
+	struct awcc_wmi_priv *priv;
+
+	priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->wdev = wdev;
+	dev_set_drvdata(&wdev->dev, priv);
+
+	priv->handler.profile_set = awcc_platform_profile_set;
+	priv->handler.profile_get = awcc_platform_profile_get;
+
+	set_bit(PLATFORM_PROFILE_LOW_POWER, priv->handler.choices);
+	set_bit(PLATFORM_PROFILE_QUIET, priv->handler.choices);
+	set_bit(PLATFORM_PROFILE_BALANCED, priv->handler.choices);
+	set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, priv->handler.choices);
+	set_bit(PLATFORM_PROFILE_PERFORMANCE, priv->handler.choices);
+
+	return platform_profile_register(&priv->handler);
+}
+
+static void awcc_wmi_remove(struct wmi_device *wdev)
+{
+	platform_profile_remove();
+}
+
+static const struct wmi_device_id awcc_wmi_id_table[] = {
+	{ .guid_string = DELL_AWCC_GUID },
+	{},
+};
+
+MODULE_DEVICE_TABLE(wmi, awcc_wmi_id_table);
+
+static struct wmi_driver awcc_wmi_driver = {
+	.driver = {
+		.name = "dell-wmi-awcc-platform-profile",
+		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
+	},
+	.id_table = awcc_wmi_id_table,
+	.probe = awcc_wmi_probe,
+	.remove = awcc_wmi_remove,
+	.no_singleton = true,
+};
+
+module_wmi_driver(awcc_wmi_driver);
+
+MODULE_AUTHOR("Kurt Borja");
+MODULE_DESCRIPTION("Dell AWCC WMI driver");
+MODULE_LICENSE("GPL");
-- 
2.46.2
[PATCH 0/4] Dell AWCC platform_profile support
Posted by Kurt Borja 1 month, 2 weeks ago
This patch adds platform_profile support for Dell devices which implement
User Selectable Thermal Tables (USTT) that are meant to be controlled by
Alienware Command Center (AWCC). These devices may include newer Alienware
M-Series, Alienware X-Series and Dell's G-Series. This patch, was tested
by me on an Alienware x15 R1.

---
v4:
 - Fixed indentation on previous code
 - Removed unnecessary (acpi_size) and (u32 *) casts
 - Return -EIO on ACPI_FAILURE
 - Appropiate prefixes given to macros
 - 0xFFFFFFFF named WMAX_FAILURE_CODE
 - Added support for a new set of thermal codes. Old ones now have USTT
   in their names
 - A new quirk has been added to differantiate between the two sets.
   thermal and thermal_ustt are mutually exclusive
 - Added documentation for WMAX interface
v3:
 - Removed extra empty line
 - 0x0B named WMAX_ARG_GET_CURRENT_PROF
 - Removed casts to the same type on functions added in this patch
 - Thermal profile to WMAX argument is now an static function and makes
   use of in-built kernel macros
 - Platform profile is now removed only if it was created first
 - create_platform_profile is now create_thermal_profile to avoid
   confusion
 - profile_get and profile_set functions renamed too to match the above
v2:
 - Moved functionality to alienware-wmi driver
 - Added thermal and gmode quirks to add support based on dmi match
 - Performance profile is now GMODE for devices that support it
 - alienware_wmax_command now is insize agnostic to support new thermal
   methods

Kurt Borja (4):
  alienware-wmi: fixed indentation and clean up
  alienware-wmi: alienware_wmax_command() is now input size agnostic
  alienware-wmi: added platform profile support
  alienware-wmi: WMAX interface documentation

 Documentation/wmi/devices/alienware-wmi.rst | 364 ++++++++++++++++++
 drivers/platform/x86/dell/Kconfig           |   1 +
 drivers/platform/x86/dell/alienware-wmi.c   | 389 ++++++++++++++++----
 3 files changed, 678 insertions(+), 76 deletions(-)
 create mode 100644 Documentation/wmi/devices/alienware-wmi.rst

-- 
2.47.0
[PATCH v4 0/4] Dell AWCC platform_profile support
Posted by Kurt Borja 1 month, 2 weeks ago
This patch adds platform_profile support for Dell devices which implement
User Selectable Thermal Tables (USTT) that are meant to be controlled by
Alienware Command Center (AWCC). These devices may include newer Alienware
M-Series, Alienware X-Series and Dell's G-Series. This patch, was tested
by me on an Alienware x15 R1.

---
v4:
 - Fixed indentation on previous code
 - Removed unnecessary (acpi_size) and (u32 *) casts
 - Return -EIO on ACPI_FAILURE
 - Appropiate prefixes given to macros
 - 0xFFFFFFFF named WMAX_FAILURE_CODE
 - Added support for a new set of thermal codes. Old ones now have USTT
   in their names
 - A new quirk has been added to differantiate between the two sets.
   thermal and thermal_ustt are mutually exclusive
 - Added documentation for WMAX interface
v3:
 - Removed extra empty line
 - 0x0B named WMAX_ARG_GET_CURRENT_PROF
 - Removed casts to the same type on functions added in this patch
 - Thermal profile to WMAX argument is now an static function and makes
   use of in-built kernel macros
 - Platform profile is now removed only if it was created first
 - create_platform_profile is now create_thermal_profile to avoid
   confusion
 - profile_get and profile_set functions renamed too to match the above
v2:
 - Moved functionality to alienware-wmi driver
 - Added thermal and gmode quirks to add support based on dmi match
 - Performance profile is now GMODE for devices that support it
 - alienware_wmax_command now is insize agnostic to support new thermal
   methods

Kurt Borja (4):
  alienware-wmi: fixed indentation and clean up
  alienware-wmi: alienware_wmax_command() is now input size agnostic
  alienware-wmi: added platform profile support
  alienware-wmi: WMAX interface documentation

 Documentation/wmi/devices/alienware-wmi.rst | 364 ++++++++++++++++++
 drivers/platform/x86/dell/Kconfig           |   1 +
 drivers/platform/x86/dell/alienware-wmi.c   | 389 ++++++++++++++++----
 3 files changed, 678 insertions(+), 76 deletions(-)
 create mode 100644 Documentation/wmi/devices/alienware-wmi.rst

-- 
2.47.0
[PATCH v4 1/4] alienware-wmi: fixed indentation and clean up
Posted by Kurt Borja 1 month, 2 weeks ago
Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
 drivers/platform/x86/dell/alienware-wmi.c | 134 +++++++++++-----------
 1 file changed, 67 insertions(+), 67 deletions(-)

diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
index f5ee62ce1..16a3fe9ac 100644
--- a/drivers/platform/x86/dell/alienware-wmi.c
+++ b/drivers/platform/x86/dell/alienware-wmi.c
@@ -116,68 +116,68 @@ static int __init dmi_matched(const struct dmi_system_id *dmi)
 
 static const struct dmi_system_id alienware_quirks[] __initconst = {
 	{
-	 .callback = dmi_matched,
-	 .ident = "Alienware X51 R3",
-	 .matches = {
-		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
-		     DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"),
-		     },
-	 .driver_data = &quirk_x51_r3,
-	 },
+		.callback = dmi_matched,
+		.ident = "Alienware X51 R3",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"),
+		},
+		.driver_data = &quirk_x51_r3,
+	},
 	{
-	 .callback = dmi_matched,
-	 .ident = "Alienware X51 R2",
-	 .matches = {
-		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
-		     DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"),
-		     },
-	 .driver_data = &quirk_x51_r1_r2,
-	 },
+		.callback = dmi_matched,
+		.ident = "Alienware X51 R2",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"),
+		},
+		.driver_data = &quirk_x51_r1_r2,
+	},
 	{
-	 .callback = dmi_matched,
-	 .ident = "Alienware X51 R1",
-	 .matches = {
-		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
-		     DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"),
-		     },
-	 .driver_data = &quirk_x51_r1_r2,
-	 },
+		.callback = dmi_matched,
+		.ident = "Alienware X51 R1",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"),
+		},
+		.driver_data = &quirk_x51_r1_r2,
+	},
 	{
-	 .callback = dmi_matched,
-	 .ident = "Alienware ASM100",
-	 .matches = {
-		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
-		     DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"),
-		     },
-	 .driver_data = &quirk_asm100,
-	 },
+		.callback = dmi_matched,
+		.ident = "Alienware ASM100",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"),
+		},
+		.driver_data = &quirk_asm100,
+	},
 	{
-	 .callback = dmi_matched,
-	 .ident = "Alienware ASM200",
-	 .matches = {
-		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
-		     DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"),
-		     },
-	 .driver_data = &quirk_asm200,
-	 },
+		.callback = dmi_matched,
+		.ident = "Alienware ASM200",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"),
+		},
+		.driver_data = &quirk_asm200,
+	},
 	{
-	 .callback = dmi_matched,
-	 .ident = "Alienware ASM201",
-	 .matches = {
-		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
-		     DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"),
-		     },
-	 .driver_data = &quirk_asm201,
-	 },
-	 {
-	 .callback = dmi_matched,
-	 .ident = "Dell Inc. Inspiron 5675",
-	 .matches = {
-		     DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
-		     DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"),
-		     },
-	 .driver_data = &quirk_inspiron5675,
-	 },
+		.callback = dmi_matched,
+		.ident = "Alienware ASM201",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"),
+		},
+		.driver_data = &quirk_asm201,
+	},
+	{
+		.callback = dmi_matched,
+		.ident = "Dell Inc. Inspiron 5675",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"),
+		},
+		.driver_data = &quirk_inspiron5675,
+	},
 	{}
 };
 
@@ -221,8 +221,8 @@ static struct platform_zone *zone_data;
 
 static struct platform_driver platform_driver = {
 	.driver = {
-		   .name = "alienware-wmi",
-		   }
+		.name = "alienware-wmi",
+	}
 };
 
 static struct attribute_group zone_attribute_group = {
@@ -292,7 +292,7 @@ static int alienware_update_led(struct platform_zone *zone)
 		guid = WMAX_CONTROL_GUID;
 		method_id = WMAX_METHOD_ZONE_CONTROL;
 
-		input.length = (acpi_size) sizeof(wmax_basic_args);
+		input.length = sizeof(wmax_basic_args);
 		input.pointer = &wmax_basic_args;
 	} else {
 		legacy_args.colors = zone->colors;
@@ -306,7 +306,7 @@ static int alienware_update_led(struct platform_zone *zone)
 			guid = LEGACY_CONTROL_GUID;
 		method_id = zone->location + 1;
 
-		input.length = (acpi_size) sizeof(legacy_args);
+		input.length = sizeof(legacy_args);
 		input.pointer = &legacy_args;
 	}
 	pr_debug("alienware-wmi: guid %s method %d\n", guid, method_id);
@@ -358,7 +358,7 @@ static int wmax_brightness(int brightness)
 		.led_mask = 0xFF,
 		.percentage = brightness,
 	};
-	input.length = (acpi_size) sizeof(args);
+	input.length = sizeof(args);
 	input.pointer = &args;
 	status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0,
 				     WMAX_METHOD_BRIGHTNESS, &input, NULL);
@@ -508,7 +508,7 @@ static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
 	struct acpi_buffer input;
 	struct acpi_buffer output;
 
-	input.length = (acpi_size) sizeof(*in_args);
+	input.length = sizeof(*in_args);
 	input.pointer = in_args;
 	if (out_data) {
 		output.length = ACPI_ALLOCATE_BUFFER;
@@ -542,7 +542,7 @@ static ssize_t show_hdmi_cable(struct device *dev,
 	};
 	status =
 	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE,
-				   (u32 *) &out_data);
+				   &out_data);
 	if (ACPI_SUCCESS(status)) {
 		if (out_data == 0)
 			return sysfs_emit(buf, "[unconnected] connected unknown\n");
@@ -563,7 +563,7 @@ static ssize_t show_hdmi_source(struct device *dev,
 	};
 	status =
 	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS,
-				   (u32 *) &out_data);
+				   &out_data);
 
 	if (ACPI_SUCCESS(status)) {
 		if (out_data == 1)
@@ -643,7 +643,7 @@ static ssize_t show_amplifier_status(struct device *dev,
 	};
 	status =
 	    alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE,
-				   (u32 *) &out_data);
+				   &out_data);
 	if (ACPI_SUCCESS(status)) {
 		if (out_data == 0)
 			return sysfs_emit(buf, "[unconnected] connected unknown\n");
@@ -695,7 +695,7 @@ static ssize_t show_deepsleep_status(struct device *dev,
 		.arg = 0,
 	};
 	status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS,
-					(u32 *) &out_data);
+					&out_data);
 	if (ACPI_SUCCESS(status)) {
 		if (out_data == 0)
 			return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
-- 
2.47.0
Re: [PATCH v4 1/4] alienware-wmi: fixed indentation and clean up
Posted by Ilpo Järvinen 1 month, 2 weeks ago
On Fri, 11 Oct 2024, Kurt Borja wrote:

All patches should provide a description of the change in the commit 
message body here (the shortlog in the subject is not enough). So please 
resubmit with the descriptions added to all patches.

The code changes themselves looked very good, thanks for doing this.

--
 i.

> Signed-off-by: Kurt Borja <kuurtb@gmail.com>
> ---
>  drivers/platform/x86/dell/alienware-wmi.c | 134 +++++++++++-----------
>  1 file changed, 67 insertions(+), 67 deletions(-)
> 
> diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
> index f5ee62ce1..16a3fe9ac 100644
> --- a/drivers/platform/x86/dell/alienware-wmi.c
> +++ b/drivers/platform/x86/dell/alienware-wmi.c
> @@ -116,68 +116,68 @@ static int __init dmi_matched(const struct dmi_system_id *dmi)
>  
>  static const struct dmi_system_id alienware_quirks[] __initconst = {
>  	{
> -	 .callback = dmi_matched,
> -	 .ident = "Alienware X51 R3",
> -	 .matches = {
> -		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> -		     DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"),
> -		     },
> -	 .driver_data = &quirk_x51_r3,
> -	 },
> +		.callback = dmi_matched,
> +		.ident = "Alienware X51 R3",
> +		.matches = {
> +			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> +			DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"),
> +		},
> +		.driver_data = &quirk_x51_r3,
> +	},
>  	{
> -	 .callback = dmi_matched,
> -	 .ident = "Alienware X51 R2",
> -	 .matches = {
> -		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> -		     DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"),
> -		     },
> -	 .driver_data = &quirk_x51_r1_r2,
> -	 },
> +		.callback = dmi_matched,
> +		.ident = "Alienware X51 R2",
> +		.matches = {
> +			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> +			DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"),
> +		},
> +		.driver_data = &quirk_x51_r1_r2,
> +	},
>  	{
> -	 .callback = dmi_matched,
> -	 .ident = "Alienware X51 R1",
> -	 .matches = {
> -		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> -		     DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"),
> -		     },
> -	 .driver_data = &quirk_x51_r1_r2,
> -	 },
> +		.callback = dmi_matched,
> +		.ident = "Alienware X51 R1",
> +		.matches = {
> +			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> +			DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"),
> +		},
> +		.driver_data = &quirk_x51_r1_r2,
> +	},
>  	{
> -	 .callback = dmi_matched,
> -	 .ident = "Alienware ASM100",
> -	 .matches = {
> -		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> -		     DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"),
> -		     },
> -	 .driver_data = &quirk_asm100,
> -	 },
> +		.callback = dmi_matched,
> +		.ident = "Alienware ASM100",
> +		.matches = {
> +			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> +			DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"),
> +		},
> +		.driver_data = &quirk_asm100,
> +	},
>  	{
> -	 .callback = dmi_matched,
> -	 .ident = "Alienware ASM200",
> -	 .matches = {
> -		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> -		     DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"),
> -		     },
> -	 .driver_data = &quirk_asm200,
> -	 },
> +		.callback = dmi_matched,
> +		.ident = "Alienware ASM200",
> +		.matches = {
> +			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> +			DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"),
> +		},
> +		.driver_data = &quirk_asm200,
> +	},
>  	{
> -	 .callback = dmi_matched,
> -	 .ident = "Alienware ASM201",
> -	 .matches = {
> -		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> -		     DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"),
> -		     },
> -	 .driver_data = &quirk_asm201,
> -	 },
> -	 {
> -	 .callback = dmi_matched,
> -	 .ident = "Dell Inc. Inspiron 5675",
> -	 .matches = {
> -		     DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
> -		     DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"),
> -		     },
> -	 .driver_data = &quirk_inspiron5675,
> -	 },
> +		.callback = dmi_matched,
> +		.ident = "Alienware ASM201",
> +		.matches = {
> +			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> +			DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"),
> +		},
> +		.driver_data = &quirk_asm201,
> +	},
> +	{
> +		.callback = dmi_matched,
> +		.ident = "Dell Inc. Inspiron 5675",
> +		.matches = {
> +			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
> +			DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"),
> +		},
> +		.driver_data = &quirk_inspiron5675,
> +	},
>  	{}
>  };
>  
> @@ -221,8 +221,8 @@ static struct platform_zone *zone_data;
>  
>  static struct platform_driver platform_driver = {
>  	.driver = {
> -		   .name = "alienware-wmi",
> -		   }
> +		.name = "alienware-wmi",
> +	}
>  };
>  
>  static struct attribute_group zone_attribute_group = {
> @@ -292,7 +292,7 @@ static int alienware_update_led(struct platform_zone *zone)
>  		guid = WMAX_CONTROL_GUID;
>  		method_id = WMAX_METHOD_ZONE_CONTROL;
>  
> -		input.length = (acpi_size) sizeof(wmax_basic_args);
> +		input.length = sizeof(wmax_basic_args);
>  		input.pointer = &wmax_basic_args;
>  	} else {
>  		legacy_args.colors = zone->colors;
> @@ -306,7 +306,7 @@ static int alienware_update_led(struct platform_zone *zone)
>  			guid = LEGACY_CONTROL_GUID;
>  		method_id = zone->location + 1;
>  
> -		input.length = (acpi_size) sizeof(legacy_args);
> +		input.length = sizeof(legacy_args);
>  		input.pointer = &legacy_args;
>  	}
>  	pr_debug("alienware-wmi: guid %s method %d\n", guid, method_id);
> @@ -358,7 +358,7 @@ static int wmax_brightness(int brightness)
>  		.led_mask = 0xFF,
>  		.percentage = brightness,
>  	};
> -	input.length = (acpi_size) sizeof(args);
> +	input.length = sizeof(args);
>  	input.pointer = &args;
>  	status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0,
>  				     WMAX_METHOD_BRIGHTNESS, &input, NULL);
> @@ -508,7 +508,7 @@ static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
>  	struct acpi_buffer input;
>  	struct acpi_buffer output;
>  
> -	input.length = (acpi_size) sizeof(*in_args);
> +	input.length = sizeof(*in_args);
>  	input.pointer = in_args;
>  	if (out_data) {
>  		output.length = ACPI_ALLOCATE_BUFFER;
> @@ -542,7 +542,7 @@ static ssize_t show_hdmi_cable(struct device *dev,
>  	};
>  	status =
>  	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE,
> -				   (u32 *) &out_data);
> +				   &out_data);
>  	if (ACPI_SUCCESS(status)) {
>  		if (out_data == 0)
>  			return sysfs_emit(buf, "[unconnected] connected unknown\n");
> @@ -563,7 +563,7 @@ static ssize_t show_hdmi_source(struct device *dev,
>  	};
>  	status =
>  	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS,
> -				   (u32 *) &out_data);
> +				   &out_data);
>  
>  	if (ACPI_SUCCESS(status)) {
>  		if (out_data == 1)
> @@ -643,7 +643,7 @@ static ssize_t show_amplifier_status(struct device *dev,
>  	};
>  	status =
>  	    alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE,
> -				   (u32 *) &out_data);
> +				   &out_data);
>  	if (ACPI_SUCCESS(status)) {
>  		if (out_data == 0)
>  			return sysfs_emit(buf, "[unconnected] connected unknown\n");
> @@ -695,7 +695,7 @@ static ssize_t show_deepsleep_status(struct device *dev,
>  		.arg = 0,
>  	};
>  	status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS,
> -					(u32 *) &out_data);
> +					&out_data);
>  	if (ACPI_SUCCESS(status)) {
>  		if (out_data == 0)
>  			return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
>
Re: [PATCH v4 1/4] alienware-wmi: fixed indentation and clean up
Posted by Kurt Borja 1 month, 2 weeks ago
All changes done in v5.

Thank you for all your help.

> > Signed-off-by: Kurt Borja <kuurtb@gmail.com>
> > ---
> >  drivers/platform/x86/dell/alienware-wmi.c | 134 +++++++++++-----------
> >  1 file changed, 67 insertions(+), 67 deletions(-)
> > 
> > diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
> > index f5ee62ce1..16a3fe9ac 100644
> > --- a/drivers/platform/x86/dell/alienware-wmi.c
> > +++ b/drivers/platform/x86/dell/alienware-wmi.c
> > @@ -116,68 +116,68 @@ static int __init dmi_matched(const struct dmi_system_id *dmi)
> >  
> >  static const struct dmi_system_id alienware_quirks[] __initconst = {
> >  	{
> > -	 .callback = dmi_matched,
> > -	 .ident = "Alienware X51 R3",
> > -	 .matches = {
> > -		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> > -		     DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"),
> > -		     },
> > -	 .driver_data = &quirk_x51_r3,
> > -	 },
> > +		.callback = dmi_matched,
> > +		.ident = "Alienware X51 R3",
> > +		.matches = {
> > +			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> > +			DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"),
> > +		},
> > +		.driver_data = &quirk_x51_r3,
> > +	},
> >  	{
> > -	 .callback = dmi_matched,
> > -	 .ident = "Alienware X51 R2",
> > -	 .matches = {
> > -		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> > -		     DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"),
> > -		     },
> > -	 .driver_data = &quirk_x51_r1_r2,
> > -	 },
> > +		.callback = dmi_matched,
> > +		.ident = "Alienware X51 R2",
> > +		.matches = {
> > +			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> > +			DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"),
> > +		},
> > +		.driver_data = &quirk_x51_r1_r2,
> > +	},
> >  	{
> > -	 .callback = dmi_matched,
> > -	 .ident = "Alienware X51 R1",
> > -	 .matches = {
> > -		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> > -		     DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"),
> > -		     },
> > -	 .driver_data = &quirk_x51_r1_r2,
> > -	 },
> > +		.callback = dmi_matched,
> > +		.ident = "Alienware X51 R1",
> > +		.matches = {
> > +			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> > +			DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"),
> > +		},
> > +		.driver_data = &quirk_x51_r1_r2,
> > +	},
> >  	{
> > -	 .callback = dmi_matched,
> > -	 .ident = "Alienware ASM100",
> > -	 .matches = {
> > -		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> > -		     DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"),
> > -		     },
> > -	 .driver_data = &quirk_asm100,
> > -	 },
> > +		.callback = dmi_matched,
> > +		.ident = "Alienware ASM100",
> > +		.matches = {
> > +			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> > +			DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"),
> > +		},
> > +		.driver_data = &quirk_asm100,
> > +	},
> >  	{
> > -	 .callback = dmi_matched,
> > -	 .ident = "Alienware ASM200",
> > -	 .matches = {
> > -		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> > -		     DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"),
> > -		     },
> > -	 .driver_data = &quirk_asm200,
> > -	 },
> > +		.callback = dmi_matched,
> > +		.ident = "Alienware ASM200",
> > +		.matches = {
> > +			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> > +			DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"),
> > +		},
> > +		.driver_data = &quirk_asm200,
> > +	},
> >  	{
> > -	 .callback = dmi_matched,
> > -	 .ident = "Alienware ASM201",
> > -	 .matches = {
> > -		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> > -		     DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"),
> > -		     },
> > -	 .driver_data = &quirk_asm201,
> > -	 },
> > -	 {
> > -	 .callback = dmi_matched,
> > -	 .ident = "Dell Inc. Inspiron 5675",
> > -	 .matches = {
> > -		     DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
> > -		     DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"),
> > -		     },
> > -	 .driver_data = &quirk_inspiron5675,
> > -	 },
> > +		.callback = dmi_matched,
> > +		.ident = "Alienware ASM201",
> > +		.matches = {
> > +			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> > +			DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"),
> > +		},
> > +		.driver_data = &quirk_asm201,
> > +	},
> > +	{
> > +		.callback = dmi_matched,
> > +		.ident = "Dell Inc. Inspiron 5675",
> > +		.matches = {
> > +			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
> > +			DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"),
> > +		},
> > +		.driver_data = &quirk_inspiron5675,
> > +	},
> >  	{}
> >  };
> >  
> > @@ -221,8 +221,8 @@ static struct platform_zone *zone_data;
> >  
> >  static struct platform_driver platform_driver = {
> >  	.driver = {
> > -		   .name = "alienware-wmi",
> > -		   }
> > +		.name = "alienware-wmi",
> > +	}
> >  };
> >  
> >  static struct attribute_group zone_attribute_group = {
> > @@ -292,7 +292,7 @@ static int alienware_update_led(struct platform_zone *zone)
> >  		guid = WMAX_CONTROL_GUID;
> >  		method_id = WMAX_METHOD_ZONE_CONTROL;
> >  
> > -		input.length = (acpi_size) sizeof(wmax_basic_args);
> > +		input.length = sizeof(wmax_basic_args);
> >  		input.pointer = &wmax_basic_args;
> >  	} else {
> >  		legacy_args.colors = zone->colors;
> > @@ -306,7 +306,7 @@ static int alienware_update_led(struct platform_zone *zone)
> >  			guid = LEGACY_CONTROL_GUID;
> >  		method_id = zone->location + 1;
> >  
> > -		input.length = (acpi_size) sizeof(legacy_args);
> > +		input.length = sizeof(legacy_args);
> >  		input.pointer = &legacy_args;
> >  	}
> >  	pr_debug("alienware-wmi: guid %s method %d\n", guid, method_id);
> > @@ -358,7 +358,7 @@ static int wmax_brightness(int brightness)
> >  		.led_mask = 0xFF,
> >  		.percentage = brightness,
> >  	};
> > -	input.length = (acpi_size) sizeof(args);
> > +	input.length = sizeof(args);
> >  	input.pointer = &args;
> >  	status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0,
> >  				     WMAX_METHOD_BRIGHTNESS, &input, NULL);
> > @@ -508,7 +508,7 @@ static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
> >  	struct acpi_buffer input;
> >  	struct acpi_buffer output;
> >  
> > -	input.length = (acpi_size) sizeof(*in_args);
> > +	input.length = sizeof(*in_args);
> >  	input.pointer = in_args;
> >  	if (out_data) {
> >  		output.length = ACPI_ALLOCATE_BUFFER;
> > @@ -542,7 +542,7 @@ static ssize_t show_hdmi_cable(struct device *dev,
> >  	};
> >  	status =
> >  	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE,
> > -				   (u32 *) &out_data);
> > +				   &out_data);
> >  	if (ACPI_SUCCESS(status)) {
> >  		if (out_data == 0)
> >  			return sysfs_emit(buf, "[unconnected] connected unknown\n");
> > @@ -563,7 +563,7 @@ static ssize_t show_hdmi_source(struct device *dev,
> >  	};
> >  	status =
> >  	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS,
> > -				   (u32 *) &out_data);
> > +				   &out_data);
> >  
> >  	if (ACPI_SUCCESS(status)) {
> >  		if (out_data == 1)
> > @@ -643,7 +643,7 @@ static ssize_t show_amplifier_status(struct device *dev,
> >  	};
> >  	status =
> >  	    alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE,
> > -				   (u32 *) &out_data);
> > +				   &out_data);
> >  	if (ACPI_SUCCESS(status)) {
> >  		if (out_data == 0)
> >  			return sysfs_emit(buf, "[unconnected] connected unknown\n");
> > @@ -695,7 +695,7 @@ static ssize_t show_deepsleep_status(struct device *dev,
> >  		.arg = 0,
> >  	};
> >  	status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS,
> > -					(u32 *) &out_data);
> > +					&out_data);
> >  	if (ACPI_SUCCESS(status)) {
> >  		if (out_data == 0)
> >  			return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
> >
[PATCH v4 2/4] alienware-wmi: alienware_wmax_command() is now input size agnostic
Posted by Kurt Borja 1 month, 2 weeks ago
Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
 drivers/platform/x86/dell/alienware-wmi.c | 29 ++++++++++++-----------
 1 file changed, 15 insertions(+), 14 deletions(-)

diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
index 16a3fe9ac..8f7a8bfef 100644
--- a/drivers/platform/x86/dell/alienware-wmi.c
+++ b/drivers/platform/x86/dell/alienware-wmi.c
@@ -500,15 +500,15 @@ static void alienware_zone_exit(struct platform_device *dev)
 	kfree(zone_attrs);
 }
 
-static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
-					  u32 command, int *out_data)
+static acpi_status alienware_wmax_command(void *in_args, size_t insize,
+					  u32 command, u32 *out_data)
 {
 	acpi_status status;
 	union acpi_object *obj;
 	struct acpi_buffer input;
 	struct acpi_buffer output;
 
-	input.length = sizeof(*in_args);
+	input.length = insize;
 	input.pointer = in_args;
 	if (out_data) {
 		output.length = ACPI_ALLOCATE_BUFFER;
@@ -541,8 +541,8 @@ static ssize_t show_hdmi_cable(struct device *dev,
 		.arg = 0,
 	};
 	status =
-	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE,
-				   &out_data);
+	    alienware_wmax_command(&in_args, sizeof(in_args),
+				   WMAX_METHOD_HDMI_CABLE, &out_data);
 	if (ACPI_SUCCESS(status)) {
 		if (out_data == 0)
 			return sysfs_emit(buf, "[unconnected] connected unknown\n");
@@ -562,8 +562,8 @@ static ssize_t show_hdmi_source(struct device *dev,
 		.arg = 0,
 	};
 	status =
-	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS,
-				   &out_data);
+	    alienware_wmax_command(&in_args, sizeof(in_args),
+				   WMAX_METHOD_HDMI_STATUS, &out_data);
 
 	if (ACPI_SUCCESS(status)) {
 		if (out_data == 1)
@@ -589,7 +589,8 @@ static ssize_t toggle_hdmi_source(struct device *dev,
 		args.arg = 3;
 	pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf);
 
-	status = alienware_wmax_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL);
+	status = alienware_wmax_command(&args, sizeof(args),
+					WMAX_METHOD_HDMI_SOURCE, NULL);
 
 	if (ACPI_FAILURE(status))
 		pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
@@ -642,8 +643,8 @@ static ssize_t show_amplifier_status(struct device *dev,
 		.arg = 0,
 	};
 	status =
-	    alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE,
-				   &out_data);
+	    alienware_wmax_command(&in_args, sizeof(in_args),
+				   WMAX_METHOD_AMPLIFIER_CABLE, &out_data);
 	if (ACPI_SUCCESS(status)) {
 		if (out_data == 0)
 			return sysfs_emit(buf, "[unconnected] connected unknown\n");
@@ -694,8 +695,8 @@ static ssize_t show_deepsleep_status(struct device *dev,
 	struct wmax_basic_args in_args = {
 		.arg = 0,
 	};
-	status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS,
-					&out_data);
+	status = alienware_wmax_command(&in_args, sizeof(in_args),
+					WMAX_METHOD_DEEP_SLEEP_STATUS, &out_data);
 	if (ACPI_SUCCESS(status)) {
 		if (out_data == 0)
 			return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
@@ -723,8 +724,8 @@ static ssize_t toggle_deepsleep(struct device *dev,
 		args.arg = 2;
 	pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf);
 
-	status = alienware_wmax_command(&args, WMAX_METHOD_DEEP_SLEEP_CONTROL,
-					NULL);
+	status = alienware_wmax_command(&args, sizeof(args),
+					WMAX_METHOD_DEEP_SLEEP_CONTROL, NULL);
 
 	if (ACPI_FAILURE(status))
 		pr_err("alienware-wmi: deep sleep control failed: results: %u\n",
-- 
2.47.0
Re: [PATCH v4 2/4] alienware-wmi: alienware_wmax_command() is now input size agnostic
Posted by Ilpo Järvinen 1 month, 2 weeks ago
On Fri, 11 Oct 2024, Kurt Borja wrote:

As mentioned, please add the description of the change here.

This change also does int * -> u32 *, which I think is fine but please 
mention the justification here. If somebody looks this change 10 years 
from now, it helps a lot when the reasoning is recorded into the change.

-- 
 i.

> Signed-off-by: Kurt Borja <kuurtb@gmail.com>
> ---
>  drivers/platform/x86/dell/alienware-wmi.c | 29 ++++++++++++-----------
>  1 file changed, 15 insertions(+), 14 deletions(-)
> 
> diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
> index 16a3fe9ac..8f7a8bfef 100644
> --- a/drivers/platform/x86/dell/alienware-wmi.c
> +++ b/drivers/platform/x86/dell/alienware-wmi.c
> @@ -500,15 +500,15 @@ static void alienware_zone_exit(struct platform_device *dev)
>  	kfree(zone_attrs);
>  }
>  
> -static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
> -					  u32 command, int *out_data)
> +static acpi_status alienware_wmax_command(void *in_args, size_t insize,
> +					  u32 command, u32 *out_data)
>  {
>  	acpi_status status;
>  	union acpi_object *obj;
>  	struct acpi_buffer input;
>  	struct acpi_buffer output;
>  
> -	input.length = sizeof(*in_args);
> +	input.length = insize;
>  	input.pointer = in_args;
>  	if (out_data) {
>  		output.length = ACPI_ALLOCATE_BUFFER;
> @@ -541,8 +541,8 @@ static ssize_t show_hdmi_cable(struct device *dev,
>  		.arg = 0,
>  	};
>  	status =
> -	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE,
> -				   &out_data);
> +	    alienware_wmax_command(&in_args, sizeof(in_args),
> +				   WMAX_METHOD_HDMI_CABLE, &out_data);
>  	if (ACPI_SUCCESS(status)) {
>  		if (out_data == 0)
>  			return sysfs_emit(buf, "[unconnected] connected unknown\n");
> @@ -562,8 +562,8 @@ static ssize_t show_hdmi_source(struct device *dev,
>  		.arg = 0,
>  	};
>  	status =
> -	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS,
> -				   &out_data);
> +	    alienware_wmax_command(&in_args, sizeof(in_args),
> +				   WMAX_METHOD_HDMI_STATUS, &out_data);
>  
>  	if (ACPI_SUCCESS(status)) {
>  		if (out_data == 1)
> @@ -589,7 +589,8 @@ static ssize_t toggle_hdmi_source(struct device *dev,
>  		args.arg = 3;
>  	pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf);
>  
> -	status = alienware_wmax_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL);
> +	status = alienware_wmax_command(&args, sizeof(args),
> +					WMAX_METHOD_HDMI_SOURCE, NULL);
>  
>  	if (ACPI_FAILURE(status))
>  		pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
> @@ -642,8 +643,8 @@ static ssize_t show_amplifier_status(struct device *dev,
>  		.arg = 0,
>  	};
>  	status =
> -	    alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE,
> -				   &out_data);
> +	    alienware_wmax_command(&in_args, sizeof(in_args),
> +				   WMAX_METHOD_AMPLIFIER_CABLE, &out_data);
>  	if (ACPI_SUCCESS(status)) {
>  		if (out_data == 0)
>  			return sysfs_emit(buf, "[unconnected] connected unknown\n");
> @@ -694,8 +695,8 @@ static ssize_t show_deepsleep_status(struct device *dev,
>  	struct wmax_basic_args in_args = {
>  		.arg = 0,
>  	};
> -	status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS,
> -					&out_data);
> +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> +					WMAX_METHOD_DEEP_SLEEP_STATUS, &out_data);
>  	if (ACPI_SUCCESS(status)) {
>  		if (out_data == 0)
>  			return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
> @@ -723,8 +724,8 @@ static ssize_t toggle_deepsleep(struct device *dev,
>  		args.arg = 2;
>  	pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf);
>  
> -	status = alienware_wmax_command(&args, WMAX_METHOD_DEEP_SLEEP_CONTROL,
> -					NULL);
> +	status = alienware_wmax_command(&args, sizeof(args),
> +					WMAX_METHOD_DEEP_SLEEP_CONTROL, NULL);
>  
>  	if (ACPI_FAILURE(status))
>  		pr_err("alienware-wmi: deep sleep control failed: results: %u\n",
>
[PATCH v4 3/4] alienware-wmi: added platform profile support
Posted by Kurt Borja 1 month, 2 weeks ago
Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
 drivers/platform/x86/dell/Kconfig         |   1 +
 drivers/platform/x86/dell/alienware-wmi.c | 236 ++++++++++++++++++++++
 2 files changed, 237 insertions(+)

diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig
index 68a49788a..b06d634cd 100644
--- a/drivers/platform/x86/dell/Kconfig
+++ b/drivers/platform/x86/dell/Kconfig
@@ -21,6 +21,7 @@ config ALIENWARE_WMI
 	depends on LEDS_CLASS
 	depends on NEW_LEDS
 	depends on ACPI_WMI
+	select ACPI_PLATFORM_PROFILE
 	help
 	 This is a driver for controlling Alienware BIOS driven
 	 features.  It exposes an interface for controlling the AlienFX
diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
index 8f7a8bfef..8af2ab7fe 100644
--- a/drivers/platform/x86/dell/alienware-wmi.c
+++ b/drivers/platform/x86/dell/alienware-wmi.c
@@ -8,8 +8,11 @@
 #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>
 
@@ -25,6 +28,12 @@
 #define WMAX_METHOD_AMPLIFIER_CABLE	0x6
 #define WMAX_METHOD_DEEP_SLEEP_CONTROL	0x0B
 #define WMAX_METHOD_DEEP_SLEEP_STATUS	0x0C
+#define WMAX_METHOD_THERMAL_INFORMATION	0x14
+#define WMAX_METHOD_THERMAL_CONTROL	0x15
+
+#define WMAX_ARG_GET_CURRENT_PROF	0x0B
+
+#define WMAX_FAILURE_CODE		0xFFFFFFFF
 
 MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
 MODULE_DESCRIPTION("Alienware special feature control");
@@ -49,11 +58,27 @@ enum WMAX_CONTROL_STATES {
 	WMAX_SUSPEND = 3,
 };
 
+enum WMAX_THERMAL_PROFILE {
+	WMAX_THERMAL_QUIET = 0x96,
+	WMAX_THERMAL_BALANCED = 0x97,
+	WMAX_THERMAL_BALANCED_PERFORMANCE = 0x98,
+	WMAX_THERMAL_PERFORMANCE = 0x99,
+	WMAX_THERMAL_USTT_LOW_POWER = 0xA5,
+	WMAX_THERMAL_USTT_QUIET = 0xA3,
+	WMAX_THERMAL_USTT_BALANCED = 0xA0,
+	WMAX_THERMAL_USTT_BALANCED_PERFORMANCE = 0xA1,
+	WMAX_THERMAL_USTT_PERFORMANCE = 0xA4,
+	WMAX_THERMAL_GMODE = 0xAB,
+};
+
 struct quirk_entry {
 	u8 num_zones;
 	u8 hdmi_mux;
 	u8 amplifier;
 	u8 deepslp;
+	u8 thermal;
+	u8 thermal_ustt;
+	u8 gmode;
 };
 
 static struct quirk_entry *quirks;
@@ -64,6 +89,9 @@ static struct quirk_entry quirk_inspiron5675 = {
 	.hdmi_mux = 0,
 	.amplifier = 0,
 	.deepslp = 0,
+	.thermal = 0,
+	.thermal_ustt = 0,
+	.gmode = 0,
 };
 
 static struct quirk_entry quirk_unknown = {
@@ -71,6 +99,9 @@ static struct quirk_entry quirk_unknown = {
 	.hdmi_mux = 0,
 	.amplifier = 0,
 	.deepslp = 0,
+	.thermal = 0,
+	.thermal_ustt = 0,
+	.gmode = 0,
 };
 
 static struct quirk_entry quirk_x51_r1_r2 = {
@@ -78,6 +109,9 @@ static struct quirk_entry quirk_x51_r1_r2 = {
 	.hdmi_mux = 0,
 	.amplifier = 0,
 	.deepslp = 0,
+	.thermal = 0,
+	.thermal_ustt = 0,
+	.gmode = 0,
 };
 
 static struct quirk_entry quirk_x51_r3 = {
@@ -85,6 +119,9 @@ static struct quirk_entry quirk_x51_r3 = {
 	.hdmi_mux = 0,
 	.amplifier = 1,
 	.deepslp = 0,
+	.thermal = 0,
+	.thermal_ustt = 0,
+	.gmode = 0,
 };
 
 static struct quirk_entry quirk_asm100 = {
@@ -92,6 +129,9 @@ static struct quirk_entry quirk_asm100 = {
 	.hdmi_mux = 1,
 	.amplifier = 0,
 	.deepslp = 0,
+	.thermal = 0,
+	.thermal_ustt = 0,
+	.gmode = 0,
 };
 
 static struct quirk_entry quirk_asm200 = {
@@ -99,6 +139,9 @@ static struct quirk_entry quirk_asm200 = {
 	.hdmi_mux = 1,
 	.amplifier = 0,
 	.deepslp = 1,
+	.thermal = 0,
+	.thermal_ustt = 0,
+	.gmode = 0,
 };
 
 static struct quirk_entry quirk_asm201 = {
@@ -106,6 +149,19 @@ static struct quirk_entry quirk_asm201 = {
 	.hdmi_mux = 1,
 	.amplifier = 1,
 	.deepslp = 1,
+	.thermal = 0,
+	.thermal_ustt = 0,
+	.gmode = 0,
+};
+
+static struct quirk_entry quirk_x15_r1 = {
+	.num_zones = 2,
+	.hdmi_mux = 0,
+	.amplifier = 0,
+	.deepslp = 0,
+	.thermal = 0,
+	.thermal_ustt = 1,
+	.gmode = 0,
 };
 
 static int __init dmi_matched(const struct dmi_system_id *dmi)
@@ -169,6 +225,15 @@ static const struct dmi_system_id alienware_quirks[] __initconst = {
 		},
 		.driver_data = &quirk_asm201,
 	},
+	{
+		.callback = dmi_matched,
+		.ident = "Alienware x15 R1",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R1")
+		},
+		.driver_data = &quirk_x15_r1,
+	},
 	{
 		.callback = dmi_matched,
 		.ident = "Dell Inc. Inspiron 5675",
@@ -218,6 +283,7 @@ static struct platform_device *platform_device;
 static struct device_attribute *zone_dev_attrs;
 static struct attribute **zone_attrs;
 static struct platform_zone *zone_data;
+static struct platform_profile_handler pp_handler;
 
 static struct platform_driver platform_driver = {
 	.driver = {
@@ -761,6 +827,168 @@ static int create_deepsleep(struct platform_device *dev)
 	return ret;
 }
 
+/*
+ * Thermal Profile control
+ *  - Provides thermal profile control through the Platform Profile API
+ */
+#define WMAX_PROFILE_MASK	GENMASK(15, 8)
+#define WMAX_PROFILE_ACTIVATE	BIT(0)
+
+static u32 profile_to_wmax_arg(enum WMAX_THERMAL_PROFILE prof)
+{
+	return FIELD_PREP(WMAX_PROFILE_MASK, prof) | WMAX_PROFILE_ACTIVATE;
+}
+
+static int thermal_profile_get(struct platform_profile_handler *pprof,
+				enum platform_profile_option *profile)
+{
+	acpi_status status;
+	u32 in_args = WMAX_ARG_GET_CURRENT_PROF;
+	u32 out_data;
+
+	status = alienware_wmax_command(&in_args, sizeof(in_args),
+					WMAX_METHOD_THERMAL_INFORMATION, &out_data);
+
+	if (ACPI_FAILURE(status))
+		return -EIO;
+
+	if (out_data == WMAX_FAILURE_CODE)
+		return -EBADRQC;
+
+	switch (out_data) {
+	case WMAX_THERMAL_USTT_LOW_POWER:
+		*profile = PLATFORM_PROFILE_LOW_POWER;
+		break;
+	case WMAX_THERMAL_QUIET:
+	case WMAX_THERMAL_USTT_QUIET:
+		*profile = PLATFORM_PROFILE_QUIET;
+		break;
+	case WMAX_THERMAL_BALANCED:
+	case WMAX_THERMAL_USTT_BALANCED:
+		*profile = PLATFORM_PROFILE_BALANCED;
+		break;
+	case WMAX_THERMAL_BALANCED_PERFORMANCE:
+	case WMAX_THERMAL_USTT_BALANCED_PERFORMANCE:
+		*profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE;
+		break;
+	case WMAX_THERMAL_GMODE:
+	case WMAX_THERMAL_PERFORMANCE:
+	case WMAX_THERMAL_USTT_PERFORMANCE:
+		*profile = PLATFORM_PROFILE_PERFORMANCE;
+		break;
+	default:
+		return -ENODATA;
+	}
+
+	return 0;
+}
+
+static int thermal_profile_set(struct platform_profile_handler *pprof,
+				enum platform_profile_option profile)
+{
+	acpi_status status;
+	u32 in_args;
+	u32 out_data;
+
+	switch (profile) {
+	case PLATFORM_PROFILE_QUIET:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET);
+		break;
+	case PLATFORM_PROFILE_BALANCED:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED);
+		break;
+	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE);
+		break;
+	case PLATFORM_PROFILE_PERFORMANCE:
+		if (quirks->gmode > 0)
+			in_args = profile_to_wmax_arg(WMAX_THERMAL_GMODE);
+		else
+			in_args = profile_to_wmax_arg(WMAX_THERMAL_PERFORMANCE);
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	status = alienware_wmax_command(&in_args, sizeof(in_args),
+					WMAX_METHOD_THERMAL_CONTROL, &out_data);
+
+	if (ACPI_FAILURE(status))
+		return -EIO;
+
+	if (out_data == WMAX_FAILURE_CODE)
+		return -EBADRQC;
+
+	return 0;
+}
+
+static int thermal_profile_set_ustt(struct platform_profile_handler *pprof,
+				    enum platform_profile_option profile)
+{
+	acpi_status status;
+	u32 in_args;
+	u32 out_data;
+
+	switch (profile) {
+	case PLATFORM_PROFILE_LOW_POWER:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_LOW_POWER);
+		break;
+	case PLATFORM_PROFILE_QUIET:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_QUIET);
+		break;
+	case PLATFORM_PROFILE_BALANCED:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_BALANCED);
+		break;
+	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_BALANCED_PERFORMANCE);
+		break;
+	case PLATFORM_PROFILE_PERFORMANCE:
+		if (quirks->gmode > 0)
+			in_args = profile_to_wmax_arg(WMAX_THERMAL_GMODE);
+		else
+			in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_PERFORMANCE);
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	status = alienware_wmax_command(&in_args, sizeof(in_args),
+					WMAX_METHOD_THERMAL_CONTROL, &out_data);
+
+	if (ACPI_FAILURE(status))
+		return -EIO;
+
+	if (out_data == WMAX_FAILURE_CODE)
+		return -EBADRQC;
+
+	return 0;
+}
+
+static int create_thermal_profile(void)
+{
+	pp_handler.profile_get = thermal_profile_get;
+
+	if (quirks->thermal > 0)
+		pp_handler.profile_set = thermal_profile_set;
+	else {
+		pp_handler.profile_set = thermal_profile_set_ustt;
+		set_bit(PLATFORM_PROFILE_LOW_POWER, pp_handler.choices);
+	}
+
+	set_bit(PLATFORM_PROFILE_QUIET, pp_handler.choices);
+	set_bit(PLATFORM_PROFILE_BALANCED, pp_handler.choices);
+	set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, pp_handler.choices);
+	set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices);
+
+	return platform_profile_register(&pp_handler);
+}
+
+static void remove_thermal_profile(void)
+{
+	if (quirks->thermal > 0)
+		platform_profile_remove();
+}
+
 static int __init alienware_wmi_init(void)
 {
 	int ret;
@@ -808,6 +1036,12 @@ static int __init alienware_wmi_init(void)
 			goto fail_prep_deepsleep;
 	}
 
+	if (quirks->thermal > 0 || quirks->thermal_ustt > 0) {
+		ret = create_thermal_profile();
+		if (ret)
+			goto fail_prep_thermal_profile;
+	}
+
 	ret = alienware_zone_init(platform_device);
 	if (ret)
 		goto fail_prep_zones;
@@ -818,6 +1052,7 @@ static int __init alienware_wmi_init(void)
 	alienware_zone_exit(platform_device);
 fail_prep_deepsleep:
 fail_prep_amplifier:
+fail_prep_thermal_profile:
 fail_prep_hdmi:
 	platform_device_del(platform_device);
 fail_platform_device2:
@@ -835,6 +1070,7 @@ static void __exit alienware_wmi_exit(void)
 	if (platform_device) {
 		alienware_zone_exit(platform_device);
 		remove_hdmi(platform_device);
+		remove_thermal_profile();
 		platform_device_unregister(platform_device);
 		platform_driver_unregister(&platform_driver);
 	}
-- 
2.47.0
[PATCH v4 4/4] alienware-wmi: WMAX interface documentation
Posted by Kurt Borja 1 month, 2 weeks ago
Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
 Documentation/wmi/devices/alienware-wmi.rst | 364 ++++++++++++++++++++
 1 file changed, 364 insertions(+)
 create mode 100644 Documentation/wmi/devices/alienware-wmi.rst

diff --git a/Documentation/wmi/devices/alienware-wmi.rst b/Documentation/wmi/devices/alienware-wmi.rst
new file mode 100644
index 000000000..cf5d6259f
--- /dev/null
+++ b/Documentation/wmi/devices/alienware-wmi.rst
@@ -0,0 +1,364 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+==============================================
+Dell AWCC WMI interface driver (alienware-wmi)
+==============================================
+
+Introduction
+============
+
+The WMI device WMAX has been implemented for many Alienware and Dell's G-Series
+models. Throughout these models, two implementations have been identified. The
+first one, used by older systems, deals with HDMI, brightness, RGB, amplifier
+and deep sleep control. The second one used by newer systems deals primarily
+with thermal, overclocking, and GPIO control.
+
+It is suspected that the latter is used by Alienware Command Center (AWCC) to
+manage manufacturer predefined thermal profiles. The alienware-wmi driver
+exposes Thermal_Information and Thermal_Control methods through the Platform
+Profile API to mimic AWCC's behavior.
+
+This newer interface, named AWCCMethodFunction has been reverse engineered, as
+Dell has not provided any official documentation. We will try to describe to the
+best of our ability its discovered inner workings.
+
+.. note::
+   The following method description may vary between models.
+
+WMI interface description
+-------------------------
+
+The WMI interface description can be decoded from the embedded binary MOF (bmof)
+data using the `bmfdec <https://github.com/pali/bmfdec>`_ utility:
+
+::
+
+ [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("WMI Function"), guid("{A70591CE-A997-11DA-B012-B622A1EF5492}")]
+ class AWCCWmiMethodFunction {
+   [key, read] string InstanceName;
+   [read] boolean Active;
+
+   [WmiMethodId(13), Implemented, read, write, Description("Return Overclocking Report.")] void Return_OverclockingReport([out] uint32 argr);
+   [WmiMethodId(14), Implemented, read, write, Description("Set OCUIBIOS Control.")] void Set_OCUIBIOSControl([in] uint32 arg2, [out] uint32 argr);
+   [WmiMethodId(15), Implemented, read, write, Description("Clear OC FailSafe Flag.")] void Clear_OCFailSafeFlag([out] uint32 argr);
+   [WmiMethodId(19), Implemented, read, write, Description("Get Fan Sensors.")] void GetFanSensors([in] uint32 arg2, [out] uint32 argr);
+   [WmiMethodId(20), Implemented, read, write, Description("Thermal Information.")] void Thermal_Information([in] uint32 arg2, [out] uint32 argr);
+   [WmiMethodId(21), Implemented, read, write, Description("Thermal Control.")] void Thermal_Control([in] uint32 arg2, [out] uint32 argr);
+   [WmiMethodId(23), Implemented, read, write, Description("MemoryOCControl.")] void MemoryOCControl([in] uint32 arg2, [out] uint32 argr);
+   [WmiMethodId(26), Implemented, read, write, Description("System Information.")] void SystemInformation([in] uint32 arg2, [out] uint32 argr);
+   [WmiMethodId(28), Implemented, read, write, Description("Power Information.")] void PowerInformation([in] uint32 arg2, [out] uint32 argr);
+   [WmiMethodId(32), Implemented, read, write, Description("FW Update GPIO toggle.")] void FWUpdateGPIOtoggle([in] uint32 arg2, [out] uint32 argr);
+   [WmiMethodId(33), Implemented, read, write, Description("Read Total of GPIOs.")] void ReadTotalofGPIOs([out] uint32 argr);
+   [WmiMethodId(34), Implemented, read, write, Description("Read GPIO pin Status.")] void ReadGPIOpPinStatus([in] uint32 arg2, [out] uint32 argr);
+   [WmiMethodId(35), Implemented, read, write, Description("Read Chassis Color.")] void ReadChassisColor([out] uint32 argr);
+   [WmiMethodId(36), Implemented, read, write, Description("Read Platform Properties.")] void ReadPlatformProperties([out] uint32 argr);
+   [WmiMethodId(128), Implemented, read, write, Description("Caldera SW installation.")] void CalderaSWInstallation([out] uint32 argr);
+   [WmiMethodId(129), Implemented, read, write, Description("Caldera SW is released.")] void CalderaSWReleased([out] uint32 argr);
+   [WmiMethodId(130), Implemented, read, write, Description("Caldera Connection Status.")] void CalderaConnectionStatus([in] uint32 arg2, [out] uint32 argr);
+   [WmiMethodId(131), Implemented, read, write, Description("Surprise Unplugged Flag Status.")] void SurpriseUnpluggedFlagStatus([out] uint32 argr);
+   [WmiMethodId(132), Implemented, read, write, Description("Clear Surprise Unplugged Flag.")] void ClearSurpriseUnpluggedFlag([out] uint32 argr);
+   [WmiMethodId(133), Implemented, read, write, Description("Cancel Undock Request.")] void CancelUndockRequest([out] uint32 argr);
+   [WmiMethodId(135), Implemented, read, write, Description("Devices in Caldera.")] void DevicesInCaldera([in] uint32 arg2, [out] uint32 argr);
+   [WmiMethodId(136), Implemented, read, write, Description("Notify BIOS for SW ready to disconnect Caldera.")] void NotifyBIOSForSWReadyToDisconnectCaldera([out] uint32 argr);
+   [WmiMethodId(160), Implemented, read, write, Description("Tobii SW installation.")] void TobiiSWinstallation([out] uint32 argr);
+   [WmiMethodId(161), Implemented, read, write, Description("Tobii SW Released.")] void TobiiSWReleased([out] uint32 argr);
+   [WmiMethodId(162), Implemented, read, write, Description("Tobii Camera Power Reset.")] void TobiiCameraPowerReset([out] uint32 argr);
+   [WmiMethodId(163), Implemented, read, write, Description("Tobii Camera Power On.")] void TobiiCameraPowerOn([out] uint32 argr);
+   [WmiMethodId(164), Implemented, read, write, Description("Tobii Camera Power Off.")] void TobiiCameraPowerOff([out] uint32 argr);
+ };
+
+Some of these methods get quite intricate so we will describe them using
+pseudo-code that vaguely resembles the original ASL code.
+
+Argument Structure
+------------------
+
+All input arguments have type **uint32** and their structure is very similar
+between methods. Usually, the first byte corresponds to a specific *operation*
+the method performs, and the subsequent bytes correspond to *arguments* passed
+to this *operation*. For example, if an operation has code 0x01 and requires an
+ID 0xA0, the argument you would pass to the method is 0xA001.
+
+
+Thermal Methods
+===============
+
+WMI method Thermal_Information([in] uint32 arg2, [out] uint32 argr)
+-------------------------------------------------------------------
+
+::
+
+ if BYTE_0(arg2) == 0x01:
+         argr = 1
+
+ if BYTE_0(arg2) == 0x02:
+         argr = UNKNOWN_CONSTANT
+
+ if BYTE_0(arg2) == 0x03:
+         if BYTE_1(arg2) == 0x00:
+                 argr = FAN_ID_0
+
+         if BYTE_1(arg2) == 0x01:
+                 argr = FAN_ID_1
+
+         if BYTE_1(arg2) == 0x02:
+                 argr = FAN_ID_2
+
+         if BYTE_1(arg2) == 0x03:
+                 argr = FAN_ID_3
+
+         if BYTE_1(arg2) == 0x04:
+                 argr = SENSOR_ID_CPU | 0x0100
+
+         if BYTE_1(arg2) == 0x05:
+                 argr = SENSOR_ID_GPU | 0x0100
+
+         if BYTE_1(arg2) == 0x06:
+                 argr = THERMAL_MODE_QUIET_ID
+
+         if BYTE_1(arg2) == 0x07:
+                 argr = THERMAL_MODE_BALANCED_ID
+
+         if BYTE_1(arg2) == 0x08:
+                 argr = THERMAL_MODE_BALANCED_PERFORMANCE_ID
+
+         if BYTE_1(arg2) == 0x09:
+                 argr = THERMAL_MODE_PERFORMANCE_ID
+
+         if BYTE_1(arg2) == 0x0A:
+                 argr = THERMAL_MODE_LOW_POWER_ID
+
+         if BYTE_1(arg2) == 0x0B:
+                 argr = THERMAL_MODE_GMODE_ID
+
+         else:
+                 argr = 0xFFFFFFFF
+
+ if BYTE_0(arg2) == 0x04:
+         if is_valid_sensor(BYTE_1(arg2)):
+                 argr = SENSOR_TEMP_C
+         else:
+                 argr = 0xFFFFFFFF
+
+ if BYTE_0(arg2) == 0x05:
+         if is_valid_fan(BYTE_1(arg2)):
+                 argr = FAN_RPM()
+
+ if BYTE_0(arg2) == 0x06:
+         skip
+
+ if BYTE_0(arg2) == 0x07:
+         argr = 0
+
+ If BYTE_0(arg2) == 0x08:
+         if is_valid_fan(BYTE_1(arg2)):
+                 argr = 0
+         else:
+                 argr = 0xFFFFFFFF
+
+ if BYTE_0(arg2) == 0x09:
+         if is_valid_fan(BYTE_1(arg2)):
+                 argr = FAN_UNKNOWN_STAT_0()
+
+         else:
+                 argr = 0xFFFFFFFF
+
+ if BYTE_0(arg2) == 0x0A:
+         argr = THERMAL_MODE_BALANCED_ID
+
+ if BYTE_0(arg2) == 0x0B:
+         argr = CURRENT_THERMAL_MODE()
+
+ if BYTE_0(arg2) == 0x0C:
+         if is_valid_fan(BYTE_1(arg2)):
+                 argr = FAN_UNKNOWN_STAT_1()
+         else:
+                 argr = 0xFFFFFFFF
+
+WMI method Thermal_Control([in] uint32 arg2, [out] uint32 argr)
+---------------------------------------------------------------
+
+::
+
+ if BYTE_0(arg2) == 0x01:
+         if is_valid_thermal_profile(BYTE_1(arg2)):
+                 SET_THERMAL_PROFILE(BYTE_1(arg2))
+                 argr = 0
+
+ if BYTE_0(arg2) == 0x02:
+         if is_valid_fan(BYTE_1(arg2)):
+                 SET_FAN_SPEED_MULTIPLIER(BYTE_2(arg2))
+                 argr = 0
+         else:
+                 argr = 0xFFFFFFFF
+
+.. note::
+   While you can manually change the fan speed multiplier with this method,
+   Dell's BIOS tends to overwrite this changes anyway.
+
+These are the known thermal profile codes:
+
+::
+
+ CUSTOM                         0x00
+
+ QUIET                          0x96
+ BALANCED                       0x97
+ BALANCED_PERFORMANCE           0x98
+ PERFORMANCE                    0x99
+
+ QUIET_USTT                     0xA3
+ BALANCED_USTT                  0xA0
+ BALANCED_PERFORMANCE_USTT      0xA1
+ PERFORMANCE_USTT               0xA4
+ LOW_POWER_USTT                 0xA5
+
+ GMODE                          0xAB
+
+Usually if a model doesn't support the first four profiles they will support
+the User Selectable Thermal Tables (USTT) profiles and vice-versa.
+
+GMODE replaces PERFORMANCE in G-Series laptops.
+
+Very grateful to `AlexIII <https://github.com/AlexIII/tcc-g15>`_ for discovering
+some of the codes compatible with G-Series laptops.
+
+WMI method GetFanSensors([in] uint32 arg2, [out] uint32 argr)
+-------------------------------------------------------------
+
+::
+
+ if BYTE_0(arg2) == 1:
+        if is_valid_fan(BYTE_1(arg2)):
+                argr = 1
+        else:
+                argr = 0
+
+ if BYTE_0(arg2) == 2:
+        if is_valid_fan(BYTE_1(arg2)):
+                if BYTE_2(arg2) == 0:
+                        argr == SENSOR_ID
+                else
+                        argr == 0xFFFFFFFF
+        else:
+                argr = 0
+
+Overclocking Methods
+====================
+
+.. warning::
+   These methods have not been tested and are only partially reverse
+   engineered.
+
+WMI method Return_OverclockingReport([out] uint32 argr)
+-------------------------------------------------------
+
+::
+
+ CSMI (0xE3, 0x99)
+ argr = 0
+
+CSMI is an unknown operation.
+
+WMI method Set_OCUIBIOSControl([in] uint32 arg2, [out] uint32 argr)
+-------------------------------------------------------------------
+
+::
+
+ CSMI (0xE3, 0x99)
+ argr = 0
+
+CSMI is an unknown operation
+
+WMI method Clear_OCFailSafeFlag([out] uint32 argr)
+--------------------------------------------------
+
+::
+
+ CSMI (0xE3, 0x99)
+ argr = 0
+
+CSMI is an unknown operation
+
+
+WMI method MemoryOCControl([in] uint32 arg2, [out] uint32 argr)
+---------------------------------------------------------------
+
+AWCC supports memory overclocking, but this method is very intricate and has
+not been deciphered yet.
+
+GPIO methods
+============
+
+These methods are probably related to some kind of firmware update system,
+through a GPIO device.
+
+.. warning::
+   These methods have not been tested and are only partially reverse
+   engineered.
+
+WMI method FWUpdateGPIOtoggle([in] uint32 arg2, [out] uint32 argr)
+------------------------------------------------------------------
+
+::
+
+ if BYTE_0(arg2) == 0:
+         if BYTE_1(arg2) == 1:
+                 SET_PIN_A_HIGH()
+         else:
+                 SET_PIN_A_LOW()
+
+ if BYTE_0(arg2) == 1:
+         if BYTE_1(arg2) == 1:
+                 SET_PIN_B_HIGH()
+
+         else:
+                 SET_PIN_B_LOW()
+
+ else:
+         argr = 1
+
+WMI method ReadTotalofGPIOs([out] uint32 argr)
+----------------------------------------------
+
+::
+
+ argr = 0x02
+
+WMI method ReadGPIOpPinStatus([in] uint32 arg2, [out] uint32 argr)
+------------------------------------------------------------------
+
+::
+
+ if BYTE_0(arg2) == 0:
+         argr = PIN_A_STATUS
+
+ if BYTE_0(arg2) == 1:
+         argr = PIN_B_STATUS
+
+
+Other information Methods
+=========================
+
+WMI method SystemInformation([in] uint32 arg2, [out] uint32 argr)
+-----------------------------------------------------------------
+
+Returns unknown information.
+
+WMI method PowerInformation([in] uint32 arg2, [out] uint32 argr)
+----------------------------------------------------------------
+
+Returns unknown information.
+
+WMI method ReadChassisColor([out] uint32 argr)
+----------------------------------------------
+
+::
+
+ argr = CHASSIS_COLOR_ID
+
+WMI method ReadPlatformProperties([out] uint32 argr)
+----------------------------------------------------
+
+Returns unknown information.
+
-- 
2.47.0
Re: [PATCH v4 4/4] alienware-wmi: WMAX interface documentation
Posted by Ilpo Järvinen 1 month, 2 weeks ago
On Fri, 11 Oct 2024, Kurt Borja wrote:

> Signed-off-by: Kurt Borja <kuurtb@gmail.com>
> ---
>  Documentation/wmi/devices/alienware-wmi.rst | 364 ++++++++++++++++++++
>  1 file changed, 364 insertions(+)
>  create mode 100644 Documentation/wmi/devices/alienware-wmi.rst
> 
> diff --git a/Documentation/wmi/devices/alienware-wmi.rst b/Documentation/wmi/devices/alienware-wmi.rst
> new file mode 100644
> index 000000000..cf5d6259f
> --- /dev/null
> +++ b/Documentation/wmi/devices/alienware-wmi.rst
> @@ -0,0 +1,364 @@
> +.. SPDX-License-Identifier: GPL-2.0-or-later
> +
> +==============================================
> +Dell AWCC WMI interface driver (alienware-wmi)
> +==============================================
> +
> +Introduction
> +============
> +
> +The WMI device WMAX has been implemented for many Alienware and Dell's G-Series
> +models. Throughout these models, two implementations have been identified. The
> +first one, used by older systems, deals with HDMI, brightness, RGB, amplifier
> +and deep sleep control. The second one used by newer systems deals primarily
> +with thermal, overclocking, and GPIO control.
> +
> +It is suspected that the latter is used by Alienware Command Center (AWCC) to
> +manage manufacturer predefined thermal profiles. The alienware-wmi driver
> +exposes Thermal_Information and Thermal_Control methods through the Platform
> +Profile API to mimic AWCC's behavior.
> +
> +This newer interface, named AWCCMethodFunction has been reverse engineered, as
> +Dell has not provided any official documentation. We will try to describe to the
> +best of our ability its discovered inner workings.
> +
> +.. note::
> +   The following method description may vary between models.
> +
> +WMI interface description
> +-------------------------
> +
> +The WMI interface description can be decoded from the embedded binary MOF (bmof)
> +data using the `bmfdec <https://github.com/pali/bmfdec>`_ utility:
> +
> +::
> +
> + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("WMI Function"), guid("{A70591CE-A997-11DA-B012-B622A1EF5492}")]
> + class AWCCWmiMethodFunction {
> +   [key, read] string InstanceName;
> +   [read] boolean Active;
> +
> +   [WmiMethodId(13), Implemented, read, write, Description("Return Overclocking Report.")] void Return_OverclockingReport([out] uint32 argr);
> +   [WmiMethodId(14), Implemented, read, write, Description("Set OCUIBIOS Control.")] void Set_OCUIBIOSControl([in] uint32 arg2, [out] uint32 argr);
> +   [WmiMethodId(15), Implemented, read, write, Description("Clear OC FailSafe Flag.")] void Clear_OCFailSafeFlag([out] uint32 argr);
> +   [WmiMethodId(19), Implemented, read, write, Description("Get Fan Sensors.")] void GetFanSensors([in] uint32 arg2, [out] uint32 argr);
> +   [WmiMethodId(20), Implemented, read, write, Description("Thermal Information.")] void Thermal_Information([in] uint32 arg2, [out] uint32 argr);
> +   [WmiMethodId(21), Implemented, read, write, Description("Thermal Control.")] void Thermal_Control([in] uint32 arg2, [out] uint32 argr);
> +   [WmiMethodId(23), Implemented, read, write, Description("MemoryOCControl.")] void MemoryOCControl([in] uint32 arg2, [out] uint32 argr);
> +   [WmiMethodId(26), Implemented, read, write, Description("System Information.")] void SystemInformation([in] uint32 arg2, [out] uint32 argr);
> +   [WmiMethodId(28), Implemented, read, write, Description("Power Information.")] void PowerInformation([in] uint32 arg2, [out] uint32 argr);
> +   [WmiMethodId(32), Implemented, read, write, Description("FW Update GPIO toggle.")] void FWUpdateGPIOtoggle([in] uint32 arg2, [out] uint32 argr);
> +   [WmiMethodId(33), Implemented, read, write, Description("Read Total of GPIOs.")] void ReadTotalofGPIOs([out] uint32 argr);
> +   [WmiMethodId(34), Implemented, read, write, Description("Read GPIO pin Status.")] void ReadGPIOpPinStatus([in] uint32 arg2, [out] uint32 argr);
> +   [WmiMethodId(35), Implemented, read, write, Description("Read Chassis Color.")] void ReadChassisColor([out] uint32 argr);
> +   [WmiMethodId(36), Implemented, read, write, Description("Read Platform Properties.")] void ReadPlatformProperties([out] uint32 argr);
> +   [WmiMethodId(128), Implemented, read, write, Description("Caldera SW installation.")] void CalderaSWInstallation([out] uint32 argr);
> +   [WmiMethodId(129), Implemented, read, write, Description("Caldera SW is released.")] void CalderaSWReleased([out] uint32 argr);
> +   [WmiMethodId(130), Implemented, read, write, Description("Caldera Connection Status.")] void CalderaConnectionStatus([in] uint32 arg2, [out] uint32 argr);
> +   [WmiMethodId(131), Implemented, read, write, Description("Surprise Unplugged Flag Status.")] void SurpriseUnpluggedFlagStatus([out] uint32 argr);
> +   [WmiMethodId(132), Implemented, read, write, Description("Clear Surprise Unplugged Flag.")] void ClearSurpriseUnpluggedFlag([out] uint32 argr);
> +   [WmiMethodId(133), Implemented, read, write, Description("Cancel Undock Request.")] void CancelUndockRequest([out] uint32 argr);
> +   [WmiMethodId(135), Implemented, read, write, Description("Devices in Caldera.")] void DevicesInCaldera([in] uint32 arg2, [out] uint32 argr);
> +   [WmiMethodId(136), Implemented, read, write, Description("Notify BIOS for SW ready to disconnect Caldera.")] void NotifyBIOSForSWReadyToDisconnectCaldera([out] uint32 argr);
> +   [WmiMethodId(160), Implemented, read, write, Description("Tobii SW installation.")] void TobiiSWinstallation([out] uint32 argr);
> +   [WmiMethodId(161), Implemented, read, write, Description("Tobii SW Released.")] void TobiiSWReleased([out] uint32 argr);
> +   [WmiMethodId(162), Implemented, read, write, Description("Tobii Camera Power Reset.")] void TobiiCameraPowerReset([out] uint32 argr);
> +   [WmiMethodId(163), Implemented, read, write, Description("Tobii Camera Power On.")] void TobiiCameraPowerOn([out] uint32 argr);
> +   [WmiMethodId(164), Implemented, read, write, Description("Tobii Camera Power Off.")] void TobiiCameraPowerOff([out] uint32 argr);
> + };
> +
> +Some of these methods get quite intricate so we will describe them using
> +pseudo-code that vaguely resembles the original ASL code.
> +
> +Argument Structure
> +------------------
> +
> +All input arguments have type **uint32** and their structure is very similar
> +between methods. Usually, the first byte corresponds to a specific *operation*
> +the method performs, and the subsequent bytes correspond to *arguments* passed
> +to this *operation*. For example, if an operation has code 0x01 and requires an
> +ID 0xA0, the argument you would pass to the method is 0xA001.
> +
> +
> +Thermal Methods
> +===============
> +
> +WMI method Thermal_Information([in] uint32 arg2, [out] uint32 argr)
> +-------------------------------------------------------------------
> +
> +::
> +
> + if BYTE_0(arg2) == 0x01:
> +         argr = 1
> +
> + if BYTE_0(arg2) == 0x02:
> +         argr = UNKNOWN_CONSTANT
> +
> + if BYTE_0(arg2) == 0x03:
> +         if BYTE_1(arg2) == 0x00:
> +                 argr = FAN_ID_0
> +
> +         if BYTE_1(arg2) == 0x01:
> +                 argr = FAN_ID_1
> +
> +         if BYTE_1(arg2) == 0x02:
> +                 argr = FAN_ID_2
> +
> +         if BYTE_1(arg2) == 0x03:
> +                 argr = FAN_ID_3
> +
> +         if BYTE_1(arg2) == 0x04:
> +                 argr = SENSOR_ID_CPU | 0x0100
> +
> +         if BYTE_1(arg2) == 0x05:
> +                 argr = SENSOR_ID_GPU | 0x0100
> +
> +         if BYTE_1(arg2) == 0x06:
> +                 argr = THERMAL_MODE_QUIET_ID
> +
> +         if BYTE_1(arg2) == 0x07:
> +                 argr = THERMAL_MODE_BALANCED_ID
> +
> +         if BYTE_1(arg2) == 0x08:
> +                 argr = THERMAL_MODE_BALANCED_PERFORMANCE_ID
> +
> +         if BYTE_1(arg2) == 0x09:
> +                 argr = THERMAL_MODE_PERFORMANCE_ID
> +
> +         if BYTE_1(arg2) == 0x0A:
> +                 argr = THERMAL_MODE_LOW_POWER_ID
> +
> +         if BYTE_1(arg2) == 0x0B:
> +                 argr = THERMAL_MODE_GMODE_ID
> +
> +         else:
> +                 argr = 0xFFFFFFFF
> +
> + if BYTE_0(arg2) == 0x04:
> +         if is_valid_sensor(BYTE_1(arg2)):
> +                 argr = SENSOR_TEMP_C
> +         else:
> +                 argr = 0xFFFFFFFF
> +
> + if BYTE_0(arg2) == 0x05:
> +         if is_valid_fan(BYTE_1(arg2)):
> +                 argr = FAN_RPM()
> +
> + if BYTE_0(arg2) == 0x06:
> +         skip
> +
> + if BYTE_0(arg2) == 0x07:
> +         argr = 0
> +
> + If BYTE_0(arg2) == 0x08:
> +         if is_valid_fan(BYTE_1(arg2)):
> +                 argr = 0
> +         else:
> +                 argr = 0xFFFFFFFF
> +
> + if BYTE_0(arg2) == 0x09:
> +         if is_valid_fan(BYTE_1(arg2)):
> +                 argr = FAN_UNKNOWN_STAT_0()
> +
> +         else:
> +                 argr = 0xFFFFFFFF
> +
> + if BYTE_0(arg2) == 0x0A:
> +         argr = THERMAL_MODE_BALANCED_ID
> +
> + if BYTE_0(arg2) == 0x0B:
> +         argr = CURRENT_THERMAL_MODE()
> +
> + if BYTE_0(arg2) == 0x0C:
> +         if is_valid_fan(BYTE_1(arg2)):
> +                 argr = FAN_UNKNOWN_STAT_1()
> +         else:
> +                 argr = 0xFFFFFFFF
> +
> +WMI method Thermal_Control([in] uint32 arg2, [out] uint32 argr)
> +---------------------------------------------------------------
> +
> +::
> +
> + if BYTE_0(arg2) == 0x01:
> +         if is_valid_thermal_profile(BYTE_1(arg2)):
> +                 SET_THERMAL_PROFILE(BYTE_1(arg2))
> +                 argr = 0
> +
> + if BYTE_0(arg2) == 0x02:
> +         if is_valid_fan(BYTE_1(arg2)):
> +                 SET_FAN_SPEED_MULTIPLIER(BYTE_2(arg2))
> +                 argr = 0
> +         else:
> +                 argr = 0xFFFFFFFF
> +
> +.. note::
> +   While you can manually change the fan speed multiplier with this method,
> +   Dell's BIOS tends to overwrite this changes anyway.
> +
> +These are the known thermal profile codes:
> +
> +::
> +
> + CUSTOM                         0x00
> +
> + QUIET                          0x96
> + BALANCED                       0x97
> + BALANCED_PERFORMANCE           0x98
> + PERFORMANCE                    0x99
> +
> + QUIET_USTT                     0xA3
> + BALANCED_USTT                  0xA0
> + BALANCED_PERFORMANCE_USTT      0xA1
> + PERFORMANCE_USTT               0xA4
> + LOW_POWER_USTT                 0xA5
> +
> + GMODE                          0xAB
> +
> +Usually if a model doesn't support the first four profiles they will support
> +the User Selectable Thermal Tables (USTT) profiles and vice-versa.
> +
> +GMODE replaces PERFORMANCE in G-Series laptops.
> +
> +Very grateful to `AlexIII <https://github.com/AlexIII/tcc-g15>`_ for discovering
> +some of the codes compatible with G-Series laptops.

Maybe use some less personal wording to give kudos and perhaps put it into 
the end of the doc.

-- 
 i.


> +WMI method GetFanSensors([in] uint32 arg2, [out] uint32 argr)
> +-------------------------------------------------------------
> +
> +::
> +
> + if BYTE_0(arg2) == 1:
> +        if is_valid_fan(BYTE_1(arg2)):
> +                argr = 1
> +        else:
> +                argr = 0
> +
> + if BYTE_0(arg2) == 2:
> +        if is_valid_fan(BYTE_1(arg2)):
> +                if BYTE_2(arg2) == 0:
> +                        argr == SENSOR_ID
> +                else
> +                        argr == 0xFFFFFFFF
> +        else:
> +                argr = 0
> +
> +Overclocking Methods
> +====================
> +
> +.. warning::
> +   These methods have not been tested and are only partially reverse
> +   engineered.
> +
> +WMI method Return_OverclockingReport([out] uint32 argr)
> +-------------------------------------------------------
> +
> +::
> +
> + CSMI (0xE3, 0x99)
> + argr = 0
> +
> +CSMI is an unknown operation.
> +
> +WMI method Set_OCUIBIOSControl([in] uint32 arg2, [out] uint32 argr)
> +-------------------------------------------------------------------
> +
> +::
> +
> + CSMI (0xE3, 0x99)
> + argr = 0
> +
> +CSMI is an unknown operation
> +
> +WMI method Clear_OCFailSafeFlag([out] uint32 argr)
> +--------------------------------------------------
> +
> +::
> +
> + CSMI (0xE3, 0x99)
> + argr = 0
> +
> +CSMI is an unknown operation
> +
> +
> +WMI method MemoryOCControl([in] uint32 arg2, [out] uint32 argr)
> +---------------------------------------------------------------
> +
> +AWCC supports memory overclocking, but this method is very intricate and has
> +not been deciphered yet.
> +
> +GPIO methods
> +============
> +
> +These methods are probably related to some kind of firmware update system,
> +through a GPIO device.
> +
> +.. warning::
> +   These methods have not been tested and are only partially reverse
> +   engineered.
> +
> +WMI method FWUpdateGPIOtoggle([in] uint32 arg2, [out] uint32 argr)
> +------------------------------------------------------------------
> +
> +::
> +
> + if BYTE_0(arg2) == 0:
> +         if BYTE_1(arg2) == 1:
> +                 SET_PIN_A_HIGH()
> +         else:
> +                 SET_PIN_A_LOW()
> +
> + if BYTE_0(arg2) == 1:
> +         if BYTE_1(arg2) == 1:
> +                 SET_PIN_B_HIGH()
> +
> +         else:
> +                 SET_PIN_B_LOW()
> +
> + else:
> +         argr = 1
> +
> +WMI method ReadTotalofGPIOs([out] uint32 argr)
> +----------------------------------------------
> +
> +::
> +
> + argr = 0x02
> +
> +WMI method ReadGPIOpPinStatus([in] uint32 arg2, [out] uint32 argr)
> +------------------------------------------------------------------
> +
> +::
> +
> + if BYTE_0(arg2) == 0:
> +         argr = PIN_A_STATUS
> +
> + if BYTE_0(arg2) == 1:
> +         argr = PIN_B_STATUS
> +
> +
> +Other information Methods
> +=========================
> +
> +WMI method SystemInformation([in] uint32 arg2, [out] uint32 argr)
> +-----------------------------------------------------------------
> +
> +Returns unknown information.
> +
> +WMI method PowerInformation([in] uint32 arg2, [out] uint32 argr)
> +----------------------------------------------------------------
> +
> +Returns unknown information.
> +
> +WMI method ReadChassisColor([out] uint32 argr)
> +----------------------------------------------
> +
> +::
> +
> + argr = CHASSIS_COLOR_ID
> +
> +WMI method ReadPlatformProperties([out] uint32 argr)
> +----------------------------------------------------
> +
> +Returns unknown information.
> +
>
[PATCH v5 0/4] Dell AWCC platform_profile support
Posted by Kurt Borja 1 month, 2 weeks ago
This patch adds platform_profile support for Dell devices which implement
User Selectable Thermal Tables (USTT) that are meant to be controlled by
Alienware Command Center (AWCC). These devices may include newer Alienware
M-Series, Alienware X-Series and Dell's G-Series. This patch, was tested
by me on an Alienware x15 R1.

---
v5:
 - Better commit messages
 - insize renamed to in_size in alienware_wmax_command() to match other
   arguments.
 - Kudos in documentation now at the end of the file
v4:
 - Fixed indentation on previous code
 - Removed unnecessary (acpi_size) and (u32 *) casts
 - Return -EIO on ACPI_FAILURE
 - Appropiate prefixes given to macros
 - 0xFFFFFFFF named WMAX_FAILURE_CODE
 - Added support for a new set of thermal codes. Old ones now have USTT
   in their names
 - A new quirk has been added to differantiate between the two sets.
   thermal and thermal_ustt are mutually exclusive
 - Added documentation for WMAX interface
v3:
 - Removed extra empty line
 - 0x0B named WMAX_ARG_GET_CURRENT_PROF
 - Removed casts to the same type on functions added in this patch
 - Thermal profile to WMAX argument is now an static function and makes
   use of in-built kernel macros
 - Platform profile is now removed only if it was created first
 - create_platform_profile is now create_thermal_profile to avoid
   confusion
 - profile_get and profile_set functions renamed too to match the above
v2:
 - Moved functionality to alienware-wmi driver
 - Added thermal and gmode quirks to add support based on dmi match
 - Performance profile is now GMODE for devices that support it
 - alienware_wmax_command now is insize agnostic to support new thermal
   methods

Kurt Borja (4):
  alienware-wmi: fixed indentation and clean up
  alienware-wmi: alienware_wmax_command() is now input size agnostic
  alienware-wmi: added platform profile support
  alienware-wmi: WMAX interface documentation

 Documentation/wmi/devices/alienware-wmi.rst | 366 ++++++++++++++++++
 drivers/platform/x86/dell/Kconfig           |   1 +
 drivers/platform/x86/dell/alienware-wmi.c   | 389 ++++++++++++++++----
 3 files changed, 680 insertions(+), 76 deletions(-)
 create mode 100644 Documentation/wmi/devices/alienware-wmi.rst

-- 
2.47.0
[PATCH v5 1/4] alienware-wmi: fixed indentation and clean up
Posted by Kurt Borja 1 month, 2 weeks ago
Fixed inconsistent indentation and removed unnecessary (acpi_size) and
(u32 *) casts.

Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
 drivers/platform/x86/dell/alienware-wmi.c | 134 +++++++++++-----------
 1 file changed, 67 insertions(+), 67 deletions(-)

diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
index f5ee62ce1..16a3fe9ac 100644
--- a/drivers/platform/x86/dell/alienware-wmi.c
+++ b/drivers/platform/x86/dell/alienware-wmi.c
@@ -116,68 +116,68 @@ static int __init dmi_matched(const struct dmi_system_id *dmi)
 
 static const struct dmi_system_id alienware_quirks[] __initconst = {
 	{
-	 .callback = dmi_matched,
-	 .ident = "Alienware X51 R3",
-	 .matches = {
-		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
-		     DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"),
-		     },
-	 .driver_data = &quirk_x51_r3,
-	 },
+		.callback = dmi_matched,
+		.ident = "Alienware X51 R3",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"),
+		},
+		.driver_data = &quirk_x51_r3,
+	},
 	{
-	 .callback = dmi_matched,
-	 .ident = "Alienware X51 R2",
-	 .matches = {
-		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
-		     DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"),
-		     },
-	 .driver_data = &quirk_x51_r1_r2,
-	 },
+		.callback = dmi_matched,
+		.ident = "Alienware X51 R2",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"),
+		},
+		.driver_data = &quirk_x51_r1_r2,
+	},
 	{
-	 .callback = dmi_matched,
-	 .ident = "Alienware X51 R1",
-	 .matches = {
-		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
-		     DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"),
-		     },
-	 .driver_data = &quirk_x51_r1_r2,
-	 },
+		.callback = dmi_matched,
+		.ident = "Alienware X51 R1",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"),
+		},
+		.driver_data = &quirk_x51_r1_r2,
+	},
 	{
-	 .callback = dmi_matched,
-	 .ident = "Alienware ASM100",
-	 .matches = {
-		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
-		     DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"),
-		     },
-	 .driver_data = &quirk_asm100,
-	 },
+		.callback = dmi_matched,
+		.ident = "Alienware ASM100",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"),
+		},
+		.driver_data = &quirk_asm100,
+	},
 	{
-	 .callback = dmi_matched,
-	 .ident = "Alienware ASM200",
-	 .matches = {
-		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
-		     DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"),
-		     },
-	 .driver_data = &quirk_asm200,
-	 },
+		.callback = dmi_matched,
+		.ident = "Alienware ASM200",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"),
+		},
+		.driver_data = &quirk_asm200,
+	},
 	{
-	 .callback = dmi_matched,
-	 .ident = "Alienware ASM201",
-	 .matches = {
-		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
-		     DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"),
-		     },
-	 .driver_data = &quirk_asm201,
-	 },
-	 {
-	 .callback = dmi_matched,
-	 .ident = "Dell Inc. Inspiron 5675",
-	 .matches = {
-		     DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
-		     DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"),
-		     },
-	 .driver_data = &quirk_inspiron5675,
-	 },
+		.callback = dmi_matched,
+		.ident = "Alienware ASM201",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"),
+		},
+		.driver_data = &quirk_asm201,
+	},
+	{
+		.callback = dmi_matched,
+		.ident = "Dell Inc. Inspiron 5675",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"),
+		},
+		.driver_data = &quirk_inspiron5675,
+	},
 	{}
 };
 
@@ -221,8 +221,8 @@ static struct platform_zone *zone_data;
 
 static struct platform_driver platform_driver = {
 	.driver = {
-		   .name = "alienware-wmi",
-		   }
+		.name = "alienware-wmi",
+	}
 };
 
 static struct attribute_group zone_attribute_group = {
@@ -292,7 +292,7 @@ static int alienware_update_led(struct platform_zone *zone)
 		guid = WMAX_CONTROL_GUID;
 		method_id = WMAX_METHOD_ZONE_CONTROL;
 
-		input.length = (acpi_size) sizeof(wmax_basic_args);
+		input.length = sizeof(wmax_basic_args);
 		input.pointer = &wmax_basic_args;
 	} else {
 		legacy_args.colors = zone->colors;
@@ -306,7 +306,7 @@ static int alienware_update_led(struct platform_zone *zone)
 			guid = LEGACY_CONTROL_GUID;
 		method_id = zone->location + 1;
 
-		input.length = (acpi_size) sizeof(legacy_args);
+		input.length = sizeof(legacy_args);
 		input.pointer = &legacy_args;
 	}
 	pr_debug("alienware-wmi: guid %s method %d\n", guid, method_id);
@@ -358,7 +358,7 @@ static int wmax_brightness(int brightness)
 		.led_mask = 0xFF,
 		.percentage = brightness,
 	};
-	input.length = (acpi_size) sizeof(args);
+	input.length = sizeof(args);
 	input.pointer = &args;
 	status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0,
 				     WMAX_METHOD_BRIGHTNESS, &input, NULL);
@@ -508,7 +508,7 @@ static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
 	struct acpi_buffer input;
 	struct acpi_buffer output;
 
-	input.length = (acpi_size) sizeof(*in_args);
+	input.length = sizeof(*in_args);
 	input.pointer = in_args;
 	if (out_data) {
 		output.length = ACPI_ALLOCATE_BUFFER;
@@ -542,7 +542,7 @@ static ssize_t show_hdmi_cable(struct device *dev,
 	};
 	status =
 	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE,
-				   (u32 *) &out_data);
+				   &out_data);
 	if (ACPI_SUCCESS(status)) {
 		if (out_data == 0)
 			return sysfs_emit(buf, "[unconnected] connected unknown\n");
@@ -563,7 +563,7 @@ static ssize_t show_hdmi_source(struct device *dev,
 	};
 	status =
 	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS,
-				   (u32 *) &out_data);
+				   &out_data);
 
 	if (ACPI_SUCCESS(status)) {
 		if (out_data == 1)
@@ -643,7 +643,7 @@ static ssize_t show_amplifier_status(struct device *dev,
 	};
 	status =
 	    alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE,
-				   (u32 *) &out_data);
+				   &out_data);
 	if (ACPI_SUCCESS(status)) {
 		if (out_data == 0)
 			return sysfs_emit(buf, "[unconnected] connected unknown\n");
@@ -695,7 +695,7 @@ static ssize_t show_deepsleep_status(struct device *dev,
 		.arg = 0,
 	};
 	status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS,
-					(u32 *) &out_data);
+					&out_data);
 	if (ACPI_SUCCESS(status)) {
 		if (out_data == 0)
 			return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
-- 
2.47.0
Re: [PATCH v5 1/4] alienware-wmi: fixed indentation and clean up
Posted by Armin Wolf 1 month, 2 weeks ago
Am 12.10.24 um 04:01 schrieb Kurt Borja:

> Fixed inconsistent indentation and removed unnecessary (acpi_size) and
> (u32 *) casts.

Reviewed-by: Armin Wolf <W_Armin@gmx.de

>
> Signed-off-by: Kurt Borja <kuurtb@gmail.com>
> ---
>   drivers/platform/x86/dell/alienware-wmi.c | 134 +++++++++++-----------
>   1 file changed, 67 insertions(+), 67 deletions(-)
>
> diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
> index f5ee62ce1..16a3fe9ac 100644
> --- a/drivers/platform/x86/dell/alienware-wmi.c
> +++ b/drivers/platform/x86/dell/alienware-wmi.c
> @@ -116,68 +116,68 @@ static int __init dmi_matched(const struct dmi_system_id *dmi)
>
>   static const struct dmi_system_id alienware_quirks[] __initconst = {
>   	{
> -	 .callback = dmi_matched,
> -	 .ident = "Alienware X51 R3",
> -	 .matches = {
> -		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> -		     DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"),
> -		     },
> -	 .driver_data = &quirk_x51_r3,
> -	 },
> +		.callback = dmi_matched,
> +		.ident = "Alienware X51 R3",
> +		.matches = {
> +			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> +			DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"),
> +		},
> +		.driver_data = &quirk_x51_r3,
> +	},
>   	{
> -	 .callback = dmi_matched,
> -	 .ident = "Alienware X51 R2",
> -	 .matches = {
> -		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> -		     DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"),
> -		     },
> -	 .driver_data = &quirk_x51_r1_r2,
> -	 },
> +		.callback = dmi_matched,
> +		.ident = "Alienware X51 R2",
> +		.matches = {
> +			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> +			DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"),
> +		},
> +		.driver_data = &quirk_x51_r1_r2,
> +	},
>   	{
> -	 .callback = dmi_matched,
> -	 .ident = "Alienware X51 R1",
> -	 .matches = {
> -		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> -		     DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"),
> -		     },
> -	 .driver_data = &quirk_x51_r1_r2,
> -	 },
> +		.callback = dmi_matched,
> +		.ident = "Alienware X51 R1",
> +		.matches = {
> +			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> +			DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"),
> +		},
> +		.driver_data = &quirk_x51_r1_r2,
> +	},
>   	{
> -	 .callback = dmi_matched,
> -	 .ident = "Alienware ASM100",
> -	 .matches = {
> -		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> -		     DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"),
> -		     },
> -	 .driver_data = &quirk_asm100,
> -	 },
> +		.callback = dmi_matched,
> +		.ident = "Alienware ASM100",
> +		.matches = {
> +			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> +			DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"),
> +		},
> +		.driver_data = &quirk_asm100,
> +	},
>   	{
> -	 .callback = dmi_matched,
> -	 .ident = "Alienware ASM200",
> -	 .matches = {
> -		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> -		     DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"),
> -		     },
> -	 .driver_data = &quirk_asm200,
> -	 },
> +		.callback = dmi_matched,
> +		.ident = "Alienware ASM200",
> +		.matches = {
> +			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> +			DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"),
> +		},
> +		.driver_data = &quirk_asm200,
> +	},
>   	{
> -	 .callback = dmi_matched,
> -	 .ident = "Alienware ASM201",
> -	 .matches = {
> -		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> -		     DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"),
> -		     },
> -	 .driver_data = &quirk_asm201,
> -	 },
> -	 {
> -	 .callback = dmi_matched,
> -	 .ident = "Dell Inc. Inspiron 5675",
> -	 .matches = {
> -		     DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
> -		     DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"),
> -		     },
> -	 .driver_data = &quirk_inspiron5675,
> -	 },
> +		.callback = dmi_matched,
> +		.ident = "Alienware ASM201",
> +		.matches = {
> +			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> +			DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"),
> +		},
> +		.driver_data = &quirk_asm201,
> +	},
> +	{
> +		.callback = dmi_matched,
> +		.ident = "Dell Inc. Inspiron 5675",
> +		.matches = {
> +			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
> +			DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"),
> +		},
> +		.driver_data = &quirk_inspiron5675,
> +	},
>   	{}
>   };
>
> @@ -221,8 +221,8 @@ static struct platform_zone *zone_data;
>
>   static struct platform_driver platform_driver = {
>   	.driver = {
> -		   .name = "alienware-wmi",
> -		   }
> +		.name = "alienware-wmi",
> +	}
>   };
>
>   static struct attribute_group zone_attribute_group = {
> @@ -292,7 +292,7 @@ static int alienware_update_led(struct platform_zone *zone)
>   		guid = WMAX_CONTROL_GUID;
>   		method_id = WMAX_METHOD_ZONE_CONTROL;
>
> -		input.length = (acpi_size) sizeof(wmax_basic_args);
> +		input.length = sizeof(wmax_basic_args);
>   		input.pointer = &wmax_basic_args;
>   	} else {
>   		legacy_args.colors = zone->colors;
> @@ -306,7 +306,7 @@ static int alienware_update_led(struct platform_zone *zone)
>   			guid = LEGACY_CONTROL_GUID;
>   		method_id = zone->location + 1;
>
> -		input.length = (acpi_size) sizeof(legacy_args);
> +		input.length = sizeof(legacy_args);
>   		input.pointer = &legacy_args;
>   	}
>   	pr_debug("alienware-wmi: guid %s method %d\n", guid, method_id);
> @@ -358,7 +358,7 @@ static int wmax_brightness(int brightness)
>   		.led_mask = 0xFF,
>   		.percentage = brightness,
>   	};
> -	input.length = (acpi_size) sizeof(args);
> +	input.length = sizeof(args);
>   	input.pointer = &args;
>   	status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0,
>   				     WMAX_METHOD_BRIGHTNESS, &input, NULL);
> @@ -508,7 +508,7 @@ static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
>   	struct acpi_buffer input;
>   	struct acpi_buffer output;
>
> -	input.length = (acpi_size) sizeof(*in_args);
> +	input.length = sizeof(*in_args);
>   	input.pointer = in_args;
>   	if (out_data) {
>   		output.length = ACPI_ALLOCATE_BUFFER;
> @@ -542,7 +542,7 @@ static ssize_t show_hdmi_cable(struct device *dev,
>   	};
>   	status =
>   	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE,
> -				   (u32 *) &out_data);
> +				   &out_data);
>   	if (ACPI_SUCCESS(status)) {
>   		if (out_data == 0)
>   			return sysfs_emit(buf, "[unconnected] connected unknown\n");
> @@ -563,7 +563,7 @@ static ssize_t show_hdmi_source(struct device *dev,
>   	};
>   	status =
>   	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS,
> -				   (u32 *) &out_data);
> +				   &out_data);
>
>   	if (ACPI_SUCCESS(status)) {
>   		if (out_data == 1)
> @@ -643,7 +643,7 @@ static ssize_t show_amplifier_status(struct device *dev,
>   	};
>   	status =
>   	    alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE,
> -				   (u32 *) &out_data);
> +				   &out_data);
>   	if (ACPI_SUCCESS(status)) {
>   		if (out_data == 0)
>   			return sysfs_emit(buf, "[unconnected] connected unknown\n");
> @@ -695,7 +695,7 @@ static ssize_t show_deepsleep_status(struct device *dev,
>   		.arg = 0,
>   	};
>   	status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS,
> -					(u32 *) &out_data);
> +					&out_data);
>   	if (ACPI_SUCCESS(status)) {
>   		if (out_data == 0)
>   			return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
Re: [PATCH v5 1/4] alienware-wmi: fixed indentation and clean up
Posted by Kurt Borja 1 month, 1 week ago
On Mon, Oct 14, 2024 at 06:26:45PM +0200, Armin Wolf wrote:
> Am 12.10.24 um 04:01 schrieb Kurt Borja:
> 
> > Fixed inconsistent indentation and removed unnecessary (acpi_size) and
> > (u32 *) casts.
> 
> Reviewed-by: Armin Wolf <W_Armin@gmx.de
> 

Thank you for your review.

Kurt

> > 
> > Signed-off-by: Kurt Borja <kuurtb@gmail.com>
> > ---
> >   drivers/platform/x86/dell/alienware-wmi.c | 134 +++++++++++-----------
> >   1 file changed, 67 insertions(+), 67 deletions(-)
> > 
> > diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
> > index f5ee62ce1..16a3fe9ac 100644
> > --- a/drivers/platform/x86/dell/alienware-wmi.c
> > +++ b/drivers/platform/x86/dell/alienware-wmi.c
> > @@ -116,68 +116,68 @@ static int __init dmi_matched(const struct dmi_system_id *dmi)
> > 
> >   static const struct dmi_system_id alienware_quirks[] __initconst = {
> >   	{
> > -	 .callback = dmi_matched,
> > -	 .ident = "Alienware X51 R3",
> > -	 .matches = {
> > -		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> > -		     DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"),
> > -		     },
> > -	 .driver_data = &quirk_x51_r3,
> > -	 },
> > +		.callback = dmi_matched,
> > +		.ident = "Alienware X51 R3",
> > +		.matches = {
> > +			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> > +			DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"),
> > +		},
> > +		.driver_data = &quirk_x51_r3,
> > +	},
> >   	{
> > -	 .callback = dmi_matched,
> > -	 .ident = "Alienware X51 R2",
> > -	 .matches = {
> > -		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> > -		     DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"),
> > -		     },
> > -	 .driver_data = &quirk_x51_r1_r2,
> > -	 },
> > +		.callback = dmi_matched,
> > +		.ident = "Alienware X51 R2",
> > +		.matches = {
> > +			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> > +			DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"),
> > +		},
> > +		.driver_data = &quirk_x51_r1_r2,
> > +	},
> >   	{
> > -	 .callback = dmi_matched,
> > -	 .ident = "Alienware X51 R1",
> > -	 .matches = {
> > -		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> > -		     DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"),
> > -		     },
> > -	 .driver_data = &quirk_x51_r1_r2,
> > -	 },
> > +		.callback = dmi_matched,
> > +		.ident = "Alienware X51 R1",
> > +		.matches = {
> > +			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> > +			DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"),
> > +		},
> > +		.driver_data = &quirk_x51_r1_r2,
> > +	},
> >   	{
> > -	 .callback = dmi_matched,
> > -	 .ident = "Alienware ASM100",
> > -	 .matches = {
> > -		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> > -		     DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"),
> > -		     },
> > -	 .driver_data = &quirk_asm100,
> > -	 },
> > +		.callback = dmi_matched,
> > +		.ident = "Alienware ASM100",
> > +		.matches = {
> > +			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> > +			DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"),
> > +		},
> > +		.driver_data = &quirk_asm100,
> > +	},
> >   	{
> > -	 .callback = dmi_matched,
> > -	 .ident = "Alienware ASM200",
> > -	 .matches = {
> > -		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> > -		     DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"),
> > -		     },
> > -	 .driver_data = &quirk_asm200,
> > -	 },
> > +		.callback = dmi_matched,
> > +		.ident = "Alienware ASM200",
> > +		.matches = {
> > +			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> > +			DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"),
> > +		},
> > +		.driver_data = &quirk_asm200,
> > +	},
> >   	{
> > -	 .callback = dmi_matched,
> > -	 .ident = "Alienware ASM201",
> > -	 .matches = {
> > -		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> > -		     DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"),
> > -		     },
> > -	 .driver_data = &quirk_asm201,
> > -	 },
> > -	 {
> > -	 .callback = dmi_matched,
> > -	 .ident = "Dell Inc. Inspiron 5675",
> > -	 .matches = {
> > -		     DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
> > -		     DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"),
> > -		     },
> > -	 .driver_data = &quirk_inspiron5675,
> > -	 },
> > +		.callback = dmi_matched,
> > +		.ident = "Alienware ASM201",
> > +		.matches = {
> > +			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> > +			DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"),
> > +		},
> > +		.driver_data = &quirk_asm201,
> > +	},
> > +	{
> > +		.callback = dmi_matched,
> > +		.ident = "Dell Inc. Inspiron 5675",
> > +		.matches = {
> > +			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
> > +			DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"),
> > +		},
> > +		.driver_data = &quirk_inspiron5675,
> > +	},
> >   	{}
> >   };
> > 
> > @@ -221,8 +221,8 @@ static struct platform_zone *zone_data;
> > 
> >   static struct platform_driver platform_driver = {
> >   	.driver = {
> > -		   .name = "alienware-wmi",
> > -		   }
> > +		.name = "alienware-wmi",
> > +	}
> >   };
> > 
> >   static struct attribute_group zone_attribute_group = {
> > @@ -292,7 +292,7 @@ static int alienware_update_led(struct platform_zone *zone)
> >   		guid = WMAX_CONTROL_GUID;
> >   		method_id = WMAX_METHOD_ZONE_CONTROL;
> > 
> > -		input.length = (acpi_size) sizeof(wmax_basic_args);
> > +		input.length = sizeof(wmax_basic_args);
> >   		input.pointer = &wmax_basic_args;
> >   	} else {
> >   		legacy_args.colors = zone->colors;
> > @@ -306,7 +306,7 @@ static int alienware_update_led(struct platform_zone *zone)
> >   			guid = LEGACY_CONTROL_GUID;
> >   		method_id = zone->location + 1;
> > 
> > -		input.length = (acpi_size) sizeof(legacy_args);
> > +		input.length = sizeof(legacy_args);
> >   		input.pointer = &legacy_args;
> >   	}
> >   	pr_debug("alienware-wmi: guid %s method %d\n", guid, method_id);
> > @@ -358,7 +358,7 @@ static int wmax_brightness(int brightness)
> >   		.led_mask = 0xFF,
> >   		.percentage = brightness,
> >   	};
> > -	input.length = (acpi_size) sizeof(args);
> > +	input.length = sizeof(args);
> >   	input.pointer = &args;
> >   	status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0,
> >   				     WMAX_METHOD_BRIGHTNESS, &input, NULL);
> > @@ -508,7 +508,7 @@ static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
> >   	struct acpi_buffer input;
> >   	struct acpi_buffer output;
> > 
> > -	input.length = (acpi_size) sizeof(*in_args);
> > +	input.length = sizeof(*in_args);
> >   	input.pointer = in_args;
> >   	if (out_data) {
> >   		output.length = ACPI_ALLOCATE_BUFFER;
> > @@ -542,7 +542,7 @@ static ssize_t show_hdmi_cable(struct device *dev,
> >   	};
> >   	status =
> >   	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE,
> > -				   (u32 *) &out_data);
> > +				   &out_data);
> >   	if (ACPI_SUCCESS(status)) {
> >   		if (out_data == 0)
> >   			return sysfs_emit(buf, "[unconnected] connected unknown\n");
> > @@ -563,7 +563,7 @@ static ssize_t show_hdmi_source(struct device *dev,
> >   	};
> >   	status =
> >   	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS,
> > -				   (u32 *) &out_data);
> > +				   &out_data);
> > 
> >   	if (ACPI_SUCCESS(status)) {
> >   		if (out_data == 1)
> > @@ -643,7 +643,7 @@ static ssize_t show_amplifier_status(struct device *dev,
> >   	};
> >   	status =
> >   	    alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE,
> > -				   (u32 *) &out_data);
> > +				   &out_data);
> >   	if (ACPI_SUCCESS(status)) {
> >   		if (out_data == 0)
> >   			return sysfs_emit(buf, "[unconnected] connected unknown\n");
> > @@ -695,7 +695,7 @@ static ssize_t show_deepsleep_status(struct device *dev,
> >   		.arg = 0,
> >   	};
> >   	status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS,
> > -					(u32 *) &out_data);
> > +					&out_data);
> >   	if (ACPI_SUCCESS(status)) {
> >   		if (out_data == 0)
> >   			return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
[PATCH v5 2/4] alienware-wmi: alienware_wmax_command() is now input size agnostic
Posted by Kurt Borja 1 month, 2 weeks ago
alienware_wmax_command() now takes void * and size_t instead of struct
wmax_basic_args to extend support to new WMAX methods. Also int *out_data
was changed to u32 *out_data, because new interface specifies u32 as output
parameter and all previous callers would pass u32 * regardless.

Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
 drivers/platform/x86/dell/alienware-wmi.c | 29 ++++++++++++-----------
 1 file changed, 15 insertions(+), 14 deletions(-)

diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
index 16a3fe9ac..b27f3b64c 100644
--- a/drivers/platform/x86/dell/alienware-wmi.c
+++ b/drivers/platform/x86/dell/alienware-wmi.c
@@ -500,15 +500,15 @@ static void alienware_zone_exit(struct platform_device *dev)
 	kfree(zone_attrs);
 }
 
-static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
-					  u32 command, int *out_data)
+static acpi_status alienware_wmax_command(void *in_args, size_t in_size,
+					  u32 command, u32 *out_data)
 {
 	acpi_status status;
 	union acpi_object *obj;
 	struct acpi_buffer input;
 	struct acpi_buffer output;
 
-	input.length = sizeof(*in_args);
+	input.length = in_size;
 	input.pointer = in_args;
 	if (out_data) {
 		output.length = ACPI_ALLOCATE_BUFFER;
@@ -541,8 +541,8 @@ static ssize_t show_hdmi_cable(struct device *dev,
 		.arg = 0,
 	};
 	status =
-	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE,
-				   &out_data);
+	    alienware_wmax_command(&in_args, sizeof(in_args),
+				   WMAX_METHOD_HDMI_CABLE, &out_data);
 	if (ACPI_SUCCESS(status)) {
 		if (out_data == 0)
 			return sysfs_emit(buf, "[unconnected] connected unknown\n");
@@ -562,8 +562,8 @@ static ssize_t show_hdmi_source(struct device *dev,
 		.arg = 0,
 	};
 	status =
-	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS,
-				   &out_data);
+	    alienware_wmax_command(&in_args, sizeof(in_args),
+				   WMAX_METHOD_HDMI_STATUS, &out_data);
 
 	if (ACPI_SUCCESS(status)) {
 		if (out_data == 1)
@@ -589,7 +589,8 @@ static ssize_t toggle_hdmi_source(struct device *dev,
 		args.arg = 3;
 	pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf);
 
-	status = alienware_wmax_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL);
+	status = alienware_wmax_command(&args, sizeof(args),
+					WMAX_METHOD_HDMI_SOURCE, NULL);
 
 	if (ACPI_FAILURE(status))
 		pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
@@ -642,8 +643,8 @@ static ssize_t show_amplifier_status(struct device *dev,
 		.arg = 0,
 	};
 	status =
-	    alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE,
-				   &out_data);
+	    alienware_wmax_command(&in_args, sizeof(in_args),
+				   WMAX_METHOD_AMPLIFIER_CABLE, &out_data);
 	if (ACPI_SUCCESS(status)) {
 		if (out_data == 0)
 			return sysfs_emit(buf, "[unconnected] connected unknown\n");
@@ -694,8 +695,8 @@ static ssize_t show_deepsleep_status(struct device *dev,
 	struct wmax_basic_args in_args = {
 		.arg = 0,
 	};
-	status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS,
-					&out_data);
+	status = alienware_wmax_command(&in_args, sizeof(in_args),
+					WMAX_METHOD_DEEP_SLEEP_STATUS, &out_data);
 	if (ACPI_SUCCESS(status)) {
 		if (out_data == 0)
 			return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
@@ -723,8 +724,8 @@ static ssize_t toggle_deepsleep(struct device *dev,
 		args.arg = 2;
 	pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf);
 
-	status = alienware_wmax_command(&args, WMAX_METHOD_DEEP_SLEEP_CONTROL,
-					NULL);
+	status = alienware_wmax_command(&args, sizeof(args),
+					WMAX_METHOD_DEEP_SLEEP_CONTROL, NULL);
 
 	if (ACPI_FAILURE(status))
 		pr_err("alienware-wmi: deep sleep control failed: results: %u\n",
-- 
2.47.0
Re: [PATCH v5 2/4] alienware-wmi: alienware_wmax_command() is now input size agnostic
Posted by Armin Wolf 1 month, 2 weeks ago
Am 12.10.24 um 04:01 schrieb Kurt Borja:

> alienware_wmax_command() now takes void * and size_t instead of struct
> wmax_basic_args to extend support to new WMAX methods. Also int *out_data
> was changed to u32 *out_data, because new interface specifies u32 as output
> parameter and all previous callers would pass u32 * regardless.

Reviewed-by: Armin Wolf <W_Armin@gmx.de>

> Signed-off-by: Kurt Borja <kuurtb@gmail.com>
> ---
>   drivers/platform/x86/dell/alienware-wmi.c | 29 ++++++++++++-----------
>   1 file changed, 15 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
> index 16a3fe9ac..b27f3b64c 100644
> --- a/drivers/platform/x86/dell/alienware-wmi.c
> +++ b/drivers/platform/x86/dell/alienware-wmi.c
> @@ -500,15 +500,15 @@ static void alienware_zone_exit(struct platform_device *dev)
>   	kfree(zone_attrs);
>   }
>
> -static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
> -					  u32 command, int *out_data)
> +static acpi_status alienware_wmax_command(void *in_args, size_t in_size,
> +					  u32 command, u32 *out_data)
>   {
>   	acpi_status status;
>   	union acpi_object *obj;
>   	struct acpi_buffer input;
>   	struct acpi_buffer output;
>
> -	input.length = sizeof(*in_args);
> +	input.length = in_size;
>   	input.pointer = in_args;
>   	if (out_data) {
>   		output.length = ACPI_ALLOCATE_BUFFER;
> @@ -541,8 +541,8 @@ static ssize_t show_hdmi_cable(struct device *dev,
>   		.arg = 0,
>   	};
>   	status =
> -	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE,
> -				   &out_data);
> +	    alienware_wmax_command(&in_args, sizeof(in_args),
> +				   WMAX_METHOD_HDMI_CABLE, &out_data);
>   	if (ACPI_SUCCESS(status)) {
>   		if (out_data == 0)
>   			return sysfs_emit(buf, "[unconnected] connected unknown\n");
> @@ -562,8 +562,8 @@ static ssize_t show_hdmi_source(struct device *dev,
>   		.arg = 0,
>   	};
>   	status =
> -	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS,
> -				   &out_data);
> +	    alienware_wmax_command(&in_args, sizeof(in_args),
> +				   WMAX_METHOD_HDMI_STATUS, &out_data);
>
>   	if (ACPI_SUCCESS(status)) {
>   		if (out_data == 1)
> @@ -589,7 +589,8 @@ static ssize_t toggle_hdmi_source(struct device *dev,
>   		args.arg = 3;
>   	pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf);
>
> -	status = alienware_wmax_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL);
> +	status = alienware_wmax_command(&args, sizeof(args),
> +					WMAX_METHOD_HDMI_SOURCE, NULL);
>
>   	if (ACPI_FAILURE(status))
>   		pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
> @@ -642,8 +643,8 @@ static ssize_t show_amplifier_status(struct device *dev,
>   		.arg = 0,
>   	};
>   	status =
> -	    alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE,
> -				   &out_data);
> +	    alienware_wmax_command(&in_args, sizeof(in_args),
> +				   WMAX_METHOD_AMPLIFIER_CABLE, &out_data);
>   	if (ACPI_SUCCESS(status)) {
>   		if (out_data == 0)
>   			return sysfs_emit(buf, "[unconnected] connected unknown\n");
> @@ -694,8 +695,8 @@ static ssize_t show_deepsleep_status(struct device *dev,
>   	struct wmax_basic_args in_args = {
>   		.arg = 0,
>   	};
> -	status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS,
> -					&out_data);
> +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> +					WMAX_METHOD_DEEP_SLEEP_STATUS, &out_data);
>   	if (ACPI_SUCCESS(status)) {
>   		if (out_data == 0)
>   			return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
> @@ -723,8 +724,8 @@ static ssize_t toggle_deepsleep(struct device *dev,
>   		args.arg = 2;
>   	pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf);
>
> -	status = alienware_wmax_command(&args, WMAX_METHOD_DEEP_SLEEP_CONTROL,
> -					NULL);
> +	status = alienware_wmax_command(&args, sizeof(args),
> +					WMAX_METHOD_DEEP_SLEEP_CONTROL, NULL);
>
>   	if (ACPI_FAILURE(status))
>   		pr_err("alienware-wmi: deep sleep control failed: results: %u\n",
Re: [PATCH v5 2/4] alienware-wmi: alienware_wmax_command() is now input size agnostic
Posted by Kurt Borja 1 month, 1 week ago
On Mon, Oct 14, 2024 at 06:30:21PM +0200, Armin Wolf wrote:
> Am 12.10.24 um 04:01 schrieb Kurt Borja:
> 
> > alienware_wmax_command() now takes void * and size_t instead of struct
> > wmax_basic_args to extend support to new WMAX methods. Also int *out_data
> > was changed to u32 *out_data, because new interface specifies u32 as output
> > parameter and all previous callers would pass u32 * regardless.
> 
> Reviewed-by: Armin Wolf <W_Armin@gmx.de>
> 

Thank you very much.

Kurt

> > Signed-off-by: Kurt Borja <kuurtb@gmail.com>
> > ---
> >   drivers/platform/x86/dell/alienware-wmi.c | 29 ++++++++++++-----------
> >   1 file changed, 15 insertions(+), 14 deletions(-)
> > 
> > diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
> > index 16a3fe9ac..b27f3b64c 100644
> > --- a/drivers/platform/x86/dell/alienware-wmi.c
> > +++ b/drivers/platform/x86/dell/alienware-wmi.c
> > @@ -500,15 +500,15 @@ static void alienware_zone_exit(struct platform_device *dev)
> >   	kfree(zone_attrs);
> >   }
> > 
> > -static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
> > -					  u32 command, int *out_data)
> > +static acpi_status alienware_wmax_command(void *in_args, size_t in_size,
> > +					  u32 command, u32 *out_data)
> >   {
> >   	acpi_status status;
> >   	union acpi_object *obj;
> >   	struct acpi_buffer input;
> >   	struct acpi_buffer output;
> > 
> > -	input.length = sizeof(*in_args);
> > +	input.length = in_size;
> >   	input.pointer = in_args;
> >   	if (out_data) {
> >   		output.length = ACPI_ALLOCATE_BUFFER;
> > @@ -541,8 +541,8 @@ static ssize_t show_hdmi_cable(struct device *dev,
> >   		.arg = 0,
> >   	};
> >   	status =
> > -	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE,
> > -				   &out_data);
> > +	    alienware_wmax_command(&in_args, sizeof(in_args),
> > +				   WMAX_METHOD_HDMI_CABLE, &out_data);
> >   	if (ACPI_SUCCESS(status)) {
> >   		if (out_data == 0)
> >   			return sysfs_emit(buf, "[unconnected] connected unknown\n");
> > @@ -562,8 +562,8 @@ static ssize_t show_hdmi_source(struct device *dev,
> >   		.arg = 0,
> >   	};
> >   	status =
> > -	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS,
> > -				   &out_data);
> > +	    alienware_wmax_command(&in_args, sizeof(in_args),
> > +				   WMAX_METHOD_HDMI_STATUS, &out_data);
> > 
> >   	if (ACPI_SUCCESS(status)) {
> >   		if (out_data == 1)
> > @@ -589,7 +589,8 @@ static ssize_t toggle_hdmi_source(struct device *dev,
> >   		args.arg = 3;
> >   	pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf);
> > 
> > -	status = alienware_wmax_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL);
> > +	status = alienware_wmax_command(&args, sizeof(args),
> > +					WMAX_METHOD_HDMI_SOURCE, NULL);
> > 
> >   	if (ACPI_FAILURE(status))
> >   		pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
> > @@ -642,8 +643,8 @@ static ssize_t show_amplifier_status(struct device *dev,
> >   		.arg = 0,
> >   	};
> >   	status =
> > -	    alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE,
> > -				   &out_data);
> > +	    alienware_wmax_command(&in_args, sizeof(in_args),
> > +				   WMAX_METHOD_AMPLIFIER_CABLE, &out_data);
> >   	if (ACPI_SUCCESS(status)) {
> >   		if (out_data == 0)
> >   			return sysfs_emit(buf, "[unconnected] connected unknown\n");
> > @@ -694,8 +695,8 @@ static ssize_t show_deepsleep_status(struct device *dev,
> >   	struct wmax_basic_args in_args = {
> >   		.arg = 0,
> >   	};
> > -	status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS,
> > -					&out_data);
> > +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> > +					WMAX_METHOD_DEEP_SLEEP_STATUS, &out_data);
> >   	if (ACPI_SUCCESS(status)) {
> >   		if (out_data == 0)
> >   			return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
> > @@ -723,8 +724,8 @@ static ssize_t toggle_deepsleep(struct device *dev,
> >   		args.arg = 2;
> >   	pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf);
> > 
> > -	status = alienware_wmax_command(&args, WMAX_METHOD_DEEP_SLEEP_CONTROL,
> > -					NULL);
> > +	status = alienware_wmax_command(&args, sizeof(args),
> > +					WMAX_METHOD_DEEP_SLEEP_CONTROL, NULL);
> > 
> >   	if (ACPI_FAILURE(status))
> >   		pr_err("alienware-wmi: deep sleep control failed: results: %u\n",
[PATCH v5 3/4] alienware-wmi: added platform profile support
Posted by Kurt Borja 1 month, 2 weeks ago
Implements platform profile support for Dell laptops with new WMAX
thermal interface, present on some Alienware X-Series, Alienware
M-Series and Dell's G-Series laptops. This implementation supports two
sets of thermal profile codes, namely *thermal* and *thermal_ustt*, plus
additional quirk *gmode* for Dell's G-Series laptops.

Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
 drivers/platform/x86/dell/Kconfig         |   1 +
 drivers/platform/x86/dell/alienware-wmi.c | 236 ++++++++++++++++++++++
 2 files changed, 237 insertions(+)

diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig
index 68a49788a..b06d634cd 100644
--- a/drivers/platform/x86/dell/Kconfig
+++ b/drivers/platform/x86/dell/Kconfig
@@ -21,6 +21,7 @@ config ALIENWARE_WMI
 	depends on LEDS_CLASS
 	depends on NEW_LEDS
 	depends on ACPI_WMI
+	select ACPI_PLATFORM_PROFILE
 	help
 	 This is a driver for controlling Alienware BIOS driven
 	 features.  It exposes an interface for controlling the AlienFX
diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
index b27f3b64c..6e30e9376 100644
--- a/drivers/platform/x86/dell/alienware-wmi.c
+++ b/drivers/platform/x86/dell/alienware-wmi.c
@@ -8,8 +8,11 @@
 #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>
 
@@ -25,6 +28,12 @@
 #define WMAX_METHOD_AMPLIFIER_CABLE	0x6
 #define WMAX_METHOD_DEEP_SLEEP_CONTROL	0x0B
 #define WMAX_METHOD_DEEP_SLEEP_STATUS	0x0C
+#define WMAX_METHOD_THERMAL_INFORMATION	0x14
+#define WMAX_METHOD_THERMAL_CONTROL	0x15
+
+#define WMAX_ARG_GET_CURRENT_PROF	0x0B
+
+#define WMAX_FAILURE_CODE		0xFFFFFFFF
 
 MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
 MODULE_DESCRIPTION("Alienware special feature control");
@@ -49,11 +58,27 @@ enum WMAX_CONTROL_STATES {
 	WMAX_SUSPEND = 3,
 };
 
+enum WMAX_THERMAL_PROFILE {
+	WMAX_THERMAL_QUIET = 0x96,
+	WMAX_THERMAL_BALANCED = 0x97,
+	WMAX_THERMAL_BALANCED_PERFORMANCE = 0x98,
+	WMAX_THERMAL_PERFORMANCE = 0x99,
+	WMAX_THERMAL_USTT_LOW_POWER = 0xA5,
+	WMAX_THERMAL_USTT_QUIET = 0xA3,
+	WMAX_THERMAL_USTT_BALANCED = 0xA0,
+	WMAX_THERMAL_USTT_BALANCED_PERFORMANCE = 0xA1,
+	WMAX_THERMAL_USTT_PERFORMANCE = 0xA4,
+	WMAX_THERMAL_GMODE = 0xAB,
+};
+
 struct quirk_entry {
 	u8 num_zones;
 	u8 hdmi_mux;
 	u8 amplifier;
 	u8 deepslp;
+	u8 thermal;
+	u8 thermal_ustt;
+	u8 gmode;
 };
 
 static struct quirk_entry *quirks;
@@ -64,6 +89,9 @@ static struct quirk_entry quirk_inspiron5675 = {
 	.hdmi_mux = 0,
 	.amplifier = 0,
 	.deepslp = 0,
+	.thermal = 0,
+	.thermal_ustt = 0,
+	.gmode = 0,
 };
 
 static struct quirk_entry quirk_unknown = {
@@ -71,6 +99,9 @@ static struct quirk_entry quirk_unknown = {
 	.hdmi_mux = 0,
 	.amplifier = 0,
 	.deepslp = 0,
+	.thermal = 0,
+	.thermal_ustt = 0,
+	.gmode = 0,
 };
 
 static struct quirk_entry quirk_x51_r1_r2 = {
@@ -78,6 +109,9 @@ static struct quirk_entry quirk_x51_r1_r2 = {
 	.hdmi_mux = 0,
 	.amplifier = 0,
 	.deepslp = 0,
+	.thermal = 0,
+	.thermal_ustt = 0,
+	.gmode = 0,
 };
 
 static struct quirk_entry quirk_x51_r3 = {
@@ -85,6 +119,9 @@ static struct quirk_entry quirk_x51_r3 = {
 	.hdmi_mux = 0,
 	.amplifier = 1,
 	.deepslp = 0,
+	.thermal = 0,
+	.thermal_ustt = 0,
+	.gmode = 0,
 };
 
 static struct quirk_entry quirk_asm100 = {
@@ -92,6 +129,9 @@ static struct quirk_entry quirk_asm100 = {
 	.hdmi_mux = 1,
 	.amplifier = 0,
 	.deepslp = 0,
+	.thermal = 0,
+	.thermal_ustt = 0,
+	.gmode = 0,
 };
 
 static struct quirk_entry quirk_asm200 = {
@@ -99,6 +139,9 @@ static struct quirk_entry quirk_asm200 = {
 	.hdmi_mux = 1,
 	.amplifier = 0,
 	.deepslp = 1,
+	.thermal = 0,
+	.thermal_ustt = 0,
+	.gmode = 0,
 };
 
 static struct quirk_entry quirk_asm201 = {
@@ -106,6 +149,19 @@ static struct quirk_entry quirk_asm201 = {
 	.hdmi_mux = 1,
 	.amplifier = 1,
 	.deepslp = 1,
+	.thermal = 0,
+	.thermal_ustt = 0,
+	.gmode = 0,
+};
+
+static struct quirk_entry quirk_x15_r1 = {
+	.num_zones = 2,
+	.hdmi_mux = 0,
+	.amplifier = 0,
+	.deepslp = 0,
+	.thermal = 0,
+	.thermal_ustt = 1,
+	.gmode = 0,
 };
 
 static int __init dmi_matched(const struct dmi_system_id *dmi)
@@ -169,6 +225,15 @@ static const struct dmi_system_id alienware_quirks[] __initconst = {
 		},
 		.driver_data = &quirk_asm201,
 	},
+	{
+		.callback = dmi_matched,
+		.ident = "Alienware x15 R1",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R1")
+		},
+		.driver_data = &quirk_x15_r1,
+	},
 	{
 		.callback = dmi_matched,
 		.ident = "Dell Inc. Inspiron 5675",
@@ -218,6 +283,7 @@ static struct platform_device *platform_device;
 static struct device_attribute *zone_dev_attrs;
 static struct attribute **zone_attrs;
 static struct platform_zone *zone_data;
+static struct platform_profile_handler pp_handler;
 
 static struct platform_driver platform_driver = {
 	.driver = {
@@ -761,6 +827,168 @@ static int create_deepsleep(struct platform_device *dev)
 	return ret;
 }
 
+/*
+ * Thermal Profile control
+ *  - Provides thermal profile control through the Platform Profile API
+ */
+#define WMAX_PROFILE_MASK	GENMASK(15, 8)
+#define WMAX_PROFILE_ACTIVATE	BIT(0)
+
+static u32 profile_to_wmax_arg(enum WMAX_THERMAL_PROFILE prof)
+{
+	return FIELD_PREP(WMAX_PROFILE_MASK, prof) | WMAX_PROFILE_ACTIVATE;
+}
+
+static int thermal_profile_get(struct platform_profile_handler *pprof,
+				enum platform_profile_option *profile)
+{
+	acpi_status status;
+	u32 in_args = WMAX_ARG_GET_CURRENT_PROF;
+	u32 out_data;
+
+	status = alienware_wmax_command(&in_args, sizeof(in_args),
+					WMAX_METHOD_THERMAL_INFORMATION, &out_data);
+
+	if (ACPI_FAILURE(status))
+		return -EIO;
+
+	if (out_data == WMAX_FAILURE_CODE)
+		return -EBADRQC;
+
+	switch (out_data) {
+	case WMAX_THERMAL_USTT_LOW_POWER:
+		*profile = PLATFORM_PROFILE_LOW_POWER;
+		break;
+	case WMAX_THERMAL_QUIET:
+	case WMAX_THERMAL_USTT_QUIET:
+		*profile = PLATFORM_PROFILE_QUIET;
+		break;
+	case WMAX_THERMAL_BALANCED:
+	case WMAX_THERMAL_USTT_BALANCED:
+		*profile = PLATFORM_PROFILE_BALANCED;
+		break;
+	case WMAX_THERMAL_BALANCED_PERFORMANCE:
+	case WMAX_THERMAL_USTT_BALANCED_PERFORMANCE:
+		*profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE;
+		break;
+	case WMAX_THERMAL_GMODE:
+	case WMAX_THERMAL_PERFORMANCE:
+	case WMAX_THERMAL_USTT_PERFORMANCE:
+		*profile = PLATFORM_PROFILE_PERFORMANCE;
+		break;
+	default:
+		return -ENODATA;
+	}
+
+	return 0;
+}
+
+static int thermal_profile_set(struct platform_profile_handler *pprof,
+				enum platform_profile_option profile)
+{
+	acpi_status status;
+	u32 in_args;
+	u32 out_data;
+
+	switch (profile) {
+	case PLATFORM_PROFILE_QUIET:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET);
+		break;
+	case PLATFORM_PROFILE_BALANCED:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED);
+		break;
+	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE);
+		break;
+	case PLATFORM_PROFILE_PERFORMANCE:
+		if (quirks->gmode > 0)
+			in_args = profile_to_wmax_arg(WMAX_THERMAL_GMODE);
+		else
+			in_args = profile_to_wmax_arg(WMAX_THERMAL_PERFORMANCE);
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	status = alienware_wmax_command(&in_args, sizeof(in_args),
+					WMAX_METHOD_THERMAL_CONTROL, &out_data);
+
+	if (ACPI_FAILURE(status))
+		return -EIO;
+
+	if (out_data == WMAX_FAILURE_CODE)
+		return -EBADRQC;
+
+	return 0;
+}
+
+static int thermal_profile_set_ustt(struct platform_profile_handler *pprof,
+				    enum platform_profile_option profile)
+{
+	acpi_status status;
+	u32 in_args;
+	u32 out_data;
+
+	switch (profile) {
+	case PLATFORM_PROFILE_LOW_POWER:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_LOW_POWER);
+		break;
+	case PLATFORM_PROFILE_QUIET:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_QUIET);
+		break;
+	case PLATFORM_PROFILE_BALANCED:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_BALANCED);
+		break;
+	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_BALANCED_PERFORMANCE);
+		break;
+	case PLATFORM_PROFILE_PERFORMANCE:
+		if (quirks->gmode > 0)
+			in_args = profile_to_wmax_arg(WMAX_THERMAL_GMODE);
+		else
+			in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_PERFORMANCE);
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	status = alienware_wmax_command(&in_args, sizeof(in_args),
+					WMAX_METHOD_THERMAL_CONTROL, &out_data);
+
+	if (ACPI_FAILURE(status))
+		return -EIO;
+
+	if (out_data == WMAX_FAILURE_CODE)
+		return -EBADRQC;
+
+	return 0;
+}
+
+static int create_thermal_profile(void)
+{
+	pp_handler.profile_get = thermal_profile_get;
+
+	if (quirks->thermal > 0)
+		pp_handler.profile_set = thermal_profile_set;
+	else {
+		pp_handler.profile_set = thermal_profile_set_ustt;
+		set_bit(PLATFORM_PROFILE_LOW_POWER, pp_handler.choices);
+	}
+
+	set_bit(PLATFORM_PROFILE_QUIET, pp_handler.choices);
+	set_bit(PLATFORM_PROFILE_BALANCED, pp_handler.choices);
+	set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, pp_handler.choices);
+	set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices);
+
+	return platform_profile_register(&pp_handler);
+}
+
+static void remove_thermal_profile(void)
+{
+	if (quirks->thermal > 0)
+		platform_profile_remove();
+}
+
 static int __init alienware_wmi_init(void)
 {
 	int ret;
@@ -808,6 +1036,12 @@ static int __init alienware_wmi_init(void)
 			goto fail_prep_deepsleep;
 	}
 
+	if (quirks->thermal > 0 || quirks->thermal_ustt > 0) {
+		ret = create_thermal_profile();
+		if (ret)
+			goto fail_prep_thermal_profile;
+	}
+
 	ret = alienware_zone_init(platform_device);
 	if (ret)
 		goto fail_prep_zones;
@@ -818,6 +1052,7 @@ static int __init alienware_wmi_init(void)
 	alienware_zone_exit(platform_device);
 fail_prep_deepsleep:
 fail_prep_amplifier:
+fail_prep_thermal_profile:
 fail_prep_hdmi:
 	platform_device_del(platform_device);
 fail_platform_device2:
@@ -835,6 +1070,7 @@ static void __exit alienware_wmi_exit(void)
 	if (platform_device) {
 		alienware_zone_exit(platform_device);
 		remove_hdmi(platform_device);
+		remove_thermal_profile();
 		platform_device_unregister(platform_device);
 		platform_driver_unregister(&platform_driver);
 	}
-- 
2.47.0
Re: [PATCH v5 3/4] alienware-wmi: added platform profile support
Posted by Ilpo Järvinen 1 month, 1 week ago
On Fri, 11 Oct 2024, Kurt Borja wrote:

> Implements platform profile support for Dell laptops with new WMAX
> thermal interface, present on some Alienware X-Series, Alienware
> M-Series and Dell's G-Series laptops. This implementation supports two
> sets of thermal profile codes, namely *thermal* and *thermal_ustt*, plus
> additional quirk *gmode* for Dell's G-Series laptops.
> 
> Signed-off-by: Kurt Borja <kuurtb@gmail.com>
> ---
>  drivers/platform/x86/dell/Kconfig         |   1 +
>  drivers/platform/x86/dell/alienware-wmi.c | 236 ++++++++++++++++++++++
>  2 files changed, 237 insertions(+)
> 
> diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig
> index 68a49788a..b06d634cd 100644
> --- a/drivers/platform/x86/dell/Kconfig
> +++ b/drivers/platform/x86/dell/Kconfig
> @@ -21,6 +21,7 @@ config ALIENWARE_WMI
>  	depends on LEDS_CLASS
>  	depends on NEW_LEDS
>  	depends on ACPI_WMI
> +	select ACPI_PLATFORM_PROFILE
>  	help
>  	 This is a driver for controlling Alienware BIOS driven
>  	 features.  It exposes an interface for controlling the AlienFX
> diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
> index b27f3b64c..6e30e9376 100644
> --- a/drivers/platform/x86/dell/alienware-wmi.c
> +++ b/drivers/platform/x86/dell/alienware-wmi.c
> @@ -8,8 +8,11 @@
>  #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>
>  
> @@ -25,6 +28,12 @@
>  #define WMAX_METHOD_AMPLIFIER_CABLE	0x6
>  #define WMAX_METHOD_DEEP_SLEEP_CONTROL	0x0B
>  #define WMAX_METHOD_DEEP_SLEEP_STATUS	0x0C
> +#define WMAX_METHOD_THERMAL_INFORMATION	0x14
> +#define WMAX_METHOD_THERMAL_CONTROL	0x15
> +
> +#define WMAX_ARG_GET_CURRENT_PROF	0x0B
> +
> +#define WMAX_FAILURE_CODE		0xFFFFFFFF
>  
>  MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
>  MODULE_DESCRIPTION("Alienware special feature control");
> @@ -49,11 +58,27 @@ enum WMAX_CONTROL_STATES {
>  	WMAX_SUSPEND = 3,
>  };
>  
> +enum WMAX_THERMAL_PROFILE {
> +	WMAX_THERMAL_QUIET = 0x96,
> +	WMAX_THERMAL_BALANCED = 0x97,
> +	WMAX_THERMAL_BALANCED_PERFORMANCE = 0x98,
> +	WMAX_THERMAL_PERFORMANCE = 0x99,
> +	WMAX_THERMAL_USTT_LOW_POWER = 0xA5,
> +	WMAX_THERMAL_USTT_QUIET = 0xA3,
> +	WMAX_THERMAL_USTT_BALANCED = 0xA0,
> +	WMAX_THERMAL_USTT_BALANCED_PERFORMANCE = 0xA1,
> +	WMAX_THERMAL_USTT_PERFORMANCE = 0xA4,
> +	WMAX_THERMAL_GMODE = 0xAB,

While doing the next version, could also align these values please.

-- 
 i.
Re: [PATCH v5 3/4] alienware-wmi: added platform profile support
Posted by Armin Wolf 1 month, 2 weeks ago
Am 12.10.24 um 04:02 schrieb Kurt Borja:

> Implements platform profile support for Dell laptops with new WMAX
> thermal interface, present on some Alienware X-Series, Alienware
> M-Series and Dell's G-Series laptops. This implementation supports two
> sets of thermal profile codes, namely *thermal* and *thermal_ustt*, plus
> additional quirk *gmode* for Dell's G-Series laptops.
>
> Signed-off-by: Kurt Borja <kuurtb@gmail.com>
> ---
>   drivers/platform/x86/dell/Kconfig         |   1 +
>   drivers/platform/x86/dell/alienware-wmi.c | 236 ++++++++++++++++++++++
>   2 files changed, 237 insertions(+)
>
> diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig
> index 68a49788a..b06d634cd 100644
> --- a/drivers/platform/x86/dell/Kconfig
> +++ b/drivers/platform/x86/dell/Kconfig
> @@ -21,6 +21,7 @@ config ALIENWARE_WMI
>   	depends on LEDS_CLASS
>   	depends on NEW_LEDS
>   	depends on ACPI_WMI
> +	select ACPI_PLATFORM_PROFILE
>   	help
>   	 This is a driver for controlling Alienware BIOS driven
>   	 features.  It exposes an interface for controlling the AlienFX
> diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
> index b27f3b64c..6e30e9376 100644
> --- a/drivers/platform/x86/dell/alienware-wmi.c
> +++ b/drivers/platform/x86/dell/alienware-wmi.c
> @@ -8,8 +8,11 @@
>   #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>
>
> @@ -25,6 +28,12 @@
>   #define WMAX_METHOD_AMPLIFIER_CABLE	0x6
>   #define WMAX_METHOD_DEEP_SLEEP_CONTROL	0x0B
>   #define WMAX_METHOD_DEEP_SLEEP_STATUS	0x0C
> +#define WMAX_METHOD_THERMAL_INFORMATION	0x14
> +#define WMAX_METHOD_THERMAL_CONTROL	0x15
> +
> +#define WMAX_ARG_GET_CURRENT_PROF	0x0B
> +
> +#define WMAX_FAILURE_CODE		0xFFFFFFFF
>
>   MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
>   MODULE_DESCRIPTION("Alienware special feature control");
> @@ -49,11 +58,27 @@ enum WMAX_CONTROL_STATES {
>   	WMAX_SUSPEND = 3,
>   };
>
> +enum WMAX_THERMAL_PROFILE {
> +	WMAX_THERMAL_QUIET = 0x96,
> +	WMAX_THERMAL_BALANCED = 0x97,
> +	WMAX_THERMAL_BALANCED_PERFORMANCE = 0x98,
> +	WMAX_THERMAL_PERFORMANCE = 0x99,
> +	WMAX_THERMAL_USTT_LOW_POWER = 0xA5,
> +	WMAX_THERMAL_USTT_QUIET = 0xA3,
> +	WMAX_THERMAL_USTT_BALANCED = 0xA0,
> +	WMAX_THERMAL_USTT_BALANCED_PERFORMANCE = 0xA1,
> +	WMAX_THERMAL_USTT_PERFORMANCE = 0xA4,
> +	WMAX_THERMAL_GMODE = 0xAB,
> +};
> +
>   struct quirk_entry {
>   	u8 num_zones;
>   	u8 hdmi_mux;
>   	u8 amplifier;
>   	u8 deepslp;
> +	u8 thermal;
> +	u8 thermal_ustt;
> +	u8 gmode;
>   };
>
>   static struct quirk_entry *quirks;
> @@ -64,6 +89,9 @@ static struct quirk_entry quirk_inspiron5675 = {
>   	.hdmi_mux = 0,
>   	.amplifier = 0,
>   	.deepslp = 0,
> +	.thermal = 0,
> +	.thermal_ustt = 0,
> +	.gmode = 0,
>   };
>
>   static struct quirk_entry quirk_unknown = {
> @@ -71,6 +99,9 @@ static struct quirk_entry quirk_unknown = {
>   	.hdmi_mux = 0,
>   	.amplifier = 0,
>   	.deepslp = 0,
> +	.thermal = 0,
> +	.thermal_ustt = 0,
> +	.gmode = 0,
>   };
>
>   static struct quirk_entry quirk_x51_r1_r2 = {
> @@ -78,6 +109,9 @@ static struct quirk_entry quirk_x51_r1_r2 = {
>   	.hdmi_mux = 0,
>   	.amplifier = 0,
>   	.deepslp = 0,
> +	.thermal = 0,
> +	.thermal_ustt = 0,
> +	.gmode = 0,
>   };
>
>   static struct quirk_entry quirk_x51_r3 = {
> @@ -85,6 +119,9 @@ static struct quirk_entry quirk_x51_r3 = {
>   	.hdmi_mux = 0,
>   	.amplifier = 1,
>   	.deepslp = 0,
> +	.thermal = 0,
> +	.thermal_ustt = 0,
> +	.gmode = 0,
>   };
>
>   static struct quirk_entry quirk_asm100 = {
> @@ -92,6 +129,9 @@ static struct quirk_entry quirk_asm100 = {
>   	.hdmi_mux = 1,
>   	.amplifier = 0,
>   	.deepslp = 0,
> +	.thermal = 0,
> +	.thermal_ustt = 0,
> +	.gmode = 0,
>   };
>
>   static struct quirk_entry quirk_asm200 = {
> @@ -99,6 +139,9 @@ static struct quirk_entry quirk_asm200 = {
>   	.hdmi_mux = 1,
>   	.amplifier = 0,
>   	.deepslp = 1,
> +	.thermal = 0,
> +	.thermal_ustt = 0,
> +	.gmode = 0,
>   };
>
>   static struct quirk_entry quirk_asm201 = {
> @@ -106,6 +149,19 @@ static struct quirk_entry quirk_asm201 = {
>   	.hdmi_mux = 1,
>   	.amplifier = 1,
>   	.deepslp = 1,
> +	.thermal = 0,
> +	.thermal_ustt = 0,
> +	.gmode = 0,
> +};
> +
> +static struct quirk_entry quirk_x15_r1 = {
> +	.num_zones = 2,
> +	.hdmi_mux = 0,
> +	.amplifier = 0,
> +	.deepslp = 0,
> +	.thermal = 0,
> +	.thermal_ustt = 1,
> +	.gmode = 0,
>   };
>
>   static int __init dmi_matched(const struct dmi_system_id *dmi)
> @@ -169,6 +225,15 @@ static const struct dmi_system_id alienware_quirks[] __initconst = {
>   		},
>   		.driver_data = &quirk_asm201,
>   	},
> +	{
> +		.callback = dmi_matched,
> +		.ident = "Alienware x15 R1",
> +		.matches = {
> +			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> +			DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R1")
> +		},
> +		.driver_data = &quirk_x15_r1,
> +	},
>   	{
>   		.callback = dmi_matched,
>   		.ident = "Dell Inc. Inspiron 5675",
> @@ -218,6 +283,7 @@ static struct platform_device *platform_device;
>   static struct device_attribute *zone_dev_attrs;
>   static struct attribute **zone_attrs;
>   static struct platform_zone *zone_data;
> +static struct platform_profile_handler pp_handler;
>
>   static struct platform_driver platform_driver = {
>   	.driver = {
> @@ -761,6 +827,168 @@ static int create_deepsleep(struct platform_device *dev)
>   	return ret;
>   }
>
> +/*
> + * Thermal Profile control
> + *  - Provides thermal profile control through the Platform Profile API
> + */
> +#define WMAX_PROFILE_MASK	GENMASK(15, 8)
> +#define WMAX_PROFILE_ACTIVATE	BIT(0)
> +
> +static u32 profile_to_wmax_arg(enum WMAX_THERMAL_PROFILE prof)
> +{
> +	return FIELD_PREP(WMAX_PROFILE_MASK, prof) | WMAX_PROFILE_ACTIVATE;
> +}
> +
> +static int thermal_profile_get(struct platform_profile_handler *pprof,
> +				enum platform_profile_option *profile)

Alignment should match open parenthesis.

> +{
> +	acpi_status status;
> +	u32 in_args = WMAX_ARG_GET_CURRENT_PROF;
> +	u32 out_data;
> +
> +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> +					WMAX_METHOD_THERMAL_INFORMATION, &out_data);
> +
> +	if (ACPI_FAILURE(status))
> +		return -EIO;
> +
> +	if (out_data == WMAX_FAILURE_CODE)
> +		return -EBADRQC;
> +
> +	switch (out_data) {
> +	case WMAX_THERMAL_USTT_LOW_POWER:
> +		*profile = PLATFORM_PROFILE_LOW_POWER;
> +		break;
> +	case WMAX_THERMAL_QUIET:
> +	case WMAX_THERMAL_USTT_QUIET:
> +		*profile = PLATFORM_PROFILE_QUIET;
> +		break;
> +	case WMAX_THERMAL_BALANCED:
> +	case WMAX_THERMAL_USTT_BALANCED:
> +		*profile = PLATFORM_PROFILE_BALANCED;
> +		break;
> +	case WMAX_THERMAL_BALANCED_PERFORMANCE:
> +	case WMAX_THERMAL_USTT_BALANCED_PERFORMANCE:
> +		*profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE;
> +		break;
> +	case WMAX_THERMAL_GMODE:
> +	case WMAX_THERMAL_PERFORMANCE:
> +	case WMAX_THERMAL_USTT_PERFORMANCE:
> +		*profile = PLATFORM_PROFILE_PERFORMANCE;
> +		break;
> +	default:
> +		return -ENODATA;
> +	}
> +
> +	return 0;
> +}
> +
> +static int thermal_profile_set(struct platform_profile_handler *pprof,
> +				enum platform_profile_option profile)
> +{

Alignment should match open parenthesis.

> +	acpi_status status;
> +	u32 in_args;
> +	u32 out_data;
> +
> +	switch (profile) {
> +	case PLATFORM_PROFILE_QUIET:
> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET);
> +		break;
> +	case PLATFORM_PROFILE_BALANCED:
> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED);
> +		break;
> +	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE);
> +		break;
> +	case PLATFORM_PROFILE_PERFORMANCE:
> +		if (quirks->gmode > 0)
> +			in_args = profile_to_wmax_arg(WMAX_THERMAL_GMODE);
> +		else
> +			in_args = profile_to_wmax_arg(WMAX_THERMAL_PERFORMANCE);
> +		break;
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +
> +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> +					WMAX_METHOD_THERMAL_CONTROL, &out_data);
> +
> +	if (ACPI_FAILURE(status))
> +		return -EIO;
> +
> +	if (out_data == WMAX_FAILURE_CODE)
> +		return -EBADRQC;
> +
> +	return 0;
> +}
> +
> +static int thermal_profile_set_ustt(struct platform_profile_handler *pprof,
> +				    enum platform_profile_option profile)
> +{
> +	acpi_status status;
> +	u32 in_args;
> +	u32 out_data;
> +
> +	switch (profile) {
> +	case PLATFORM_PROFILE_LOW_POWER:
> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_LOW_POWER);
> +		break;
> +	case PLATFORM_PROFILE_QUIET:
> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_QUIET);
> +		break;
> +	case PLATFORM_PROFILE_BALANCED:
> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_BALANCED);
> +		break;
> +	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_BALANCED_PERFORMANCE);
> +		break;
> +	case PLATFORM_PROFILE_PERFORMANCE:
> +		if (quirks->gmode > 0)
> +			in_args = profile_to_wmax_arg(WMAX_THERMAL_GMODE);
> +		else
> +			in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_PERFORMANCE);
> +		break;
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +
> +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> +					WMAX_METHOD_THERMAL_CONTROL, &out_data);
> +
> +	if (ACPI_FAILURE(status))
> +		return -EIO;
> +
> +	if (out_data == WMAX_FAILURE_CODE)
> +		return -EBADRQC;
> +
> +	return 0;
> +}
> +
> +static int create_thermal_profile(void)
> +{
> +	pp_handler.profile_get = thermal_profile_get;
> +
> +	if (quirks->thermal > 0)
> +		pp_handler.profile_set = thermal_profile_set;

Braces {} should be used on all arms of this statement.

> +	else {
> +		pp_handler.profile_set = thermal_profile_set_ustt;
> +		set_bit(PLATFORM_PROFILE_LOW_POWER, pp_handler.choices);
> +	}
> +
> +	set_bit(PLATFORM_PROFILE_QUIET, pp_handler.choices);
> +	set_bit(PLATFORM_PROFILE_BALANCED, pp_handler.choices);
> +	set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, pp_handler.choices);
> +	set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices);
> +
> +	return platform_profile_register(&pp_handler);
> +}
> +
> +static void remove_thermal_profile(void)
> +{
> +	if (quirks->thermal > 0)
> +		platform_profile_remove();

platform_profile_remove() should also be called when quirks->thermal_ustt is set.

> +}
> +
>   static int __init alienware_wmi_init(void)
>   {
>   	int ret;
> @@ -808,6 +1036,12 @@ static int __init alienware_wmi_init(void)
>   			goto fail_prep_deepsleep;
>   	}
>
> +	if (quirks->thermal > 0 || quirks->thermal_ustt > 0) {
> +		ret = create_thermal_profile();
> +		if (ret)
> +			goto fail_prep_thermal_profile;
> +	}
> +
>   	ret = alienware_zone_init(platform_device);
>   	if (ret)
>   		goto fail_prep_zones;
> @@ -818,6 +1052,7 @@ static int __init alienware_wmi_init(void)
>   	alienware_zone_exit(platform_device);
>   fail_prep_deepsleep:
>   fail_prep_amplifier:
> +fail_prep_thermal_profile:

fail_prep_thermal_profile should come before fail_prep_deepsleep for proper rollback in case of an error.
Also fail_prep_zones should call remove_thermal_profile().

>   fail_prep_hdmi:
>   	platform_device_del(platform_device);
>   fail_platform_device2:
> @@ -835,6 +1070,7 @@ static void __exit alienware_wmi_exit(void)
>   	if (platform_device) {
>   		alienware_zone_exit(platform_device);
>   		remove_hdmi(platform_device);
> +		remove_thermal_profile();

Please move remove_thermal_profile() above remove_hdmi().

Otherwise, the patch look good.

Thanks,
Armin Wolf

>   		platform_device_unregister(platform_device);
>   		platform_driver_unregister(&platform_driver);
>   	}
Re: [PATCH v5 3/4] alienware-wmi: added platform profile support
Posted by Kurt Borja 1 month, 1 week ago
On Mon, Oct 14, 2024 at 06:40:49PM +0200, Armin Wolf wrote:
> Am 12.10.24 um 04:02 schrieb Kurt Borja:
> 
> > Implements platform profile support for Dell laptops with new WMAX
> > thermal interface, present on some Alienware X-Series, Alienware
> > M-Series and Dell's G-Series laptops. This implementation supports two
> > sets of thermal profile codes, namely *thermal* and *thermal_ustt*, plus
> > additional quirk *gmode* for Dell's G-Series laptops.
> > 
> > Signed-off-by: Kurt Borja <kuurtb@gmail.com>
> > ---
> >   drivers/platform/x86/dell/Kconfig         |   1 +
> >   drivers/platform/x86/dell/alienware-wmi.c | 236 ++++++++++++++++++++++
> >   2 files changed, 237 insertions(+)
> > 
> > diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig
> > index 68a49788a..b06d634cd 100644
> > --- a/drivers/platform/x86/dell/Kconfig
> > +++ b/drivers/platform/x86/dell/Kconfig
> > @@ -21,6 +21,7 @@ config ALIENWARE_WMI
> >   	depends on LEDS_CLASS
> >   	depends on NEW_LEDS
> >   	depends on ACPI_WMI
> > +	select ACPI_PLATFORM_PROFILE
> >   	help
> >   	 This is a driver for controlling Alienware BIOS driven
> >   	 features.  It exposes an interface for controlling the AlienFX
> > diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
> > index b27f3b64c..6e30e9376 100644
> > --- a/drivers/platform/x86/dell/alienware-wmi.c
> > +++ b/drivers/platform/x86/dell/alienware-wmi.c
> > @@ -8,8 +8,11 @@
> >   #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>
> > 
> > @@ -25,6 +28,12 @@
> >   #define WMAX_METHOD_AMPLIFIER_CABLE	0x6
> >   #define WMAX_METHOD_DEEP_SLEEP_CONTROL	0x0B
> >   #define WMAX_METHOD_DEEP_SLEEP_STATUS	0x0C
> > +#define WMAX_METHOD_THERMAL_INFORMATION	0x14
> > +#define WMAX_METHOD_THERMAL_CONTROL	0x15
> > +
> > +#define WMAX_ARG_GET_CURRENT_PROF	0x0B
> > +
> > +#define WMAX_FAILURE_CODE		0xFFFFFFFF
> > 
> >   MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
> >   MODULE_DESCRIPTION("Alienware special feature control");
> > @@ -49,11 +58,27 @@ enum WMAX_CONTROL_STATES {
> >   	WMAX_SUSPEND = 3,
> >   };
> > 
> > +enum WMAX_THERMAL_PROFILE {
> > +	WMAX_THERMAL_QUIET = 0x96,
> > +	WMAX_THERMAL_BALANCED = 0x97,
> > +	WMAX_THERMAL_BALANCED_PERFORMANCE = 0x98,
> > +	WMAX_THERMAL_PERFORMANCE = 0x99,
> > +	WMAX_THERMAL_USTT_LOW_POWER = 0xA5,
> > +	WMAX_THERMAL_USTT_QUIET = 0xA3,
> > +	WMAX_THERMAL_USTT_BALANCED = 0xA0,
> > +	WMAX_THERMAL_USTT_BALANCED_PERFORMANCE = 0xA1,
> > +	WMAX_THERMAL_USTT_PERFORMANCE = 0xA4,
> > +	WMAX_THERMAL_GMODE = 0xAB,
> > +};
> > +
> >   struct quirk_entry {
> >   	u8 num_zones;
> >   	u8 hdmi_mux;
> >   	u8 amplifier;
> >   	u8 deepslp;
> > +	u8 thermal;
> > +	u8 thermal_ustt;
> > +	u8 gmode;
> >   };
> > 
> >   static struct quirk_entry *quirks;
> > @@ -64,6 +89,9 @@ static struct quirk_entry quirk_inspiron5675 = {
> >   	.hdmi_mux = 0,
> >   	.amplifier = 0,
> >   	.deepslp = 0,
> > +	.thermal = 0,
> > +	.thermal_ustt = 0,
> > +	.gmode = 0,
> >   };
> > 
> >   static struct quirk_entry quirk_unknown = {
> > @@ -71,6 +99,9 @@ static struct quirk_entry quirk_unknown = {
> >   	.hdmi_mux = 0,
> >   	.amplifier = 0,
> >   	.deepslp = 0,
> > +	.thermal = 0,
> > +	.thermal_ustt = 0,
> > +	.gmode = 0,
> >   };
> > 
> >   static struct quirk_entry quirk_x51_r1_r2 = {
> > @@ -78,6 +109,9 @@ static struct quirk_entry quirk_x51_r1_r2 = {
> >   	.hdmi_mux = 0,
> >   	.amplifier = 0,
> >   	.deepslp = 0,
> > +	.thermal = 0,
> > +	.thermal_ustt = 0,
> > +	.gmode = 0,
> >   };
> > 
> >   static struct quirk_entry quirk_x51_r3 = {
> > @@ -85,6 +119,9 @@ static struct quirk_entry quirk_x51_r3 = {
> >   	.hdmi_mux = 0,
> >   	.amplifier = 1,
> >   	.deepslp = 0,
> > +	.thermal = 0,
> > +	.thermal_ustt = 0,
> > +	.gmode = 0,
> >   };
> > 
> >   static struct quirk_entry quirk_asm100 = {
> > @@ -92,6 +129,9 @@ static struct quirk_entry quirk_asm100 = {
> >   	.hdmi_mux = 1,
> >   	.amplifier = 0,
> >   	.deepslp = 0,
> > +	.thermal = 0,
> > +	.thermal_ustt = 0,
> > +	.gmode = 0,
> >   };
> > 
> >   static struct quirk_entry quirk_asm200 = {
> > @@ -99,6 +139,9 @@ static struct quirk_entry quirk_asm200 = {
> >   	.hdmi_mux = 1,
> >   	.amplifier = 0,
> >   	.deepslp = 1,
> > +	.thermal = 0,
> > +	.thermal_ustt = 0,
> > +	.gmode = 0,
> >   };
> > 
> >   static struct quirk_entry quirk_asm201 = {
> > @@ -106,6 +149,19 @@ static struct quirk_entry quirk_asm201 = {
> >   	.hdmi_mux = 1,
> >   	.amplifier = 1,
> >   	.deepslp = 1,
> > +	.thermal = 0,
> > +	.thermal_ustt = 0,
> > +	.gmode = 0,
> > +};
> > +
> > +static struct quirk_entry quirk_x15_r1 = {
> > +	.num_zones = 2,
> > +	.hdmi_mux = 0,
> > +	.amplifier = 0,
> > +	.deepslp = 0,
> > +	.thermal = 0,
> > +	.thermal_ustt = 1,
> > +	.gmode = 0,
> >   };
> > 
> >   static int __init dmi_matched(const struct dmi_system_id *dmi)
> > @@ -169,6 +225,15 @@ static const struct dmi_system_id alienware_quirks[] __initconst = {
> >   		},
> >   		.driver_data = &quirk_asm201,
> >   	},
> > +	{
> > +		.callback = dmi_matched,
> > +		.ident = "Alienware x15 R1",
> > +		.matches = {
> > +			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> > +			DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R1")
> > +		},
> > +		.driver_data = &quirk_x15_r1,
> > +	},
> >   	{
> >   		.callback = dmi_matched,
> >   		.ident = "Dell Inc. Inspiron 5675",
> > @@ -218,6 +283,7 @@ static struct platform_device *platform_device;
> >   static struct device_attribute *zone_dev_attrs;
> >   static struct attribute **zone_attrs;
> >   static struct platform_zone *zone_data;
> > +static struct platform_profile_handler pp_handler;
> > 
> >   static struct platform_driver platform_driver = {
> >   	.driver = {
> > @@ -761,6 +827,168 @@ static int create_deepsleep(struct platform_device *dev)
> >   	return ret;
> >   }
> > 
> > +/*
> > + * Thermal Profile control
> > + *  - Provides thermal profile control through the Platform Profile API
> > + */
> > +#define WMAX_PROFILE_MASK	GENMASK(15, 8)
> > +#define WMAX_PROFILE_ACTIVATE	BIT(0)
> > +
> > +static u32 profile_to_wmax_arg(enum WMAX_THERMAL_PROFILE prof)
> > +{
> > +	return FIELD_PREP(WMAX_PROFILE_MASK, prof) | WMAX_PROFILE_ACTIVATE;
> > +}
> > +
> > +static int thermal_profile_get(struct platform_profile_handler *pprof,
> > +				enum platform_profile_option *profile)
> 
> Alignment should match open parenthesis.

I will fix it.

>
> > +{
> > +	acpi_status status;
> > +	u32 in_args = WMAX_ARG_GET_CURRENT_PROF;
> > +	u32 out_data;
> > +
> > +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> > +					WMAX_METHOD_THERMAL_INFORMATION, &out_data);
> > +
> > +	if (ACPI_FAILURE(status))
> > +		return -EIO;
> > +
> > +	if (out_data == WMAX_FAILURE_CODE)
> > +		return -EBADRQC;
> > +
> > +	switch (out_data) {
> > +	case WMAX_THERMAL_USTT_LOW_POWER:
> > +		*profile = PLATFORM_PROFILE_LOW_POWER;
> > +		break;
> > +	case WMAX_THERMAL_QUIET:
> > +	case WMAX_THERMAL_USTT_QUIET:
> > +		*profile = PLATFORM_PROFILE_QUIET;
> > +		break;
> > +	case WMAX_THERMAL_BALANCED:
> > +	case WMAX_THERMAL_USTT_BALANCED:
> > +		*profile = PLATFORM_PROFILE_BALANCED;
> > +		break;
> > +	case WMAX_THERMAL_BALANCED_PERFORMANCE:
> > +	case WMAX_THERMAL_USTT_BALANCED_PERFORMANCE:
> > +		*profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE;
> > +		break;
> > +	case WMAX_THERMAL_GMODE:
> > +	case WMAX_THERMAL_PERFORMANCE:
> > +	case WMAX_THERMAL_USTT_PERFORMANCE:
> > +		*profile = PLATFORM_PROFILE_PERFORMANCE;
> > +		break;
> > +	default:
> > +		return -ENODATA;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int thermal_profile_set(struct platform_profile_handler *pprof,
> > +				enum platform_profile_option profile)
> > +{
> 
> Alignment should match open parenthesis.
> 
> > +	acpi_status status;
> > +	u32 in_args;
> > +	u32 out_data;
> > +
> > +	switch (profile) {
> > +	case PLATFORM_PROFILE_QUIET:
> > +		in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET);
> > +		break;
> > +	case PLATFORM_PROFILE_BALANCED:
> > +		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED);
> > +		break;
> > +	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
> > +		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE);
> > +		break;
> > +	case PLATFORM_PROFILE_PERFORMANCE:
> > +		if (quirks->gmode > 0)
> > +			in_args = profile_to_wmax_arg(WMAX_THERMAL_GMODE);
> > +		else
> > +			in_args = profile_to_wmax_arg(WMAX_THERMAL_PERFORMANCE);
> > +		break;
> > +	default:
> > +		return -EOPNOTSUPP;
> > +	}
> > +
> > +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> > +					WMAX_METHOD_THERMAL_CONTROL, &out_data);
> > +
> > +	if (ACPI_FAILURE(status))
> > +		return -EIO;
> > +
> > +	if (out_data == WMAX_FAILURE_CODE)
> > +		return -EBADRQC;
> > +
> > +	return 0;
> > +}
> > +
> > +static int thermal_profile_set_ustt(struct platform_profile_handler *pprof,
> > +				    enum platform_profile_option profile)
> > +{
> > +	acpi_status status;
> > +	u32 in_args;
> > +	u32 out_data;
> > +
> > +	switch (profile) {
> > +	case PLATFORM_PROFILE_LOW_POWER:
> > +		in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_LOW_POWER);
> > +		break;
> > +	case PLATFORM_PROFILE_QUIET:
> > +		in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_QUIET);
> > +		break;
> > +	case PLATFORM_PROFILE_BALANCED:
> > +		in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_BALANCED);
> > +		break;
> > +	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
> > +		in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_BALANCED_PERFORMANCE);
> > +		break;
> > +	case PLATFORM_PROFILE_PERFORMANCE:
> > +		if (quirks->gmode > 0)
> > +			in_args = profile_to_wmax_arg(WMAX_THERMAL_GMODE);
> > +		else
> > +			in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_PERFORMANCE);
> > +		break;
> > +	default:
> > +		return -EOPNOTSUPP;
> > +	}
> > +
> > +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> > +					WMAX_METHOD_THERMAL_CONTROL, &out_data);
> > +
> > +	if (ACPI_FAILURE(status))
> > +		return -EIO;
> > +
> > +	if (out_data == WMAX_FAILURE_CODE)
> > +		return -EBADRQC;
> > +
> > +	return 0;
> > +}
> > +
> > +static int create_thermal_profile(void)
> > +{
> > +	pp_handler.profile_get = thermal_profile_get;
> > +
> > +	if (quirks->thermal > 0)
> > +		pp_handler.profile_set = thermal_profile_set;
> 
> Braces {} should be used on all arms of this statement.

Ok.

>
> > +	else {
> > +		pp_handler.profile_set = thermal_profile_set_ustt;
> > +		set_bit(PLATFORM_PROFILE_LOW_POWER, pp_handler.choices);
> > +	}
> > +
> > +	set_bit(PLATFORM_PROFILE_QUIET, pp_handler.choices);
> > +	set_bit(PLATFORM_PROFILE_BALANCED, pp_handler.choices);
> > +	set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, pp_handler.choices);
> > +	set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices);
> > +
> > +	return platform_profile_register(&pp_handler);
> > +}
> > +
> > +static void remove_thermal_profile(void)
> > +{
> > +	if (quirks->thermal > 0)
> > +		platform_profile_remove();
> 
> platform_profile_remove() should also be called when quirks->thermal_ustt is set.

Thank you for catching it.

>
> > +}
> > +
> >   static int __init alienware_wmi_init(void)
> >   {
> >   	int ret;
> > @@ -808,6 +1036,12 @@ static int __init alienware_wmi_init(void)
> >   			goto fail_prep_deepsleep;
> >   	}
> > 
> > +	if (quirks->thermal > 0 || quirks->thermal_ustt > 0) {
> > +		ret = create_thermal_profile();
> > +		if (ret)
> > +			goto fail_prep_thermal_profile;
> > +	}
> > +
> >   	ret = alienware_zone_init(platform_device);
> >   	if (ret)
> >   		goto fail_prep_zones;
> > @@ -818,6 +1052,7 @@ static int __init alienware_wmi_init(void)
> >   	alienware_zone_exit(platform_device);
> >   fail_prep_deepsleep:
> >   fail_prep_amplifier:
> > +fail_prep_thermal_profile:
> 
> fail_prep_thermal_profile should come before fail_prep_deepsleep for proper rollback in case of an error.
> Also fail_prep_zones should call remove_thermal_profile().

Ok.

>
> >   fail_prep_hdmi:
> >   	platform_device_del(platform_device);
> >   fail_platform_device2:
> > @@ -835,6 +1070,7 @@ static void __exit alienware_wmi_exit(void)
> >   	if (platform_device) {
> >   		alienware_zone_exit(platform_device);
> >   		remove_hdmi(platform_device);
> > +		remove_thermal_profile();
> 
> Please move remove_thermal_profile() above remove_hdmi().

Ok.

> 
> Otherwise, the patch look good.
> 
> Thanks,
> Armin Wolf
> 

Thank you.

Kurt

> >   		platform_device_unregister(platform_device);
> >   		platform_driver_unregister(&platform_driver);
> >   	}
[PATCH v5 4/4] alienware-wmi: WMAX interface documentation
Posted by Kurt Borja 1 month, 2 weeks ago
Added documentation for new WMAX interface, present on some Alienware
X-Series, Alienware M-Series and Dell's G-Series laptops.

Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
 Documentation/wmi/devices/alienware-wmi.rst | 366 ++++++++++++++++++++
 1 file changed, 366 insertions(+)
 create mode 100644 Documentation/wmi/devices/alienware-wmi.rst

diff --git a/Documentation/wmi/devices/alienware-wmi.rst b/Documentation/wmi/devices/alienware-wmi.rst
new file mode 100644
index 000000000..77460b91c
--- /dev/null
+++ b/Documentation/wmi/devices/alienware-wmi.rst
@@ -0,0 +1,366 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+==============================================
+Dell AWCC WMI interface driver (alienware-wmi)
+==============================================
+
+Introduction
+============
+
+The WMI device WMAX has been implemented for many Alienware and Dell's G-Series
+models. Throughout these models, two implementations have been identified. The
+first one, used by older systems, deals with HDMI, brightness, RGB, amplifier
+and deep sleep control. The second one used by newer systems deals primarily
+with thermal, overclocking, and GPIO control.
+
+It is suspected that the latter is used by Alienware Command Center (AWCC) to
+manage manufacturer predefined thermal profiles. The alienware-wmi driver
+exposes Thermal_Information and Thermal_Control methods through the Platform
+Profile API to mimic AWCC's behavior.
+
+This newer interface, named AWCCMethodFunction has been reverse engineered, as
+Dell has not provided any official documentation. We will try to describe to the
+best of our ability its discovered inner workings.
+
+.. note::
+   The following method description may vary between models.
+
+WMI interface description
+-------------------------
+
+The WMI interface description can be decoded from the embedded binary MOF (bmof)
+data using the `bmfdec <https://github.com/pali/bmfdec>`_ utility:
+
+::
+
+ [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("WMI Function"), guid("{A70591CE-A997-11DA-B012-B622A1EF5492}")]
+ class AWCCWmiMethodFunction {
+   [key, read] string InstanceName;
+   [read] boolean Active;
+
+   [WmiMethodId(13), Implemented, read, write, Description("Return Overclocking Report.")] void Return_OverclockingReport([out] uint32 argr);
+   [WmiMethodId(14), Implemented, read, write, Description("Set OCUIBIOS Control.")] void Set_OCUIBIOSControl([in] uint32 arg2, [out] uint32 argr);
+   [WmiMethodId(15), Implemented, read, write, Description("Clear OC FailSafe Flag.")] void Clear_OCFailSafeFlag([out] uint32 argr);
+   [WmiMethodId(19), Implemented, read, write, Description("Get Fan Sensors.")] void GetFanSensors([in] uint32 arg2, [out] uint32 argr);
+   [WmiMethodId(20), Implemented, read, write, Description("Thermal Information.")] void Thermal_Information([in] uint32 arg2, [out] uint32 argr);
+   [WmiMethodId(21), Implemented, read, write, Description("Thermal Control.")] void Thermal_Control([in] uint32 arg2, [out] uint32 argr);
+   [WmiMethodId(23), Implemented, read, write, Description("MemoryOCControl.")] void MemoryOCControl([in] uint32 arg2, [out] uint32 argr);
+   [WmiMethodId(26), Implemented, read, write, Description("System Information.")] void SystemInformation([in] uint32 arg2, [out] uint32 argr);
+   [WmiMethodId(28), Implemented, read, write, Description("Power Information.")] void PowerInformation([in] uint32 arg2, [out] uint32 argr);
+   [WmiMethodId(32), Implemented, read, write, Description("FW Update GPIO toggle.")] void FWUpdateGPIOtoggle([in] uint32 arg2, [out] uint32 argr);
+   [WmiMethodId(33), Implemented, read, write, Description("Read Total of GPIOs.")] void ReadTotalofGPIOs([out] uint32 argr);
+   [WmiMethodId(34), Implemented, read, write, Description("Read GPIO pin Status.")] void ReadGPIOpPinStatus([in] uint32 arg2, [out] uint32 argr);
+   [WmiMethodId(35), Implemented, read, write, Description("Read Chassis Color.")] void ReadChassisColor([out] uint32 argr);
+   [WmiMethodId(36), Implemented, read, write, Description("Read Platform Properties.")] void ReadPlatformProperties([out] uint32 argr);
+   [WmiMethodId(128), Implemented, read, write, Description("Caldera SW installation.")] void CalderaSWInstallation([out] uint32 argr);
+   [WmiMethodId(129), Implemented, read, write, Description("Caldera SW is released.")] void CalderaSWReleased([out] uint32 argr);
+   [WmiMethodId(130), Implemented, read, write, Description("Caldera Connection Status.")] void CalderaConnectionStatus([in] uint32 arg2, [out] uint32 argr);
+   [WmiMethodId(131), Implemented, read, write, Description("Surprise Unplugged Flag Status.")] void SurpriseUnpluggedFlagStatus([out] uint32 argr);
+   [WmiMethodId(132), Implemented, read, write, Description("Clear Surprise Unplugged Flag.")] void ClearSurpriseUnpluggedFlag([out] uint32 argr);
+   [WmiMethodId(133), Implemented, read, write, Description("Cancel Undock Request.")] void CancelUndockRequest([out] uint32 argr);
+   [WmiMethodId(135), Implemented, read, write, Description("Devices in Caldera.")] void DevicesInCaldera([in] uint32 arg2, [out] uint32 argr);
+   [WmiMethodId(136), Implemented, read, write, Description("Notify BIOS for SW ready to disconnect Caldera.")] void NotifyBIOSForSWReadyToDisconnectCaldera([out] uint32 argr);
+   [WmiMethodId(160), Implemented, read, write, Description("Tobii SW installation.")] void TobiiSWinstallation([out] uint32 argr);
+   [WmiMethodId(161), Implemented, read, write, Description("Tobii SW Released.")] void TobiiSWReleased([out] uint32 argr);
+   [WmiMethodId(162), Implemented, read, write, Description("Tobii Camera Power Reset.")] void TobiiCameraPowerReset([out] uint32 argr);
+   [WmiMethodId(163), Implemented, read, write, Description("Tobii Camera Power On.")] void TobiiCameraPowerOn([out] uint32 argr);
+   [WmiMethodId(164), Implemented, read, write, Description("Tobii Camera Power Off.")] void TobiiCameraPowerOff([out] uint32 argr);
+ };
+
+Some of these methods get quite intricate so we will describe them using
+pseudo-code that vaguely resembles the original ASL code.
+
+Argument Structure
+------------------
+
+All input arguments have type **uint32** and their structure is very similar
+between methods. Usually, the first byte corresponds to a specific *operation*
+the method performs, and the subsequent bytes correspond to *arguments* passed
+to this *operation*. For example, if an operation has code 0x01 and requires an
+ID 0xA0, the argument you would pass to the method is 0xA001.
+
+
+Thermal Methods
+===============
+
+WMI method Thermal_Information([in] uint32 arg2, [out] uint32 argr)
+-------------------------------------------------------------------
+
+::
+
+ if BYTE_0(arg2) == 0x01:
+         argr = 1
+
+ if BYTE_0(arg2) == 0x02:
+         argr = UNKNOWN_CONSTANT
+
+ if BYTE_0(arg2) == 0x03:
+         if BYTE_1(arg2) == 0x00:
+                 argr = FAN_ID_0
+
+         if BYTE_1(arg2) == 0x01:
+                 argr = FAN_ID_1
+
+         if BYTE_1(arg2) == 0x02:
+                 argr = FAN_ID_2
+
+         if BYTE_1(arg2) == 0x03:
+                 argr = FAN_ID_3
+
+         if BYTE_1(arg2) == 0x04:
+                 argr = SENSOR_ID_CPU | 0x0100
+
+         if BYTE_1(arg2) == 0x05:
+                 argr = SENSOR_ID_GPU | 0x0100
+
+         if BYTE_1(arg2) == 0x06:
+                 argr = THERMAL_MODE_QUIET_ID
+
+         if BYTE_1(arg2) == 0x07:
+                 argr = THERMAL_MODE_BALANCED_ID
+
+         if BYTE_1(arg2) == 0x08:
+                 argr = THERMAL_MODE_BALANCED_PERFORMANCE_ID
+
+         if BYTE_1(arg2) == 0x09:
+                 argr = THERMAL_MODE_PERFORMANCE_ID
+
+         if BYTE_1(arg2) == 0x0A:
+                 argr = THERMAL_MODE_LOW_POWER_ID
+
+         if BYTE_1(arg2) == 0x0B:
+                 argr = THERMAL_MODE_GMODE_ID
+
+         else:
+                 argr = 0xFFFFFFFF
+
+ if BYTE_0(arg2) == 0x04:
+         if is_valid_sensor(BYTE_1(arg2)):
+                 argr = SENSOR_TEMP_C
+         else:
+                 argr = 0xFFFFFFFF
+
+ if BYTE_0(arg2) == 0x05:
+         if is_valid_fan(BYTE_1(arg2)):
+                 argr = FAN_RPM()
+
+ if BYTE_0(arg2) == 0x06:
+         skip
+
+ if BYTE_0(arg2) == 0x07:
+         argr = 0
+
+ If BYTE_0(arg2) == 0x08:
+         if is_valid_fan(BYTE_1(arg2)):
+                 argr = 0
+         else:
+                 argr = 0xFFFFFFFF
+
+ if BYTE_0(arg2) == 0x09:
+         if is_valid_fan(BYTE_1(arg2)):
+                 argr = FAN_UNKNOWN_STAT_0()
+
+         else:
+                 argr = 0xFFFFFFFF
+
+ if BYTE_0(arg2) == 0x0A:
+         argr = THERMAL_MODE_BALANCED_ID
+
+ if BYTE_0(arg2) == 0x0B:
+         argr = CURRENT_THERMAL_MODE()
+
+ if BYTE_0(arg2) == 0x0C:
+         if is_valid_fan(BYTE_1(arg2)):
+                 argr = FAN_UNKNOWN_STAT_1()
+         else:
+                 argr = 0xFFFFFFFF
+
+WMI method Thermal_Control([in] uint32 arg2, [out] uint32 argr)
+---------------------------------------------------------------
+
+::
+
+ if BYTE_0(arg2) == 0x01:
+         if is_valid_thermal_profile(BYTE_1(arg2)):
+                 SET_THERMAL_PROFILE(BYTE_1(arg2))
+                 argr = 0
+
+ if BYTE_0(arg2) == 0x02:
+         if is_valid_fan(BYTE_1(arg2)):
+                 SET_FAN_SPEED_MULTIPLIER(BYTE_2(arg2))
+                 argr = 0
+         else:
+                 argr = 0xFFFFFFFF
+
+.. note::
+   While you can manually change the fan speed multiplier with this method,
+   Dell's BIOS tends to overwrite this changes anyway.
+
+These are the known thermal profile codes:
+
+::
+
+ CUSTOM                         0x00
+
+ QUIET                          0x96
+ BALANCED                       0x97
+ BALANCED_PERFORMANCE           0x98
+ PERFORMANCE                    0x99
+
+ QUIET_USTT                     0xA3
+ BALANCED_USTT                  0xA0
+ BALANCED_PERFORMANCE_USTT      0xA1
+ PERFORMANCE_USTT               0xA4
+ LOW_POWER_USTT                 0xA5
+
+ GMODE                          0xAB
+
+Usually if a model doesn't support the first four profiles they will support
+the User Selectable Thermal Tables (USTT) profiles and vice-versa.
+
+GMODE replaces PERFORMANCE in G-Series laptops.
+
+WMI method GetFanSensors([in] uint32 arg2, [out] uint32 argr)
+-------------------------------------------------------------
+
+::
+
+ if BYTE_0(arg2) == 1:
+        if is_valid_fan(BYTE_1(arg2)):
+                argr = 1
+        else:
+                argr = 0
+
+ if BYTE_0(arg2) == 2:
+        if is_valid_fan(BYTE_1(arg2)):
+                if BYTE_2(arg2) == 0:
+                        argr == SENSOR_ID
+                else
+                        argr == 0xFFFFFFFF
+        else:
+                argr = 0
+
+Overclocking Methods
+====================
+
+.. warning::
+   These methods have not been tested and are only partially reverse
+   engineered.
+
+WMI method Return_OverclockingReport([out] uint32 argr)
+-------------------------------------------------------
+
+::
+
+ CSMI (0xE3, 0x99)
+ argr = 0
+
+CSMI is an unknown operation.
+
+WMI method Set_OCUIBIOSControl([in] uint32 arg2, [out] uint32 argr)
+-------------------------------------------------------------------
+
+::
+
+ CSMI (0xE3, 0x99)
+ argr = 0
+
+CSMI is an unknown operation
+
+WMI method Clear_OCFailSafeFlag([out] uint32 argr)
+--------------------------------------------------
+
+::
+
+ CSMI (0xE3, 0x99)
+ argr = 0
+
+CSMI is an unknown operation
+
+
+WMI method MemoryOCControl([in] uint32 arg2, [out] uint32 argr)
+---------------------------------------------------------------
+
+AWCC supports memory overclocking, but this method is very intricate and has
+not been deciphered yet.
+
+GPIO methods
+============
+
+These methods are probably related to some kind of firmware update system,
+through a GPIO device.
+
+.. warning::
+   These methods have not been tested and are only partially reverse
+   engineered.
+
+WMI method FWUpdateGPIOtoggle([in] uint32 arg2, [out] uint32 argr)
+------------------------------------------------------------------
+
+::
+
+ if BYTE_0(arg2) == 0:
+         if BYTE_1(arg2) == 1:
+                 SET_PIN_A_HIGH()
+         else:
+                 SET_PIN_A_LOW()
+
+ if BYTE_0(arg2) == 1:
+         if BYTE_1(arg2) == 1:
+                 SET_PIN_B_HIGH()
+
+         else:
+                 SET_PIN_B_LOW()
+
+ else:
+         argr = 1
+
+WMI method ReadTotalofGPIOs([out] uint32 argr)
+----------------------------------------------
+
+::
+
+ argr = 0x02
+
+WMI method ReadGPIOpPinStatus([in] uint32 arg2, [out] uint32 argr)
+------------------------------------------------------------------
+
+::
+
+ if BYTE_0(arg2) == 0:
+         argr = PIN_A_STATUS
+
+ if BYTE_0(arg2) == 1:
+         argr = PIN_B_STATUS
+
+Other information Methods
+=========================
+
+WMI method SystemInformation([in] uint32 arg2, [out] uint32 argr)
+-----------------------------------------------------------------
+
+Returns unknown information.
+
+WMI method PowerInformation([in] uint32 arg2, [out] uint32 argr)
+----------------------------------------------------------------
+
+Returns unknown information.
+
+WMI method ReadChassisColor([out] uint32 argr)
+----------------------------------------------
+
+::
+
+ argr = CHASSIS_COLOR_ID
+
+WMI method ReadPlatformProperties([out] uint32 argr)
+----------------------------------------------------
+
+Returns unknown information.
+
+Acknowledgements
+================
+
+Kudos to `AlexIII <https://github.com/AlexIII/tcc-g15>`_ for documenting
+and testing avaliable thermal profile codes.
+
-- 
2.47.0
Re: [PATCH v5 4/4] alienware-wmi: WMAX interface documentation
Posted by Armin Wolf 1 month, 2 weeks ago
Am 12.10.24 um 04:03 schrieb Kurt Borja:

> Added documentation for new WMAX interface, present on some Alienware
> X-Series, Alienware M-Series and Dell's G-Series laptops.
>
> Signed-off-by: Kurt Borja <kuurtb@gmail.com>
> ---
>   Documentation/wmi/devices/alienware-wmi.rst | 366 ++++++++++++++++++++
>   1 file changed, 366 insertions(+)
>   create mode 100644 Documentation/wmi/devices/alienware-wmi.rst

Please update the MAINTAINERS entry for the alienware-wmi driver to
include this documentation file.

>
> diff --git a/Documentation/wmi/devices/alienware-wmi.rst b/Documentation/wmi/devices/alienware-wmi.rst
> new file mode 100644
> index 000000000..77460b91c
> --- /dev/null
> +++ b/Documentation/wmi/devices/alienware-wmi.rst
> @@ -0,0 +1,366 @@
> +.. SPDX-License-Identifier: GPL-2.0-or-later
> +
> +==============================================
> +Dell AWCC WMI interface driver (alienware-wmi)
> +==============================================
> +
> +Introduction
> +============
> +
> +The WMI device WMAX has been implemented for many Alienware and Dell's G-Series
> +models. Throughout these models, two implementations have been identified. The
> +first one, used by older systems, deals with HDMI, brightness, RGB, amplifier
> +and deep sleep control. The second one used by newer systems deals primarily
> +with thermal, overclocking, and GPIO control.
> +
> +It is suspected that the latter is used by Alienware Command Center (AWCC) to
> +manage manufacturer predefined thermal profiles. The alienware-wmi driver
> +exposes Thermal_Information and Thermal_Control methods through the Platform
> +Profile API to mimic AWCC's behavior.
> +
> +This newer interface, named AWCCMethodFunction has been reverse engineered, as
> +Dell has not provided any official documentation. We will try to describe to the
> +best of our ability its discovered inner workings.
> +
> +.. note::
> +   The following method description may vary between models.
> +
> +WMI interface description
> +-------------------------
> +
> +The WMI interface description can be decoded from the embedded binary MOF (bmof)
> +data using the `bmfdec <https://github.com/pali/bmfdec>`_ utility:
> +
> +::
> +
> + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("WMI Function"), guid("{A70591CE-A997-11DA-B012-B622A1EF5492}")]
> + class AWCCWmiMethodFunction {
> +   [key, read] string InstanceName;
> +   [read] boolean Active;
> +
> +   [WmiMethodId(13), Implemented, read, write, Description("Return Overclocking Report.")] void Return_OverclockingReport([out] uint32 argr);
> +   [WmiMethodId(14), Implemented, read, write, Description("Set OCUIBIOS Control.")] void Set_OCUIBIOSControl([in] uint32 arg2, [out] uint32 argr);
> +   [WmiMethodId(15), Implemented, read, write, Description("Clear OC FailSafe Flag.")] void Clear_OCFailSafeFlag([out] uint32 argr);
> +   [WmiMethodId(19), Implemented, read, write, Description("Get Fan Sensors.")] void GetFanSensors([in] uint32 arg2, [out] uint32 argr);
> +   [WmiMethodId(20), Implemented, read, write, Description("Thermal Information.")] void Thermal_Information([in] uint32 arg2, [out] uint32 argr);
> +   [WmiMethodId(21), Implemented, read, write, Description("Thermal Control.")] void Thermal_Control([in] uint32 arg2, [out] uint32 argr);
> +   [WmiMethodId(23), Implemented, read, write, Description("MemoryOCControl.")] void MemoryOCControl([in] uint32 arg2, [out] uint32 argr);
> +   [WmiMethodId(26), Implemented, read, write, Description("System Information.")] void SystemInformation([in] uint32 arg2, [out] uint32 argr);
> +   [WmiMethodId(28), Implemented, read, write, Description("Power Information.")] void PowerInformation([in] uint32 arg2, [out] uint32 argr);
> +   [WmiMethodId(32), Implemented, read, write, Description("FW Update GPIO toggle.")] void FWUpdateGPIOtoggle([in] uint32 arg2, [out] uint32 argr);
> +   [WmiMethodId(33), Implemented, read, write, Description("Read Total of GPIOs.")] void ReadTotalofGPIOs([out] uint32 argr);
> +   [WmiMethodId(34), Implemented, read, write, Description("Read GPIO pin Status.")] void ReadGPIOpPinStatus([in] uint32 arg2, [out] uint32 argr);
> +   [WmiMethodId(35), Implemented, read, write, Description("Read Chassis Color.")] void ReadChassisColor([out] uint32 argr);
> +   [WmiMethodId(36), Implemented, read, write, Description("Read Platform Properties.")] void ReadPlatformProperties([out] uint32 argr);
> +   [WmiMethodId(128), Implemented, read, write, Description("Caldera SW installation.")] void CalderaSWInstallation([out] uint32 argr);
> +   [WmiMethodId(129), Implemented, read, write, Description("Caldera SW is released.")] void CalderaSWReleased([out] uint32 argr);
> +   [WmiMethodId(130), Implemented, read, write, Description("Caldera Connection Status.")] void CalderaConnectionStatus([in] uint32 arg2, [out] uint32 argr);
> +   [WmiMethodId(131), Implemented, read, write, Description("Surprise Unplugged Flag Status.")] void SurpriseUnpluggedFlagStatus([out] uint32 argr);
> +   [WmiMethodId(132), Implemented, read, write, Description("Clear Surprise Unplugged Flag.")] void ClearSurpriseUnpluggedFlag([out] uint32 argr);
> +   [WmiMethodId(133), Implemented, read, write, Description("Cancel Undock Request.")] void CancelUndockRequest([out] uint32 argr);
> +   [WmiMethodId(135), Implemented, read, write, Description("Devices in Caldera.")] void DevicesInCaldera([in] uint32 arg2, [out] uint32 argr);
> +   [WmiMethodId(136), Implemented, read, write, Description("Notify BIOS for SW ready to disconnect Caldera.")] void NotifyBIOSForSWReadyToDisconnectCaldera([out] uint32 argr);
> +   [WmiMethodId(160), Implemented, read, write, Description("Tobii SW installation.")] void TobiiSWinstallation([out] uint32 argr);
> +   [WmiMethodId(161), Implemented, read, write, Description("Tobii SW Released.")] void TobiiSWReleased([out] uint32 argr);
> +   [WmiMethodId(162), Implemented, read, write, Description("Tobii Camera Power Reset.")] void TobiiCameraPowerReset([out] uint32 argr);
> +   [WmiMethodId(163), Implemented, read, write, Description("Tobii Camera Power On.")] void TobiiCameraPowerOn([out] uint32 argr);
> +   [WmiMethodId(164), Implemented, read, write, Description("Tobii Camera Power Off.")] void TobiiCameraPowerOff([out] uint32 argr);
> + };
> +
> +Some of these methods get quite intricate so we will describe them using
> +pseudo-code that vaguely resembles the original ASL code.
> +
> +Argument Structure
> +------------------
> +
> +All input arguments have type **uint32** and their structure is very similar
> +between methods. Usually, the first byte corresponds to a specific *operation*
> +the method performs, and the subsequent bytes correspond to *arguments* passed
> +to this *operation*. For example, if an operation has code 0x01 and requires an
> +ID 0xA0, the argument you would pass to the method is 0xA001.
> +
> +
> +Thermal Methods
> +===============
> +
> +WMI method Thermal_Information([in] uint32 arg2, [out] uint32 argr)
> +-------------------------------------------------------------------
> +
> +::
> +
> + if BYTE_0(arg2) == 0x01:
> +         argr = 1
> +
> + if BYTE_0(arg2) == 0x02:
> +         argr = UNKNOWN_CONSTANT
> +
> + if BYTE_0(arg2) == 0x03:
> +         if BYTE_1(arg2) == 0x00:
> +                 argr = FAN_ID_0
> +
> +         if BYTE_1(arg2) == 0x01:
> +                 argr = FAN_ID_1
> +
> +         if BYTE_1(arg2) == 0x02:
> +                 argr = FAN_ID_2
> +
> +         if BYTE_1(arg2) == 0x03:
> +                 argr = FAN_ID_3
> +
> +         if BYTE_1(arg2) == 0x04:
> +                 argr = SENSOR_ID_CPU | 0x0100
> +
> +         if BYTE_1(arg2) == 0x05:
> +                 argr = SENSOR_ID_GPU | 0x0100
> +
> +         if BYTE_1(arg2) == 0x06:
> +                 argr = THERMAL_MODE_QUIET_ID
> +
> +         if BYTE_1(arg2) == 0x07:
> +                 argr = THERMAL_MODE_BALANCED_ID
> +
> +         if BYTE_1(arg2) == 0x08:
> +                 argr = THERMAL_MODE_BALANCED_PERFORMANCE_ID
> +
> +         if BYTE_1(arg2) == 0x09:
> +                 argr = THERMAL_MODE_PERFORMANCE_ID
> +
> +         if BYTE_1(arg2) == 0x0A:
> +                 argr = THERMAL_MODE_LOW_POWER_ID
> +
> +         if BYTE_1(arg2) == 0x0B:
> +                 argr = THERMAL_MODE_GMODE_ID
> +
> +         else:
> +                 argr = 0xFFFFFFFF
> +
> + if BYTE_0(arg2) == 0x04:
> +         if is_valid_sensor(BYTE_1(arg2)):
> +                 argr = SENSOR_TEMP_C
> +         else:
> +                 argr = 0xFFFFFFFF
> +
> + if BYTE_0(arg2) == 0x05:
> +         if is_valid_fan(BYTE_1(arg2)):
> +                 argr = FAN_RPM()
> +
> + if BYTE_0(arg2) == 0x06:
> +         skip
> +
> + if BYTE_0(arg2) == 0x07:
> +         argr = 0
> +
> + If BYTE_0(arg2) == 0x08:
> +         if is_valid_fan(BYTE_1(arg2)):
> +                 argr = 0
> +         else:
> +                 argr = 0xFFFFFFFF
> +
> + if BYTE_0(arg2) == 0x09:
> +         if is_valid_fan(BYTE_1(arg2)):
> +                 argr = FAN_UNKNOWN_STAT_0()
> +
> +         else:
> +                 argr = 0xFFFFFFFF
> +
> + if BYTE_0(arg2) == 0x0A:
> +         argr = THERMAL_MODE_BALANCED_ID
> +
> + if BYTE_0(arg2) == 0x0B:
> +         argr = CURRENT_THERMAL_MODE()
> +
> + if BYTE_0(arg2) == 0x0C:
> +         if is_valid_fan(BYTE_1(arg2)):
> +                 argr = FAN_UNKNOWN_STAT_1()
> +         else:
> +                 argr = 0xFFFFFFFF
> +
> +WMI method Thermal_Control([in] uint32 arg2, [out] uint32 argr)
> +---------------------------------------------------------------
> +
> +::
> +
> + if BYTE_0(arg2) == 0x01:
> +         if is_valid_thermal_profile(BYTE_1(arg2)):
> +                 SET_THERMAL_PROFILE(BYTE_1(arg2))
> +                 argr = 0
> +
> + if BYTE_0(arg2) == 0x02:
> +         if is_valid_fan(BYTE_1(arg2)):
> +                 SET_FAN_SPEED_MULTIPLIER(BYTE_2(arg2))
> +                 argr = 0
> +         else:
> +                 argr = 0xFFFFFFFF
> +
> +.. note::
> +   While you can manually change the fan speed multiplier with this method,
> +   Dell's BIOS tends to overwrite this changes anyway.
> +
> +These are the known thermal profile codes:
> +
> +::
> +
> + CUSTOM                         0x00
> +
> + QUIET                          0x96
> + BALANCED                       0x97
> + BALANCED_PERFORMANCE           0x98
> + PERFORMANCE                    0x99
> +
> + QUIET_USTT                     0xA3
> + BALANCED_USTT                  0xA0
> + BALANCED_PERFORMANCE_USTT      0xA1
> + PERFORMANCE_USTT               0xA4
> + LOW_POWER_USTT                 0xA5
> +
> + GMODE                          0xAB
> +
> +Usually if a model doesn't support the first four profiles they will support
> +the User Selectable Thermal Tables (USTT) profiles and vice-versa.
> +
> +GMODE replaces PERFORMANCE in G-Series laptops.
> +
> +WMI method GetFanSensors([in] uint32 arg2, [out] uint32 argr)
> +-------------------------------------------------------------
> +
> +::
> +
> + if BYTE_0(arg2) == 1:
> +        if is_valid_fan(BYTE_1(arg2)):
> +                argr = 1
> +        else:
> +                argr = 0
> +
> + if BYTE_0(arg2) == 2:
> +        if is_valid_fan(BYTE_1(arg2)):
> +                if BYTE_2(arg2) == 0:
> +                        argr == SENSOR_ID
> +                else
> +                        argr == 0xFFFFFFFF
> +        else:
> +                argr = 0
> +
> +Overclocking Methods
> +====================
> +
> +.. warning::
> +   These methods have not been tested and are only partially reverse
> +   engineered.
> +
> +WMI method Return_OverclockingReport([out] uint32 argr)
> +-------------------------------------------------------
> +
> +::
> +
> + CSMI (0xE3, 0x99)
> + argr = 0
> +
> +CSMI is an unknown operation.
> +
> +WMI method Set_OCUIBIOSControl([in] uint32 arg2, [out] uint32 argr)
> +-------------------------------------------------------------------
> +
> +::
> +
> + CSMI (0xE3, 0x99)
> + argr = 0
> +
> +CSMI is an unknown operation

Missing ".".

> +
> +WMI method Clear_OCFailSafeFlag([out] uint32 argr)
> +--------------------------------------------------
> +
> +::
> +
> + CSMI (0xE3, 0x99)
> + argr = 0
> +
> +CSMI is an unknown operation

Missing ".".

> +
> +
> +WMI method MemoryOCControl([in] uint32 arg2, [out] uint32 argr)
> +---------------------------------------------------------------
> +
> +AWCC supports memory overclocking, but this method is very intricate and has
> +not been deciphered yet.
> +
> +GPIO methods
> +============
> +
> +These methods are probably related to some kind of firmware update system,
> +through a GPIO device.
> +
> +.. warning::
> +   These methods have not been tested and are only partially reverse
> +   engineered.
> +
> +WMI method FWUpdateGPIOtoggle([in] uint32 arg2, [out] uint32 argr)
> +------------------------------------------------------------------
> +
> +::
> +
> + if BYTE_0(arg2) == 0:
> +         if BYTE_1(arg2) == 1:
> +                 SET_PIN_A_HIGH()
> +         else:
> +                 SET_PIN_A_LOW()
> +
> + if BYTE_0(arg2) == 1:
> +         if BYTE_1(arg2) == 1:
> +                 SET_PIN_B_HIGH()
> +
> +         else:
> +                 SET_PIN_B_LOW()
> +
> + else:
> +         argr = 1
> +
> +WMI method ReadTotalofGPIOs([out] uint32 argr)
> +----------------------------------------------
> +
> +::
> +
> + argr = 0x02
> +
> +WMI method ReadGPIOpPinStatus([in] uint32 arg2, [out] uint32 argr)
> +------------------------------------------------------------------
> +
> +::
> +
> + if BYTE_0(arg2) == 0:
> +         argr = PIN_A_STATUS
> +
> + if BYTE_0(arg2) == 1:
> +         argr = PIN_B_STATUS
> +
> +Other information Methods
> +=========================
> +
> +WMI method SystemInformation([in] uint32 arg2, [out] uint32 argr)
> +-----------------------------------------------------------------
> +
> +Returns unknown information.
> +
> +WMI method PowerInformation([in] uint32 arg2, [out] uint32 argr)
> +----------------------------------------------------------------
> +
> +Returns unknown information.
> +
> +WMI method ReadChassisColor([out] uint32 argr)
> +----------------------------------------------
> +
> +::
> +
> + argr = CHASSIS_COLOR_ID
> +
> +WMI method ReadPlatformProperties([out] uint32 argr)
> +----------------------------------------------------
> +
> +Returns unknown information.
> +
> +Acknowledgements
> +================
> +
> +Kudos to `AlexIII <https://github.com/AlexIII/tcc-g15>`_ for documenting
> +and testing avaliable thermal profile codes.

avaliable -> available.

Other than that this looks very good.

Thanks,
Armin Wolf

> +
Re: [PATCH v5 4/4] alienware-wmi: WMAX interface documentation
Posted by Kurt Borja 1 month, 1 week ago
On Mon, Oct 14, 2024 at 07:10:15PM +0200, Armin Wolf wrote:
> Am 12.10.24 um 04:03 schrieb Kurt Borja:
> 
> > Added documentation for new WMAX interface, present on some Alienware
> > X-Series, Alienware M-Series and Dell's G-Series laptops.
> > 
> > Signed-off-by: Kurt Borja <kuurtb@gmail.com>
> > ---
> >   Documentation/wmi/devices/alienware-wmi.rst | 366 ++++++++++++++++++++
> >   1 file changed, 366 insertions(+)
> >   create mode 100644 Documentation/wmi/devices/alienware-wmi.rst
> 
> Please update the MAINTAINERS entry for the alienware-wmi driver to
> include this documentation file.

Ok.

> 
> > 
> > diff --git a/Documentation/wmi/devices/alienware-wmi.rst b/Documentation/wmi/devices/alienware-wmi.rst
> > new file mode 100644
> > index 000000000..77460b91c
> > --- /dev/null
> > +++ b/Documentation/wmi/devices/alienware-wmi.rst
> > @@ -0,0 +1,366 @@
> > +.. SPDX-License-Identifier: GPL-2.0-or-later
> > +
> > +==============================================
> > +Dell AWCC WMI interface driver (alienware-wmi)
> > +==============================================
> > +
> > +Introduction
> > +============
> > +
> > +The WMI device WMAX has been implemented for many Alienware and Dell's G-Series
> > +models. Throughout these models, two implementations have been identified. The
> > +first one, used by older systems, deals with HDMI, brightness, RGB, amplifier
> > +and deep sleep control. The second one used by newer systems deals primarily
> > +with thermal, overclocking, and GPIO control.
> > +
> > +It is suspected that the latter is used by Alienware Command Center (AWCC) to
> > +manage manufacturer predefined thermal profiles. The alienware-wmi driver
> > +exposes Thermal_Information and Thermal_Control methods through the Platform
> > +Profile API to mimic AWCC's behavior.
> > +
> > +This newer interface, named AWCCMethodFunction has been reverse engineered, as
> > +Dell has not provided any official documentation. We will try to describe to the
> > +best of our ability its discovered inner workings.
> > +
> > +.. note::
> > +   The following method description may vary between models.
> > +
> > +WMI interface description
> > +-------------------------
> > +
> > +The WMI interface description can be decoded from the embedded binary MOF (bmof)
> > +data using the `bmfdec <https://github.com/pali/bmfdec>`_ utility:
> > +
> > +::
> > +
> > + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("WMI Function"), guid("{A70591CE-A997-11DA-B012-B622A1EF5492}")]
> > + class AWCCWmiMethodFunction {
> > +   [key, read] string InstanceName;
> > +   [read] boolean Active;
> > +
> > +   [WmiMethodId(13), Implemented, read, write, Description("Return Overclocking Report.")] void Return_OverclockingReport([out] uint32 argr);
> > +   [WmiMethodId(14), Implemented, read, write, Description("Set OCUIBIOS Control.")] void Set_OCUIBIOSControl([in] uint32 arg2, [out] uint32 argr);
> > +   [WmiMethodId(15), Implemented, read, write, Description("Clear OC FailSafe Flag.")] void Clear_OCFailSafeFlag([out] uint32 argr);
> > +   [WmiMethodId(19), Implemented, read, write, Description("Get Fan Sensors.")] void GetFanSensors([in] uint32 arg2, [out] uint32 argr);
> > +   [WmiMethodId(20), Implemented, read, write, Description("Thermal Information.")] void Thermal_Information([in] uint32 arg2, [out] uint32 argr);
> > +   [WmiMethodId(21), Implemented, read, write, Description("Thermal Control.")] void Thermal_Control([in] uint32 arg2, [out] uint32 argr);
> > +   [WmiMethodId(23), Implemented, read, write, Description("MemoryOCControl.")] void MemoryOCControl([in] uint32 arg2, [out] uint32 argr);
> > +   [WmiMethodId(26), Implemented, read, write, Description("System Information.")] void SystemInformation([in] uint32 arg2, [out] uint32 argr);
> > +   [WmiMethodId(28), Implemented, read, write, Description("Power Information.")] void PowerInformation([in] uint32 arg2, [out] uint32 argr);
> > +   [WmiMethodId(32), Implemented, read, write, Description("FW Update GPIO toggle.")] void FWUpdateGPIOtoggle([in] uint32 arg2, [out] uint32 argr);
> > +   [WmiMethodId(33), Implemented, read, write, Description("Read Total of GPIOs.")] void ReadTotalofGPIOs([out] uint32 argr);
> > +   [WmiMethodId(34), Implemented, read, write, Description("Read GPIO pin Status.")] void ReadGPIOpPinStatus([in] uint32 arg2, [out] uint32 argr);
> > +   [WmiMethodId(35), Implemented, read, write, Description("Read Chassis Color.")] void ReadChassisColor([out] uint32 argr);
> > +   [WmiMethodId(36), Implemented, read, write, Description("Read Platform Properties.")] void ReadPlatformProperties([out] uint32 argr);
> > +   [WmiMethodId(128), Implemented, read, write, Description("Caldera SW installation.")] void CalderaSWInstallation([out] uint32 argr);
> > +   [WmiMethodId(129), Implemented, read, write, Description("Caldera SW is released.")] void CalderaSWReleased([out] uint32 argr);
> > +   [WmiMethodId(130), Implemented, read, write, Description("Caldera Connection Status.")] void CalderaConnectionStatus([in] uint32 arg2, [out] uint32 argr);
> > +   [WmiMethodId(131), Implemented, read, write, Description("Surprise Unplugged Flag Status.")] void SurpriseUnpluggedFlagStatus([out] uint32 argr);
> > +   [WmiMethodId(132), Implemented, read, write, Description("Clear Surprise Unplugged Flag.")] void ClearSurpriseUnpluggedFlag([out] uint32 argr);
> > +   [WmiMethodId(133), Implemented, read, write, Description("Cancel Undock Request.")] void CancelUndockRequest([out] uint32 argr);
> > +   [WmiMethodId(135), Implemented, read, write, Description("Devices in Caldera.")] void DevicesInCaldera([in] uint32 arg2, [out] uint32 argr);
> > +   [WmiMethodId(136), Implemented, read, write, Description("Notify BIOS for SW ready to disconnect Caldera.")] void NotifyBIOSForSWReadyToDisconnectCaldera([out] uint32 argr);
> > +   [WmiMethodId(160), Implemented, read, write, Description("Tobii SW installation.")] void TobiiSWinstallation([out] uint32 argr);
> > +   [WmiMethodId(161), Implemented, read, write, Description("Tobii SW Released.")] void TobiiSWReleased([out] uint32 argr);
> > +   [WmiMethodId(162), Implemented, read, write, Description("Tobii Camera Power Reset.")] void TobiiCameraPowerReset([out] uint32 argr);
> > +   [WmiMethodId(163), Implemented, read, write, Description("Tobii Camera Power On.")] void TobiiCameraPowerOn([out] uint32 argr);
> > +   [WmiMethodId(164), Implemented, read, write, Description("Tobii Camera Power Off.")] void TobiiCameraPowerOff([out] uint32 argr);
> > + };
> > +
> > +Some of these methods get quite intricate so we will describe them using
> > +pseudo-code that vaguely resembles the original ASL code.
> > +
> > +Argument Structure
> > +------------------
> > +
> > +All input arguments have type **uint32** and their structure is very similar
> > +between methods. Usually, the first byte corresponds to a specific *operation*
> > +the method performs, and the subsequent bytes correspond to *arguments* passed
> > +to this *operation*. For example, if an operation has code 0x01 and requires an
> > +ID 0xA0, the argument you would pass to the method is 0xA001.
> > +
> > +
> > +Thermal Methods
> > +===============
> > +
> > +WMI method Thermal_Information([in] uint32 arg2, [out] uint32 argr)
> > +-------------------------------------------------------------------
> > +
> > +::
> > +
> > + if BYTE_0(arg2) == 0x01:
> > +         argr = 1
> > +
> > + if BYTE_0(arg2) == 0x02:
> > +         argr = UNKNOWN_CONSTANT
> > +
> > + if BYTE_0(arg2) == 0x03:
> > +         if BYTE_1(arg2) == 0x00:
> > +                 argr = FAN_ID_0
> > +
> > +         if BYTE_1(arg2) == 0x01:
> > +                 argr = FAN_ID_1
> > +
> > +         if BYTE_1(arg2) == 0x02:
> > +                 argr = FAN_ID_2
> > +
> > +         if BYTE_1(arg2) == 0x03:
> > +                 argr = FAN_ID_3
> > +
> > +         if BYTE_1(arg2) == 0x04:
> > +                 argr = SENSOR_ID_CPU | 0x0100
> > +
> > +         if BYTE_1(arg2) == 0x05:
> > +                 argr = SENSOR_ID_GPU | 0x0100
> > +
> > +         if BYTE_1(arg2) == 0x06:
> > +                 argr = THERMAL_MODE_QUIET_ID
> > +
> > +         if BYTE_1(arg2) == 0x07:
> > +                 argr = THERMAL_MODE_BALANCED_ID
> > +
> > +         if BYTE_1(arg2) == 0x08:
> > +                 argr = THERMAL_MODE_BALANCED_PERFORMANCE_ID
> > +
> > +         if BYTE_1(arg2) == 0x09:
> > +                 argr = THERMAL_MODE_PERFORMANCE_ID
> > +
> > +         if BYTE_1(arg2) == 0x0A:
> > +                 argr = THERMAL_MODE_LOW_POWER_ID
> > +
> > +         if BYTE_1(arg2) == 0x0B:
> > +                 argr = THERMAL_MODE_GMODE_ID
> > +
> > +         else:
> > +                 argr = 0xFFFFFFFF
> > +
> > + if BYTE_0(arg2) == 0x04:
> > +         if is_valid_sensor(BYTE_1(arg2)):
> > +                 argr = SENSOR_TEMP_C
> > +         else:
> > +                 argr = 0xFFFFFFFF
> > +
> > + if BYTE_0(arg2) == 0x05:
> > +         if is_valid_fan(BYTE_1(arg2)):
> > +                 argr = FAN_RPM()
> > +
> > + if BYTE_0(arg2) == 0x06:
> > +         skip
> > +
> > + if BYTE_0(arg2) == 0x07:
> > +         argr = 0
> > +
> > + If BYTE_0(arg2) == 0x08:
> > +         if is_valid_fan(BYTE_1(arg2)):
> > +                 argr = 0
> > +         else:
> > +                 argr = 0xFFFFFFFF
> > +
> > + if BYTE_0(arg2) == 0x09:
> > +         if is_valid_fan(BYTE_1(arg2)):
> > +                 argr = FAN_UNKNOWN_STAT_0()
> > +
> > +         else:
> > +                 argr = 0xFFFFFFFF
> > +
> > + if BYTE_0(arg2) == 0x0A:
> > +         argr = THERMAL_MODE_BALANCED_ID
> > +
> > + if BYTE_0(arg2) == 0x0B:
> > +         argr = CURRENT_THERMAL_MODE()
> > +
> > + if BYTE_0(arg2) == 0x0C:
> > +         if is_valid_fan(BYTE_1(arg2)):
> > +                 argr = FAN_UNKNOWN_STAT_1()
> > +         else:
> > +                 argr = 0xFFFFFFFF
> > +
> > +WMI method Thermal_Control([in] uint32 arg2, [out] uint32 argr)
> > +---------------------------------------------------------------
> > +
> > +::
> > +
> > + if BYTE_0(arg2) == 0x01:
> > +         if is_valid_thermal_profile(BYTE_1(arg2)):
> > +                 SET_THERMAL_PROFILE(BYTE_1(arg2))
> > +                 argr = 0
> > +
> > + if BYTE_0(arg2) == 0x02:
> > +         if is_valid_fan(BYTE_1(arg2)):
> > +                 SET_FAN_SPEED_MULTIPLIER(BYTE_2(arg2))
> > +                 argr = 0
> > +         else:
> > +                 argr = 0xFFFFFFFF
> > +
> > +.. note::
> > +   While you can manually change the fan speed multiplier with this method,
> > +   Dell's BIOS tends to overwrite this changes anyway.
> > +
> > +These are the known thermal profile codes:
> > +
> > +::
> > +
> > + CUSTOM                         0x00
> > +
> > + QUIET                          0x96
> > + BALANCED                       0x97
> > + BALANCED_PERFORMANCE           0x98
> > + PERFORMANCE                    0x99
> > +
> > + QUIET_USTT                     0xA3
> > + BALANCED_USTT                  0xA0
> > + BALANCED_PERFORMANCE_USTT      0xA1
> > + PERFORMANCE_USTT               0xA4
> > + LOW_POWER_USTT                 0xA5
> > +
> > + GMODE                          0xAB
> > +
> > +Usually if a model doesn't support the first four profiles they will support
> > +the User Selectable Thermal Tables (USTT) profiles and vice-versa.
> > +
> > +GMODE replaces PERFORMANCE in G-Series laptops.
> > +
> > +WMI method GetFanSensors([in] uint32 arg2, [out] uint32 argr)
> > +-------------------------------------------------------------
> > +
> > +::
> > +
> > + if BYTE_0(arg2) == 1:
> > +        if is_valid_fan(BYTE_1(arg2)):
> > +                argr = 1
> > +        else:
> > +                argr = 0
> > +
> > + if BYTE_0(arg2) == 2:
> > +        if is_valid_fan(BYTE_1(arg2)):
> > +                if BYTE_2(arg2) == 0:
> > +                        argr == SENSOR_ID
> > +                else
> > +                        argr == 0xFFFFFFFF
> > +        else:
> > +                argr = 0
> > +
> > +Overclocking Methods
> > +====================
> > +
> > +.. warning::
> > +   These methods have not been tested and are only partially reverse
> > +   engineered.
> > +
> > +WMI method Return_OverclockingReport([out] uint32 argr)
> > +-------------------------------------------------------
> > +
> > +::
> > +
> > + CSMI (0xE3, 0x99)
> > + argr = 0
> > +
> > +CSMI is an unknown operation.
> > +
> > +WMI method Set_OCUIBIOSControl([in] uint32 arg2, [out] uint32 argr)
> > +-------------------------------------------------------------------
> > +
> > +::
> > +
> > + CSMI (0xE3, 0x99)
> > + argr = 0
> > +
> > +CSMI is an unknown operation
> 
> Missing ".".
> 
> > +
> > +WMI method Clear_OCFailSafeFlag([out] uint32 argr)
> > +--------------------------------------------------
> > +
> > +::
> > +
> > + CSMI (0xE3, 0x99)
> > + argr = 0
> > +
> > +CSMI is an unknown operation
> 
> Missing ".".
> 
> > +
> > +
> > +WMI method MemoryOCControl([in] uint32 arg2, [out] uint32 argr)
> > +---------------------------------------------------------------
> > +
> > +AWCC supports memory overclocking, but this method is very intricate and has
> > +not been deciphered yet.
> > +
> > +GPIO methods
> > +============
> > +
> > +These methods are probably related to some kind of firmware update system,
> > +through a GPIO device.
> > +
> > +.. warning::
> > +   These methods have not been tested and are only partially reverse
> > +   engineered.
> > +
> > +WMI method FWUpdateGPIOtoggle([in] uint32 arg2, [out] uint32 argr)
> > +------------------------------------------------------------------
> > +
> > +::
> > +
> > + if BYTE_0(arg2) == 0:
> > +         if BYTE_1(arg2) == 1:
> > +                 SET_PIN_A_HIGH()
> > +         else:
> > +                 SET_PIN_A_LOW()
> > +
> > + if BYTE_0(arg2) == 1:
> > +         if BYTE_1(arg2) == 1:
> > +                 SET_PIN_B_HIGH()
> > +
> > +         else:
> > +                 SET_PIN_B_LOW()
> > +
> > + else:
> > +         argr = 1
> > +
> > +WMI method ReadTotalofGPIOs([out] uint32 argr)
> > +----------------------------------------------
> > +
> > +::
> > +
> > + argr = 0x02
> > +
> > +WMI method ReadGPIOpPinStatus([in] uint32 arg2, [out] uint32 argr)
> > +------------------------------------------------------------------
> > +
> > +::
> > +
> > + if BYTE_0(arg2) == 0:
> > +         argr = PIN_A_STATUS
> > +
> > + if BYTE_0(arg2) == 1:
> > +         argr = PIN_B_STATUS
> > +
> > +Other information Methods
> > +=========================
> > +
> > +WMI method SystemInformation([in] uint32 arg2, [out] uint32 argr)
> > +-----------------------------------------------------------------
> > +
> > +Returns unknown information.
> > +
> > +WMI method PowerInformation([in] uint32 arg2, [out] uint32 argr)
> > +----------------------------------------------------------------
> > +
> > +Returns unknown information.
> > +
> > +WMI method ReadChassisColor([out] uint32 argr)
> > +----------------------------------------------
> > +
> > +::
> > +
> > + argr = CHASSIS_COLOR_ID
> > +
> > +WMI method ReadPlatformProperties([out] uint32 argr)
> > +----------------------------------------------------
> > +
> > +Returns unknown information.
> > +
> > +Acknowledgements
> > +================
> > +
> > +Kudos to `AlexIII <https://github.com/AlexIII/tcc-g15>`_ for documenting
> > +and testing avaliable thermal profile codes.
> 
> avaliable -> available.
> 
> Other than that this looks very good.
> 
> Thanks,
> Armin Wolf

I will fix the typos. Thanks.

Kurt

> 
> > +
Re: [PATCH] Dell AWCC platform_profile support
Posted by Armin Wolf 1 month, 3 weeks ago
Am 07.10.24 um 11:33 schrieb Kurt Borja:

> This patch adds platform_profile support for Dell devices which implement
> User Selectable Thermal Tables (USTT) that are meant to be controlled by
> Alienware Command Center (AWCC). These devices may include newer Alienware
> M-Series, Alienware X-Series and Dell's G-Series. This patch, was tested
> by me on an Alienware x15 R1.
>
> It is suspected that Alienware Command Center manages thermal profiles
> through the WMI interface, specifically through a device with identifier
> \_SB_.AMW1.WMAX. This device was reverse engineered and the relevant
> functionality is documented here [1]. This driver interacts with this
> WMI device and thus is able to mimic AWCC's thermal profiles functionality
> through the platform_profile API. In consequence the user would be able
> to set and retrieve thermal profiles, which are just fan speed profiles.
>
> This driver was heavily inspired on inspur_platform_profile, special
> thanks.
>
> Notes:
>   - Performance (FullSpeed) profile is a special profile which has it's own
>     entry in the Firmware Settings of the Alienware x15 R1. It also changes
>     the color of the F1 key. I suspect this behavior would be replicated in
>     other X-Series or M-Series laptops.
>   - G-Mode is a profile documented on [1] which mimics the behavior of
>     FullSpeed mode but it does not have an entry on the Firmware Settings of
>     the Alienware x15 R1, this may correspond to the G-Mode functionality on
>     G-Series laptops (activated by a special button) but I cannot test it. I
>     did not include this code in the driver as G-Mode causes unexpected
>     behavior on X-Series laptops.
>
> Thanks for your time and patiente in advance.
>
> Regards,
>
> Kurt
>
> [1] https://gist.github.com/kuu-rt/b22328ff2b454be505387e2a38c61ee4

Hi,

this WMI device is already handled by the alienware-wmi driver. Could you please integrate
this functionality into this driver instead of creating a new one?

Thanks,
Armin Wolf

> Signed-off-by: Kurt Borja <kuurtb@gmail.com>
> ---
>   drivers/platform/x86/dell/Kconfig         |   9 +
>   drivers/platform/x86/dell/Makefile        |   1 +
>   drivers/platform/x86/dell/dell-wmi-awcc.c | 204 ++++++++++++++++++++++
>   3 files changed, 214 insertions(+)
>   create mode 100644 drivers/platform/x86/dell/dell-wmi-awcc.c
>
> diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig
> index 68a49788a..20300ff98 100644
> --- a/drivers/platform/x86/dell/Kconfig
> +++ b/drivers/platform/x86/dell/Kconfig
> @@ -27,6 +27,15 @@ config ALIENWARE_WMI
>   	 zones on Alienware machines that don't contain a dedicated AlienFX
>   	 USB MCU such as the X51 and X51-R2.
>
> +config AWCC_PLATFORM_PROFILE
> +	tristate "AWCC Platform Profile support"
> +	depends on ACPI_WMI
> +	select ACPI_PLATFORM_PROFILE
> +	help
> +	 This driver provides platform_profile support for selecting thermal
> +	 profiles on Dell devices with User Selectable Thermal Tables,
> +	 controlled by AWCC's WMI interface.
> +
>   config DCDBAS
>   	tristate "Dell Systems Management Base Driver"
>   	default m
> diff --git a/drivers/platform/x86/dell/Makefile b/drivers/platform/x86/dell/Makefile
> index 79d60f1bf..bfef99580 100644
> --- a/drivers/platform/x86/dell/Makefile
> +++ b/drivers/platform/x86/dell/Makefile
> @@ -23,4 +23,5 @@ obj-$(CONFIG_DELL_WMI_AIO)		+= dell-wmi-aio.o
>   obj-$(CONFIG_DELL_WMI_DESCRIPTOR)	+= dell-wmi-descriptor.o
>   obj-$(CONFIG_DELL_WMI_DDV)		+= dell-wmi-ddv.o
>   obj-$(CONFIG_DELL_WMI_LED)		+= dell-wmi-led.o
> +obj-$(CONFIG_AWCC_PLATFORM_PROFILE)	+= dell-wmi-awcc.o
>   obj-$(CONFIG_DELL_WMI_SYSMAN)		+= dell-wmi-sysman/
> diff --git a/drivers/platform/x86/dell/dell-wmi-awcc.c b/drivers/platform/x86/dell/dell-wmi-awcc.c
> new file mode 100644
> index 000000000..0837d1bc6
> --- /dev/null
> +++ b/drivers/platform/x86/dell/dell-wmi-awcc.c
> @@ -0,0 +1,204 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + *  WMI driver for Dell's AWCC platform_profile
> + *
> + *  Copyright (c) Kurt Borja <kuurtb@gmail.com>
> + *
> + */
> +
> +#include <linux/acpi.h>
> +#include <linux/device.h>
> +#include <linux/module.h>
> +#include <linux/platform_profile.h>
> +#include <linux/wmi.h>
> +
> +#define PROF_TO_ARG(mode) ((mode << 8) | 1)
> +
> +#define DELL_AWCC_GUID "A70591CE-A997-11DA-B012-B622A1EF5492"
> +
> +enum awcc_wmi_method {
> +	AWCC_WMI_THERMAL_INFORMATION = 0x14,
> +	AWCC_WMI_THERMAL_CONTROL = 0x15,
> +};
> +
> +enum awcc_tmp_profile {
> +	AWCC_TMP_PROFILE_BALANCED = 0xA0,
> +	AWCC_TMP_PROFILE_BALANCED_PERFORMANCE = 0xA1,
> +	AWCC_TMP_PROFILE_COOL = 0xA2,
> +	AWCC_TMP_PROFILE_QUIET = 0xA3,
> +	AWCC_TMP_PROFILE_PERFORMANCE = 0xA4,
> +	AWCC_TMP_PROFILE_LOW_POWER = 0xA5,
> +};
> +
> +struct awcc_wmi_priv {
> +	struct wmi_device *wdev;
> +	struct platform_profile_handler handler;
> +};
> +
> +static int awcc_wmi_query(struct wmi_device *wdev, enum awcc_wmi_method method,
> +			  u32 arg, u32 *res)
> +{
> +	struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
> +	const struct acpi_buffer in = { sizeof(arg), &arg };
> +	union acpi_object *obj;
> +	acpi_status status;
> +	int ret = 0;
> +
> +	status = wmidev_evaluate_method(wdev, 0x0, method, &in, &out);
> +
> +	if (ACPI_FAILURE(status))
> +		return -EIO;
> +
> +	obj = out.pointer;
> +	if (!obj)
> +		return -ENODATA;
> +
> +	if (obj->type != ACPI_TYPE_INTEGER) {
> +		ret = -EINVAL;
> +		goto out_free;
> +	}
> +
> +	if (obj->integer.value <= U32_MAX)
> +		*res = (u32)obj->integer.value;
> +	else
> +		ret = -ERANGE;
> +
> +out_free:
> +	kfree(obj);
> +
> +	return ret;
> +}
> +
> +static int awcc_platform_profile_get(struct platform_profile_handler *pprof,
> +				     enum platform_profile_option *profile)
> +{
> +	struct awcc_wmi_priv *priv =
> +		container_of(pprof, struct awcc_wmi_priv, handler);
> +
> +	u32 res;
> +	int ret;
> +
> +	ret = awcc_wmi_query(priv->wdev, AWCC_WMI_THERMAL_INFORMATION, 0x0B,
> +			     &res);
> +
> +	if (ret < 0)
> +		return ret;
> +
> +	if (res < 0)
> +		return -EBADRQC;
> +
> +	switch (res) {
> +	case AWCC_TMP_PROFILE_LOW_POWER:
> +		*profile = PLATFORM_PROFILE_LOW_POWER;
> +		break;
> +	case AWCC_TMP_PROFILE_QUIET:
> +		*profile = PLATFORM_PROFILE_QUIET;
> +		break;
> +	case AWCC_TMP_PROFILE_BALANCED:
> +		*profile = PLATFORM_PROFILE_BALANCED;
> +		break;
> +	case AWCC_TMP_PROFILE_BALANCED_PERFORMANCE:
> +		*profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE;
> +		break;
> +	case AWCC_TMP_PROFILE_PERFORMANCE:
> +		*profile = PLATFORM_PROFILE_PERFORMANCE;
> +		break;
> +	default:
> +		return -ENODATA;
> +	}
> +
> +	return 0;
> +}
> +
> +static int awcc_platform_profile_set(struct platform_profile_handler *pprof,
> +				     enum platform_profile_option profile)
> +{
> +	struct awcc_wmi_priv *priv =
> +		container_of(pprof, struct awcc_wmi_priv, handler);
> +
> +	u32 arg;
> +	u32 res;
> +	int ret;
> +
> +	switch (profile) {
> +	case PLATFORM_PROFILE_LOW_POWER:
> +		arg = PROF_TO_ARG(AWCC_TMP_PROFILE_LOW_POWER);
> +		break;
> +	case PLATFORM_PROFILE_QUIET:
> +		arg = PROF_TO_ARG(AWCC_TMP_PROFILE_QUIET);
> +		break;
> +	case PLATFORM_PROFILE_BALANCED:
> +		arg = PROF_TO_ARG(AWCC_TMP_PROFILE_BALANCED);
> +		break;
> +	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
> +		arg = PROF_TO_ARG(AWCC_TMP_PROFILE_BALANCED_PERFORMANCE);
> +		break;
> +	case PLATFORM_PROFILE_PERFORMANCE:
> +		arg = PROF_TO_ARG(AWCC_TMP_PROFILE_PERFORMANCE);
> +		break;
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +
> +	ret = awcc_wmi_query(priv->wdev, AWCC_WMI_THERMAL_CONTROL, arg, &res);
> +
> +	if (ret < 0)
> +		return ret;
> +
> +	if (res < 0)
> +		return -EBADRQC;
> +
> +	return 0;
> +}
> +
> +static int awcc_wmi_probe(struct wmi_device *wdev, const void *context)
> +{
> +	struct awcc_wmi_priv *priv;
> +
> +	priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->wdev = wdev;
> +	dev_set_drvdata(&wdev->dev, priv);
> +
> +	priv->handler.profile_set = awcc_platform_profile_set;
> +	priv->handler.profile_get = awcc_platform_profile_get;
> +
> +	set_bit(PLATFORM_PROFILE_LOW_POWER, priv->handler.choices);
> +	set_bit(PLATFORM_PROFILE_QUIET, priv->handler.choices);
> +	set_bit(PLATFORM_PROFILE_BALANCED, priv->handler.choices);
> +	set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, priv->handler.choices);
> +	set_bit(PLATFORM_PROFILE_PERFORMANCE, priv->handler.choices);
> +
> +	return platform_profile_register(&priv->handler);
> +}
> +
> +static void awcc_wmi_remove(struct wmi_device *wdev)
> +{
> +	platform_profile_remove();
> +}
> +
> +static const struct wmi_device_id awcc_wmi_id_table[] = {
> +	{ .guid_string = DELL_AWCC_GUID },
> +	{},
> +};
> +
> +MODULE_DEVICE_TABLE(wmi, awcc_wmi_id_table);
> +
> +static struct wmi_driver awcc_wmi_driver = {
> +	.driver = {
> +		.name = "dell-wmi-awcc-platform-profile",
> +		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
> +	},
> +	.id_table = awcc_wmi_id_table,
> +	.probe = awcc_wmi_probe,
> +	.remove = awcc_wmi_remove,
> +	.no_singleton = true,
> +};
> +
> +module_wmi_driver(awcc_wmi_driver);
> +
> +MODULE_AUTHOR("Kurt Borja");
> +MODULE_DESCRIPTION("Dell AWCC WMI driver");
> +MODULE_LICENSE("GPL");
Re: [PATCH] Dell AWCC platform_profile support
Posted by Kurt Borja 1 month, 3 weeks ago
On Mon, Oct 07, 2024 at 02:24:52PM +0200, Armin Wolf wrote:
> Hi,
>
> this WMI device is already handled by the alienware-wmi driver. Could you please integrate
> this functionality into this driver instead of creating a new one?
>
> Thanks,
> Armin Wolf

Hi,

Thank you for your feedback.

Although they the same name and same GUID, both interfaces are very
different. Alienware x15's WMAX method doesn't support any of the methods
listed on alienware-wmi driver and the de-compiled MOF file on [1] which is
an open source alternative to AWCC, makes me think this might be the case
for various other newer models (G, M, X Series).

Still I could implement it as a quirk of newer models. Would this be ok?

My only worry was that it could make alienware-wmi's logic overly complex
and cumbersome, as it would support two very different interfaces with the
same GUID.


Kurt

[1] https://github.com/AlexIII/tcc-g15/blob/master/WMI-AWCC-doc.md
[PATCH] alienware-wmi: Dell AWCC platform_profile support
Posted by Kurt Borja 1 month, 2 weeks ago
This patch adds platform_profile support for Dell devices which implement
User Selectable Thermal Tables (USTT) that are meant to be controlled by
Alienware Command Center (AWCC). These devices may include newer Alienware
M-Series, Alienware X-Series and Dell's G-Series. This patch, was tested
by me on an Alienware x15 R1.

It is suspected that Alienware Command Center manages thermal profiles
through the WMI interface, specifically through a device with identifier
\_SB_.AMW1.WMAX. This device was reverse engineered and the relevant
functionality is documented here [1]. This driver interacts with this
WMI device and thus is able to mimic AWCC's thermal profiles functionality
through the platform_profile API. In consequence the user would be able
to set and retrieve thermal profiles, which are just fan speed profiles.

This driver was heavily inspired on inspur_platform_profile, special
thanks.

Notes:
 - Performance (FullSpeed) profile is a special profile which has it's own
   entry in the Firmware Settings of the Alienware x15 R1. It also changes
   the color of the F1 key. I suspect this behavior would be replicated in
   other X-Series or M-Series laptops.
 - G-Mode is a profile documented on [1] which mimics the behavior of
   FullSpeed mode but it does not have an entry on the Firmware Settings of
   the Alienware x15 R1, this may correspond to the G-Mode functionality on
   G-Series laptops (activated by a special button) but I cannot test it. I
   did not include this code in the driver as G-Mode causes unexpected
   behavior on X-Series laptops.

Signed-off-by: Kurt Borja <kuurtb@gmail.com>

---
v3:
 - Removed extra empty line
 - 0x0B named WMAX_ARG_GET_CURRENT_PROF
 - Removed casts to the same type on functions added in this patch
 - Thermal profile to WMAX argument is now an static function and makes
   use of in-built kernel macros
 - Platform profile is now removed only if it was created first
 - create_platform_profile is now create_thermal_profile to avoid
   confusion
 - profile_get and profile_set functions renamed too to match the above
v2:
 - Moved functionality to alienware-wmi driver
 - Added thermal and gmode quirks to add support based on dmi match
 - Performance profile is now GMODE for devices that support it
 - alienware_wmax_command now is insize agnostic to support new thermal
   methods
---
 drivers/platform/x86/dell/Kconfig         |   1 +
 drivers/platform/x86/dell/alienware-wmi.c | 238 ++++++++++++++++++++--
 2 files changed, 226 insertions(+), 13 deletions(-)

diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig
index 68a49788a..b06d634cd 100644
--- a/drivers/platform/x86/dell/Kconfig
+++ b/drivers/platform/x86/dell/Kconfig
@@ -21,6 +21,7 @@ config ALIENWARE_WMI
 	depends on LEDS_CLASS
 	depends on NEW_LEDS
 	depends on ACPI_WMI
+	select ACPI_PLATFORM_PROFILE
 	help
 	 This is a driver for controlling Alienware BIOS driven
 	 features.  It exposes an interface for controlling the AlienFX
diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
index f5ee62ce1..e3ef4b10b 100644
--- a/drivers/platform/x86/dell/alienware-wmi.c
+++ b/drivers/platform/x86/dell/alienware-wmi.c
@@ -10,6 +10,7 @@
 #include <linux/acpi.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
+#include <linux/platform_profile.h>
 #include <linux/dmi.h>
 #include <linux/leds.h>
 
@@ -25,6 +26,10 @@
 #define WMAX_METHOD_AMPLIFIER_CABLE	0x6
 #define WMAX_METHOD_DEEP_SLEEP_CONTROL	0x0B
 #define WMAX_METHOD_DEEP_SLEEP_STATUS	0x0C
+#define WMAX_METHOD_THERMAL_INFORMATION	0x14
+#define WMAX_METHOD_THERMAL_CONTROL	0x15
+
+#define WMAX_ARG_GET_CURRENT_PROF	0x0B
 
 MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
 MODULE_DESCRIPTION("Alienware special feature control");
@@ -49,11 +54,22 @@ enum WMAX_CONTROL_STATES {
 	WMAX_SUSPEND = 3,
 };
 
+enum WMAX_THERMAL_PROFILE {
+	WMAX_THERMAL_QUIET = 0xA3,
+	WMAX_THERMAL_BALANCED = 0xA0,
+	WMAX_THERMAL_BALANCED_PERFORMANCE = 0xA1,
+	WMAX_THERMAL_PERFORMANCE = 0xA4,
+	WMAX_THERMAL_GMODE = 0xAB,
+	WMAX_THERMAL_LOW_POWER	= 0xA5,
+};
+
 struct quirk_entry {
 	u8 num_zones;
 	u8 hdmi_mux;
 	u8 amplifier;
 	u8 deepslp;
+	u8 thermal;
+	u8 gmode;
 };
 
 static struct quirk_entry *quirks;
@@ -64,6 +80,8 @@ static struct quirk_entry quirk_inspiron5675 = {
 	.hdmi_mux = 0,
 	.amplifier = 0,
 	.deepslp = 0,
+	.thermal = 0,
+	.gmode = 0,
 };
 
 static struct quirk_entry quirk_unknown = {
@@ -71,6 +89,8 @@ static struct quirk_entry quirk_unknown = {
 	.hdmi_mux = 0,
 	.amplifier = 0,
 	.deepslp = 0,
+	.thermal = 0,
+	.gmode = 0,
 };
 
 static struct quirk_entry quirk_x51_r1_r2 = {
@@ -78,6 +98,8 @@ static struct quirk_entry quirk_x51_r1_r2 = {
 	.hdmi_mux = 0,
 	.amplifier = 0,
 	.deepslp = 0,
+	.thermal = 0,
+	.gmode = 0,
 };
 
 static struct quirk_entry quirk_x51_r3 = {
@@ -85,6 +107,8 @@ static struct quirk_entry quirk_x51_r3 = {
 	.hdmi_mux = 0,
 	.amplifier = 1,
 	.deepslp = 0,
+	.thermal = 0,
+	.gmode = 0,
 };
 
 static struct quirk_entry quirk_asm100 = {
@@ -92,6 +116,8 @@ static struct quirk_entry quirk_asm100 = {
 	.hdmi_mux = 1,
 	.amplifier = 0,
 	.deepslp = 0,
+	.thermal = 0,
+	.gmode = 0,
 };
 
 static struct quirk_entry quirk_asm200 = {
@@ -99,6 +125,8 @@ static struct quirk_entry quirk_asm200 = {
 	.hdmi_mux = 1,
 	.amplifier = 0,
 	.deepslp = 1,
+	.thermal = 0,
+	.gmode = 0,
 };
 
 static struct quirk_entry quirk_asm201 = {
@@ -106,6 +134,17 @@ static struct quirk_entry quirk_asm201 = {
 	.hdmi_mux = 1,
 	.amplifier = 1,
 	.deepslp = 1,
+	.thermal = 0,
+	.gmode = 0,
+};
+
+static struct quirk_entry quirk_x15_r1 = {
+	.num_zones = 2,
+	.hdmi_mux = 0,
+	.amplifier = 0,
+	.deepslp = 0,
+	.thermal = 1,
+	.gmode = 0,
 };
 
 static int __init dmi_matched(const struct dmi_system_id *dmi)
@@ -169,6 +208,15 @@ static const struct dmi_system_id alienware_quirks[] __initconst = {
 		     },
 	 .driver_data = &quirk_asm201,
 	 },
+	 {
+	 .callback = dmi_matched,
+	 .ident = "Alienware x15 R1",
+	 .matches = {
+		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+		     DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R1")
+		    },
+	 .driver_data = &quirk_x15_r1,
+	},
 	 {
 	 .callback = dmi_matched,
 	 .ident = "Dell Inc. Inspiron 5675",
@@ -218,6 +266,7 @@ static struct platform_device *platform_device;
 static struct device_attribute *zone_dev_attrs;
 static struct attribute **zone_attrs;
 static struct platform_zone *zone_data;
+static struct platform_profile_handler pp_handler;
 
 static struct platform_driver platform_driver = {
 	.driver = {
@@ -500,7 +549,7 @@ static void alienware_zone_exit(struct platform_device *dev)
 	kfree(zone_attrs);
 }
 
-static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
+static acpi_status alienware_wmax_command(void *in_args, size_t insize,
 					  u32 command, int *out_data)
 {
 	acpi_status status;
@@ -508,7 +557,7 @@ static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
 	struct acpi_buffer input;
 	struct acpi_buffer output;
 
-	input.length = (acpi_size) sizeof(*in_args);
+	input.length = (acpi_size) insize;
 	input.pointer = in_args;
 	if (out_data) {
 		output.length = ACPI_ALLOCATE_BUFFER;
@@ -541,8 +590,8 @@ static ssize_t show_hdmi_cable(struct device *dev,
 		.arg = 0,
 	};
 	status =
-	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE,
-				   (u32 *) &out_data);
+	    alienware_wmax_command(&in_args, sizeof(in_args),
+				   WMAX_METHOD_HDMI_CABLE, (u32 *) &out_data);
 	if (ACPI_SUCCESS(status)) {
 		if (out_data == 0)
 			return sysfs_emit(buf, "[unconnected] connected unknown\n");
@@ -562,8 +611,8 @@ static ssize_t show_hdmi_source(struct device *dev,
 		.arg = 0,
 	};
 	status =
-	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS,
-				   (u32 *) &out_data);
+	    alienware_wmax_command(&in_args, sizeof(in_args),
+				   WMAX_METHOD_HDMI_STATUS, (u32 *) &out_data);
 
 	if (ACPI_SUCCESS(status)) {
 		if (out_data == 1)
@@ -589,7 +638,8 @@ static ssize_t toggle_hdmi_source(struct device *dev,
 		args.arg = 3;
 	pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf);
 
-	status = alienware_wmax_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL);
+	status = alienware_wmax_command(&args, sizeof(args),
+					WMAX_METHOD_HDMI_SOURCE, NULL);
 
 	if (ACPI_FAILURE(status))
 		pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
@@ -642,8 +692,8 @@ static ssize_t show_amplifier_status(struct device *dev,
 		.arg = 0,
 	};
 	status =
-	    alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE,
-				   (u32 *) &out_data);
+	    alienware_wmax_command(&in_args, sizeof(in_args),
+				   WMAX_METHOD_AMPLIFIER_CABLE, (u32 *) &out_data);
 	if (ACPI_SUCCESS(status)) {
 		if (out_data == 0)
 			return sysfs_emit(buf, "[unconnected] connected unknown\n");
@@ -694,8 +744,8 @@ static ssize_t show_deepsleep_status(struct device *dev,
 	struct wmax_basic_args in_args = {
 		.arg = 0,
 	};
-	status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS,
-					(u32 *) &out_data);
+	status = alienware_wmax_command(&in_args, sizeof(in_args),
+					WMAX_METHOD_DEEP_SLEEP_STATUS, (u32 *) &out_data);
 	if (ACPI_SUCCESS(status)) {
 		if (out_data == 0)
 			return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
@@ -723,8 +773,8 @@ static ssize_t toggle_deepsleep(struct device *dev,
 		args.arg = 2;
 	pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf);
 
-	status = alienware_wmax_command(&args, WMAX_METHOD_DEEP_SLEEP_CONTROL,
-					NULL);
+	status = alienware_wmax_command(&args, sizeof(args),
+					WMAX_METHOD_DEEP_SLEEP_CONTROL, NULL);
 
 	if (ACPI_FAILURE(status))
 		pr_err("alienware-wmi: deep sleep control failed: results: %u\n",
@@ -760,6 +810,160 @@ static int create_deepsleep(struct platform_device *dev)
 	return ret;
 }
 
+/*
+ * Thermal Profile control
+ *  - Provides thermal profile control through the Platform Profile API
+ */
+#define PROFILE_MASK		GENMASK(15,8)
+#define PROFILE_ACTIVATE	BIT(0)
+
+static u32 profile_to_wmax_arg(enum WMAX_THERMAL_PROFILE prof)
+{
+	return FIELD_PREP(PROFILE_MASK, prof) | PROFILE_ACTIVATE;
+}
+
+static int thermal_profile_get(struct platform_profile_handler *pprof,
+				enum platform_profile_option *profile)
+{
+	acpi_status status;
+	u32 in_args = WMAX_ARG_GET_CURRENT_PROF;
+	u32 out_data;
+
+	status = alienware_wmax_command(&in_args, sizeof(in_args),
+					WMAX_METHOD_THERMAL_INFORMATION, &out_data);
+
+	if (ACPI_FAILURE(status))
+		return -EOPNOTSUPP;
+
+	if (out_data == 0xFFFFFFFF)
+		return -EBADRQC;
+
+	switch (out_data) {
+	case WMAX_THERMAL_LOW_POWER:
+		*profile = PLATFORM_PROFILE_LOW_POWER;
+		break;
+	case WMAX_THERMAL_QUIET:
+		*profile = PLATFORM_PROFILE_QUIET;
+		break;
+	case WMAX_THERMAL_BALANCED:
+		*profile = PLATFORM_PROFILE_BALANCED;
+		break;
+	case WMAX_THERMAL_BALANCED_PERFORMANCE:
+		*profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE;
+		break;
+	case WMAX_THERMAL_PERFORMANCE:
+	case WMAX_THERMAL_GMODE:
+		*profile = PLATFORM_PROFILE_PERFORMANCE;
+		break;
+	default:
+		return -ENODATA;
+	}
+
+	return 0;
+}
+
+static int thermal_profile_set(struct platform_profile_handler *pprof,
+				enum platform_profile_option profile)
+{
+	acpi_status status;
+	u32 in_args;
+	u32 out_data;
+
+	switch (profile) {
+	case PLATFORM_PROFILE_LOW_POWER:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_LOW_POWER);
+		break;
+	case PLATFORM_PROFILE_QUIET:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET);
+		break;
+	case PLATFORM_PROFILE_BALANCED:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED);
+		break;
+	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE);
+		break;
+	case PLATFORM_PROFILE_PERFORMANCE:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_PERFORMANCE);
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	status = alienware_wmax_command(&in_args, sizeof(in_args),
+					WMAX_METHOD_THERMAL_CONTROL, &out_data);
+
+	if (ACPI_FAILURE(status))
+		return -EOPNOTSUPP;
+
+	if (out_data == 0xFFFFFFFF)
+		return -EBADRQC;
+
+	return 0;
+}
+
+static int gmode_thermal_profile_set(struct platform_profile_handler *pprof,
+				     enum platform_profile_option profile)
+{
+	acpi_status status;
+	u32 in_args;
+	u32 out_data;
+
+	switch (profile) {
+	case PLATFORM_PROFILE_LOW_POWER:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_LOW_POWER);
+		break;
+	case PLATFORM_PROFILE_QUIET:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET);
+		break;
+	case PLATFORM_PROFILE_BALANCED:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED);
+		break;
+	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE);
+		break;
+	case PLATFORM_PROFILE_PERFORMANCE:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_GMODE);
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	status = alienware_wmax_command(&in_args, sizeof(in_args),
+					WMAX_METHOD_THERMAL_CONTROL, &out_data);
+
+	if (ACPI_FAILURE(status))
+		return -EOPNOTSUPP;
+
+	if (out_data == 0xFFFFFFFF)
+		return -EBADRQC;
+
+	return 0;
+}
+
+static int create_thermal_profile(void)
+{
+	pp_handler.profile_get = thermal_profile_get;
+
+	if (quirks->gmode > 0)
+		pp_handler.profile_set = gmode_thermal_profile_set;
+	else
+		pp_handler.profile_set = thermal_profile_set;
+
+	set_bit(PLATFORM_PROFILE_LOW_POWER, pp_handler.choices);
+	set_bit(PLATFORM_PROFILE_QUIET, pp_handler.choices);
+	set_bit(PLATFORM_PROFILE_BALANCED, pp_handler.choices);
+	set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, pp_handler.choices);
+	set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices);
+
+	return platform_profile_register(&pp_handler);
+}
+
+static void remove_thermal_profile(void)
+{
+	if (quirks->thermal > 0)
+		platform_profile_remove();
+}
+
 static int __init alienware_wmi_init(void)
 {
 	int ret;
@@ -807,6 +1011,12 @@ static int __init alienware_wmi_init(void)
 			goto fail_prep_deepsleep;
 	}
 
+	if (quirks->thermal > 0) {
+		ret = create_thermal_profile();
+		if (ret)
+			goto fail_prep_thermal_profile;
+	}
+
 	ret = alienware_zone_init(platform_device);
 	if (ret)
 		goto fail_prep_zones;
@@ -817,6 +1027,7 @@ static int __init alienware_wmi_init(void)
 	alienware_zone_exit(platform_device);
 fail_prep_deepsleep:
 fail_prep_amplifier:
+fail_prep_thermal_profile:
 fail_prep_hdmi:
 	platform_device_del(platform_device);
 fail_platform_device2:
@@ -834,6 +1045,7 @@ static void __exit alienware_wmi_exit(void)
 	if (platform_device) {
 		alienware_zone_exit(platform_device);
 		remove_hdmi(platform_device);
+		remove_thermal_profile();
 		platform_device_unregister(platform_device);
 		platform_driver_unregister(&platform_driver);
 	}
-- 
2.46.2
Re: [PATCH] alienware-wmi: Dell AWCC platform_profile support
Posted by Kurt Borja 1 month, 2 weeks ago
Sorry. Please ignore.
[PATCH v3] alienware-wmi: Dell AWCC platform_profile support
Posted by Kurt Borja 1 month, 2 weeks ago
This patch adds platform_profile support for Dell devices which implement
User Selectable Thermal Tables (USTT) that are meant to be controlled by
Alienware Command Center (AWCC). These devices may include newer Alienware
M-Series, Alienware X-Series and Dell's G-Series. This patch, was tested
by me on an Alienware x15 R1.

It is suspected that Alienware Command Center manages thermal profiles
through the WMI interface, specifically through a device with identifier
\_SB_.AMW1.WMAX. This device was reverse engineered and the relevant
functionality is documented here [1]. This driver interacts with this
WMI device and thus is able to mimic AWCC's thermal profiles functionality
through the platform_profile API. In consequence the user would be able
to set and retrieve thermal profiles, which are just fan speed profiles.

This driver was heavily inspired on inspur_platform_profile, special
thanks.

Notes:
 - Performance (FullSpeed) profile is a special profile which has it's own
   entry in the Firmware Settings of the Alienware x15 R1. It also changes
   the color of the F1 key. I suspect this behavior would be replicated in
   other X-Series or M-Series laptops.
 - G-Mode is a profile documented on [1] which mimics the behavior of
   FullSpeed mode but it does not have an entry on the Firmware Settings of
   the Alienware x15 R1, this may correspond to the G-Mode functionality on
   G-Series laptops (activated by a special button) but I cannot test it. I
   did not include this code in the driver as G-Mode causes unexpected
   behavior on X-Series laptops.

Signed-off-by: Kurt Borja <kuurtb@gmail.com>

---
v3:
 - Removed extra empty line
 - 0x0B named WMAX_ARG_GET_CURRENT_PROF
 - Removed casts to the same type on functions added in this patch
 - Thermal profile to WMAX argument is now an static function and makes
   use of in-built kernel macros
 - Platform profile is now removed only if it was created first
 - create_platform_profile is now create_thermal_profile to avoid
   confusion
 - profile_get and profile_set functions renamed too to match the above
v2:
 - Moved functionality to alienware-wmi driver
 - Added thermal and gmode quirks to add support based on dmi match
 - Performance profile is now GMODE for devices that support it
 - alienware_wmax_command now is insize agnostic to support new thermal
   methods
---
 drivers/platform/x86/dell/Kconfig         |   1 +
 drivers/platform/x86/dell/alienware-wmi.c | 238 ++++++++++++++++++++--
 2 files changed, 226 insertions(+), 13 deletions(-)

diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig
index 68a49788a..b06d634cd 100644
--- a/drivers/platform/x86/dell/Kconfig
+++ b/drivers/platform/x86/dell/Kconfig
@@ -21,6 +21,7 @@ config ALIENWARE_WMI
 	depends on LEDS_CLASS
 	depends on NEW_LEDS
 	depends on ACPI_WMI
+	select ACPI_PLATFORM_PROFILE
 	help
 	 This is a driver for controlling Alienware BIOS driven
 	 features.  It exposes an interface for controlling the AlienFX
diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
index f5ee62ce1..e3ef4b10b 100644
--- a/drivers/platform/x86/dell/alienware-wmi.c
+++ b/drivers/platform/x86/dell/alienware-wmi.c
@@ -10,6 +10,7 @@
 #include <linux/acpi.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
+#include <linux/platform_profile.h>
 #include <linux/dmi.h>
 #include <linux/leds.h>
 
@@ -25,6 +26,10 @@
 #define WMAX_METHOD_AMPLIFIER_CABLE	0x6
 #define WMAX_METHOD_DEEP_SLEEP_CONTROL	0x0B
 #define WMAX_METHOD_DEEP_SLEEP_STATUS	0x0C
+#define WMAX_METHOD_THERMAL_INFORMATION	0x14
+#define WMAX_METHOD_THERMAL_CONTROL	0x15
+
+#define WMAX_ARG_GET_CURRENT_PROF	0x0B
 
 MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
 MODULE_DESCRIPTION("Alienware special feature control");
@@ -49,11 +54,22 @@ enum WMAX_CONTROL_STATES {
 	WMAX_SUSPEND = 3,
 };
 
+enum WMAX_THERMAL_PROFILE {
+	WMAX_THERMAL_QUIET = 0xA3,
+	WMAX_THERMAL_BALANCED = 0xA0,
+	WMAX_THERMAL_BALANCED_PERFORMANCE = 0xA1,
+	WMAX_THERMAL_PERFORMANCE = 0xA4,
+	WMAX_THERMAL_GMODE = 0xAB,
+	WMAX_THERMAL_LOW_POWER	= 0xA5,
+};
+
 struct quirk_entry {
 	u8 num_zones;
 	u8 hdmi_mux;
 	u8 amplifier;
 	u8 deepslp;
+	u8 thermal;
+	u8 gmode;
 };
 
 static struct quirk_entry *quirks;
@@ -64,6 +80,8 @@ static struct quirk_entry quirk_inspiron5675 = {
 	.hdmi_mux = 0,
 	.amplifier = 0,
 	.deepslp = 0,
+	.thermal = 0,
+	.gmode = 0,
 };
 
 static struct quirk_entry quirk_unknown = {
@@ -71,6 +89,8 @@ static struct quirk_entry quirk_unknown = {
 	.hdmi_mux = 0,
 	.amplifier = 0,
 	.deepslp = 0,
+	.thermal = 0,
+	.gmode = 0,
 };
 
 static struct quirk_entry quirk_x51_r1_r2 = {
@@ -78,6 +98,8 @@ static struct quirk_entry quirk_x51_r1_r2 = {
 	.hdmi_mux = 0,
 	.amplifier = 0,
 	.deepslp = 0,
+	.thermal = 0,
+	.gmode = 0,
 };
 
 static struct quirk_entry quirk_x51_r3 = {
@@ -85,6 +107,8 @@ static struct quirk_entry quirk_x51_r3 = {
 	.hdmi_mux = 0,
 	.amplifier = 1,
 	.deepslp = 0,
+	.thermal = 0,
+	.gmode = 0,
 };
 
 static struct quirk_entry quirk_asm100 = {
@@ -92,6 +116,8 @@ static struct quirk_entry quirk_asm100 = {
 	.hdmi_mux = 1,
 	.amplifier = 0,
 	.deepslp = 0,
+	.thermal = 0,
+	.gmode = 0,
 };
 
 static struct quirk_entry quirk_asm200 = {
@@ -99,6 +125,8 @@ static struct quirk_entry quirk_asm200 = {
 	.hdmi_mux = 1,
 	.amplifier = 0,
 	.deepslp = 1,
+	.thermal = 0,
+	.gmode = 0,
 };
 
 static struct quirk_entry quirk_asm201 = {
@@ -106,6 +134,17 @@ static struct quirk_entry quirk_asm201 = {
 	.hdmi_mux = 1,
 	.amplifier = 1,
 	.deepslp = 1,
+	.thermal = 0,
+	.gmode = 0,
+};
+
+static struct quirk_entry quirk_x15_r1 = {
+	.num_zones = 2,
+	.hdmi_mux = 0,
+	.amplifier = 0,
+	.deepslp = 0,
+	.thermal = 1,
+	.gmode = 0,
 };
 
 static int __init dmi_matched(const struct dmi_system_id *dmi)
@@ -169,6 +208,15 @@ static const struct dmi_system_id alienware_quirks[] __initconst = {
 		     },
 	 .driver_data = &quirk_asm201,
 	 },
+	 {
+	 .callback = dmi_matched,
+	 .ident = "Alienware x15 R1",
+	 .matches = {
+		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+		     DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R1")
+		    },
+	 .driver_data = &quirk_x15_r1,
+	},
 	 {
 	 .callback = dmi_matched,
 	 .ident = "Dell Inc. Inspiron 5675",
@@ -218,6 +266,7 @@ static struct platform_device *platform_device;
 static struct device_attribute *zone_dev_attrs;
 static struct attribute **zone_attrs;
 static struct platform_zone *zone_data;
+static struct platform_profile_handler pp_handler;
 
 static struct platform_driver platform_driver = {
 	.driver = {
@@ -500,7 +549,7 @@ static void alienware_zone_exit(struct platform_device *dev)
 	kfree(zone_attrs);
 }
 
-static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
+static acpi_status alienware_wmax_command(void *in_args, size_t insize,
 					  u32 command, int *out_data)
 {
 	acpi_status status;
@@ -508,7 +557,7 @@ static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
 	struct acpi_buffer input;
 	struct acpi_buffer output;
 
-	input.length = (acpi_size) sizeof(*in_args);
+	input.length = (acpi_size) insize;
 	input.pointer = in_args;
 	if (out_data) {
 		output.length = ACPI_ALLOCATE_BUFFER;
@@ -541,8 +590,8 @@ static ssize_t show_hdmi_cable(struct device *dev,
 		.arg = 0,
 	};
 	status =
-	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE,
-				   (u32 *) &out_data);
+	    alienware_wmax_command(&in_args, sizeof(in_args),
+				   WMAX_METHOD_HDMI_CABLE, (u32 *) &out_data);
 	if (ACPI_SUCCESS(status)) {
 		if (out_data == 0)
 			return sysfs_emit(buf, "[unconnected] connected unknown\n");
@@ -562,8 +611,8 @@ static ssize_t show_hdmi_source(struct device *dev,
 		.arg = 0,
 	};
 	status =
-	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS,
-				   (u32 *) &out_data);
+	    alienware_wmax_command(&in_args, sizeof(in_args),
+				   WMAX_METHOD_HDMI_STATUS, (u32 *) &out_data);
 
 	if (ACPI_SUCCESS(status)) {
 		if (out_data == 1)
@@ -589,7 +638,8 @@ static ssize_t toggle_hdmi_source(struct device *dev,
 		args.arg = 3;
 	pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf);
 
-	status = alienware_wmax_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL);
+	status = alienware_wmax_command(&args, sizeof(args),
+					WMAX_METHOD_HDMI_SOURCE, NULL);
 
 	if (ACPI_FAILURE(status))
 		pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
@@ -642,8 +692,8 @@ static ssize_t show_amplifier_status(struct device *dev,
 		.arg = 0,
 	};
 	status =
-	    alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE,
-				   (u32 *) &out_data);
+	    alienware_wmax_command(&in_args, sizeof(in_args),
+				   WMAX_METHOD_AMPLIFIER_CABLE, (u32 *) &out_data);
 	if (ACPI_SUCCESS(status)) {
 		if (out_data == 0)
 			return sysfs_emit(buf, "[unconnected] connected unknown\n");
@@ -694,8 +744,8 @@ static ssize_t show_deepsleep_status(struct device *dev,
 	struct wmax_basic_args in_args = {
 		.arg = 0,
 	};
-	status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS,
-					(u32 *) &out_data);
+	status = alienware_wmax_command(&in_args, sizeof(in_args),
+					WMAX_METHOD_DEEP_SLEEP_STATUS, (u32 *) &out_data);
 	if (ACPI_SUCCESS(status)) {
 		if (out_data == 0)
 			return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
@@ -723,8 +773,8 @@ static ssize_t toggle_deepsleep(struct device *dev,
 		args.arg = 2;
 	pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf);
 
-	status = alienware_wmax_command(&args, WMAX_METHOD_DEEP_SLEEP_CONTROL,
-					NULL);
+	status = alienware_wmax_command(&args, sizeof(args),
+					WMAX_METHOD_DEEP_SLEEP_CONTROL, NULL);
 
 	if (ACPI_FAILURE(status))
 		pr_err("alienware-wmi: deep sleep control failed: results: %u\n",
@@ -760,6 +810,160 @@ static int create_deepsleep(struct platform_device *dev)
 	return ret;
 }
 
+/*
+ * Thermal Profile control
+ *  - Provides thermal profile control through the Platform Profile API
+ */
+#define PROFILE_MASK		GENMASK(15,8)
+#define PROFILE_ACTIVATE	BIT(0)
+
+static u32 profile_to_wmax_arg(enum WMAX_THERMAL_PROFILE prof)
+{
+	return FIELD_PREP(PROFILE_MASK, prof) | PROFILE_ACTIVATE;
+}
+
+static int thermal_profile_get(struct platform_profile_handler *pprof,
+				enum platform_profile_option *profile)
+{
+	acpi_status status;
+	u32 in_args = WMAX_ARG_GET_CURRENT_PROF;
+	u32 out_data;
+
+	status = alienware_wmax_command(&in_args, sizeof(in_args),
+					WMAX_METHOD_THERMAL_INFORMATION, &out_data);
+
+	if (ACPI_FAILURE(status))
+		return -EOPNOTSUPP;
+
+	if (out_data == 0xFFFFFFFF)
+		return -EBADRQC;
+
+	switch (out_data) {
+	case WMAX_THERMAL_LOW_POWER:
+		*profile = PLATFORM_PROFILE_LOW_POWER;
+		break;
+	case WMAX_THERMAL_QUIET:
+		*profile = PLATFORM_PROFILE_QUIET;
+		break;
+	case WMAX_THERMAL_BALANCED:
+		*profile = PLATFORM_PROFILE_BALANCED;
+		break;
+	case WMAX_THERMAL_BALANCED_PERFORMANCE:
+		*profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE;
+		break;
+	case WMAX_THERMAL_PERFORMANCE:
+	case WMAX_THERMAL_GMODE:
+		*profile = PLATFORM_PROFILE_PERFORMANCE;
+		break;
+	default:
+		return -ENODATA;
+	}
+
+	return 0;
+}
+
+static int thermal_profile_set(struct platform_profile_handler *pprof,
+				enum platform_profile_option profile)
+{
+	acpi_status status;
+	u32 in_args;
+	u32 out_data;
+
+	switch (profile) {
+	case PLATFORM_PROFILE_LOW_POWER:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_LOW_POWER);
+		break;
+	case PLATFORM_PROFILE_QUIET:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET);
+		break;
+	case PLATFORM_PROFILE_BALANCED:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED);
+		break;
+	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE);
+		break;
+	case PLATFORM_PROFILE_PERFORMANCE:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_PERFORMANCE);
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	status = alienware_wmax_command(&in_args, sizeof(in_args),
+					WMAX_METHOD_THERMAL_CONTROL, &out_data);
+
+	if (ACPI_FAILURE(status))
+		return -EOPNOTSUPP;
+
+	if (out_data == 0xFFFFFFFF)
+		return -EBADRQC;
+
+	return 0;
+}
+
+static int gmode_thermal_profile_set(struct platform_profile_handler *pprof,
+				     enum platform_profile_option profile)
+{
+	acpi_status status;
+	u32 in_args;
+	u32 out_data;
+
+	switch (profile) {
+	case PLATFORM_PROFILE_LOW_POWER:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_LOW_POWER);
+		break;
+	case PLATFORM_PROFILE_QUIET:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET);
+		break;
+	case PLATFORM_PROFILE_BALANCED:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED);
+		break;
+	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE);
+		break;
+	case PLATFORM_PROFILE_PERFORMANCE:
+		in_args = profile_to_wmax_arg(WMAX_THERMAL_GMODE);
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	status = alienware_wmax_command(&in_args, sizeof(in_args),
+					WMAX_METHOD_THERMAL_CONTROL, &out_data);
+
+	if (ACPI_FAILURE(status))
+		return -EOPNOTSUPP;
+
+	if (out_data == 0xFFFFFFFF)
+		return -EBADRQC;
+
+	return 0;
+}
+
+static int create_thermal_profile(void)
+{
+	pp_handler.profile_get = thermal_profile_get;
+
+	if (quirks->gmode > 0)
+		pp_handler.profile_set = gmode_thermal_profile_set;
+	else
+		pp_handler.profile_set = thermal_profile_set;
+
+	set_bit(PLATFORM_PROFILE_LOW_POWER, pp_handler.choices);
+	set_bit(PLATFORM_PROFILE_QUIET, pp_handler.choices);
+	set_bit(PLATFORM_PROFILE_BALANCED, pp_handler.choices);
+	set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, pp_handler.choices);
+	set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices);
+
+	return platform_profile_register(&pp_handler);
+}
+
+static void remove_thermal_profile(void)
+{
+	if (quirks->thermal > 0)
+		platform_profile_remove();
+}
+
 static int __init alienware_wmi_init(void)
 {
 	int ret;
@@ -807,6 +1011,12 @@ static int __init alienware_wmi_init(void)
 			goto fail_prep_deepsleep;
 	}
 
+	if (quirks->thermal > 0) {
+		ret = create_thermal_profile();
+		if (ret)
+			goto fail_prep_thermal_profile;
+	}
+
 	ret = alienware_zone_init(platform_device);
 	if (ret)
 		goto fail_prep_zones;
@@ -817,6 +1027,7 @@ static int __init alienware_wmi_init(void)
 	alienware_zone_exit(platform_device);
 fail_prep_deepsleep:
 fail_prep_amplifier:
+fail_prep_thermal_profile:
 fail_prep_hdmi:
 	platform_device_del(platform_device);
 fail_platform_device2:
@@ -834,6 +1045,7 @@ static void __exit alienware_wmi_exit(void)
 	if (platform_device) {
 		alienware_zone_exit(platform_device);
 		remove_hdmi(platform_device);
+		remove_thermal_profile();
 		platform_device_unregister(platform_device);
 		platform_driver_unregister(&platform_driver);
 	}
-- 
2.46.2
Re: [PATCH v3] alienware-wmi: Dell AWCC platform_profile support
Posted by kernel test robot 1 month, 1 week ago
Hi Kurt,

kernel test robot noticed the following build errors:

[auto build test ERROR on linus/master]
[also build test ERROR on v6.12-rc3 next-20241014]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Kurt-Borja/alienware-wmi-Dell-AWCC-platform_profile-support/20241011-184337
base:   linus/master
patch link:    https://lore.kernel.org/r/20241008195642.36677-2-kuurtb%40gmail.com
patch subject: [PATCH v3] alienware-wmi: Dell AWCC platform_profile support
config: x86_64-buildonly-randconfig-004-20241015 (https://download.01.org/0day-ci/archive/20241015/202410150939.BgH8WpE3-lkp@intel.com/config)
compiler: gcc-12 (Debian 12.2.0-14) 12.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20241015/202410150939.BgH8WpE3-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202410150939.BgH8WpE3-lkp@intel.com/

All errors (new ones prefixed by >>):

   drivers/platform/x86/dell/alienware-wmi.c: In function 'profile_to_wmax_arg':
>> drivers/platform/x86/dell/alienware-wmi.c:822:16: error: implicit declaration of function 'FIELD_PREP' [-Werror=implicit-function-declaration]
     822 |         return FIELD_PREP(PROFILE_MASK, prof) | PROFILE_ACTIVATE;
         |                ^~~~~~~~~~
   cc1: some warnings being treated as errors


vim +/FIELD_PREP +822 drivers/platform/x86/dell/alienware-wmi.c

   819	
   820	static u32 profile_to_wmax_arg(enum WMAX_THERMAL_PROFILE prof)
   821	{
 > 822		return FIELD_PREP(PROFILE_MASK, prof) | PROFILE_ACTIVATE;
   823	}
   824	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Re: [PATCH v3] alienware-wmi: Dell AWCC platform_profile support
Posted by kernel test robot 1 month, 2 weeks ago
Hi Kurt,

kernel test robot noticed the following build errors:

[auto build test ERROR on linus/master]
[also build test ERROR on v6.12-rc2 next-20241009]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Kurt-Borja/alienware-wmi-Dell-AWCC-platform_profile-support/20241009-040025
base:   linus/master
patch link:    https://lore.kernel.org/r/20241008195642.36677-2-kuurtb%40gmail.com
patch subject: [PATCH v3] alienware-wmi: Dell AWCC platform_profile support
config: i386-randconfig-r051-20241010 (https://download.01.org/0day-ci/archive/20241010/202410101120.w4OLAnaI-lkp@intel.com/config)
compiler: clang version 18.1.8 (https://github.com/llvm/llvm-project 3b5b5c1ec4a3095ab096dd780e84d7ab81f3d7ff)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20241010/202410101120.w4OLAnaI-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202410101120.w4OLAnaI-lkp@intel.com/

All errors (new ones prefixed by >>):

>> drivers/platform/x86/dell/alienware-wmi.c:822:9: error: call to undeclared function 'FIELD_PREP'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
     822 |         return FIELD_PREP(PROFILE_MASK, prof) | PROFILE_ACTIVATE;
         |                ^
   1 error generated.


vim +/FIELD_PREP +822 drivers/platform/x86/dell/alienware-wmi.c

   819	
   820	static u32 profile_to_wmax_arg(enum WMAX_THERMAL_PROFILE prof)
   821	{
 > 822		return FIELD_PREP(PROFILE_MASK, prof) | PROFILE_ACTIVATE;
   823	}
   824	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Re: [PATCH v3] alienware-wmi: Dell AWCC platform_profile support
Posted by Ilpo Järvinen 1 month, 2 weeks ago
On Thu, 10 Oct 2024, kernel test robot wrote:

> Hi Kurt,
> 
> kernel test robot noticed the following build errors:
> 
> [auto build test ERROR on linus/master]
> [also build test ERROR on v6.12-rc2 next-20241009]
> [If your patch is applied to the wrong git tree, kindly drop us a note.
> And when submitting patch, we suggest to use '--base' as documented in
> https://git-scm.com/docs/git-format-patch#_base_tree_information]
> 
> url:    https://github.com/intel-lab-lkp/linux/commits/Kurt-Borja/alienware-wmi-Dell-AWCC-platform_profile-support/20241009-040025
> base:   linus/master
> patch link:    https://lore.kernel.org/r/20241008195642.36677-2-kuurtb%40gmail.com
> patch subject: [PATCH v3] alienware-wmi: Dell AWCC platform_profile support
> config: i386-randconfig-r051-20241010 (https://download.01.org/0day-ci/archive/20241010/202410101120.w4OLAnaI-lkp@intel.com/config)
> compiler: clang version 18.1.8 (https://github.com/llvm/llvm-project 3b5b5c1ec4a3095ab096dd780e84d7ab81f3d7ff)
> reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20241010/202410101120.w4OLAnaI-lkp@intel.com/reproduce)
> 
> If you fix the issue in a separate patch/commit (i.e. not just a new version of
> the same patch/commit), kindly add following tags
> | Reported-by: kernel test robot <lkp@intel.com>
> | Closes: https://lore.kernel.org/oe-kbuild-all/202410101120.w4OLAnaI-lkp@intel.com/
> 
> All errors (new ones prefixed by >>):
> 
> >> drivers/platform/x86/dell/alienware-wmi.c:822:9: error: call to undeclared function 'FIELD_PREP'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
>      822 |         return FIELD_PREP(PROFILE_MASK, prof) | PROFILE_ACTIVATE;
>          |                ^
>    1 error generated.
> 
> 
> vim +/FIELD_PREP +822 drivers/platform/x86/dell/alienware-wmi.c
> 
>    819	
>    820	static u32 profile_to_wmax_arg(enum WMAX_THERMAL_PROFILE prof)
>    821	{
>  > 822		return FIELD_PREP(PROFILE_MASK, prof) | PROFILE_ACTIVATE;
>    823	}
>    824	

For the one time I forget to mention that please add the necessary 
headers, it immediately bites (that was in my mind at one point but in 
the end I forgot to add it).

So please, add the headers both for FIELD_PREP() and GENMASK().

-- 
 i.
Re: [PATCH v3] alienware-wmi: Dell AWCC platform_profile support
Posted by Kurt Borja 1 month, 2 weeks ago
Thank you! I noticed the kernel thanks to the kernel robot too. I will
be sure to add them in v4.

Kurt
Re: [PATCH v3] alienware-wmi: Dell AWCC platform_profile support
Posted by Ilpo Järvinen 1 month, 2 weeks ago
On Tue, 8 Oct 2024, Kurt Borja wrote:

> This patch adds platform_profile support for Dell devices which implement
> User Selectable Thermal Tables (USTT) that are meant to be controlled by
> Alienware Command Center (AWCC). These devices may include newer Alienware
> M-Series, Alienware X-Series and Dell's G-Series. This patch, was tested
> by me on an Alienware x15 R1.
> 
> It is suspected that Alienware Command Center manages thermal profiles
> through the WMI interface, specifically through a device with identifier
> \_SB_.AMW1.WMAX. This device was reverse engineered and the relevant
> functionality is documented here [1]. This driver interacts with this
> WMI device and thus is able to mimic AWCC's thermal profiles functionality
> through the platform_profile API. In consequence the user would be able
> to set and retrieve thermal profiles, which are just fan speed profiles.
> 
> This driver was heavily inspired on inspur_platform_profile, special
> thanks.
> 
> Notes:
>  - Performance (FullSpeed) profile is a special profile which has it's own
>    entry in the Firmware Settings of the Alienware x15 R1. It also changes
>    the color of the F1 key. I suspect this behavior would be replicated in
>    other X-Series or M-Series laptops.
>  - G-Mode is a profile documented on [1] which mimics the behavior of
>    FullSpeed mode but it does not have an entry on the Firmware Settings of
>    the Alienware x15 R1, this may correspond to the G-Mode functionality on
>    G-Series laptops (activated by a special button) but I cannot test it. I
>    did not include this code in the driver as G-Mode causes unexpected
>    behavior on X-Series laptops.
> 
> Signed-off-by: Kurt Borja <kuurtb@gmail.com>
> 
> ---
> v3:
>  - Removed extra empty line
>  - 0x0B named WMAX_ARG_GET_CURRENT_PROF
>  - Removed casts to the same type on functions added in this patch
>  - Thermal profile to WMAX argument is now an static function and makes
>    use of in-built kernel macros
>  - Platform profile is now removed only if it was created first
>  - create_platform_profile is now create_thermal_profile to avoid
>    confusion
>  - profile_get and profile_set functions renamed too to match the above
> v2:
>  - Moved functionality to alienware-wmi driver
>  - Added thermal and gmode quirks to add support based on dmi match
>  - Performance profile is now GMODE for devices that support it
>  - alienware_wmax_command now is insize agnostic to support new thermal
>    methods
> ---
>  drivers/platform/x86/dell/Kconfig         |   1 +
>  drivers/platform/x86/dell/alienware-wmi.c | 238 ++++++++++++++++++++--
>  2 files changed, 226 insertions(+), 13 deletions(-)
> 
> diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig
> index 68a49788a..b06d634cd 100644
> --- a/drivers/platform/x86/dell/Kconfig
> +++ b/drivers/platform/x86/dell/Kconfig
> @@ -21,6 +21,7 @@ config ALIENWARE_WMI
>  	depends on LEDS_CLASS
>  	depends on NEW_LEDS
>  	depends on ACPI_WMI
> +	select ACPI_PLATFORM_PROFILE
>  	help
>  	 This is a driver for controlling Alienware BIOS driven
>  	 features.  It exposes an interface for controlling the AlienFX
> diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
> index f5ee62ce1..e3ef4b10b 100644
> --- a/drivers/platform/x86/dell/alienware-wmi.c
> +++ b/drivers/platform/x86/dell/alienware-wmi.c
> @@ -10,6 +10,7 @@
>  #include <linux/acpi.h>
>  #include <linux/module.h>
>  #include <linux/platform_device.h>
> +#include <linux/platform_profile.h>
>  #include <linux/dmi.h>
>  #include <linux/leds.h>
>  
> @@ -25,6 +26,10 @@
>  #define WMAX_METHOD_AMPLIFIER_CABLE	0x6
>  #define WMAX_METHOD_DEEP_SLEEP_CONTROL	0x0B
>  #define WMAX_METHOD_DEEP_SLEEP_STATUS	0x0C
> +#define WMAX_METHOD_THERMAL_INFORMATION	0x14
> +#define WMAX_METHOD_THERMAL_CONTROL	0x15
> +
> +#define WMAX_ARG_GET_CURRENT_PROF	0x0B
>  
>  MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
>  MODULE_DESCRIPTION("Alienware special feature control");
> @@ -49,11 +54,22 @@ enum WMAX_CONTROL_STATES {
>  	WMAX_SUSPEND = 3,
>  };
>  
> +enum WMAX_THERMAL_PROFILE {
> +	WMAX_THERMAL_QUIET = 0xA3,
> +	WMAX_THERMAL_BALANCED = 0xA0,
> +	WMAX_THERMAL_BALANCED_PERFORMANCE = 0xA1,
> +	WMAX_THERMAL_PERFORMANCE = 0xA4,
> +	WMAX_THERMAL_GMODE = 0xAB,
> +	WMAX_THERMAL_LOW_POWER	= 0xA5,
> +};
> +
>  struct quirk_entry {
>  	u8 num_zones;
>  	u8 hdmi_mux;
>  	u8 amplifier;
>  	u8 deepslp;
> +	u8 thermal;
> +	u8 gmode;
>  };
>  
>  static struct quirk_entry *quirks;
> @@ -64,6 +80,8 @@ static struct quirk_entry quirk_inspiron5675 = {
>  	.hdmi_mux = 0,
>  	.amplifier = 0,
>  	.deepslp = 0,
> +	.thermal = 0,
> +	.gmode = 0,
>  };
>  
>  static struct quirk_entry quirk_unknown = {
> @@ -71,6 +89,8 @@ static struct quirk_entry quirk_unknown = {
>  	.hdmi_mux = 0,
>  	.amplifier = 0,
>  	.deepslp = 0,
> +	.thermal = 0,
> +	.gmode = 0,
>  };
>  
>  static struct quirk_entry quirk_x51_r1_r2 = {
> @@ -78,6 +98,8 @@ static struct quirk_entry quirk_x51_r1_r2 = {
>  	.hdmi_mux = 0,
>  	.amplifier = 0,
>  	.deepslp = 0,
> +	.thermal = 0,
> +	.gmode = 0,
>  };
>  
>  static struct quirk_entry quirk_x51_r3 = {
> @@ -85,6 +107,8 @@ static struct quirk_entry quirk_x51_r3 = {
>  	.hdmi_mux = 0,
>  	.amplifier = 1,
>  	.deepslp = 0,
> +	.thermal = 0,
> +	.gmode = 0,
>  };
>  
>  static struct quirk_entry quirk_asm100 = {
> @@ -92,6 +116,8 @@ static struct quirk_entry quirk_asm100 = {
>  	.hdmi_mux = 1,
>  	.amplifier = 0,
>  	.deepslp = 0,
> +	.thermal = 0,
> +	.gmode = 0,
>  };
>  
>  static struct quirk_entry quirk_asm200 = {
> @@ -99,6 +125,8 @@ static struct quirk_entry quirk_asm200 = {
>  	.hdmi_mux = 1,
>  	.amplifier = 0,
>  	.deepslp = 1,
> +	.thermal = 0,
> +	.gmode = 0,
>  };
>  
>  static struct quirk_entry quirk_asm201 = {
> @@ -106,6 +134,17 @@ static struct quirk_entry quirk_asm201 = {
>  	.hdmi_mux = 1,
>  	.amplifier = 1,
>  	.deepslp = 1,
> +	.thermal = 0,
> +	.gmode = 0,
> +};
> +
> +static struct quirk_entry quirk_x15_r1 = {
> +	.num_zones = 2,
> +	.hdmi_mux = 0,
> +	.amplifier = 0,
> +	.deepslp = 0,
> +	.thermal = 1,
> +	.gmode = 0,
>  };
>  
>  static int __init dmi_matched(const struct dmi_system_id *dmi)
> @@ -169,6 +208,15 @@ static const struct dmi_system_id alienware_quirks[] __initconst = {
>  		     },
>  	 .driver_data = &quirk_asm201,
>  	 },
> +	 {
> +	 .callback = dmi_matched,
> +	 .ident = "Alienware x15 R1",
> +	 .matches = {
> +		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> +		     DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R1")
> +		    },
> +	 .driver_data = &quirk_x15_r1,
> +	},
>  	 {
>  	 .callback = dmi_matched,
>  	 .ident = "Dell Inc. Inspiron 5675",
> @@ -218,6 +266,7 @@ static struct platform_device *platform_device;
>  static struct device_attribute *zone_dev_attrs;
>  static struct attribute **zone_attrs;
>  static struct platform_zone *zone_data;
> +static struct platform_profile_handler pp_handler;
>  
>  static struct platform_driver platform_driver = {
>  	.driver = {
> @@ -500,7 +549,7 @@ static void alienware_zone_exit(struct platform_device *dev)
>  	kfree(zone_attrs);
>  }
>  
> -static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
> +static acpi_status alienware_wmax_command(void *in_args, size_t insize,
>  					  u32 command, int *out_data)
>  {
>  	acpi_status status;
> @@ -508,7 +557,7 @@ static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
>  	struct acpi_buffer input;
>  	struct acpi_buffer output;
>  
> -	input.length = (acpi_size) sizeof(*in_args);
> +	input.length = (acpi_size) insize;
>  	input.pointer = in_args;
>  	if (out_data) {
>  		output.length = ACPI_ALLOCATE_BUFFER;
> @@ -541,8 +590,8 @@ static ssize_t show_hdmi_cable(struct device *dev,
>  		.arg = 0,
>  	};
>  	status =
> -	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE,
> -				   (u32 *) &out_data);
> +	    alienware_wmax_command(&in_args, sizeof(in_args),
> +				   WMAX_METHOD_HDMI_CABLE, (u32 *) &out_data);
>  	if (ACPI_SUCCESS(status)) {
>  		if (out_data == 0)
>  			return sysfs_emit(buf, "[unconnected] connected unknown\n");
> @@ -562,8 +611,8 @@ static ssize_t show_hdmi_source(struct device *dev,
>  		.arg = 0,
>  	};
>  	status =
> -	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS,
> -				   (u32 *) &out_data);
> +	    alienware_wmax_command(&in_args, sizeof(in_args),
> +				   WMAX_METHOD_HDMI_STATUS, (u32 *) &out_data);
>  
>  	if (ACPI_SUCCESS(status)) {
>  		if (out_data == 1)
> @@ -589,7 +638,8 @@ static ssize_t toggle_hdmi_source(struct device *dev,
>  		args.arg = 3;
>  	pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf);
>  
> -	status = alienware_wmax_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL);
> +	status = alienware_wmax_command(&args, sizeof(args),
> +					WMAX_METHOD_HDMI_SOURCE, NULL);
>  
>  	if (ACPI_FAILURE(status))
>  		pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
> @@ -642,8 +692,8 @@ static ssize_t show_amplifier_status(struct device *dev,
>  		.arg = 0,
>  	};
>  	status =
> -	    alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE,
> -				   (u32 *) &out_data);
> +	    alienware_wmax_command(&in_args, sizeof(in_args),
> +				   WMAX_METHOD_AMPLIFIER_CABLE, (u32 *) &out_data);
>  	if (ACPI_SUCCESS(status)) {
>  		if (out_data == 0)
>  			return sysfs_emit(buf, "[unconnected] connected unknown\n");
> @@ -694,8 +744,8 @@ static ssize_t show_deepsleep_status(struct device *dev,
>  	struct wmax_basic_args in_args = {
>  		.arg = 0,
>  	};
> -	status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS,
> -					(u32 *) &out_data);
> +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> +					WMAX_METHOD_DEEP_SLEEP_STATUS, (u32 *) &out_data);
>  	if (ACPI_SUCCESS(status)) {
>  		if (out_data == 0)
>  			return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
> @@ -723,8 +773,8 @@ static ssize_t toggle_deepsleep(struct device *dev,
>  		args.arg = 2;
>  	pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf);
>  
> -	status = alienware_wmax_command(&args, WMAX_METHOD_DEEP_SLEEP_CONTROL,
> -					NULL);
> +	status = alienware_wmax_command(&args, sizeof(args),
> +					WMAX_METHOD_DEEP_SLEEP_CONTROL, NULL);
>  
>  	if (ACPI_FAILURE(status))
>  		pr_err("alienware-wmi: deep sleep control failed: results: %u\n",
> @@ -760,6 +810,160 @@ static int create_deepsleep(struct platform_device *dev)
>  	return ret;
>  }
>  
> +/*
> + * Thermal Profile control
> + *  - Provides thermal profile control through the Platform Profile API
> + */
> +#define PROFILE_MASK		GENMASK(15,8)

Space after comma.

> +#define PROFILE_ACTIVATE	BIT(0)

Add driver specific prefix to the defines please.

> +
> +static u32 profile_to_wmax_arg(enum WMAX_THERMAL_PROFILE prof)
> +{
> +	return FIELD_PREP(PROFILE_MASK, prof) | PROFILE_ACTIVATE;
> +}
> +
> +static int thermal_profile_get(struct platform_profile_handler *pprof,
> +				enum platform_profile_option *profile)
> +{
> +	acpi_status status;
> +	u32 in_args = WMAX_ARG_GET_CURRENT_PROF;
> +	u32 out_data;
> +
> +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> +					WMAX_METHOD_THERMAL_INFORMATION, &out_data);

Are you sure you want to keep out_data as u32? I'm not very happy how
alienware_wmax_command() takes int * but all callers seem to prefer u32 *
(or pass NULL). Should the out_data parameter for alienware_wmax_command() 
be u32 * or int *?

In general, if you find something that doesn't make sense, it often just
is an indication that some cleanup is in order. We're more than happy to
consider such patches along with the feature patches as then things are
moving into the correct direction even if the progress would be slow.

> +	if (ACPI_FAILURE(status))
> +		return -EOPNOTSUPP;
> +
> +	if (out_data == 0xFFFFFFFF)

This constant too should be named if the data really is u32 and not a 
negative error code in which case I'd be fine with < 0 (without naming
the error code) like in the original but it would need int as the type for 
the compare to work.

> +		return -EBADRQC;
> +
> +	switch (out_data) {
> +	case WMAX_THERMAL_LOW_POWER:
> +		*profile = PLATFORM_PROFILE_LOW_POWER;
> +		break;
> +	case WMAX_THERMAL_QUIET:
> +		*profile = PLATFORM_PROFILE_QUIET;
> +		break;
> +	case WMAX_THERMAL_BALANCED:
> +		*profile = PLATFORM_PROFILE_BALANCED;
> +		break;
> +	case WMAX_THERMAL_BALANCED_PERFORMANCE:
> +		*profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE;
> +		break;
> +	case WMAX_THERMAL_PERFORMANCE:
> +	case WMAX_THERMAL_GMODE:
> +		*profile = PLATFORM_PROFILE_PERFORMANCE;
> +		break;
> +	default:
> +		return -ENODATA;
> +	}
> +
> +	return 0;
> +}
> +
> +static int thermal_profile_set(struct platform_profile_handler *pprof,
> +				enum platform_profile_option profile)
> +{
> +	acpi_status status;
> +	u32 in_args;
> +	u32 out_data;
> +
> +	switch (profile) {
> +	case PLATFORM_PROFILE_LOW_POWER:
> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_LOW_POWER);
> +		break;
> +	case PLATFORM_PROFILE_QUIET:
> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET);
> +		break;
> +	case PLATFORM_PROFILE_BALANCED:
> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED);
> +		break;
> +	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE);
> +		break;
> +	case PLATFORM_PROFILE_PERFORMANCE:
> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_PERFORMANCE);
> +		break;
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +
> +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> +					WMAX_METHOD_THERMAL_CONTROL, &out_data);
> +
> +	if (ACPI_FAILURE(status))
> +		return -EOPNOTSUPP;
> +
> +	if (out_data == 0xFFFFFFFF)

Ditto.

> +		return -EBADRQC;
> +
> +	return 0;
> +}
> +
> +static int gmode_thermal_profile_set(struct platform_profile_handler *pprof,
> +				     enum platform_profile_option profile)
> +{
> +	acpi_status status;
> +	u32 in_args;
> +	u32 out_data;
> +
> +	switch (profile) {
> +	case PLATFORM_PROFILE_LOW_POWER:
> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_LOW_POWER);
> +		break;
> +	case PLATFORM_PROFILE_QUIET:
> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET);
> +		break;
> +	case PLATFORM_PROFILE_BALANCED:
> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED);
> +		break;
> +	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE);
> +		break;
> +	case PLATFORM_PROFILE_PERFORMANCE:
> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_GMODE);
> +		break;
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +
> +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> +					WMAX_METHOD_THERMAL_CONTROL, &out_data);
> +
> +	if (ACPI_FAILURE(status))
> +		return -EOPNOTSUPP;
> +
> +	if (out_data == 0xFFFFFFFF)

Ditto.

-- 
 i.

> +		return -EBADRQC;
> +
> +	return 0;
> +}
> +
> +static int create_thermal_profile(void)
> +{
> +	pp_handler.profile_get = thermal_profile_get;
> +
> +	if (quirks->gmode > 0)
> +		pp_handler.profile_set = gmode_thermal_profile_set;
> +	else
> +		pp_handler.profile_set = thermal_profile_set;
> +
> +	set_bit(PLATFORM_PROFILE_LOW_POWER, pp_handler.choices);
> +	set_bit(PLATFORM_PROFILE_QUIET, pp_handler.choices);
> +	set_bit(PLATFORM_PROFILE_BALANCED, pp_handler.choices);
> +	set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, pp_handler.choices);
> +	set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices);
> +
> +	return platform_profile_register(&pp_handler);
> +}
> +
> +static void remove_thermal_profile(void)
> +{
> +	if (quirks->thermal > 0)
> +		platform_profile_remove();
> +}
> +
>  static int __init alienware_wmi_init(void)
>  {
>  	int ret;
> @@ -807,6 +1011,12 @@ static int __init alienware_wmi_init(void)
>  			goto fail_prep_deepsleep;
>  	}
>  
> +	if (quirks->thermal > 0) {
> +		ret = create_thermal_profile();
> +		if (ret)
> +			goto fail_prep_thermal_profile;
> +	}
> +
>  	ret = alienware_zone_init(platform_device);
>  	if (ret)
>  		goto fail_prep_zones;
> @@ -817,6 +1027,7 @@ static int __init alienware_wmi_init(void)
>  	alienware_zone_exit(platform_device);
>  fail_prep_deepsleep:
>  fail_prep_amplifier:
> +fail_prep_thermal_profile:
>  fail_prep_hdmi:
>  	platform_device_del(platform_device);
>  fail_platform_device2:
> @@ -834,6 +1045,7 @@ static void __exit alienware_wmi_exit(void)
>  	if (platform_device) {
>  		alienware_zone_exit(platform_device);
>  		remove_hdmi(platform_device);
> +		remove_thermal_profile();
>  		platform_device_unregister(platform_device);
>  		platform_driver_unregister(&platform_driver);
>  	}
>
Re: [PATCH v3] alienware-wmi: Dell AWCC platform_profile support
Posted by Kurt Borja 1 month, 2 weeks ago
Ok.

> > +#define PROFILE_ACTIVATE	BIT(0)
> 
> Add driver specific prefix to the defines please.
> 

Ok.

> > +
> > +static u32 profile_to_wmax_arg(enum WMAX_THERMAL_PROFILE prof)
> > +{
> > +	return FIELD_PREP(PROFILE_MASK, prof) | PROFILE_ACTIVATE;
> > +}
> > +
> > +static int thermal_profile_get(struct platform_profile_handler *pprof,
> > +				enum platform_profile_option *profile)
> > +{
> > +	acpi_status status;
> > +	u32 in_args = WMAX_ARG_GET_CURRENT_PROF;
> > +	u32 out_data;
> > +
> > +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> > +					WMAX_METHOD_THERMAL_INFORMATION, &out_data);
> 
> Are you sure you want to keep out_data as u32? I'm not very happy how
> alienware_wmax_command() takes int * but all callers seem to prefer u32 *
> (or pass NULL). 
>

I don't have the old WMAX interface but the new one specifies an [out]
uint32 argument for all methods, I guess it's the same for the old one.

> Should the out_data parameter for alienware_wmax_command() 
> be u32 * or int *?
 
I'd say we go with u32 * for everything to resemble as much as possible
the WMAX interface description. That way there won't be confusion in the 
future.

I will name 0xFFFFFFFF appropriately.

> In general, if you find something that doesn't make sense, it often just
> is an indication that some cleanup is in order. We're more than happy to
> consider such patches along with the feature patches as then things are
> moving into the correct direction even if the progress would be slow.

All right. I will keep it in mind.

I will send a clean up patch together with the split Armin requested
together with v4.

> > +	if (ACPI_FAILURE(status))
> > +		return -EOPNOTSUPP;
> > +
> > +	if (out_data == 0xFFFFFFFF)
> 
> This constant too should be named if the data really is u32 and not a 
> negative error code in which case I'd be fine with < 0 (without naming
> the error code) like in the original but it would need int as the type for 
> the compare to work.
> 
> > +		return -EBADRQC;
> > +
> > +	switch (out_data) {
> > +	case WMAX_THERMAL_LOW_POWER:
> > +		*profile = PLATFORM_PROFILE_LOW_POWER;
> > +		break;
> > +	case WMAX_THERMAL_QUIET:
> > +		*profile = PLATFORM_PROFILE_QUIET;
> > +		break;
> > +	case WMAX_THERMAL_BALANCED:
> > +		*profile = PLATFORM_PROFILE_BALANCED;
> > +		break;
> > +	case WMAX_THERMAL_BALANCED_PERFORMANCE:
> > +		*profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE;
> > +		break;
> > +	case WMAX_THERMAL_PERFORMANCE:
> > +	case WMAX_THERMAL_GMODE:
> > +		*profile = PLATFORM_PROFILE_PERFORMANCE;
> > +		break;
> > +	default:
> > +		return -ENODATA;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int thermal_profile_set(struct platform_profile_handler *pprof,
> > +				enum platform_profile_option profile)
> > +{
> > +	acpi_status status;
> > +	u32 in_args;
> > +	u32 out_data;
> > +
> > +	switch (profile) {
> > +	case PLATFORM_PROFILE_LOW_POWER:
> > +		in_args = profile_to_wmax_arg(WMAX_THERMAL_LOW_POWER);
> > +		break;
> > +	case PLATFORM_PROFILE_QUIET:
> > +		in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET);
> > +		break;
> > +	case PLATFORM_PROFILE_BALANCED:
> > +		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED);
> > +		break;
> > +	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
> > +		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE);
> > +		break;
> > +	case PLATFORM_PROFILE_PERFORMANCE:
> > +		in_args = profile_to_wmax_arg(WMAX_THERMAL_PERFORMANCE);
> > +		break;
> > +	default:
> > +		return -EOPNOTSUPP;
> > +	}
> > +
> > +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> > +					WMAX_METHOD_THERMAL_CONTROL, &out_data);
> > +
> > +	if (ACPI_FAILURE(status))
> > +		return -EOPNOTSUPP;
> > +
> > +	if (out_data == 0xFFFFFFFF)
> 
> Ditto.
> 
> > +		return -EBADRQC;
> > +
> > +	return 0;
> > +}
> > +
> > +static int gmode_thermal_profile_set(struct platform_profile_handler *pprof,
> > +				     enum platform_profile_option profile)
> > +{
> > +	acpi_status status;
> > +	u32 in_args;
> > +	u32 out_data;
> > +
> > +	switch (profile) {
> > +	case PLATFORM_PROFILE_LOW_POWER:
> > +		in_args = profile_to_wmax_arg(WMAX_THERMAL_LOW_POWER);
> > +		break;
> > +	case PLATFORM_PROFILE_QUIET:
> > +		in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET);
> > +		break;
> > +	case PLATFORM_PROFILE_BALANCED:
> > +		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED);
> > +		break;
> > +	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
> > +		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE);
> > +		break;
> > +	case PLATFORM_PROFILE_PERFORMANCE:
> > +		in_args = profile_to_wmax_arg(WMAX_THERMAL_GMODE);
> > +		break;
> > +	default:
> > +		return -EOPNOTSUPP;
> > +	}
> > +
> > +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> > +					WMAX_METHOD_THERMAL_CONTROL, &out_data);
> > +
> > +	if (ACPI_FAILURE(status))
> > +		return -EOPNOTSUPP;
> > +
> > +	if (out_data == 0xFFFFFFFF)
> 
> Ditto.
> 
> -- 
>  i.
> 

Thank you for your feedback!

Kurt

> > +		return -EBADRQC;
> > +
> > +	return 0;
> > +}
> > +
> > +static int create_thermal_profile(void)
> > +{
> > +	pp_handler.profile_get = thermal_profile_get;
> > +
> > +	if (quirks->gmode > 0)
> > +		pp_handler.profile_set = gmode_thermal_profile_set;
> > +	else
> > +		pp_handler.profile_set = thermal_profile_set;
> > +
> > +	set_bit(PLATFORM_PROFILE_LOW_POWER, pp_handler.choices);
> > +	set_bit(PLATFORM_PROFILE_QUIET, pp_handler.choices);
> > +	set_bit(PLATFORM_PROFILE_BALANCED, pp_handler.choices);
> > +	set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, pp_handler.choices);
> > +	set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices);
> > +
> > +	return platform_profile_register(&pp_handler);
> > +}
> > +
> > +static void remove_thermal_profile(void)
> > +{
> > +	if (quirks->thermal > 0)
> > +		platform_profile_remove();
> > +}
> > +
> >  static int __init alienware_wmi_init(void)
> >  {
> >  	int ret;
> > @@ -807,6 +1011,12 @@ static int __init alienware_wmi_init(void)
> >  			goto fail_prep_deepsleep;
> >  	}
> >  
> > +	if (quirks->thermal > 0) {
> > +		ret = create_thermal_profile();
> > +		if (ret)
> > +			goto fail_prep_thermal_profile;
> > +	}
> > +
> >  	ret = alienware_zone_init(platform_device);
> >  	if (ret)
> >  		goto fail_prep_zones;
> > @@ -817,6 +1027,7 @@ static int __init alienware_wmi_init(void)
> >  	alienware_zone_exit(platform_device);
> >  fail_prep_deepsleep:
> >  fail_prep_amplifier:
> > +fail_prep_thermal_profile:
> >  fail_prep_hdmi:
> >  	platform_device_del(platform_device);
> >  fail_platform_device2:
> > @@ -834,6 +1045,7 @@ static void __exit alienware_wmi_exit(void)
> >  	if (platform_device) {
> >  		alienware_zone_exit(platform_device);
> >  		remove_hdmi(platform_device);
> > +		remove_thermal_profile();
> >  		platform_device_unregister(platform_device);
> >  		platform_driver_unregister(&platform_driver);
> >  	}
> >
Re: [PATCH v3] alienware-wmi: Dell AWCC platform_profile support
Posted by Armin Wolf 1 month, 2 weeks ago
Am 08.10.24 um 21:56 schrieb Kurt Borja:

> This patch adds platform_profile support for Dell devices which implement
> User Selectable Thermal Tables (USTT) that are meant to be controlled by
> Alienware Command Center (AWCC). These devices may include newer Alienware
> M-Series, Alienware X-Series and Dell's G-Series. This patch, was tested
> by me on an Alienware x15 R1.
>
> It is suspected that Alienware Command Center manages thermal profiles
> through the WMI interface, specifically through a device with identifier
> \_SB_.AMW1.WMAX. This device was reverse engineered and the relevant
> functionality is documented here [1]. This driver interacts with this
> WMI device and thus is able to mimic AWCC's thermal profiles functionality
> through the platform_profile API. In consequence the user would be able
> to set and retrieve thermal profiles, which are just fan speed profiles.

Can you write a short piece of documentation at Documentation/wmi/devices/
to describe the Alienware WMI interface? This would be helpful for future developers.

> This driver was heavily inspired on inspur_platform_profile, special
> thanks.
>
> Notes:
>   - Performance (FullSpeed) profile is a special profile which has it's own
>     entry in the Firmware Settings of the Alienware x15 R1. It also changes
>     the color of the F1 key. I suspect this behavior would be replicated in
>     other X-Series or M-Series laptops.
>   - G-Mode is a profile documented on [1] which mimics the behavior of
>     FullSpeed mode but it does not have an entry on the Firmware Settings of
>     the Alienware x15 R1, this may correspond to the G-Mode functionality on
>     G-Series laptops (activated by a special button) but I cannot test it. I
>     did not include this code in the driver as G-Mode causes unexpected
>     behavior on X-Series laptops.
>
> Signed-off-by: Kurt Borja <kuurtb@gmail.com>
>
> ---
> v3:
>   - Removed extra empty line
>   - 0x0B named WMAX_ARG_GET_CURRENT_PROF
>   - Removed casts to the same type on functions added in this patch
>   - Thermal profile to WMAX argument is now an static function and makes
>     use of in-built kernel macros
>   - Platform profile is now removed only if it was created first
>   - create_platform_profile is now create_thermal_profile to avoid
>     confusion
>   - profile_get and profile_set functions renamed too to match the above
> v2:
>   - Moved functionality to alienware-wmi driver
>   - Added thermal and gmode quirks to add support based on dmi match
>   - Performance profile is now GMODE for devices that support it
>   - alienware_wmax_command now is insize agnostic to support new thermal
>     methods
> ---
>   drivers/platform/x86/dell/Kconfig         |   1 +
>   drivers/platform/x86/dell/alienware-wmi.c | 238 ++++++++++++++++++++--
>   2 files changed, 226 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig
> index 68a49788a..b06d634cd 100644
> --- a/drivers/platform/x86/dell/Kconfig
> +++ b/drivers/platform/x86/dell/Kconfig
> @@ -21,6 +21,7 @@ config ALIENWARE_WMI
>   	depends on LEDS_CLASS
>   	depends on NEW_LEDS
>   	depends on ACPI_WMI
> +	select ACPI_PLATFORM_PROFILE
>   	help
>   	 This is a driver for controlling Alienware BIOS driven
>   	 features.  It exposes an interface for controlling the AlienFX
> diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
> index f5ee62ce1..e3ef4b10b 100644
> --- a/drivers/platform/x86/dell/alienware-wmi.c
> +++ b/drivers/platform/x86/dell/alienware-wmi.c
> @@ -10,6 +10,7 @@
>   #include <linux/acpi.h>
>   #include <linux/module.h>
>   #include <linux/platform_device.h>
> +#include <linux/platform_profile.h>
>   #include <linux/dmi.h>
>   #include <linux/leds.h>
>
> @@ -25,6 +26,10 @@
>   #define WMAX_METHOD_AMPLIFIER_CABLE	0x6
>   #define WMAX_METHOD_DEEP_SLEEP_CONTROL	0x0B
>   #define WMAX_METHOD_DEEP_SLEEP_STATUS	0x0C
> +#define WMAX_METHOD_THERMAL_INFORMATION	0x14
> +#define WMAX_METHOD_THERMAL_CONTROL	0x15
> +
> +#define WMAX_ARG_GET_CURRENT_PROF	0x0B
>
>   MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
>   MODULE_DESCRIPTION("Alienware special feature control");
> @@ -49,11 +54,22 @@ enum WMAX_CONTROL_STATES {
>   	WMAX_SUSPEND = 3,
>   };
>
> +enum WMAX_THERMAL_PROFILE {
> +	WMAX_THERMAL_QUIET = 0xA3,
> +	WMAX_THERMAL_BALANCED = 0xA0,
> +	WMAX_THERMAL_BALANCED_PERFORMANCE = 0xA1,
> +	WMAX_THERMAL_PERFORMANCE = 0xA4,
> +	WMAX_THERMAL_GMODE = 0xAB,
> +	WMAX_THERMAL_LOW_POWER	= 0xA5,
> +};
> +
>   struct quirk_entry {
>   	u8 num_zones;
>   	u8 hdmi_mux;
>   	u8 amplifier;
>   	u8 deepslp;
> +	u8 thermal;
> +	u8 gmode;
>   };
>
>   static struct quirk_entry *quirks;
> @@ -64,6 +80,8 @@ static struct quirk_entry quirk_inspiron5675 = {
>   	.hdmi_mux = 0,
>   	.amplifier = 0,
>   	.deepslp = 0,
> +	.thermal = 0,
> +	.gmode = 0,
>   };
>
>   static struct quirk_entry quirk_unknown = {
> @@ -71,6 +89,8 @@ static struct quirk_entry quirk_unknown = {
>   	.hdmi_mux = 0,
>   	.amplifier = 0,
>   	.deepslp = 0,
> +	.thermal = 0,
> +	.gmode = 0,
>   };
>
>   static struct quirk_entry quirk_x51_r1_r2 = {
> @@ -78,6 +98,8 @@ static struct quirk_entry quirk_x51_r1_r2 = {
>   	.hdmi_mux = 0,
>   	.amplifier = 0,
>   	.deepslp = 0,
> +	.thermal = 0,
> +	.gmode = 0,
>   };
>
>   static struct quirk_entry quirk_x51_r3 = {
> @@ -85,6 +107,8 @@ static struct quirk_entry quirk_x51_r3 = {
>   	.hdmi_mux = 0,
>   	.amplifier = 1,
>   	.deepslp = 0,
> +	.thermal = 0,
> +	.gmode = 0,
>   };
>
>   static struct quirk_entry quirk_asm100 = {
> @@ -92,6 +116,8 @@ static struct quirk_entry quirk_asm100 = {
>   	.hdmi_mux = 1,
>   	.amplifier = 0,
>   	.deepslp = 0,
> +	.thermal = 0,
> +	.gmode = 0,
>   };
>
>   static struct quirk_entry quirk_asm200 = {
> @@ -99,6 +125,8 @@ static struct quirk_entry quirk_asm200 = {
>   	.hdmi_mux = 1,
>   	.amplifier = 0,
>   	.deepslp = 1,
> +	.thermal = 0,
> +	.gmode = 0,
>   };
>
>   static struct quirk_entry quirk_asm201 = {
> @@ -106,6 +134,17 @@ static struct quirk_entry quirk_asm201 = {
>   	.hdmi_mux = 1,
>   	.amplifier = 1,
>   	.deepslp = 1,
> +	.thermal = 0,
> +	.gmode = 0,
> +};
> +
> +static struct quirk_entry quirk_x15_r1 = {
> +	.num_zones = 2,
> +	.hdmi_mux = 0,
> +	.amplifier = 0,
> +	.deepslp = 0,
> +	.thermal = 1,
> +	.gmode = 0,
>   };
>
>   static int __init dmi_matched(const struct dmi_system_id *dmi)
> @@ -169,6 +208,15 @@ static const struct dmi_system_id alienware_quirks[] __initconst = {
>   		     },
>   	 .driver_data = &quirk_asm201,
>   	 },
> +	 {
> +	 .callback = dmi_matched,
> +	 .ident = "Alienware x15 R1",
> +	 .matches = {
> +		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> +		     DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R1")
> +		    },
> +	 .driver_data = &quirk_x15_r1,
> +	},
>   	 {
>   	 .callback = dmi_matched,
>   	 .ident = "Dell Inc. Inspiron 5675",
> @@ -218,6 +266,7 @@ static struct platform_device *platform_device;
>   static struct device_attribute *zone_dev_attrs;
>   static struct attribute **zone_attrs;
>   static struct platform_zone *zone_data;
> +static struct platform_profile_handler pp_handler;
>
>   static struct platform_driver platform_driver = {
>   	.driver = {
> @@ -500,7 +549,7 @@ static void alienware_zone_exit(struct platform_device *dev)
>   	kfree(zone_attrs);
>   }
>
> -static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
> +static acpi_status alienware_wmax_command(void *in_args, size_t insize,
>   					  u32 command, int *out_data)
>   {

Can you split this change into a separate patch? This would make review a bit easier.

>   	acpi_status status;
> @@ -508,7 +557,7 @@ static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
>   	struct acpi_buffer input;
>   	struct acpi_buffer output;
>
> -	input.length = (acpi_size) sizeof(*in_args);
> +	input.length = (acpi_size) insize;

Please drop the cast to acpi_size.

>   	input.pointer = in_args;
>   	if (out_data) {
>   		output.length = ACPI_ALLOCATE_BUFFER;
> @@ -541,8 +590,8 @@ static ssize_t show_hdmi_cable(struct device *dev,
>   		.arg = 0,
>   	};
>   	status =
> -	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE,
> -				   (u32 *) &out_data);
> +	    alienware_wmax_command(&in_args, sizeof(in_args),
> +				   WMAX_METHOD_HDMI_CABLE, (u32 *) &out_data);
>   	if (ACPI_SUCCESS(status)) {
>   		if (out_data == 0)
>   			return sysfs_emit(buf, "[unconnected] connected unknown\n");
> @@ -562,8 +611,8 @@ static ssize_t show_hdmi_source(struct device *dev,
>   		.arg = 0,
>   	};
>   	status =
> -	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS,
> -				   (u32 *) &out_data);
> +	    alienware_wmax_command(&in_args, sizeof(in_args),
> +				   WMAX_METHOD_HDMI_STATUS, (u32 *) &out_data);
>
>   	if (ACPI_SUCCESS(status)) {
>   		if (out_data == 1)
> @@ -589,7 +638,8 @@ static ssize_t toggle_hdmi_source(struct device *dev,
>   		args.arg = 3;
>   	pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf);
>
> -	status = alienware_wmax_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL);
> +	status = alienware_wmax_command(&args, sizeof(args),
> +					WMAX_METHOD_HDMI_SOURCE, NULL);
>
>   	if (ACPI_FAILURE(status))
>   		pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
> @@ -642,8 +692,8 @@ static ssize_t show_amplifier_status(struct device *dev,
>   		.arg = 0,
>   	};
>   	status =
> -	    alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE,
> -				   (u32 *) &out_data);
> +	    alienware_wmax_command(&in_args, sizeof(in_args),
> +				   WMAX_METHOD_AMPLIFIER_CABLE, (u32 *) &out_data);
>   	if (ACPI_SUCCESS(status)) {
>   		if (out_data == 0)
>   			return sysfs_emit(buf, "[unconnected] connected unknown\n");
> @@ -694,8 +744,8 @@ static ssize_t show_deepsleep_status(struct device *dev,
>   	struct wmax_basic_args in_args = {
>   		.arg = 0,
>   	};
> -	status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS,
> -					(u32 *) &out_data);
> +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> +					WMAX_METHOD_DEEP_SLEEP_STATUS, (u32 *) &out_data);
>   	if (ACPI_SUCCESS(status)) {
>   		if (out_data == 0)
>   			return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
> @@ -723,8 +773,8 @@ static ssize_t toggle_deepsleep(struct device *dev,
>   		args.arg = 2;
>   	pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf);
>
> -	status = alienware_wmax_command(&args, WMAX_METHOD_DEEP_SLEEP_CONTROL,
> -					NULL);
> +	status = alienware_wmax_command(&args, sizeof(args),
> +					WMAX_METHOD_DEEP_SLEEP_CONTROL, NULL);
>
>   	if (ACPI_FAILURE(status))
>   		pr_err("alienware-wmi: deep sleep control failed: results: %u\n",
> @@ -760,6 +810,160 @@ static int create_deepsleep(struct platform_device *dev)
>   	return ret;
>   }
>
> +/*
> + * Thermal Profile control
> + *  - Provides thermal profile control through the Platform Profile API
> + */
> +#define PROFILE_MASK		GENMASK(15,8)
> +#define PROFILE_ACTIVATE	BIT(0)
> +
> +static u32 profile_to_wmax_arg(enum WMAX_THERMAL_PROFILE prof)
> +{
> +	return FIELD_PREP(PROFILE_MASK, prof) | PROFILE_ACTIVATE;
> +}
> +
> +static int thermal_profile_get(struct platform_profile_handler *pprof,
> +				enum platform_profile_option *profile)
> +{
> +	acpi_status status;
> +	u32 in_args = WMAX_ARG_GET_CURRENT_PROF;
> +	u32 out_data;
> +
> +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> +					WMAX_METHOD_THERMAL_INFORMATION, &out_data);
> +
> +	if (ACPI_FAILURE(status))
> +		return -EOPNOTSUPP;

Please return -EIO.

> +
> +	if (out_data == 0xFFFFFFFF)
> +		return -EBADRQC;
> +
> +	switch (out_data) {
> +	case WMAX_THERMAL_LOW_POWER:
> +		*profile = PLATFORM_PROFILE_LOW_POWER;
> +		break;
> +	case WMAX_THERMAL_QUIET:
> +		*profile = PLATFORM_PROFILE_QUIET;
> +		break;
> +	case WMAX_THERMAL_BALANCED:
> +		*profile = PLATFORM_PROFILE_BALANCED;
> +		break;
> +	case WMAX_THERMAL_BALANCED_PERFORMANCE:
> +		*profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE;
> +		break;
> +	case WMAX_THERMAL_PERFORMANCE:
> +	case WMAX_THERMAL_GMODE:
> +		*profile = PLATFORM_PROFILE_PERFORMANCE;
> +		break;
> +	default:
> +		return -ENODATA;
> +	}
> +
> +	return 0;
> +}
> +
> +static int thermal_profile_set(struct platform_profile_handler *pprof,
> +				enum platform_profile_option profile)
> +{
> +	acpi_status status;
> +	u32 in_args;
> +	u32 out_data;
> +
> +	switch (profile) {
> +	case PLATFORM_PROFILE_LOW_POWER:
> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_LOW_POWER);
> +		break;
> +	case PLATFORM_PROFILE_QUIET:
> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET);
> +		break;
> +	case PLATFORM_PROFILE_BALANCED:
> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED);
> +		break;
> +	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE);
> +		break;
> +	case PLATFORM_PROFILE_PERFORMANCE:
> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_PERFORMANCE);
> +		break;
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +
> +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> +					WMAX_METHOD_THERMAL_CONTROL, &out_data);
> +
> +	if (ACPI_FAILURE(status))
> +		return -EOPNOTSUPP;

Return -EIO.

> +
> +	if (out_data == 0xFFFFFFFF)
> +		return -EBADRQC;
> +
> +	return 0;
> +}
> +
> +static int gmode_thermal_profile_set(struct platform_profile_handler *pprof,
> +				     enum platform_profile_option profile)
> +{
> +	acpi_status status;
> +	u32 in_args;
> +	u32 out_data;
> +
> +	switch (profile) {
> +	case PLATFORM_PROFILE_LOW_POWER:
> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_LOW_POWER);
> +		break;
> +	case PLATFORM_PROFILE_QUIET:
> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET);
> +		break;
> +	case PLATFORM_PROFILE_BALANCED:
> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED);
> +		break;
> +	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE);
> +		break;
> +	case PLATFORM_PROFILE_PERFORMANCE:
> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_GMODE);
> +		break;
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +
> +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> +					WMAX_METHOD_THERMAL_CONTROL, &out_data);
> +
> +	if (ACPI_FAILURE(status))
> +		return -EOPNOTSUPP;

Return -EIO.

Otherwise the patch looks quite good.

Thanks,
Armin Wolf

> +
> +	if (out_data == 0xFFFFFFFF)
> +		return -EBADRQC;
> +
> +	return 0;
> +}
> +
> +static int create_thermal_profile(void)
> +{
> +	pp_handler.profile_get = thermal_profile_get;
> +
> +	if (quirks->gmode > 0)
> +		pp_handler.profile_set = gmode_thermal_profile_set;
> +	else
> +		pp_handler.profile_set = thermal_profile_set;
> +
> +	set_bit(PLATFORM_PROFILE_LOW_POWER, pp_handler.choices);
> +	set_bit(PLATFORM_PROFILE_QUIET, pp_handler.choices);
> +	set_bit(PLATFORM_PROFILE_BALANCED, pp_handler.choices);
> +	set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, pp_handler.choices);
> +	set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices);
> +
> +	return platform_profile_register(&pp_handler);
> +}
> +
> +static void remove_thermal_profile(void)
> +{
> +	if (quirks->thermal > 0)
> +		platform_profile_remove();
> +}
> +
>   static int __init alienware_wmi_init(void)
>   {
>   	int ret;
> @@ -807,6 +1011,12 @@ static int __init alienware_wmi_init(void)
>   			goto fail_prep_deepsleep;
>   	}
>
> +	if (quirks->thermal > 0) {
> +		ret = create_thermal_profile();
> +		if (ret)
> +			goto fail_prep_thermal_profile;
> +	}
> +
>   	ret = alienware_zone_init(platform_device);
>   	if (ret)
>   		goto fail_prep_zones;
> @@ -817,6 +1027,7 @@ static int __init alienware_wmi_init(void)
>   	alienware_zone_exit(platform_device);
>   fail_prep_deepsleep:
>   fail_prep_amplifier:
> +fail_prep_thermal_profile:
>   fail_prep_hdmi:
>   	platform_device_del(platform_device);
>   fail_platform_device2:
> @@ -834,6 +1045,7 @@ static void __exit alienware_wmi_exit(void)
>   	if (platform_device) {
>   		alienware_zone_exit(platform_device);
>   		remove_hdmi(platform_device);
> +		remove_thermal_profile();
>   		platform_device_unregister(platform_device);
>   		platform_driver_unregister(&platform_driver);
>   	}
Re: [PATCH v3] alienware-wmi: Dell AWCC platform_profile support
Posted by Kurt Borja 1 month, 2 weeks ago
Yes, of course.

> > This driver was heavily inspired on inspur_platform_profile, special
> > thanks.
> > 
> > Notes:
> >   - Performance (FullSpeed) profile is a special profile which has it's own
> >     entry in the Firmware Settings of the Alienware x15 R1. It also changes
> >     the color of the F1 key. I suspect this behavior would be replicated in
> >     other X-Series or M-Series laptops.
> >   - G-Mode is a profile documented on [1] which mimics the behavior of
> >     FullSpeed mode but it does not have an entry on the Firmware Settings of
> >     the Alienware x15 R1, this may correspond to the G-Mode functionality on
> >     G-Series laptops (activated by a special button) but I cannot test it. I
> >     did not include this code in the driver as G-Mode causes unexpected
> >     behavior on X-Series laptops.
> > 
> > Signed-off-by: Kurt Borja <kuurtb@gmail.com>
> > 
> > ---
> > v3:
> >   - Removed extra empty line
> >   - 0x0B named WMAX_ARG_GET_CURRENT_PROF
> >   - Removed casts to the same type on functions added in this patch
> >   - Thermal profile to WMAX argument is now an static function and makes
> >     use of in-built kernel macros
> >   - Platform profile is now removed only if it was created first
> >   - create_platform_profile is now create_thermal_profile to avoid
> >     confusion
> >   - profile_get and profile_set functions renamed too to match the above
> > v2:
> >   - Moved functionality to alienware-wmi driver
> >   - Added thermal and gmode quirks to add support based on dmi match
> >   - Performance profile is now GMODE for devices that support it
> >   - alienware_wmax_command now is insize agnostic to support new thermal
> >     methods
> > ---
> >   drivers/platform/x86/dell/Kconfig         |   1 +
> >   drivers/platform/x86/dell/alienware-wmi.c | 238 ++++++++++++++++++++--
> >   2 files changed, 226 insertions(+), 13 deletions(-)
> > 
> > diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig
> > index 68a49788a..b06d634cd 100644
> > --- a/drivers/platform/x86/dell/Kconfig
> > +++ b/drivers/platform/x86/dell/Kconfig
> > @@ -21,6 +21,7 @@ config ALIENWARE_WMI
> >   	depends on LEDS_CLASS
> >   	depends on NEW_LEDS
> >   	depends on ACPI_WMI
> > +	select ACPI_PLATFORM_PROFILE
> >   	help
> >   	 This is a driver for controlling Alienware BIOS driven
> >   	 features.  It exposes an interface for controlling the AlienFX
> > diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
> > index f5ee62ce1..e3ef4b10b 100644
> > --- a/drivers/platform/x86/dell/alienware-wmi.c
> > +++ b/drivers/platform/x86/dell/alienware-wmi.c
> > @@ -10,6 +10,7 @@
> >   #include <linux/acpi.h>
> >   #include <linux/module.h>
> >   #include <linux/platform_device.h>
> > +#include <linux/platform_profile.h>
> >   #include <linux/dmi.h>
> >   #include <linux/leds.h>
> > 
> > @@ -25,6 +26,10 @@
> >   #define WMAX_METHOD_AMPLIFIER_CABLE	0x6
> >   #define WMAX_METHOD_DEEP_SLEEP_CONTROL	0x0B
> >   #define WMAX_METHOD_DEEP_SLEEP_STATUS	0x0C
> > +#define WMAX_METHOD_THERMAL_INFORMATION	0x14
> > +#define WMAX_METHOD_THERMAL_CONTROL	0x15
> > +
> > +#define WMAX_ARG_GET_CURRENT_PROF	0x0B
> > 
> >   MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
> >   MODULE_DESCRIPTION("Alienware special feature control");
> > @@ -49,11 +54,22 @@ enum WMAX_CONTROL_STATES {
> >   	WMAX_SUSPEND = 3,
> >   };
> > 
> > +enum WMAX_THERMAL_PROFILE {
> > +	WMAX_THERMAL_QUIET = 0xA3,
> > +	WMAX_THERMAL_BALANCED = 0xA0,
> > +	WMAX_THERMAL_BALANCED_PERFORMANCE = 0xA1,
> > +	WMAX_THERMAL_PERFORMANCE = 0xA4,
> > +	WMAX_THERMAL_GMODE = 0xAB,
> > +	WMAX_THERMAL_LOW_POWER	= 0xA5,
> > +};
> > +
> >   struct quirk_entry {
> >   	u8 num_zones;
> >   	u8 hdmi_mux;
> >   	u8 amplifier;
> >   	u8 deepslp;
> > +	u8 thermal;
> > +	u8 gmode;
> >   };
> > 
> >   static struct quirk_entry *quirks;
> > @@ -64,6 +80,8 @@ static struct quirk_entry quirk_inspiron5675 = {
> >   	.hdmi_mux = 0,
> >   	.amplifier = 0,
> >   	.deepslp = 0,
> > +	.thermal = 0,
> > +	.gmode = 0,
> >   };
> > 
> >   static struct quirk_entry quirk_unknown = {
> > @@ -71,6 +89,8 @@ static struct quirk_entry quirk_unknown = {
> >   	.hdmi_mux = 0,
> >   	.amplifier = 0,
> >   	.deepslp = 0,
> > +	.thermal = 0,
> > +	.gmode = 0,
> >   };
> > 
> >   static struct quirk_entry quirk_x51_r1_r2 = {
> > @@ -78,6 +98,8 @@ static struct quirk_entry quirk_x51_r1_r2 = {
> >   	.hdmi_mux = 0,
> >   	.amplifier = 0,
> >   	.deepslp = 0,
> > +	.thermal = 0,
> > +	.gmode = 0,
> >   };
> > 
> >   static struct quirk_entry quirk_x51_r3 = {
> > @@ -85,6 +107,8 @@ static struct quirk_entry quirk_x51_r3 = {
> >   	.hdmi_mux = 0,
> >   	.amplifier = 1,
> >   	.deepslp = 0,
> > +	.thermal = 0,
> > +	.gmode = 0,
> >   };
> > 
> >   static struct quirk_entry quirk_asm100 = {
> > @@ -92,6 +116,8 @@ static struct quirk_entry quirk_asm100 = {
> >   	.hdmi_mux = 1,
> >   	.amplifier = 0,
> >   	.deepslp = 0,
> > +	.thermal = 0,
> > +	.gmode = 0,
> >   };
> > 
> >   static struct quirk_entry quirk_asm200 = {
> > @@ -99,6 +125,8 @@ static struct quirk_entry quirk_asm200 = {
> >   	.hdmi_mux = 1,
> >   	.amplifier = 0,
> >   	.deepslp = 1,
> > +	.thermal = 0,
> > +	.gmode = 0,
> >   };
> > 
> >   static struct quirk_entry quirk_asm201 = {
> > @@ -106,6 +134,17 @@ static struct quirk_entry quirk_asm201 = {
> >   	.hdmi_mux = 1,
> >   	.amplifier = 1,
> >   	.deepslp = 1,
> > +	.thermal = 0,
> > +	.gmode = 0,
> > +};
> > +
> > +static struct quirk_entry quirk_x15_r1 = {
> > +	.num_zones = 2,
> > +	.hdmi_mux = 0,
> > +	.amplifier = 0,
> > +	.deepslp = 0,
> > +	.thermal = 1,
> > +	.gmode = 0,
> >   };
> > 
> >   static int __init dmi_matched(const struct dmi_system_id *dmi)
> > @@ -169,6 +208,15 @@ static const struct dmi_system_id alienware_quirks[] __initconst = {
> >   		     },
> >   	 .driver_data = &quirk_asm201,
> >   	 },
> > +	 {
> > +	 .callback = dmi_matched,
> > +	 .ident = "Alienware x15 R1",
> > +	 .matches = {
> > +		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> > +		     DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R1")
> > +		    },
> > +	 .driver_data = &quirk_x15_r1,
> > +	},
> >   	 {
> >   	 .callback = dmi_matched,
> >   	 .ident = "Dell Inc. Inspiron 5675",
> > @@ -218,6 +266,7 @@ static struct platform_device *platform_device;
> >   static struct device_attribute *zone_dev_attrs;
> >   static struct attribute **zone_attrs;
> >   static struct platform_zone *zone_data;
> > +static struct platform_profile_handler pp_handler;
> > 
> >   static struct platform_driver platform_driver = {
> >   	.driver = {
> > @@ -500,7 +549,7 @@ static void alienware_zone_exit(struct platform_device *dev)
> >   	kfree(zone_attrs);
> >   }
> > 
> > -static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
> > +static acpi_status alienware_wmax_command(void *in_args, size_t insize,
> >   					  u32 command, int *out_data)
> >   {
> 
> Can you split this change into a separate patch? This would make review a bit easier.
> 

Yes, sure. In that case should I mention this patch depends on that one
in v4?


> >   	acpi_status status;
> > @@ -508,7 +557,7 @@ static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
> >   	struct acpi_buffer input;
> >   	struct acpi_buffer output;
> > 
> > -	input.length = (acpi_size) sizeof(*in_args);
> > +	input.length = (acpi_size) insize;
> 
> Please drop the cast to acpi_size.
> 

Ok.

> >   	input.pointer = in_args;
> >   	if (out_data) {
> >   		output.length = ACPI_ALLOCATE_BUFFER;
> > @@ -541,8 +590,8 @@ static ssize_t show_hdmi_cable(struct device *dev,
> >   		.arg = 0,
> >   	};
> >   	status =
> > -	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE,
> > -				   (u32 *) &out_data);
> > +	    alienware_wmax_command(&in_args, sizeof(in_args),
> > +				   WMAX_METHOD_HDMI_CABLE, (u32 *) &out_data);
> >   	if (ACPI_SUCCESS(status)) {
> >   		if (out_data == 0)
> >   			return sysfs_emit(buf, "[unconnected] connected unknown\n");
> > @@ -562,8 +611,8 @@ static ssize_t show_hdmi_source(struct device *dev,
> >   		.arg = 0,
> >   	};
> >   	status =
> > -	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS,
> > -				   (u32 *) &out_data);
> > +	    alienware_wmax_command(&in_args, sizeof(in_args),
> > +				   WMAX_METHOD_HDMI_STATUS, (u32 *) &out_data);
> > 
> >   	if (ACPI_SUCCESS(status)) {
> >   		if (out_data == 1)
> > @@ -589,7 +638,8 @@ static ssize_t toggle_hdmi_source(struct device *dev,
> >   		args.arg = 3;
> >   	pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf);
> > 
> > -	status = alienware_wmax_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL);
> > +	status = alienware_wmax_command(&args, sizeof(args),
> > +					WMAX_METHOD_HDMI_SOURCE, NULL);
> > 
> >   	if (ACPI_FAILURE(status))
> >   		pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
> > @@ -642,8 +692,8 @@ static ssize_t show_amplifier_status(struct device *dev,
> >   		.arg = 0,
> >   	};
> >   	status =
> > -	    alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE,
> > -				   (u32 *) &out_data);
> > +	    alienware_wmax_command(&in_args, sizeof(in_args),
> > +				   WMAX_METHOD_AMPLIFIER_CABLE, (u32 *) &out_data);
> >   	if (ACPI_SUCCESS(status)) {
> >   		if (out_data == 0)
> >   			return sysfs_emit(buf, "[unconnected] connected unknown\n");
> > @@ -694,8 +744,8 @@ static ssize_t show_deepsleep_status(struct device *dev,
> >   	struct wmax_basic_args in_args = {
> >   		.arg = 0,
> >   	};
> > -	status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS,
> > -					(u32 *) &out_data);
> > +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> > +					WMAX_METHOD_DEEP_SLEEP_STATUS, (u32 *) &out_data);
> >   	if (ACPI_SUCCESS(status)) {
> >   		if (out_data == 0)
> >   			return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
> > @@ -723,8 +773,8 @@ static ssize_t toggle_deepsleep(struct device *dev,
> >   		args.arg = 2;
> >   	pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf);
> > 
> > -	status = alienware_wmax_command(&args, WMAX_METHOD_DEEP_SLEEP_CONTROL,
> > -					NULL);
> > +	status = alienware_wmax_command(&args, sizeof(args),
> > +					WMAX_METHOD_DEEP_SLEEP_CONTROL, NULL);
> > 
> >   	if (ACPI_FAILURE(status))
> >   		pr_err("alienware-wmi: deep sleep control failed: results: %u\n",
> > @@ -760,6 +810,160 @@ static int create_deepsleep(struct platform_device *dev)
> >   	return ret;
> >   }
> > 
> > +/*
> > + * Thermal Profile control
> > + *  - Provides thermal profile control through the Platform Profile API
> > + */
> > +#define PROFILE_MASK		GENMASK(15,8)
> > +#define PROFILE_ACTIVATE	BIT(0)
> > +
> > +static u32 profile_to_wmax_arg(enum WMAX_THERMAL_PROFILE prof)
> > +{
> > +	return FIELD_PREP(PROFILE_MASK, prof) | PROFILE_ACTIVATE;
> > +}
> > +
> > +static int thermal_profile_get(struct platform_profile_handler *pprof,
> > +				enum platform_profile_option *profile)
> > +{
> > +	acpi_status status;
> > +	u32 in_args = WMAX_ARG_GET_CURRENT_PROF;
> > +	u32 out_data;
> > +
> > +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> > +					WMAX_METHOD_THERMAL_INFORMATION, &out_data);
> > +
> > +	if (ACPI_FAILURE(status))
> > +		return -EOPNOTSUPP;
> 
> Please return -EIO.
> 

Ok.

> > +
> > +	if (out_data == 0xFFFFFFFF)
> > +		return -EBADRQC;
> > +
> > +	switch (out_data) {
> > +	case WMAX_THERMAL_LOW_POWER:
> > +		*profile = PLATFORM_PROFILE_LOW_POWER;
> > +		break;
> > +	case WMAX_THERMAL_QUIET:
> > +		*profile = PLATFORM_PROFILE_QUIET;
> > +		break;
> > +	case WMAX_THERMAL_BALANCED:
> > +		*profile = PLATFORM_PROFILE_BALANCED;
> > +		break;
> > +	case WMAX_THERMAL_BALANCED_PERFORMANCE:
> > +		*profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE;
> > +		break;
> > +	case WMAX_THERMAL_PERFORMANCE:
> > +	case WMAX_THERMAL_GMODE:
> > +		*profile = PLATFORM_PROFILE_PERFORMANCE;
> > +		break;
> > +	default:
> > +		return -ENODATA;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int thermal_profile_set(struct platform_profile_handler *pprof,
> > +				enum platform_profile_option profile)
> > +{
> > +	acpi_status status;
> > +	u32 in_args;
> > +	u32 out_data;
> > +
> > +	switch (profile) {
> > +	case PLATFORM_PROFILE_LOW_POWER:
> > +		in_args = profile_to_wmax_arg(WMAX_THERMAL_LOW_POWER);
> > +		break;
> > +	case PLATFORM_PROFILE_QUIET:
> > +		in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET);
> > +		break;
> > +	case PLATFORM_PROFILE_BALANCED:
> > +		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED);
> > +		break;
> > +	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
> > +		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE);
> > +		break;
> > +	case PLATFORM_PROFILE_PERFORMANCE:
> > +		in_args = profile_to_wmax_arg(WMAX_THERMAL_PERFORMANCE);
> > +		break;
> > +	default:
> > +		return -EOPNOTSUPP;
> > +	}
> > +
> > +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> > +					WMAX_METHOD_THERMAL_CONTROL, &out_data);
> > +
> > +	if (ACPI_FAILURE(status))
> > +		return -EOPNOTSUPP;
> 
> Return -EIO.
> 
> > +
> > +	if (out_data == 0xFFFFFFFF)
> > +		return -EBADRQC;
> > +
> > +	return 0;
> > +}
> > +
> > +static int gmode_thermal_profile_set(struct platform_profile_handler *pprof,
> > +				     enum platform_profile_option profile)
> > +{
> > +	acpi_status status;
> > +	u32 in_args;
> > +	u32 out_data;
> > +
> > +	switch (profile) {
> > +	case PLATFORM_PROFILE_LOW_POWER:
> > +		in_args = profile_to_wmax_arg(WMAX_THERMAL_LOW_POWER);
> > +		break;
> > +	case PLATFORM_PROFILE_QUIET:
> > +		in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET);
> > +		break;
> > +	case PLATFORM_PROFILE_BALANCED:
> > +		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED);
> > +		break;
> > +	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
> > +		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE);
> > +		break;
> > +	case PLATFORM_PROFILE_PERFORMANCE:
> > +		in_args = profile_to_wmax_arg(WMAX_THERMAL_GMODE);
> > +		break;
> > +	default:
> > +		return -EOPNOTSUPP;
> > +	}
> > +
> > +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> > +					WMAX_METHOD_THERMAL_CONTROL, &out_data);
> > +
> > +	if (ACPI_FAILURE(status))
> > +		return -EOPNOTSUPP;
> 
> Return -EIO.
> 
> Otherwise the patch looks quite good.
> 

Thank you! It's my first time working on the kernel.

> Thanks,
> Armin Wolf

Your feedback is appreciated.

Kurt

> > +
> > +	if (out_data == 0xFFFFFFFF)
> > +		return -EBADRQC;
> > +
> > +	return 0;
> > +}
> > +
> > +static int create_thermal_profile(void)
> > +{
> > +	pp_handler.profile_get = thermal_profile_get;
> > +
> > +	if (quirks->gmode > 0)
> > +		pp_handler.profile_set = gmode_thermal_profile_set;
> > +	else
> > +		pp_handler.profile_set = thermal_profile_set;
> > +
> > +	set_bit(PLATFORM_PROFILE_LOW_POWER, pp_handler.choices);
> > +	set_bit(PLATFORM_PROFILE_QUIET, pp_handler.choices);
> > +	set_bit(PLATFORM_PROFILE_BALANCED, pp_handler.choices);
> > +	set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, pp_handler.choices);
> > +	set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices);
> > +
> > +	return platform_profile_register(&pp_handler);
> > +}
> > +
> > +static void remove_thermal_profile(void)
> > +{
> > +	if (quirks->thermal > 0)
> > +		platform_profile_remove();
> > +}
> > +
> >   static int __init alienware_wmi_init(void)
> >   {
> >   	int ret;
> > @@ -807,6 +1011,12 @@ static int __init alienware_wmi_init(void)
> >   			goto fail_prep_deepsleep;
> >   	}
> > 
> > +	if (quirks->thermal > 0) {
> > +		ret = create_thermal_profile();
> > +		if (ret)
> > +			goto fail_prep_thermal_profile;
> > +	}
> > +
> >   	ret = alienware_zone_init(platform_device);
> >   	if (ret)
> >   		goto fail_prep_zones;
> > @@ -817,6 +1027,7 @@ static int __init alienware_wmi_init(void)
> >   	alienware_zone_exit(platform_device);
> >   fail_prep_deepsleep:
> >   fail_prep_amplifier:
> > +fail_prep_thermal_profile:
> >   fail_prep_hdmi:
> >   	platform_device_del(platform_device);
> >   fail_platform_device2:
> > @@ -834,6 +1045,7 @@ static void __exit alienware_wmi_exit(void)
> >   	if (platform_device) {
> >   		alienware_zone_exit(platform_device);
> >   		remove_hdmi(platform_device);
> > +		remove_thermal_profile();
> >   		platform_device_unregister(platform_device);
> >   		platform_driver_unregister(&platform_driver);
> >   	}
Re: [PATCH v3] alienware-wmi: Dell AWCC platform_profile support
Posted by Armin Wolf 1 month, 2 weeks ago
Am 09.10.24 um 16:48 schrieb Kurt Borja:

> Yes, of course.
>
>>> This driver was heavily inspired on inspur_platform_profile, special
>>> thanks.
>>>
>>> Notes:
>>>    - Performance (FullSpeed) profile is a special profile which has it's own
>>>      entry in the Firmware Settings of the Alienware x15 R1. It also changes
>>>      the color of the F1 key. I suspect this behavior would be replicated in
>>>      other X-Series or M-Series laptops.
>>>    - G-Mode is a profile documented on [1] which mimics the behavior of
>>>      FullSpeed mode but it does not have an entry on the Firmware Settings of
>>>      the Alienware x15 R1, this may correspond to the G-Mode functionality on
>>>      G-Series laptops (activated by a special button) but I cannot test it. I
>>>      did not include this code in the driver as G-Mode causes unexpected
>>>      behavior on X-Series laptops.
>>>
>>> Signed-off-by: Kurt Borja <kuurtb@gmail.com>
>>>
>>> ---
>>> v3:
>>>    - Removed extra empty line
>>>    - 0x0B named WMAX_ARG_GET_CURRENT_PROF
>>>    - Removed casts to the same type on functions added in this patch
>>>    - Thermal profile to WMAX argument is now an static function and makes
>>>      use of in-built kernel macros
>>>    - Platform profile is now removed only if it was created first
>>>    - create_platform_profile is now create_thermal_profile to avoid
>>>      confusion
>>>    - profile_get and profile_set functions renamed too to match the above
>>> v2:
>>>    - Moved functionality to alienware-wmi driver
>>>    - Added thermal and gmode quirks to add support based on dmi match
>>>    - Performance profile is now GMODE for devices that support it
>>>    - alienware_wmax_command now is insize agnostic to support new thermal
>>>      methods
>>> ---
>>>    drivers/platform/x86/dell/Kconfig         |   1 +
>>>    drivers/platform/x86/dell/alienware-wmi.c | 238 ++++++++++++++++++++--
>>>    2 files changed, 226 insertions(+), 13 deletions(-)
>>>
>>> diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig
>>> index 68a49788a..b06d634cd 100644
>>> --- a/drivers/platform/x86/dell/Kconfig
>>> +++ b/drivers/platform/x86/dell/Kconfig
>>> @@ -21,6 +21,7 @@ config ALIENWARE_WMI
>>>    	depends on LEDS_CLASS
>>>    	depends on NEW_LEDS
>>>    	depends on ACPI_WMI
>>> +	select ACPI_PLATFORM_PROFILE
>>>    	help
>>>    	 This is a driver for controlling Alienware BIOS driven
>>>    	 features.  It exposes an interface for controlling the AlienFX
>>> diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
>>> index f5ee62ce1..e3ef4b10b 100644
>>> --- a/drivers/platform/x86/dell/alienware-wmi.c
>>> +++ b/drivers/platform/x86/dell/alienware-wmi.c
>>> @@ -10,6 +10,7 @@
>>>    #include <linux/acpi.h>
>>>    #include <linux/module.h>
>>>    #include <linux/platform_device.h>
>>> +#include <linux/platform_profile.h>
>>>    #include <linux/dmi.h>
>>>    #include <linux/leds.h>
>>>
>>> @@ -25,6 +26,10 @@
>>>    #define WMAX_METHOD_AMPLIFIER_CABLE	0x6
>>>    #define WMAX_METHOD_DEEP_SLEEP_CONTROL	0x0B
>>>    #define WMAX_METHOD_DEEP_SLEEP_STATUS	0x0C
>>> +#define WMAX_METHOD_THERMAL_INFORMATION	0x14
>>> +#define WMAX_METHOD_THERMAL_CONTROL	0x15
>>> +
>>> +#define WMAX_ARG_GET_CURRENT_PROF	0x0B
>>>
>>>    MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
>>>    MODULE_DESCRIPTION("Alienware special feature control");
>>> @@ -49,11 +54,22 @@ enum WMAX_CONTROL_STATES {
>>>    	WMAX_SUSPEND = 3,
>>>    };
>>>
>>> +enum WMAX_THERMAL_PROFILE {
>>> +	WMAX_THERMAL_QUIET = 0xA3,
>>> +	WMAX_THERMAL_BALANCED = 0xA0,
>>> +	WMAX_THERMAL_BALANCED_PERFORMANCE = 0xA1,
>>> +	WMAX_THERMAL_PERFORMANCE = 0xA4,
>>> +	WMAX_THERMAL_GMODE = 0xAB,
>>> +	WMAX_THERMAL_LOW_POWER	= 0xA5,
>>> +};
>>> +
>>>    struct quirk_entry {
>>>    	u8 num_zones;
>>>    	u8 hdmi_mux;
>>>    	u8 amplifier;
>>>    	u8 deepslp;
>>> +	u8 thermal;
>>> +	u8 gmode;
>>>    };
>>>
>>>    static struct quirk_entry *quirks;
>>> @@ -64,6 +80,8 @@ static struct quirk_entry quirk_inspiron5675 = {
>>>    	.hdmi_mux = 0,
>>>    	.amplifier = 0,
>>>    	.deepslp = 0,
>>> +	.thermal = 0,
>>> +	.gmode = 0,
>>>    };
>>>
>>>    static struct quirk_entry quirk_unknown = {
>>> @@ -71,6 +89,8 @@ static struct quirk_entry quirk_unknown = {
>>>    	.hdmi_mux = 0,
>>>    	.amplifier = 0,
>>>    	.deepslp = 0,
>>> +	.thermal = 0,
>>> +	.gmode = 0,
>>>    };
>>>
>>>    static struct quirk_entry quirk_x51_r1_r2 = {
>>> @@ -78,6 +98,8 @@ static struct quirk_entry quirk_x51_r1_r2 = {
>>>    	.hdmi_mux = 0,
>>>    	.amplifier = 0,
>>>    	.deepslp = 0,
>>> +	.thermal = 0,
>>> +	.gmode = 0,
>>>    };
>>>
>>>    static struct quirk_entry quirk_x51_r3 = {
>>> @@ -85,6 +107,8 @@ static struct quirk_entry quirk_x51_r3 = {
>>>    	.hdmi_mux = 0,
>>>    	.amplifier = 1,
>>>    	.deepslp = 0,
>>> +	.thermal = 0,
>>> +	.gmode = 0,
>>>    };
>>>
>>>    static struct quirk_entry quirk_asm100 = {
>>> @@ -92,6 +116,8 @@ static struct quirk_entry quirk_asm100 = {
>>>    	.hdmi_mux = 1,
>>>    	.amplifier = 0,
>>>    	.deepslp = 0,
>>> +	.thermal = 0,
>>> +	.gmode = 0,
>>>    };
>>>
>>>    static struct quirk_entry quirk_asm200 = {
>>> @@ -99,6 +125,8 @@ static struct quirk_entry quirk_asm200 = {
>>>    	.hdmi_mux = 1,
>>>    	.amplifier = 0,
>>>    	.deepslp = 1,
>>> +	.thermal = 0,
>>> +	.gmode = 0,
>>>    };
>>>
>>>    static struct quirk_entry quirk_asm201 = {
>>> @@ -106,6 +134,17 @@ static struct quirk_entry quirk_asm201 = {
>>>    	.hdmi_mux = 1,
>>>    	.amplifier = 1,
>>>    	.deepslp = 1,
>>> +	.thermal = 0,
>>> +	.gmode = 0,
>>> +};
>>> +
>>> +static struct quirk_entry quirk_x15_r1 = {
>>> +	.num_zones = 2,
>>> +	.hdmi_mux = 0,
>>> +	.amplifier = 0,
>>> +	.deepslp = 0,
>>> +	.thermal = 1,
>>> +	.gmode = 0,
>>>    };
>>>
>>>    static int __init dmi_matched(const struct dmi_system_id *dmi)
>>> @@ -169,6 +208,15 @@ static const struct dmi_system_id alienware_quirks[] __initconst = {
>>>    		     },
>>>    	 .driver_data = &quirk_asm201,
>>>    	 },
>>> +	 {
>>> +	 .callback = dmi_matched,
>>> +	 .ident = "Alienware x15 R1",
>>> +	 .matches = {
>>> +		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
>>> +		     DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R1")
>>> +		    },
>>> +	 .driver_data = &quirk_x15_r1,
>>> +	},
>>>    	 {
>>>    	 .callback = dmi_matched,
>>>    	 .ident = "Dell Inc. Inspiron 5675",
>>> @@ -218,6 +266,7 @@ static struct platform_device *platform_device;
>>>    static struct device_attribute *zone_dev_attrs;
>>>    static struct attribute **zone_attrs;
>>>    static struct platform_zone *zone_data;
>>> +static struct platform_profile_handler pp_handler;
>>>
>>>    static struct platform_driver platform_driver = {
>>>    	.driver = {
>>> @@ -500,7 +549,7 @@ static void alienware_zone_exit(struct platform_device *dev)
>>>    	kfree(zone_attrs);
>>>    }
>>>
>>> -static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
>>> +static acpi_status alienware_wmax_command(void *in_args, size_t insize,
>>>    					  u32 command, int *out_data)
>>>    {
>> Can you split this change into a separate patch? This would make review a bit easier.
>>
> Yes, sure. In that case should I mention this patch depends on that one
> in v4?

Yes, but only in the description of the resulting patch series.

Thanks,
Armin Wolf

>>>    	acpi_status status;
>>> @@ -508,7 +557,7 @@ static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
>>>    	struct acpi_buffer input;
>>>    	struct acpi_buffer output;
>>>
>>> -	input.length = (acpi_size) sizeof(*in_args);
>>> +	input.length = (acpi_size) insize;
>> Please drop the cast to acpi_size.
>>
> Ok.
>
>>>    	input.pointer = in_args;
>>>    	if (out_data) {
>>>    		output.length = ACPI_ALLOCATE_BUFFER;
>>> @@ -541,8 +590,8 @@ static ssize_t show_hdmi_cable(struct device *dev,
>>>    		.arg = 0,
>>>    	};
>>>    	status =
>>> -	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE,
>>> -				   (u32 *) &out_data);
>>> +	    alienware_wmax_command(&in_args, sizeof(in_args),
>>> +				   WMAX_METHOD_HDMI_CABLE, (u32 *) &out_data);
>>>    	if (ACPI_SUCCESS(status)) {
>>>    		if (out_data == 0)
>>>    			return sysfs_emit(buf, "[unconnected] connected unknown\n");
>>> @@ -562,8 +611,8 @@ static ssize_t show_hdmi_source(struct device *dev,
>>>    		.arg = 0,
>>>    	};
>>>    	status =
>>> -	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS,
>>> -				   (u32 *) &out_data);
>>> +	    alienware_wmax_command(&in_args, sizeof(in_args),
>>> +				   WMAX_METHOD_HDMI_STATUS, (u32 *) &out_data);
>>>
>>>    	if (ACPI_SUCCESS(status)) {
>>>    		if (out_data == 1)
>>> @@ -589,7 +638,8 @@ static ssize_t toggle_hdmi_source(struct device *dev,
>>>    		args.arg = 3;
>>>    	pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf);
>>>
>>> -	status = alienware_wmax_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL);
>>> +	status = alienware_wmax_command(&args, sizeof(args),
>>> +					WMAX_METHOD_HDMI_SOURCE, NULL);
>>>
>>>    	if (ACPI_FAILURE(status))
>>>    		pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
>>> @@ -642,8 +692,8 @@ static ssize_t show_amplifier_status(struct device *dev,
>>>    		.arg = 0,
>>>    	};
>>>    	status =
>>> -	    alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE,
>>> -				   (u32 *) &out_data);
>>> +	    alienware_wmax_command(&in_args, sizeof(in_args),
>>> +				   WMAX_METHOD_AMPLIFIER_CABLE, (u32 *) &out_data);
>>>    	if (ACPI_SUCCESS(status)) {
>>>    		if (out_data == 0)
>>>    			return sysfs_emit(buf, "[unconnected] connected unknown\n");
>>> @@ -694,8 +744,8 @@ static ssize_t show_deepsleep_status(struct device *dev,
>>>    	struct wmax_basic_args in_args = {
>>>    		.arg = 0,
>>>    	};
>>> -	status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS,
>>> -					(u32 *) &out_data);
>>> +	status = alienware_wmax_command(&in_args, sizeof(in_args),
>>> +					WMAX_METHOD_DEEP_SLEEP_STATUS, (u32 *) &out_data);
>>>    	if (ACPI_SUCCESS(status)) {
>>>    		if (out_data == 0)
>>>    			return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
>>> @@ -723,8 +773,8 @@ static ssize_t toggle_deepsleep(struct device *dev,
>>>    		args.arg = 2;
>>>    	pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf);
>>>
>>> -	status = alienware_wmax_command(&args, WMAX_METHOD_DEEP_SLEEP_CONTROL,
>>> -					NULL);
>>> +	status = alienware_wmax_command(&args, sizeof(args),
>>> +					WMAX_METHOD_DEEP_SLEEP_CONTROL, NULL);
>>>
>>>    	if (ACPI_FAILURE(status))
>>>    		pr_err("alienware-wmi: deep sleep control failed: results: %u\n",
>>> @@ -760,6 +810,160 @@ static int create_deepsleep(struct platform_device *dev)
>>>    	return ret;
>>>    }
>>>
>>> +/*
>>> + * Thermal Profile control
>>> + *  - Provides thermal profile control through the Platform Profile API
>>> + */
>>> +#define PROFILE_MASK		GENMASK(15,8)
>>> +#define PROFILE_ACTIVATE	BIT(0)
>>> +
>>> +static u32 profile_to_wmax_arg(enum WMAX_THERMAL_PROFILE prof)
>>> +{
>>> +	return FIELD_PREP(PROFILE_MASK, prof) | PROFILE_ACTIVATE;
>>> +}
>>> +
>>> +static int thermal_profile_get(struct platform_profile_handler *pprof,
>>> +				enum platform_profile_option *profile)
>>> +{
>>> +	acpi_status status;
>>> +	u32 in_args = WMAX_ARG_GET_CURRENT_PROF;
>>> +	u32 out_data;
>>> +
>>> +	status = alienware_wmax_command(&in_args, sizeof(in_args),
>>> +					WMAX_METHOD_THERMAL_INFORMATION, &out_data);
>>> +
>>> +	if (ACPI_FAILURE(status))
>>> +		return -EOPNOTSUPP;
>> Please return -EIO.
>>
> Ok.
>
>>> +
>>> +	if (out_data == 0xFFFFFFFF)
>>> +		return -EBADRQC;
>>> +
>>> +	switch (out_data) {
>>> +	case WMAX_THERMAL_LOW_POWER:
>>> +		*profile = PLATFORM_PROFILE_LOW_POWER;
>>> +		break;
>>> +	case WMAX_THERMAL_QUIET:
>>> +		*profile = PLATFORM_PROFILE_QUIET;
>>> +		break;
>>> +	case WMAX_THERMAL_BALANCED:
>>> +		*profile = PLATFORM_PROFILE_BALANCED;
>>> +		break;
>>> +	case WMAX_THERMAL_BALANCED_PERFORMANCE:
>>> +		*profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE;
>>> +		break;
>>> +	case WMAX_THERMAL_PERFORMANCE:
>>> +	case WMAX_THERMAL_GMODE:
>>> +		*profile = PLATFORM_PROFILE_PERFORMANCE;
>>> +		break;
>>> +	default:
>>> +		return -ENODATA;
>>> +	}
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static int thermal_profile_set(struct platform_profile_handler *pprof,
>>> +				enum platform_profile_option profile)
>>> +{
>>> +	acpi_status status;
>>> +	u32 in_args;
>>> +	u32 out_data;
>>> +
>>> +	switch (profile) {
>>> +	case PLATFORM_PROFILE_LOW_POWER:
>>> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_LOW_POWER);
>>> +		break;
>>> +	case PLATFORM_PROFILE_QUIET:
>>> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET);
>>> +		break;
>>> +	case PLATFORM_PROFILE_BALANCED:
>>> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED);
>>> +		break;
>>> +	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
>>> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE);
>>> +		break;
>>> +	case PLATFORM_PROFILE_PERFORMANCE:
>>> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_PERFORMANCE);
>>> +		break;
>>> +	default:
>>> +		return -EOPNOTSUPP;
>>> +	}
>>> +
>>> +	status = alienware_wmax_command(&in_args, sizeof(in_args),
>>> +					WMAX_METHOD_THERMAL_CONTROL, &out_data);
>>> +
>>> +	if (ACPI_FAILURE(status))
>>> +		return -EOPNOTSUPP;
>> Return -EIO.
>>
>>> +
>>> +	if (out_data == 0xFFFFFFFF)
>>> +		return -EBADRQC;
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static int gmode_thermal_profile_set(struct platform_profile_handler *pprof,
>>> +				     enum platform_profile_option profile)
>>> +{
>>> +	acpi_status status;
>>> +	u32 in_args;
>>> +	u32 out_data;
>>> +
>>> +	switch (profile) {
>>> +	case PLATFORM_PROFILE_LOW_POWER:
>>> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_LOW_POWER);
>>> +		break;
>>> +	case PLATFORM_PROFILE_QUIET:
>>> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET);
>>> +		break;
>>> +	case PLATFORM_PROFILE_BALANCED:
>>> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED);
>>> +		break;
>>> +	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
>>> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE);
>>> +		break;
>>> +	case PLATFORM_PROFILE_PERFORMANCE:
>>> +		in_args = profile_to_wmax_arg(WMAX_THERMAL_GMODE);
>>> +		break;
>>> +	default:
>>> +		return -EOPNOTSUPP;
>>> +	}
>>> +
>>> +	status = alienware_wmax_command(&in_args, sizeof(in_args),
>>> +					WMAX_METHOD_THERMAL_CONTROL, &out_data);
>>> +
>>> +	if (ACPI_FAILURE(status))
>>> +		return -EOPNOTSUPP;
>> Return -EIO.
>>
>> Otherwise the patch looks quite good.
>>
> Thank you! It's my first time working on the kernel.
>
>> Thanks,
>> Armin Wolf
> Your feedback is appreciated.
>
> Kurt
>
>>> +
>>> +	if (out_data == 0xFFFFFFFF)
>>> +		return -EBADRQC;
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static int create_thermal_profile(void)
>>> +{
>>> +	pp_handler.profile_get = thermal_profile_get;
>>> +
>>> +	if (quirks->gmode > 0)
>>> +		pp_handler.profile_set = gmode_thermal_profile_set;
>>> +	else
>>> +		pp_handler.profile_set = thermal_profile_set;
>>> +
>>> +	set_bit(PLATFORM_PROFILE_LOW_POWER, pp_handler.choices);
>>> +	set_bit(PLATFORM_PROFILE_QUIET, pp_handler.choices);
>>> +	set_bit(PLATFORM_PROFILE_BALANCED, pp_handler.choices);
>>> +	set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, pp_handler.choices);
>>> +	set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices);
>>> +
>>> +	return platform_profile_register(&pp_handler);
>>> +}
>>> +
>>> +static void remove_thermal_profile(void)
>>> +{
>>> +	if (quirks->thermal > 0)
>>> +		platform_profile_remove();
>>> +}
>>> +
>>>    static int __init alienware_wmi_init(void)
>>>    {
>>>    	int ret;
>>> @@ -807,6 +1011,12 @@ static int __init alienware_wmi_init(void)
>>>    			goto fail_prep_deepsleep;
>>>    	}
>>>
>>> +	if (quirks->thermal > 0) {
>>> +		ret = create_thermal_profile();
>>> +		if (ret)
>>> +			goto fail_prep_thermal_profile;
>>> +	}
>>> +
>>>    	ret = alienware_zone_init(platform_device);
>>>    	if (ret)
>>>    		goto fail_prep_zones;
>>> @@ -817,6 +1027,7 @@ static int __init alienware_wmi_init(void)
>>>    	alienware_zone_exit(platform_device);
>>>    fail_prep_deepsleep:
>>>    fail_prep_amplifier:
>>> +fail_prep_thermal_profile:
>>>    fail_prep_hdmi:
>>>    	platform_device_del(platform_device);
>>>    fail_platform_device2:
>>> @@ -834,6 +1045,7 @@ static void __exit alienware_wmi_exit(void)
>>>    	if (platform_device) {
>>>    		alienware_zone_exit(platform_device);
>>>    		remove_hdmi(platform_device);
>>> +		remove_thermal_profile();
>>>    		platform_device_unregister(platform_device);
>>>    		platform_driver_unregister(&platform_driver);
>>>    	}
[PATCH v2] alienware-wmi: Dell AWCC platform_profile support
Posted by Kurt Borja 1 month, 2 weeks ago
This patch adds platform_profile support for Dell devices which implement
User Selectable Thermal Tables (USTT) that are meant to be controlled by
Alienware Command Center (AWCC). These devices may include newer Alienware
M-Series, Alienware X-Series and Dell's G-Series. This patch, was tested
by me on an Alienware x15 R1.

It is suspected that Alienware Command Center manages thermal profiles
through the WMI interface, specifically through a device with identifier
\_SB_.AMW1.WMAX. This device was reverse engineered and the relevant
functionality is documented here [1]. This driver interacts with this
WMI device and thus is able to mimic AWCC's thermal profiles functionality
through the platform_profile API. In consequence the user would be able
to set and retrieve thermal profiles, which are just fan speed profiles.

This driver was heavily inspired on inspur_platform_profile, special
thanks.

Notes:
 - Performance (FullSpeed) profile is a special profile which has it's own
   entry in the Firmware Settings of the Alienware x15 R1. It also changes
   the color of the F1 key. I suspect this behavior would be replicated in
   other X-Series or M-Series laptops.
 - G-Mode is a profile documented on [1] which mimics the behavior of
   FullSpeed mode but it does not have an entry on the Firmware Settings of
   the Alienware x15 R1, this may correspond to the G-Mode functionality on
   G-Series laptops (activated by a special button) but I cannot test it. I
   did not include this code in the driver as G-Mode causes unexpected
   behavior on X-Series laptops.

---
v2:
 - Moved functionality to alienware-wmi driver
 - Added thermal and gmode quirks to add support based on dmi match
 - Performance profile is now GMODE for devices that support it
 - alienware_wmax_command now is insize agnostic to support new thermal
   methods
---
 drivers/platform/x86/dell/Kconfig         |   1 +
 drivers/platform/x86/dell/alienware-wmi.c | 214 ++++++++++++++++++++--
 2 files changed, 202 insertions(+), 13 deletions(-)

diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig
index 68a49788a..b06d634cd 100644
--- a/drivers/platform/x86/dell/Kconfig
+++ b/drivers/platform/x86/dell/Kconfig
@@ -21,6 +21,7 @@ config ALIENWARE_WMI
 	depends on LEDS_CLASS
 	depends on NEW_LEDS
 	depends on ACPI_WMI
+	select ACPI_PLATFORM_PROFILE
 	help
 	 This is a driver for controlling Alienware BIOS driven
 	 features.  It exposes an interface for controlling the AlienFX
diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
index f5ee62ce1..47abb533c 100644
--- a/drivers/platform/x86/dell/alienware-wmi.c
+++ b/drivers/platform/x86/dell/alienware-wmi.c
@@ -10,6 +10,7 @@
 #include <linux/acpi.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
+#include <linux/platform_profile.h>
 #include <linux/dmi.h>
 #include <linux/leds.h>
 
@@ -25,6 +26,15 @@
 #define WMAX_METHOD_AMPLIFIER_CABLE	0x6
 #define WMAX_METHOD_DEEP_SLEEP_CONTROL	0x0B
 #define WMAX_METHOD_DEEP_SLEEP_STATUS	0x0C
+#define WMAX_METHOD_THERMAL_INFORMATION	0x14
+#define WMAX_METHOD_THERMAL_CONTROL	0x15
+
+#define WMAX_THERMAL_QUIET			0xA3
+#define WMAX_THERMAL_BALANCED			0xA0
+#define WMAX_THERMAL_BALANCED_PERFORMANCE	0xA1
+#define WMAX_THERMAL_PERFORMANCE		0xA4
+#define WMAX_THERMAL_GMODE			0xAB
+#define WMAX_THERMAL_LOW_POWER			0xA5
 
 MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
 MODULE_DESCRIPTION("Alienware special feature control");
@@ -54,6 +64,8 @@ struct quirk_entry {
 	u8 hdmi_mux;
 	u8 amplifier;
 	u8 deepslp;
+	u8 thermal;
+	u8 gmode;
 };
 
 static struct quirk_entry *quirks;
@@ -64,6 +76,8 @@ static struct quirk_entry quirk_inspiron5675 = {
 	.hdmi_mux = 0,
 	.amplifier = 0,
 	.deepslp = 0,
+	.thermal = 0,
+	.gmode = 0,
 };
 
 static struct quirk_entry quirk_unknown = {
@@ -71,6 +85,8 @@ static struct quirk_entry quirk_unknown = {
 	.hdmi_mux = 0,
 	.amplifier = 0,
 	.deepslp = 0,
+	.thermal = 0,
+	.gmode = 0,
 };
 
 static struct quirk_entry quirk_x51_r1_r2 = {
@@ -78,6 +94,8 @@ static struct quirk_entry quirk_x51_r1_r2 = {
 	.hdmi_mux = 0,
 	.amplifier = 0,
 	.deepslp = 0,
+	.thermal = 0,
+	.gmode = 0,
 };
 
 static struct quirk_entry quirk_x51_r3 = {
@@ -85,6 +103,8 @@ static struct quirk_entry quirk_x51_r3 = {
 	.hdmi_mux = 0,
 	.amplifier = 1,
 	.deepslp = 0,
+	.thermal = 0,
+	.gmode = 0,
 };
 
 static struct quirk_entry quirk_asm100 = {
@@ -92,6 +112,8 @@ static struct quirk_entry quirk_asm100 = {
 	.hdmi_mux = 1,
 	.amplifier = 0,
 	.deepslp = 0,
+	.thermal = 0,
+	.gmode = 0,
 };
 
 static struct quirk_entry quirk_asm200 = {
@@ -99,6 +121,8 @@ static struct quirk_entry quirk_asm200 = {
 	.hdmi_mux = 1,
 	.amplifier = 0,
 	.deepslp = 1,
+	.thermal = 0,
+	.gmode = 0,
 };
 
 static struct quirk_entry quirk_asm201 = {
@@ -106,6 +130,17 @@ static struct quirk_entry quirk_asm201 = {
 	.hdmi_mux = 1,
 	.amplifier = 1,
 	.deepslp = 1,
+	.thermal = 0,
+	.gmode = 0,
+};
+
+static struct quirk_entry quirk_x15_r1 = {
+	.num_zones = 2,
+	.hdmi_mux = 0,
+	.amplifier = 0,
+	.deepslp = 0,
+	.thermal = 1,
+	.gmode = 0,
 };
 
 static int __init dmi_matched(const struct dmi_system_id *dmi)
@@ -169,6 +204,15 @@ static const struct dmi_system_id alienware_quirks[] __initconst = {
 		     },
 	 .driver_data = &quirk_asm201,
 	 },
+	 {
+	 .callback = dmi_matched,
+	 .ident = "Alienware x15 R1",
+	 .matches = {
+		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+		     DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R1")
+		    },
+	 .driver_data = &quirk_x15_r1,
+	},
 	 {
 	 .callback = dmi_matched,
 	 .ident = "Dell Inc. Inspiron 5675",
@@ -218,6 +262,7 @@ static struct platform_device *platform_device;
 static struct device_attribute *zone_dev_attrs;
 static struct attribute **zone_attrs;
 static struct platform_zone *zone_data;
+static struct platform_profile_handler pp_handler;
 
 static struct platform_driver platform_driver = {
 	.driver = {
@@ -500,7 +545,7 @@ static void alienware_zone_exit(struct platform_device *dev)
 	kfree(zone_attrs);
 }
 
-static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
+static acpi_status alienware_wmax_command(void *in_args, size_t insize,
 					  u32 command, int *out_data)
 {
 	acpi_status status;
@@ -508,7 +553,7 @@ static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
 	struct acpi_buffer input;
 	struct acpi_buffer output;
 
-	input.length = (acpi_size) sizeof(*in_args);
+	input.length = (acpi_size) insize;
 	input.pointer = in_args;
 	if (out_data) {
 		output.length = ACPI_ALLOCATE_BUFFER;
@@ -541,8 +586,8 @@ static ssize_t show_hdmi_cable(struct device *dev,
 		.arg = 0,
 	};
 	status =
-	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE,
-				   (u32 *) &out_data);
+	    alienware_wmax_command(&in_args, sizeof(in_args),
+				   WMAX_METHOD_HDMI_CABLE, (u32 *) &out_data);
 	if (ACPI_SUCCESS(status)) {
 		if (out_data == 0)
 			return sysfs_emit(buf, "[unconnected] connected unknown\n");
@@ -562,8 +607,8 @@ static ssize_t show_hdmi_source(struct device *dev,
 		.arg = 0,
 	};
 	status =
-	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS,
-				   (u32 *) &out_data);
+	    alienware_wmax_command(&in_args, sizeof(in_args),
+				   WMAX_METHOD_HDMI_STATUS, (u32 *) &out_data);
 
 	if (ACPI_SUCCESS(status)) {
 		if (out_data == 1)
@@ -589,7 +634,8 @@ static ssize_t toggle_hdmi_source(struct device *dev,
 		args.arg = 3;
 	pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf);
 
-	status = alienware_wmax_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL);
+	status = alienware_wmax_command(&args, sizeof(args),
+					WMAX_METHOD_HDMI_SOURCE, NULL);
 
 	if (ACPI_FAILURE(status))
 		pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
@@ -642,8 +688,8 @@ static ssize_t show_amplifier_status(struct device *dev,
 		.arg = 0,
 	};
 	status =
-	    alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE,
-				   (u32 *) &out_data);
+	    alienware_wmax_command(&in_args, sizeof(in_args),
+				   WMAX_METHOD_AMPLIFIER_CABLE, (u32 *) &out_data);
 	if (ACPI_SUCCESS(status)) {
 		if (out_data == 0)
 			return sysfs_emit(buf, "[unconnected] connected unknown\n");
@@ -694,8 +740,8 @@ static ssize_t show_deepsleep_status(struct device *dev,
 	struct wmax_basic_args in_args = {
 		.arg = 0,
 	};
-	status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS,
-					(u32 *) &out_data);
+	status = alienware_wmax_command(&in_args, sizeof(in_args),
+					WMAX_METHOD_DEEP_SLEEP_STATUS, (u32 *) &out_data);
 	if (ACPI_SUCCESS(status)) {
 		if (out_data == 0)
 			return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
@@ -723,8 +769,8 @@ static ssize_t toggle_deepsleep(struct device *dev,
 		args.arg = 2;
 	pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf);
 
-	status = alienware_wmax_command(&args, WMAX_METHOD_DEEP_SLEEP_CONTROL,
-					NULL);
+	status = alienware_wmax_command(&args, sizeof(args),
+					WMAX_METHOD_DEEP_SLEEP_CONTROL, NULL);
 
 	if (ACPI_FAILURE(status))
 		pr_err("alienware-wmi: deep sleep control failed: results: %u\n",
@@ -760,6 +806,140 @@ static int create_deepsleep(struct platform_device *dev)
 	return ret;
 }
 
+/*
+ * Thermal Profile control
+ *  - Provides thermal profile control through the Platform Profile API
+ */
+
+static int platform_profile_get(struct platform_profile_handler *pprof,
+				enum platform_profile_option *profile)
+{
+	acpi_status status;
+	u32 in_args = 0x0B;
+	u32 out_data;
+
+	status = alienware_wmax_command(&in_args, sizeof(in_args),
+					WMAX_METHOD_THERMAL_INFORMATION, (u32 *) &out_data);
+
+	if (ACPI_FAILURE(status) || out_data < 0)
+		return -EOPNOTSUPP;
+
+	switch (out_data) {
+	case WMAX_THERMAL_LOW_POWER:
+		*profile = PLATFORM_PROFILE_LOW_POWER;
+		break;
+	case WMAX_THERMAL_QUIET:
+		*profile = PLATFORM_PROFILE_QUIET;
+		break;
+	case WMAX_THERMAL_BALANCED:
+		*profile = PLATFORM_PROFILE_BALANCED;
+		break;
+	case WMAX_THERMAL_BALANCED_PERFORMANCE:
+		*profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE;
+		break;
+	case WMAX_THERMAL_PERFORMANCE:
+	case WMAX_THERMAL_GMODE:
+		*profile = PLATFORM_PROFILE_PERFORMANCE;
+		break;
+	default:
+		return -ENODATA;
+	}
+
+	return 0;
+}
+
+#define SET_MASK(prof) ((prof << 8) | 1)
+
+static int platform_profile_set(struct platform_profile_handler *pprof,
+				enum platform_profile_option profile)
+{
+	acpi_status status;
+	u32 in_args;
+	u32 out_data;
+
+	switch (profile) {
+	case PLATFORM_PROFILE_LOW_POWER:
+		in_args = SET_MASK(WMAX_THERMAL_LOW_POWER);
+		break;
+	case PLATFORM_PROFILE_QUIET:
+		in_args = SET_MASK(WMAX_THERMAL_QUIET);
+		break;
+	case PLATFORM_PROFILE_BALANCED:
+		in_args = SET_MASK(WMAX_THERMAL_BALANCED);
+		break;
+	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
+		in_args = SET_MASK(WMAX_THERMAL_BALANCED_PERFORMANCE);
+		break;
+	case PLATFORM_PROFILE_PERFORMANCE:
+		in_args = SET_MASK(WMAX_THERMAL_PERFORMANCE);
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	status = alienware_wmax_command(&in_args, sizeof(in_args),
+					WMAX_METHOD_THERMAL_CONTROL, (u32 *) &out_data);
+
+	if (ACPI_FAILURE(status) || out_data < 0)
+		return -EOPNOTSUPP;
+
+	return 0;
+}
+
+static int gmode_platform_profile_set(struct platform_profile_handler *pprof,
+				      enum platform_profile_option profile)
+{
+	acpi_status status;
+	u32 in_args;
+	u32 out_data;
+
+	switch (profile) {
+	case PLATFORM_PROFILE_LOW_POWER:
+		in_args = SET_MASK(WMAX_THERMAL_LOW_POWER);
+		break;
+	case PLATFORM_PROFILE_QUIET:
+		in_args = SET_MASK(WMAX_THERMAL_QUIET);
+		break;
+	case PLATFORM_PROFILE_BALANCED:
+		in_args = SET_MASK(WMAX_THERMAL_BALANCED);
+		break;
+	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
+		in_args = SET_MASK(WMAX_THERMAL_BALANCED_PERFORMANCE);
+		break;
+	case PLATFORM_PROFILE_PERFORMANCE:
+		in_args = SET_MASK(WMAX_THERMAL_GMODE);
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	status = alienware_wmax_command(&in_args, sizeof(in_args),
+					WMAX_METHOD_THERMAL_CONTROL, (u32 *) &out_data);
+
+	if (ACPI_FAILURE(status) || out_data < 0)
+		return -EOPNOTSUPP;
+
+	return 0;
+}
+
+static int create_platform_profile(void)
+{
+	pp_handler.profile_get = platform_profile_get;
+
+	if (quirks->gmode > 0)
+		pp_handler.profile_set = gmode_platform_profile_set;
+	else
+		pp_handler.profile_set = platform_profile_set;
+
+	set_bit(PLATFORM_PROFILE_LOW_POWER, pp_handler.choices);
+	set_bit(PLATFORM_PROFILE_QUIET, pp_handler.choices);
+	set_bit(PLATFORM_PROFILE_BALANCED, pp_handler.choices);
+	set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, pp_handler.choices);
+	set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices);
+
+	return platform_profile_register(&pp_handler);
+}
+
 static int __init alienware_wmi_init(void)
 {
 	int ret;
@@ -807,6 +987,12 @@ static int __init alienware_wmi_init(void)
 			goto fail_prep_deepsleep;
 	}
 
+	if (quirks->thermal > 0) {
+		ret = create_platform_profile();
+		if (ret)
+			goto fail_prep_thermal_profile;
+	}
+
 	ret = alienware_zone_init(platform_device);
 	if (ret)
 		goto fail_prep_zones;
@@ -817,6 +1003,7 @@ static int __init alienware_wmi_init(void)
 	alienware_zone_exit(platform_device);
 fail_prep_deepsleep:
 fail_prep_amplifier:
+fail_prep_thermal_profile:
 fail_prep_hdmi:
 	platform_device_del(platform_device);
 fail_platform_device2:
@@ -836,6 +1023,7 @@ static void __exit alienware_wmi_exit(void)
 		remove_hdmi(platform_device);
 		platform_device_unregister(platform_device);
 		platform_driver_unregister(&platform_driver);
+		platform_profile_remove();
 	}
 }
 
-- 
2.46.2
Re: [PATCH v2] alienware-wmi: Dell AWCC platform_profile support
Posted by Ilpo Järvinen 1 month, 2 weeks ago
On Tue, 8 Oct 2024, Kurt Borja wrote:

> This patch adds platform_profile support for Dell devices which implement
> User Selectable Thermal Tables (USTT) that are meant to be controlled by
> Alienware Command Center (AWCC). These devices may include newer Alienware
> M-Series, Alienware X-Series and Dell's G-Series. This patch, was tested
> by me on an Alienware x15 R1.
> 
> It is suspected that Alienware Command Center manages thermal profiles
> through the WMI interface, specifically through a device with identifier
> \_SB_.AMW1.WMAX. This device was reverse engineered and the relevant
> functionality is documented here [1]. This driver interacts with this
> WMI device and thus is able to mimic AWCC's thermal profiles functionality
> through the platform_profile API. In consequence the user would be able
> to set and retrieve thermal profiles, which are just fan speed profiles.
> 
> This driver was heavily inspired on inspur_platform_profile, special
> thanks.
> 
> Notes:
>  - Performance (FullSpeed) profile is a special profile which has it's own
>    entry in the Firmware Settings of the Alienware x15 R1. It also changes
>    the color of the F1 key. I suspect this behavior would be replicated in
>    other X-Series or M-Series laptops.
>  - G-Mode is a profile documented on [1] which mimics the behavior of
>    FullSpeed mode but it does not have an entry on the Firmware Settings of
>    the Alienware x15 R1, this may correspond to the G-Mode functionality on
>    G-Series laptops (activated by a special button) but I cannot test it. I
>    did not include this code in the driver as G-Mode causes unexpected
>    behavior on X-Series laptops.
> 
> ---
> v2:
>  - Moved functionality to alienware-wmi driver
>  - Added thermal and gmode quirks to add support based on dmi match
>  - Performance profile is now GMODE for devices that support it
>  - alienware_wmax_command now is insize agnostic to support new thermal
>    methods
> ---
>  drivers/platform/x86/dell/Kconfig         |   1 +
>  drivers/platform/x86/dell/alienware-wmi.c | 214 ++++++++++++++++++++--
>  2 files changed, 202 insertions(+), 13 deletions(-)
> 
> diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig
> index 68a49788a..b06d634cd 100644
> --- a/drivers/platform/x86/dell/Kconfig
> +++ b/drivers/platform/x86/dell/Kconfig
> @@ -21,6 +21,7 @@ config ALIENWARE_WMI
>  	depends on LEDS_CLASS
>  	depends on NEW_LEDS
>  	depends on ACPI_WMI
> +	select ACPI_PLATFORM_PROFILE
>  	help
>  	 This is a driver for controlling Alienware BIOS driven
>  	 features.  It exposes an interface for controlling the AlienFX
> diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
> index f5ee62ce1..47abb533c 100644
> --- a/drivers/platform/x86/dell/alienware-wmi.c
> +++ b/drivers/platform/x86/dell/alienware-wmi.c
> @@ -10,6 +10,7 @@
>  #include <linux/acpi.h>
>  #include <linux/module.h>
>  #include <linux/platform_device.h>
> +#include <linux/platform_profile.h>
>  #include <linux/dmi.h>
>  #include <linux/leds.h>
>  
> @@ -25,6 +26,15 @@
>  #define WMAX_METHOD_AMPLIFIER_CABLE	0x6
>  #define WMAX_METHOD_DEEP_SLEEP_CONTROL	0x0B
>  #define WMAX_METHOD_DEEP_SLEEP_STATUS	0x0C
> +#define WMAX_METHOD_THERMAL_INFORMATION	0x14
> +#define WMAX_METHOD_THERMAL_CONTROL	0x15
> +
> +#define WMAX_THERMAL_QUIET			0xA3
> +#define WMAX_THERMAL_BALANCED			0xA0
> +#define WMAX_THERMAL_BALANCED_PERFORMANCE	0xA1
> +#define WMAX_THERMAL_PERFORMANCE		0xA4
> +#define WMAX_THERMAL_GMODE			0xAB
> +#define WMAX_THERMAL_LOW_POWER			0xA5
>  
>  MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
>  MODULE_DESCRIPTION("Alienware special feature control");
> @@ -54,6 +64,8 @@ struct quirk_entry {
>  	u8 hdmi_mux;
>  	u8 amplifier;
>  	u8 deepslp;
> +	u8 thermal;
> +	u8 gmode;
>  };
>  
>  static struct quirk_entry *quirks;
> @@ -64,6 +76,8 @@ static struct quirk_entry quirk_inspiron5675 = {
>  	.hdmi_mux = 0,
>  	.amplifier = 0,
>  	.deepslp = 0,
> +	.thermal = 0,
> +	.gmode = 0,
>  };
>  
>  static struct quirk_entry quirk_unknown = {
> @@ -71,6 +85,8 @@ static struct quirk_entry quirk_unknown = {
>  	.hdmi_mux = 0,
>  	.amplifier = 0,
>  	.deepslp = 0,
> +	.thermal = 0,
> +	.gmode = 0,
>  };
>  
>  static struct quirk_entry quirk_x51_r1_r2 = {
> @@ -78,6 +94,8 @@ static struct quirk_entry quirk_x51_r1_r2 = {
>  	.hdmi_mux = 0,
>  	.amplifier = 0,
>  	.deepslp = 0,
> +	.thermal = 0,
> +	.gmode = 0,
>  };
>  
>  static struct quirk_entry quirk_x51_r3 = {
> @@ -85,6 +103,8 @@ static struct quirk_entry quirk_x51_r3 = {
>  	.hdmi_mux = 0,
>  	.amplifier = 1,
>  	.deepslp = 0,
> +	.thermal = 0,
> +	.gmode = 0,
>  };
>  
>  static struct quirk_entry quirk_asm100 = {
> @@ -92,6 +112,8 @@ static struct quirk_entry quirk_asm100 = {
>  	.hdmi_mux = 1,
>  	.amplifier = 0,
>  	.deepslp = 0,
> +	.thermal = 0,
> +	.gmode = 0,
>  };
>  
>  static struct quirk_entry quirk_asm200 = {
> @@ -99,6 +121,8 @@ static struct quirk_entry quirk_asm200 = {
>  	.hdmi_mux = 1,
>  	.amplifier = 0,
>  	.deepslp = 1,
> +	.thermal = 0,
> +	.gmode = 0,
>  };
>  
>  static struct quirk_entry quirk_asm201 = {
> @@ -106,6 +130,17 @@ static struct quirk_entry quirk_asm201 = {
>  	.hdmi_mux = 1,
>  	.amplifier = 1,
>  	.deepslp = 1,
> +	.thermal = 0,
> +	.gmode = 0,
> +};
> +
> +static struct quirk_entry quirk_x15_r1 = {
> +	.num_zones = 2,
> +	.hdmi_mux = 0,
> +	.amplifier = 0,
> +	.deepslp = 0,
> +	.thermal = 1,
> +	.gmode = 0,
>  };
>  
>  static int __init dmi_matched(const struct dmi_system_id *dmi)
> @@ -169,6 +204,15 @@ static const struct dmi_system_id alienware_quirks[] __initconst = {
>  		     },
>  	 .driver_data = &quirk_asm201,
>  	 },
> +	 {
> +	 .callback = dmi_matched,
> +	 .ident = "Alienware x15 R1",
> +	 .matches = {
> +		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
> +		     DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R1")
> +		    },
> +	 .driver_data = &quirk_x15_r1,
> +	},
>  	 {
>  	 .callback = dmi_matched,
>  	 .ident = "Dell Inc. Inspiron 5675",
> @@ -218,6 +262,7 @@ static struct platform_device *platform_device;
>  static struct device_attribute *zone_dev_attrs;
>  static struct attribute **zone_attrs;
>  static struct platform_zone *zone_data;
> +static struct platform_profile_handler pp_handler;
>  
>  static struct platform_driver platform_driver = {
>  	.driver = {
> @@ -500,7 +545,7 @@ static void alienware_zone_exit(struct platform_device *dev)
>  	kfree(zone_attrs);
>  }
>  
> -static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
> +static acpi_status alienware_wmax_command(void *in_args, size_t insize,
>  					  u32 command, int *out_data)
>  {
>  	acpi_status status;
> @@ -508,7 +553,7 @@ static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
>  	struct acpi_buffer input;
>  	struct acpi_buffer output;
>  
> -	input.length = (acpi_size) sizeof(*in_args);
> +	input.length = (acpi_size) insize;
>  	input.pointer = in_args;
>  	if (out_data) {
>  		output.length = ACPI_ALLOCATE_BUFFER;
> @@ -541,8 +586,8 @@ static ssize_t show_hdmi_cable(struct device *dev,
>  		.arg = 0,
>  	};
>  	status =
> -	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE,
> -				   (u32 *) &out_data);
> +	    alienware_wmax_command(&in_args, sizeof(in_args),
> +				   WMAX_METHOD_HDMI_CABLE, (u32 *) &out_data);
>  	if (ACPI_SUCCESS(status)) {
>  		if (out_data == 0)
>  			return sysfs_emit(buf, "[unconnected] connected unknown\n");
> @@ -562,8 +607,8 @@ static ssize_t show_hdmi_source(struct device *dev,
>  		.arg = 0,
>  	};
>  	status =
> -	    alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS,
> -				   (u32 *) &out_data);
> +	    alienware_wmax_command(&in_args, sizeof(in_args),
> +				   WMAX_METHOD_HDMI_STATUS, (u32 *) &out_data);
>  
>  	if (ACPI_SUCCESS(status)) {
>  		if (out_data == 1)
> @@ -589,7 +634,8 @@ static ssize_t toggle_hdmi_source(struct device *dev,
>  		args.arg = 3;
>  	pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf);
>  
> -	status = alienware_wmax_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL);
> +	status = alienware_wmax_command(&args, sizeof(args),
> +					WMAX_METHOD_HDMI_SOURCE, NULL);
>  
>  	if (ACPI_FAILURE(status))
>  		pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
> @@ -642,8 +688,8 @@ static ssize_t show_amplifier_status(struct device *dev,
>  		.arg = 0,
>  	};
>  	status =
> -	    alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE,
> -				   (u32 *) &out_data);
> +	    alienware_wmax_command(&in_args, sizeof(in_args),
> +				   WMAX_METHOD_AMPLIFIER_CABLE, (u32 *) &out_data);
>  	if (ACPI_SUCCESS(status)) {
>  		if (out_data == 0)
>  			return sysfs_emit(buf, "[unconnected] connected unknown\n");
> @@ -694,8 +740,8 @@ static ssize_t show_deepsleep_status(struct device *dev,
>  	struct wmax_basic_args in_args = {
>  		.arg = 0,
>  	};
> -	status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS,
> -					(u32 *) &out_data);
> +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> +					WMAX_METHOD_DEEP_SLEEP_STATUS, (u32 *) &out_data);
>  	if (ACPI_SUCCESS(status)) {
>  		if (out_data == 0)
>  			return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
> @@ -723,8 +769,8 @@ static ssize_t toggle_deepsleep(struct device *dev,
>  		args.arg = 2;
>  	pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf);
>  
> -	status = alienware_wmax_command(&args, WMAX_METHOD_DEEP_SLEEP_CONTROL,
> -					NULL);
> +	status = alienware_wmax_command(&args, sizeof(args),
> +					WMAX_METHOD_DEEP_SLEEP_CONTROL, NULL);
>  
>  	if (ACPI_FAILURE(status))
>  		pr_err("alienware-wmi: deep sleep control failed: results: %u\n",
> @@ -760,6 +806,140 @@ static int create_deepsleep(struct platform_device *dev)
>  	return ret;
>  }
>  
> +/*
> + * Thermal Profile control
> + *  - Provides thermal profile control through the Platform Profile API
> + */
> +

Extra newline.

> +static int platform_profile_get(struct platform_profile_handler *pprof,
> +				enum platform_profile_option *profile)
> +{
> +	acpi_status status;
> +	u32 in_args = 0x0B;

Use a named define instead of a magic number.

> +	u32 out_data;
> +
> +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> +					WMAX_METHOD_THERMAL_INFORMATION, (u32 *) &out_data);

Casting to the same type??

Also, alienware_wmax_command() inputs int * which makes the cast look even 
more odd?!?

I can see there are pre-existing bogus (u32 *) casts too which would be 
nice to clean up in another patch.

> +
> +	if (ACPI_FAILURE(status) || out_data < 0)

u32 cannot be < 0??

Is this an indication of a problem in the error handling?

> +		return -EOPNOTSUPP;
> +
> +	switch (out_data) {
> +	case WMAX_THERMAL_LOW_POWER:
> +		*profile = PLATFORM_PROFILE_LOW_POWER;
> +		break;
> +	case WMAX_THERMAL_QUIET:
> +		*profile = PLATFORM_PROFILE_QUIET;
> +		break;
> +	case WMAX_THERMAL_BALANCED:
> +		*profile = PLATFORM_PROFILE_BALANCED;
> +		break;
> +	case WMAX_THERMAL_BALANCED_PERFORMANCE:
> +		*profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE;
> +		break;
> +	case WMAX_THERMAL_PERFORMANCE:
> +	case WMAX_THERMAL_GMODE:
> +		*profile = PLATFORM_PROFILE_PERFORMANCE;
> +		break;
> +	default:
> +		return -ENODATA;
> +	}
> +
> +	return 0;
> +}
> +
> +#define SET_MASK(prof) ((prof << 8) | 1)

Name these with two defines. One define for naming that magic 1 and 
another for the profile field, use GENMASK() + FIELD_PREP() for it.

Also, there's no need for this to be macro so change it into a static 
function.

> +static int platform_profile_set(struct platform_profile_handler *pprof,
> +				enum platform_profile_option profile)
> +{
> +	acpi_status status;
> +	u32 in_args;
> +	u32 out_data;
> +
> +	switch (profile) {
> +	case PLATFORM_PROFILE_LOW_POWER:
> +		in_args = SET_MASK(WMAX_THERMAL_LOW_POWER);
> +		break;
> +	case PLATFORM_PROFILE_QUIET:
> +		in_args = SET_MASK(WMAX_THERMAL_QUIET);
> +		break;
> +	case PLATFORM_PROFILE_BALANCED:
> +		in_args = SET_MASK(WMAX_THERMAL_BALANCED);
> +		break;
> +	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
> +		in_args = SET_MASK(WMAX_THERMAL_BALANCED_PERFORMANCE);
> +		break;
> +	case PLATFORM_PROFILE_PERFORMANCE:
> +		in_args = SET_MASK(WMAX_THERMAL_PERFORMANCE);
> +		break;
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +
> +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> +					WMAX_METHOD_THERMAL_CONTROL, (u32 *) &out_data);

Cast to same type.

> +
> +	if (ACPI_FAILURE(status) || out_data < 0)

u32 cannot be < 0.

> +		return -EOPNOTSUPP;
> +
> +	return 0;
> +}
> +
> +static int gmode_platform_profile_set(struct platform_profile_handler *pprof,
> +				      enum platform_profile_option profile)
> +{
> +	acpi_status status;
> +	u32 in_args;
> +	u32 out_data;
> +
> +	switch (profile) {
> +	case PLATFORM_PROFILE_LOW_POWER:
> +		in_args = SET_MASK(WMAX_THERMAL_LOW_POWER);
> +		break;
> +	case PLATFORM_PROFILE_QUIET:
> +		in_args = SET_MASK(WMAX_THERMAL_QUIET);
> +		break;
> +	case PLATFORM_PROFILE_BALANCED:
> +		in_args = SET_MASK(WMAX_THERMAL_BALANCED);
> +		break;
> +	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
> +		in_args = SET_MASK(WMAX_THERMAL_BALANCED_PERFORMANCE);
> +		break;
> +	case PLATFORM_PROFILE_PERFORMANCE:
> +		in_args = SET_MASK(WMAX_THERMAL_GMODE);
> +		break;
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +
> +	status = alienware_wmax_command(&in_args, sizeof(in_args),

> +					WMAX_METHOD_THERMAL_CONTROL, (u32 *) &out_data);
> +	if (ACPI_FAILURE(status) || out_data < 0)

The same two issues here with the types.

> +		return -EOPNOTSUPP;
> +
> +	return 0;
> +}
> +
> +static int create_platform_profile(void)
> +{
> +	pp_handler.profile_get = platform_profile_get;
> +
> +	if (quirks->gmode > 0)
> +		pp_handler.profile_set = gmode_platform_profile_set;
> +	else
> +		pp_handler.profile_set = platform_profile_set;
> +
> +	set_bit(PLATFORM_PROFILE_LOW_POWER, pp_handler.choices);
> +	set_bit(PLATFORM_PROFILE_QUIET, pp_handler.choices);
> +	set_bit(PLATFORM_PROFILE_BALANCED, pp_handler.choices);
> +	set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, pp_handler.choices);
> +	set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices);
> +
> +	return platform_profile_register(&pp_handler);
> +}
> +
>  static int __init alienware_wmi_init(void)
>  {
>  	int ret;
> @@ -807,6 +987,12 @@ static int __init alienware_wmi_init(void)
>  			goto fail_prep_deepsleep;
>  	}
>  
> +	if (quirks->thermal > 0) {
> +		ret = create_platform_profile();
> +		if (ret)
> +			goto fail_prep_thermal_profile;
> +	}
> +
>  	ret = alienware_zone_init(platform_device);
>  	if (ret)
>  		goto fail_prep_zones;
> @@ -817,6 +1003,7 @@ static int __init alienware_wmi_init(void)
>  	alienware_zone_exit(platform_device);
>  fail_prep_deepsleep:
>  fail_prep_amplifier:
> +fail_prep_thermal_profile:
>  fail_prep_hdmi:
>  	platform_device_del(platform_device);
>  fail_platform_device2:
> @@ -836,6 +1023,7 @@ static void __exit alienware_wmi_exit(void)
>  		remove_hdmi(platform_device);
>  		platform_device_unregister(platform_device);
>  		platform_driver_unregister(&platform_driver);
> +		platform_profile_remove();

IIRC, I once checked that this will lead to a crash if it wasn't created 
first (which in your driver isn't always the case).

-- 
 i.
Re: [PATCH v2] alienware-wmi: Dell AWCC platform_profile support
Posted by Kurt Borja 1 month, 2 weeks ago
Removed.

> > +static int platform_profile_get(struct platform_profile_handler *pprof,
> > +				enum platform_profile_option *profile)
> > +{
> > +	acpi_status status;
> > +	u32 in_args = 0x0B;
> 
> Use a named define instead of a magic number.
> 

Done

> > +	u32 out_data;
> > +
> > +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> > +					WMAX_METHOD_THERMAL_INFORMATION, (u32 *) &out_data);
> 
> Casting to the same type??
> 
> Also, alienware_wmax_command() inputs int * which makes the cast look even 
> more odd?!?
> 
> I can see there are pre-existing bogus (u32 *) casts too which would be 
> nice to clean up in another patch.
> 

Yes I thought it was odd but added them just in case. I can submit a
patch cleaning those up after this one.

> > +
> > +	if (ACPI_FAILURE(status) || out_data < 0)
> 
> u32 cannot be < 0??
> 
> Is this an indication of a problem in the error handling?
> 

Yes it was, thank you for catching it.

> > +		return -EOPNOTSUPP;
> > +
> > +	switch (out_data) {
> > +	case WMAX_THERMAL_LOW_POWER:
> > +		*profile = PLATFORM_PROFILE_LOW_POWER;
> > +		break;
> > +	case WMAX_THERMAL_QUIET:
> > +		*profile = PLATFORM_PROFILE_QUIET;
> > +		break;
> > +	case WMAX_THERMAL_BALANCED:
> > +		*profile = PLATFORM_PROFILE_BALANCED;
> > +		break;
> > +	case WMAX_THERMAL_BALANCED_PERFORMANCE:
> > +		*profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE;
> > +		break;
> > +	case WMAX_THERMAL_PERFORMANCE:
> > +	case WMAX_THERMAL_GMODE:
> > +		*profile = PLATFORM_PROFILE_PERFORMANCE;
> > +		break;
> > +	default:
> > +		return -ENODATA;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +#define SET_MASK(prof) ((prof << 8) | 1)
> 
> Name these with two defines. One define for naming that magic 1 and 
> another for the profile field, use GENMASK() + FIELD_PREP() for it.
> 
> Also, there's no need for this to be macro so change it into a static 
> function.
> 

Done.

> > +static int platform_profile_set(struct platform_profile_handler *pprof,
> > +				enum platform_profile_option profile)
> > +{
> > +	acpi_status status;
> > +	u32 in_args;
> > +	u32 out_data;
> > +
> > +	switch (profile) {
> > +	case PLATFORM_PROFILE_LOW_POWER:
> > +		in_args = SET_MASK(WMAX_THERMAL_LOW_POWER);
> > +		break;
> > +	case PLATFORM_PROFILE_QUIET:
> > +		in_args = SET_MASK(WMAX_THERMAL_QUIET);
> > +		break;
> > +	case PLATFORM_PROFILE_BALANCED:
> > +		in_args = SET_MASK(WMAX_THERMAL_BALANCED);
> > +		break;
> > +	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
> > +		in_args = SET_MASK(WMAX_THERMAL_BALANCED_PERFORMANCE);
> > +		break;
> > +	case PLATFORM_PROFILE_PERFORMANCE:
> > +		in_args = SET_MASK(WMAX_THERMAL_PERFORMANCE);
> > +		break;
> > +	default:
> > +		return -EOPNOTSUPP;
> > +	}
> > +
> > +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> > +					WMAX_METHOD_THERMAL_CONTROL, (u32 *) &out_data);
> 
> Cast to same type.
> 
> > +
> > +	if (ACPI_FAILURE(status) || out_data < 0)
> 
> u32 cannot be < 0.
> 
> > +		return -EOPNOTSUPP;
> > +
> > +	return 0;
> > +}
> > +
> > +static int gmode_platform_profile_set(struct platform_profile_handler *pprof,
> > +				      enum platform_profile_option profile)
> > +{
> > +	acpi_status status;
> > +	u32 in_args;
> > +	u32 out_data;
> > +
> > +	switch (profile) {
> > +	case PLATFORM_PROFILE_LOW_POWER:
> > +		in_args = SET_MASK(WMAX_THERMAL_LOW_POWER);
> > +		break;
> > +	case PLATFORM_PROFILE_QUIET:
> > +		in_args = SET_MASK(WMAX_THERMAL_QUIET);
> > +		break;
> > +	case PLATFORM_PROFILE_BALANCED:
> > +		in_args = SET_MASK(WMAX_THERMAL_BALANCED);
> > +		break;
> > +	case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
> > +		in_args = SET_MASK(WMAX_THERMAL_BALANCED_PERFORMANCE);
> > +		break;
> > +	case PLATFORM_PROFILE_PERFORMANCE:
> > +		in_args = SET_MASK(WMAX_THERMAL_GMODE);
> > +		break;
> > +	default:
> > +		return -EOPNOTSUPP;
> > +	}
> > +
> > +	status = alienware_wmax_command(&in_args, sizeof(in_args),
> 
> > +					WMAX_METHOD_THERMAL_CONTROL, (u32 *) &out_data);
> > +	if (ACPI_FAILURE(status) || out_data < 0)
> 
> The same two issues here with the types.
> 
> > +		return -EOPNOTSUPP;
> > +
> > +	return 0;
> > +}
> > +
> > +static int create_platform_profile(void)
> > +{
> > +	pp_handler.profile_get = platform_profile_get;
> > +
> > +	if (quirks->gmode > 0)
> > +		pp_handler.profile_set = gmode_platform_profile_set;
> > +	else
> > +		pp_handler.profile_set = platform_profile_set;
> > +
> > +	set_bit(PLATFORM_PROFILE_LOW_POWER, pp_handler.choices);
> > +	set_bit(PLATFORM_PROFILE_QUIET, pp_handler.choices);
> > +	set_bit(PLATFORM_PROFILE_BALANCED, pp_handler.choices);
> > +	set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, pp_handler.choices);
> > +	set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices);
> > +
> > +	return platform_profile_register(&pp_handler);
> > +}
> > +
> >  static int __init alienware_wmi_init(void)
> >  {
> >  	int ret;
> > @@ -807,6 +987,12 @@ static int __init alienware_wmi_init(void)
> >  			goto fail_prep_deepsleep;
> >  	}
> >  
> > +	if (quirks->thermal > 0) {
> > +		ret = create_platform_profile();
> > +		if (ret)
> > +			goto fail_prep_thermal_profile;
> > +	}
> > +
> >  	ret = alienware_zone_init(platform_device);
> >  	if (ret)
> >  		goto fail_prep_zones;
> > @@ -817,6 +1003,7 @@ static int __init alienware_wmi_init(void)
> >  	alienware_zone_exit(platform_device);
> >  fail_prep_deepsleep:
> >  fail_prep_amplifier:
> > +fail_prep_thermal_profile:
> >  fail_prep_hdmi:
> >  	platform_device_del(platform_device);
> >  fail_platform_device2:
> > @@ -836,6 +1023,7 @@ static void __exit alienware_wmi_exit(void)
> >  		remove_hdmi(platform_device);
> >  		platform_device_unregister(platform_device);
> >  		platform_driver_unregister(&platform_driver);
> > +		platform_profile_remove();
> 
> IIRC, I once checked that this will lead to a crash if it wasn't created 
> first (which in your driver isn't always the case).
> 

Fixed.

> -- 
>  i.

Thank you for your feedback. I will submit v3 with the corrections soon.

Kurt