* [PATCH v10 1/5] alienware-wmi: fixed indentation and clean up
2024-10-29 13:51 [PATCH v10 0/5] Dell AWCC platform_profile support Kurt Borja
@ 2024-10-29 13:52 ` Kurt Borja
2024-10-29 13:53 ` [PATCH v10 2/5] alienware-wmi: alienware_wmax_command() is now input size agnostic Kurt Borja
` (3 subsequent siblings)
4 siblings, 0 replies; 13+ messages in thread
From: Kurt Borja @ 2024-10-29 13:52 UTC (permalink / raw)
To: kuurtb; +Cc: W_Armin, hdegoede, ilpo.jarvinen, linux-kernel,
platform-driver-x86
Fixed inconsistent indentation and removed unnecessary (acpi_size) and
(u32 *) casts.
Signed-off-by: Kurt Borja <kuurtb@gmail.com>
Reviewed-by: Armin Wolf <W_Armin@gmx.de>
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
---
v10:
- Unchanged
v9:
- Unchanged
v8:
- Unchanged
v7:
- Unchanged
v6:
- Unchanged
---
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
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v10 2/5] alienware-wmi: alienware_wmax_command() is now input size agnostic
2024-10-29 13:51 [PATCH v10 0/5] Dell AWCC platform_profile support Kurt Borja
2024-10-29 13:52 ` [PATCH v10 1/5] alienware-wmi: fixed indentation and clean up Kurt Borja
@ 2024-10-29 13:53 ` Kurt Borja
2024-10-29 13:53 ` [PATCH v10 3/5] alienware-wmi: added platform profile support Kurt Borja
` (2 subsequent siblings)
4 siblings, 0 replies; 13+ messages in thread
From: Kurt Borja @ 2024-10-29 13:53 UTC (permalink / raw)
To: kuurtb; +Cc: W_Armin, hdegoede, ilpo.jarvinen, linux-kernel,
platform-driver-x86
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>
Reviewed-by: Armin Wolf <W_Armin@gmx.de>
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
---
v10:
- Unchanged
v9:
- Unchanged
v8:
- Unchanged
v7:
- Unchanged
v6:
- Unchanged
---
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
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v10 3/5] alienware-wmi: added platform profile support
2024-10-29 13:51 [PATCH v10 0/5] Dell AWCC platform_profile support Kurt Borja
2024-10-29 13:52 ` [PATCH v10 1/5] alienware-wmi: fixed indentation and clean up Kurt Borja
2024-10-29 13:53 ` [PATCH v10 2/5] alienware-wmi: alienware_wmax_command() is now input size agnostic Kurt Borja
@ 2024-10-29 13:53 ` Kurt Borja
2024-10-29 15:44 ` Ilpo Järvinen
2024-10-29 13:53 ` [PATCH v10 4/5] alienware-wmi: added force module parameters Kurt Borja
2024-10-29 13:54 ` [PATCH v10 5/5] alienware-wmi: WMAX interface documentation Kurt Borja
4 siblings, 1 reply; 13+ messages in thread
From: Kurt Borja @ 2024-10-29 13:53 UTC (permalink / raw)
To: kuurtb; +Cc: W_Armin, hdegoede, ilpo.jarvinen, linux-kernel,
platform-driver-x86
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 interface is suspected to be used by
Alienware Command Center (AWCC), which is not available for linux
systems, to manage thermal profiles.
This implementation makes use of three WMI methods, namely
THERMAL_CONTROL, THERMAL_INFORMATION and GAME_SHIFT_STATUS, which take
u32 as input and output arguments. Each method has a set of supported
operations specified in their respective enums.
Not all models with WMAX WMI interface support these methods. Because of
this, models have to manually declare support through new quirks
`thermal` for THERMAL_CONTROL and THERMAL_INFORMATION and `gmode` for
GAME_SHIFT_STATUS.
Wrappers written for these methods support multiple operations.
THERMAL_CONTROL switches thermal modes through operation
ACTIVATE_PROFILE. Available thermal codes are auto-detected at runtime
and matched against a list of known thermal codes:
Thermal Table "User Selectable Thermal Tables" (USTT):
BALANCED 0xA0
BALANCED_PERFORMANCE 0xA1
COOL 0xA2
QUIET 0xA3
PERFORMANCE 0xA4
LOW_POWER 0xA5
Thermal Table Basic:
QUIET 0x96
BALANCED 0x97
BALANCED_PERFORMANCE 0x98
PERFORMANCE 0x99
Devices are known to implement only one of these tables without mixing
their thermal codes.
The fact that the least significant digit of every thermal code is
consecutive of one another is exploited to efficiently match codes
through arrays.
Autodetection of available codes is done through operation LIST_IDS of
method THERMAL_INFORMATION. This operation lists fan IDs, CPU sensor ID,
GPU sensor ID and available thermal profile codes, *in that order*. As
number of fans and thermal codes is very model dependent, almost every
ID is scanned and matched based on conditions found on
is_wmax_thermal_code(). The known upper bound for the number of IDs is
13, corresponding to a device that have 4 fans, 2 sensors and 7 thermal
codes.
Additionally G-Series laptops have a key called G-key, which (with AWCC
proprietary driver) switches the thermal mode to an special mode named
GMODE with code 0xAB and changes Game Shift Status to 1. Game Shift is a
mode the manufacturer claims, increases gaming performance.
GAME_SHIFT_STATUS method is used to mimic this behavior when selecting
PLATFORM_PROFILE_PERFORMANCE option.
All of these profiles are known to only change fan speed profiles,
although there are untested claims that some of them also change power
profiles.
Activating a thermal mode with method THERMAL_CONTROL may cause short
hangs. This is a known problem present on every platform.
Signed-off-by: Kurt Borja <kuurtb@gmail.com>
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
---
v10:
- Corrected THERMAL_MODE_BASIC_BALANCED -> THERMAL_MODE_BASIC_QUIET in
is_wmax_thermal_code() logic
- `thermal` and `gmode` quirks now have to be manually selected,
because not all Dell devices posses the new WMI thermal methods.
- Because of the above reason, errors in create_thermal_profile are now
propagated
v9:
- Bool comparisons are now coherent with their type
v8:
- Fixed alignment in wmax_mode_to_platform_profile[]
- Quirk thermal and gmode changed from u8 -> bool
- Autodetected quirk entries are not initialized
- is_wmax_thermal_code refactored to increase readibility
- is_wmax_thermal_code now covers all possibilities
- Better commit message
v7:
- Method operations are now clearly listed as separate enums
- wmax_thermal_modes are now listed without codes in order to support
autodetection, as well as getting and setting thermal profiles
cleanly through arrays
- Added wmax_mode_to_platform_profile[]
- Added struct wmax_u32_args to replace bit mask approach of
constructing arguments for wmax methods
- create_thermal_profile now autodetects available thermal codes
through operation 0x03 of THERMAL_INFORMATION method. These are
codes are stored in supported_thermal_profiles[]
- thermal_profile_get now uses wmax_mode_to_platform_profile[] instead of
switch-case approach
- thermal_profile_set now uses supported_thermal_profiles[] instead of
switch-case approach
- When gmode is autodetected, thermal_profile_set also sets Game Shift
status accordingly
v6:
- Fixed alignment on some function definitions
- Fixed braces on if statment
- Removed quirk thermal_ustt
- Now quirk thermal can take values defined in enum WMAX_THERMAL_TABLE.
- Proper removal of thermal_profile
---
drivers/platform/x86/dell/Kconfig | 1 +
drivers/platform/x86/dell/alienware-wmi.c | 306 ++++++++++++++++++++++
2 files changed, 307 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..1d62c2ce7 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,13 @@
#define WMAX_METHOD_AMPLIFIER_CABLE 0x6
#define WMAX_METHOD_DEEP_SLEEP_CONTROL 0x0B
#define WMAX_METHOD_DEEP_SLEEP_STATUS 0x0C
+#define WMAX_METHOD_THERMAL_INFORMATION 0x14
+#define WMAX_METHOD_THERMAL_CONTROL 0x15
+#define WMAX_METHOD_GAME_SHIFT_STATUS 0x25
+
+#define WMAX_THERMAL_MODE_GMODE 0xAB
+
+#define WMAX_FAILURE_CODE 0xFFFFFFFF
MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
MODULE_DESCRIPTION("Alienware special feature control");
@@ -49,11 +59,59 @@ enum WMAX_CONTROL_STATES {
WMAX_SUSPEND = 3,
};
+enum WMAX_THERMAL_INFORMATION_OPERATIONS {
+ WMAX_OPERATION_LIST_IDS = 0x03,
+ WMAX_OPERATION_CURRENT_PROFILE = 0x0B,
+};
+
+enum WMAX_THERMAL_CONTROL_OPERATIONS {
+ WMAX_OPERATION_ACTIVATE_PROFILE = 0x01,
+};
+
+enum WMAX_GAME_SHIFT_STATUS_OPERATIONS {
+ WMAX_OPERATION_TOGGLE_GAME_SHIFT = 0x01,
+ WMAX_OPERATION_GET_GAME_SHIFT_STATUS = 0x02,
+};
+
+enum WMAX_THERMAL_TABLES {
+ WMAX_THERMAL_TABLE_BASIC = 0x90,
+ WMAX_THERMAL_TABLE_USTT = 0xA0,
+};
+
+enum wmax_thermal_mode {
+ THERMAL_MODE_USTT_BALANCED,
+ THERMAL_MODE_USTT_BALANCED_PERFORMANCE,
+ THERMAL_MODE_USTT_COOL,
+ THERMAL_MODE_USTT_QUIET,
+ THERMAL_MODE_USTT_PERFORMANCE,
+ THERMAL_MODE_USTT_LOW_POWER,
+ THERMAL_MODE_BASIC_QUIET,
+ THERMAL_MODE_BASIC_BALANCED,
+ THERMAL_MODE_BASIC_BALANCED_PERFORMANCE,
+ THERMAL_MODE_BASIC_PERFORMANCE,
+ THERMAL_MODE_LAST,
+};
+
+static const enum platform_profile_option wmax_mode_to_platform_profile[THERMAL_MODE_LAST] = {
+ [THERMAL_MODE_USTT_BALANCED] = PLATFORM_PROFILE_BALANCED,
+ [THERMAL_MODE_USTT_BALANCED_PERFORMANCE] = PLATFORM_PROFILE_BALANCED_PERFORMANCE,
+ [THERMAL_MODE_USTT_COOL] = PLATFORM_PROFILE_COOL,
+ [THERMAL_MODE_USTT_QUIET] = PLATFORM_PROFILE_QUIET,
+ [THERMAL_MODE_USTT_PERFORMANCE] = PLATFORM_PROFILE_PERFORMANCE,
+ [THERMAL_MODE_USTT_LOW_POWER] = PLATFORM_PROFILE_LOW_POWER,
+ [THERMAL_MODE_BASIC_QUIET] = PLATFORM_PROFILE_QUIET,
+ [THERMAL_MODE_BASIC_BALANCED] = PLATFORM_PROFILE_BALANCED,
+ [THERMAL_MODE_BASIC_BALANCED_PERFORMANCE] = PLATFORM_PROFILE_BALANCED_PERFORMANCE,
+ [THERMAL_MODE_BASIC_PERFORMANCE] = PLATFORM_PROFILE_PERFORMANCE,
+};
+
struct quirk_entry {
u8 num_zones;
u8 hdmi_mux;
u8 amplifier;
u8 deepslp;
+ bool thermal;
+ bool gmode;
};
static struct quirk_entry *quirks;
@@ -64,6 +122,8 @@ static struct quirk_entry quirk_inspiron5675 = {
.hdmi_mux = 0,
.amplifier = 0,
.deepslp = 0,
+ .thermal = false,
+ .gmode = false,
};
static struct quirk_entry quirk_unknown = {
@@ -71,6 +131,8 @@ static struct quirk_entry quirk_unknown = {
.hdmi_mux = 0,
.amplifier = 0,
.deepslp = 0,
+ .thermal = false,
+ .gmode = false,
};
static struct quirk_entry quirk_x51_r1_r2 = {
@@ -78,6 +140,8 @@ static struct quirk_entry quirk_x51_r1_r2 = {
.hdmi_mux = 0,
.amplifier = 0,
.deepslp = 0,
+ .thermal = false,
+ .gmode = false,
};
static struct quirk_entry quirk_x51_r3 = {
@@ -85,6 +149,8 @@ static struct quirk_entry quirk_x51_r3 = {
.hdmi_mux = 0,
.amplifier = 1,
.deepslp = 0,
+ .thermal = false,
+ .gmode = false,
};
static struct quirk_entry quirk_asm100 = {
@@ -92,6 +158,8 @@ static struct quirk_entry quirk_asm100 = {
.hdmi_mux = 1,
.amplifier = 0,
.deepslp = 0,
+ .thermal = false,
+ .gmode = false,
};
static struct quirk_entry quirk_asm200 = {
@@ -99,6 +167,8 @@ static struct quirk_entry quirk_asm200 = {
.hdmi_mux = 1,
.amplifier = 0,
.deepslp = 1,
+ .thermal = false,
+ .gmode = false,
};
static struct quirk_entry quirk_asm201 = {
@@ -106,6 +176,17 @@ static struct quirk_entry quirk_asm201 = {
.hdmi_mux = 1,
.amplifier = 1,
.deepslp = 1,
+ .thermal = false,
+ .gmode = false,
+};
+
+static struct quirk_entry quirk_x_series = {
+ .num_zones = 2,
+ .hdmi_mux = 0,
+ .amplifier = 0,
+ .deepslp = 0,
+ .thermal = true,
+ .gmode = false,
};
static int __init dmi_matched(const struct dmi_system_id *dmi)
@@ -178,6 +259,15 @@ static const struct dmi_system_id alienware_quirks[] __initconst = {
},
.driver_data = &quirk_inspiron5675,
},
+ {
+ .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_x_series,
+ },
{}
};
@@ -214,10 +304,19 @@ struct wmax_led_args {
u8 state;
} __packed;
+struct wmax_u32_args {
+ u8 operation;
+ u8 arg1;
+ u8 arg2;
+ u8 arg3;
+};
+
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 enum wmax_thermal_mode supported_thermal_profiles[PLATFORM_PROFILE_LAST];
static struct platform_driver platform_driver = {
.driver = {
@@ -761,6 +860,204 @@ static int create_deepsleep(struct platform_device *dev)
return ret;
}
+/*
+ * Thermal Profile control
+ * - Provides thermal profile control through the Platform Profile API
+ */
+#define WMAX_THERMAL_TABLE_MASK GENMASK(7, 4)
+#define WMAX_THERMAL_MODE_MASK GENMASK(3, 0)
+#define WMAX_SENSOR_ID_MASK BIT(8)
+
+static bool is_wmax_thermal_code(u32 code)
+{
+ if (code & WMAX_SENSOR_ID_MASK)
+ return false;
+
+ if ((code & WMAX_THERMAL_MODE_MASK) >= THERMAL_MODE_LAST)
+ return false;
+
+ if ((code & WMAX_THERMAL_TABLE_MASK) == WMAX_THERMAL_TABLE_BASIC &&
+ (code & WMAX_THERMAL_MODE_MASK) >= THERMAL_MODE_BASIC_QUIET)
+ return true;
+
+ if ((code & WMAX_THERMAL_TABLE_MASK) == WMAX_THERMAL_TABLE_USTT &&
+ (code & WMAX_THERMAL_MODE_MASK) <= THERMAL_MODE_USTT_LOW_POWER)
+ return true;
+
+ return false;
+}
+
+static int wmax_thermal_information(u8 operation, u8 arg, u32 *out_data)
+{
+ acpi_status status;
+ struct wmax_u32_args in_args = {
+ .operation = operation,
+ .arg1 = arg,
+ .arg2 = 0,
+ .arg3 = 0,
+ };
+
+ status = alienware_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;
+
+ return 0;
+}
+
+static int wmax_thermal_control(u8 profile)
+{
+ acpi_status status;
+ struct wmax_u32_args in_args = {
+ .operation = WMAX_OPERATION_ACTIVATE_PROFILE,
+ .arg1 = profile,
+ .arg2 = 0,
+ .arg3 = 0,
+ };
+ u32 out_data;
+
+ status = alienware_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 wmax_game_shift_status(u8 operation, u32 *out_data)
+{
+ acpi_status status;
+ struct wmax_u32_args in_args = {
+ .operation = operation,
+ .arg1 = 0,
+ .arg2 = 0,
+ .arg3 = 0,
+ };
+
+ status = alienware_wmax_command(&in_args, sizeof(in_args),
+ WMAX_METHOD_GAME_SHIFT_STATUS,
+ out_data);
+
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ if (*out_data == WMAX_FAILURE_CODE)
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+static int thermal_profile_get(struct platform_profile_handler *pprof,
+ enum platform_profile_option *profile)
+{
+ u32 out_data;
+ int ret;
+
+ ret = wmax_thermal_information(WMAX_OPERATION_CURRENT_PROFILE,
+ 0, &out_data);
+
+ if (ret < 0)
+ return ret;
+
+ if (out_data == WMAX_THERMAL_MODE_GMODE) {
+ *profile = PLATFORM_PROFILE_PERFORMANCE;
+ return 0;
+ }
+
+ if (!is_wmax_thermal_code(out_data))
+ return -ENODATA;
+
+ out_data &= WMAX_THERMAL_MODE_MASK;
+ *profile = wmax_mode_to_platform_profile[out_data];
+
+ return 0;
+}
+
+static int thermal_profile_set(struct platform_profile_handler *pprof,
+ enum platform_profile_option profile)
+{
+ if (quirks->gmode) {
+ u32 gmode_status;
+ int ret;
+
+ ret = wmax_game_shift_status(WMAX_OPERATION_GET_GAME_SHIFT_STATUS,
+ &gmode_status);
+
+ if (ret < 0)
+ return ret;
+
+ if ((profile == PLATFORM_PROFILE_PERFORMANCE && !gmode_status) ||
+ (profile != PLATFORM_PROFILE_PERFORMANCE && gmode_status)) {
+ ret = wmax_game_shift_status(WMAX_OPERATION_TOGGLE_GAME_SHIFT,
+ &gmode_status);
+
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ return wmax_thermal_control(supported_thermal_profiles[profile]);
+}
+
+static int create_thermal_profile(void)
+{
+ u32 out_data;
+ enum wmax_thermal_mode mode;
+ enum platform_profile_option profile;
+ int ret;
+
+ for (u8 i = 0x2; i <= 0xD; i++) {
+ ret = wmax_thermal_information(WMAX_OPERATION_LIST_IDS,
+ i, &out_data);
+
+ if (ret == -EIO)
+ return ret;
+
+ if (ret == -EBADRQC)
+ break;
+
+ if (!is_wmax_thermal_code(out_data))
+ continue;
+
+ mode = out_data & WMAX_THERMAL_MODE_MASK;
+ profile = wmax_mode_to_platform_profile[mode];
+ supported_thermal_profiles[profile] = out_data;
+
+ set_bit(profile, pp_handler.choices);
+ }
+
+ if (bitmap_empty(pp_handler.choices, PLATFORM_PROFILE_LAST))
+ return -ENODEV;
+
+ if (quirks->gmode) {
+ supported_thermal_profiles[PLATFORM_PROFILE_PERFORMANCE] =
+ WMAX_THERMAL_MODE_GMODE;
+
+ set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices);
+ }
+
+ pp_handler.profile_get = thermal_profile_get;
+ pp_handler.profile_set = thermal_profile_set;
+
+ return platform_profile_register(&pp_handler);
+}
+
+static void remove_thermal_profile(void)
+{
+ if (quirks->thermal)
+ platform_profile_remove();
+}
+
static int __init alienware_wmi_init(void)
{
int ret;
@@ -808,6 +1105,12 @@ static int __init alienware_wmi_init(void)
goto fail_prep_deepsleep;
}
+ if (quirks->thermal) {
+ ret = create_thermal_profile();
+ if (ret)
+ goto fail_prep_thermal_profile;
+ }
+
ret = alienware_zone_init(platform_device);
if (ret)
goto fail_prep_zones;
@@ -816,6 +1119,8 @@ static int __init alienware_wmi_init(void)
fail_prep_zones:
alienware_zone_exit(platform_device);
+ remove_thermal_profile();
+fail_prep_thermal_profile:
fail_prep_deepsleep:
fail_prep_amplifier:
fail_prep_hdmi:
@@ -835,6 +1140,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
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH v10 3/5] alienware-wmi: added platform profile support
2024-10-29 13:53 ` [PATCH v10 3/5] alienware-wmi: added platform profile support Kurt Borja
@ 2024-10-29 15:44 ` Ilpo Järvinen
2024-10-29 15:59 ` Kurt Borja
0 siblings, 1 reply; 13+ messages in thread
From: Ilpo Järvinen @ 2024-10-29 15:44 UTC (permalink / raw)
To: Kurt Borja; +Cc: W_Armin, Hans de Goede, LKML, platform-driver-x86
[-- Attachment #1: Type: text/plain, Size: 17815 bytes --]
On Tue, 29 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 interface is suspected to be used by
> Alienware Command Center (AWCC), which is not available for linux
> systems, to manage thermal profiles.
>
> This implementation makes use of three WMI methods, namely
> THERMAL_CONTROL, THERMAL_INFORMATION and GAME_SHIFT_STATUS, which take
> u32 as input and output arguments. Each method has a set of supported
> operations specified in their respective enums.
>
> Not all models with WMAX WMI interface support these methods. Because of
> this, models have to manually declare support through new quirks
> `thermal` for THERMAL_CONTROL and THERMAL_INFORMATION and `gmode` for
> GAME_SHIFT_STATUS.
>
> Wrappers written for these methods support multiple operations.
>
> THERMAL_CONTROL switches thermal modes through operation
> ACTIVATE_PROFILE. Available thermal codes are auto-detected at runtime
> and matched against a list of known thermal codes:
>
> Thermal Table "User Selectable Thermal Tables" (USTT):
> BALANCED 0xA0
> BALANCED_PERFORMANCE 0xA1
> COOL 0xA2
> QUIET 0xA3
> PERFORMANCE 0xA4
> LOW_POWER 0xA5
>
> Thermal Table Basic:
> QUIET 0x96
> BALANCED 0x97
> BALANCED_PERFORMANCE 0x98
> PERFORMANCE 0x99
>
> Devices are known to implement only one of these tables without mixing
> their thermal codes.
>
> The fact that the least significant digit of every thermal code is
> consecutive of one another is exploited to efficiently match codes
> through arrays.
>
> Autodetection of available codes is done through operation LIST_IDS of
> method THERMAL_INFORMATION. This operation lists fan IDs, CPU sensor ID,
> GPU sensor ID and available thermal profile codes, *in that order*. As
> number of fans and thermal codes is very model dependent, almost every
> ID is scanned and matched based on conditions found on
> is_wmax_thermal_code(). The known upper bound for the number of IDs is
> 13, corresponding to a device that have 4 fans, 2 sensors and 7 thermal
> codes.
>
> Additionally G-Series laptops have a key called G-key, which (with AWCC
> proprietary driver) switches the thermal mode to an special mode named
> GMODE with code 0xAB and changes Game Shift Status to 1. Game Shift is a
> mode the manufacturer claims, increases gaming performance.
>
> GAME_SHIFT_STATUS method is used to mimic this behavior when selecting
> PLATFORM_PROFILE_PERFORMANCE option.
>
> All of these profiles are known to only change fan speed profiles,
> although there are untested claims that some of them also change power
> profiles.
>
> Activating a thermal mode with method THERMAL_CONTROL may cause short
> hangs. This is a known problem present on every platform.
>
> Signed-off-by: Kurt Borja <kuurtb@gmail.com>
> Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
> ---
> v10:
> - Corrected THERMAL_MODE_BASIC_BALANCED -> THERMAL_MODE_BASIC_QUIET in
> is_wmax_thermal_code() logic
> - `thermal` and `gmode` quirks now have to be manually selected,
> because not all Dell devices posses the new WMI thermal methods.
> - Because of the above reason, errors in create_thermal_profile are now
> propagated
> v9:
> - Bool comparisons are now coherent with their type
> v8:
> - Fixed alignment in wmax_mode_to_platform_profile[]
> - Quirk thermal and gmode changed from u8 -> bool
> - Autodetected quirk entries are not initialized
> - is_wmax_thermal_code refactored to increase readibility
> - is_wmax_thermal_code now covers all possibilities
> - Better commit message
> v7:
> - Method operations are now clearly listed as separate enums
> - wmax_thermal_modes are now listed without codes in order to support
> autodetection, as well as getting and setting thermal profiles
> cleanly through arrays
> - Added wmax_mode_to_platform_profile[]
> - Added struct wmax_u32_args to replace bit mask approach of
> constructing arguments for wmax methods
> - create_thermal_profile now autodetects available thermal codes
> through operation 0x03 of THERMAL_INFORMATION method. These are
> codes are stored in supported_thermal_profiles[]
> - thermal_profile_get now uses wmax_mode_to_platform_profile[] instead of
> switch-case approach
> - thermal_profile_set now uses supported_thermal_profiles[] instead of
> switch-case approach
> - When gmode is autodetected, thermal_profile_set also sets Game Shift
> status accordingly
> v6:
> - Fixed alignment on some function definitions
> - Fixed braces on if statment
> - Removed quirk thermal_ustt
> - Now quirk thermal can take values defined in enum WMAX_THERMAL_TABLE.
> - Proper removal of thermal_profile
> ---
> drivers/platform/x86/dell/Kconfig | 1 +
> drivers/platform/x86/dell/alienware-wmi.c | 306 ++++++++++++++++++++++
> 2 files changed, 307 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..1d62c2ce7 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,13 @@
> #define WMAX_METHOD_AMPLIFIER_CABLE 0x6
> #define WMAX_METHOD_DEEP_SLEEP_CONTROL 0x0B
> #define WMAX_METHOD_DEEP_SLEEP_STATUS 0x0C
> +#define WMAX_METHOD_THERMAL_INFORMATION 0x14
> +#define WMAX_METHOD_THERMAL_CONTROL 0x15
> +#define WMAX_METHOD_GAME_SHIFT_STATUS 0x25
> +
> +#define WMAX_THERMAL_MODE_GMODE 0xAB
> +
> +#define WMAX_FAILURE_CODE 0xFFFFFFFF
>
> MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
> MODULE_DESCRIPTION("Alienware special feature control");
> @@ -49,11 +59,59 @@ enum WMAX_CONTROL_STATES {
> WMAX_SUSPEND = 3,
> };
>
> +enum WMAX_THERMAL_INFORMATION_OPERATIONS {
> + WMAX_OPERATION_LIST_IDS = 0x03,
> + WMAX_OPERATION_CURRENT_PROFILE = 0x0B,
> +};
> +
> +enum WMAX_THERMAL_CONTROL_OPERATIONS {
> + WMAX_OPERATION_ACTIVATE_PROFILE = 0x01,
> +};
> +
> +enum WMAX_GAME_SHIFT_STATUS_OPERATIONS {
> + WMAX_OPERATION_TOGGLE_GAME_SHIFT = 0x01,
> + WMAX_OPERATION_GET_GAME_SHIFT_STATUS = 0x02,
> +};
> +
> +enum WMAX_THERMAL_TABLES {
> + WMAX_THERMAL_TABLE_BASIC = 0x90,
> + WMAX_THERMAL_TABLE_USTT = 0xA0,
> +};
> +
> +enum wmax_thermal_mode {
> + THERMAL_MODE_USTT_BALANCED,
> + THERMAL_MODE_USTT_BALANCED_PERFORMANCE,
> + THERMAL_MODE_USTT_COOL,
> + THERMAL_MODE_USTT_QUIET,
> + THERMAL_MODE_USTT_PERFORMANCE,
> + THERMAL_MODE_USTT_LOW_POWER,
> + THERMAL_MODE_BASIC_QUIET,
> + THERMAL_MODE_BASIC_BALANCED,
> + THERMAL_MODE_BASIC_BALANCED_PERFORMANCE,
> + THERMAL_MODE_BASIC_PERFORMANCE,
> + THERMAL_MODE_LAST,
> +};
> +
> +static const enum platform_profile_option wmax_mode_to_platform_profile[THERMAL_MODE_LAST] = {
> + [THERMAL_MODE_USTT_BALANCED] = PLATFORM_PROFILE_BALANCED,
> + [THERMAL_MODE_USTT_BALANCED_PERFORMANCE] = PLATFORM_PROFILE_BALANCED_PERFORMANCE,
> + [THERMAL_MODE_USTT_COOL] = PLATFORM_PROFILE_COOL,
> + [THERMAL_MODE_USTT_QUIET] = PLATFORM_PROFILE_QUIET,
> + [THERMAL_MODE_USTT_PERFORMANCE] = PLATFORM_PROFILE_PERFORMANCE,
> + [THERMAL_MODE_USTT_LOW_POWER] = PLATFORM_PROFILE_LOW_POWER,
> + [THERMAL_MODE_BASIC_QUIET] = PLATFORM_PROFILE_QUIET,
> + [THERMAL_MODE_BASIC_BALANCED] = PLATFORM_PROFILE_BALANCED,
> + [THERMAL_MODE_BASIC_BALANCED_PERFORMANCE] = PLATFORM_PROFILE_BALANCED_PERFORMANCE,
> + [THERMAL_MODE_BASIC_PERFORMANCE] = PLATFORM_PROFILE_PERFORMANCE,
> +};
> +
> struct quirk_entry {
> u8 num_zones;
> u8 hdmi_mux;
> u8 amplifier;
> u8 deepslp;
> + bool thermal;
> + bool gmode;
> };
>
> static struct quirk_entry *quirks;
> @@ -64,6 +122,8 @@ static struct quirk_entry quirk_inspiron5675 = {
> .hdmi_mux = 0,
> .amplifier = 0,
> .deepslp = 0,
> + .thermal = false,
> + .gmode = false,
> };
>
> static struct quirk_entry quirk_unknown = {
> @@ -71,6 +131,8 @@ static struct quirk_entry quirk_unknown = {
> .hdmi_mux = 0,
> .amplifier = 0,
> .deepslp = 0,
> + .thermal = false,
> + .gmode = false,
> };
>
> static struct quirk_entry quirk_x51_r1_r2 = {
> @@ -78,6 +140,8 @@ static struct quirk_entry quirk_x51_r1_r2 = {
> .hdmi_mux = 0,
> .amplifier = 0,
> .deepslp = 0,
> + .thermal = false,
> + .gmode = false,
> };
>
> static struct quirk_entry quirk_x51_r3 = {
> @@ -85,6 +149,8 @@ static struct quirk_entry quirk_x51_r3 = {
> .hdmi_mux = 0,
> .amplifier = 1,
> .deepslp = 0,
> + .thermal = false,
> + .gmode = false,
> };
>
> static struct quirk_entry quirk_asm100 = {
> @@ -92,6 +158,8 @@ static struct quirk_entry quirk_asm100 = {
> .hdmi_mux = 1,
> .amplifier = 0,
> .deepslp = 0,
> + .thermal = false,
> + .gmode = false,
> };
>
> static struct quirk_entry quirk_asm200 = {
> @@ -99,6 +167,8 @@ static struct quirk_entry quirk_asm200 = {
> .hdmi_mux = 1,
> .amplifier = 0,
> .deepslp = 1,
> + .thermal = false,
> + .gmode = false,
> };
>
> static struct quirk_entry quirk_asm201 = {
> @@ -106,6 +176,17 @@ static struct quirk_entry quirk_asm201 = {
> .hdmi_mux = 1,
> .amplifier = 1,
> .deepslp = 1,
> + .thermal = false,
> + .gmode = false,
> +};
> +
> +static struct quirk_entry quirk_x_series = {
> + .num_zones = 2,
> + .hdmi_mux = 0,
> + .amplifier = 0,
> + .deepslp = 0,
> + .thermal = true,
> + .gmode = false,
> };
So now gmode is always false unless the module parameter from patch 4 is
given?
--
i.
> static int __init dmi_matched(const struct dmi_system_id *dmi)
> @@ -178,6 +259,15 @@ static const struct dmi_system_id alienware_quirks[] __initconst = {
> },
> .driver_data = &quirk_inspiron5675,
> },
> + {
> + .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_x_series,
> + },
> {}
> };
>
> @@ -214,10 +304,19 @@ struct wmax_led_args {
> u8 state;
> } __packed;
>
> +struct wmax_u32_args {
> + u8 operation;
> + u8 arg1;
> + u8 arg2;
> + u8 arg3;
> +};
> +
> 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 enum wmax_thermal_mode supported_thermal_profiles[PLATFORM_PROFILE_LAST];
>
> static struct platform_driver platform_driver = {
> .driver = {
> @@ -761,6 +860,204 @@ static int create_deepsleep(struct platform_device *dev)
> return ret;
> }
>
> +/*
> + * Thermal Profile control
> + * - Provides thermal profile control through the Platform Profile API
> + */
> +#define WMAX_THERMAL_TABLE_MASK GENMASK(7, 4)
> +#define WMAX_THERMAL_MODE_MASK GENMASK(3, 0)
> +#define WMAX_SENSOR_ID_MASK BIT(8)
> +
> +static bool is_wmax_thermal_code(u32 code)
> +{
> + if (code & WMAX_SENSOR_ID_MASK)
> + return false;
> +
> + if ((code & WMAX_THERMAL_MODE_MASK) >= THERMAL_MODE_LAST)
> + return false;
> +
> + if ((code & WMAX_THERMAL_TABLE_MASK) == WMAX_THERMAL_TABLE_BASIC &&
> + (code & WMAX_THERMAL_MODE_MASK) >= THERMAL_MODE_BASIC_QUIET)
> + return true;
> +
> + if ((code & WMAX_THERMAL_TABLE_MASK) == WMAX_THERMAL_TABLE_USTT &&
> + (code & WMAX_THERMAL_MODE_MASK) <= THERMAL_MODE_USTT_LOW_POWER)
> + return true;
> +
> + return false;
> +}
> +
> +static int wmax_thermal_information(u8 operation, u8 arg, u32 *out_data)
> +{
> + acpi_status status;
> + struct wmax_u32_args in_args = {
> + .operation = operation,
> + .arg1 = arg,
> + .arg2 = 0,
> + .arg3 = 0,
> + };
> +
> + status = alienware_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;
> +
> + return 0;
> +}
> +
> +static int wmax_thermal_control(u8 profile)
> +{
> + acpi_status status;
> + struct wmax_u32_args in_args = {
> + .operation = WMAX_OPERATION_ACTIVATE_PROFILE,
> + .arg1 = profile,
> + .arg2 = 0,
> + .arg3 = 0,
> + };
> + u32 out_data;
> +
> + status = alienware_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 wmax_game_shift_status(u8 operation, u32 *out_data)
> +{
> + acpi_status status;
> + struct wmax_u32_args in_args = {
> + .operation = operation,
> + .arg1 = 0,
> + .arg2 = 0,
> + .arg3 = 0,
> + };
> +
> + status = alienware_wmax_command(&in_args, sizeof(in_args),
> + WMAX_METHOD_GAME_SHIFT_STATUS,
> + out_data);
> +
> + if (ACPI_FAILURE(status))
> + return -EIO;
> +
> + if (*out_data == WMAX_FAILURE_CODE)
> + return -EOPNOTSUPP;
> +
> + return 0;
> +}
> +
> +static int thermal_profile_get(struct platform_profile_handler *pprof,
> + enum platform_profile_option *profile)
> +{
> + u32 out_data;
> + int ret;
> +
> + ret = wmax_thermal_information(WMAX_OPERATION_CURRENT_PROFILE,
> + 0, &out_data);
> +
> + if (ret < 0)
> + return ret;
> +
> + if (out_data == WMAX_THERMAL_MODE_GMODE) {
> + *profile = PLATFORM_PROFILE_PERFORMANCE;
> + return 0;
> + }
> +
> + if (!is_wmax_thermal_code(out_data))
> + return -ENODATA;
> +
> + out_data &= WMAX_THERMAL_MODE_MASK;
> + *profile = wmax_mode_to_platform_profile[out_data];
> +
> + return 0;
> +}
> +
> +static int thermal_profile_set(struct platform_profile_handler *pprof,
> + enum platform_profile_option profile)
> +{
> + if (quirks->gmode) {
> + u32 gmode_status;
> + int ret;
> +
> + ret = wmax_game_shift_status(WMAX_OPERATION_GET_GAME_SHIFT_STATUS,
> + &gmode_status);
> +
> + if (ret < 0)
> + return ret;
> +
> + if ((profile == PLATFORM_PROFILE_PERFORMANCE && !gmode_status) ||
> + (profile != PLATFORM_PROFILE_PERFORMANCE && gmode_status)) {
> + ret = wmax_game_shift_status(WMAX_OPERATION_TOGGLE_GAME_SHIFT,
> + &gmode_status);
> +
> + if (ret < 0)
> + return ret;
> + }
> + }
> +
> + return wmax_thermal_control(supported_thermal_profiles[profile]);
> +}
> +
> +static int create_thermal_profile(void)
> +{
> + u32 out_data;
> + enum wmax_thermal_mode mode;
> + enum platform_profile_option profile;
> + int ret;
> +
> + for (u8 i = 0x2; i <= 0xD; i++) {
> + ret = wmax_thermal_information(WMAX_OPERATION_LIST_IDS,
> + i, &out_data);
> +
> + if (ret == -EIO)
> + return ret;
> +
> + if (ret == -EBADRQC)
> + break;
> +
> + if (!is_wmax_thermal_code(out_data))
> + continue;
> +
> + mode = out_data & WMAX_THERMAL_MODE_MASK;
> + profile = wmax_mode_to_platform_profile[mode];
> + supported_thermal_profiles[profile] = out_data;
> +
> + set_bit(profile, pp_handler.choices);
> + }
> +
> + if (bitmap_empty(pp_handler.choices, PLATFORM_PROFILE_LAST))
> + return -ENODEV;
> +
> + if (quirks->gmode) {
> + supported_thermal_profiles[PLATFORM_PROFILE_PERFORMANCE] =
> + WMAX_THERMAL_MODE_GMODE;
> +
> + set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices);
> + }
> +
> + pp_handler.profile_get = thermal_profile_get;
> + pp_handler.profile_set = thermal_profile_set;
> +
> + return platform_profile_register(&pp_handler);
> +}
> +
> +static void remove_thermal_profile(void)
> +{
> + if (quirks->thermal)
> + platform_profile_remove();
> +}
> +
> static int __init alienware_wmi_init(void)
> {
> int ret;
> @@ -808,6 +1105,12 @@ static int __init alienware_wmi_init(void)
> goto fail_prep_deepsleep;
> }
>
> + if (quirks->thermal) {
> + ret = create_thermal_profile();
> + if (ret)
> + goto fail_prep_thermal_profile;
> + }
> +
> ret = alienware_zone_init(platform_device);
> if (ret)
> goto fail_prep_zones;
> @@ -816,6 +1119,8 @@ static int __init alienware_wmi_init(void)
>
> fail_prep_zones:
> alienware_zone_exit(platform_device);
> + remove_thermal_profile();
> +fail_prep_thermal_profile:
> fail_prep_deepsleep:
> fail_prep_amplifier:
> fail_prep_hdmi:
> @@ -835,6 +1140,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);
> }
>
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH v10 3/5] alienware-wmi: added platform profile support
2024-10-29 15:44 ` Ilpo Järvinen
@ 2024-10-29 15:59 ` Kurt Borja
2024-10-29 17:06 ` Ilpo Järvinen
0 siblings, 1 reply; 13+ messages in thread
From: Kurt Borja @ 2024-10-29 15:59 UTC (permalink / raw)
To: Ilpo Järvinen; +Cc: W_Armin, Hans de Goede, LKML, platform-driver-x86
On Tue, Oct 29, 2024 at 05:44:05PM +0200, Ilpo Järvinen wrote:
> On Tue, 29 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 interface is suspected to be used by
> > Alienware Command Center (AWCC), which is not available for linux
> > systems, to manage thermal profiles.
> >
> > This implementation makes use of three WMI methods, namely
> > THERMAL_CONTROL, THERMAL_INFORMATION and GAME_SHIFT_STATUS, which take
> > u32 as input and output arguments. Each method has a set of supported
> > operations specified in their respective enums.
> >
> > Not all models with WMAX WMI interface support these methods. Because of
> > this, models have to manually declare support through new quirks
> > `thermal` for THERMAL_CONTROL and THERMAL_INFORMATION and `gmode` for
> > GAME_SHIFT_STATUS.
> >
> > Wrappers written for these methods support multiple operations.
> >
> > THERMAL_CONTROL switches thermal modes through operation
> > ACTIVATE_PROFILE. Available thermal codes are auto-detected at runtime
> > and matched against a list of known thermal codes:
> >
> > Thermal Table "User Selectable Thermal Tables" (USTT):
> > BALANCED 0xA0
> > BALANCED_PERFORMANCE 0xA1
> > COOL 0xA2
> > QUIET 0xA3
> > PERFORMANCE 0xA4
> > LOW_POWER 0xA5
> >
> > Thermal Table Basic:
> > QUIET 0x96
> > BALANCED 0x97
> > BALANCED_PERFORMANCE 0x98
> > PERFORMANCE 0x99
> >
> > Devices are known to implement only one of these tables without mixing
> > their thermal codes.
> >
> > The fact that the least significant digit of every thermal code is
> > consecutive of one another is exploited to efficiently match codes
> > through arrays.
> >
> > Autodetection of available codes is done through operation LIST_IDS of
> > method THERMAL_INFORMATION. This operation lists fan IDs, CPU sensor ID,
> > GPU sensor ID and available thermal profile codes, *in that order*. As
> > number of fans and thermal codes is very model dependent, almost every
> > ID is scanned and matched based on conditions found on
> > is_wmax_thermal_code(). The known upper bound for the number of IDs is
> > 13, corresponding to a device that have 4 fans, 2 sensors and 7 thermal
> > codes.
> >
> > Additionally G-Series laptops have a key called G-key, which (with AWCC
> > proprietary driver) switches the thermal mode to an special mode named
> > GMODE with code 0xAB and changes Game Shift Status to 1. Game Shift is a
> > mode the manufacturer claims, increases gaming performance.
> >
> > GAME_SHIFT_STATUS method is used to mimic this behavior when selecting
> > PLATFORM_PROFILE_PERFORMANCE option.
> >
> > All of these profiles are known to only change fan speed profiles,
> > although there are untested claims that some of them also change power
> > profiles.
> >
> > Activating a thermal mode with method THERMAL_CONTROL may cause short
> > hangs. This is a known problem present on every platform.
> >
> > Signed-off-by: Kurt Borja <kuurtb@gmail.com>
> > Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
> > ---
> > v10:
> > - Corrected THERMAL_MODE_BASIC_BALANCED -> THERMAL_MODE_BASIC_QUIET in
> > is_wmax_thermal_code() logic
> > - `thermal` and `gmode` quirks now have to be manually selected,
> > because not all Dell devices posses the new WMI thermal methods.
> > - Because of the above reason, errors in create_thermal_profile are now
> > propagated
> > v9:
> > - Bool comparisons are now coherent with their type
> > v8:
> > - Fixed alignment in wmax_mode_to_platform_profile[]
> > - Quirk thermal and gmode changed from u8 -> bool
> > - Autodetected quirk entries are not initialized
> > - is_wmax_thermal_code refactored to increase readibility
> > - is_wmax_thermal_code now covers all possibilities
> > - Better commit message
> > v7:
> > - Method operations are now clearly listed as separate enums
> > - wmax_thermal_modes are now listed without codes in order to support
> > autodetection, as well as getting and setting thermal profiles
> > cleanly through arrays
> > - Added wmax_mode_to_platform_profile[]
> > - Added struct wmax_u32_args to replace bit mask approach of
> > constructing arguments for wmax methods
> > - create_thermal_profile now autodetects available thermal codes
> > through operation 0x03 of THERMAL_INFORMATION method. These are
> > codes are stored in supported_thermal_profiles[]
> > - thermal_profile_get now uses wmax_mode_to_platform_profile[] instead of
> > switch-case approach
> > - thermal_profile_set now uses supported_thermal_profiles[] instead of
> > switch-case approach
> > - When gmode is autodetected, thermal_profile_set also sets Game Shift
> > status accordingly
> > v6:
> > - Fixed alignment on some function definitions
> > - Fixed braces on if statment
> > - Removed quirk thermal_ustt
> > - Now quirk thermal can take values defined in enum WMAX_THERMAL_TABLE.
> > - Proper removal of thermal_profile
> > ---
> > drivers/platform/x86/dell/Kconfig | 1 +
> > drivers/platform/x86/dell/alienware-wmi.c | 306 ++++++++++++++++++++++
> > 2 files changed, 307 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..1d62c2ce7 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,13 @@
> > #define WMAX_METHOD_AMPLIFIER_CABLE 0x6
> > #define WMAX_METHOD_DEEP_SLEEP_CONTROL 0x0B
> > #define WMAX_METHOD_DEEP_SLEEP_STATUS 0x0C
> > +#define WMAX_METHOD_THERMAL_INFORMATION 0x14
> > +#define WMAX_METHOD_THERMAL_CONTROL 0x15
> > +#define WMAX_METHOD_GAME_SHIFT_STATUS 0x25
> > +
> > +#define WMAX_THERMAL_MODE_GMODE 0xAB
> > +
> > +#define WMAX_FAILURE_CODE 0xFFFFFFFF
> >
> > MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
> > MODULE_DESCRIPTION("Alienware special feature control");
> > @@ -49,11 +59,59 @@ enum WMAX_CONTROL_STATES {
> > WMAX_SUSPEND = 3,
> > };
> >
> > +enum WMAX_THERMAL_INFORMATION_OPERATIONS {
> > + WMAX_OPERATION_LIST_IDS = 0x03,
> > + WMAX_OPERATION_CURRENT_PROFILE = 0x0B,
> > +};
> > +
> > +enum WMAX_THERMAL_CONTROL_OPERATIONS {
> > + WMAX_OPERATION_ACTIVATE_PROFILE = 0x01,
> > +};
> > +
> > +enum WMAX_GAME_SHIFT_STATUS_OPERATIONS {
> > + WMAX_OPERATION_TOGGLE_GAME_SHIFT = 0x01,
> > + WMAX_OPERATION_GET_GAME_SHIFT_STATUS = 0x02,
> > +};
> > +
> > +enum WMAX_THERMAL_TABLES {
> > + WMAX_THERMAL_TABLE_BASIC = 0x90,
> > + WMAX_THERMAL_TABLE_USTT = 0xA0,
> > +};
> > +
> > +enum wmax_thermal_mode {
> > + THERMAL_MODE_USTT_BALANCED,
> > + THERMAL_MODE_USTT_BALANCED_PERFORMANCE,
> > + THERMAL_MODE_USTT_COOL,
> > + THERMAL_MODE_USTT_QUIET,
> > + THERMAL_MODE_USTT_PERFORMANCE,
> > + THERMAL_MODE_USTT_LOW_POWER,
> > + THERMAL_MODE_BASIC_QUIET,
> > + THERMAL_MODE_BASIC_BALANCED,
> > + THERMAL_MODE_BASIC_BALANCED_PERFORMANCE,
> > + THERMAL_MODE_BASIC_PERFORMANCE,
> > + THERMAL_MODE_LAST,
> > +};
> > +
> > +static const enum platform_profile_option wmax_mode_to_platform_profile[THERMAL_MODE_LAST] = {
> > + [THERMAL_MODE_USTT_BALANCED] = PLATFORM_PROFILE_BALANCED,
> > + [THERMAL_MODE_USTT_BALANCED_PERFORMANCE] = PLATFORM_PROFILE_BALANCED_PERFORMANCE,
> > + [THERMAL_MODE_USTT_COOL] = PLATFORM_PROFILE_COOL,
> > + [THERMAL_MODE_USTT_QUIET] = PLATFORM_PROFILE_QUIET,
> > + [THERMAL_MODE_USTT_PERFORMANCE] = PLATFORM_PROFILE_PERFORMANCE,
> > + [THERMAL_MODE_USTT_LOW_POWER] = PLATFORM_PROFILE_LOW_POWER,
> > + [THERMAL_MODE_BASIC_QUIET] = PLATFORM_PROFILE_QUIET,
> > + [THERMAL_MODE_BASIC_BALANCED] = PLATFORM_PROFILE_BALANCED,
> > + [THERMAL_MODE_BASIC_BALANCED_PERFORMANCE] = PLATFORM_PROFILE_BALANCED_PERFORMANCE,
> > + [THERMAL_MODE_BASIC_PERFORMANCE] = PLATFORM_PROFILE_PERFORMANCE,
> > +};
> > +
> > struct quirk_entry {
> > u8 num_zones;
> > u8 hdmi_mux;
> > u8 amplifier;
> > u8 deepslp;
> > + bool thermal;
> > + bool gmode;
> > };
> >
> > static struct quirk_entry *quirks;
> > @@ -64,6 +122,8 @@ static struct quirk_entry quirk_inspiron5675 = {
> > .hdmi_mux = 0,
> > .amplifier = 0,
> > .deepslp = 0,
> > + .thermal = false,
> > + .gmode = false,
> > };
> >
> > static struct quirk_entry quirk_unknown = {
> > @@ -71,6 +131,8 @@ static struct quirk_entry quirk_unknown = {
> > .hdmi_mux = 0,
> > .amplifier = 0,
> > .deepslp = 0,
> > + .thermal = false,
> > + .gmode = false,
> > };
> >
> > static struct quirk_entry quirk_x51_r1_r2 = {
> > @@ -78,6 +140,8 @@ static struct quirk_entry quirk_x51_r1_r2 = {
> > .hdmi_mux = 0,
> > .amplifier = 0,
> > .deepslp = 0,
> > + .thermal = false,
> > + .gmode = false,
> > };
> >
> > static struct quirk_entry quirk_x51_r3 = {
> > @@ -85,6 +149,8 @@ static struct quirk_entry quirk_x51_r3 = {
> > .hdmi_mux = 0,
> > .amplifier = 1,
> > .deepslp = 0,
> > + .thermal = false,
> > + .gmode = false,
> > };
> >
> > static struct quirk_entry quirk_asm100 = {
> > @@ -92,6 +158,8 @@ static struct quirk_entry quirk_asm100 = {
> > .hdmi_mux = 1,
> > .amplifier = 0,
> > .deepslp = 0,
> > + .thermal = false,
> > + .gmode = false,
> > };
> >
> > static struct quirk_entry quirk_asm200 = {
> > @@ -99,6 +167,8 @@ static struct quirk_entry quirk_asm200 = {
> > .hdmi_mux = 1,
> > .amplifier = 0,
> > .deepslp = 1,
> > + .thermal = false,
> > + .gmode = false,
> > };
> >
> > static struct quirk_entry quirk_asm201 = {
> > @@ -106,6 +176,17 @@ static struct quirk_entry quirk_asm201 = {
> > .hdmi_mux = 1,
> > .amplifier = 1,
> > .deepslp = 1,
> > + .thermal = false,
> > + .gmode = false,
> > +};
> > +
> > +static struct quirk_entry quirk_x_series = {
> > + .num_zones = 2,
> > + .hdmi_mux = 0,
> > + .amplifier = 0,
> > + .deepslp = 0,
> > + .thermal = true,
> > + .gmode = false,
> > };
>
> So now gmode is always false unless the module parameter from patch 4 is
> given?
G-Series laptops can also register a quirk_entry with gmode set in the
future. I can do it ahead of time, but I don't have a G-Series laptop to
test it.
Kurt
>
> --
> i.
>
> > static int __init dmi_matched(const struct dmi_system_id *dmi)
> > @@ -178,6 +259,15 @@ static const struct dmi_system_id alienware_quirks[] __initconst = {
> > },
> > .driver_data = &quirk_inspiron5675,
> > },
> > + {
> > + .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_x_series,
> > + },
> > {}
> > };
> >
> > @@ -214,10 +304,19 @@ struct wmax_led_args {
> > u8 state;
> > } __packed;
> >
> > +struct wmax_u32_args {
> > + u8 operation;
> > + u8 arg1;
> > + u8 arg2;
> > + u8 arg3;
> > +};
> > +
> > 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 enum wmax_thermal_mode supported_thermal_profiles[PLATFORM_PROFILE_LAST];
> >
> > static struct platform_driver platform_driver = {
> > .driver = {
> > @@ -761,6 +860,204 @@ static int create_deepsleep(struct platform_device *dev)
> > return ret;
> > }
> >
> > +/*
> > + * Thermal Profile control
> > + * - Provides thermal profile control through the Platform Profile API
> > + */
> > +#define WMAX_THERMAL_TABLE_MASK GENMASK(7, 4)
> > +#define WMAX_THERMAL_MODE_MASK GENMASK(3, 0)
> > +#define WMAX_SENSOR_ID_MASK BIT(8)
> > +
> > +static bool is_wmax_thermal_code(u32 code)
> > +{
> > + if (code & WMAX_SENSOR_ID_MASK)
> > + return false;
> > +
> > + if ((code & WMAX_THERMAL_MODE_MASK) >= THERMAL_MODE_LAST)
> > + return false;
> > +
> > + if ((code & WMAX_THERMAL_TABLE_MASK) == WMAX_THERMAL_TABLE_BASIC &&
> > + (code & WMAX_THERMAL_MODE_MASK) >= THERMAL_MODE_BASIC_QUIET)
> > + return true;
> > +
> > + if ((code & WMAX_THERMAL_TABLE_MASK) == WMAX_THERMAL_TABLE_USTT &&
> > + (code & WMAX_THERMAL_MODE_MASK) <= THERMAL_MODE_USTT_LOW_POWER)
> > + return true;
> > +
> > + return false;
> > +}
> > +
> > +static int wmax_thermal_information(u8 operation, u8 arg, u32 *out_data)
> > +{
> > + acpi_status status;
> > + struct wmax_u32_args in_args = {
> > + .operation = operation,
> > + .arg1 = arg,
> > + .arg2 = 0,
> > + .arg3 = 0,
> > + };
> > +
> > + status = alienware_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;
> > +
> > + return 0;
> > +}
> > +
> > +static int wmax_thermal_control(u8 profile)
> > +{
> > + acpi_status status;
> > + struct wmax_u32_args in_args = {
> > + .operation = WMAX_OPERATION_ACTIVATE_PROFILE,
> > + .arg1 = profile,
> > + .arg2 = 0,
> > + .arg3 = 0,
> > + };
> > + u32 out_data;
> > +
> > + status = alienware_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 wmax_game_shift_status(u8 operation, u32 *out_data)
> > +{
> > + acpi_status status;
> > + struct wmax_u32_args in_args = {
> > + .operation = operation,
> > + .arg1 = 0,
> > + .arg2 = 0,
> > + .arg3 = 0,
> > + };
> > +
> > + status = alienware_wmax_command(&in_args, sizeof(in_args),
> > + WMAX_METHOD_GAME_SHIFT_STATUS,
> > + out_data);
> > +
> > + if (ACPI_FAILURE(status))
> > + return -EIO;
> > +
> > + if (*out_data == WMAX_FAILURE_CODE)
> > + return -EOPNOTSUPP;
> > +
> > + return 0;
> > +}
> > +
> > +static int thermal_profile_get(struct platform_profile_handler *pprof,
> > + enum platform_profile_option *profile)
> > +{
> > + u32 out_data;
> > + int ret;
> > +
> > + ret = wmax_thermal_information(WMAX_OPERATION_CURRENT_PROFILE,
> > + 0, &out_data);
> > +
> > + if (ret < 0)
> > + return ret;
> > +
> > + if (out_data == WMAX_THERMAL_MODE_GMODE) {
> > + *profile = PLATFORM_PROFILE_PERFORMANCE;
> > + return 0;
> > + }
> > +
> > + if (!is_wmax_thermal_code(out_data))
> > + return -ENODATA;
> > +
> > + out_data &= WMAX_THERMAL_MODE_MASK;
> > + *profile = wmax_mode_to_platform_profile[out_data];
> > +
> > + return 0;
> > +}
> > +
> > +static int thermal_profile_set(struct platform_profile_handler *pprof,
> > + enum platform_profile_option profile)
> > +{
> > + if (quirks->gmode) {
> > + u32 gmode_status;
> > + int ret;
> > +
> > + ret = wmax_game_shift_status(WMAX_OPERATION_GET_GAME_SHIFT_STATUS,
> > + &gmode_status);
> > +
> > + if (ret < 0)
> > + return ret;
> > +
> > + if ((profile == PLATFORM_PROFILE_PERFORMANCE && !gmode_status) ||
> > + (profile != PLATFORM_PROFILE_PERFORMANCE && gmode_status)) {
> > + ret = wmax_game_shift_status(WMAX_OPERATION_TOGGLE_GAME_SHIFT,
> > + &gmode_status);
> > +
> > + if (ret < 0)
> > + return ret;
> > + }
> > + }
> > +
> > + return wmax_thermal_control(supported_thermal_profiles[profile]);
> > +}
> > +
> > +static int create_thermal_profile(void)
> > +{
> > + u32 out_data;
> > + enum wmax_thermal_mode mode;
> > + enum platform_profile_option profile;
> > + int ret;
> > +
> > + for (u8 i = 0x2; i <= 0xD; i++) {
> > + ret = wmax_thermal_information(WMAX_OPERATION_LIST_IDS,
> > + i, &out_data);
> > +
> > + if (ret == -EIO)
> > + return ret;
> > +
> > + if (ret == -EBADRQC)
> > + break;
> > +
> > + if (!is_wmax_thermal_code(out_data))
> > + continue;
> > +
> > + mode = out_data & WMAX_THERMAL_MODE_MASK;
> > + profile = wmax_mode_to_platform_profile[mode];
> > + supported_thermal_profiles[profile] = out_data;
> > +
> > + set_bit(profile, pp_handler.choices);
> > + }
> > +
> > + if (bitmap_empty(pp_handler.choices, PLATFORM_PROFILE_LAST))
> > + return -ENODEV;
> > +
> > + if (quirks->gmode) {
> > + supported_thermal_profiles[PLATFORM_PROFILE_PERFORMANCE] =
> > + WMAX_THERMAL_MODE_GMODE;
> > +
> > + set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices);
> > + }
> > +
> > + pp_handler.profile_get = thermal_profile_get;
> > + pp_handler.profile_set = thermal_profile_set;
> > +
> > + return platform_profile_register(&pp_handler);
> > +}
> > +
> > +static void remove_thermal_profile(void)
> > +{
> > + if (quirks->thermal)
> > + platform_profile_remove();
> > +}
> > +
> > static int __init alienware_wmi_init(void)
> > {
> > int ret;
> > @@ -808,6 +1105,12 @@ static int __init alienware_wmi_init(void)
> > goto fail_prep_deepsleep;
> > }
> >
> > + if (quirks->thermal) {
> > + ret = create_thermal_profile();
> > + if (ret)
> > + goto fail_prep_thermal_profile;
> > + }
> > +
> > ret = alienware_zone_init(platform_device);
> > if (ret)
> > goto fail_prep_zones;
> > @@ -816,6 +1119,8 @@ static int __init alienware_wmi_init(void)
> >
> > fail_prep_zones:
> > alienware_zone_exit(platform_device);
> > + remove_thermal_profile();
> > +fail_prep_thermal_profile:
> > fail_prep_deepsleep:
> > fail_prep_amplifier:
> > fail_prep_hdmi:
> > @@ -835,6 +1140,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);
> > }
> >
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH v10 3/5] alienware-wmi: added platform profile support
2024-10-29 15:59 ` Kurt Borja
@ 2024-10-29 17:06 ` Ilpo Järvinen
2024-10-29 23:11 ` Armin Wolf
0 siblings, 1 reply; 13+ messages in thread
From: Ilpo Järvinen @ 2024-10-29 17:06 UTC (permalink / raw)
To: Kurt Borja; +Cc: W_Armin, Hans de Goede, LKML, platform-driver-x86
[-- Attachment #1: Type: text/plain, Size: 12098 bytes --]
On Tue, 29 Oct 2024, Kurt Borja wrote:
> On Tue, Oct 29, 2024 at 05:44:05PM +0200, Ilpo Järvinen wrote:
> > On Tue, 29 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 interface is suspected to be used by
> > > Alienware Command Center (AWCC), which is not available for linux
> > > systems, to manage thermal profiles.
> > >
> > > This implementation makes use of three WMI methods, namely
> > > THERMAL_CONTROL, THERMAL_INFORMATION and GAME_SHIFT_STATUS, which take
> > > u32 as input and output arguments. Each method has a set of supported
> > > operations specified in their respective enums.
> > >
> > > Not all models with WMAX WMI interface support these methods. Because of
> > > this, models have to manually declare support through new quirks
> > > `thermal` for THERMAL_CONTROL and THERMAL_INFORMATION and `gmode` for
> > > GAME_SHIFT_STATUS.
> > >
> > > Wrappers written for these methods support multiple operations.
> > >
> > > THERMAL_CONTROL switches thermal modes through operation
> > > ACTIVATE_PROFILE. Available thermal codes are auto-detected at runtime
> > > and matched against a list of known thermal codes:
> > >
> > > Thermal Table "User Selectable Thermal Tables" (USTT):
> > > BALANCED 0xA0
> > > BALANCED_PERFORMANCE 0xA1
> > > COOL 0xA2
> > > QUIET 0xA3
> > > PERFORMANCE 0xA4
> > > LOW_POWER 0xA5
> > >
> > > Thermal Table Basic:
> > > QUIET 0x96
> > > BALANCED 0x97
> > > BALANCED_PERFORMANCE 0x98
> > > PERFORMANCE 0x99
> > >
> > > Devices are known to implement only one of these tables without mixing
> > > their thermal codes.
> > >
> > > The fact that the least significant digit of every thermal code is
> > > consecutive of one another is exploited to efficiently match codes
> > > through arrays.
> > >
> > > Autodetection of available codes is done through operation LIST_IDS of
> > > method THERMAL_INFORMATION. This operation lists fan IDs, CPU sensor ID,
> > > GPU sensor ID and available thermal profile codes, *in that order*. As
> > > number of fans and thermal codes is very model dependent, almost every
> > > ID is scanned and matched based on conditions found on
> > > is_wmax_thermal_code(). The known upper bound for the number of IDs is
> > > 13, corresponding to a device that have 4 fans, 2 sensors and 7 thermal
> > > codes.
> > >
> > > Additionally G-Series laptops have a key called G-key, which (with AWCC
> > > proprietary driver) switches the thermal mode to an special mode named
> > > GMODE with code 0xAB and changes Game Shift Status to 1. Game Shift is a
> > > mode the manufacturer claims, increases gaming performance.
> > >
> > > GAME_SHIFT_STATUS method is used to mimic this behavior when selecting
> > > PLATFORM_PROFILE_PERFORMANCE option.
> > >
> > > All of these profiles are known to only change fan speed profiles,
> > > although there are untested claims that some of them also change power
> > > profiles.
> > >
> > > Activating a thermal mode with method THERMAL_CONTROL may cause short
> > > hangs. This is a known problem present on every platform.
> > >
> > > Signed-off-by: Kurt Borja <kuurtb@gmail.com>
> > > Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
> > > ---
> > > v10:
> > > - Corrected THERMAL_MODE_BASIC_BALANCED -> THERMAL_MODE_BASIC_QUIET in
> > > is_wmax_thermal_code() logic
> > > - `thermal` and `gmode` quirks now have to be manually selected,
> > > because not all Dell devices posses the new WMI thermal methods.
> > > - Because of the above reason, errors in create_thermal_profile are now
> > > propagated
> > > v9:
> > > - Bool comparisons are now coherent with their type
> > > v8:
> > > - Fixed alignment in wmax_mode_to_platform_profile[]
> > > - Quirk thermal and gmode changed from u8 -> bool
> > > - Autodetected quirk entries are not initialized
> > > - is_wmax_thermal_code refactored to increase readibility
> > > - is_wmax_thermal_code now covers all possibilities
> > > - Better commit message
> > > v7:
> > > - Method operations are now clearly listed as separate enums
> > > - wmax_thermal_modes are now listed without codes in order to support
> > > autodetection, as well as getting and setting thermal profiles
> > > cleanly through arrays
> > > - Added wmax_mode_to_platform_profile[]
> > > - Added struct wmax_u32_args to replace bit mask approach of
> > > constructing arguments for wmax methods
> > > - create_thermal_profile now autodetects available thermal codes
> > > through operation 0x03 of THERMAL_INFORMATION method. These are
> > > codes are stored in supported_thermal_profiles[]
> > > - thermal_profile_get now uses wmax_mode_to_platform_profile[] instead of
> > > switch-case approach
> > > - thermal_profile_set now uses supported_thermal_profiles[] instead of
> > > switch-case approach
> > > - When gmode is autodetected, thermal_profile_set also sets Game Shift
> > > status accordingly
> > > v6:
> > > - Fixed alignment on some function definitions
> > > - Fixed braces on if statment
> > > - Removed quirk thermal_ustt
> > > - Now quirk thermal can take values defined in enum WMAX_THERMAL_TABLE.
> > > - Proper removal of thermal_profile
> > > ---
> > > drivers/platform/x86/dell/Kconfig | 1 +
> > > drivers/platform/x86/dell/alienware-wmi.c | 306 ++++++++++++++++++++++
> > > 2 files changed, 307 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..1d62c2ce7 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,13 @@
> > > #define WMAX_METHOD_AMPLIFIER_CABLE 0x6
> > > #define WMAX_METHOD_DEEP_SLEEP_CONTROL 0x0B
> > > #define WMAX_METHOD_DEEP_SLEEP_STATUS 0x0C
> > > +#define WMAX_METHOD_THERMAL_INFORMATION 0x14
> > > +#define WMAX_METHOD_THERMAL_CONTROL 0x15
> > > +#define WMAX_METHOD_GAME_SHIFT_STATUS 0x25
> > > +
> > > +#define WMAX_THERMAL_MODE_GMODE 0xAB
> > > +
> > > +#define WMAX_FAILURE_CODE 0xFFFFFFFF
> > >
> > > MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
> > > MODULE_DESCRIPTION("Alienware special feature control");
> > > @@ -49,11 +59,59 @@ enum WMAX_CONTROL_STATES {
> > > WMAX_SUSPEND = 3,
> > > };
> > >
> > > +enum WMAX_THERMAL_INFORMATION_OPERATIONS {
> > > + WMAX_OPERATION_LIST_IDS = 0x03,
> > > + WMAX_OPERATION_CURRENT_PROFILE = 0x0B,
> > > +};
> > > +
> > > +enum WMAX_THERMAL_CONTROL_OPERATIONS {
> > > + WMAX_OPERATION_ACTIVATE_PROFILE = 0x01,
> > > +};
> > > +
> > > +enum WMAX_GAME_SHIFT_STATUS_OPERATIONS {
> > > + WMAX_OPERATION_TOGGLE_GAME_SHIFT = 0x01,
> > > + WMAX_OPERATION_GET_GAME_SHIFT_STATUS = 0x02,
> > > +};
> > > +
> > > +enum WMAX_THERMAL_TABLES {
> > > + WMAX_THERMAL_TABLE_BASIC = 0x90,
> > > + WMAX_THERMAL_TABLE_USTT = 0xA0,
> > > +};
> > > +
> > > +enum wmax_thermal_mode {
> > > + THERMAL_MODE_USTT_BALANCED,
> > > + THERMAL_MODE_USTT_BALANCED_PERFORMANCE,
> > > + THERMAL_MODE_USTT_COOL,
> > > + THERMAL_MODE_USTT_QUIET,
> > > + THERMAL_MODE_USTT_PERFORMANCE,
> > > + THERMAL_MODE_USTT_LOW_POWER,
> > > + THERMAL_MODE_BASIC_QUIET,
> > > + THERMAL_MODE_BASIC_BALANCED,
> > > + THERMAL_MODE_BASIC_BALANCED_PERFORMANCE,
> > > + THERMAL_MODE_BASIC_PERFORMANCE,
> > > + THERMAL_MODE_LAST,
> > > +};
> > > +
> > > +static const enum platform_profile_option wmax_mode_to_platform_profile[THERMAL_MODE_LAST] = {
> > > + [THERMAL_MODE_USTT_BALANCED] = PLATFORM_PROFILE_BALANCED,
> > > + [THERMAL_MODE_USTT_BALANCED_PERFORMANCE] = PLATFORM_PROFILE_BALANCED_PERFORMANCE,
> > > + [THERMAL_MODE_USTT_COOL] = PLATFORM_PROFILE_COOL,
> > > + [THERMAL_MODE_USTT_QUIET] = PLATFORM_PROFILE_QUIET,
> > > + [THERMAL_MODE_USTT_PERFORMANCE] = PLATFORM_PROFILE_PERFORMANCE,
> > > + [THERMAL_MODE_USTT_LOW_POWER] = PLATFORM_PROFILE_LOW_POWER,
> > > + [THERMAL_MODE_BASIC_QUIET] = PLATFORM_PROFILE_QUIET,
> > > + [THERMAL_MODE_BASIC_BALANCED] = PLATFORM_PROFILE_BALANCED,
> > > + [THERMAL_MODE_BASIC_BALANCED_PERFORMANCE] = PLATFORM_PROFILE_BALANCED_PERFORMANCE,
> > > + [THERMAL_MODE_BASIC_PERFORMANCE] = PLATFORM_PROFILE_PERFORMANCE,
> > > +};
> > > +
> > > struct quirk_entry {
> > > u8 num_zones;
> > > u8 hdmi_mux;
> > > u8 amplifier;
> > > u8 deepslp;
> > > + bool thermal;
> > > + bool gmode;
> > > };
> > >
> > > static struct quirk_entry *quirks;
> > > @@ -64,6 +122,8 @@ static struct quirk_entry quirk_inspiron5675 = {
> > > .hdmi_mux = 0,
> > > .amplifier = 0,
> > > .deepslp = 0,
> > > + .thermal = false,
> > > + .gmode = false,
> > > };
> > >
> > > static struct quirk_entry quirk_unknown = {
> > > @@ -71,6 +131,8 @@ static struct quirk_entry quirk_unknown = {
> > > .hdmi_mux = 0,
> > > .amplifier = 0,
> > > .deepslp = 0,
> > > + .thermal = false,
> > > + .gmode = false,
> > > };
> > >
> > > static struct quirk_entry quirk_x51_r1_r2 = {
> > > @@ -78,6 +140,8 @@ static struct quirk_entry quirk_x51_r1_r2 = {
> > > .hdmi_mux = 0,
> > > .amplifier = 0,
> > > .deepslp = 0,
> > > + .thermal = false,
> > > + .gmode = false,
> > > };
> > >
> > > static struct quirk_entry quirk_x51_r3 = {
> > > @@ -85,6 +149,8 @@ static struct quirk_entry quirk_x51_r3 = {
> > > .hdmi_mux = 0,
> > > .amplifier = 1,
> > > .deepslp = 0,
> > > + .thermal = false,
> > > + .gmode = false,
> > > };
> > >
> > > static struct quirk_entry quirk_asm100 = {
> > > @@ -92,6 +158,8 @@ static struct quirk_entry quirk_asm100 = {
> > > .hdmi_mux = 1,
> > > .amplifier = 0,
> > > .deepslp = 0,
> > > + .thermal = false,
> > > + .gmode = false,
> > > };
> > >
> > > static struct quirk_entry quirk_asm200 = {
> > > @@ -99,6 +167,8 @@ static struct quirk_entry quirk_asm200 = {
> > > .hdmi_mux = 1,
> > > .amplifier = 0,
> > > .deepslp = 1,
> > > + .thermal = false,
> > > + .gmode = false,
> > > };
> > >
> > > static struct quirk_entry quirk_asm201 = {
> > > @@ -106,6 +176,17 @@ static struct quirk_entry quirk_asm201 = {
> > > .hdmi_mux = 1,
> > > .amplifier = 1,
> > > .deepslp = 1,
> > > + .thermal = false,
> > > + .gmode = false,
> > > +};
> > > +
> > > +static struct quirk_entry quirk_x_series = {
> > > + .num_zones = 2,
> > > + .hdmi_mux = 0,
> > > + .amplifier = 0,
> > > + .deepslp = 0,
> > > + .thermal = true,
> > > + .gmode = false,
> > > };
> >
> > So now gmode is always false unless the module parameter from patch 4 is
> > given?
>
> G-Series laptops can also register a quirk_entry with gmode set in the
> future. I can do it ahead of time, but I don't have a G-Series laptop to
> test it.
Understood. I'm fine with this in the current form if it's fine for Armin
too.
--
i.
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH v10 3/5] alienware-wmi: added platform profile support
2024-10-29 17:06 ` Ilpo Järvinen
@ 2024-10-29 23:11 ` Armin Wolf
2024-10-30 0:02 ` Kurt Borja
0 siblings, 1 reply; 13+ messages in thread
From: Armin Wolf @ 2024-10-29 23:11 UTC (permalink / raw)
To: Ilpo Järvinen, Kurt Borja; +Cc: Hans de Goede, LKML, platform-driver-x86
Am 29.10.24 um 18:06 schrieb Ilpo Järvinen:
> On Tue, 29 Oct 2024, Kurt Borja wrote:
>
>> On Tue, Oct 29, 2024 at 05:44:05PM +0200, Ilpo Järvinen wrote:
>>> On Tue, 29 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 interface is suspected to be used by
>>>> Alienware Command Center (AWCC), which is not available for linux
>>>> systems, to manage thermal profiles.
>>>>
>>>> This implementation makes use of three WMI methods, namely
>>>> THERMAL_CONTROL, THERMAL_INFORMATION and GAME_SHIFT_STATUS, which take
>>>> u32 as input and output arguments. Each method has a set of supported
>>>> operations specified in their respective enums.
>>>>
>>>> Not all models with WMAX WMI interface support these methods. Because of
>>>> this, models have to manually declare support through new quirks
>>>> `thermal` for THERMAL_CONTROL and THERMAL_INFORMATION and `gmode` for
>>>> GAME_SHIFT_STATUS.
>>>>
>>>> Wrappers written for these methods support multiple operations.
>>>>
>>>> THERMAL_CONTROL switches thermal modes through operation
>>>> ACTIVATE_PROFILE. Available thermal codes are auto-detected at runtime
>>>> and matched against a list of known thermal codes:
>>>>
>>>> Thermal Table "User Selectable Thermal Tables" (USTT):
>>>> BALANCED 0xA0
>>>> BALANCED_PERFORMANCE 0xA1
>>>> COOL 0xA2
>>>> QUIET 0xA3
>>>> PERFORMANCE 0xA4
>>>> LOW_POWER 0xA5
>>>>
>>>> Thermal Table Basic:
>>>> QUIET 0x96
>>>> BALANCED 0x97
>>>> BALANCED_PERFORMANCE 0x98
>>>> PERFORMANCE 0x99
>>>>
>>>> Devices are known to implement only one of these tables without mixing
>>>> their thermal codes.
>>>>
>>>> The fact that the least significant digit of every thermal code is
>>>> consecutive of one another is exploited to efficiently match codes
>>>> through arrays.
>>>>
>>>> Autodetection of available codes is done through operation LIST_IDS of
>>>> method THERMAL_INFORMATION. This operation lists fan IDs, CPU sensor ID,
>>>> GPU sensor ID and available thermal profile codes, *in that order*. As
>>>> number of fans and thermal codes is very model dependent, almost every
>>>> ID is scanned and matched based on conditions found on
>>>> is_wmax_thermal_code(). The known upper bound for the number of IDs is
>>>> 13, corresponding to a device that have 4 fans, 2 sensors and 7 thermal
>>>> codes.
>>>>
>>>> Additionally G-Series laptops have a key called G-key, which (with AWCC
>>>> proprietary driver) switches the thermal mode to an special mode named
>>>> GMODE with code 0xAB and changes Game Shift Status to 1. Game Shift is a
>>>> mode the manufacturer claims, increases gaming performance.
>>>>
>>>> GAME_SHIFT_STATUS method is used to mimic this behavior when selecting
>>>> PLATFORM_PROFILE_PERFORMANCE option.
>>>>
>>>> All of these profiles are known to only change fan speed profiles,
>>>> although there are untested claims that some of them also change power
>>>> profiles.
>>>>
>>>> Activating a thermal mode with method THERMAL_CONTROL may cause short
>>>> hangs. This is a known problem present on every platform.
>>>>
>>>> Signed-off-by: Kurt Borja <kuurtb@gmail.com>
>>>> Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
>>>> ---
>>>> v10:
>>>> - Corrected THERMAL_MODE_BASIC_BALANCED -> THERMAL_MODE_BASIC_QUIET in
>>>> is_wmax_thermal_code() logic
>>>> - `thermal` and `gmode` quirks now have to be manually selected,
>>>> because not all Dell devices posses the new WMI thermal methods.
>>>> - Because of the above reason, errors in create_thermal_profile are now
>>>> propagated
>>>> v9:
>>>> - Bool comparisons are now coherent with their type
>>>> v8:
>>>> - Fixed alignment in wmax_mode_to_platform_profile[]
>>>> - Quirk thermal and gmode changed from u8 -> bool
>>>> - Autodetected quirk entries are not initialized
>>>> - is_wmax_thermal_code refactored to increase readibility
>>>> - is_wmax_thermal_code now covers all possibilities
>>>> - Better commit message
>>>> v7:
>>>> - Method operations are now clearly listed as separate enums
>>>> - wmax_thermal_modes are now listed without codes in order to support
>>>> autodetection, as well as getting and setting thermal profiles
>>>> cleanly through arrays
>>>> - Added wmax_mode_to_platform_profile[]
>>>> - Added struct wmax_u32_args to replace bit mask approach of
>>>> constructing arguments for wmax methods
>>>> - create_thermal_profile now autodetects available thermal codes
>>>> through operation 0x03 of THERMAL_INFORMATION method. These are
>>>> codes are stored in supported_thermal_profiles[]
>>>> - thermal_profile_get now uses wmax_mode_to_platform_profile[] instead of
>>>> switch-case approach
>>>> - thermal_profile_set now uses supported_thermal_profiles[] instead of
>>>> switch-case approach
>>>> - When gmode is autodetected, thermal_profile_set also sets Game Shift
>>>> status accordingly
>>>> v6:
>>>> - Fixed alignment on some function definitions
>>>> - Fixed braces on if statment
>>>> - Removed quirk thermal_ustt
>>>> - Now quirk thermal can take values defined in enum WMAX_THERMAL_TABLE.
>>>> - Proper removal of thermal_profile
>>>> ---
>>>> drivers/platform/x86/dell/Kconfig | 1 +
>>>> drivers/platform/x86/dell/alienware-wmi.c | 306 ++++++++++++++++++++++
>>>> 2 files changed, 307 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..1d62c2ce7 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,13 @@
>>>> #define WMAX_METHOD_AMPLIFIER_CABLE 0x6
>>>> #define WMAX_METHOD_DEEP_SLEEP_CONTROL 0x0B
>>>> #define WMAX_METHOD_DEEP_SLEEP_STATUS 0x0C
>>>> +#define WMAX_METHOD_THERMAL_INFORMATION 0x14
>>>> +#define WMAX_METHOD_THERMAL_CONTROL 0x15
>>>> +#define WMAX_METHOD_GAME_SHIFT_STATUS 0x25
>>>> +
>>>> +#define WMAX_THERMAL_MODE_GMODE 0xAB
>>>> +
>>>> +#define WMAX_FAILURE_CODE 0xFFFFFFFF
>>>>
>>>> MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
>>>> MODULE_DESCRIPTION("Alienware special feature control");
>>>> @@ -49,11 +59,59 @@ enum WMAX_CONTROL_STATES {
>>>> WMAX_SUSPEND = 3,
>>>> };
>>>>
>>>> +enum WMAX_THERMAL_INFORMATION_OPERATIONS {
>>>> + WMAX_OPERATION_LIST_IDS = 0x03,
>>>> + WMAX_OPERATION_CURRENT_PROFILE = 0x0B,
>>>> +};
>>>> +
>>>> +enum WMAX_THERMAL_CONTROL_OPERATIONS {
>>>> + WMAX_OPERATION_ACTIVATE_PROFILE = 0x01,
>>>> +};
>>>> +
>>>> +enum WMAX_GAME_SHIFT_STATUS_OPERATIONS {
>>>> + WMAX_OPERATION_TOGGLE_GAME_SHIFT = 0x01,
>>>> + WMAX_OPERATION_GET_GAME_SHIFT_STATUS = 0x02,
>>>> +};
>>>> +
>>>> +enum WMAX_THERMAL_TABLES {
>>>> + WMAX_THERMAL_TABLE_BASIC = 0x90,
>>>> + WMAX_THERMAL_TABLE_USTT = 0xA0,
>>>> +};
>>>> +
>>>> +enum wmax_thermal_mode {
>>>> + THERMAL_MODE_USTT_BALANCED,
>>>> + THERMAL_MODE_USTT_BALANCED_PERFORMANCE,
>>>> + THERMAL_MODE_USTT_COOL,
>>>> + THERMAL_MODE_USTT_QUIET,
>>>> + THERMAL_MODE_USTT_PERFORMANCE,
>>>> + THERMAL_MODE_USTT_LOW_POWER,
>>>> + THERMAL_MODE_BASIC_QUIET,
>>>> + THERMAL_MODE_BASIC_BALANCED,
>>>> + THERMAL_MODE_BASIC_BALANCED_PERFORMANCE,
>>>> + THERMAL_MODE_BASIC_PERFORMANCE,
>>>> + THERMAL_MODE_LAST,
>>>> +};
>>>> +
>>>> +static const enum platform_profile_option wmax_mode_to_platform_profile[THERMAL_MODE_LAST] = {
>>>> + [THERMAL_MODE_USTT_BALANCED] = PLATFORM_PROFILE_BALANCED,
>>>> + [THERMAL_MODE_USTT_BALANCED_PERFORMANCE] = PLATFORM_PROFILE_BALANCED_PERFORMANCE,
>>>> + [THERMAL_MODE_USTT_COOL] = PLATFORM_PROFILE_COOL,
>>>> + [THERMAL_MODE_USTT_QUIET] = PLATFORM_PROFILE_QUIET,
>>>> + [THERMAL_MODE_USTT_PERFORMANCE] = PLATFORM_PROFILE_PERFORMANCE,
>>>> + [THERMAL_MODE_USTT_LOW_POWER] = PLATFORM_PROFILE_LOW_POWER,
>>>> + [THERMAL_MODE_BASIC_QUIET] = PLATFORM_PROFILE_QUIET,
>>>> + [THERMAL_MODE_BASIC_BALANCED] = PLATFORM_PROFILE_BALANCED,
>>>> + [THERMAL_MODE_BASIC_BALANCED_PERFORMANCE] = PLATFORM_PROFILE_BALANCED_PERFORMANCE,
>>>> + [THERMAL_MODE_BASIC_PERFORMANCE] = PLATFORM_PROFILE_PERFORMANCE,
>>>> +};
>>>> +
>>>> struct quirk_entry {
>>>> u8 num_zones;
>>>> u8 hdmi_mux;
>>>> u8 amplifier;
>>>> u8 deepslp;
>>>> + bool thermal;
>>>> + bool gmode;
>>>> };
>>>>
>>>> static struct quirk_entry *quirks;
>>>> @@ -64,6 +122,8 @@ static struct quirk_entry quirk_inspiron5675 = {
>>>> .hdmi_mux = 0,
>>>> .amplifier = 0,
>>>> .deepslp = 0,
>>>> + .thermal = false,
>>>> + .gmode = false,
>>>> };
>>>>
>>>> static struct quirk_entry quirk_unknown = {
>>>> @@ -71,6 +131,8 @@ static struct quirk_entry quirk_unknown = {
>>>> .hdmi_mux = 0,
>>>> .amplifier = 0,
>>>> .deepslp = 0,
>>>> + .thermal = false,
>>>> + .gmode = false,
>>>> };
>>>>
>>>> static struct quirk_entry quirk_x51_r1_r2 = {
>>>> @@ -78,6 +140,8 @@ static struct quirk_entry quirk_x51_r1_r2 = {
>>>> .hdmi_mux = 0,
>>>> .amplifier = 0,
>>>> .deepslp = 0,
>>>> + .thermal = false,
>>>> + .gmode = false,
>>>> };
>>>>
>>>> static struct quirk_entry quirk_x51_r3 = {
>>>> @@ -85,6 +149,8 @@ static struct quirk_entry quirk_x51_r3 = {
>>>> .hdmi_mux = 0,
>>>> .amplifier = 1,
>>>> .deepslp = 0,
>>>> + .thermal = false,
>>>> + .gmode = false,
>>>> };
>>>>
>>>> static struct quirk_entry quirk_asm100 = {
>>>> @@ -92,6 +158,8 @@ static struct quirk_entry quirk_asm100 = {
>>>> .hdmi_mux = 1,
>>>> .amplifier = 0,
>>>> .deepslp = 0,
>>>> + .thermal = false,
>>>> + .gmode = false,
>>>> };
>>>>
>>>> static struct quirk_entry quirk_asm200 = {
>>>> @@ -99,6 +167,8 @@ static struct quirk_entry quirk_asm200 = {
>>>> .hdmi_mux = 1,
>>>> .amplifier = 0,
>>>> .deepslp = 1,
>>>> + .thermal = false,
>>>> + .gmode = false,
>>>> };
>>>>
>>>> static struct quirk_entry quirk_asm201 = {
>>>> @@ -106,6 +176,17 @@ static struct quirk_entry quirk_asm201 = {
>>>> .hdmi_mux = 1,
>>>> .amplifier = 1,
>>>> .deepslp = 1,
>>>> + .thermal = false,
>>>> + .gmode = false,
>>>> +};
>>>> +
>>>> +static struct quirk_entry quirk_x_series = {
>>>> + .num_zones = 2,
>>>> + .hdmi_mux = 0,
>>>> + .amplifier = 0,
>>>> + .deepslp = 0,
>>>> + .thermal = true,
>>>> + .gmode = false,
>>>> };
>>> So now gmode is always false unless the module parameter from patch 4 is
>>> given?
>> G-Series laptops can also register a quirk_entry with gmode set in the
>> future. I can do it ahead of time, but I don't have a G-Series laptop to
>> test it.
> Understood. I'm fine with this in the current form if it's fine for Armin
> too.
I am fine with this, we can add the necessary quirks later should someone have access
to the necessary hardware.
Reviewed-by: Armin Wolf <W_Armin@gmx.de>
>
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH v10 3/5] alienware-wmi: added platform profile support
2024-10-29 23:11 ` Armin Wolf
@ 2024-10-30 0:02 ` Kurt Borja
0 siblings, 0 replies; 13+ messages in thread
From: Kurt Borja @ 2024-10-30 0:02 UTC (permalink / raw)
To: Armin Wolf; +Cc: Ilpo Järvinen, Hans de Goede, LKML, platform-driver-x86
On Wed, Oct 30, 2024 at 12:11:51AM +0100, Armin Wolf wrote:
> Am 29.10.24 um 18:06 schrieb Ilpo Järvinen:
>
> > On Tue, 29 Oct 2024, Kurt Borja wrote:
> >
> > > On Tue, Oct 29, 2024 at 05:44:05PM +0200, Ilpo Järvinen wrote:
> > > > On Tue, 29 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 interface is suspected to be used by
> > > > > Alienware Command Center (AWCC), which is not available for linux
> > > > > systems, to manage thermal profiles.
> > > > >
> > > > > This implementation makes use of three WMI methods, namely
> > > > > THERMAL_CONTROL, THERMAL_INFORMATION and GAME_SHIFT_STATUS, which take
> > > > > u32 as input and output arguments. Each method has a set of supported
> > > > > operations specified in their respective enums.
> > > > >
> > > > > Not all models with WMAX WMI interface support these methods. Because of
> > > > > this, models have to manually declare support through new quirks
> > > > > `thermal` for THERMAL_CONTROL and THERMAL_INFORMATION and `gmode` for
> > > > > GAME_SHIFT_STATUS.
> > > > >
> > > > > Wrappers written for these methods support multiple operations.
> > > > >
> > > > > THERMAL_CONTROL switches thermal modes through operation
> > > > > ACTIVATE_PROFILE. Available thermal codes are auto-detected at runtime
> > > > > and matched against a list of known thermal codes:
> > > > >
> > > > > Thermal Table "User Selectable Thermal Tables" (USTT):
> > > > > BALANCED 0xA0
> > > > > BALANCED_PERFORMANCE 0xA1
> > > > > COOL 0xA2
> > > > > QUIET 0xA3
> > > > > PERFORMANCE 0xA4
> > > > > LOW_POWER 0xA5
> > > > >
> > > > > Thermal Table Basic:
> > > > > QUIET 0x96
> > > > > BALANCED 0x97
> > > > > BALANCED_PERFORMANCE 0x98
> > > > > PERFORMANCE 0x99
> > > > >
> > > > > Devices are known to implement only one of these tables without mixing
> > > > > their thermal codes.
> > > > >
> > > > > The fact that the least significant digit of every thermal code is
> > > > > consecutive of one another is exploited to efficiently match codes
> > > > > through arrays.
> > > > >
> > > > > Autodetection of available codes is done through operation LIST_IDS of
> > > > > method THERMAL_INFORMATION. This operation lists fan IDs, CPU sensor ID,
> > > > > GPU sensor ID and available thermal profile codes, *in that order*. As
> > > > > number of fans and thermal codes is very model dependent, almost every
> > > > > ID is scanned and matched based on conditions found on
> > > > > is_wmax_thermal_code(). The known upper bound for the number of IDs is
> > > > > 13, corresponding to a device that have 4 fans, 2 sensors and 7 thermal
> > > > > codes.
> > > > >
> > > > > Additionally G-Series laptops have a key called G-key, which (with AWCC
> > > > > proprietary driver) switches the thermal mode to an special mode named
> > > > > GMODE with code 0xAB and changes Game Shift Status to 1. Game Shift is a
> > > > > mode the manufacturer claims, increases gaming performance.
> > > > >
> > > > > GAME_SHIFT_STATUS method is used to mimic this behavior when selecting
> > > > > PLATFORM_PROFILE_PERFORMANCE option.
> > > > >
> > > > > All of these profiles are known to only change fan speed profiles,
> > > > > although there are untested claims that some of them also change power
> > > > > profiles.
> > > > >
> > > > > Activating a thermal mode with method THERMAL_CONTROL may cause short
> > > > > hangs. This is a known problem present on every platform.
> > > > >
> > > > > Signed-off-by: Kurt Borja <kuurtb@gmail.com>
> > > > > Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
> > > > > ---
> > > > > v10:
> > > > > - Corrected THERMAL_MODE_BASIC_BALANCED -> THERMAL_MODE_BASIC_QUIET in
> > > > > is_wmax_thermal_code() logic
> > > > > - `thermal` and `gmode` quirks now have to be manually selected,
> > > > > because not all Dell devices posses the new WMI thermal methods.
> > > > > - Because of the above reason, errors in create_thermal_profile are now
> > > > > propagated
> > > > > v9:
> > > > > - Bool comparisons are now coherent with their type
> > > > > v8:
> > > > > - Fixed alignment in wmax_mode_to_platform_profile[]
> > > > > - Quirk thermal and gmode changed from u8 -> bool
> > > > > - Autodetected quirk entries are not initialized
> > > > > - is_wmax_thermal_code refactored to increase readibility
> > > > > - is_wmax_thermal_code now covers all possibilities
> > > > > - Better commit message
> > > > > v7:
> > > > > - Method operations are now clearly listed as separate enums
> > > > > - wmax_thermal_modes are now listed without codes in order to support
> > > > > autodetection, as well as getting and setting thermal profiles
> > > > > cleanly through arrays
> > > > > - Added wmax_mode_to_platform_profile[]
> > > > > - Added struct wmax_u32_args to replace bit mask approach of
> > > > > constructing arguments for wmax methods
> > > > > - create_thermal_profile now autodetects available thermal codes
> > > > > through operation 0x03 of THERMAL_INFORMATION method. These are
> > > > > codes are stored in supported_thermal_profiles[]
> > > > > - thermal_profile_get now uses wmax_mode_to_platform_profile[] instead of
> > > > > switch-case approach
> > > > > - thermal_profile_set now uses supported_thermal_profiles[] instead of
> > > > > switch-case approach
> > > > > - When gmode is autodetected, thermal_profile_set also sets Game Shift
> > > > > status accordingly
> > > > > v6:
> > > > > - Fixed alignment on some function definitions
> > > > > - Fixed braces on if statment
> > > > > - Removed quirk thermal_ustt
> > > > > - Now quirk thermal can take values defined in enum WMAX_THERMAL_TABLE.
> > > > > - Proper removal of thermal_profile
> > > > > ---
> > > > > drivers/platform/x86/dell/Kconfig | 1 +
> > > > > drivers/platform/x86/dell/alienware-wmi.c | 306 ++++++++++++++++++++++
> > > > > 2 files changed, 307 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..1d62c2ce7 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,13 @@
> > > > > #define WMAX_METHOD_AMPLIFIER_CABLE 0x6
> > > > > #define WMAX_METHOD_DEEP_SLEEP_CONTROL 0x0B
> > > > > #define WMAX_METHOD_DEEP_SLEEP_STATUS 0x0C
> > > > > +#define WMAX_METHOD_THERMAL_INFORMATION 0x14
> > > > > +#define WMAX_METHOD_THERMAL_CONTROL 0x15
> > > > > +#define WMAX_METHOD_GAME_SHIFT_STATUS 0x25
> > > > > +
> > > > > +#define WMAX_THERMAL_MODE_GMODE 0xAB
> > > > > +
> > > > > +#define WMAX_FAILURE_CODE 0xFFFFFFFF
> > > > >
> > > > > MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
> > > > > MODULE_DESCRIPTION("Alienware special feature control");
> > > > > @@ -49,11 +59,59 @@ enum WMAX_CONTROL_STATES {
> > > > > WMAX_SUSPEND = 3,
> > > > > };
> > > > >
> > > > > +enum WMAX_THERMAL_INFORMATION_OPERATIONS {
> > > > > + WMAX_OPERATION_LIST_IDS = 0x03,
> > > > > + WMAX_OPERATION_CURRENT_PROFILE = 0x0B,
> > > > > +};
> > > > > +
> > > > > +enum WMAX_THERMAL_CONTROL_OPERATIONS {
> > > > > + WMAX_OPERATION_ACTIVATE_PROFILE = 0x01,
> > > > > +};
> > > > > +
> > > > > +enum WMAX_GAME_SHIFT_STATUS_OPERATIONS {
> > > > > + WMAX_OPERATION_TOGGLE_GAME_SHIFT = 0x01,
> > > > > + WMAX_OPERATION_GET_GAME_SHIFT_STATUS = 0x02,
> > > > > +};
> > > > > +
> > > > > +enum WMAX_THERMAL_TABLES {
> > > > > + WMAX_THERMAL_TABLE_BASIC = 0x90,
> > > > > + WMAX_THERMAL_TABLE_USTT = 0xA0,
> > > > > +};
> > > > > +
> > > > > +enum wmax_thermal_mode {
> > > > > + THERMAL_MODE_USTT_BALANCED,
> > > > > + THERMAL_MODE_USTT_BALANCED_PERFORMANCE,
> > > > > + THERMAL_MODE_USTT_COOL,
> > > > > + THERMAL_MODE_USTT_QUIET,
> > > > > + THERMAL_MODE_USTT_PERFORMANCE,
> > > > > + THERMAL_MODE_USTT_LOW_POWER,
> > > > > + THERMAL_MODE_BASIC_QUIET,
> > > > > + THERMAL_MODE_BASIC_BALANCED,
> > > > > + THERMAL_MODE_BASIC_BALANCED_PERFORMANCE,
> > > > > + THERMAL_MODE_BASIC_PERFORMANCE,
> > > > > + THERMAL_MODE_LAST,
> > > > > +};
> > > > > +
> > > > > +static const enum platform_profile_option wmax_mode_to_platform_profile[THERMAL_MODE_LAST] = {
> > > > > + [THERMAL_MODE_USTT_BALANCED] = PLATFORM_PROFILE_BALANCED,
> > > > > + [THERMAL_MODE_USTT_BALANCED_PERFORMANCE] = PLATFORM_PROFILE_BALANCED_PERFORMANCE,
> > > > > + [THERMAL_MODE_USTT_COOL] = PLATFORM_PROFILE_COOL,
> > > > > + [THERMAL_MODE_USTT_QUIET] = PLATFORM_PROFILE_QUIET,
> > > > > + [THERMAL_MODE_USTT_PERFORMANCE] = PLATFORM_PROFILE_PERFORMANCE,
> > > > > + [THERMAL_MODE_USTT_LOW_POWER] = PLATFORM_PROFILE_LOW_POWER,
> > > > > + [THERMAL_MODE_BASIC_QUIET] = PLATFORM_PROFILE_QUIET,
> > > > > + [THERMAL_MODE_BASIC_BALANCED] = PLATFORM_PROFILE_BALANCED,
> > > > > + [THERMAL_MODE_BASIC_BALANCED_PERFORMANCE] = PLATFORM_PROFILE_BALANCED_PERFORMANCE,
> > > > > + [THERMAL_MODE_BASIC_PERFORMANCE] = PLATFORM_PROFILE_PERFORMANCE,
> > > > > +};
> > > > > +
> > > > > struct quirk_entry {
> > > > > u8 num_zones;
> > > > > u8 hdmi_mux;
> > > > > u8 amplifier;
> > > > > u8 deepslp;
> > > > > + bool thermal;
> > > > > + bool gmode;
> > > > > };
> > > > >
> > > > > static struct quirk_entry *quirks;
> > > > > @@ -64,6 +122,8 @@ static struct quirk_entry quirk_inspiron5675 = {
> > > > > .hdmi_mux = 0,
> > > > > .amplifier = 0,
> > > > > .deepslp = 0,
> > > > > + .thermal = false,
> > > > > + .gmode = false,
> > > > > };
> > > > >
> > > > > static struct quirk_entry quirk_unknown = {
> > > > > @@ -71,6 +131,8 @@ static struct quirk_entry quirk_unknown = {
> > > > > .hdmi_mux = 0,
> > > > > .amplifier = 0,
> > > > > .deepslp = 0,
> > > > > + .thermal = false,
> > > > > + .gmode = false,
> > > > > };
> > > > >
> > > > > static struct quirk_entry quirk_x51_r1_r2 = {
> > > > > @@ -78,6 +140,8 @@ static struct quirk_entry quirk_x51_r1_r2 = {
> > > > > .hdmi_mux = 0,
> > > > > .amplifier = 0,
> > > > > .deepslp = 0,
> > > > > + .thermal = false,
> > > > > + .gmode = false,
> > > > > };
> > > > >
> > > > > static struct quirk_entry quirk_x51_r3 = {
> > > > > @@ -85,6 +149,8 @@ static struct quirk_entry quirk_x51_r3 = {
> > > > > .hdmi_mux = 0,
> > > > > .amplifier = 1,
> > > > > .deepslp = 0,
> > > > > + .thermal = false,
> > > > > + .gmode = false,
> > > > > };
> > > > >
> > > > > static struct quirk_entry quirk_asm100 = {
> > > > > @@ -92,6 +158,8 @@ static struct quirk_entry quirk_asm100 = {
> > > > > .hdmi_mux = 1,
> > > > > .amplifier = 0,
> > > > > .deepslp = 0,
> > > > > + .thermal = false,
> > > > > + .gmode = false,
> > > > > };
> > > > >
> > > > > static struct quirk_entry quirk_asm200 = {
> > > > > @@ -99,6 +167,8 @@ static struct quirk_entry quirk_asm200 = {
> > > > > .hdmi_mux = 1,
> > > > > .amplifier = 0,
> > > > > .deepslp = 1,
> > > > > + .thermal = false,
> > > > > + .gmode = false,
> > > > > };
> > > > >
> > > > > static struct quirk_entry quirk_asm201 = {
> > > > > @@ -106,6 +176,17 @@ static struct quirk_entry quirk_asm201 = {
> > > > > .hdmi_mux = 1,
> > > > > .amplifier = 1,
> > > > > .deepslp = 1,
> > > > > + .thermal = false,
> > > > > + .gmode = false,
> > > > > +};
> > > > > +
> > > > > +static struct quirk_entry quirk_x_series = {
> > > > > + .num_zones = 2,
> > > > > + .hdmi_mux = 0,
> > > > > + .amplifier = 0,
> > > > > + .deepslp = 0,
> > > > > + .thermal = true,
> > > > > + .gmode = false,
> > > > > };
> > > > So now gmode is always false unless the module parameter from patch 4 is
> > > > given?
> > > G-Series laptops can also register a quirk_entry with gmode set in the
> > > future. I can do it ahead of time, but I don't have a G-Series laptop to
> > > test it.
> > Understood. I'm fine with this in the current form if it's fine for Armin
> > too.
>
> I am fine with this, we can add the necessary quirks later should someone have access
> to the necessary hardware.
I'm glad to hear that! Thank you both.
Kurt
>
> Reviewed-by: Armin Wolf <W_Armin@gmx.de>
>
> >
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v10 4/5] alienware-wmi: added force module parameters
2024-10-29 13:51 [PATCH v10 0/5] Dell AWCC platform_profile support Kurt Borja
` (2 preceding siblings ...)
2024-10-29 13:53 ` [PATCH v10 3/5] alienware-wmi: added platform profile support Kurt Borja
@ 2024-10-29 13:53 ` Kurt Borja
2024-10-29 23:13 ` Armin Wolf
2024-10-29 13:54 ` [PATCH v10 5/5] alienware-wmi: WMAX interface documentation Kurt Borja
4 siblings, 1 reply; 13+ messages in thread
From: Kurt Borja @ 2024-10-29 13:53 UTC (permalink / raw)
To: kuurtb; +Cc: W_Armin, hdegoede, ilpo.jarvinen, linux-kernel,
platform-driver-x86
Added force_platform_profile and force_gmode unsafe module parameters,
allowing users to force `thermal` and `gmode` quirks respectively.
Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
v10:
- Introduced
---
drivers/platform/x86/dell/alienware-wmi.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
index 1d62c2ce7..91f0e09d0 100644
--- a/drivers/platform/x86/dell/alienware-wmi.c
+++ b/drivers/platform/x86/dell/alienware-wmi.c
@@ -42,6 +42,14 @@ MODULE_LICENSE("GPL");
MODULE_ALIAS("wmi:" LEGACY_CONTROL_GUID);
MODULE_ALIAS("wmi:" WMAX_CONTROL_GUID);
+static bool force_platform_profile;
+module_param_unsafe(force_platform_profile, bool, 0);
+MODULE_PARM_DESC(force_platform_profile, "Forces auto-detecting thermal profiles without checking if WMI thermal backend is available");
+
+static bool force_gmode;
+module_param_unsafe(force_gmode, bool, 0);
+MODULE_PARM_DESC(force_gmode, "Forces G-Mode when performance profile is selected");
+
enum INTERFACE_FLAGS {
LEGACY,
WMAX,
@@ -1075,6 +1083,16 @@ static int __init alienware_wmi_init(void)
if (quirks == NULL)
quirks = &quirk_unknown;
+ if (force_platform_profile)
+ quirks->thermal = true;
+
+ if (force_gmode) {
+ if (quirks->thermal)
+ quirks->gmode = true;
+ else
+ pr_warn("alienware-wmi: force_gmode requieres platform profile support");
+ }
+
ret = platform_driver_register(&platform_driver);
if (ret)
goto fail_platform_driver;
--
2.47.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH v10 4/5] alienware-wmi: added force module parameters
2024-10-29 13:53 ` [PATCH v10 4/5] alienware-wmi: added force module parameters Kurt Borja
@ 2024-10-29 23:13 ` Armin Wolf
2024-10-30 0:03 ` Kurt Borja
0 siblings, 1 reply; 13+ messages in thread
From: Armin Wolf @ 2024-10-29 23:13 UTC (permalink / raw)
To: Kurt Borja; +Cc: hdegoede, ilpo.jarvinen, linux-kernel, platform-driver-x86
Am 29.10.24 um 14:53 schrieb Kurt Borja:
> Added force_platform_profile and force_gmode unsafe module parameters,
> allowing users to force `thermal` and `gmode` quirks respectively.
>
> Signed-off-by: Kurt Borja <kuurtb@gmail.com>
> ---
> v10:
> - Introduced
> ---
> drivers/platform/x86/dell/alienware-wmi.c | 18 ++++++++++++++++++
> 1 file changed, 18 insertions(+)
>
> diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
> index 1d62c2ce7..91f0e09d0 100644
> --- a/drivers/platform/x86/dell/alienware-wmi.c
> +++ b/drivers/platform/x86/dell/alienware-wmi.c
> @@ -42,6 +42,14 @@ MODULE_LICENSE("GPL");
> MODULE_ALIAS("wmi:" LEGACY_CONTROL_GUID);
> MODULE_ALIAS("wmi:" WMAX_CONTROL_GUID);
>
> +static bool force_platform_profile;
> +module_param_unsafe(force_platform_profile, bool, 0);
> +MODULE_PARM_DESC(force_platform_profile, "Forces auto-detecting thermal profiles without checking if WMI thermal backend is available");
> +
> +static bool force_gmode;
> +module_param_unsafe(force_gmode, bool, 0);
> +MODULE_PARM_DESC(force_gmode, "Forces G-Mode when performance profile is selected");
> +
> enum INTERFACE_FLAGS {
> LEGACY,
> WMAX,
> @@ -1075,6 +1083,16 @@ static int __init alienware_wmi_init(void)
> if (quirks == NULL)
> quirks = &quirk_unknown;
>
> + if (force_platform_profile)
> + quirks->thermal = true;
> +
> + if (force_gmode) {
> + if (quirks->thermal)
> + quirks->gmode = true;
> + else
> + pr_warn("alienware-wmi: force_gmode requieres platform profile support");
Please drop the "alienware-wmi:" prefix, it should get added automatically.
With that being fixed:
Reviewed-by: Armin Wolf <W_Armin@gmx.de>
> + }
> +
> ret = platform_driver_register(&platform_driver);
> if (ret)
> goto fail_platform_driver;
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH v10 4/5] alienware-wmi: added force module parameters
2024-10-29 23:13 ` Armin Wolf
@ 2024-10-30 0:03 ` Kurt Borja
0 siblings, 0 replies; 13+ messages in thread
From: Kurt Borja @ 2024-10-30 0:03 UTC (permalink / raw)
To: Armin Wolf; +Cc: hdegoede, ilpo.jarvinen, linux-kernel, platform-driver-x86
On Wed, Oct 30, 2024 at 12:13:45AM +0100, Armin Wolf wrote:
> Am 29.10.24 um 14:53 schrieb Kurt Borja:
>
> > Added force_platform_profile and force_gmode unsafe module parameters,
> > allowing users to force `thermal` and `gmode` quirks respectively.
> >
> > Signed-off-by: Kurt Borja <kuurtb@gmail.com>
> > ---
> > v10:
> > - Introduced
> > ---
> > drivers/platform/x86/dell/alienware-wmi.c | 18 ++++++++++++++++++
> > 1 file changed, 18 insertions(+)
> >
> > diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
> > index 1d62c2ce7..91f0e09d0 100644
> > --- a/drivers/platform/x86/dell/alienware-wmi.c
> > +++ b/drivers/platform/x86/dell/alienware-wmi.c
> > @@ -42,6 +42,14 @@ MODULE_LICENSE("GPL");
> > MODULE_ALIAS("wmi:" LEGACY_CONTROL_GUID);
> > MODULE_ALIAS("wmi:" WMAX_CONTROL_GUID);
> >
> > +static bool force_platform_profile;
> > +module_param_unsafe(force_platform_profile, bool, 0);
> > +MODULE_PARM_DESC(force_platform_profile, "Forces auto-detecting thermal profiles without checking if WMI thermal backend is available");
> > +
> > +static bool force_gmode;
> > +module_param_unsafe(force_gmode, bool, 0);
> > +MODULE_PARM_DESC(force_gmode, "Forces G-Mode when performance profile is selected");
> > +
> > enum INTERFACE_FLAGS {
> > LEGACY,
> > WMAX,
> > @@ -1075,6 +1083,16 @@ static int __init alienware_wmi_init(void)
> > if (quirks == NULL)
> > quirks = &quirk_unknown;
> >
> > + if (force_platform_profile)
> > + quirks->thermal = true;
> > +
> > + if (force_gmode) {
> > + if (quirks->thermal)
> > + quirks->gmode = true;
> > + else
> > + pr_warn("alienware-wmi: force_gmode requieres platform profile support");
>
> Please drop the "alienware-wmi:" prefix, it should get added automatically.
Right away.
>
> With that being fixed:
>
> Reviewed-by: Armin Wolf <W_Armin@gmx.de>
Thank you for your review!
Kurt
>
> > + }
> > +
> > ret = platform_driver_register(&platform_driver);
> > if (ret)
> > goto fail_platform_driver;
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v10 5/5] alienware-wmi: WMAX interface documentation
2024-10-29 13:51 [PATCH v10 0/5] Dell AWCC platform_profile support Kurt Borja
` (3 preceding siblings ...)
2024-10-29 13:53 ` [PATCH v10 4/5] alienware-wmi: added force module parameters Kurt Borja
@ 2024-10-29 13:54 ` Kurt Borja
4 siblings, 0 replies; 13+ messages in thread
From: Kurt Borja @ 2024-10-29 13:54 UTC (permalink / raw)
To: kuurtb; +Cc: W_Armin, hdegoede, ilpo.jarvinen, linux-kernel,
platform-driver-x86
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>
Reviewed-by: Armin Wolf <W_Armin@gmx.de>
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
---
v10:
- Unchanged
v9:
- Unchanged
v8:
- Unchanged
v7:
- Added GameShiftStatus method to documentation
- Added remark about operation 0x03 of Thermal_Information method
- Removed undocumented methods
v6:
- Fixed typos
- Included new file in MAINTAINERS
---
Documentation/wmi/devices/alienware-wmi.rst | 388 ++++++++++++++++++++
MAINTAINERS | 1 +
2 files changed, 389 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..36a67ff9a
--- /dev/null
+++ b/Documentation/wmi/devices/alienware-wmi.rst
@@ -0,0 +1,388 @@
+.. 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 be incomplete and some operations have
+ different implementations between devices.
+
+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(37), Implemented, read, write, Description("Game Shift Status.")] void GameShiftStatus([in] uint32 arg2, [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.
+
+Methods not described in the following document have unknown behavior.
+
+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
+
+Operation 0x03 list all available fan IDs, sensor IDs and thermal profile
+codes in order, but different models may have different number of fans and
+thermal profiles. These are the known ranges:
+
+* Fan IDs: from 2 up to 4
+* Sensor IDs: 2
+* Thermal profile codes: from 1 up to 7
+
+In total BYTE_1(ARG2) may range from 0x5 up to 0xD depending on the model.
+
+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
+
+ BALANCED_USTT 0xA0
+ BALANCED_PERFORMANCE_USTT 0xA1
+ COOL_USTT 0xA2
+ QUIET_USTT 0xA3
+ PERFORMANCE_USTT 0xA4
+ LOW_POWER_USTT 0xA5
+
+ QUIET 0x96
+ BALANCED 0x97
+ BALANCED_PERFORMANCE 0x98
+ PERFORMANCE 0x99
+
+ 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 GameShiftStatus([in] uint32 arg2, [out] uint32 argr)
+---------------------------------------------------------------
+
+::
+
+ if BYTE_0(arg2) == 0x1:
+ TOGGLE_GAME_SHIFT()
+ argr = GET_GAME_SHIFT_STATUS()
+
+ if BYTE_0(arg2) == 0x2:
+ argr = GET_GAME_SHIFT_STATUS()
+
+Game Shift Status does not change the fan speed profile but it could be some
+sort of CPU/GPU power profile. Benchmarks have not been done.
+
+This method is only present on Dell's G-Series laptops and it's implementation
+implies GMODE thermal profile is available, even if operation 0x03 of
+Thermal_Information does not list it.
+
+G-key on Dell's G-Series laptops also changes Game Shift status, so both are
+directly related.
+
+WMI method GetFanSensors([in] uint32 arg2, [out] uint32 argr)
+-------------------------------------------------------------
+
+::
+
+ if BYTE_0(arg2) == 0x1:
+ if is_valid_fan(BYTE_1(arg2)):
+ argr = 1
+ else:
+ argr = 0
+
+ if BYTE_0(arg2) == 0x2:
+ 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 ReadChassisColor([out] uint32 argr)
+----------------------------------------------
+
+::
+
+ argr = CHASSIS_COLOR_ID
+
+Acknowledgements
+================
+
+Kudos to `AlexIII <https://github.com/AlexIII/tcc-g15>`_ for documenting
+and testing available thermal profile codes.
+
diff --git a/MAINTAINERS b/MAINTAINERS
index c27f31907..25f6de4c2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -792,6 +792,7 @@ F: drivers/perf/alibaba_uncore_drw_pmu.c
ALIENWARE WMI DRIVER
L: Dell.Client.Kernel@dell.com
S: Maintained
+F: Documentation/wmi/devices/alienware-wmi.rst
F: drivers/platform/x86/dell/alienware-wmi.c
ALLEGRO DVT VIDEO IP CORE DRIVER
--
2.47.0
^ permalink raw reply related [flat|nested] 13+ messages in thread