drivers/platform/x86/dell/Kconfig | 9 + drivers/platform/x86/dell/Makefile | 1 + drivers/platform/x86/dell/dell-wmi-awcc.c | 204 ++++++++++++++++++++++ 3 files changed, 214 insertions(+) create mode 100644 drivers/platform/x86/dell/dell-wmi-awcc.c
This patch adds platform_profile support for Dell devices which implement
User Selectable Thermal Tables (USTT) that are meant to be controlled by
Alienware Command Center (AWCC). These devices may include newer Alienware
M-Series, Alienware X-Series and Dell's G-Series. This patch, was tested
by me on an Alienware x15 R1.
It is suspected that Alienware Command Center manages thermal profiles
through the WMI interface, specifically through a device with identifier
\_SB_.AMW1.WMAX. This device was reverse engineered and the relevant
functionality is documented here [1]. This driver interacts with this
WMI device and thus is able to mimic AWCC's thermal profiles functionality
through the platform_profile API. In consequence the user would be able
to set and retrieve thermal profiles, which are just fan speed profiles.
This driver was heavily inspired on inspur_platform_profile, special
thanks.
Notes:
- Performance (FullSpeed) profile is a special profile which has it's own
entry in the Firmware Settings of the Alienware x15 R1. It also changes
the color of the F1 key. I suspect this behavior would be replicated in
other X-Series or M-Series laptops.
- G-Mode is a profile documented on [1] which mimics the behavior of
FullSpeed mode but it does not have an entry on the Firmware Settings of
the Alienware x15 R1, this may correspond to the G-Mode functionality on
G-Series laptops (activated by a special button) but I cannot test it. I
did not include this code in the driver as G-Mode causes unexpected
behavior on X-Series laptops.
Thanks for your time and patiente in advance.
Regards,
Kurt
[1] https://gist.github.com/kuu-rt/b22328ff2b454be505387e2a38c61ee4
Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
drivers/platform/x86/dell/Kconfig | 9 +
drivers/platform/x86/dell/Makefile | 1 +
drivers/platform/x86/dell/dell-wmi-awcc.c | 204 ++++++++++++++++++++++
3 files changed, 214 insertions(+)
create mode 100644 drivers/platform/x86/dell/dell-wmi-awcc.c
diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig
index 68a49788a..20300ff98 100644
--- a/drivers/platform/x86/dell/Kconfig
+++ b/drivers/platform/x86/dell/Kconfig
@@ -27,6 +27,15 @@ config ALIENWARE_WMI
zones on Alienware machines that don't contain a dedicated AlienFX
USB MCU such as the X51 and X51-R2.
+config AWCC_PLATFORM_PROFILE
+ tristate "AWCC Platform Profile support"
+ depends on ACPI_WMI
+ select ACPI_PLATFORM_PROFILE
+ help
+ This driver provides platform_profile support for selecting thermal
+ profiles on Dell devices with User Selectable Thermal Tables,
+ controlled by AWCC's WMI interface.
+
config DCDBAS
tristate "Dell Systems Management Base Driver"
default m
diff --git a/drivers/platform/x86/dell/Makefile b/drivers/platform/x86/dell/Makefile
index 79d60f1bf..bfef99580 100644
--- a/drivers/platform/x86/dell/Makefile
+++ b/drivers/platform/x86/dell/Makefile
@@ -23,4 +23,5 @@ obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o
obj-$(CONFIG_DELL_WMI_DESCRIPTOR) += dell-wmi-descriptor.o
obj-$(CONFIG_DELL_WMI_DDV) += dell-wmi-ddv.o
obj-$(CONFIG_DELL_WMI_LED) += dell-wmi-led.o
+obj-$(CONFIG_AWCC_PLATFORM_PROFILE) += dell-wmi-awcc.o
obj-$(CONFIG_DELL_WMI_SYSMAN) += dell-wmi-sysman/
diff --git a/drivers/platform/x86/dell/dell-wmi-awcc.c b/drivers/platform/x86/dell/dell-wmi-awcc.c
new file mode 100644
index 000000000..0837d1bc6
--- /dev/null
+++ b/drivers/platform/x86/dell/dell-wmi-awcc.c
@@ -0,0 +1,204 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * WMI driver for Dell's AWCC platform_profile
+ *
+ * Copyright (c) Kurt Borja <kuurtb@gmail.com>
+ *
+ */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/platform_profile.h>
+#include <linux/wmi.h>
+
+#define PROF_TO_ARG(mode) ((mode << 8) | 1)
+
+#define DELL_AWCC_GUID "A70591CE-A997-11DA-B012-B622A1EF5492"
+
+enum awcc_wmi_method {
+ AWCC_WMI_THERMAL_INFORMATION = 0x14,
+ AWCC_WMI_THERMAL_CONTROL = 0x15,
+};
+
+enum awcc_tmp_profile {
+ AWCC_TMP_PROFILE_BALANCED = 0xA0,
+ AWCC_TMP_PROFILE_BALANCED_PERFORMANCE = 0xA1,
+ AWCC_TMP_PROFILE_COOL = 0xA2,
+ AWCC_TMP_PROFILE_QUIET = 0xA3,
+ AWCC_TMP_PROFILE_PERFORMANCE = 0xA4,
+ AWCC_TMP_PROFILE_LOW_POWER = 0xA5,
+};
+
+struct awcc_wmi_priv {
+ struct wmi_device *wdev;
+ struct platform_profile_handler handler;
+};
+
+static int awcc_wmi_query(struct wmi_device *wdev, enum awcc_wmi_method method,
+ u32 arg, u32 *res)
+{
+ struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
+ const struct acpi_buffer in = { sizeof(arg), &arg };
+ union acpi_object *obj;
+ acpi_status status;
+ int ret = 0;
+
+ status = wmidev_evaluate_method(wdev, 0x0, method, &in, &out);
+
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ obj = out.pointer;
+ if (!obj)
+ return -ENODATA;
+
+ if (obj->type != ACPI_TYPE_INTEGER) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+
+ if (obj->integer.value <= U32_MAX)
+ *res = (u32)obj->integer.value;
+ else
+ ret = -ERANGE;
+
+out_free:
+ kfree(obj);
+
+ return ret;
+}
+
+static int awcc_platform_profile_get(struct platform_profile_handler *pprof,
+ enum platform_profile_option *profile)
+{
+ struct awcc_wmi_priv *priv =
+ container_of(pprof, struct awcc_wmi_priv, handler);
+
+ u32 res;
+ int ret;
+
+ ret = awcc_wmi_query(priv->wdev, AWCC_WMI_THERMAL_INFORMATION, 0x0B,
+ &res);
+
+ if (ret < 0)
+ return ret;
+
+ if (res < 0)
+ return -EBADRQC;
+
+ switch (res) {
+ case AWCC_TMP_PROFILE_LOW_POWER:
+ *profile = PLATFORM_PROFILE_LOW_POWER;
+ break;
+ case AWCC_TMP_PROFILE_QUIET:
+ *profile = PLATFORM_PROFILE_QUIET;
+ break;
+ case AWCC_TMP_PROFILE_BALANCED:
+ *profile = PLATFORM_PROFILE_BALANCED;
+ break;
+ case AWCC_TMP_PROFILE_BALANCED_PERFORMANCE:
+ *profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE;
+ break;
+ case AWCC_TMP_PROFILE_PERFORMANCE:
+ *profile = PLATFORM_PROFILE_PERFORMANCE;
+ break;
+ default:
+ return -ENODATA;
+ }
+
+ return 0;
+}
+
+static int awcc_platform_profile_set(struct platform_profile_handler *pprof,
+ enum platform_profile_option profile)
+{
+ struct awcc_wmi_priv *priv =
+ container_of(pprof, struct awcc_wmi_priv, handler);
+
+ u32 arg;
+ u32 res;
+ int ret;
+
+ switch (profile) {
+ case PLATFORM_PROFILE_LOW_POWER:
+ arg = PROF_TO_ARG(AWCC_TMP_PROFILE_LOW_POWER);
+ break;
+ case PLATFORM_PROFILE_QUIET:
+ arg = PROF_TO_ARG(AWCC_TMP_PROFILE_QUIET);
+ break;
+ case PLATFORM_PROFILE_BALANCED:
+ arg = PROF_TO_ARG(AWCC_TMP_PROFILE_BALANCED);
+ break;
+ case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
+ arg = PROF_TO_ARG(AWCC_TMP_PROFILE_BALANCED_PERFORMANCE);
+ break;
+ case PLATFORM_PROFILE_PERFORMANCE:
+ arg = PROF_TO_ARG(AWCC_TMP_PROFILE_PERFORMANCE);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ ret = awcc_wmi_query(priv->wdev, AWCC_WMI_THERMAL_CONTROL, arg, &res);
+
+ if (ret < 0)
+ return ret;
+
+ if (res < 0)
+ return -EBADRQC;
+
+ return 0;
+}
+
+static int awcc_wmi_probe(struct wmi_device *wdev, const void *context)
+{
+ struct awcc_wmi_priv *priv;
+
+ priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->wdev = wdev;
+ dev_set_drvdata(&wdev->dev, priv);
+
+ priv->handler.profile_set = awcc_platform_profile_set;
+ priv->handler.profile_get = awcc_platform_profile_get;
+
+ set_bit(PLATFORM_PROFILE_LOW_POWER, priv->handler.choices);
+ set_bit(PLATFORM_PROFILE_QUIET, priv->handler.choices);
+ set_bit(PLATFORM_PROFILE_BALANCED, priv->handler.choices);
+ set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, priv->handler.choices);
+ set_bit(PLATFORM_PROFILE_PERFORMANCE, priv->handler.choices);
+
+ return platform_profile_register(&priv->handler);
+}
+
+static void awcc_wmi_remove(struct wmi_device *wdev)
+{
+ platform_profile_remove();
+}
+
+static const struct wmi_device_id awcc_wmi_id_table[] = {
+ { .guid_string = DELL_AWCC_GUID },
+ {},
+};
+
+MODULE_DEVICE_TABLE(wmi, awcc_wmi_id_table);
+
+static struct wmi_driver awcc_wmi_driver = {
+ .driver = {
+ .name = "dell-wmi-awcc-platform-profile",
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ },
+ .id_table = awcc_wmi_id_table,
+ .probe = awcc_wmi_probe,
+ .remove = awcc_wmi_remove,
+ .no_singleton = true,
+};
+
+module_wmi_driver(awcc_wmi_driver);
+
+MODULE_AUTHOR("Kurt Borja");
+MODULE_DESCRIPTION("Dell AWCC WMI driver");
+MODULE_LICENSE("GPL");
--
2.46.2
This patch adds platform_profile support for Dell devices which implement User Selectable Thermal Tables (USTT) that are meant to be controlled by Alienware Command Center (AWCC). These devices may include newer Alienware M-Series, Alienware X-Series and Dell's G-Series. This patch, was tested by me on an Alienware x15 R1. --- v4: - Fixed indentation on previous code - Removed unnecessary (acpi_size) and (u32 *) casts - Return -EIO on ACPI_FAILURE - Appropiate prefixes given to macros - 0xFFFFFFFF named WMAX_FAILURE_CODE - Added support for a new set of thermal codes. Old ones now have USTT in their names - A new quirk has been added to differantiate between the two sets. thermal and thermal_ustt are mutually exclusive - Added documentation for WMAX interface v3: - Removed extra empty line - 0x0B named WMAX_ARG_GET_CURRENT_PROF - Removed casts to the same type on functions added in this patch - Thermal profile to WMAX argument is now an static function and makes use of in-built kernel macros - Platform profile is now removed only if it was created first - create_platform_profile is now create_thermal_profile to avoid confusion - profile_get and profile_set functions renamed too to match the above v2: - Moved functionality to alienware-wmi driver - Added thermal and gmode quirks to add support based on dmi match - Performance profile is now GMODE for devices that support it - alienware_wmax_command now is insize agnostic to support new thermal methods Kurt Borja (4): alienware-wmi: fixed indentation and clean up alienware-wmi: alienware_wmax_command() is now input size agnostic alienware-wmi: added platform profile support alienware-wmi: WMAX interface documentation Documentation/wmi/devices/alienware-wmi.rst | 364 ++++++++++++++++++ drivers/platform/x86/dell/Kconfig | 1 + drivers/platform/x86/dell/alienware-wmi.c | 389 ++++++++++++++++---- 3 files changed, 678 insertions(+), 76 deletions(-) create mode 100644 Documentation/wmi/devices/alienware-wmi.rst -- 2.47.0
This patch adds platform_profile support for Dell devices which implement User Selectable Thermal Tables (USTT) that are meant to be controlled by Alienware Command Center (AWCC). These devices may include newer Alienware M-Series, Alienware X-Series and Dell's G-Series. This patch, was tested by me on an Alienware x15 R1. --- v4: - Fixed indentation on previous code - Removed unnecessary (acpi_size) and (u32 *) casts - Return -EIO on ACPI_FAILURE - Appropiate prefixes given to macros - 0xFFFFFFFF named WMAX_FAILURE_CODE - Added support for a new set of thermal codes. Old ones now have USTT in their names - A new quirk has been added to differantiate between the two sets. thermal and thermal_ustt are mutually exclusive - Added documentation for WMAX interface v3: - Removed extra empty line - 0x0B named WMAX_ARG_GET_CURRENT_PROF - Removed casts to the same type on functions added in this patch - Thermal profile to WMAX argument is now an static function and makes use of in-built kernel macros - Platform profile is now removed only if it was created first - create_platform_profile is now create_thermal_profile to avoid confusion - profile_get and profile_set functions renamed too to match the above v2: - Moved functionality to alienware-wmi driver - Added thermal and gmode quirks to add support based on dmi match - Performance profile is now GMODE for devices that support it - alienware_wmax_command now is insize agnostic to support new thermal methods Kurt Borja (4): alienware-wmi: fixed indentation and clean up alienware-wmi: alienware_wmax_command() is now input size agnostic alienware-wmi: added platform profile support alienware-wmi: WMAX interface documentation Documentation/wmi/devices/alienware-wmi.rst | 364 ++++++++++++++++++ drivers/platform/x86/dell/Kconfig | 1 + drivers/platform/x86/dell/alienware-wmi.c | 389 ++++++++++++++++---- 3 files changed, 678 insertions(+), 76 deletions(-) create mode 100644 Documentation/wmi/devices/alienware-wmi.rst -- 2.47.0
Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
drivers/platform/x86/dell/alienware-wmi.c | 134 +++++++++++-----------
1 file changed, 67 insertions(+), 67 deletions(-)
diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
index f5ee62ce1..16a3fe9ac 100644
--- a/drivers/platform/x86/dell/alienware-wmi.c
+++ b/drivers/platform/x86/dell/alienware-wmi.c
@@ -116,68 +116,68 @@ static int __init dmi_matched(const struct dmi_system_id *dmi)
static const struct dmi_system_id alienware_quirks[] __initconst = {
{
- .callback = dmi_matched,
- .ident = "Alienware X51 R3",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
- DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"),
- },
- .driver_data = &quirk_x51_r3,
- },
+ .callback = dmi_matched,
+ .ident = "Alienware X51 R3",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"),
+ },
+ .driver_data = &quirk_x51_r3,
+ },
{
- .callback = dmi_matched,
- .ident = "Alienware X51 R2",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
- DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"),
- },
- .driver_data = &quirk_x51_r1_r2,
- },
+ .callback = dmi_matched,
+ .ident = "Alienware X51 R2",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"),
+ },
+ .driver_data = &quirk_x51_r1_r2,
+ },
{
- .callback = dmi_matched,
- .ident = "Alienware X51 R1",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
- DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"),
- },
- .driver_data = &quirk_x51_r1_r2,
- },
+ .callback = dmi_matched,
+ .ident = "Alienware X51 R1",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"),
+ },
+ .driver_data = &quirk_x51_r1_r2,
+ },
{
- .callback = dmi_matched,
- .ident = "Alienware ASM100",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
- DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"),
- },
- .driver_data = &quirk_asm100,
- },
+ .callback = dmi_matched,
+ .ident = "Alienware ASM100",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"),
+ },
+ .driver_data = &quirk_asm100,
+ },
{
- .callback = dmi_matched,
- .ident = "Alienware ASM200",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
- DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"),
- },
- .driver_data = &quirk_asm200,
- },
+ .callback = dmi_matched,
+ .ident = "Alienware ASM200",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"),
+ },
+ .driver_data = &quirk_asm200,
+ },
{
- .callback = dmi_matched,
- .ident = "Alienware ASM201",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
- DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"),
- },
- .driver_data = &quirk_asm201,
- },
- {
- .callback = dmi_matched,
- .ident = "Dell Inc. Inspiron 5675",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"),
- },
- .driver_data = &quirk_inspiron5675,
- },
+ .callback = dmi_matched,
+ .ident = "Alienware ASM201",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"),
+ },
+ .driver_data = &quirk_asm201,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Dell Inc. Inspiron 5675",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"),
+ },
+ .driver_data = &quirk_inspiron5675,
+ },
{}
};
@@ -221,8 +221,8 @@ static struct platform_zone *zone_data;
static struct platform_driver platform_driver = {
.driver = {
- .name = "alienware-wmi",
- }
+ .name = "alienware-wmi",
+ }
};
static struct attribute_group zone_attribute_group = {
@@ -292,7 +292,7 @@ static int alienware_update_led(struct platform_zone *zone)
guid = WMAX_CONTROL_GUID;
method_id = WMAX_METHOD_ZONE_CONTROL;
- input.length = (acpi_size) sizeof(wmax_basic_args);
+ input.length = sizeof(wmax_basic_args);
input.pointer = &wmax_basic_args;
} else {
legacy_args.colors = zone->colors;
@@ -306,7 +306,7 @@ static int alienware_update_led(struct platform_zone *zone)
guid = LEGACY_CONTROL_GUID;
method_id = zone->location + 1;
- input.length = (acpi_size) sizeof(legacy_args);
+ input.length = sizeof(legacy_args);
input.pointer = &legacy_args;
}
pr_debug("alienware-wmi: guid %s method %d\n", guid, method_id);
@@ -358,7 +358,7 @@ static int wmax_brightness(int brightness)
.led_mask = 0xFF,
.percentage = brightness,
};
- input.length = (acpi_size) sizeof(args);
+ input.length = sizeof(args);
input.pointer = &args;
status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0,
WMAX_METHOD_BRIGHTNESS, &input, NULL);
@@ -508,7 +508,7 @@ static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
struct acpi_buffer input;
struct acpi_buffer output;
- input.length = (acpi_size) sizeof(*in_args);
+ input.length = sizeof(*in_args);
input.pointer = in_args;
if (out_data) {
output.length = ACPI_ALLOCATE_BUFFER;
@@ -542,7 +542,7 @@ static ssize_t show_hdmi_cable(struct device *dev,
};
status =
alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE,
- (u32 *) &out_data);
+ &out_data);
if (ACPI_SUCCESS(status)) {
if (out_data == 0)
return sysfs_emit(buf, "[unconnected] connected unknown\n");
@@ -563,7 +563,7 @@ static ssize_t show_hdmi_source(struct device *dev,
};
status =
alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS,
- (u32 *) &out_data);
+ &out_data);
if (ACPI_SUCCESS(status)) {
if (out_data == 1)
@@ -643,7 +643,7 @@ static ssize_t show_amplifier_status(struct device *dev,
};
status =
alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE,
- (u32 *) &out_data);
+ &out_data);
if (ACPI_SUCCESS(status)) {
if (out_data == 0)
return sysfs_emit(buf, "[unconnected] connected unknown\n");
@@ -695,7 +695,7 @@ static ssize_t show_deepsleep_status(struct device *dev,
.arg = 0,
};
status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS,
- (u32 *) &out_data);
+ &out_data);
if (ACPI_SUCCESS(status)) {
if (out_data == 0)
return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
--
2.47.0
On Fri, 11 Oct 2024, Kurt Borja wrote: All patches should provide a description of the change in the commit message body here (the shortlog in the subject is not enough). So please resubmit with the descriptions added to all patches. The code changes themselves looked very good, thanks for doing this. -- i. > Signed-off-by: Kurt Borja <kuurtb@gmail.com> > --- > drivers/platform/x86/dell/alienware-wmi.c | 134 +++++++++++----------- > 1 file changed, 67 insertions(+), 67 deletions(-) > > diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c > index f5ee62ce1..16a3fe9ac 100644 > --- a/drivers/platform/x86/dell/alienware-wmi.c > +++ b/drivers/platform/x86/dell/alienware-wmi.c > @@ -116,68 +116,68 @@ static int __init dmi_matched(const struct dmi_system_id *dmi) > > static const struct dmi_system_id alienware_quirks[] __initconst = { > { > - .callback = dmi_matched, > - .ident = "Alienware X51 R3", > - .matches = { > - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > - DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"), > - }, > - .driver_data = &quirk_x51_r3, > - }, > + .callback = dmi_matched, > + .ident = "Alienware X51 R3", > + .matches = { > + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"), > + }, > + .driver_data = &quirk_x51_r3, > + }, > { > - .callback = dmi_matched, > - .ident = "Alienware X51 R2", > - .matches = { > - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > - DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"), > - }, > - .driver_data = &quirk_x51_r1_r2, > - }, > + .callback = dmi_matched, > + .ident = "Alienware X51 R2", > + .matches = { > + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"), > + }, > + .driver_data = &quirk_x51_r1_r2, > + }, > { > - .callback = dmi_matched, > - .ident = "Alienware X51 R1", > - .matches = { > - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > - DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"), > - }, > - .driver_data = &quirk_x51_r1_r2, > - }, > + .callback = dmi_matched, > + .ident = "Alienware X51 R1", > + .matches = { > + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"), > + }, > + .driver_data = &quirk_x51_r1_r2, > + }, > { > - .callback = dmi_matched, > - .ident = "Alienware ASM100", > - .matches = { > - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > - DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"), > - }, > - .driver_data = &quirk_asm100, > - }, > + .callback = dmi_matched, > + .ident = "Alienware ASM100", > + .matches = { > + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > + DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"), > + }, > + .driver_data = &quirk_asm100, > + }, > { > - .callback = dmi_matched, > - .ident = "Alienware ASM200", > - .matches = { > - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > - DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"), > - }, > - .driver_data = &quirk_asm200, > - }, > + .callback = dmi_matched, > + .ident = "Alienware ASM200", > + .matches = { > + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > + DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"), > + }, > + .driver_data = &quirk_asm200, > + }, > { > - .callback = dmi_matched, > - .ident = "Alienware ASM201", > - .matches = { > - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > - DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"), > - }, > - .driver_data = &quirk_asm201, > - }, > - { > - .callback = dmi_matched, > - .ident = "Dell Inc. Inspiron 5675", > - .matches = { > - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), > - DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"), > - }, > - .driver_data = &quirk_inspiron5675, > - }, > + .callback = dmi_matched, > + .ident = "Alienware ASM201", > + .matches = { > + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > + DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"), > + }, > + .driver_data = &quirk_asm201, > + }, > + { > + .callback = dmi_matched, > + .ident = "Dell Inc. Inspiron 5675", > + .matches = { > + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), > + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"), > + }, > + .driver_data = &quirk_inspiron5675, > + }, > {} > }; > > @@ -221,8 +221,8 @@ static struct platform_zone *zone_data; > > static struct platform_driver platform_driver = { > .driver = { > - .name = "alienware-wmi", > - } > + .name = "alienware-wmi", > + } > }; > > static struct attribute_group zone_attribute_group = { > @@ -292,7 +292,7 @@ static int alienware_update_led(struct platform_zone *zone) > guid = WMAX_CONTROL_GUID; > method_id = WMAX_METHOD_ZONE_CONTROL; > > - input.length = (acpi_size) sizeof(wmax_basic_args); > + input.length = sizeof(wmax_basic_args); > input.pointer = &wmax_basic_args; > } else { > legacy_args.colors = zone->colors; > @@ -306,7 +306,7 @@ static int alienware_update_led(struct platform_zone *zone) > guid = LEGACY_CONTROL_GUID; > method_id = zone->location + 1; > > - input.length = (acpi_size) sizeof(legacy_args); > + input.length = sizeof(legacy_args); > input.pointer = &legacy_args; > } > pr_debug("alienware-wmi: guid %s method %d\n", guid, method_id); > @@ -358,7 +358,7 @@ static int wmax_brightness(int brightness) > .led_mask = 0xFF, > .percentage = brightness, > }; > - input.length = (acpi_size) sizeof(args); > + input.length = sizeof(args); > input.pointer = &args; > status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0, > WMAX_METHOD_BRIGHTNESS, &input, NULL); > @@ -508,7 +508,7 @@ static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args, > struct acpi_buffer input; > struct acpi_buffer output; > > - input.length = (acpi_size) sizeof(*in_args); > + input.length = sizeof(*in_args); > input.pointer = in_args; > if (out_data) { > output.length = ACPI_ALLOCATE_BUFFER; > @@ -542,7 +542,7 @@ static ssize_t show_hdmi_cable(struct device *dev, > }; > status = > alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE, > - (u32 *) &out_data); > + &out_data); > if (ACPI_SUCCESS(status)) { > if (out_data == 0) > return sysfs_emit(buf, "[unconnected] connected unknown\n"); > @@ -563,7 +563,7 @@ static ssize_t show_hdmi_source(struct device *dev, > }; > status = > alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS, > - (u32 *) &out_data); > + &out_data); > > if (ACPI_SUCCESS(status)) { > if (out_data == 1) > @@ -643,7 +643,7 @@ static ssize_t show_amplifier_status(struct device *dev, > }; > status = > alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE, > - (u32 *) &out_data); > + &out_data); > if (ACPI_SUCCESS(status)) { > if (out_data == 0) > return sysfs_emit(buf, "[unconnected] connected unknown\n"); > @@ -695,7 +695,7 @@ static ssize_t show_deepsleep_status(struct device *dev, > .arg = 0, > }; > status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS, > - (u32 *) &out_data); > + &out_data); > if (ACPI_SUCCESS(status)) { > if (out_data == 0) > return sysfs_emit(buf, "[disabled] s5 s5_s4\n"); >
All changes done in v5. Thank you for all your help. > > Signed-off-by: Kurt Borja <kuurtb@gmail.com> > > --- > > drivers/platform/x86/dell/alienware-wmi.c | 134 +++++++++++----------- > > 1 file changed, 67 insertions(+), 67 deletions(-) > > > > diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c > > index f5ee62ce1..16a3fe9ac 100644 > > --- a/drivers/platform/x86/dell/alienware-wmi.c > > +++ b/drivers/platform/x86/dell/alienware-wmi.c > > @@ -116,68 +116,68 @@ static int __init dmi_matched(const struct dmi_system_id *dmi) > > > > static const struct dmi_system_id alienware_quirks[] __initconst = { > > { > > - .callback = dmi_matched, > > - .ident = "Alienware X51 R3", > > - .matches = { > > - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > > - DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"), > > - }, > > - .driver_data = &quirk_x51_r3, > > - }, > > + .callback = dmi_matched, > > + .ident = "Alienware X51 R3", > > + .matches = { > > + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > > + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"), > > + }, > > + .driver_data = &quirk_x51_r3, > > + }, > > { > > - .callback = dmi_matched, > > - .ident = "Alienware X51 R2", > > - .matches = { > > - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > > - DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"), > > - }, > > - .driver_data = &quirk_x51_r1_r2, > > - }, > > + .callback = dmi_matched, > > + .ident = "Alienware X51 R2", > > + .matches = { > > + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > > + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"), > > + }, > > + .driver_data = &quirk_x51_r1_r2, > > + }, > > { > > - .callback = dmi_matched, > > - .ident = "Alienware X51 R1", > > - .matches = { > > - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > > - DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"), > > - }, > > - .driver_data = &quirk_x51_r1_r2, > > - }, > > + .callback = dmi_matched, > > + .ident = "Alienware X51 R1", > > + .matches = { > > + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > > + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"), > > + }, > > + .driver_data = &quirk_x51_r1_r2, > > + }, > > { > > - .callback = dmi_matched, > > - .ident = "Alienware ASM100", > > - .matches = { > > - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > > - DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"), > > - }, > > - .driver_data = &quirk_asm100, > > - }, > > + .callback = dmi_matched, > > + .ident = "Alienware ASM100", > > + .matches = { > > + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > > + DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"), > > + }, > > + .driver_data = &quirk_asm100, > > + }, > > { > > - .callback = dmi_matched, > > - .ident = "Alienware ASM200", > > - .matches = { > > - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > > - DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"), > > - }, > > - .driver_data = &quirk_asm200, > > - }, > > + .callback = dmi_matched, > > + .ident = "Alienware ASM200", > > + .matches = { > > + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > > + DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"), > > + }, > > + .driver_data = &quirk_asm200, > > + }, > > { > > - .callback = dmi_matched, > > - .ident = "Alienware ASM201", > > - .matches = { > > - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > > - DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"), > > - }, > > - .driver_data = &quirk_asm201, > > - }, > > - { > > - .callback = dmi_matched, > > - .ident = "Dell Inc. Inspiron 5675", > > - .matches = { > > - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), > > - DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"), > > - }, > > - .driver_data = &quirk_inspiron5675, > > - }, > > + .callback = dmi_matched, > > + .ident = "Alienware ASM201", > > + .matches = { > > + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > > + DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"), > > + }, > > + .driver_data = &quirk_asm201, > > + }, > > + { > > + .callback = dmi_matched, > > + .ident = "Dell Inc. Inspiron 5675", > > + .matches = { > > + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), > > + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"), > > + }, > > + .driver_data = &quirk_inspiron5675, > > + }, > > {} > > }; > > > > @@ -221,8 +221,8 @@ static struct platform_zone *zone_data; > > > > static struct platform_driver platform_driver = { > > .driver = { > > - .name = "alienware-wmi", > > - } > > + .name = "alienware-wmi", > > + } > > }; > > > > static struct attribute_group zone_attribute_group = { > > @@ -292,7 +292,7 @@ static int alienware_update_led(struct platform_zone *zone) > > guid = WMAX_CONTROL_GUID; > > method_id = WMAX_METHOD_ZONE_CONTROL; > > > > - input.length = (acpi_size) sizeof(wmax_basic_args); > > + input.length = sizeof(wmax_basic_args); > > input.pointer = &wmax_basic_args; > > } else { > > legacy_args.colors = zone->colors; > > @@ -306,7 +306,7 @@ static int alienware_update_led(struct platform_zone *zone) > > guid = LEGACY_CONTROL_GUID; > > method_id = zone->location + 1; > > > > - input.length = (acpi_size) sizeof(legacy_args); > > + input.length = sizeof(legacy_args); > > input.pointer = &legacy_args; > > } > > pr_debug("alienware-wmi: guid %s method %d\n", guid, method_id); > > @@ -358,7 +358,7 @@ static int wmax_brightness(int brightness) > > .led_mask = 0xFF, > > .percentage = brightness, > > }; > > - input.length = (acpi_size) sizeof(args); > > + input.length = sizeof(args); > > input.pointer = &args; > > status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0, > > WMAX_METHOD_BRIGHTNESS, &input, NULL); > > @@ -508,7 +508,7 @@ static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args, > > struct acpi_buffer input; > > struct acpi_buffer output; > > > > - input.length = (acpi_size) sizeof(*in_args); > > + input.length = sizeof(*in_args); > > input.pointer = in_args; > > if (out_data) { > > output.length = ACPI_ALLOCATE_BUFFER; > > @@ -542,7 +542,7 @@ static ssize_t show_hdmi_cable(struct device *dev, > > }; > > status = > > alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE, > > - (u32 *) &out_data); > > + &out_data); > > if (ACPI_SUCCESS(status)) { > > if (out_data == 0) > > return sysfs_emit(buf, "[unconnected] connected unknown\n"); > > @@ -563,7 +563,7 @@ static ssize_t show_hdmi_source(struct device *dev, > > }; > > status = > > alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS, > > - (u32 *) &out_data); > > + &out_data); > > > > if (ACPI_SUCCESS(status)) { > > if (out_data == 1) > > @@ -643,7 +643,7 @@ static ssize_t show_amplifier_status(struct device *dev, > > }; > > status = > > alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE, > > - (u32 *) &out_data); > > + &out_data); > > if (ACPI_SUCCESS(status)) { > > if (out_data == 0) > > return sysfs_emit(buf, "[unconnected] connected unknown\n"); > > @@ -695,7 +695,7 @@ static ssize_t show_deepsleep_status(struct device *dev, > > .arg = 0, > > }; > > status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS, > > - (u32 *) &out_data); > > + &out_data); > > if (ACPI_SUCCESS(status)) { > > if (out_data == 0) > > return sysfs_emit(buf, "[disabled] s5 s5_s4\n"); > >
Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
drivers/platform/x86/dell/alienware-wmi.c | 29 ++++++++++++-----------
1 file changed, 15 insertions(+), 14 deletions(-)
diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
index 16a3fe9ac..8f7a8bfef 100644
--- a/drivers/platform/x86/dell/alienware-wmi.c
+++ b/drivers/platform/x86/dell/alienware-wmi.c
@@ -500,15 +500,15 @@ static void alienware_zone_exit(struct platform_device *dev)
kfree(zone_attrs);
}
-static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
- u32 command, int *out_data)
+static acpi_status alienware_wmax_command(void *in_args, size_t insize,
+ u32 command, u32 *out_data)
{
acpi_status status;
union acpi_object *obj;
struct acpi_buffer input;
struct acpi_buffer output;
- input.length = sizeof(*in_args);
+ input.length = insize;
input.pointer = in_args;
if (out_data) {
output.length = ACPI_ALLOCATE_BUFFER;
@@ -541,8 +541,8 @@ static ssize_t show_hdmi_cable(struct device *dev,
.arg = 0,
};
status =
- alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE,
- &out_data);
+ alienware_wmax_command(&in_args, sizeof(in_args),
+ WMAX_METHOD_HDMI_CABLE, &out_data);
if (ACPI_SUCCESS(status)) {
if (out_data == 0)
return sysfs_emit(buf, "[unconnected] connected unknown\n");
@@ -562,8 +562,8 @@ static ssize_t show_hdmi_source(struct device *dev,
.arg = 0,
};
status =
- alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS,
- &out_data);
+ alienware_wmax_command(&in_args, sizeof(in_args),
+ WMAX_METHOD_HDMI_STATUS, &out_data);
if (ACPI_SUCCESS(status)) {
if (out_data == 1)
@@ -589,7 +589,8 @@ static ssize_t toggle_hdmi_source(struct device *dev,
args.arg = 3;
pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf);
- status = alienware_wmax_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL);
+ status = alienware_wmax_command(&args, sizeof(args),
+ WMAX_METHOD_HDMI_SOURCE, NULL);
if (ACPI_FAILURE(status))
pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
@@ -642,8 +643,8 @@ static ssize_t show_amplifier_status(struct device *dev,
.arg = 0,
};
status =
- alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE,
- &out_data);
+ alienware_wmax_command(&in_args, sizeof(in_args),
+ WMAX_METHOD_AMPLIFIER_CABLE, &out_data);
if (ACPI_SUCCESS(status)) {
if (out_data == 0)
return sysfs_emit(buf, "[unconnected] connected unknown\n");
@@ -694,8 +695,8 @@ static ssize_t show_deepsleep_status(struct device *dev,
struct wmax_basic_args in_args = {
.arg = 0,
};
- status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS,
- &out_data);
+ status = alienware_wmax_command(&in_args, sizeof(in_args),
+ WMAX_METHOD_DEEP_SLEEP_STATUS, &out_data);
if (ACPI_SUCCESS(status)) {
if (out_data == 0)
return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
@@ -723,8 +724,8 @@ static ssize_t toggle_deepsleep(struct device *dev,
args.arg = 2;
pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf);
- status = alienware_wmax_command(&args, WMAX_METHOD_DEEP_SLEEP_CONTROL,
- NULL);
+ status = alienware_wmax_command(&args, sizeof(args),
+ WMAX_METHOD_DEEP_SLEEP_CONTROL, NULL);
if (ACPI_FAILURE(status))
pr_err("alienware-wmi: deep sleep control failed: results: %u\n",
--
2.47.0
On Fri, 11 Oct 2024, Kurt Borja wrote: As mentioned, please add the description of the change here. This change also does int * -> u32 *, which I think is fine but please mention the justification here. If somebody looks this change 10 years from now, it helps a lot when the reasoning is recorded into the change. -- i. > Signed-off-by: Kurt Borja <kuurtb@gmail.com> > --- > drivers/platform/x86/dell/alienware-wmi.c | 29 ++++++++++++----------- > 1 file changed, 15 insertions(+), 14 deletions(-) > > diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c > index 16a3fe9ac..8f7a8bfef 100644 > --- a/drivers/platform/x86/dell/alienware-wmi.c > +++ b/drivers/platform/x86/dell/alienware-wmi.c > @@ -500,15 +500,15 @@ static void alienware_zone_exit(struct platform_device *dev) > kfree(zone_attrs); > } > > -static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args, > - u32 command, int *out_data) > +static acpi_status alienware_wmax_command(void *in_args, size_t insize, > + u32 command, u32 *out_data) > { > acpi_status status; > union acpi_object *obj; > struct acpi_buffer input; > struct acpi_buffer output; > > - input.length = sizeof(*in_args); > + input.length = insize; > input.pointer = in_args; > if (out_data) { > output.length = ACPI_ALLOCATE_BUFFER; > @@ -541,8 +541,8 @@ static ssize_t show_hdmi_cable(struct device *dev, > .arg = 0, > }; > status = > - alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE, > - &out_data); > + alienware_wmax_command(&in_args, sizeof(in_args), > + WMAX_METHOD_HDMI_CABLE, &out_data); > if (ACPI_SUCCESS(status)) { > if (out_data == 0) > return sysfs_emit(buf, "[unconnected] connected unknown\n"); > @@ -562,8 +562,8 @@ static ssize_t show_hdmi_source(struct device *dev, > .arg = 0, > }; > status = > - alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS, > - &out_data); > + alienware_wmax_command(&in_args, sizeof(in_args), > + WMAX_METHOD_HDMI_STATUS, &out_data); > > if (ACPI_SUCCESS(status)) { > if (out_data == 1) > @@ -589,7 +589,8 @@ static ssize_t toggle_hdmi_source(struct device *dev, > args.arg = 3; > pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf); > > - status = alienware_wmax_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL); > + status = alienware_wmax_command(&args, sizeof(args), > + WMAX_METHOD_HDMI_SOURCE, NULL); > > if (ACPI_FAILURE(status)) > pr_err("alienware-wmi: HDMI toggle failed: results: %u\n", > @@ -642,8 +643,8 @@ static ssize_t show_amplifier_status(struct device *dev, > .arg = 0, > }; > status = > - alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE, > - &out_data); > + alienware_wmax_command(&in_args, sizeof(in_args), > + WMAX_METHOD_AMPLIFIER_CABLE, &out_data); > if (ACPI_SUCCESS(status)) { > if (out_data == 0) > return sysfs_emit(buf, "[unconnected] connected unknown\n"); > @@ -694,8 +695,8 @@ static ssize_t show_deepsleep_status(struct device *dev, > struct wmax_basic_args in_args = { > .arg = 0, > }; > - status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS, > - &out_data); > + status = alienware_wmax_command(&in_args, sizeof(in_args), > + WMAX_METHOD_DEEP_SLEEP_STATUS, &out_data); > if (ACPI_SUCCESS(status)) { > if (out_data == 0) > return sysfs_emit(buf, "[disabled] s5 s5_s4\n"); > @@ -723,8 +724,8 @@ static ssize_t toggle_deepsleep(struct device *dev, > args.arg = 2; > pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf); > > - status = alienware_wmax_command(&args, WMAX_METHOD_DEEP_SLEEP_CONTROL, > - NULL); > + status = alienware_wmax_command(&args, sizeof(args), > + WMAX_METHOD_DEEP_SLEEP_CONTROL, NULL); > > if (ACPI_FAILURE(status)) > pr_err("alienware-wmi: deep sleep control failed: results: %u\n", >
Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
drivers/platform/x86/dell/Kconfig | 1 +
drivers/platform/x86/dell/alienware-wmi.c | 236 ++++++++++++++++++++++
2 files changed, 237 insertions(+)
diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig
index 68a49788a..b06d634cd 100644
--- a/drivers/platform/x86/dell/Kconfig
+++ b/drivers/platform/x86/dell/Kconfig
@@ -21,6 +21,7 @@ config ALIENWARE_WMI
depends on LEDS_CLASS
depends on NEW_LEDS
depends on ACPI_WMI
+ select ACPI_PLATFORM_PROFILE
help
This is a driver for controlling Alienware BIOS driven
features. It exposes an interface for controlling the AlienFX
diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
index 8f7a8bfef..8af2ab7fe 100644
--- a/drivers/platform/x86/dell/alienware-wmi.c
+++ b/drivers/platform/x86/dell/alienware-wmi.c
@@ -8,8 +8,11 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/acpi.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/platform_profile.h>
#include <linux/dmi.h>
#include <linux/leds.h>
@@ -25,6 +28,12 @@
#define WMAX_METHOD_AMPLIFIER_CABLE 0x6
#define WMAX_METHOD_DEEP_SLEEP_CONTROL 0x0B
#define WMAX_METHOD_DEEP_SLEEP_STATUS 0x0C
+#define WMAX_METHOD_THERMAL_INFORMATION 0x14
+#define WMAX_METHOD_THERMAL_CONTROL 0x15
+
+#define WMAX_ARG_GET_CURRENT_PROF 0x0B
+
+#define WMAX_FAILURE_CODE 0xFFFFFFFF
MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
MODULE_DESCRIPTION("Alienware special feature control");
@@ -49,11 +58,27 @@ enum WMAX_CONTROL_STATES {
WMAX_SUSPEND = 3,
};
+enum WMAX_THERMAL_PROFILE {
+ WMAX_THERMAL_QUIET = 0x96,
+ WMAX_THERMAL_BALANCED = 0x97,
+ WMAX_THERMAL_BALANCED_PERFORMANCE = 0x98,
+ WMAX_THERMAL_PERFORMANCE = 0x99,
+ WMAX_THERMAL_USTT_LOW_POWER = 0xA5,
+ WMAX_THERMAL_USTT_QUIET = 0xA3,
+ WMAX_THERMAL_USTT_BALANCED = 0xA0,
+ WMAX_THERMAL_USTT_BALANCED_PERFORMANCE = 0xA1,
+ WMAX_THERMAL_USTT_PERFORMANCE = 0xA4,
+ WMAX_THERMAL_GMODE = 0xAB,
+};
+
struct quirk_entry {
u8 num_zones;
u8 hdmi_mux;
u8 amplifier;
u8 deepslp;
+ u8 thermal;
+ u8 thermal_ustt;
+ u8 gmode;
};
static struct quirk_entry *quirks;
@@ -64,6 +89,9 @@ static struct quirk_entry quirk_inspiron5675 = {
.hdmi_mux = 0,
.amplifier = 0,
.deepslp = 0,
+ .thermal = 0,
+ .thermal_ustt = 0,
+ .gmode = 0,
};
static struct quirk_entry quirk_unknown = {
@@ -71,6 +99,9 @@ static struct quirk_entry quirk_unknown = {
.hdmi_mux = 0,
.amplifier = 0,
.deepslp = 0,
+ .thermal = 0,
+ .thermal_ustt = 0,
+ .gmode = 0,
};
static struct quirk_entry quirk_x51_r1_r2 = {
@@ -78,6 +109,9 @@ static struct quirk_entry quirk_x51_r1_r2 = {
.hdmi_mux = 0,
.amplifier = 0,
.deepslp = 0,
+ .thermal = 0,
+ .thermal_ustt = 0,
+ .gmode = 0,
};
static struct quirk_entry quirk_x51_r3 = {
@@ -85,6 +119,9 @@ static struct quirk_entry quirk_x51_r3 = {
.hdmi_mux = 0,
.amplifier = 1,
.deepslp = 0,
+ .thermal = 0,
+ .thermal_ustt = 0,
+ .gmode = 0,
};
static struct quirk_entry quirk_asm100 = {
@@ -92,6 +129,9 @@ static struct quirk_entry quirk_asm100 = {
.hdmi_mux = 1,
.amplifier = 0,
.deepslp = 0,
+ .thermal = 0,
+ .thermal_ustt = 0,
+ .gmode = 0,
};
static struct quirk_entry quirk_asm200 = {
@@ -99,6 +139,9 @@ static struct quirk_entry quirk_asm200 = {
.hdmi_mux = 1,
.amplifier = 0,
.deepslp = 1,
+ .thermal = 0,
+ .thermal_ustt = 0,
+ .gmode = 0,
};
static struct quirk_entry quirk_asm201 = {
@@ -106,6 +149,19 @@ static struct quirk_entry quirk_asm201 = {
.hdmi_mux = 1,
.amplifier = 1,
.deepslp = 1,
+ .thermal = 0,
+ .thermal_ustt = 0,
+ .gmode = 0,
+};
+
+static struct quirk_entry quirk_x15_r1 = {
+ .num_zones = 2,
+ .hdmi_mux = 0,
+ .amplifier = 0,
+ .deepslp = 0,
+ .thermal = 0,
+ .thermal_ustt = 1,
+ .gmode = 0,
};
static int __init dmi_matched(const struct dmi_system_id *dmi)
@@ -169,6 +225,15 @@ static const struct dmi_system_id alienware_quirks[] __initconst = {
},
.driver_data = &quirk_asm201,
},
+ {
+ .callback = dmi_matched,
+ .ident = "Alienware x15 R1",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R1")
+ },
+ .driver_data = &quirk_x15_r1,
+ },
{
.callback = dmi_matched,
.ident = "Dell Inc. Inspiron 5675",
@@ -218,6 +283,7 @@ static struct platform_device *platform_device;
static struct device_attribute *zone_dev_attrs;
static struct attribute **zone_attrs;
static struct platform_zone *zone_data;
+static struct platform_profile_handler pp_handler;
static struct platform_driver platform_driver = {
.driver = {
@@ -761,6 +827,168 @@ static int create_deepsleep(struct platform_device *dev)
return ret;
}
+/*
+ * Thermal Profile control
+ * - Provides thermal profile control through the Platform Profile API
+ */
+#define WMAX_PROFILE_MASK GENMASK(15, 8)
+#define WMAX_PROFILE_ACTIVATE BIT(0)
+
+static u32 profile_to_wmax_arg(enum WMAX_THERMAL_PROFILE prof)
+{
+ return FIELD_PREP(WMAX_PROFILE_MASK, prof) | WMAX_PROFILE_ACTIVATE;
+}
+
+static int thermal_profile_get(struct platform_profile_handler *pprof,
+ enum platform_profile_option *profile)
+{
+ acpi_status status;
+ u32 in_args = WMAX_ARG_GET_CURRENT_PROF;
+ u32 out_data;
+
+ status = alienware_wmax_command(&in_args, sizeof(in_args),
+ WMAX_METHOD_THERMAL_INFORMATION, &out_data);
+
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ if (out_data == WMAX_FAILURE_CODE)
+ return -EBADRQC;
+
+ switch (out_data) {
+ case WMAX_THERMAL_USTT_LOW_POWER:
+ *profile = PLATFORM_PROFILE_LOW_POWER;
+ break;
+ case WMAX_THERMAL_QUIET:
+ case WMAX_THERMAL_USTT_QUIET:
+ *profile = PLATFORM_PROFILE_QUIET;
+ break;
+ case WMAX_THERMAL_BALANCED:
+ case WMAX_THERMAL_USTT_BALANCED:
+ *profile = PLATFORM_PROFILE_BALANCED;
+ break;
+ case WMAX_THERMAL_BALANCED_PERFORMANCE:
+ case WMAX_THERMAL_USTT_BALANCED_PERFORMANCE:
+ *profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE;
+ break;
+ case WMAX_THERMAL_GMODE:
+ case WMAX_THERMAL_PERFORMANCE:
+ case WMAX_THERMAL_USTT_PERFORMANCE:
+ *profile = PLATFORM_PROFILE_PERFORMANCE;
+ break;
+ default:
+ return -ENODATA;
+ }
+
+ return 0;
+}
+
+static int thermal_profile_set(struct platform_profile_handler *pprof,
+ enum platform_profile_option profile)
+{
+ acpi_status status;
+ u32 in_args;
+ u32 out_data;
+
+ switch (profile) {
+ case PLATFORM_PROFILE_QUIET:
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET);
+ break;
+ case PLATFORM_PROFILE_BALANCED:
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED);
+ break;
+ case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE);
+ break;
+ case PLATFORM_PROFILE_PERFORMANCE:
+ if (quirks->gmode > 0)
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_GMODE);
+ else
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_PERFORMANCE);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ status = alienware_wmax_command(&in_args, sizeof(in_args),
+ WMAX_METHOD_THERMAL_CONTROL, &out_data);
+
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ if (out_data == WMAX_FAILURE_CODE)
+ return -EBADRQC;
+
+ return 0;
+}
+
+static int thermal_profile_set_ustt(struct platform_profile_handler *pprof,
+ enum platform_profile_option profile)
+{
+ acpi_status status;
+ u32 in_args;
+ u32 out_data;
+
+ switch (profile) {
+ case PLATFORM_PROFILE_LOW_POWER:
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_LOW_POWER);
+ break;
+ case PLATFORM_PROFILE_QUIET:
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_QUIET);
+ break;
+ case PLATFORM_PROFILE_BALANCED:
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_BALANCED);
+ break;
+ case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_BALANCED_PERFORMANCE);
+ break;
+ case PLATFORM_PROFILE_PERFORMANCE:
+ if (quirks->gmode > 0)
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_GMODE);
+ else
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_PERFORMANCE);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ status = alienware_wmax_command(&in_args, sizeof(in_args),
+ WMAX_METHOD_THERMAL_CONTROL, &out_data);
+
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ if (out_data == WMAX_FAILURE_CODE)
+ return -EBADRQC;
+
+ return 0;
+}
+
+static int create_thermal_profile(void)
+{
+ pp_handler.profile_get = thermal_profile_get;
+
+ if (quirks->thermal > 0)
+ pp_handler.profile_set = thermal_profile_set;
+ else {
+ pp_handler.profile_set = thermal_profile_set_ustt;
+ set_bit(PLATFORM_PROFILE_LOW_POWER, pp_handler.choices);
+ }
+
+ set_bit(PLATFORM_PROFILE_QUIET, pp_handler.choices);
+ set_bit(PLATFORM_PROFILE_BALANCED, pp_handler.choices);
+ set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, pp_handler.choices);
+ set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices);
+
+ return platform_profile_register(&pp_handler);
+}
+
+static void remove_thermal_profile(void)
+{
+ if (quirks->thermal > 0)
+ platform_profile_remove();
+}
+
static int __init alienware_wmi_init(void)
{
int ret;
@@ -808,6 +1036,12 @@ static int __init alienware_wmi_init(void)
goto fail_prep_deepsleep;
}
+ if (quirks->thermal > 0 || quirks->thermal_ustt > 0) {
+ ret = create_thermal_profile();
+ if (ret)
+ goto fail_prep_thermal_profile;
+ }
+
ret = alienware_zone_init(platform_device);
if (ret)
goto fail_prep_zones;
@@ -818,6 +1052,7 @@ static int __init alienware_wmi_init(void)
alienware_zone_exit(platform_device);
fail_prep_deepsleep:
fail_prep_amplifier:
+fail_prep_thermal_profile:
fail_prep_hdmi:
platform_device_del(platform_device);
fail_platform_device2:
@@ -835,6 +1070,7 @@ static void __exit alienware_wmi_exit(void)
if (platform_device) {
alienware_zone_exit(platform_device);
remove_hdmi(platform_device);
+ remove_thermal_profile();
platform_device_unregister(platform_device);
platform_driver_unregister(&platform_driver);
}
--
2.47.0
Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
Documentation/wmi/devices/alienware-wmi.rst | 364 ++++++++++++++++++++
1 file changed, 364 insertions(+)
create mode 100644 Documentation/wmi/devices/alienware-wmi.rst
diff --git a/Documentation/wmi/devices/alienware-wmi.rst b/Documentation/wmi/devices/alienware-wmi.rst
new file mode 100644
index 000000000..cf5d6259f
--- /dev/null
+++ b/Documentation/wmi/devices/alienware-wmi.rst
@@ -0,0 +1,364 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+==============================================
+Dell AWCC WMI interface driver (alienware-wmi)
+==============================================
+
+Introduction
+============
+
+The WMI device WMAX has been implemented for many Alienware and Dell's G-Series
+models. Throughout these models, two implementations have been identified. The
+first one, used by older systems, deals with HDMI, brightness, RGB, amplifier
+and deep sleep control. The second one used by newer systems deals primarily
+with thermal, overclocking, and GPIO control.
+
+It is suspected that the latter is used by Alienware Command Center (AWCC) to
+manage manufacturer predefined thermal profiles. The alienware-wmi driver
+exposes Thermal_Information and Thermal_Control methods through the Platform
+Profile API to mimic AWCC's behavior.
+
+This newer interface, named AWCCMethodFunction has been reverse engineered, as
+Dell has not provided any official documentation. We will try to describe to the
+best of our ability its discovered inner workings.
+
+.. note::
+ The following method description may vary between models.
+
+WMI interface description
+-------------------------
+
+The WMI interface description can be decoded from the embedded binary MOF (bmof)
+data using the `bmfdec <https://github.com/pali/bmfdec>`_ utility:
+
+::
+
+ [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("WMI Function"), guid("{A70591CE-A997-11DA-B012-B622A1EF5492}")]
+ class AWCCWmiMethodFunction {
+ [key, read] string InstanceName;
+ [read] boolean Active;
+
+ [WmiMethodId(13), Implemented, read, write, Description("Return Overclocking Report.")] void Return_OverclockingReport([out] uint32 argr);
+ [WmiMethodId(14), Implemented, read, write, Description("Set OCUIBIOS Control.")] void Set_OCUIBIOSControl([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(15), Implemented, read, write, Description("Clear OC FailSafe Flag.")] void Clear_OCFailSafeFlag([out] uint32 argr);
+ [WmiMethodId(19), Implemented, read, write, Description("Get Fan Sensors.")] void GetFanSensors([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(20), Implemented, read, write, Description("Thermal Information.")] void Thermal_Information([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(21), Implemented, read, write, Description("Thermal Control.")] void Thermal_Control([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(23), Implemented, read, write, Description("MemoryOCControl.")] void MemoryOCControl([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(26), Implemented, read, write, Description("System Information.")] void SystemInformation([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(28), Implemented, read, write, Description("Power Information.")] void PowerInformation([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(32), Implemented, read, write, Description("FW Update GPIO toggle.")] void FWUpdateGPIOtoggle([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(33), Implemented, read, write, Description("Read Total of GPIOs.")] void ReadTotalofGPIOs([out] uint32 argr);
+ [WmiMethodId(34), Implemented, read, write, Description("Read GPIO pin Status.")] void ReadGPIOpPinStatus([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(35), Implemented, read, write, Description("Read Chassis Color.")] void ReadChassisColor([out] uint32 argr);
+ [WmiMethodId(36), Implemented, read, write, Description("Read Platform Properties.")] void ReadPlatformProperties([out] uint32 argr);
+ [WmiMethodId(128), Implemented, read, write, Description("Caldera SW installation.")] void CalderaSWInstallation([out] uint32 argr);
+ [WmiMethodId(129), Implemented, read, write, Description("Caldera SW is released.")] void CalderaSWReleased([out] uint32 argr);
+ [WmiMethodId(130), Implemented, read, write, Description("Caldera Connection Status.")] void CalderaConnectionStatus([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(131), Implemented, read, write, Description("Surprise Unplugged Flag Status.")] void SurpriseUnpluggedFlagStatus([out] uint32 argr);
+ [WmiMethodId(132), Implemented, read, write, Description("Clear Surprise Unplugged Flag.")] void ClearSurpriseUnpluggedFlag([out] uint32 argr);
+ [WmiMethodId(133), Implemented, read, write, Description("Cancel Undock Request.")] void CancelUndockRequest([out] uint32 argr);
+ [WmiMethodId(135), Implemented, read, write, Description("Devices in Caldera.")] void DevicesInCaldera([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(136), Implemented, read, write, Description("Notify BIOS for SW ready to disconnect Caldera.")] void NotifyBIOSForSWReadyToDisconnectCaldera([out] uint32 argr);
+ [WmiMethodId(160), Implemented, read, write, Description("Tobii SW installation.")] void TobiiSWinstallation([out] uint32 argr);
+ [WmiMethodId(161), Implemented, read, write, Description("Tobii SW Released.")] void TobiiSWReleased([out] uint32 argr);
+ [WmiMethodId(162), Implemented, read, write, Description("Tobii Camera Power Reset.")] void TobiiCameraPowerReset([out] uint32 argr);
+ [WmiMethodId(163), Implemented, read, write, Description("Tobii Camera Power On.")] void TobiiCameraPowerOn([out] uint32 argr);
+ [WmiMethodId(164), Implemented, read, write, Description("Tobii Camera Power Off.")] void TobiiCameraPowerOff([out] uint32 argr);
+ };
+
+Some of these methods get quite intricate so we will describe them using
+pseudo-code that vaguely resembles the original ASL code.
+
+Argument Structure
+------------------
+
+All input arguments have type **uint32** and their structure is very similar
+between methods. Usually, the first byte corresponds to a specific *operation*
+the method performs, and the subsequent bytes correspond to *arguments* passed
+to this *operation*. For example, if an operation has code 0x01 and requires an
+ID 0xA0, the argument you would pass to the method is 0xA001.
+
+
+Thermal Methods
+===============
+
+WMI method Thermal_Information([in] uint32 arg2, [out] uint32 argr)
+-------------------------------------------------------------------
+
+::
+
+ if BYTE_0(arg2) == 0x01:
+ argr = 1
+
+ if BYTE_0(arg2) == 0x02:
+ argr = UNKNOWN_CONSTANT
+
+ if BYTE_0(arg2) == 0x03:
+ if BYTE_1(arg2) == 0x00:
+ argr = FAN_ID_0
+
+ if BYTE_1(arg2) == 0x01:
+ argr = FAN_ID_1
+
+ if BYTE_1(arg2) == 0x02:
+ argr = FAN_ID_2
+
+ if BYTE_1(arg2) == 0x03:
+ argr = FAN_ID_3
+
+ if BYTE_1(arg2) == 0x04:
+ argr = SENSOR_ID_CPU | 0x0100
+
+ if BYTE_1(arg2) == 0x05:
+ argr = SENSOR_ID_GPU | 0x0100
+
+ if BYTE_1(arg2) == 0x06:
+ argr = THERMAL_MODE_QUIET_ID
+
+ if BYTE_1(arg2) == 0x07:
+ argr = THERMAL_MODE_BALANCED_ID
+
+ if BYTE_1(arg2) == 0x08:
+ argr = THERMAL_MODE_BALANCED_PERFORMANCE_ID
+
+ if BYTE_1(arg2) == 0x09:
+ argr = THERMAL_MODE_PERFORMANCE_ID
+
+ if BYTE_1(arg2) == 0x0A:
+ argr = THERMAL_MODE_LOW_POWER_ID
+
+ if BYTE_1(arg2) == 0x0B:
+ argr = THERMAL_MODE_GMODE_ID
+
+ else:
+ argr = 0xFFFFFFFF
+
+ if BYTE_0(arg2) == 0x04:
+ if is_valid_sensor(BYTE_1(arg2)):
+ argr = SENSOR_TEMP_C
+ else:
+ argr = 0xFFFFFFFF
+
+ if BYTE_0(arg2) == 0x05:
+ if is_valid_fan(BYTE_1(arg2)):
+ argr = FAN_RPM()
+
+ if BYTE_0(arg2) == 0x06:
+ skip
+
+ if BYTE_0(arg2) == 0x07:
+ argr = 0
+
+ If BYTE_0(arg2) == 0x08:
+ if is_valid_fan(BYTE_1(arg2)):
+ argr = 0
+ else:
+ argr = 0xFFFFFFFF
+
+ if BYTE_0(arg2) == 0x09:
+ if is_valid_fan(BYTE_1(arg2)):
+ argr = FAN_UNKNOWN_STAT_0()
+
+ else:
+ argr = 0xFFFFFFFF
+
+ if BYTE_0(arg2) == 0x0A:
+ argr = THERMAL_MODE_BALANCED_ID
+
+ if BYTE_0(arg2) == 0x0B:
+ argr = CURRENT_THERMAL_MODE()
+
+ if BYTE_0(arg2) == 0x0C:
+ if is_valid_fan(BYTE_1(arg2)):
+ argr = FAN_UNKNOWN_STAT_1()
+ else:
+ argr = 0xFFFFFFFF
+
+WMI method Thermal_Control([in] uint32 arg2, [out] uint32 argr)
+---------------------------------------------------------------
+
+::
+
+ if BYTE_0(arg2) == 0x01:
+ if is_valid_thermal_profile(BYTE_1(arg2)):
+ SET_THERMAL_PROFILE(BYTE_1(arg2))
+ argr = 0
+
+ if BYTE_0(arg2) == 0x02:
+ if is_valid_fan(BYTE_1(arg2)):
+ SET_FAN_SPEED_MULTIPLIER(BYTE_2(arg2))
+ argr = 0
+ else:
+ argr = 0xFFFFFFFF
+
+.. note::
+ While you can manually change the fan speed multiplier with this method,
+ Dell's BIOS tends to overwrite this changes anyway.
+
+These are the known thermal profile codes:
+
+::
+
+ CUSTOM 0x00
+
+ QUIET 0x96
+ BALANCED 0x97
+ BALANCED_PERFORMANCE 0x98
+ PERFORMANCE 0x99
+
+ QUIET_USTT 0xA3
+ BALANCED_USTT 0xA0
+ BALANCED_PERFORMANCE_USTT 0xA1
+ PERFORMANCE_USTT 0xA4
+ LOW_POWER_USTT 0xA5
+
+ GMODE 0xAB
+
+Usually if a model doesn't support the first four profiles they will support
+the User Selectable Thermal Tables (USTT) profiles and vice-versa.
+
+GMODE replaces PERFORMANCE in G-Series laptops.
+
+Very grateful to `AlexIII <https://github.com/AlexIII/tcc-g15>`_ for discovering
+some of the codes compatible with G-Series laptops.
+
+WMI method GetFanSensors([in] uint32 arg2, [out] uint32 argr)
+-------------------------------------------------------------
+
+::
+
+ if BYTE_0(arg2) == 1:
+ if is_valid_fan(BYTE_1(arg2)):
+ argr = 1
+ else:
+ argr = 0
+
+ if BYTE_0(arg2) == 2:
+ if is_valid_fan(BYTE_1(arg2)):
+ if BYTE_2(arg2) == 0:
+ argr == SENSOR_ID
+ else
+ argr == 0xFFFFFFFF
+ else:
+ argr = 0
+
+Overclocking Methods
+====================
+
+.. warning::
+ These methods have not been tested and are only partially reverse
+ engineered.
+
+WMI method Return_OverclockingReport([out] uint32 argr)
+-------------------------------------------------------
+
+::
+
+ CSMI (0xE3, 0x99)
+ argr = 0
+
+CSMI is an unknown operation.
+
+WMI method Set_OCUIBIOSControl([in] uint32 arg2, [out] uint32 argr)
+-------------------------------------------------------------------
+
+::
+
+ CSMI (0xE3, 0x99)
+ argr = 0
+
+CSMI is an unknown operation
+
+WMI method Clear_OCFailSafeFlag([out] uint32 argr)
+--------------------------------------------------
+
+::
+
+ CSMI (0xE3, 0x99)
+ argr = 0
+
+CSMI is an unknown operation
+
+
+WMI method MemoryOCControl([in] uint32 arg2, [out] uint32 argr)
+---------------------------------------------------------------
+
+AWCC supports memory overclocking, but this method is very intricate and has
+not been deciphered yet.
+
+GPIO methods
+============
+
+These methods are probably related to some kind of firmware update system,
+through a GPIO device.
+
+.. warning::
+ These methods have not been tested and are only partially reverse
+ engineered.
+
+WMI method FWUpdateGPIOtoggle([in] uint32 arg2, [out] uint32 argr)
+------------------------------------------------------------------
+
+::
+
+ if BYTE_0(arg2) == 0:
+ if BYTE_1(arg2) == 1:
+ SET_PIN_A_HIGH()
+ else:
+ SET_PIN_A_LOW()
+
+ if BYTE_0(arg2) == 1:
+ if BYTE_1(arg2) == 1:
+ SET_PIN_B_HIGH()
+
+ else:
+ SET_PIN_B_LOW()
+
+ else:
+ argr = 1
+
+WMI method ReadTotalofGPIOs([out] uint32 argr)
+----------------------------------------------
+
+::
+
+ argr = 0x02
+
+WMI method ReadGPIOpPinStatus([in] uint32 arg2, [out] uint32 argr)
+------------------------------------------------------------------
+
+::
+
+ if BYTE_0(arg2) == 0:
+ argr = PIN_A_STATUS
+
+ if BYTE_0(arg2) == 1:
+ argr = PIN_B_STATUS
+
+
+Other information Methods
+=========================
+
+WMI method SystemInformation([in] uint32 arg2, [out] uint32 argr)
+-----------------------------------------------------------------
+
+Returns unknown information.
+
+WMI method PowerInformation([in] uint32 arg2, [out] uint32 argr)
+----------------------------------------------------------------
+
+Returns unknown information.
+
+WMI method ReadChassisColor([out] uint32 argr)
+----------------------------------------------
+
+::
+
+ argr = CHASSIS_COLOR_ID
+
+WMI method ReadPlatformProperties([out] uint32 argr)
+----------------------------------------------------
+
+Returns unknown information.
+
--
2.47.0
On Fri, 11 Oct 2024, Kurt Borja wrote: > Signed-off-by: Kurt Borja <kuurtb@gmail.com> > --- > Documentation/wmi/devices/alienware-wmi.rst | 364 ++++++++++++++++++++ > 1 file changed, 364 insertions(+) > create mode 100644 Documentation/wmi/devices/alienware-wmi.rst > > diff --git a/Documentation/wmi/devices/alienware-wmi.rst b/Documentation/wmi/devices/alienware-wmi.rst > new file mode 100644 > index 000000000..cf5d6259f > --- /dev/null > +++ b/Documentation/wmi/devices/alienware-wmi.rst > @@ -0,0 +1,364 @@ > +.. SPDX-License-Identifier: GPL-2.0-or-later > + > +============================================== > +Dell AWCC WMI interface driver (alienware-wmi) > +============================================== > + > +Introduction > +============ > + > +The WMI device WMAX has been implemented for many Alienware and Dell's G-Series > +models. Throughout these models, two implementations have been identified. The > +first one, used by older systems, deals with HDMI, brightness, RGB, amplifier > +and deep sleep control. The second one used by newer systems deals primarily > +with thermal, overclocking, and GPIO control. > + > +It is suspected that the latter is used by Alienware Command Center (AWCC) to > +manage manufacturer predefined thermal profiles. The alienware-wmi driver > +exposes Thermal_Information and Thermal_Control methods through the Platform > +Profile API to mimic AWCC's behavior. > + > +This newer interface, named AWCCMethodFunction has been reverse engineered, as > +Dell has not provided any official documentation. We will try to describe to the > +best of our ability its discovered inner workings. > + > +.. note:: > + The following method description may vary between models. > + > +WMI interface description > +------------------------- > + > +The WMI interface description can be decoded from the embedded binary MOF (bmof) > +data using the `bmfdec <https://github.com/pali/bmfdec>`_ utility: > + > +:: > + > + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("WMI Function"), guid("{A70591CE-A997-11DA-B012-B622A1EF5492}")] > + class AWCCWmiMethodFunction { > + [key, read] string InstanceName; > + [read] boolean Active; > + > + [WmiMethodId(13), Implemented, read, write, Description("Return Overclocking Report.")] void Return_OverclockingReport([out] uint32 argr); > + [WmiMethodId(14), Implemented, read, write, Description("Set OCUIBIOS Control.")] void Set_OCUIBIOSControl([in] uint32 arg2, [out] uint32 argr); > + [WmiMethodId(15), Implemented, read, write, Description("Clear OC FailSafe Flag.")] void Clear_OCFailSafeFlag([out] uint32 argr); > + [WmiMethodId(19), Implemented, read, write, Description("Get Fan Sensors.")] void GetFanSensors([in] uint32 arg2, [out] uint32 argr); > + [WmiMethodId(20), Implemented, read, write, Description("Thermal Information.")] void Thermal_Information([in] uint32 arg2, [out] uint32 argr); > + [WmiMethodId(21), Implemented, read, write, Description("Thermal Control.")] void Thermal_Control([in] uint32 arg2, [out] uint32 argr); > + [WmiMethodId(23), Implemented, read, write, Description("MemoryOCControl.")] void MemoryOCControl([in] uint32 arg2, [out] uint32 argr); > + [WmiMethodId(26), Implemented, read, write, Description("System Information.")] void SystemInformation([in] uint32 arg2, [out] uint32 argr); > + [WmiMethodId(28), Implemented, read, write, Description("Power Information.")] void PowerInformation([in] uint32 arg2, [out] uint32 argr); > + [WmiMethodId(32), Implemented, read, write, Description("FW Update GPIO toggle.")] void FWUpdateGPIOtoggle([in] uint32 arg2, [out] uint32 argr); > + [WmiMethodId(33), Implemented, read, write, Description("Read Total of GPIOs.")] void ReadTotalofGPIOs([out] uint32 argr); > + [WmiMethodId(34), Implemented, read, write, Description("Read GPIO pin Status.")] void ReadGPIOpPinStatus([in] uint32 arg2, [out] uint32 argr); > + [WmiMethodId(35), Implemented, read, write, Description("Read Chassis Color.")] void ReadChassisColor([out] uint32 argr); > + [WmiMethodId(36), Implemented, read, write, Description("Read Platform Properties.")] void ReadPlatformProperties([out] uint32 argr); > + [WmiMethodId(128), Implemented, read, write, Description("Caldera SW installation.")] void CalderaSWInstallation([out] uint32 argr); > + [WmiMethodId(129), Implemented, read, write, Description("Caldera SW is released.")] void CalderaSWReleased([out] uint32 argr); > + [WmiMethodId(130), Implemented, read, write, Description("Caldera Connection Status.")] void CalderaConnectionStatus([in] uint32 arg2, [out] uint32 argr); > + [WmiMethodId(131), Implemented, read, write, Description("Surprise Unplugged Flag Status.")] void SurpriseUnpluggedFlagStatus([out] uint32 argr); > + [WmiMethodId(132), Implemented, read, write, Description("Clear Surprise Unplugged Flag.")] void ClearSurpriseUnpluggedFlag([out] uint32 argr); > + [WmiMethodId(133), Implemented, read, write, Description("Cancel Undock Request.")] void CancelUndockRequest([out] uint32 argr); > + [WmiMethodId(135), Implemented, read, write, Description("Devices in Caldera.")] void DevicesInCaldera([in] uint32 arg2, [out] uint32 argr); > + [WmiMethodId(136), Implemented, read, write, Description("Notify BIOS for SW ready to disconnect Caldera.")] void NotifyBIOSForSWReadyToDisconnectCaldera([out] uint32 argr); > + [WmiMethodId(160), Implemented, read, write, Description("Tobii SW installation.")] void TobiiSWinstallation([out] uint32 argr); > + [WmiMethodId(161), Implemented, read, write, Description("Tobii SW Released.")] void TobiiSWReleased([out] uint32 argr); > + [WmiMethodId(162), Implemented, read, write, Description("Tobii Camera Power Reset.")] void TobiiCameraPowerReset([out] uint32 argr); > + [WmiMethodId(163), Implemented, read, write, Description("Tobii Camera Power On.")] void TobiiCameraPowerOn([out] uint32 argr); > + [WmiMethodId(164), Implemented, read, write, Description("Tobii Camera Power Off.")] void TobiiCameraPowerOff([out] uint32 argr); > + }; > + > +Some of these methods get quite intricate so we will describe them using > +pseudo-code that vaguely resembles the original ASL code. > + > +Argument Structure > +------------------ > + > +All input arguments have type **uint32** and their structure is very similar > +between methods. Usually, the first byte corresponds to a specific *operation* > +the method performs, and the subsequent bytes correspond to *arguments* passed > +to this *operation*. For example, if an operation has code 0x01 and requires an > +ID 0xA0, the argument you would pass to the method is 0xA001. > + > + > +Thermal Methods > +=============== > + > +WMI method Thermal_Information([in] uint32 arg2, [out] uint32 argr) > +------------------------------------------------------------------- > + > +:: > + > + if BYTE_0(arg2) == 0x01: > + argr = 1 > + > + if BYTE_0(arg2) == 0x02: > + argr = UNKNOWN_CONSTANT > + > + if BYTE_0(arg2) == 0x03: > + if BYTE_1(arg2) == 0x00: > + argr = FAN_ID_0 > + > + if BYTE_1(arg2) == 0x01: > + argr = FAN_ID_1 > + > + if BYTE_1(arg2) == 0x02: > + argr = FAN_ID_2 > + > + if BYTE_1(arg2) == 0x03: > + argr = FAN_ID_3 > + > + if BYTE_1(arg2) == 0x04: > + argr = SENSOR_ID_CPU | 0x0100 > + > + if BYTE_1(arg2) == 0x05: > + argr = SENSOR_ID_GPU | 0x0100 > + > + if BYTE_1(arg2) == 0x06: > + argr = THERMAL_MODE_QUIET_ID > + > + if BYTE_1(arg2) == 0x07: > + argr = THERMAL_MODE_BALANCED_ID > + > + if BYTE_1(arg2) == 0x08: > + argr = THERMAL_MODE_BALANCED_PERFORMANCE_ID > + > + if BYTE_1(arg2) == 0x09: > + argr = THERMAL_MODE_PERFORMANCE_ID > + > + if BYTE_1(arg2) == 0x0A: > + argr = THERMAL_MODE_LOW_POWER_ID > + > + if BYTE_1(arg2) == 0x0B: > + argr = THERMAL_MODE_GMODE_ID > + > + else: > + argr = 0xFFFFFFFF > + > + if BYTE_0(arg2) == 0x04: > + if is_valid_sensor(BYTE_1(arg2)): > + argr = SENSOR_TEMP_C > + else: > + argr = 0xFFFFFFFF > + > + if BYTE_0(arg2) == 0x05: > + if is_valid_fan(BYTE_1(arg2)): > + argr = FAN_RPM() > + > + if BYTE_0(arg2) == 0x06: > + skip > + > + if BYTE_0(arg2) == 0x07: > + argr = 0 > + > + If BYTE_0(arg2) == 0x08: > + if is_valid_fan(BYTE_1(arg2)): > + argr = 0 > + else: > + argr = 0xFFFFFFFF > + > + if BYTE_0(arg2) == 0x09: > + if is_valid_fan(BYTE_1(arg2)): > + argr = FAN_UNKNOWN_STAT_0() > + > + else: > + argr = 0xFFFFFFFF > + > + if BYTE_0(arg2) == 0x0A: > + argr = THERMAL_MODE_BALANCED_ID > + > + if BYTE_0(arg2) == 0x0B: > + argr = CURRENT_THERMAL_MODE() > + > + if BYTE_0(arg2) == 0x0C: > + if is_valid_fan(BYTE_1(arg2)): > + argr = FAN_UNKNOWN_STAT_1() > + else: > + argr = 0xFFFFFFFF > + > +WMI method Thermal_Control([in] uint32 arg2, [out] uint32 argr) > +--------------------------------------------------------------- > + > +:: > + > + if BYTE_0(arg2) == 0x01: > + if is_valid_thermal_profile(BYTE_1(arg2)): > + SET_THERMAL_PROFILE(BYTE_1(arg2)) > + argr = 0 > + > + if BYTE_0(arg2) == 0x02: > + if is_valid_fan(BYTE_1(arg2)): > + SET_FAN_SPEED_MULTIPLIER(BYTE_2(arg2)) > + argr = 0 > + else: > + argr = 0xFFFFFFFF > + > +.. note:: > + While you can manually change the fan speed multiplier with this method, > + Dell's BIOS tends to overwrite this changes anyway. > + > +These are the known thermal profile codes: > + > +:: > + > + CUSTOM 0x00 > + > + QUIET 0x96 > + BALANCED 0x97 > + BALANCED_PERFORMANCE 0x98 > + PERFORMANCE 0x99 > + > + QUIET_USTT 0xA3 > + BALANCED_USTT 0xA0 > + BALANCED_PERFORMANCE_USTT 0xA1 > + PERFORMANCE_USTT 0xA4 > + LOW_POWER_USTT 0xA5 > + > + GMODE 0xAB > + > +Usually if a model doesn't support the first four profiles they will support > +the User Selectable Thermal Tables (USTT) profiles and vice-versa. > + > +GMODE replaces PERFORMANCE in G-Series laptops. > + > +Very grateful to `AlexIII <https://github.com/AlexIII/tcc-g15>`_ for discovering > +some of the codes compatible with G-Series laptops. Maybe use some less personal wording to give kudos and perhaps put it into the end of the doc. -- i. > +WMI method GetFanSensors([in] uint32 arg2, [out] uint32 argr) > +------------------------------------------------------------- > + > +:: > + > + if BYTE_0(arg2) == 1: > + if is_valid_fan(BYTE_1(arg2)): > + argr = 1 > + else: > + argr = 0 > + > + if BYTE_0(arg2) == 2: > + if is_valid_fan(BYTE_1(arg2)): > + if BYTE_2(arg2) == 0: > + argr == SENSOR_ID > + else > + argr == 0xFFFFFFFF > + else: > + argr = 0 > + > +Overclocking Methods > +==================== > + > +.. warning:: > + These methods have not been tested and are only partially reverse > + engineered. > + > +WMI method Return_OverclockingReport([out] uint32 argr) > +------------------------------------------------------- > + > +:: > + > + CSMI (0xE3, 0x99) > + argr = 0 > + > +CSMI is an unknown operation. > + > +WMI method Set_OCUIBIOSControl([in] uint32 arg2, [out] uint32 argr) > +------------------------------------------------------------------- > + > +:: > + > + CSMI (0xE3, 0x99) > + argr = 0 > + > +CSMI is an unknown operation > + > +WMI method Clear_OCFailSafeFlag([out] uint32 argr) > +-------------------------------------------------- > + > +:: > + > + CSMI (0xE3, 0x99) > + argr = 0 > + > +CSMI is an unknown operation > + > + > +WMI method MemoryOCControl([in] uint32 arg2, [out] uint32 argr) > +--------------------------------------------------------------- > + > +AWCC supports memory overclocking, but this method is very intricate and has > +not been deciphered yet. > + > +GPIO methods > +============ > + > +These methods are probably related to some kind of firmware update system, > +through a GPIO device. > + > +.. warning:: > + These methods have not been tested and are only partially reverse > + engineered. > + > +WMI method FWUpdateGPIOtoggle([in] uint32 arg2, [out] uint32 argr) > +------------------------------------------------------------------ > + > +:: > + > + if BYTE_0(arg2) == 0: > + if BYTE_1(arg2) == 1: > + SET_PIN_A_HIGH() > + else: > + SET_PIN_A_LOW() > + > + if BYTE_0(arg2) == 1: > + if BYTE_1(arg2) == 1: > + SET_PIN_B_HIGH() > + > + else: > + SET_PIN_B_LOW() > + > + else: > + argr = 1 > + > +WMI method ReadTotalofGPIOs([out] uint32 argr) > +---------------------------------------------- > + > +:: > + > + argr = 0x02 > + > +WMI method ReadGPIOpPinStatus([in] uint32 arg2, [out] uint32 argr) > +------------------------------------------------------------------ > + > +:: > + > + if BYTE_0(arg2) == 0: > + argr = PIN_A_STATUS > + > + if BYTE_0(arg2) == 1: > + argr = PIN_B_STATUS > + > + > +Other information Methods > +========================= > + > +WMI method SystemInformation([in] uint32 arg2, [out] uint32 argr) > +----------------------------------------------------------------- > + > +Returns unknown information. > + > +WMI method PowerInformation([in] uint32 arg2, [out] uint32 argr) > +---------------------------------------------------------------- > + > +Returns unknown information. > + > +WMI method ReadChassisColor([out] uint32 argr) > +---------------------------------------------- > + > +:: > + > + argr = CHASSIS_COLOR_ID > + > +WMI method ReadPlatformProperties([out] uint32 argr) > +---------------------------------------------------- > + > +Returns unknown information. > + >
This patch adds platform_profile support for Dell devices which implement User Selectable Thermal Tables (USTT) that are meant to be controlled by Alienware Command Center (AWCC). These devices may include newer Alienware M-Series, Alienware X-Series and Dell's G-Series. This patch, was tested by me on an Alienware x15 R1. --- v5: - Better commit messages - insize renamed to in_size in alienware_wmax_command() to match other arguments. - Kudos in documentation now at the end of the file v4: - Fixed indentation on previous code - Removed unnecessary (acpi_size) and (u32 *) casts - Return -EIO on ACPI_FAILURE - Appropiate prefixes given to macros - 0xFFFFFFFF named WMAX_FAILURE_CODE - Added support for a new set of thermal codes. Old ones now have USTT in their names - A new quirk has been added to differantiate between the two sets. thermal and thermal_ustt are mutually exclusive - Added documentation for WMAX interface v3: - Removed extra empty line - 0x0B named WMAX_ARG_GET_CURRENT_PROF - Removed casts to the same type on functions added in this patch - Thermal profile to WMAX argument is now an static function and makes use of in-built kernel macros - Platform profile is now removed only if it was created first - create_platform_profile is now create_thermal_profile to avoid confusion - profile_get and profile_set functions renamed too to match the above v2: - Moved functionality to alienware-wmi driver - Added thermal and gmode quirks to add support based on dmi match - Performance profile is now GMODE for devices that support it - alienware_wmax_command now is insize agnostic to support new thermal methods Kurt Borja (4): alienware-wmi: fixed indentation and clean up alienware-wmi: alienware_wmax_command() is now input size agnostic alienware-wmi: added platform profile support alienware-wmi: WMAX interface documentation Documentation/wmi/devices/alienware-wmi.rst | 366 ++++++++++++++++++ drivers/platform/x86/dell/Kconfig | 1 + drivers/platform/x86/dell/alienware-wmi.c | 389 ++++++++++++++++---- 3 files changed, 680 insertions(+), 76 deletions(-) create mode 100644 Documentation/wmi/devices/alienware-wmi.rst -- 2.47.0
Fixed inconsistent indentation and removed unnecessary (acpi_size) and
(u32 *) casts.
Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
drivers/platform/x86/dell/alienware-wmi.c | 134 +++++++++++-----------
1 file changed, 67 insertions(+), 67 deletions(-)
diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
index f5ee62ce1..16a3fe9ac 100644
--- a/drivers/platform/x86/dell/alienware-wmi.c
+++ b/drivers/platform/x86/dell/alienware-wmi.c
@@ -116,68 +116,68 @@ static int __init dmi_matched(const struct dmi_system_id *dmi)
static const struct dmi_system_id alienware_quirks[] __initconst = {
{
- .callback = dmi_matched,
- .ident = "Alienware X51 R3",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
- DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"),
- },
- .driver_data = &quirk_x51_r3,
- },
+ .callback = dmi_matched,
+ .ident = "Alienware X51 R3",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"),
+ },
+ .driver_data = &quirk_x51_r3,
+ },
{
- .callback = dmi_matched,
- .ident = "Alienware X51 R2",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
- DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"),
- },
- .driver_data = &quirk_x51_r1_r2,
- },
+ .callback = dmi_matched,
+ .ident = "Alienware X51 R2",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"),
+ },
+ .driver_data = &quirk_x51_r1_r2,
+ },
{
- .callback = dmi_matched,
- .ident = "Alienware X51 R1",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
- DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"),
- },
- .driver_data = &quirk_x51_r1_r2,
- },
+ .callback = dmi_matched,
+ .ident = "Alienware X51 R1",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"),
+ },
+ .driver_data = &quirk_x51_r1_r2,
+ },
{
- .callback = dmi_matched,
- .ident = "Alienware ASM100",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
- DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"),
- },
- .driver_data = &quirk_asm100,
- },
+ .callback = dmi_matched,
+ .ident = "Alienware ASM100",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"),
+ },
+ .driver_data = &quirk_asm100,
+ },
{
- .callback = dmi_matched,
- .ident = "Alienware ASM200",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
- DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"),
- },
- .driver_data = &quirk_asm200,
- },
+ .callback = dmi_matched,
+ .ident = "Alienware ASM200",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"),
+ },
+ .driver_data = &quirk_asm200,
+ },
{
- .callback = dmi_matched,
- .ident = "Alienware ASM201",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
- DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"),
- },
- .driver_data = &quirk_asm201,
- },
- {
- .callback = dmi_matched,
- .ident = "Dell Inc. Inspiron 5675",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"),
- },
- .driver_data = &quirk_inspiron5675,
- },
+ .callback = dmi_matched,
+ .ident = "Alienware ASM201",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"),
+ },
+ .driver_data = &quirk_asm201,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Dell Inc. Inspiron 5675",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"),
+ },
+ .driver_data = &quirk_inspiron5675,
+ },
{}
};
@@ -221,8 +221,8 @@ static struct platform_zone *zone_data;
static struct platform_driver platform_driver = {
.driver = {
- .name = "alienware-wmi",
- }
+ .name = "alienware-wmi",
+ }
};
static struct attribute_group zone_attribute_group = {
@@ -292,7 +292,7 @@ static int alienware_update_led(struct platform_zone *zone)
guid = WMAX_CONTROL_GUID;
method_id = WMAX_METHOD_ZONE_CONTROL;
- input.length = (acpi_size) sizeof(wmax_basic_args);
+ input.length = sizeof(wmax_basic_args);
input.pointer = &wmax_basic_args;
} else {
legacy_args.colors = zone->colors;
@@ -306,7 +306,7 @@ static int alienware_update_led(struct platform_zone *zone)
guid = LEGACY_CONTROL_GUID;
method_id = zone->location + 1;
- input.length = (acpi_size) sizeof(legacy_args);
+ input.length = sizeof(legacy_args);
input.pointer = &legacy_args;
}
pr_debug("alienware-wmi: guid %s method %d\n", guid, method_id);
@@ -358,7 +358,7 @@ static int wmax_brightness(int brightness)
.led_mask = 0xFF,
.percentage = brightness,
};
- input.length = (acpi_size) sizeof(args);
+ input.length = sizeof(args);
input.pointer = &args;
status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0,
WMAX_METHOD_BRIGHTNESS, &input, NULL);
@@ -508,7 +508,7 @@ static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
struct acpi_buffer input;
struct acpi_buffer output;
- input.length = (acpi_size) sizeof(*in_args);
+ input.length = sizeof(*in_args);
input.pointer = in_args;
if (out_data) {
output.length = ACPI_ALLOCATE_BUFFER;
@@ -542,7 +542,7 @@ static ssize_t show_hdmi_cable(struct device *dev,
};
status =
alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE,
- (u32 *) &out_data);
+ &out_data);
if (ACPI_SUCCESS(status)) {
if (out_data == 0)
return sysfs_emit(buf, "[unconnected] connected unknown\n");
@@ -563,7 +563,7 @@ static ssize_t show_hdmi_source(struct device *dev,
};
status =
alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS,
- (u32 *) &out_data);
+ &out_data);
if (ACPI_SUCCESS(status)) {
if (out_data == 1)
@@ -643,7 +643,7 @@ static ssize_t show_amplifier_status(struct device *dev,
};
status =
alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE,
- (u32 *) &out_data);
+ &out_data);
if (ACPI_SUCCESS(status)) {
if (out_data == 0)
return sysfs_emit(buf, "[unconnected] connected unknown\n");
@@ -695,7 +695,7 @@ static ssize_t show_deepsleep_status(struct device *dev,
.arg = 0,
};
status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS,
- (u32 *) &out_data);
+ &out_data);
if (ACPI_SUCCESS(status)) {
if (out_data == 0)
return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
--
2.47.0
Am 12.10.24 um 04:01 schrieb Kurt Borja: > Fixed inconsistent indentation and removed unnecessary (acpi_size) and > (u32 *) casts. Reviewed-by: Armin Wolf <W_Armin@gmx.de > > Signed-off-by: Kurt Borja <kuurtb@gmail.com> > --- > drivers/platform/x86/dell/alienware-wmi.c | 134 +++++++++++----------- > 1 file changed, 67 insertions(+), 67 deletions(-) > > diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c > index f5ee62ce1..16a3fe9ac 100644 > --- a/drivers/platform/x86/dell/alienware-wmi.c > +++ b/drivers/platform/x86/dell/alienware-wmi.c > @@ -116,68 +116,68 @@ static int __init dmi_matched(const struct dmi_system_id *dmi) > > static const struct dmi_system_id alienware_quirks[] __initconst = { > { > - .callback = dmi_matched, > - .ident = "Alienware X51 R3", > - .matches = { > - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > - DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"), > - }, > - .driver_data = &quirk_x51_r3, > - }, > + .callback = dmi_matched, > + .ident = "Alienware X51 R3", > + .matches = { > + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"), > + }, > + .driver_data = &quirk_x51_r3, > + }, > { > - .callback = dmi_matched, > - .ident = "Alienware X51 R2", > - .matches = { > - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > - DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"), > - }, > - .driver_data = &quirk_x51_r1_r2, > - }, > + .callback = dmi_matched, > + .ident = "Alienware X51 R2", > + .matches = { > + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"), > + }, > + .driver_data = &quirk_x51_r1_r2, > + }, > { > - .callback = dmi_matched, > - .ident = "Alienware X51 R1", > - .matches = { > - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > - DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"), > - }, > - .driver_data = &quirk_x51_r1_r2, > - }, > + .callback = dmi_matched, > + .ident = "Alienware X51 R1", > + .matches = { > + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"), > + }, > + .driver_data = &quirk_x51_r1_r2, > + }, > { > - .callback = dmi_matched, > - .ident = "Alienware ASM100", > - .matches = { > - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > - DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"), > - }, > - .driver_data = &quirk_asm100, > - }, > + .callback = dmi_matched, > + .ident = "Alienware ASM100", > + .matches = { > + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > + DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"), > + }, > + .driver_data = &quirk_asm100, > + }, > { > - .callback = dmi_matched, > - .ident = "Alienware ASM200", > - .matches = { > - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > - DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"), > - }, > - .driver_data = &quirk_asm200, > - }, > + .callback = dmi_matched, > + .ident = "Alienware ASM200", > + .matches = { > + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > + DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"), > + }, > + .driver_data = &quirk_asm200, > + }, > { > - .callback = dmi_matched, > - .ident = "Alienware ASM201", > - .matches = { > - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > - DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"), > - }, > - .driver_data = &quirk_asm201, > - }, > - { > - .callback = dmi_matched, > - .ident = "Dell Inc. Inspiron 5675", > - .matches = { > - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), > - DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"), > - }, > - .driver_data = &quirk_inspiron5675, > - }, > + .callback = dmi_matched, > + .ident = "Alienware ASM201", > + .matches = { > + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > + DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"), > + }, > + .driver_data = &quirk_asm201, > + }, > + { > + .callback = dmi_matched, > + .ident = "Dell Inc. Inspiron 5675", > + .matches = { > + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), > + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"), > + }, > + .driver_data = &quirk_inspiron5675, > + }, > {} > }; > > @@ -221,8 +221,8 @@ static struct platform_zone *zone_data; > > static struct platform_driver platform_driver = { > .driver = { > - .name = "alienware-wmi", > - } > + .name = "alienware-wmi", > + } > }; > > static struct attribute_group zone_attribute_group = { > @@ -292,7 +292,7 @@ static int alienware_update_led(struct platform_zone *zone) > guid = WMAX_CONTROL_GUID; > method_id = WMAX_METHOD_ZONE_CONTROL; > > - input.length = (acpi_size) sizeof(wmax_basic_args); > + input.length = sizeof(wmax_basic_args); > input.pointer = &wmax_basic_args; > } else { > legacy_args.colors = zone->colors; > @@ -306,7 +306,7 @@ static int alienware_update_led(struct platform_zone *zone) > guid = LEGACY_CONTROL_GUID; > method_id = zone->location + 1; > > - input.length = (acpi_size) sizeof(legacy_args); > + input.length = sizeof(legacy_args); > input.pointer = &legacy_args; > } > pr_debug("alienware-wmi: guid %s method %d\n", guid, method_id); > @@ -358,7 +358,7 @@ static int wmax_brightness(int brightness) > .led_mask = 0xFF, > .percentage = brightness, > }; > - input.length = (acpi_size) sizeof(args); > + input.length = sizeof(args); > input.pointer = &args; > status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0, > WMAX_METHOD_BRIGHTNESS, &input, NULL); > @@ -508,7 +508,7 @@ static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args, > struct acpi_buffer input; > struct acpi_buffer output; > > - input.length = (acpi_size) sizeof(*in_args); > + input.length = sizeof(*in_args); > input.pointer = in_args; > if (out_data) { > output.length = ACPI_ALLOCATE_BUFFER; > @@ -542,7 +542,7 @@ static ssize_t show_hdmi_cable(struct device *dev, > }; > status = > alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE, > - (u32 *) &out_data); > + &out_data); > if (ACPI_SUCCESS(status)) { > if (out_data == 0) > return sysfs_emit(buf, "[unconnected] connected unknown\n"); > @@ -563,7 +563,7 @@ static ssize_t show_hdmi_source(struct device *dev, > }; > status = > alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS, > - (u32 *) &out_data); > + &out_data); > > if (ACPI_SUCCESS(status)) { > if (out_data == 1) > @@ -643,7 +643,7 @@ static ssize_t show_amplifier_status(struct device *dev, > }; > status = > alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE, > - (u32 *) &out_data); > + &out_data); > if (ACPI_SUCCESS(status)) { > if (out_data == 0) > return sysfs_emit(buf, "[unconnected] connected unknown\n"); > @@ -695,7 +695,7 @@ static ssize_t show_deepsleep_status(struct device *dev, > .arg = 0, > }; > status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS, > - (u32 *) &out_data); > + &out_data); > if (ACPI_SUCCESS(status)) { > if (out_data == 0) > return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
On Mon, Oct 14, 2024 at 06:26:45PM +0200, Armin Wolf wrote: > Am 12.10.24 um 04:01 schrieb Kurt Borja: > > > Fixed inconsistent indentation and removed unnecessary (acpi_size) and > > (u32 *) casts. > > Reviewed-by: Armin Wolf <W_Armin@gmx.de > Thank you for your review. Kurt > > > > Signed-off-by: Kurt Borja <kuurtb@gmail.com> > > --- > > drivers/platform/x86/dell/alienware-wmi.c | 134 +++++++++++----------- > > 1 file changed, 67 insertions(+), 67 deletions(-) > > > > diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c > > index f5ee62ce1..16a3fe9ac 100644 > > --- a/drivers/platform/x86/dell/alienware-wmi.c > > +++ b/drivers/platform/x86/dell/alienware-wmi.c > > @@ -116,68 +116,68 @@ static int __init dmi_matched(const struct dmi_system_id *dmi) > > > > static const struct dmi_system_id alienware_quirks[] __initconst = { > > { > > - .callback = dmi_matched, > > - .ident = "Alienware X51 R3", > > - .matches = { > > - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > > - DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"), > > - }, > > - .driver_data = &quirk_x51_r3, > > - }, > > + .callback = dmi_matched, > > + .ident = "Alienware X51 R3", > > + .matches = { > > + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > > + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"), > > + }, > > + .driver_data = &quirk_x51_r3, > > + }, > > { > > - .callback = dmi_matched, > > - .ident = "Alienware X51 R2", > > - .matches = { > > - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > > - DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"), > > - }, > > - .driver_data = &quirk_x51_r1_r2, > > - }, > > + .callback = dmi_matched, > > + .ident = "Alienware X51 R2", > > + .matches = { > > + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > > + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"), > > + }, > > + .driver_data = &quirk_x51_r1_r2, > > + }, > > { > > - .callback = dmi_matched, > > - .ident = "Alienware X51 R1", > > - .matches = { > > - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > > - DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"), > > - }, > > - .driver_data = &quirk_x51_r1_r2, > > - }, > > + .callback = dmi_matched, > > + .ident = "Alienware X51 R1", > > + .matches = { > > + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > > + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"), > > + }, > > + .driver_data = &quirk_x51_r1_r2, > > + }, > > { > > - .callback = dmi_matched, > > - .ident = "Alienware ASM100", > > - .matches = { > > - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > > - DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"), > > - }, > > - .driver_data = &quirk_asm100, > > - }, > > + .callback = dmi_matched, > > + .ident = "Alienware ASM100", > > + .matches = { > > + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > > + DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"), > > + }, > > + .driver_data = &quirk_asm100, > > + }, > > { > > - .callback = dmi_matched, > > - .ident = "Alienware ASM200", > > - .matches = { > > - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > > - DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"), > > - }, > > - .driver_data = &quirk_asm200, > > - }, > > + .callback = dmi_matched, > > + .ident = "Alienware ASM200", > > + .matches = { > > + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > > + DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"), > > + }, > > + .driver_data = &quirk_asm200, > > + }, > > { > > - .callback = dmi_matched, > > - .ident = "Alienware ASM201", > > - .matches = { > > - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > > - DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"), > > - }, > > - .driver_data = &quirk_asm201, > > - }, > > - { > > - .callback = dmi_matched, > > - .ident = "Dell Inc. Inspiron 5675", > > - .matches = { > > - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), > > - DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"), > > - }, > > - .driver_data = &quirk_inspiron5675, > > - }, > > + .callback = dmi_matched, > > + .ident = "Alienware ASM201", > > + .matches = { > > + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > > + DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"), > > + }, > > + .driver_data = &quirk_asm201, > > + }, > > + { > > + .callback = dmi_matched, > > + .ident = "Dell Inc. Inspiron 5675", > > + .matches = { > > + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), > > + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"), > > + }, > > + .driver_data = &quirk_inspiron5675, > > + }, > > {} > > }; > > > > @@ -221,8 +221,8 @@ static struct platform_zone *zone_data; > > > > static struct platform_driver platform_driver = { > > .driver = { > > - .name = "alienware-wmi", > > - } > > + .name = "alienware-wmi", > > + } > > }; > > > > static struct attribute_group zone_attribute_group = { > > @@ -292,7 +292,7 @@ static int alienware_update_led(struct platform_zone *zone) > > guid = WMAX_CONTROL_GUID; > > method_id = WMAX_METHOD_ZONE_CONTROL; > > > > - input.length = (acpi_size) sizeof(wmax_basic_args); > > + input.length = sizeof(wmax_basic_args); > > input.pointer = &wmax_basic_args; > > } else { > > legacy_args.colors = zone->colors; > > @@ -306,7 +306,7 @@ static int alienware_update_led(struct platform_zone *zone) > > guid = LEGACY_CONTROL_GUID; > > method_id = zone->location + 1; > > > > - input.length = (acpi_size) sizeof(legacy_args); > > + input.length = sizeof(legacy_args); > > input.pointer = &legacy_args; > > } > > pr_debug("alienware-wmi: guid %s method %d\n", guid, method_id); > > @@ -358,7 +358,7 @@ static int wmax_brightness(int brightness) > > .led_mask = 0xFF, > > .percentage = brightness, > > }; > > - input.length = (acpi_size) sizeof(args); > > + input.length = sizeof(args); > > input.pointer = &args; > > status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0, > > WMAX_METHOD_BRIGHTNESS, &input, NULL); > > @@ -508,7 +508,7 @@ static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args, > > struct acpi_buffer input; > > struct acpi_buffer output; > > > > - input.length = (acpi_size) sizeof(*in_args); > > + input.length = sizeof(*in_args); > > input.pointer = in_args; > > if (out_data) { > > output.length = ACPI_ALLOCATE_BUFFER; > > @@ -542,7 +542,7 @@ static ssize_t show_hdmi_cable(struct device *dev, > > }; > > status = > > alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE, > > - (u32 *) &out_data); > > + &out_data); > > if (ACPI_SUCCESS(status)) { > > if (out_data == 0) > > return sysfs_emit(buf, "[unconnected] connected unknown\n"); > > @@ -563,7 +563,7 @@ static ssize_t show_hdmi_source(struct device *dev, > > }; > > status = > > alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS, > > - (u32 *) &out_data); > > + &out_data); > > > > if (ACPI_SUCCESS(status)) { > > if (out_data == 1) > > @@ -643,7 +643,7 @@ static ssize_t show_amplifier_status(struct device *dev, > > }; > > status = > > alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE, > > - (u32 *) &out_data); > > + &out_data); > > if (ACPI_SUCCESS(status)) { > > if (out_data == 0) > > return sysfs_emit(buf, "[unconnected] connected unknown\n"); > > @@ -695,7 +695,7 @@ static ssize_t show_deepsleep_status(struct device *dev, > > .arg = 0, > > }; > > status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS, > > - (u32 *) &out_data); > > + &out_data); > > if (ACPI_SUCCESS(status)) { > > if (out_data == 0) > > return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
alienware_wmax_command() now takes void * and size_t instead of struct
wmax_basic_args to extend support to new WMAX methods. Also int *out_data
was changed to u32 *out_data, because new interface specifies u32 as output
parameter and all previous callers would pass u32 * regardless.
Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
drivers/platform/x86/dell/alienware-wmi.c | 29 ++++++++++++-----------
1 file changed, 15 insertions(+), 14 deletions(-)
diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
index 16a3fe9ac..b27f3b64c 100644
--- a/drivers/platform/x86/dell/alienware-wmi.c
+++ b/drivers/platform/x86/dell/alienware-wmi.c
@@ -500,15 +500,15 @@ static void alienware_zone_exit(struct platform_device *dev)
kfree(zone_attrs);
}
-static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
- u32 command, int *out_data)
+static acpi_status alienware_wmax_command(void *in_args, size_t in_size,
+ u32 command, u32 *out_data)
{
acpi_status status;
union acpi_object *obj;
struct acpi_buffer input;
struct acpi_buffer output;
- input.length = sizeof(*in_args);
+ input.length = in_size;
input.pointer = in_args;
if (out_data) {
output.length = ACPI_ALLOCATE_BUFFER;
@@ -541,8 +541,8 @@ static ssize_t show_hdmi_cable(struct device *dev,
.arg = 0,
};
status =
- alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE,
- &out_data);
+ alienware_wmax_command(&in_args, sizeof(in_args),
+ WMAX_METHOD_HDMI_CABLE, &out_data);
if (ACPI_SUCCESS(status)) {
if (out_data == 0)
return sysfs_emit(buf, "[unconnected] connected unknown\n");
@@ -562,8 +562,8 @@ static ssize_t show_hdmi_source(struct device *dev,
.arg = 0,
};
status =
- alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS,
- &out_data);
+ alienware_wmax_command(&in_args, sizeof(in_args),
+ WMAX_METHOD_HDMI_STATUS, &out_data);
if (ACPI_SUCCESS(status)) {
if (out_data == 1)
@@ -589,7 +589,8 @@ static ssize_t toggle_hdmi_source(struct device *dev,
args.arg = 3;
pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf);
- status = alienware_wmax_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL);
+ status = alienware_wmax_command(&args, sizeof(args),
+ WMAX_METHOD_HDMI_SOURCE, NULL);
if (ACPI_FAILURE(status))
pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
@@ -642,8 +643,8 @@ static ssize_t show_amplifier_status(struct device *dev,
.arg = 0,
};
status =
- alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE,
- &out_data);
+ alienware_wmax_command(&in_args, sizeof(in_args),
+ WMAX_METHOD_AMPLIFIER_CABLE, &out_data);
if (ACPI_SUCCESS(status)) {
if (out_data == 0)
return sysfs_emit(buf, "[unconnected] connected unknown\n");
@@ -694,8 +695,8 @@ static ssize_t show_deepsleep_status(struct device *dev,
struct wmax_basic_args in_args = {
.arg = 0,
};
- status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS,
- &out_data);
+ status = alienware_wmax_command(&in_args, sizeof(in_args),
+ WMAX_METHOD_DEEP_SLEEP_STATUS, &out_data);
if (ACPI_SUCCESS(status)) {
if (out_data == 0)
return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
@@ -723,8 +724,8 @@ static ssize_t toggle_deepsleep(struct device *dev,
args.arg = 2;
pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf);
- status = alienware_wmax_command(&args, WMAX_METHOD_DEEP_SLEEP_CONTROL,
- NULL);
+ status = alienware_wmax_command(&args, sizeof(args),
+ WMAX_METHOD_DEEP_SLEEP_CONTROL, NULL);
if (ACPI_FAILURE(status))
pr_err("alienware-wmi: deep sleep control failed: results: %u\n",
--
2.47.0
Am 12.10.24 um 04:01 schrieb Kurt Borja: > alienware_wmax_command() now takes void * and size_t instead of struct > wmax_basic_args to extend support to new WMAX methods. Also int *out_data > was changed to u32 *out_data, because new interface specifies u32 as output > parameter and all previous callers would pass u32 * regardless. Reviewed-by: Armin Wolf <W_Armin@gmx.de> > Signed-off-by: Kurt Borja <kuurtb@gmail.com> > --- > drivers/platform/x86/dell/alienware-wmi.c | 29 ++++++++++++----------- > 1 file changed, 15 insertions(+), 14 deletions(-) > > diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c > index 16a3fe9ac..b27f3b64c 100644 > --- a/drivers/platform/x86/dell/alienware-wmi.c > +++ b/drivers/platform/x86/dell/alienware-wmi.c > @@ -500,15 +500,15 @@ static void alienware_zone_exit(struct platform_device *dev) > kfree(zone_attrs); > } > > -static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args, > - u32 command, int *out_data) > +static acpi_status alienware_wmax_command(void *in_args, size_t in_size, > + u32 command, u32 *out_data) > { > acpi_status status; > union acpi_object *obj; > struct acpi_buffer input; > struct acpi_buffer output; > > - input.length = sizeof(*in_args); > + input.length = in_size; > input.pointer = in_args; > if (out_data) { > output.length = ACPI_ALLOCATE_BUFFER; > @@ -541,8 +541,8 @@ static ssize_t show_hdmi_cable(struct device *dev, > .arg = 0, > }; > status = > - alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE, > - &out_data); > + alienware_wmax_command(&in_args, sizeof(in_args), > + WMAX_METHOD_HDMI_CABLE, &out_data); > if (ACPI_SUCCESS(status)) { > if (out_data == 0) > return sysfs_emit(buf, "[unconnected] connected unknown\n"); > @@ -562,8 +562,8 @@ static ssize_t show_hdmi_source(struct device *dev, > .arg = 0, > }; > status = > - alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS, > - &out_data); > + alienware_wmax_command(&in_args, sizeof(in_args), > + WMAX_METHOD_HDMI_STATUS, &out_data); > > if (ACPI_SUCCESS(status)) { > if (out_data == 1) > @@ -589,7 +589,8 @@ static ssize_t toggle_hdmi_source(struct device *dev, > args.arg = 3; > pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf); > > - status = alienware_wmax_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL); > + status = alienware_wmax_command(&args, sizeof(args), > + WMAX_METHOD_HDMI_SOURCE, NULL); > > if (ACPI_FAILURE(status)) > pr_err("alienware-wmi: HDMI toggle failed: results: %u\n", > @@ -642,8 +643,8 @@ static ssize_t show_amplifier_status(struct device *dev, > .arg = 0, > }; > status = > - alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE, > - &out_data); > + alienware_wmax_command(&in_args, sizeof(in_args), > + WMAX_METHOD_AMPLIFIER_CABLE, &out_data); > if (ACPI_SUCCESS(status)) { > if (out_data == 0) > return sysfs_emit(buf, "[unconnected] connected unknown\n"); > @@ -694,8 +695,8 @@ static ssize_t show_deepsleep_status(struct device *dev, > struct wmax_basic_args in_args = { > .arg = 0, > }; > - status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS, > - &out_data); > + status = alienware_wmax_command(&in_args, sizeof(in_args), > + WMAX_METHOD_DEEP_SLEEP_STATUS, &out_data); > if (ACPI_SUCCESS(status)) { > if (out_data == 0) > return sysfs_emit(buf, "[disabled] s5 s5_s4\n"); > @@ -723,8 +724,8 @@ static ssize_t toggle_deepsleep(struct device *dev, > args.arg = 2; > pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf); > > - status = alienware_wmax_command(&args, WMAX_METHOD_DEEP_SLEEP_CONTROL, > - NULL); > + status = alienware_wmax_command(&args, sizeof(args), > + WMAX_METHOD_DEEP_SLEEP_CONTROL, NULL); > > if (ACPI_FAILURE(status)) > pr_err("alienware-wmi: deep sleep control failed: results: %u\n",
On Mon, Oct 14, 2024 at 06:30:21PM +0200, Armin Wolf wrote: > Am 12.10.24 um 04:01 schrieb Kurt Borja: > > > alienware_wmax_command() now takes void * and size_t instead of struct > > wmax_basic_args to extend support to new WMAX methods. Also int *out_data > > was changed to u32 *out_data, because new interface specifies u32 as output > > parameter and all previous callers would pass u32 * regardless. > > Reviewed-by: Armin Wolf <W_Armin@gmx.de> > Thank you very much. Kurt > > Signed-off-by: Kurt Borja <kuurtb@gmail.com> > > --- > > drivers/platform/x86/dell/alienware-wmi.c | 29 ++++++++++++----------- > > 1 file changed, 15 insertions(+), 14 deletions(-) > > > > diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c > > index 16a3fe9ac..b27f3b64c 100644 > > --- a/drivers/platform/x86/dell/alienware-wmi.c > > +++ b/drivers/platform/x86/dell/alienware-wmi.c > > @@ -500,15 +500,15 @@ static void alienware_zone_exit(struct platform_device *dev) > > kfree(zone_attrs); > > } > > > > -static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args, > > - u32 command, int *out_data) > > +static acpi_status alienware_wmax_command(void *in_args, size_t in_size, > > + u32 command, u32 *out_data) > > { > > acpi_status status; > > union acpi_object *obj; > > struct acpi_buffer input; > > struct acpi_buffer output; > > > > - input.length = sizeof(*in_args); > > + input.length = in_size; > > input.pointer = in_args; > > if (out_data) { > > output.length = ACPI_ALLOCATE_BUFFER; > > @@ -541,8 +541,8 @@ static ssize_t show_hdmi_cable(struct device *dev, > > .arg = 0, > > }; > > status = > > - alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE, > > - &out_data); > > + alienware_wmax_command(&in_args, sizeof(in_args), > > + WMAX_METHOD_HDMI_CABLE, &out_data); > > if (ACPI_SUCCESS(status)) { > > if (out_data == 0) > > return sysfs_emit(buf, "[unconnected] connected unknown\n"); > > @@ -562,8 +562,8 @@ static ssize_t show_hdmi_source(struct device *dev, > > .arg = 0, > > }; > > status = > > - alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS, > > - &out_data); > > + alienware_wmax_command(&in_args, sizeof(in_args), > > + WMAX_METHOD_HDMI_STATUS, &out_data); > > > > if (ACPI_SUCCESS(status)) { > > if (out_data == 1) > > @@ -589,7 +589,8 @@ static ssize_t toggle_hdmi_source(struct device *dev, > > args.arg = 3; > > pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf); > > > > - status = alienware_wmax_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL); > > + status = alienware_wmax_command(&args, sizeof(args), > > + WMAX_METHOD_HDMI_SOURCE, NULL); > > > > if (ACPI_FAILURE(status)) > > pr_err("alienware-wmi: HDMI toggle failed: results: %u\n", > > @@ -642,8 +643,8 @@ static ssize_t show_amplifier_status(struct device *dev, > > .arg = 0, > > }; > > status = > > - alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE, > > - &out_data); > > + alienware_wmax_command(&in_args, sizeof(in_args), > > + WMAX_METHOD_AMPLIFIER_CABLE, &out_data); > > if (ACPI_SUCCESS(status)) { > > if (out_data == 0) > > return sysfs_emit(buf, "[unconnected] connected unknown\n"); > > @@ -694,8 +695,8 @@ static ssize_t show_deepsleep_status(struct device *dev, > > struct wmax_basic_args in_args = { > > .arg = 0, > > }; > > - status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS, > > - &out_data); > > + status = alienware_wmax_command(&in_args, sizeof(in_args), > > + WMAX_METHOD_DEEP_SLEEP_STATUS, &out_data); > > if (ACPI_SUCCESS(status)) { > > if (out_data == 0) > > return sysfs_emit(buf, "[disabled] s5 s5_s4\n"); > > @@ -723,8 +724,8 @@ static ssize_t toggle_deepsleep(struct device *dev, > > args.arg = 2; > > pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf); > > > > - status = alienware_wmax_command(&args, WMAX_METHOD_DEEP_SLEEP_CONTROL, > > - NULL); > > + status = alienware_wmax_command(&args, sizeof(args), > > + WMAX_METHOD_DEEP_SLEEP_CONTROL, NULL); > > > > if (ACPI_FAILURE(status)) > > pr_err("alienware-wmi: deep sleep control failed: results: %u\n",
Implements platform profile support for Dell laptops with new WMAX
thermal interface, present on some Alienware X-Series, Alienware
M-Series and Dell's G-Series laptops. This implementation supports two
sets of thermal profile codes, namely *thermal* and *thermal_ustt*, plus
additional quirk *gmode* for Dell's G-Series laptops.
Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
drivers/platform/x86/dell/Kconfig | 1 +
drivers/platform/x86/dell/alienware-wmi.c | 236 ++++++++++++++++++++++
2 files changed, 237 insertions(+)
diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig
index 68a49788a..b06d634cd 100644
--- a/drivers/platform/x86/dell/Kconfig
+++ b/drivers/platform/x86/dell/Kconfig
@@ -21,6 +21,7 @@ config ALIENWARE_WMI
depends on LEDS_CLASS
depends on NEW_LEDS
depends on ACPI_WMI
+ select ACPI_PLATFORM_PROFILE
help
This is a driver for controlling Alienware BIOS driven
features. It exposes an interface for controlling the AlienFX
diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
index b27f3b64c..6e30e9376 100644
--- a/drivers/platform/x86/dell/alienware-wmi.c
+++ b/drivers/platform/x86/dell/alienware-wmi.c
@@ -8,8 +8,11 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/acpi.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/platform_profile.h>
#include <linux/dmi.h>
#include <linux/leds.h>
@@ -25,6 +28,12 @@
#define WMAX_METHOD_AMPLIFIER_CABLE 0x6
#define WMAX_METHOD_DEEP_SLEEP_CONTROL 0x0B
#define WMAX_METHOD_DEEP_SLEEP_STATUS 0x0C
+#define WMAX_METHOD_THERMAL_INFORMATION 0x14
+#define WMAX_METHOD_THERMAL_CONTROL 0x15
+
+#define WMAX_ARG_GET_CURRENT_PROF 0x0B
+
+#define WMAX_FAILURE_CODE 0xFFFFFFFF
MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
MODULE_DESCRIPTION("Alienware special feature control");
@@ -49,11 +58,27 @@ enum WMAX_CONTROL_STATES {
WMAX_SUSPEND = 3,
};
+enum WMAX_THERMAL_PROFILE {
+ WMAX_THERMAL_QUIET = 0x96,
+ WMAX_THERMAL_BALANCED = 0x97,
+ WMAX_THERMAL_BALANCED_PERFORMANCE = 0x98,
+ WMAX_THERMAL_PERFORMANCE = 0x99,
+ WMAX_THERMAL_USTT_LOW_POWER = 0xA5,
+ WMAX_THERMAL_USTT_QUIET = 0xA3,
+ WMAX_THERMAL_USTT_BALANCED = 0xA0,
+ WMAX_THERMAL_USTT_BALANCED_PERFORMANCE = 0xA1,
+ WMAX_THERMAL_USTT_PERFORMANCE = 0xA4,
+ WMAX_THERMAL_GMODE = 0xAB,
+};
+
struct quirk_entry {
u8 num_zones;
u8 hdmi_mux;
u8 amplifier;
u8 deepslp;
+ u8 thermal;
+ u8 thermal_ustt;
+ u8 gmode;
};
static struct quirk_entry *quirks;
@@ -64,6 +89,9 @@ static struct quirk_entry quirk_inspiron5675 = {
.hdmi_mux = 0,
.amplifier = 0,
.deepslp = 0,
+ .thermal = 0,
+ .thermal_ustt = 0,
+ .gmode = 0,
};
static struct quirk_entry quirk_unknown = {
@@ -71,6 +99,9 @@ static struct quirk_entry quirk_unknown = {
.hdmi_mux = 0,
.amplifier = 0,
.deepslp = 0,
+ .thermal = 0,
+ .thermal_ustt = 0,
+ .gmode = 0,
};
static struct quirk_entry quirk_x51_r1_r2 = {
@@ -78,6 +109,9 @@ static struct quirk_entry quirk_x51_r1_r2 = {
.hdmi_mux = 0,
.amplifier = 0,
.deepslp = 0,
+ .thermal = 0,
+ .thermal_ustt = 0,
+ .gmode = 0,
};
static struct quirk_entry quirk_x51_r3 = {
@@ -85,6 +119,9 @@ static struct quirk_entry quirk_x51_r3 = {
.hdmi_mux = 0,
.amplifier = 1,
.deepslp = 0,
+ .thermal = 0,
+ .thermal_ustt = 0,
+ .gmode = 0,
};
static struct quirk_entry quirk_asm100 = {
@@ -92,6 +129,9 @@ static struct quirk_entry quirk_asm100 = {
.hdmi_mux = 1,
.amplifier = 0,
.deepslp = 0,
+ .thermal = 0,
+ .thermal_ustt = 0,
+ .gmode = 0,
};
static struct quirk_entry quirk_asm200 = {
@@ -99,6 +139,9 @@ static struct quirk_entry quirk_asm200 = {
.hdmi_mux = 1,
.amplifier = 0,
.deepslp = 1,
+ .thermal = 0,
+ .thermal_ustt = 0,
+ .gmode = 0,
};
static struct quirk_entry quirk_asm201 = {
@@ -106,6 +149,19 @@ static struct quirk_entry quirk_asm201 = {
.hdmi_mux = 1,
.amplifier = 1,
.deepslp = 1,
+ .thermal = 0,
+ .thermal_ustt = 0,
+ .gmode = 0,
+};
+
+static struct quirk_entry quirk_x15_r1 = {
+ .num_zones = 2,
+ .hdmi_mux = 0,
+ .amplifier = 0,
+ .deepslp = 0,
+ .thermal = 0,
+ .thermal_ustt = 1,
+ .gmode = 0,
};
static int __init dmi_matched(const struct dmi_system_id *dmi)
@@ -169,6 +225,15 @@ static const struct dmi_system_id alienware_quirks[] __initconst = {
},
.driver_data = &quirk_asm201,
},
+ {
+ .callback = dmi_matched,
+ .ident = "Alienware x15 R1",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R1")
+ },
+ .driver_data = &quirk_x15_r1,
+ },
{
.callback = dmi_matched,
.ident = "Dell Inc. Inspiron 5675",
@@ -218,6 +283,7 @@ static struct platform_device *platform_device;
static struct device_attribute *zone_dev_attrs;
static struct attribute **zone_attrs;
static struct platform_zone *zone_data;
+static struct platform_profile_handler pp_handler;
static struct platform_driver platform_driver = {
.driver = {
@@ -761,6 +827,168 @@ static int create_deepsleep(struct platform_device *dev)
return ret;
}
+/*
+ * Thermal Profile control
+ * - Provides thermal profile control through the Platform Profile API
+ */
+#define WMAX_PROFILE_MASK GENMASK(15, 8)
+#define WMAX_PROFILE_ACTIVATE BIT(0)
+
+static u32 profile_to_wmax_arg(enum WMAX_THERMAL_PROFILE prof)
+{
+ return FIELD_PREP(WMAX_PROFILE_MASK, prof) | WMAX_PROFILE_ACTIVATE;
+}
+
+static int thermal_profile_get(struct platform_profile_handler *pprof,
+ enum platform_profile_option *profile)
+{
+ acpi_status status;
+ u32 in_args = WMAX_ARG_GET_CURRENT_PROF;
+ u32 out_data;
+
+ status = alienware_wmax_command(&in_args, sizeof(in_args),
+ WMAX_METHOD_THERMAL_INFORMATION, &out_data);
+
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ if (out_data == WMAX_FAILURE_CODE)
+ return -EBADRQC;
+
+ switch (out_data) {
+ case WMAX_THERMAL_USTT_LOW_POWER:
+ *profile = PLATFORM_PROFILE_LOW_POWER;
+ break;
+ case WMAX_THERMAL_QUIET:
+ case WMAX_THERMAL_USTT_QUIET:
+ *profile = PLATFORM_PROFILE_QUIET;
+ break;
+ case WMAX_THERMAL_BALANCED:
+ case WMAX_THERMAL_USTT_BALANCED:
+ *profile = PLATFORM_PROFILE_BALANCED;
+ break;
+ case WMAX_THERMAL_BALANCED_PERFORMANCE:
+ case WMAX_THERMAL_USTT_BALANCED_PERFORMANCE:
+ *profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE;
+ break;
+ case WMAX_THERMAL_GMODE:
+ case WMAX_THERMAL_PERFORMANCE:
+ case WMAX_THERMAL_USTT_PERFORMANCE:
+ *profile = PLATFORM_PROFILE_PERFORMANCE;
+ break;
+ default:
+ return -ENODATA;
+ }
+
+ return 0;
+}
+
+static int thermal_profile_set(struct platform_profile_handler *pprof,
+ enum platform_profile_option profile)
+{
+ acpi_status status;
+ u32 in_args;
+ u32 out_data;
+
+ switch (profile) {
+ case PLATFORM_PROFILE_QUIET:
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET);
+ break;
+ case PLATFORM_PROFILE_BALANCED:
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED);
+ break;
+ case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE);
+ break;
+ case PLATFORM_PROFILE_PERFORMANCE:
+ if (quirks->gmode > 0)
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_GMODE);
+ else
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_PERFORMANCE);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ status = alienware_wmax_command(&in_args, sizeof(in_args),
+ WMAX_METHOD_THERMAL_CONTROL, &out_data);
+
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ if (out_data == WMAX_FAILURE_CODE)
+ return -EBADRQC;
+
+ return 0;
+}
+
+static int thermal_profile_set_ustt(struct platform_profile_handler *pprof,
+ enum platform_profile_option profile)
+{
+ acpi_status status;
+ u32 in_args;
+ u32 out_data;
+
+ switch (profile) {
+ case PLATFORM_PROFILE_LOW_POWER:
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_LOW_POWER);
+ break;
+ case PLATFORM_PROFILE_QUIET:
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_QUIET);
+ break;
+ case PLATFORM_PROFILE_BALANCED:
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_BALANCED);
+ break;
+ case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_BALANCED_PERFORMANCE);
+ break;
+ case PLATFORM_PROFILE_PERFORMANCE:
+ if (quirks->gmode > 0)
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_GMODE);
+ else
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_PERFORMANCE);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ status = alienware_wmax_command(&in_args, sizeof(in_args),
+ WMAX_METHOD_THERMAL_CONTROL, &out_data);
+
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ if (out_data == WMAX_FAILURE_CODE)
+ return -EBADRQC;
+
+ return 0;
+}
+
+static int create_thermal_profile(void)
+{
+ pp_handler.profile_get = thermal_profile_get;
+
+ if (quirks->thermal > 0)
+ pp_handler.profile_set = thermal_profile_set;
+ else {
+ pp_handler.profile_set = thermal_profile_set_ustt;
+ set_bit(PLATFORM_PROFILE_LOW_POWER, pp_handler.choices);
+ }
+
+ set_bit(PLATFORM_PROFILE_QUIET, pp_handler.choices);
+ set_bit(PLATFORM_PROFILE_BALANCED, pp_handler.choices);
+ set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, pp_handler.choices);
+ set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices);
+
+ return platform_profile_register(&pp_handler);
+}
+
+static void remove_thermal_profile(void)
+{
+ if (quirks->thermal > 0)
+ platform_profile_remove();
+}
+
static int __init alienware_wmi_init(void)
{
int ret;
@@ -808,6 +1036,12 @@ static int __init alienware_wmi_init(void)
goto fail_prep_deepsleep;
}
+ if (quirks->thermal > 0 || quirks->thermal_ustt > 0) {
+ ret = create_thermal_profile();
+ if (ret)
+ goto fail_prep_thermal_profile;
+ }
+
ret = alienware_zone_init(platform_device);
if (ret)
goto fail_prep_zones;
@@ -818,6 +1052,7 @@ static int __init alienware_wmi_init(void)
alienware_zone_exit(platform_device);
fail_prep_deepsleep:
fail_prep_amplifier:
+fail_prep_thermal_profile:
fail_prep_hdmi:
platform_device_del(platform_device);
fail_platform_device2:
@@ -835,6 +1070,7 @@ static void __exit alienware_wmi_exit(void)
if (platform_device) {
alienware_zone_exit(platform_device);
remove_hdmi(platform_device);
+ remove_thermal_profile();
platform_device_unregister(platform_device);
platform_driver_unregister(&platform_driver);
}
--
2.47.0
On Fri, 11 Oct 2024, Kurt Borja wrote: > Implements platform profile support for Dell laptops with new WMAX > thermal interface, present on some Alienware X-Series, Alienware > M-Series and Dell's G-Series laptops. This implementation supports two > sets of thermal profile codes, namely *thermal* and *thermal_ustt*, plus > additional quirk *gmode* for Dell's G-Series laptops. > > Signed-off-by: Kurt Borja <kuurtb@gmail.com> > --- > drivers/platform/x86/dell/Kconfig | 1 + > drivers/platform/x86/dell/alienware-wmi.c | 236 ++++++++++++++++++++++ > 2 files changed, 237 insertions(+) > > diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig > index 68a49788a..b06d634cd 100644 > --- a/drivers/platform/x86/dell/Kconfig > +++ b/drivers/platform/x86/dell/Kconfig > @@ -21,6 +21,7 @@ config ALIENWARE_WMI > depends on LEDS_CLASS > depends on NEW_LEDS > depends on ACPI_WMI > + select ACPI_PLATFORM_PROFILE > help > This is a driver for controlling Alienware BIOS driven > features. It exposes an interface for controlling the AlienFX > diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c > index b27f3b64c..6e30e9376 100644 > --- a/drivers/platform/x86/dell/alienware-wmi.c > +++ b/drivers/platform/x86/dell/alienware-wmi.c > @@ -8,8 +8,11 @@ > #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt > > #include <linux/acpi.h> > +#include <linux/bitfield.h> > +#include <linux/bits.h> > #include <linux/module.h> > #include <linux/platform_device.h> > +#include <linux/platform_profile.h> > #include <linux/dmi.h> > #include <linux/leds.h> > > @@ -25,6 +28,12 @@ > #define WMAX_METHOD_AMPLIFIER_CABLE 0x6 > #define WMAX_METHOD_DEEP_SLEEP_CONTROL 0x0B > #define WMAX_METHOD_DEEP_SLEEP_STATUS 0x0C > +#define WMAX_METHOD_THERMAL_INFORMATION 0x14 > +#define WMAX_METHOD_THERMAL_CONTROL 0x15 > + > +#define WMAX_ARG_GET_CURRENT_PROF 0x0B > + > +#define WMAX_FAILURE_CODE 0xFFFFFFFF > > MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>"); > MODULE_DESCRIPTION("Alienware special feature control"); > @@ -49,11 +58,27 @@ enum WMAX_CONTROL_STATES { > WMAX_SUSPEND = 3, > }; > > +enum WMAX_THERMAL_PROFILE { > + WMAX_THERMAL_QUIET = 0x96, > + WMAX_THERMAL_BALANCED = 0x97, > + WMAX_THERMAL_BALANCED_PERFORMANCE = 0x98, > + WMAX_THERMAL_PERFORMANCE = 0x99, > + WMAX_THERMAL_USTT_LOW_POWER = 0xA5, > + WMAX_THERMAL_USTT_QUIET = 0xA3, > + WMAX_THERMAL_USTT_BALANCED = 0xA0, > + WMAX_THERMAL_USTT_BALANCED_PERFORMANCE = 0xA1, > + WMAX_THERMAL_USTT_PERFORMANCE = 0xA4, > + WMAX_THERMAL_GMODE = 0xAB, While doing the next version, could also align these values please. -- i.
Am 12.10.24 um 04:02 schrieb Kurt Borja: > Implements platform profile support for Dell laptops with new WMAX > thermal interface, present on some Alienware X-Series, Alienware > M-Series and Dell's G-Series laptops. This implementation supports two > sets of thermal profile codes, namely *thermal* and *thermal_ustt*, plus > additional quirk *gmode* for Dell's G-Series laptops. > > Signed-off-by: Kurt Borja <kuurtb@gmail.com> > --- > drivers/platform/x86/dell/Kconfig | 1 + > drivers/platform/x86/dell/alienware-wmi.c | 236 ++++++++++++++++++++++ > 2 files changed, 237 insertions(+) > > diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig > index 68a49788a..b06d634cd 100644 > --- a/drivers/platform/x86/dell/Kconfig > +++ b/drivers/platform/x86/dell/Kconfig > @@ -21,6 +21,7 @@ config ALIENWARE_WMI > depends on LEDS_CLASS > depends on NEW_LEDS > depends on ACPI_WMI > + select ACPI_PLATFORM_PROFILE > help > This is a driver for controlling Alienware BIOS driven > features. It exposes an interface for controlling the AlienFX > diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c > index b27f3b64c..6e30e9376 100644 > --- a/drivers/platform/x86/dell/alienware-wmi.c > +++ b/drivers/platform/x86/dell/alienware-wmi.c > @@ -8,8 +8,11 @@ > #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt > > #include <linux/acpi.h> > +#include <linux/bitfield.h> > +#include <linux/bits.h> > #include <linux/module.h> > #include <linux/platform_device.h> > +#include <linux/platform_profile.h> > #include <linux/dmi.h> > #include <linux/leds.h> > > @@ -25,6 +28,12 @@ > #define WMAX_METHOD_AMPLIFIER_CABLE 0x6 > #define WMAX_METHOD_DEEP_SLEEP_CONTROL 0x0B > #define WMAX_METHOD_DEEP_SLEEP_STATUS 0x0C > +#define WMAX_METHOD_THERMAL_INFORMATION 0x14 > +#define WMAX_METHOD_THERMAL_CONTROL 0x15 > + > +#define WMAX_ARG_GET_CURRENT_PROF 0x0B > + > +#define WMAX_FAILURE_CODE 0xFFFFFFFF > > MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>"); > MODULE_DESCRIPTION("Alienware special feature control"); > @@ -49,11 +58,27 @@ enum WMAX_CONTROL_STATES { > WMAX_SUSPEND = 3, > }; > > +enum WMAX_THERMAL_PROFILE { > + WMAX_THERMAL_QUIET = 0x96, > + WMAX_THERMAL_BALANCED = 0x97, > + WMAX_THERMAL_BALANCED_PERFORMANCE = 0x98, > + WMAX_THERMAL_PERFORMANCE = 0x99, > + WMAX_THERMAL_USTT_LOW_POWER = 0xA5, > + WMAX_THERMAL_USTT_QUIET = 0xA3, > + WMAX_THERMAL_USTT_BALANCED = 0xA0, > + WMAX_THERMAL_USTT_BALANCED_PERFORMANCE = 0xA1, > + WMAX_THERMAL_USTT_PERFORMANCE = 0xA4, > + WMAX_THERMAL_GMODE = 0xAB, > +}; > + > struct quirk_entry { > u8 num_zones; > u8 hdmi_mux; > u8 amplifier; > u8 deepslp; > + u8 thermal; > + u8 thermal_ustt; > + u8 gmode; > }; > > static struct quirk_entry *quirks; > @@ -64,6 +89,9 @@ static struct quirk_entry quirk_inspiron5675 = { > .hdmi_mux = 0, > .amplifier = 0, > .deepslp = 0, > + .thermal = 0, > + .thermal_ustt = 0, > + .gmode = 0, > }; > > static struct quirk_entry quirk_unknown = { > @@ -71,6 +99,9 @@ static struct quirk_entry quirk_unknown = { > .hdmi_mux = 0, > .amplifier = 0, > .deepslp = 0, > + .thermal = 0, > + .thermal_ustt = 0, > + .gmode = 0, > }; > > static struct quirk_entry quirk_x51_r1_r2 = { > @@ -78,6 +109,9 @@ static struct quirk_entry quirk_x51_r1_r2 = { > .hdmi_mux = 0, > .amplifier = 0, > .deepslp = 0, > + .thermal = 0, > + .thermal_ustt = 0, > + .gmode = 0, > }; > > static struct quirk_entry quirk_x51_r3 = { > @@ -85,6 +119,9 @@ static struct quirk_entry quirk_x51_r3 = { > .hdmi_mux = 0, > .amplifier = 1, > .deepslp = 0, > + .thermal = 0, > + .thermal_ustt = 0, > + .gmode = 0, > }; > > static struct quirk_entry quirk_asm100 = { > @@ -92,6 +129,9 @@ static struct quirk_entry quirk_asm100 = { > .hdmi_mux = 1, > .amplifier = 0, > .deepslp = 0, > + .thermal = 0, > + .thermal_ustt = 0, > + .gmode = 0, > }; > > static struct quirk_entry quirk_asm200 = { > @@ -99,6 +139,9 @@ static struct quirk_entry quirk_asm200 = { > .hdmi_mux = 1, > .amplifier = 0, > .deepslp = 1, > + .thermal = 0, > + .thermal_ustt = 0, > + .gmode = 0, > }; > > static struct quirk_entry quirk_asm201 = { > @@ -106,6 +149,19 @@ static struct quirk_entry quirk_asm201 = { > .hdmi_mux = 1, > .amplifier = 1, > .deepslp = 1, > + .thermal = 0, > + .thermal_ustt = 0, > + .gmode = 0, > +}; > + > +static struct quirk_entry quirk_x15_r1 = { > + .num_zones = 2, > + .hdmi_mux = 0, > + .amplifier = 0, > + .deepslp = 0, > + .thermal = 0, > + .thermal_ustt = 1, > + .gmode = 0, > }; > > static int __init dmi_matched(const struct dmi_system_id *dmi) > @@ -169,6 +225,15 @@ static const struct dmi_system_id alienware_quirks[] __initconst = { > }, > .driver_data = &quirk_asm201, > }, > + { > + .callback = dmi_matched, > + .ident = "Alienware x15 R1", > + .matches = { > + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R1") > + }, > + .driver_data = &quirk_x15_r1, > + }, > { > .callback = dmi_matched, > .ident = "Dell Inc. Inspiron 5675", > @@ -218,6 +283,7 @@ static struct platform_device *platform_device; > static struct device_attribute *zone_dev_attrs; > static struct attribute **zone_attrs; > static struct platform_zone *zone_data; > +static struct platform_profile_handler pp_handler; > > static struct platform_driver platform_driver = { > .driver = { > @@ -761,6 +827,168 @@ static int create_deepsleep(struct platform_device *dev) > return ret; > } > > +/* > + * Thermal Profile control > + * - Provides thermal profile control through the Platform Profile API > + */ > +#define WMAX_PROFILE_MASK GENMASK(15, 8) > +#define WMAX_PROFILE_ACTIVATE BIT(0) > + > +static u32 profile_to_wmax_arg(enum WMAX_THERMAL_PROFILE prof) > +{ > + return FIELD_PREP(WMAX_PROFILE_MASK, prof) | WMAX_PROFILE_ACTIVATE; > +} > + > +static int thermal_profile_get(struct platform_profile_handler *pprof, > + enum platform_profile_option *profile) Alignment should match open parenthesis. > +{ > + acpi_status status; > + u32 in_args = WMAX_ARG_GET_CURRENT_PROF; > + u32 out_data; > + > + status = alienware_wmax_command(&in_args, sizeof(in_args), > + WMAX_METHOD_THERMAL_INFORMATION, &out_data); > + > + if (ACPI_FAILURE(status)) > + return -EIO; > + > + if (out_data == WMAX_FAILURE_CODE) > + return -EBADRQC; > + > + switch (out_data) { > + case WMAX_THERMAL_USTT_LOW_POWER: > + *profile = PLATFORM_PROFILE_LOW_POWER; > + break; > + case WMAX_THERMAL_QUIET: > + case WMAX_THERMAL_USTT_QUIET: > + *profile = PLATFORM_PROFILE_QUIET; > + break; > + case WMAX_THERMAL_BALANCED: > + case WMAX_THERMAL_USTT_BALANCED: > + *profile = PLATFORM_PROFILE_BALANCED; > + break; > + case WMAX_THERMAL_BALANCED_PERFORMANCE: > + case WMAX_THERMAL_USTT_BALANCED_PERFORMANCE: > + *profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE; > + break; > + case WMAX_THERMAL_GMODE: > + case WMAX_THERMAL_PERFORMANCE: > + case WMAX_THERMAL_USTT_PERFORMANCE: > + *profile = PLATFORM_PROFILE_PERFORMANCE; > + break; > + default: > + return -ENODATA; > + } > + > + return 0; > +} > + > +static int thermal_profile_set(struct platform_profile_handler *pprof, > + enum platform_profile_option profile) > +{ Alignment should match open parenthesis. > + acpi_status status; > + u32 in_args; > + u32 out_data; > + > + switch (profile) { > + case PLATFORM_PROFILE_QUIET: > + in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET); > + break; > + case PLATFORM_PROFILE_BALANCED: > + in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED); > + break; > + case PLATFORM_PROFILE_BALANCED_PERFORMANCE: > + in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE); > + break; > + case PLATFORM_PROFILE_PERFORMANCE: > + if (quirks->gmode > 0) > + in_args = profile_to_wmax_arg(WMAX_THERMAL_GMODE); > + else > + in_args = profile_to_wmax_arg(WMAX_THERMAL_PERFORMANCE); > + break; > + default: > + return -EOPNOTSUPP; > + } > + > + status = alienware_wmax_command(&in_args, sizeof(in_args), > + WMAX_METHOD_THERMAL_CONTROL, &out_data); > + > + if (ACPI_FAILURE(status)) > + return -EIO; > + > + if (out_data == WMAX_FAILURE_CODE) > + return -EBADRQC; > + > + return 0; > +} > + > +static int thermal_profile_set_ustt(struct platform_profile_handler *pprof, > + enum platform_profile_option profile) > +{ > + acpi_status status; > + u32 in_args; > + u32 out_data; > + > + switch (profile) { > + case PLATFORM_PROFILE_LOW_POWER: > + in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_LOW_POWER); > + break; > + case PLATFORM_PROFILE_QUIET: > + in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_QUIET); > + break; > + case PLATFORM_PROFILE_BALANCED: > + in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_BALANCED); > + break; > + case PLATFORM_PROFILE_BALANCED_PERFORMANCE: > + in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_BALANCED_PERFORMANCE); > + break; > + case PLATFORM_PROFILE_PERFORMANCE: > + if (quirks->gmode > 0) > + in_args = profile_to_wmax_arg(WMAX_THERMAL_GMODE); > + else > + in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_PERFORMANCE); > + break; > + default: > + return -EOPNOTSUPP; > + } > + > + status = alienware_wmax_command(&in_args, sizeof(in_args), > + WMAX_METHOD_THERMAL_CONTROL, &out_data); > + > + if (ACPI_FAILURE(status)) > + return -EIO; > + > + if (out_data == WMAX_FAILURE_CODE) > + return -EBADRQC; > + > + return 0; > +} > + > +static int create_thermal_profile(void) > +{ > + pp_handler.profile_get = thermal_profile_get; > + > + if (quirks->thermal > 0) > + pp_handler.profile_set = thermal_profile_set; Braces {} should be used on all arms of this statement. > + else { > + pp_handler.profile_set = thermal_profile_set_ustt; > + set_bit(PLATFORM_PROFILE_LOW_POWER, pp_handler.choices); > + } > + > + set_bit(PLATFORM_PROFILE_QUIET, pp_handler.choices); > + set_bit(PLATFORM_PROFILE_BALANCED, pp_handler.choices); > + set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, pp_handler.choices); > + set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices); > + > + return platform_profile_register(&pp_handler); > +} > + > +static void remove_thermal_profile(void) > +{ > + if (quirks->thermal > 0) > + platform_profile_remove(); platform_profile_remove() should also be called when quirks->thermal_ustt is set. > +} > + > static int __init alienware_wmi_init(void) > { > int ret; > @@ -808,6 +1036,12 @@ static int __init alienware_wmi_init(void) > goto fail_prep_deepsleep; > } > > + if (quirks->thermal > 0 || quirks->thermal_ustt > 0) { > + ret = create_thermal_profile(); > + if (ret) > + goto fail_prep_thermal_profile; > + } > + > ret = alienware_zone_init(platform_device); > if (ret) > goto fail_prep_zones; > @@ -818,6 +1052,7 @@ static int __init alienware_wmi_init(void) > alienware_zone_exit(platform_device); > fail_prep_deepsleep: > fail_prep_amplifier: > +fail_prep_thermal_profile: fail_prep_thermal_profile should come before fail_prep_deepsleep for proper rollback in case of an error. Also fail_prep_zones should call remove_thermal_profile(). > fail_prep_hdmi: > platform_device_del(platform_device); > fail_platform_device2: > @@ -835,6 +1070,7 @@ static void __exit alienware_wmi_exit(void) > if (platform_device) { > alienware_zone_exit(platform_device); > remove_hdmi(platform_device); > + remove_thermal_profile(); Please move remove_thermal_profile() above remove_hdmi(). Otherwise, the patch look good. Thanks, Armin Wolf > platform_device_unregister(platform_device); > platform_driver_unregister(&platform_driver); > }
On Mon, Oct 14, 2024 at 06:40:49PM +0200, Armin Wolf wrote: > Am 12.10.24 um 04:02 schrieb Kurt Borja: > > > Implements platform profile support for Dell laptops with new WMAX > > thermal interface, present on some Alienware X-Series, Alienware > > M-Series and Dell's G-Series laptops. This implementation supports two > > sets of thermal profile codes, namely *thermal* and *thermal_ustt*, plus > > additional quirk *gmode* for Dell's G-Series laptops. > > > > Signed-off-by: Kurt Borja <kuurtb@gmail.com> > > --- > > drivers/platform/x86/dell/Kconfig | 1 + > > drivers/platform/x86/dell/alienware-wmi.c | 236 ++++++++++++++++++++++ > > 2 files changed, 237 insertions(+) > > > > diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig > > index 68a49788a..b06d634cd 100644 > > --- a/drivers/platform/x86/dell/Kconfig > > +++ b/drivers/platform/x86/dell/Kconfig > > @@ -21,6 +21,7 @@ config ALIENWARE_WMI > > depends on LEDS_CLASS > > depends on NEW_LEDS > > depends on ACPI_WMI > > + select ACPI_PLATFORM_PROFILE > > help > > This is a driver for controlling Alienware BIOS driven > > features. It exposes an interface for controlling the AlienFX > > diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c > > index b27f3b64c..6e30e9376 100644 > > --- a/drivers/platform/x86/dell/alienware-wmi.c > > +++ b/drivers/platform/x86/dell/alienware-wmi.c > > @@ -8,8 +8,11 @@ > > #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt > > > > #include <linux/acpi.h> > > +#include <linux/bitfield.h> > > +#include <linux/bits.h> > > #include <linux/module.h> > > #include <linux/platform_device.h> > > +#include <linux/platform_profile.h> > > #include <linux/dmi.h> > > #include <linux/leds.h> > > > > @@ -25,6 +28,12 @@ > > #define WMAX_METHOD_AMPLIFIER_CABLE 0x6 > > #define WMAX_METHOD_DEEP_SLEEP_CONTROL 0x0B > > #define WMAX_METHOD_DEEP_SLEEP_STATUS 0x0C > > +#define WMAX_METHOD_THERMAL_INFORMATION 0x14 > > +#define WMAX_METHOD_THERMAL_CONTROL 0x15 > > + > > +#define WMAX_ARG_GET_CURRENT_PROF 0x0B > > + > > +#define WMAX_FAILURE_CODE 0xFFFFFFFF > > > > MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>"); > > MODULE_DESCRIPTION("Alienware special feature control"); > > @@ -49,11 +58,27 @@ enum WMAX_CONTROL_STATES { > > WMAX_SUSPEND = 3, > > }; > > > > +enum WMAX_THERMAL_PROFILE { > > + WMAX_THERMAL_QUIET = 0x96, > > + WMAX_THERMAL_BALANCED = 0x97, > > + WMAX_THERMAL_BALANCED_PERFORMANCE = 0x98, > > + WMAX_THERMAL_PERFORMANCE = 0x99, > > + WMAX_THERMAL_USTT_LOW_POWER = 0xA5, > > + WMAX_THERMAL_USTT_QUIET = 0xA3, > > + WMAX_THERMAL_USTT_BALANCED = 0xA0, > > + WMAX_THERMAL_USTT_BALANCED_PERFORMANCE = 0xA1, > > + WMAX_THERMAL_USTT_PERFORMANCE = 0xA4, > > + WMAX_THERMAL_GMODE = 0xAB, > > +}; > > + > > struct quirk_entry { > > u8 num_zones; > > u8 hdmi_mux; > > u8 amplifier; > > u8 deepslp; > > + u8 thermal; > > + u8 thermal_ustt; > > + u8 gmode; > > }; > > > > static struct quirk_entry *quirks; > > @@ -64,6 +89,9 @@ static struct quirk_entry quirk_inspiron5675 = { > > .hdmi_mux = 0, > > .amplifier = 0, > > .deepslp = 0, > > + .thermal = 0, > > + .thermal_ustt = 0, > > + .gmode = 0, > > }; > > > > static struct quirk_entry quirk_unknown = { > > @@ -71,6 +99,9 @@ static struct quirk_entry quirk_unknown = { > > .hdmi_mux = 0, > > .amplifier = 0, > > .deepslp = 0, > > + .thermal = 0, > > + .thermal_ustt = 0, > > + .gmode = 0, > > }; > > > > static struct quirk_entry quirk_x51_r1_r2 = { > > @@ -78,6 +109,9 @@ static struct quirk_entry quirk_x51_r1_r2 = { > > .hdmi_mux = 0, > > .amplifier = 0, > > .deepslp = 0, > > + .thermal = 0, > > + .thermal_ustt = 0, > > + .gmode = 0, > > }; > > > > static struct quirk_entry quirk_x51_r3 = { > > @@ -85,6 +119,9 @@ static struct quirk_entry quirk_x51_r3 = { > > .hdmi_mux = 0, > > .amplifier = 1, > > .deepslp = 0, > > + .thermal = 0, > > + .thermal_ustt = 0, > > + .gmode = 0, > > }; > > > > static struct quirk_entry quirk_asm100 = { > > @@ -92,6 +129,9 @@ static struct quirk_entry quirk_asm100 = { > > .hdmi_mux = 1, > > .amplifier = 0, > > .deepslp = 0, > > + .thermal = 0, > > + .thermal_ustt = 0, > > + .gmode = 0, > > }; > > > > static struct quirk_entry quirk_asm200 = { > > @@ -99,6 +139,9 @@ static struct quirk_entry quirk_asm200 = { > > .hdmi_mux = 1, > > .amplifier = 0, > > .deepslp = 1, > > + .thermal = 0, > > + .thermal_ustt = 0, > > + .gmode = 0, > > }; > > > > static struct quirk_entry quirk_asm201 = { > > @@ -106,6 +149,19 @@ static struct quirk_entry quirk_asm201 = { > > .hdmi_mux = 1, > > .amplifier = 1, > > .deepslp = 1, > > + .thermal = 0, > > + .thermal_ustt = 0, > > + .gmode = 0, > > +}; > > + > > +static struct quirk_entry quirk_x15_r1 = { > > + .num_zones = 2, > > + .hdmi_mux = 0, > > + .amplifier = 0, > > + .deepslp = 0, > > + .thermal = 0, > > + .thermal_ustt = 1, > > + .gmode = 0, > > }; > > > > static int __init dmi_matched(const struct dmi_system_id *dmi) > > @@ -169,6 +225,15 @@ static const struct dmi_system_id alienware_quirks[] __initconst = { > > }, > > .driver_data = &quirk_asm201, > > }, > > + { > > + .callback = dmi_matched, > > + .ident = "Alienware x15 R1", > > + .matches = { > > + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > > + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R1") > > + }, > > + .driver_data = &quirk_x15_r1, > > + }, > > { > > .callback = dmi_matched, > > .ident = "Dell Inc. Inspiron 5675", > > @@ -218,6 +283,7 @@ static struct platform_device *platform_device; > > static struct device_attribute *zone_dev_attrs; > > static struct attribute **zone_attrs; > > static struct platform_zone *zone_data; > > +static struct platform_profile_handler pp_handler; > > > > static struct platform_driver platform_driver = { > > .driver = { > > @@ -761,6 +827,168 @@ static int create_deepsleep(struct platform_device *dev) > > return ret; > > } > > > > +/* > > + * Thermal Profile control > > + * - Provides thermal profile control through the Platform Profile API > > + */ > > +#define WMAX_PROFILE_MASK GENMASK(15, 8) > > +#define WMAX_PROFILE_ACTIVATE BIT(0) > > + > > +static u32 profile_to_wmax_arg(enum WMAX_THERMAL_PROFILE prof) > > +{ > > + return FIELD_PREP(WMAX_PROFILE_MASK, prof) | WMAX_PROFILE_ACTIVATE; > > +} > > + > > +static int thermal_profile_get(struct platform_profile_handler *pprof, > > + enum platform_profile_option *profile) > > Alignment should match open parenthesis. I will fix it. > > > +{ > > + acpi_status status; > > + u32 in_args = WMAX_ARG_GET_CURRENT_PROF; > > + u32 out_data; > > + > > + status = alienware_wmax_command(&in_args, sizeof(in_args), > > + WMAX_METHOD_THERMAL_INFORMATION, &out_data); > > + > > + if (ACPI_FAILURE(status)) > > + return -EIO; > > + > > + if (out_data == WMAX_FAILURE_CODE) > > + return -EBADRQC; > > + > > + switch (out_data) { > > + case WMAX_THERMAL_USTT_LOW_POWER: > > + *profile = PLATFORM_PROFILE_LOW_POWER; > > + break; > > + case WMAX_THERMAL_QUIET: > > + case WMAX_THERMAL_USTT_QUIET: > > + *profile = PLATFORM_PROFILE_QUIET; > > + break; > > + case WMAX_THERMAL_BALANCED: > > + case WMAX_THERMAL_USTT_BALANCED: > > + *profile = PLATFORM_PROFILE_BALANCED; > > + break; > > + case WMAX_THERMAL_BALANCED_PERFORMANCE: > > + case WMAX_THERMAL_USTT_BALANCED_PERFORMANCE: > > + *profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE; > > + break; > > + case WMAX_THERMAL_GMODE: > > + case WMAX_THERMAL_PERFORMANCE: > > + case WMAX_THERMAL_USTT_PERFORMANCE: > > + *profile = PLATFORM_PROFILE_PERFORMANCE; > > + break; > > + default: > > + return -ENODATA; > > + } > > + > > + return 0; > > +} > > + > > +static int thermal_profile_set(struct platform_profile_handler *pprof, > > + enum platform_profile_option profile) > > +{ > > Alignment should match open parenthesis. > > > + acpi_status status; > > + u32 in_args; > > + u32 out_data; > > + > > + switch (profile) { > > + case PLATFORM_PROFILE_QUIET: > > + in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET); > > + break; > > + case PLATFORM_PROFILE_BALANCED: > > + in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED); > > + break; > > + case PLATFORM_PROFILE_BALANCED_PERFORMANCE: > > + in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE); > > + break; > > + case PLATFORM_PROFILE_PERFORMANCE: > > + if (quirks->gmode > 0) > > + in_args = profile_to_wmax_arg(WMAX_THERMAL_GMODE); > > + else > > + in_args = profile_to_wmax_arg(WMAX_THERMAL_PERFORMANCE); > > + break; > > + default: > > + return -EOPNOTSUPP; > > + } > > + > > + status = alienware_wmax_command(&in_args, sizeof(in_args), > > + WMAX_METHOD_THERMAL_CONTROL, &out_data); > > + > > + if (ACPI_FAILURE(status)) > > + return -EIO; > > + > > + if (out_data == WMAX_FAILURE_CODE) > > + return -EBADRQC; > > + > > + return 0; > > +} > > + > > +static int thermal_profile_set_ustt(struct platform_profile_handler *pprof, > > + enum platform_profile_option profile) > > +{ > > + acpi_status status; > > + u32 in_args; > > + u32 out_data; > > + > > + switch (profile) { > > + case PLATFORM_PROFILE_LOW_POWER: > > + in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_LOW_POWER); > > + break; > > + case PLATFORM_PROFILE_QUIET: > > + in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_QUIET); > > + break; > > + case PLATFORM_PROFILE_BALANCED: > > + in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_BALANCED); > > + break; > > + case PLATFORM_PROFILE_BALANCED_PERFORMANCE: > > + in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_BALANCED_PERFORMANCE); > > + break; > > + case PLATFORM_PROFILE_PERFORMANCE: > > + if (quirks->gmode > 0) > > + in_args = profile_to_wmax_arg(WMAX_THERMAL_GMODE); > > + else > > + in_args = profile_to_wmax_arg(WMAX_THERMAL_USTT_PERFORMANCE); > > + break; > > + default: > > + return -EOPNOTSUPP; > > + } > > + > > + status = alienware_wmax_command(&in_args, sizeof(in_args), > > + WMAX_METHOD_THERMAL_CONTROL, &out_data); > > + > > + if (ACPI_FAILURE(status)) > > + return -EIO; > > + > > + if (out_data == WMAX_FAILURE_CODE) > > + return -EBADRQC; > > + > > + return 0; > > +} > > + > > +static int create_thermal_profile(void) > > +{ > > + pp_handler.profile_get = thermal_profile_get; > > + > > + if (quirks->thermal > 0) > > + pp_handler.profile_set = thermal_profile_set; > > Braces {} should be used on all arms of this statement. Ok. > > > + else { > > + pp_handler.profile_set = thermal_profile_set_ustt; > > + set_bit(PLATFORM_PROFILE_LOW_POWER, pp_handler.choices); > > + } > > + > > + set_bit(PLATFORM_PROFILE_QUIET, pp_handler.choices); > > + set_bit(PLATFORM_PROFILE_BALANCED, pp_handler.choices); > > + set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, pp_handler.choices); > > + set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices); > > + > > + return platform_profile_register(&pp_handler); > > +} > > + > > +static void remove_thermal_profile(void) > > +{ > > + if (quirks->thermal > 0) > > + platform_profile_remove(); > > platform_profile_remove() should also be called when quirks->thermal_ustt is set. Thank you for catching it. > > > +} > > + > > static int __init alienware_wmi_init(void) > > { > > int ret; > > @@ -808,6 +1036,12 @@ static int __init alienware_wmi_init(void) > > goto fail_prep_deepsleep; > > } > > > > + if (quirks->thermal > 0 || quirks->thermal_ustt > 0) { > > + ret = create_thermal_profile(); > > + if (ret) > > + goto fail_prep_thermal_profile; > > + } > > + > > ret = alienware_zone_init(platform_device); > > if (ret) > > goto fail_prep_zones; > > @@ -818,6 +1052,7 @@ static int __init alienware_wmi_init(void) > > alienware_zone_exit(platform_device); > > fail_prep_deepsleep: > > fail_prep_amplifier: > > +fail_prep_thermal_profile: > > fail_prep_thermal_profile should come before fail_prep_deepsleep for proper rollback in case of an error. > Also fail_prep_zones should call remove_thermal_profile(). Ok. > > > fail_prep_hdmi: > > platform_device_del(platform_device); > > fail_platform_device2: > > @@ -835,6 +1070,7 @@ static void __exit alienware_wmi_exit(void) > > if (platform_device) { > > alienware_zone_exit(platform_device); > > remove_hdmi(platform_device); > > + remove_thermal_profile(); > > Please move remove_thermal_profile() above remove_hdmi(). Ok. > > Otherwise, the patch look good. > > Thanks, > Armin Wolf > Thank you. Kurt > > platform_device_unregister(platform_device); > > platform_driver_unregister(&platform_driver); > > }
Added documentation for new WMAX interface, present on some Alienware
X-Series, Alienware M-Series and Dell's G-Series laptops.
Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
Documentation/wmi/devices/alienware-wmi.rst | 366 ++++++++++++++++++++
1 file changed, 366 insertions(+)
create mode 100644 Documentation/wmi/devices/alienware-wmi.rst
diff --git a/Documentation/wmi/devices/alienware-wmi.rst b/Documentation/wmi/devices/alienware-wmi.rst
new file mode 100644
index 000000000..77460b91c
--- /dev/null
+++ b/Documentation/wmi/devices/alienware-wmi.rst
@@ -0,0 +1,366 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+==============================================
+Dell AWCC WMI interface driver (alienware-wmi)
+==============================================
+
+Introduction
+============
+
+The WMI device WMAX has been implemented for many Alienware and Dell's G-Series
+models. Throughout these models, two implementations have been identified. The
+first one, used by older systems, deals with HDMI, brightness, RGB, amplifier
+and deep sleep control. The second one used by newer systems deals primarily
+with thermal, overclocking, and GPIO control.
+
+It is suspected that the latter is used by Alienware Command Center (AWCC) to
+manage manufacturer predefined thermal profiles. The alienware-wmi driver
+exposes Thermal_Information and Thermal_Control methods through the Platform
+Profile API to mimic AWCC's behavior.
+
+This newer interface, named AWCCMethodFunction has been reverse engineered, as
+Dell has not provided any official documentation. We will try to describe to the
+best of our ability its discovered inner workings.
+
+.. note::
+ The following method description may vary between models.
+
+WMI interface description
+-------------------------
+
+The WMI interface description can be decoded from the embedded binary MOF (bmof)
+data using the `bmfdec <https://github.com/pali/bmfdec>`_ utility:
+
+::
+
+ [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("WMI Function"), guid("{A70591CE-A997-11DA-B012-B622A1EF5492}")]
+ class AWCCWmiMethodFunction {
+ [key, read] string InstanceName;
+ [read] boolean Active;
+
+ [WmiMethodId(13), Implemented, read, write, Description("Return Overclocking Report.")] void Return_OverclockingReport([out] uint32 argr);
+ [WmiMethodId(14), Implemented, read, write, Description("Set OCUIBIOS Control.")] void Set_OCUIBIOSControl([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(15), Implemented, read, write, Description("Clear OC FailSafe Flag.")] void Clear_OCFailSafeFlag([out] uint32 argr);
+ [WmiMethodId(19), Implemented, read, write, Description("Get Fan Sensors.")] void GetFanSensors([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(20), Implemented, read, write, Description("Thermal Information.")] void Thermal_Information([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(21), Implemented, read, write, Description("Thermal Control.")] void Thermal_Control([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(23), Implemented, read, write, Description("MemoryOCControl.")] void MemoryOCControl([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(26), Implemented, read, write, Description("System Information.")] void SystemInformation([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(28), Implemented, read, write, Description("Power Information.")] void PowerInformation([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(32), Implemented, read, write, Description("FW Update GPIO toggle.")] void FWUpdateGPIOtoggle([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(33), Implemented, read, write, Description("Read Total of GPIOs.")] void ReadTotalofGPIOs([out] uint32 argr);
+ [WmiMethodId(34), Implemented, read, write, Description("Read GPIO pin Status.")] void ReadGPIOpPinStatus([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(35), Implemented, read, write, Description("Read Chassis Color.")] void ReadChassisColor([out] uint32 argr);
+ [WmiMethodId(36), Implemented, read, write, Description("Read Platform Properties.")] void ReadPlatformProperties([out] uint32 argr);
+ [WmiMethodId(128), Implemented, read, write, Description("Caldera SW installation.")] void CalderaSWInstallation([out] uint32 argr);
+ [WmiMethodId(129), Implemented, read, write, Description("Caldera SW is released.")] void CalderaSWReleased([out] uint32 argr);
+ [WmiMethodId(130), Implemented, read, write, Description("Caldera Connection Status.")] void CalderaConnectionStatus([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(131), Implemented, read, write, Description("Surprise Unplugged Flag Status.")] void SurpriseUnpluggedFlagStatus([out] uint32 argr);
+ [WmiMethodId(132), Implemented, read, write, Description("Clear Surprise Unplugged Flag.")] void ClearSurpriseUnpluggedFlag([out] uint32 argr);
+ [WmiMethodId(133), Implemented, read, write, Description("Cancel Undock Request.")] void CancelUndockRequest([out] uint32 argr);
+ [WmiMethodId(135), Implemented, read, write, Description("Devices in Caldera.")] void DevicesInCaldera([in] uint32 arg2, [out] uint32 argr);
+ [WmiMethodId(136), Implemented, read, write, Description("Notify BIOS for SW ready to disconnect Caldera.")] void NotifyBIOSForSWReadyToDisconnectCaldera([out] uint32 argr);
+ [WmiMethodId(160), Implemented, read, write, Description("Tobii SW installation.")] void TobiiSWinstallation([out] uint32 argr);
+ [WmiMethodId(161), Implemented, read, write, Description("Tobii SW Released.")] void TobiiSWReleased([out] uint32 argr);
+ [WmiMethodId(162), Implemented, read, write, Description("Tobii Camera Power Reset.")] void TobiiCameraPowerReset([out] uint32 argr);
+ [WmiMethodId(163), Implemented, read, write, Description("Tobii Camera Power On.")] void TobiiCameraPowerOn([out] uint32 argr);
+ [WmiMethodId(164), Implemented, read, write, Description("Tobii Camera Power Off.")] void TobiiCameraPowerOff([out] uint32 argr);
+ };
+
+Some of these methods get quite intricate so we will describe them using
+pseudo-code that vaguely resembles the original ASL code.
+
+Argument Structure
+------------------
+
+All input arguments have type **uint32** and their structure is very similar
+between methods. Usually, the first byte corresponds to a specific *operation*
+the method performs, and the subsequent bytes correspond to *arguments* passed
+to this *operation*. For example, if an operation has code 0x01 and requires an
+ID 0xA0, the argument you would pass to the method is 0xA001.
+
+
+Thermal Methods
+===============
+
+WMI method Thermal_Information([in] uint32 arg2, [out] uint32 argr)
+-------------------------------------------------------------------
+
+::
+
+ if BYTE_0(arg2) == 0x01:
+ argr = 1
+
+ if BYTE_0(arg2) == 0x02:
+ argr = UNKNOWN_CONSTANT
+
+ if BYTE_0(arg2) == 0x03:
+ if BYTE_1(arg2) == 0x00:
+ argr = FAN_ID_0
+
+ if BYTE_1(arg2) == 0x01:
+ argr = FAN_ID_1
+
+ if BYTE_1(arg2) == 0x02:
+ argr = FAN_ID_2
+
+ if BYTE_1(arg2) == 0x03:
+ argr = FAN_ID_3
+
+ if BYTE_1(arg2) == 0x04:
+ argr = SENSOR_ID_CPU | 0x0100
+
+ if BYTE_1(arg2) == 0x05:
+ argr = SENSOR_ID_GPU | 0x0100
+
+ if BYTE_1(arg2) == 0x06:
+ argr = THERMAL_MODE_QUIET_ID
+
+ if BYTE_1(arg2) == 0x07:
+ argr = THERMAL_MODE_BALANCED_ID
+
+ if BYTE_1(arg2) == 0x08:
+ argr = THERMAL_MODE_BALANCED_PERFORMANCE_ID
+
+ if BYTE_1(arg2) == 0x09:
+ argr = THERMAL_MODE_PERFORMANCE_ID
+
+ if BYTE_1(arg2) == 0x0A:
+ argr = THERMAL_MODE_LOW_POWER_ID
+
+ if BYTE_1(arg2) == 0x0B:
+ argr = THERMAL_MODE_GMODE_ID
+
+ else:
+ argr = 0xFFFFFFFF
+
+ if BYTE_0(arg2) == 0x04:
+ if is_valid_sensor(BYTE_1(arg2)):
+ argr = SENSOR_TEMP_C
+ else:
+ argr = 0xFFFFFFFF
+
+ if BYTE_0(arg2) == 0x05:
+ if is_valid_fan(BYTE_1(arg2)):
+ argr = FAN_RPM()
+
+ if BYTE_0(arg2) == 0x06:
+ skip
+
+ if BYTE_0(arg2) == 0x07:
+ argr = 0
+
+ If BYTE_0(arg2) == 0x08:
+ if is_valid_fan(BYTE_1(arg2)):
+ argr = 0
+ else:
+ argr = 0xFFFFFFFF
+
+ if BYTE_0(arg2) == 0x09:
+ if is_valid_fan(BYTE_1(arg2)):
+ argr = FAN_UNKNOWN_STAT_0()
+
+ else:
+ argr = 0xFFFFFFFF
+
+ if BYTE_0(arg2) == 0x0A:
+ argr = THERMAL_MODE_BALANCED_ID
+
+ if BYTE_0(arg2) == 0x0B:
+ argr = CURRENT_THERMAL_MODE()
+
+ if BYTE_0(arg2) == 0x0C:
+ if is_valid_fan(BYTE_1(arg2)):
+ argr = FAN_UNKNOWN_STAT_1()
+ else:
+ argr = 0xFFFFFFFF
+
+WMI method Thermal_Control([in] uint32 arg2, [out] uint32 argr)
+---------------------------------------------------------------
+
+::
+
+ if BYTE_0(arg2) == 0x01:
+ if is_valid_thermal_profile(BYTE_1(arg2)):
+ SET_THERMAL_PROFILE(BYTE_1(arg2))
+ argr = 0
+
+ if BYTE_0(arg2) == 0x02:
+ if is_valid_fan(BYTE_1(arg2)):
+ SET_FAN_SPEED_MULTIPLIER(BYTE_2(arg2))
+ argr = 0
+ else:
+ argr = 0xFFFFFFFF
+
+.. note::
+ While you can manually change the fan speed multiplier with this method,
+ Dell's BIOS tends to overwrite this changes anyway.
+
+These are the known thermal profile codes:
+
+::
+
+ CUSTOM 0x00
+
+ QUIET 0x96
+ BALANCED 0x97
+ BALANCED_PERFORMANCE 0x98
+ PERFORMANCE 0x99
+
+ QUIET_USTT 0xA3
+ BALANCED_USTT 0xA0
+ BALANCED_PERFORMANCE_USTT 0xA1
+ PERFORMANCE_USTT 0xA4
+ LOW_POWER_USTT 0xA5
+
+ GMODE 0xAB
+
+Usually if a model doesn't support the first four profiles they will support
+the User Selectable Thermal Tables (USTT) profiles and vice-versa.
+
+GMODE replaces PERFORMANCE in G-Series laptops.
+
+WMI method GetFanSensors([in] uint32 arg2, [out] uint32 argr)
+-------------------------------------------------------------
+
+::
+
+ if BYTE_0(arg2) == 1:
+ if is_valid_fan(BYTE_1(arg2)):
+ argr = 1
+ else:
+ argr = 0
+
+ if BYTE_0(arg2) == 2:
+ if is_valid_fan(BYTE_1(arg2)):
+ if BYTE_2(arg2) == 0:
+ argr == SENSOR_ID
+ else
+ argr == 0xFFFFFFFF
+ else:
+ argr = 0
+
+Overclocking Methods
+====================
+
+.. warning::
+ These methods have not been tested and are only partially reverse
+ engineered.
+
+WMI method Return_OverclockingReport([out] uint32 argr)
+-------------------------------------------------------
+
+::
+
+ CSMI (0xE3, 0x99)
+ argr = 0
+
+CSMI is an unknown operation.
+
+WMI method Set_OCUIBIOSControl([in] uint32 arg2, [out] uint32 argr)
+-------------------------------------------------------------------
+
+::
+
+ CSMI (0xE3, 0x99)
+ argr = 0
+
+CSMI is an unknown operation
+
+WMI method Clear_OCFailSafeFlag([out] uint32 argr)
+--------------------------------------------------
+
+::
+
+ CSMI (0xE3, 0x99)
+ argr = 0
+
+CSMI is an unknown operation
+
+
+WMI method MemoryOCControl([in] uint32 arg2, [out] uint32 argr)
+---------------------------------------------------------------
+
+AWCC supports memory overclocking, but this method is very intricate and has
+not been deciphered yet.
+
+GPIO methods
+============
+
+These methods are probably related to some kind of firmware update system,
+through a GPIO device.
+
+.. warning::
+ These methods have not been tested and are only partially reverse
+ engineered.
+
+WMI method FWUpdateGPIOtoggle([in] uint32 arg2, [out] uint32 argr)
+------------------------------------------------------------------
+
+::
+
+ if BYTE_0(arg2) == 0:
+ if BYTE_1(arg2) == 1:
+ SET_PIN_A_HIGH()
+ else:
+ SET_PIN_A_LOW()
+
+ if BYTE_0(arg2) == 1:
+ if BYTE_1(arg2) == 1:
+ SET_PIN_B_HIGH()
+
+ else:
+ SET_PIN_B_LOW()
+
+ else:
+ argr = 1
+
+WMI method ReadTotalofGPIOs([out] uint32 argr)
+----------------------------------------------
+
+::
+
+ argr = 0x02
+
+WMI method ReadGPIOpPinStatus([in] uint32 arg2, [out] uint32 argr)
+------------------------------------------------------------------
+
+::
+
+ if BYTE_0(arg2) == 0:
+ argr = PIN_A_STATUS
+
+ if BYTE_0(arg2) == 1:
+ argr = PIN_B_STATUS
+
+Other information Methods
+=========================
+
+WMI method SystemInformation([in] uint32 arg2, [out] uint32 argr)
+-----------------------------------------------------------------
+
+Returns unknown information.
+
+WMI method PowerInformation([in] uint32 arg2, [out] uint32 argr)
+----------------------------------------------------------------
+
+Returns unknown information.
+
+WMI method ReadChassisColor([out] uint32 argr)
+----------------------------------------------
+
+::
+
+ argr = CHASSIS_COLOR_ID
+
+WMI method ReadPlatformProperties([out] uint32 argr)
+----------------------------------------------------
+
+Returns unknown information.
+
+Acknowledgements
+================
+
+Kudos to `AlexIII <https://github.com/AlexIII/tcc-g15>`_ for documenting
+and testing avaliable thermal profile codes.
+
--
2.47.0
Am 12.10.24 um 04:03 schrieb Kurt Borja: > Added documentation for new WMAX interface, present on some Alienware > X-Series, Alienware M-Series and Dell's G-Series laptops. > > Signed-off-by: Kurt Borja <kuurtb@gmail.com> > --- > Documentation/wmi/devices/alienware-wmi.rst | 366 ++++++++++++++++++++ > 1 file changed, 366 insertions(+) > create mode 100644 Documentation/wmi/devices/alienware-wmi.rst Please update the MAINTAINERS entry for the alienware-wmi driver to include this documentation file. > > diff --git a/Documentation/wmi/devices/alienware-wmi.rst b/Documentation/wmi/devices/alienware-wmi.rst > new file mode 100644 > index 000000000..77460b91c > --- /dev/null > +++ b/Documentation/wmi/devices/alienware-wmi.rst > @@ -0,0 +1,366 @@ > +.. SPDX-License-Identifier: GPL-2.0-or-later > + > +============================================== > +Dell AWCC WMI interface driver (alienware-wmi) > +============================================== > + > +Introduction > +============ > + > +The WMI device WMAX has been implemented for many Alienware and Dell's G-Series > +models. Throughout these models, two implementations have been identified. The > +first one, used by older systems, deals with HDMI, brightness, RGB, amplifier > +and deep sleep control. The second one used by newer systems deals primarily > +with thermal, overclocking, and GPIO control. > + > +It is suspected that the latter is used by Alienware Command Center (AWCC) to > +manage manufacturer predefined thermal profiles. The alienware-wmi driver > +exposes Thermal_Information and Thermal_Control methods through the Platform > +Profile API to mimic AWCC's behavior. > + > +This newer interface, named AWCCMethodFunction has been reverse engineered, as > +Dell has not provided any official documentation. We will try to describe to the > +best of our ability its discovered inner workings. > + > +.. note:: > + The following method description may vary between models. > + > +WMI interface description > +------------------------- > + > +The WMI interface description can be decoded from the embedded binary MOF (bmof) > +data using the `bmfdec <https://github.com/pali/bmfdec>`_ utility: > + > +:: > + > + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("WMI Function"), guid("{A70591CE-A997-11DA-B012-B622A1EF5492}")] > + class AWCCWmiMethodFunction { > + [key, read] string InstanceName; > + [read] boolean Active; > + > + [WmiMethodId(13), Implemented, read, write, Description("Return Overclocking Report.")] void Return_OverclockingReport([out] uint32 argr); > + [WmiMethodId(14), Implemented, read, write, Description("Set OCUIBIOS Control.")] void Set_OCUIBIOSControl([in] uint32 arg2, [out] uint32 argr); > + [WmiMethodId(15), Implemented, read, write, Description("Clear OC FailSafe Flag.")] void Clear_OCFailSafeFlag([out] uint32 argr); > + [WmiMethodId(19), Implemented, read, write, Description("Get Fan Sensors.")] void GetFanSensors([in] uint32 arg2, [out] uint32 argr); > + [WmiMethodId(20), Implemented, read, write, Description("Thermal Information.")] void Thermal_Information([in] uint32 arg2, [out] uint32 argr); > + [WmiMethodId(21), Implemented, read, write, Description("Thermal Control.")] void Thermal_Control([in] uint32 arg2, [out] uint32 argr); > + [WmiMethodId(23), Implemented, read, write, Description("MemoryOCControl.")] void MemoryOCControl([in] uint32 arg2, [out] uint32 argr); > + [WmiMethodId(26), Implemented, read, write, Description("System Information.")] void SystemInformation([in] uint32 arg2, [out] uint32 argr); > + [WmiMethodId(28), Implemented, read, write, Description("Power Information.")] void PowerInformation([in] uint32 arg2, [out] uint32 argr); > + [WmiMethodId(32), Implemented, read, write, Description("FW Update GPIO toggle.")] void FWUpdateGPIOtoggle([in] uint32 arg2, [out] uint32 argr); > + [WmiMethodId(33), Implemented, read, write, Description("Read Total of GPIOs.")] void ReadTotalofGPIOs([out] uint32 argr); > + [WmiMethodId(34), Implemented, read, write, Description("Read GPIO pin Status.")] void ReadGPIOpPinStatus([in] uint32 arg2, [out] uint32 argr); > + [WmiMethodId(35), Implemented, read, write, Description("Read Chassis Color.")] void ReadChassisColor([out] uint32 argr); > + [WmiMethodId(36), Implemented, read, write, Description("Read Platform Properties.")] void ReadPlatformProperties([out] uint32 argr); > + [WmiMethodId(128), Implemented, read, write, Description("Caldera SW installation.")] void CalderaSWInstallation([out] uint32 argr); > + [WmiMethodId(129), Implemented, read, write, Description("Caldera SW is released.")] void CalderaSWReleased([out] uint32 argr); > + [WmiMethodId(130), Implemented, read, write, Description("Caldera Connection Status.")] void CalderaConnectionStatus([in] uint32 arg2, [out] uint32 argr); > + [WmiMethodId(131), Implemented, read, write, Description("Surprise Unplugged Flag Status.")] void SurpriseUnpluggedFlagStatus([out] uint32 argr); > + [WmiMethodId(132), Implemented, read, write, Description("Clear Surprise Unplugged Flag.")] void ClearSurpriseUnpluggedFlag([out] uint32 argr); > + [WmiMethodId(133), Implemented, read, write, Description("Cancel Undock Request.")] void CancelUndockRequest([out] uint32 argr); > + [WmiMethodId(135), Implemented, read, write, Description("Devices in Caldera.")] void DevicesInCaldera([in] uint32 arg2, [out] uint32 argr); > + [WmiMethodId(136), Implemented, read, write, Description("Notify BIOS for SW ready to disconnect Caldera.")] void NotifyBIOSForSWReadyToDisconnectCaldera([out] uint32 argr); > + [WmiMethodId(160), Implemented, read, write, Description("Tobii SW installation.")] void TobiiSWinstallation([out] uint32 argr); > + [WmiMethodId(161), Implemented, read, write, Description("Tobii SW Released.")] void TobiiSWReleased([out] uint32 argr); > + [WmiMethodId(162), Implemented, read, write, Description("Tobii Camera Power Reset.")] void TobiiCameraPowerReset([out] uint32 argr); > + [WmiMethodId(163), Implemented, read, write, Description("Tobii Camera Power On.")] void TobiiCameraPowerOn([out] uint32 argr); > + [WmiMethodId(164), Implemented, read, write, Description("Tobii Camera Power Off.")] void TobiiCameraPowerOff([out] uint32 argr); > + }; > + > +Some of these methods get quite intricate so we will describe them using > +pseudo-code that vaguely resembles the original ASL code. > + > +Argument Structure > +------------------ > + > +All input arguments have type **uint32** and their structure is very similar > +between methods. Usually, the first byte corresponds to a specific *operation* > +the method performs, and the subsequent bytes correspond to *arguments* passed > +to this *operation*. For example, if an operation has code 0x01 and requires an > +ID 0xA0, the argument you would pass to the method is 0xA001. > + > + > +Thermal Methods > +=============== > + > +WMI method Thermal_Information([in] uint32 arg2, [out] uint32 argr) > +------------------------------------------------------------------- > + > +:: > + > + if BYTE_0(arg2) == 0x01: > + argr = 1 > + > + if BYTE_0(arg2) == 0x02: > + argr = UNKNOWN_CONSTANT > + > + if BYTE_0(arg2) == 0x03: > + if BYTE_1(arg2) == 0x00: > + argr = FAN_ID_0 > + > + if BYTE_1(arg2) == 0x01: > + argr = FAN_ID_1 > + > + if BYTE_1(arg2) == 0x02: > + argr = FAN_ID_2 > + > + if BYTE_1(arg2) == 0x03: > + argr = FAN_ID_3 > + > + if BYTE_1(arg2) == 0x04: > + argr = SENSOR_ID_CPU | 0x0100 > + > + if BYTE_1(arg2) == 0x05: > + argr = SENSOR_ID_GPU | 0x0100 > + > + if BYTE_1(arg2) == 0x06: > + argr = THERMAL_MODE_QUIET_ID > + > + if BYTE_1(arg2) == 0x07: > + argr = THERMAL_MODE_BALANCED_ID > + > + if BYTE_1(arg2) == 0x08: > + argr = THERMAL_MODE_BALANCED_PERFORMANCE_ID > + > + if BYTE_1(arg2) == 0x09: > + argr = THERMAL_MODE_PERFORMANCE_ID > + > + if BYTE_1(arg2) == 0x0A: > + argr = THERMAL_MODE_LOW_POWER_ID > + > + if BYTE_1(arg2) == 0x0B: > + argr = THERMAL_MODE_GMODE_ID > + > + else: > + argr = 0xFFFFFFFF > + > + if BYTE_0(arg2) == 0x04: > + if is_valid_sensor(BYTE_1(arg2)): > + argr = SENSOR_TEMP_C > + else: > + argr = 0xFFFFFFFF > + > + if BYTE_0(arg2) == 0x05: > + if is_valid_fan(BYTE_1(arg2)): > + argr = FAN_RPM() > + > + if BYTE_0(arg2) == 0x06: > + skip > + > + if BYTE_0(arg2) == 0x07: > + argr = 0 > + > + If BYTE_0(arg2) == 0x08: > + if is_valid_fan(BYTE_1(arg2)): > + argr = 0 > + else: > + argr = 0xFFFFFFFF > + > + if BYTE_0(arg2) == 0x09: > + if is_valid_fan(BYTE_1(arg2)): > + argr = FAN_UNKNOWN_STAT_0() > + > + else: > + argr = 0xFFFFFFFF > + > + if BYTE_0(arg2) == 0x0A: > + argr = THERMAL_MODE_BALANCED_ID > + > + if BYTE_0(arg2) == 0x0B: > + argr = CURRENT_THERMAL_MODE() > + > + if BYTE_0(arg2) == 0x0C: > + if is_valid_fan(BYTE_1(arg2)): > + argr = FAN_UNKNOWN_STAT_1() > + else: > + argr = 0xFFFFFFFF > + > +WMI method Thermal_Control([in] uint32 arg2, [out] uint32 argr) > +--------------------------------------------------------------- > + > +:: > + > + if BYTE_0(arg2) == 0x01: > + if is_valid_thermal_profile(BYTE_1(arg2)): > + SET_THERMAL_PROFILE(BYTE_1(arg2)) > + argr = 0 > + > + if BYTE_0(arg2) == 0x02: > + if is_valid_fan(BYTE_1(arg2)): > + SET_FAN_SPEED_MULTIPLIER(BYTE_2(arg2)) > + argr = 0 > + else: > + argr = 0xFFFFFFFF > + > +.. note:: > + While you can manually change the fan speed multiplier with this method, > + Dell's BIOS tends to overwrite this changes anyway. > + > +These are the known thermal profile codes: > + > +:: > + > + CUSTOM 0x00 > + > + QUIET 0x96 > + BALANCED 0x97 > + BALANCED_PERFORMANCE 0x98 > + PERFORMANCE 0x99 > + > + QUIET_USTT 0xA3 > + BALANCED_USTT 0xA0 > + BALANCED_PERFORMANCE_USTT 0xA1 > + PERFORMANCE_USTT 0xA4 > + LOW_POWER_USTT 0xA5 > + > + GMODE 0xAB > + > +Usually if a model doesn't support the first four profiles they will support > +the User Selectable Thermal Tables (USTT) profiles and vice-versa. > + > +GMODE replaces PERFORMANCE in G-Series laptops. > + > +WMI method GetFanSensors([in] uint32 arg2, [out] uint32 argr) > +------------------------------------------------------------- > + > +:: > + > + if BYTE_0(arg2) == 1: > + if is_valid_fan(BYTE_1(arg2)): > + argr = 1 > + else: > + argr = 0 > + > + if BYTE_0(arg2) == 2: > + if is_valid_fan(BYTE_1(arg2)): > + if BYTE_2(arg2) == 0: > + argr == SENSOR_ID > + else > + argr == 0xFFFFFFFF > + else: > + argr = 0 > + > +Overclocking Methods > +==================== > + > +.. warning:: > + These methods have not been tested and are only partially reverse > + engineered. > + > +WMI method Return_OverclockingReport([out] uint32 argr) > +------------------------------------------------------- > + > +:: > + > + CSMI (0xE3, 0x99) > + argr = 0 > + > +CSMI is an unknown operation. > + > +WMI method Set_OCUIBIOSControl([in] uint32 arg2, [out] uint32 argr) > +------------------------------------------------------------------- > + > +:: > + > + CSMI (0xE3, 0x99) > + argr = 0 > + > +CSMI is an unknown operation Missing ".". > + > +WMI method Clear_OCFailSafeFlag([out] uint32 argr) > +-------------------------------------------------- > + > +:: > + > + CSMI (0xE3, 0x99) > + argr = 0 > + > +CSMI is an unknown operation Missing ".". > + > + > +WMI method MemoryOCControl([in] uint32 arg2, [out] uint32 argr) > +--------------------------------------------------------------- > + > +AWCC supports memory overclocking, but this method is very intricate and has > +not been deciphered yet. > + > +GPIO methods > +============ > + > +These methods are probably related to some kind of firmware update system, > +through a GPIO device. > + > +.. warning:: > + These methods have not been tested and are only partially reverse > + engineered. > + > +WMI method FWUpdateGPIOtoggle([in] uint32 arg2, [out] uint32 argr) > +------------------------------------------------------------------ > + > +:: > + > + if BYTE_0(arg2) == 0: > + if BYTE_1(arg2) == 1: > + SET_PIN_A_HIGH() > + else: > + SET_PIN_A_LOW() > + > + if BYTE_0(arg2) == 1: > + if BYTE_1(arg2) == 1: > + SET_PIN_B_HIGH() > + > + else: > + SET_PIN_B_LOW() > + > + else: > + argr = 1 > + > +WMI method ReadTotalofGPIOs([out] uint32 argr) > +---------------------------------------------- > + > +:: > + > + argr = 0x02 > + > +WMI method ReadGPIOpPinStatus([in] uint32 arg2, [out] uint32 argr) > +------------------------------------------------------------------ > + > +:: > + > + if BYTE_0(arg2) == 0: > + argr = PIN_A_STATUS > + > + if BYTE_0(arg2) == 1: > + argr = PIN_B_STATUS > + > +Other information Methods > +========================= > + > +WMI method SystemInformation([in] uint32 arg2, [out] uint32 argr) > +----------------------------------------------------------------- > + > +Returns unknown information. > + > +WMI method PowerInformation([in] uint32 arg2, [out] uint32 argr) > +---------------------------------------------------------------- > + > +Returns unknown information. > + > +WMI method ReadChassisColor([out] uint32 argr) > +---------------------------------------------- > + > +:: > + > + argr = CHASSIS_COLOR_ID > + > +WMI method ReadPlatformProperties([out] uint32 argr) > +---------------------------------------------------- > + > +Returns unknown information. > + > +Acknowledgements > +================ > + > +Kudos to `AlexIII <https://github.com/AlexIII/tcc-g15>`_ for documenting > +and testing avaliable thermal profile codes. avaliable -> available. Other than that this looks very good. Thanks, Armin Wolf > +
On Mon, Oct 14, 2024 at 07:10:15PM +0200, Armin Wolf wrote: > Am 12.10.24 um 04:03 schrieb Kurt Borja: > > > Added documentation for new WMAX interface, present on some Alienware > > X-Series, Alienware M-Series and Dell's G-Series laptops. > > > > Signed-off-by: Kurt Borja <kuurtb@gmail.com> > > --- > > Documentation/wmi/devices/alienware-wmi.rst | 366 ++++++++++++++++++++ > > 1 file changed, 366 insertions(+) > > create mode 100644 Documentation/wmi/devices/alienware-wmi.rst > > Please update the MAINTAINERS entry for the alienware-wmi driver to > include this documentation file. Ok. > > > > > diff --git a/Documentation/wmi/devices/alienware-wmi.rst b/Documentation/wmi/devices/alienware-wmi.rst > > new file mode 100644 > > index 000000000..77460b91c > > --- /dev/null > > +++ b/Documentation/wmi/devices/alienware-wmi.rst > > @@ -0,0 +1,366 @@ > > +.. SPDX-License-Identifier: GPL-2.0-or-later > > + > > +============================================== > > +Dell AWCC WMI interface driver (alienware-wmi) > > +============================================== > > + > > +Introduction > > +============ > > + > > +The WMI device WMAX has been implemented for many Alienware and Dell's G-Series > > +models. Throughout these models, two implementations have been identified. The > > +first one, used by older systems, deals with HDMI, brightness, RGB, amplifier > > +and deep sleep control. The second one used by newer systems deals primarily > > +with thermal, overclocking, and GPIO control. > > + > > +It is suspected that the latter is used by Alienware Command Center (AWCC) to > > +manage manufacturer predefined thermal profiles. The alienware-wmi driver > > +exposes Thermal_Information and Thermal_Control methods through the Platform > > +Profile API to mimic AWCC's behavior. > > + > > +This newer interface, named AWCCMethodFunction has been reverse engineered, as > > +Dell has not provided any official documentation. We will try to describe to the > > +best of our ability its discovered inner workings. > > + > > +.. note:: > > + The following method description may vary between models. > > + > > +WMI interface description > > +------------------------- > > + > > +The WMI interface description can be decoded from the embedded binary MOF (bmof) > > +data using the `bmfdec <https://github.com/pali/bmfdec>`_ utility: > > + > > +:: > > + > > + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("WMI Function"), guid("{A70591CE-A997-11DA-B012-B622A1EF5492}")] > > + class AWCCWmiMethodFunction { > > + [key, read] string InstanceName; > > + [read] boolean Active; > > + > > + [WmiMethodId(13), Implemented, read, write, Description("Return Overclocking Report.")] void Return_OverclockingReport([out] uint32 argr); > > + [WmiMethodId(14), Implemented, read, write, Description("Set OCUIBIOS Control.")] void Set_OCUIBIOSControl([in] uint32 arg2, [out] uint32 argr); > > + [WmiMethodId(15), Implemented, read, write, Description("Clear OC FailSafe Flag.")] void Clear_OCFailSafeFlag([out] uint32 argr); > > + [WmiMethodId(19), Implemented, read, write, Description("Get Fan Sensors.")] void GetFanSensors([in] uint32 arg2, [out] uint32 argr); > > + [WmiMethodId(20), Implemented, read, write, Description("Thermal Information.")] void Thermal_Information([in] uint32 arg2, [out] uint32 argr); > > + [WmiMethodId(21), Implemented, read, write, Description("Thermal Control.")] void Thermal_Control([in] uint32 arg2, [out] uint32 argr); > > + [WmiMethodId(23), Implemented, read, write, Description("MemoryOCControl.")] void MemoryOCControl([in] uint32 arg2, [out] uint32 argr); > > + [WmiMethodId(26), Implemented, read, write, Description("System Information.")] void SystemInformation([in] uint32 arg2, [out] uint32 argr); > > + [WmiMethodId(28), Implemented, read, write, Description("Power Information.")] void PowerInformation([in] uint32 arg2, [out] uint32 argr); > > + [WmiMethodId(32), Implemented, read, write, Description("FW Update GPIO toggle.")] void FWUpdateGPIOtoggle([in] uint32 arg2, [out] uint32 argr); > > + [WmiMethodId(33), Implemented, read, write, Description("Read Total of GPIOs.")] void ReadTotalofGPIOs([out] uint32 argr); > > + [WmiMethodId(34), Implemented, read, write, Description("Read GPIO pin Status.")] void ReadGPIOpPinStatus([in] uint32 arg2, [out] uint32 argr); > > + [WmiMethodId(35), Implemented, read, write, Description("Read Chassis Color.")] void ReadChassisColor([out] uint32 argr); > > + [WmiMethodId(36), Implemented, read, write, Description("Read Platform Properties.")] void ReadPlatformProperties([out] uint32 argr); > > + [WmiMethodId(128), Implemented, read, write, Description("Caldera SW installation.")] void CalderaSWInstallation([out] uint32 argr); > > + [WmiMethodId(129), Implemented, read, write, Description("Caldera SW is released.")] void CalderaSWReleased([out] uint32 argr); > > + [WmiMethodId(130), Implemented, read, write, Description("Caldera Connection Status.")] void CalderaConnectionStatus([in] uint32 arg2, [out] uint32 argr); > > + [WmiMethodId(131), Implemented, read, write, Description("Surprise Unplugged Flag Status.")] void SurpriseUnpluggedFlagStatus([out] uint32 argr); > > + [WmiMethodId(132), Implemented, read, write, Description("Clear Surprise Unplugged Flag.")] void ClearSurpriseUnpluggedFlag([out] uint32 argr); > > + [WmiMethodId(133), Implemented, read, write, Description("Cancel Undock Request.")] void CancelUndockRequest([out] uint32 argr); > > + [WmiMethodId(135), Implemented, read, write, Description("Devices in Caldera.")] void DevicesInCaldera([in] uint32 arg2, [out] uint32 argr); > > + [WmiMethodId(136), Implemented, read, write, Description("Notify BIOS for SW ready to disconnect Caldera.")] void NotifyBIOSForSWReadyToDisconnectCaldera([out] uint32 argr); > > + [WmiMethodId(160), Implemented, read, write, Description("Tobii SW installation.")] void TobiiSWinstallation([out] uint32 argr); > > + [WmiMethodId(161), Implemented, read, write, Description("Tobii SW Released.")] void TobiiSWReleased([out] uint32 argr); > > + [WmiMethodId(162), Implemented, read, write, Description("Tobii Camera Power Reset.")] void TobiiCameraPowerReset([out] uint32 argr); > > + [WmiMethodId(163), Implemented, read, write, Description("Tobii Camera Power On.")] void TobiiCameraPowerOn([out] uint32 argr); > > + [WmiMethodId(164), Implemented, read, write, Description("Tobii Camera Power Off.")] void TobiiCameraPowerOff([out] uint32 argr); > > + }; > > + > > +Some of these methods get quite intricate so we will describe them using > > +pseudo-code that vaguely resembles the original ASL code. > > + > > +Argument Structure > > +------------------ > > + > > +All input arguments have type **uint32** and their structure is very similar > > +between methods. Usually, the first byte corresponds to a specific *operation* > > +the method performs, and the subsequent bytes correspond to *arguments* passed > > +to this *operation*. For example, if an operation has code 0x01 and requires an > > +ID 0xA0, the argument you would pass to the method is 0xA001. > > + > > + > > +Thermal Methods > > +=============== > > + > > +WMI method Thermal_Information([in] uint32 arg2, [out] uint32 argr) > > +------------------------------------------------------------------- > > + > > +:: > > + > > + if BYTE_0(arg2) == 0x01: > > + argr = 1 > > + > > + if BYTE_0(arg2) == 0x02: > > + argr = UNKNOWN_CONSTANT > > + > > + if BYTE_0(arg2) == 0x03: > > + if BYTE_1(arg2) == 0x00: > > + argr = FAN_ID_0 > > + > > + if BYTE_1(arg2) == 0x01: > > + argr = FAN_ID_1 > > + > > + if BYTE_1(arg2) == 0x02: > > + argr = FAN_ID_2 > > + > > + if BYTE_1(arg2) == 0x03: > > + argr = FAN_ID_3 > > + > > + if BYTE_1(arg2) == 0x04: > > + argr = SENSOR_ID_CPU | 0x0100 > > + > > + if BYTE_1(arg2) == 0x05: > > + argr = SENSOR_ID_GPU | 0x0100 > > + > > + if BYTE_1(arg2) == 0x06: > > + argr = THERMAL_MODE_QUIET_ID > > + > > + if BYTE_1(arg2) == 0x07: > > + argr = THERMAL_MODE_BALANCED_ID > > + > > + if BYTE_1(arg2) == 0x08: > > + argr = THERMAL_MODE_BALANCED_PERFORMANCE_ID > > + > > + if BYTE_1(arg2) == 0x09: > > + argr = THERMAL_MODE_PERFORMANCE_ID > > + > > + if BYTE_1(arg2) == 0x0A: > > + argr = THERMAL_MODE_LOW_POWER_ID > > + > > + if BYTE_1(arg2) == 0x0B: > > + argr = THERMAL_MODE_GMODE_ID > > + > > + else: > > + argr = 0xFFFFFFFF > > + > > + if BYTE_0(arg2) == 0x04: > > + if is_valid_sensor(BYTE_1(arg2)): > > + argr = SENSOR_TEMP_C > > + else: > > + argr = 0xFFFFFFFF > > + > > + if BYTE_0(arg2) == 0x05: > > + if is_valid_fan(BYTE_1(arg2)): > > + argr = FAN_RPM() > > + > > + if BYTE_0(arg2) == 0x06: > > + skip > > + > > + if BYTE_0(arg2) == 0x07: > > + argr = 0 > > + > > + If BYTE_0(arg2) == 0x08: > > + if is_valid_fan(BYTE_1(arg2)): > > + argr = 0 > > + else: > > + argr = 0xFFFFFFFF > > + > > + if BYTE_0(arg2) == 0x09: > > + if is_valid_fan(BYTE_1(arg2)): > > + argr = FAN_UNKNOWN_STAT_0() > > + > > + else: > > + argr = 0xFFFFFFFF > > + > > + if BYTE_0(arg2) == 0x0A: > > + argr = THERMAL_MODE_BALANCED_ID > > + > > + if BYTE_0(arg2) == 0x0B: > > + argr = CURRENT_THERMAL_MODE() > > + > > + if BYTE_0(arg2) == 0x0C: > > + if is_valid_fan(BYTE_1(arg2)): > > + argr = FAN_UNKNOWN_STAT_1() > > + else: > > + argr = 0xFFFFFFFF > > + > > +WMI method Thermal_Control([in] uint32 arg2, [out] uint32 argr) > > +--------------------------------------------------------------- > > + > > +:: > > + > > + if BYTE_0(arg2) == 0x01: > > + if is_valid_thermal_profile(BYTE_1(arg2)): > > + SET_THERMAL_PROFILE(BYTE_1(arg2)) > > + argr = 0 > > + > > + if BYTE_0(arg2) == 0x02: > > + if is_valid_fan(BYTE_1(arg2)): > > + SET_FAN_SPEED_MULTIPLIER(BYTE_2(arg2)) > > + argr = 0 > > + else: > > + argr = 0xFFFFFFFF > > + > > +.. note:: > > + While you can manually change the fan speed multiplier with this method, > > + Dell's BIOS tends to overwrite this changes anyway. > > + > > +These are the known thermal profile codes: > > + > > +:: > > + > > + CUSTOM 0x00 > > + > > + QUIET 0x96 > > + BALANCED 0x97 > > + BALANCED_PERFORMANCE 0x98 > > + PERFORMANCE 0x99 > > + > > + QUIET_USTT 0xA3 > > + BALANCED_USTT 0xA0 > > + BALANCED_PERFORMANCE_USTT 0xA1 > > + PERFORMANCE_USTT 0xA4 > > + LOW_POWER_USTT 0xA5 > > + > > + GMODE 0xAB > > + > > +Usually if a model doesn't support the first four profiles they will support > > +the User Selectable Thermal Tables (USTT) profiles and vice-versa. > > + > > +GMODE replaces PERFORMANCE in G-Series laptops. > > + > > +WMI method GetFanSensors([in] uint32 arg2, [out] uint32 argr) > > +------------------------------------------------------------- > > + > > +:: > > + > > + if BYTE_0(arg2) == 1: > > + if is_valid_fan(BYTE_1(arg2)): > > + argr = 1 > > + else: > > + argr = 0 > > + > > + if BYTE_0(arg2) == 2: > > + if is_valid_fan(BYTE_1(arg2)): > > + if BYTE_2(arg2) == 0: > > + argr == SENSOR_ID > > + else > > + argr == 0xFFFFFFFF > > + else: > > + argr = 0 > > + > > +Overclocking Methods > > +==================== > > + > > +.. warning:: > > + These methods have not been tested and are only partially reverse > > + engineered. > > + > > +WMI method Return_OverclockingReport([out] uint32 argr) > > +------------------------------------------------------- > > + > > +:: > > + > > + CSMI (0xE3, 0x99) > > + argr = 0 > > + > > +CSMI is an unknown operation. > > + > > +WMI method Set_OCUIBIOSControl([in] uint32 arg2, [out] uint32 argr) > > +------------------------------------------------------------------- > > + > > +:: > > + > > + CSMI (0xE3, 0x99) > > + argr = 0 > > + > > +CSMI is an unknown operation > > Missing ".". > > > + > > +WMI method Clear_OCFailSafeFlag([out] uint32 argr) > > +-------------------------------------------------- > > + > > +:: > > + > > + CSMI (0xE3, 0x99) > > + argr = 0 > > + > > +CSMI is an unknown operation > > Missing ".". > > > + > > + > > +WMI method MemoryOCControl([in] uint32 arg2, [out] uint32 argr) > > +--------------------------------------------------------------- > > + > > +AWCC supports memory overclocking, but this method is very intricate and has > > +not been deciphered yet. > > + > > +GPIO methods > > +============ > > + > > +These methods are probably related to some kind of firmware update system, > > +through a GPIO device. > > + > > +.. warning:: > > + These methods have not been tested and are only partially reverse > > + engineered. > > + > > +WMI method FWUpdateGPIOtoggle([in] uint32 arg2, [out] uint32 argr) > > +------------------------------------------------------------------ > > + > > +:: > > + > > + if BYTE_0(arg2) == 0: > > + if BYTE_1(arg2) == 1: > > + SET_PIN_A_HIGH() > > + else: > > + SET_PIN_A_LOW() > > + > > + if BYTE_0(arg2) == 1: > > + if BYTE_1(arg2) == 1: > > + SET_PIN_B_HIGH() > > + > > + else: > > + SET_PIN_B_LOW() > > + > > + else: > > + argr = 1 > > + > > +WMI method ReadTotalofGPIOs([out] uint32 argr) > > +---------------------------------------------- > > + > > +:: > > + > > + argr = 0x02 > > + > > +WMI method ReadGPIOpPinStatus([in] uint32 arg2, [out] uint32 argr) > > +------------------------------------------------------------------ > > + > > +:: > > + > > + if BYTE_0(arg2) == 0: > > + argr = PIN_A_STATUS > > + > > + if BYTE_0(arg2) == 1: > > + argr = PIN_B_STATUS > > + > > +Other information Methods > > +========================= > > + > > +WMI method SystemInformation([in] uint32 arg2, [out] uint32 argr) > > +----------------------------------------------------------------- > > + > > +Returns unknown information. > > + > > +WMI method PowerInformation([in] uint32 arg2, [out] uint32 argr) > > +---------------------------------------------------------------- > > + > > +Returns unknown information. > > + > > +WMI method ReadChassisColor([out] uint32 argr) > > +---------------------------------------------- > > + > > +:: > > + > > + argr = CHASSIS_COLOR_ID > > + > > +WMI method ReadPlatformProperties([out] uint32 argr) > > +---------------------------------------------------- > > + > > +Returns unknown information. > > + > > +Acknowledgements > > +================ > > + > > +Kudos to `AlexIII <https://github.com/AlexIII/tcc-g15>`_ for documenting > > +and testing avaliable thermal profile codes. > > avaliable -> available. > > Other than that this looks very good. > > Thanks, > Armin Wolf I will fix the typos. Thanks. Kurt > > > +
Am 07.10.24 um 11:33 schrieb Kurt Borja: > This patch adds platform_profile support for Dell devices which implement > User Selectable Thermal Tables (USTT) that are meant to be controlled by > Alienware Command Center (AWCC). These devices may include newer Alienware > M-Series, Alienware X-Series and Dell's G-Series. This patch, was tested > by me on an Alienware x15 R1. > > It is suspected that Alienware Command Center manages thermal profiles > through the WMI interface, specifically through a device with identifier > \_SB_.AMW1.WMAX. This device was reverse engineered and the relevant > functionality is documented here [1]. This driver interacts with this > WMI device and thus is able to mimic AWCC's thermal profiles functionality > through the platform_profile API. In consequence the user would be able > to set and retrieve thermal profiles, which are just fan speed profiles. > > This driver was heavily inspired on inspur_platform_profile, special > thanks. > > Notes: > - Performance (FullSpeed) profile is a special profile which has it's own > entry in the Firmware Settings of the Alienware x15 R1. It also changes > the color of the F1 key. I suspect this behavior would be replicated in > other X-Series or M-Series laptops. > - G-Mode is a profile documented on [1] which mimics the behavior of > FullSpeed mode but it does not have an entry on the Firmware Settings of > the Alienware x15 R1, this may correspond to the G-Mode functionality on > G-Series laptops (activated by a special button) but I cannot test it. I > did not include this code in the driver as G-Mode causes unexpected > behavior on X-Series laptops. > > Thanks for your time and patiente in advance. > > Regards, > > Kurt > > [1] https://gist.github.com/kuu-rt/b22328ff2b454be505387e2a38c61ee4 Hi, this WMI device is already handled by the alienware-wmi driver. Could you please integrate this functionality into this driver instead of creating a new one? Thanks, Armin Wolf > Signed-off-by: Kurt Borja <kuurtb@gmail.com> > --- > drivers/platform/x86/dell/Kconfig | 9 + > drivers/platform/x86/dell/Makefile | 1 + > drivers/platform/x86/dell/dell-wmi-awcc.c | 204 ++++++++++++++++++++++ > 3 files changed, 214 insertions(+) > create mode 100644 drivers/platform/x86/dell/dell-wmi-awcc.c > > diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig > index 68a49788a..20300ff98 100644 > --- a/drivers/platform/x86/dell/Kconfig > +++ b/drivers/platform/x86/dell/Kconfig > @@ -27,6 +27,15 @@ config ALIENWARE_WMI > zones on Alienware machines that don't contain a dedicated AlienFX > USB MCU such as the X51 and X51-R2. > > +config AWCC_PLATFORM_PROFILE > + tristate "AWCC Platform Profile support" > + depends on ACPI_WMI > + select ACPI_PLATFORM_PROFILE > + help > + This driver provides platform_profile support for selecting thermal > + profiles on Dell devices with User Selectable Thermal Tables, > + controlled by AWCC's WMI interface. > + > config DCDBAS > tristate "Dell Systems Management Base Driver" > default m > diff --git a/drivers/platform/x86/dell/Makefile b/drivers/platform/x86/dell/Makefile > index 79d60f1bf..bfef99580 100644 > --- a/drivers/platform/x86/dell/Makefile > +++ b/drivers/platform/x86/dell/Makefile > @@ -23,4 +23,5 @@ obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o > obj-$(CONFIG_DELL_WMI_DESCRIPTOR) += dell-wmi-descriptor.o > obj-$(CONFIG_DELL_WMI_DDV) += dell-wmi-ddv.o > obj-$(CONFIG_DELL_WMI_LED) += dell-wmi-led.o > +obj-$(CONFIG_AWCC_PLATFORM_PROFILE) += dell-wmi-awcc.o > obj-$(CONFIG_DELL_WMI_SYSMAN) += dell-wmi-sysman/ > diff --git a/drivers/platform/x86/dell/dell-wmi-awcc.c b/drivers/platform/x86/dell/dell-wmi-awcc.c > new file mode 100644 > index 000000000..0837d1bc6 > --- /dev/null > +++ b/drivers/platform/x86/dell/dell-wmi-awcc.c > @@ -0,0 +1,204 @@ > +// SPDX-License-Identifier: GPL-2.0-or-later > +/* > + * WMI driver for Dell's AWCC platform_profile > + * > + * Copyright (c) Kurt Borja <kuurtb@gmail.com> > + * > + */ > + > +#include <linux/acpi.h> > +#include <linux/device.h> > +#include <linux/module.h> > +#include <linux/platform_profile.h> > +#include <linux/wmi.h> > + > +#define PROF_TO_ARG(mode) ((mode << 8) | 1) > + > +#define DELL_AWCC_GUID "A70591CE-A997-11DA-B012-B622A1EF5492" > + > +enum awcc_wmi_method { > + AWCC_WMI_THERMAL_INFORMATION = 0x14, > + AWCC_WMI_THERMAL_CONTROL = 0x15, > +}; > + > +enum awcc_tmp_profile { > + AWCC_TMP_PROFILE_BALANCED = 0xA0, > + AWCC_TMP_PROFILE_BALANCED_PERFORMANCE = 0xA1, > + AWCC_TMP_PROFILE_COOL = 0xA2, > + AWCC_TMP_PROFILE_QUIET = 0xA3, > + AWCC_TMP_PROFILE_PERFORMANCE = 0xA4, > + AWCC_TMP_PROFILE_LOW_POWER = 0xA5, > +}; > + > +struct awcc_wmi_priv { > + struct wmi_device *wdev; > + struct platform_profile_handler handler; > +}; > + > +static int awcc_wmi_query(struct wmi_device *wdev, enum awcc_wmi_method method, > + u32 arg, u32 *res) > +{ > + struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL }; > + const struct acpi_buffer in = { sizeof(arg), &arg }; > + union acpi_object *obj; > + acpi_status status; > + int ret = 0; > + > + status = wmidev_evaluate_method(wdev, 0x0, method, &in, &out); > + > + if (ACPI_FAILURE(status)) > + return -EIO; > + > + obj = out.pointer; > + if (!obj) > + return -ENODATA; > + > + if (obj->type != ACPI_TYPE_INTEGER) { > + ret = -EINVAL; > + goto out_free; > + } > + > + if (obj->integer.value <= U32_MAX) > + *res = (u32)obj->integer.value; > + else > + ret = -ERANGE; > + > +out_free: > + kfree(obj); > + > + return ret; > +} > + > +static int awcc_platform_profile_get(struct platform_profile_handler *pprof, > + enum platform_profile_option *profile) > +{ > + struct awcc_wmi_priv *priv = > + container_of(pprof, struct awcc_wmi_priv, handler); > + > + u32 res; > + int ret; > + > + ret = awcc_wmi_query(priv->wdev, AWCC_WMI_THERMAL_INFORMATION, 0x0B, > + &res); > + > + if (ret < 0) > + return ret; > + > + if (res < 0) > + return -EBADRQC; > + > + switch (res) { > + case AWCC_TMP_PROFILE_LOW_POWER: > + *profile = PLATFORM_PROFILE_LOW_POWER; > + break; > + case AWCC_TMP_PROFILE_QUIET: > + *profile = PLATFORM_PROFILE_QUIET; > + break; > + case AWCC_TMP_PROFILE_BALANCED: > + *profile = PLATFORM_PROFILE_BALANCED; > + break; > + case AWCC_TMP_PROFILE_BALANCED_PERFORMANCE: > + *profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE; > + break; > + case AWCC_TMP_PROFILE_PERFORMANCE: > + *profile = PLATFORM_PROFILE_PERFORMANCE; > + break; > + default: > + return -ENODATA; > + } > + > + return 0; > +} > + > +static int awcc_platform_profile_set(struct platform_profile_handler *pprof, > + enum platform_profile_option profile) > +{ > + struct awcc_wmi_priv *priv = > + container_of(pprof, struct awcc_wmi_priv, handler); > + > + u32 arg; > + u32 res; > + int ret; > + > + switch (profile) { > + case PLATFORM_PROFILE_LOW_POWER: > + arg = PROF_TO_ARG(AWCC_TMP_PROFILE_LOW_POWER); > + break; > + case PLATFORM_PROFILE_QUIET: > + arg = PROF_TO_ARG(AWCC_TMP_PROFILE_QUIET); > + break; > + case PLATFORM_PROFILE_BALANCED: > + arg = PROF_TO_ARG(AWCC_TMP_PROFILE_BALANCED); > + break; > + case PLATFORM_PROFILE_BALANCED_PERFORMANCE: > + arg = PROF_TO_ARG(AWCC_TMP_PROFILE_BALANCED_PERFORMANCE); > + break; > + case PLATFORM_PROFILE_PERFORMANCE: > + arg = PROF_TO_ARG(AWCC_TMP_PROFILE_PERFORMANCE); > + break; > + default: > + return -EOPNOTSUPP; > + } > + > + ret = awcc_wmi_query(priv->wdev, AWCC_WMI_THERMAL_CONTROL, arg, &res); > + > + if (ret < 0) > + return ret; > + > + if (res < 0) > + return -EBADRQC; > + > + return 0; > +} > + > +static int awcc_wmi_probe(struct wmi_device *wdev, const void *context) > +{ > + struct awcc_wmi_priv *priv; > + > + priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + > + priv->wdev = wdev; > + dev_set_drvdata(&wdev->dev, priv); > + > + priv->handler.profile_set = awcc_platform_profile_set; > + priv->handler.profile_get = awcc_platform_profile_get; > + > + set_bit(PLATFORM_PROFILE_LOW_POWER, priv->handler.choices); > + set_bit(PLATFORM_PROFILE_QUIET, priv->handler.choices); > + set_bit(PLATFORM_PROFILE_BALANCED, priv->handler.choices); > + set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, priv->handler.choices); > + set_bit(PLATFORM_PROFILE_PERFORMANCE, priv->handler.choices); > + > + return platform_profile_register(&priv->handler); > +} > + > +static void awcc_wmi_remove(struct wmi_device *wdev) > +{ > + platform_profile_remove(); > +} > + > +static const struct wmi_device_id awcc_wmi_id_table[] = { > + { .guid_string = DELL_AWCC_GUID }, > + {}, > +}; > + > +MODULE_DEVICE_TABLE(wmi, awcc_wmi_id_table); > + > +static struct wmi_driver awcc_wmi_driver = { > + .driver = { > + .name = "dell-wmi-awcc-platform-profile", > + .probe_type = PROBE_PREFER_ASYNCHRONOUS, > + }, > + .id_table = awcc_wmi_id_table, > + .probe = awcc_wmi_probe, > + .remove = awcc_wmi_remove, > + .no_singleton = true, > +}; > + > +module_wmi_driver(awcc_wmi_driver); > + > +MODULE_AUTHOR("Kurt Borja"); > +MODULE_DESCRIPTION("Dell AWCC WMI driver"); > +MODULE_LICENSE("GPL");
On Mon, Oct 07, 2024 at 02:24:52PM +0200, Armin Wolf wrote: > Hi, > > this WMI device is already handled by the alienware-wmi driver. Could you please integrate > this functionality into this driver instead of creating a new one? > > Thanks, > Armin Wolf Hi, Thank you for your feedback. Although they the same name and same GUID, both interfaces are very different. Alienware x15's WMAX method doesn't support any of the methods listed on alienware-wmi driver and the de-compiled MOF file on [1] which is an open source alternative to AWCC, makes me think this might be the case for various other newer models (G, M, X Series). Still I could implement it as a quirk of newer models. Would this be ok? My only worry was that it could make alienware-wmi's logic overly complex and cumbersome, as it would support two very different interfaces with the same GUID. Kurt [1] https://github.com/AlexIII/tcc-g15/blob/master/WMI-AWCC-doc.md
This patch adds platform_profile support for Dell devices which implement
User Selectable Thermal Tables (USTT) that are meant to be controlled by
Alienware Command Center (AWCC). These devices may include newer Alienware
M-Series, Alienware X-Series and Dell's G-Series. This patch, was tested
by me on an Alienware x15 R1.
It is suspected that Alienware Command Center manages thermal profiles
through the WMI interface, specifically through a device with identifier
\_SB_.AMW1.WMAX. This device was reverse engineered and the relevant
functionality is documented here [1]. This driver interacts with this
WMI device and thus is able to mimic AWCC's thermal profiles functionality
through the platform_profile API. In consequence the user would be able
to set and retrieve thermal profiles, which are just fan speed profiles.
This driver was heavily inspired on inspur_platform_profile, special
thanks.
Notes:
- Performance (FullSpeed) profile is a special profile which has it's own
entry in the Firmware Settings of the Alienware x15 R1. It also changes
the color of the F1 key. I suspect this behavior would be replicated in
other X-Series or M-Series laptops.
- G-Mode is a profile documented on [1] which mimics the behavior of
FullSpeed mode but it does not have an entry on the Firmware Settings of
the Alienware x15 R1, this may correspond to the G-Mode functionality on
G-Series laptops (activated by a special button) but I cannot test it. I
did not include this code in the driver as G-Mode causes unexpected
behavior on X-Series laptops.
Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
v3:
- Removed extra empty line
- 0x0B named WMAX_ARG_GET_CURRENT_PROF
- Removed casts to the same type on functions added in this patch
- Thermal profile to WMAX argument is now an static function and makes
use of in-built kernel macros
- Platform profile is now removed only if it was created first
- create_platform_profile is now create_thermal_profile to avoid
confusion
- profile_get and profile_set functions renamed too to match the above
v2:
- Moved functionality to alienware-wmi driver
- Added thermal and gmode quirks to add support based on dmi match
- Performance profile is now GMODE for devices that support it
- alienware_wmax_command now is insize agnostic to support new thermal
methods
---
drivers/platform/x86/dell/Kconfig | 1 +
drivers/platform/x86/dell/alienware-wmi.c | 238 ++++++++++++++++++++--
2 files changed, 226 insertions(+), 13 deletions(-)
diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig
index 68a49788a..b06d634cd 100644
--- a/drivers/platform/x86/dell/Kconfig
+++ b/drivers/platform/x86/dell/Kconfig
@@ -21,6 +21,7 @@ config ALIENWARE_WMI
depends on LEDS_CLASS
depends on NEW_LEDS
depends on ACPI_WMI
+ select ACPI_PLATFORM_PROFILE
help
This is a driver for controlling Alienware BIOS driven
features. It exposes an interface for controlling the AlienFX
diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
index f5ee62ce1..e3ef4b10b 100644
--- a/drivers/platform/x86/dell/alienware-wmi.c
+++ b/drivers/platform/x86/dell/alienware-wmi.c
@@ -10,6 +10,7 @@
#include <linux/acpi.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/platform_profile.h>
#include <linux/dmi.h>
#include <linux/leds.h>
@@ -25,6 +26,10 @@
#define WMAX_METHOD_AMPLIFIER_CABLE 0x6
#define WMAX_METHOD_DEEP_SLEEP_CONTROL 0x0B
#define WMAX_METHOD_DEEP_SLEEP_STATUS 0x0C
+#define WMAX_METHOD_THERMAL_INFORMATION 0x14
+#define WMAX_METHOD_THERMAL_CONTROL 0x15
+
+#define WMAX_ARG_GET_CURRENT_PROF 0x0B
MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
MODULE_DESCRIPTION("Alienware special feature control");
@@ -49,11 +54,22 @@ enum WMAX_CONTROL_STATES {
WMAX_SUSPEND = 3,
};
+enum WMAX_THERMAL_PROFILE {
+ WMAX_THERMAL_QUIET = 0xA3,
+ WMAX_THERMAL_BALANCED = 0xA0,
+ WMAX_THERMAL_BALANCED_PERFORMANCE = 0xA1,
+ WMAX_THERMAL_PERFORMANCE = 0xA4,
+ WMAX_THERMAL_GMODE = 0xAB,
+ WMAX_THERMAL_LOW_POWER = 0xA5,
+};
+
struct quirk_entry {
u8 num_zones;
u8 hdmi_mux;
u8 amplifier;
u8 deepslp;
+ u8 thermal;
+ u8 gmode;
};
static struct quirk_entry *quirks;
@@ -64,6 +80,8 @@ static struct quirk_entry quirk_inspiron5675 = {
.hdmi_mux = 0,
.amplifier = 0,
.deepslp = 0,
+ .thermal = 0,
+ .gmode = 0,
};
static struct quirk_entry quirk_unknown = {
@@ -71,6 +89,8 @@ static struct quirk_entry quirk_unknown = {
.hdmi_mux = 0,
.amplifier = 0,
.deepslp = 0,
+ .thermal = 0,
+ .gmode = 0,
};
static struct quirk_entry quirk_x51_r1_r2 = {
@@ -78,6 +98,8 @@ static struct quirk_entry quirk_x51_r1_r2 = {
.hdmi_mux = 0,
.amplifier = 0,
.deepslp = 0,
+ .thermal = 0,
+ .gmode = 0,
};
static struct quirk_entry quirk_x51_r3 = {
@@ -85,6 +107,8 @@ static struct quirk_entry quirk_x51_r3 = {
.hdmi_mux = 0,
.amplifier = 1,
.deepslp = 0,
+ .thermal = 0,
+ .gmode = 0,
};
static struct quirk_entry quirk_asm100 = {
@@ -92,6 +116,8 @@ static struct quirk_entry quirk_asm100 = {
.hdmi_mux = 1,
.amplifier = 0,
.deepslp = 0,
+ .thermal = 0,
+ .gmode = 0,
};
static struct quirk_entry quirk_asm200 = {
@@ -99,6 +125,8 @@ static struct quirk_entry quirk_asm200 = {
.hdmi_mux = 1,
.amplifier = 0,
.deepslp = 1,
+ .thermal = 0,
+ .gmode = 0,
};
static struct quirk_entry quirk_asm201 = {
@@ -106,6 +134,17 @@ static struct quirk_entry quirk_asm201 = {
.hdmi_mux = 1,
.amplifier = 1,
.deepslp = 1,
+ .thermal = 0,
+ .gmode = 0,
+};
+
+static struct quirk_entry quirk_x15_r1 = {
+ .num_zones = 2,
+ .hdmi_mux = 0,
+ .amplifier = 0,
+ .deepslp = 0,
+ .thermal = 1,
+ .gmode = 0,
};
static int __init dmi_matched(const struct dmi_system_id *dmi)
@@ -169,6 +208,15 @@ static const struct dmi_system_id alienware_quirks[] __initconst = {
},
.driver_data = &quirk_asm201,
},
+ {
+ .callback = dmi_matched,
+ .ident = "Alienware x15 R1",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R1")
+ },
+ .driver_data = &quirk_x15_r1,
+ },
{
.callback = dmi_matched,
.ident = "Dell Inc. Inspiron 5675",
@@ -218,6 +266,7 @@ static struct platform_device *platform_device;
static struct device_attribute *zone_dev_attrs;
static struct attribute **zone_attrs;
static struct platform_zone *zone_data;
+static struct platform_profile_handler pp_handler;
static struct platform_driver platform_driver = {
.driver = {
@@ -500,7 +549,7 @@ static void alienware_zone_exit(struct platform_device *dev)
kfree(zone_attrs);
}
-static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
+static acpi_status alienware_wmax_command(void *in_args, size_t insize,
u32 command, int *out_data)
{
acpi_status status;
@@ -508,7 +557,7 @@ static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
struct acpi_buffer input;
struct acpi_buffer output;
- input.length = (acpi_size) sizeof(*in_args);
+ input.length = (acpi_size) insize;
input.pointer = in_args;
if (out_data) {
output.length = ACPI_ALLOCATE_BUFFER;
@@ -541,8 +590,8 @@ static ssize_t show_hdmi_cable(struct device *dev,
.arg = 0,
};
status =
- alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE,
- (u32 *) &out_data);
+ alienware_wmax_command(&in_args, sizeof(in_args),
+ WMAX_METHOD_HDMI_CABLE, (u32 *) &out_data);
if (ACPI_SUCCESS(status)) {
if (out_data == 0)
return sysfs_emit(buf, "[unconnected] connected unknown\n");
@@ -562,8 +611,8 @@ static ssize_t show_hdmi_source(struct device *dev,
.arg = 0,
};
status =
- alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS,
- (u32 *) &out_data);
+ alienware_wmax_command(&in_args, sizeof(in_args),
+ WMAX_METHOD_HDMI_STATUS, (u32 *) &out_data);
if (ACPI_SUCCESS(status)) {
if (out_data == 1)
@@ -589,7 +638,8 @@ static ssize_t toggle_hdmi_source(struct device *dev,
args.arg = 3;
pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf);
- status = alienware_wmax_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL);
+ status = alienware_wmax_command(&args, sizeof(args),
+ WMAX_METHOD_HDMI_SOURCE, NULL);
if (ACPI_FAILURE(status))
pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
@@ -642,8 +692,8 @@ static ssize_t show_amplifier_status(struct device *dev,
.arg = 0,
};
status =
- alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE,
- (u32 *) &out_data);
+ alienware_wmax_command(&in_args, sizeof(in_args),
+ WMAX_METHOD_AMPLIFIER_CABLE, (u32 *) &out_data);
if (ACPI_SUCCESS(status)) {
if (out_data == 0)
return sysfs_emit(buf, "[unconnected] connected unknown\n");
@@ -694,8 +744,8 @@ static ssize_t show_deepsleep_status(struct device *dev,
struct wmax_basic_args in_args = {
.arg = 0,
};
- status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS,
- (u32 *) &out_data);
+ status = alienware_wmax_command(&in_args, sizeof(in_args),
+ WMAX_METHOD_DEEP_SLEEP_STATUS, (u32 *) &out_data);
if (ACPI_SUCCESS(status)) {
if (out_data == 0)
return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
@@ -723,8 +773,8 @@ static ssize_t toggle_deepsleep(struct device *dev,
args.arg = 2;
pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf);
- status = alienware_wmax_command(&args, WMAX_METHOD_DEEP_SLEEP_CONTROL,
- NULL);
+ status = alienware_wmax_command(&args, sizeof(args),
+ WMAX_METHOD_DEEP_SLEEP_CONTROL, NULL);
if (ACPI_FAILURE(status))
pr_err("alienware-wmi: deep sleep control failed: results: %u\n",
@@ -760,6 +810,160 @@ static int create_deepsleep(struct platform_device *dev)
return ret;
}
+/*
+ * Thermal Profile control
+ * - Provides thermal profile control through the Platform Profile API
+ */
+#define PROFILE_MASK GENMASK(15,8)
+#define PROFILE_ACTIVATE BIT(0)
+
+static u32 profile_to_wmax_arg(enum WMAX_THERMAL_PROFILE prof)
+{
+ return FIELD_PREP(PROFILE_MASK, prof) | PROFILE_ACTIVATE;
+}
+
+static int thermal_profile_get(struct platform_profile_handler *pprof,
+ enum platform_profile_option *profile)
+{
+ acpi_status status;
+ u32 in_args = WMAX_ARG_GET_CURRENT_PROF;
+ u32 out_data;
+
+ status = alienware_wmax_command(&in_args, sizeof(in_args),
+ WMAX_METHOD_THERMAL_INFORMATION, &out_data);
+
+ if (ACPI_FAILURE(status))
+ return -EOPNOTSUPP;
+
+ if (out_data == 0xFFFFFFFF)
+ return -EBADRQC;
+
+ switch (out_data) {
+ case WMAX_THERMAL_LOW_POWER:
+ *profile = PLATFORM_PROFILE_LOW_POWER;
+ break;
+ case WMAX_THERMAL_QUIET:
+ *profile = PLATFORM_PROFILE_QUIET;
+ break;
+ case WMAX_THERMAL_BALANCED:
+ *profile = PLATFORM_PROFILE_BALANCED;
+ break;
+ case WMAX_THERMAL_BALANCED_PERFORMANCE:
+ *profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE;
+ break;
+ case WMAX_THERMAL_PERFORMANCE:
+ case WMAX_THERMAL_GMODE:
+ *profile = PLATFORM_PROFILE_PERFORMANCE;
+ break;
+ default:
+ return -ENODATA;
+ }
+
+ return 0;
+}
+
+static int thermal_profile_set(struct platform_profile_handler *pprof,
+ enum platform_profile_option profile)
+{
+ acpi_status status;
+ u32 in_args;
+ u32 out_data;
+
+ switch (profile) {
+ case PLATFORM_PROFILE_LOW_POWER:
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_LOW_POWER);
+ break;
+ case PLATFORM_PROFILE_QUIET:
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET);
+ break;
+ case PLATFORM_PROFILE_BALANCED:
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED);
+ break;
+ case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE);
+ break;
+ case PLATFORM_PROFILE_PERFORMANCE:
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_PERFORMANCE);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ status = alienware_wmax_command(&in_args, sizeof(in_args),
+ WMAX_METHOD_THERMAL_CONTROL, &out_data);
+
+ if (ACPI_FAILURE(status))
+ return -EOPNOTSUPP;
+
+ if (out_data == 0xFFFFFFFF)
+ return -EBADRQC;
+
+ return 0;
+}
+
+static int gmode_thermal_profile_set(struct platform_profile_handler *pprof,
+ enum platform_profile_option profile)
+{
+ acpi_status status;
+ u32 in_args;
+ u32 out_data;
+
+ switch (profile) {
+ case PLATFORM_PROFILE_LOW_POWER:
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_LOW_POWER);
+ break;
+ case PLATFORM_PROFILE_QUIET:
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET);
+ break;
+ case PLATFORM_PROFILE_BALANCED:
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED);
+ break;
+ case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE);
+ break;
+ case PLATFORM_PROFILE_PERFORMANCE:
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_GMODE);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ status = alienware_wmax_command(&in_args, sizeof(in_args),
+ WMAX_METHOD_THERMAL_CONTROL, &out_data);
+
+ if (ACPI_FAILURE(status))
+ return -EOPNOTSUPP;
+
+ if (out_data == 0xFFFFFFFF)
+ return -EBADRQC;
+
+ return 0;
+}
+
+static int create_thermal_profile(void)
+{
+ pp_handler.profile_get = thermal_profile_get;
+
+ if (quirks->gmode > 0)
+ pp_handler.profile_set = gmode_thermal_profile_set;
+ else
+ pp_handler.profile_set = thermal_profile_set;
+
+ set_bit(PLATFORM_PROFILE_LOW_POWER, pp_handler.choices);
+ set_bit(PLATFORM_PROFILE_QUIET, pp_handler.choices);
+ set_bit(PLATFORM_PROFILE_BALANCED, pp_handler.choices);
+ set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, pp_handler.choices);
+ set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices);
+
+ return platform_profile_register(&pp_handler);
+}
+
+static void remove_thermal_profile(void)
+{
+ if (quirks->thermal > 0)
+ platform_profile_remove();
+}
+
static int __init alienware_wmi_init(void)
{
int ret;
@@ -807,6 +1011,12 @@ static int __init alienware_wmi_init(void)
goto fail_prep_deepsleep;
}
+ if (quirks->thermal > 0) {
+ ret = create_thermal_profile();
+ if (ret)
+ goto fail_prep_thermal_profile;
+ }
+
ret = alienware_zone_init(platform_device);
if (ret)
goto fail_prep_zones;
@@ -817,6 +1027,7 @@ static int __init alienware_wmi_init(void)
alienware_zone_exit(platform_device);
fail_prep_deepsleep:
fail_prep_amplifier:
+fail_prep_thermal_profile:
fail_prep_hdmi:
platform_device_del(platform_device);
fail_platform_device2:
@@ -834,6 +1045,7 @@ static void __exit alienware_wmi_exit(void)
if (platform_device) {
alienware_zone_exit(platform_device);
remove_hdmi(platform_device);
+ remove_thermal_profile();
platform_device_unregister(platform_device);
platform_driver_unregister(&platform_driver);
}
--
2.46.2
Sorry. Please ignore.
This patch adds platform_profile support for Dell devices which implement
User Selectable Thermal Tables (USTT) that are meant to be controlled by
Alienware Command Center (AWCC). These devices may include newer Alienware
M-Series, Alienware X-Series and Dell's G-Series. This patch, was tested
by me on an Alienware x15 R1.
It is suspected that Alienware Command Center manages thermal profiles
through the WMI interface, specifically through a device with identifier
\_SB_.AMW1.WMAX. This device was reverse engineered and the relevant
functionality is documented here [1]. This driver interacts with this
WMI device and thus is able to mimic AWCC's thermal profiles functionality
through the platform_profile API. In consequence the user would be able
to set and retrieve thermal profiles, which are just fan speed profiles.
This driver was heavily inspired on inspur_platform_profile, special
thanks.
Notes:
- Performance (FullSpeed) profile is a special profile which has it's own
entry in the Firmware Settings of the Alienware x15 R1. It also changes
the color of the F1 key. I suspect this behavior would be replicated in
other X-Series or M-Series laptops.
- G-Mode is a profile documented on [1] which mimics the behavior of
FullSpeed mode but it does not have an entry on the Firmware Settings of
the Alienware x15 R1, this may correspond to the G-Mode functionality on
G-Series laptops (activated by a special button) but I cannot test it. I
did not include this code in the driver as G-Mode causes unexpected
behavior on X-Series laptops.
Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
v3:
- Removed extra empty line
- 0x0B named WMAX_ARG_GET_CURRENT_PROF
- Removed casts to the same type on functions added in this patch
- Thermal profile to WMAX argument is now an static function and makes
use of in-built kernel macros
- Platform profile is now removed only if it was created first
- create_platform_profile is now create_thermal_profile to avoid
confusion
- profile_get and profile_set functions renamed too to match the above
v2:
- Moved functionality to alienware-wmi driver
- Added thermal and gmode quirks to add support based on dmi match
- Performance profile is now GMODE for devices that support it
- alienware_wmax_command now is insize agnostic to support new thermal
methods
---
drivers/platform/x86/dell/Kconfig | 1 +
drivers/platform/x86/dell/alienware-wmi.c | 238 ++++++++++++++++++++--
2 files changed, 226 insertions(+), 13 deletions(-)
diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig
index 68a49788a..b06d634cd 100644
--- a/drivers/platform/x86/dell/Kconfig
+++ b/drivers/platform/x86/dell/Kconfig
@@ -21,6 +21,7 @@ config ALIENWARE_WMI
depends on LEDS_CLASS
depends on NEW_LEDS
depends on ACPI_WMI
+ select ACPI_PLATFORM_PROFILE
help
This is a driver for controlling Alienware BIOS driven
features. It exposes an interface for controlling the AlienFX
diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
index f5ee62ce1..e3ef4b10b 100644
--- a/drivers/platform/x86/dell/alienware-wmi.c
+++ b/drivers/platform/x86/dell/alienware-wmi.c
@@ -10,6 +10,7 @@
#include <linux/acpi.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/platform_profile.h>
#include <linux/dmi.h>
#include <linux/leds.h>
@@ -25,6 +26,10 @@
#define WMAX_METHOD_AMPLIFIER_CABLE 0x6
#define WMAX_METHOD_DEEP_SLEEP_CONTROL 0x0B
#define WMAX_METHOD_DEEP_SLEEP_STATUS 0x0C
+#define WMAX_METHOD_THERMAL_INFORMATION 0x14
+#define WMAX_METHOD_THERMAL_CONTROL 0x15
+
+#define WMAX_ARG_GET_CURRENT_PROF 0x0B
MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
MODULE_DESCRIPTION("Alienware special feature control");
@@ -49,11 +54,22 @@ enum WMAX_CONTROL_STATES {
WMAX_SUSPEND = 3,
};
+enum WMAX_THERMAL_PROFILE {
+ WMAX_THERMAL_QUIET = 0xA3,
+ WMAX_THERMAL_BALANCED = 0xA0,
+ WMAX_THERMAL_BALANCED_PERFORMANCE = 0xA1,
+ WMAX_THERMAL_PERFORMANCE = 0xA4,
+ WMAX_THERMAL_GMODE = 0xAB,
+ WMAX_THERMAL_LOW_POWER = 0xA5,
+};
+
struct quirk_entry {
u8 num_zones;
u8 hdmi_mux;
u8 amplifier;
u8 deepslp;
+ u8 thermal;
+ u8 gmode;
};
static struct quirk_entry *quirks;
@@ -64,6 +80,8 @@ static struct quirk_entry quirk_inspiron5675 = {
.hdmi_mux = 0,
.amplifier = 0,
.deepslp = 0,
+ .thermal = 0,
+ .gmode = 0,
};
static struct quirk_entry quirk_unknown = {
@@ -71,6 +89,8 @@ static struct quirk_entry quirk_unknown = {
.hdmi_mux = 0,
.amplifier = 0,
.deepslp = 0,
+ .thermal = 0,
+ .gmode = 0,
};
static struct quirk_entry quirk_x51_r1_r2 = {
@@ -78,6 +98,8 @@ static struct quirk_entry quirk_x51_r1_r2 = {
.hdmi_mux = 0,
.amplifier = 0,
.deepslp = 0,
+ .thermal = 0,
+ .gmode = 0,
};
static struct quirk_entry quirk_x51_r3 = {
@@ -85,6 +107,8 @@ static struct quirk_entry quirk_x51_r3 = {
.hdmi_mux = 0,
.amplifier = 1,
.deepslp = 0,
+ .thermal = 0,
+ .gmode = 0,
};
static struct quirk_entry quirk_asm100 = {
@@ -92,6 +116,8 @@ static struct quirk_entry quirk_asm100 = {
.hdmi_mux = 1,
.amplifier = 0,
.deepslp = 0,
+ .thermal = 0,
+ .gmode = 0,
};
static struct quirk_entry quirk_asm200 = {
@@ -99,6 +125,8 @@ static struct quirk_entry quirk_asm200 = {
.hdmi_mux = 1,
.amplifier = 0,
.deepslp = 1,
+ .thermal = 0,
+ .gmode = 0,
};
static struct quirk_entry quirk_asm201 = {
@@ -106,6 +134,17 @@ static struct quirk_entry quirk_asm201 = {
.hdmi_mux = 1,
.amplifier = 1,
.deepslp = 1,
+ .thermal = 0,
+ .gmode = 0,
+};
+
+static struct quirk_entry quirk_x15_r1 = {
+ .num_zones = 2,
+ .hdmi_mux = 0,
+ .amplifier = 0,
+ .deepslp = 0,
+ .thermal = 1,
+ .gmode = 0,
};
static int __init dmi_matched(const struct dmi_system_id *dmi)
@@ -169,6 +208,15 @@ static const struct dmi_system_id alienware_quirks[] __initconst = {
},
.driver_data = &quirk_asm201,
},
+ {
+ .callback = dmi_matched,
+ .ident = "Alienware x15 R1",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R1")
+ },
+ .driver_data = &quirk_x15_r1,
+ },
{
.callback = dmi_matched,
.ident = "Dell Inc. Inspiron 5675",
@@ -218,6 +266,7 @@ static struct platform_device *platform_device;
static struct device_attribute *zone_dev_attrs;
static struct attribute **zone_attrs;
static struct platform_zone *zone_data;
+static struct platform_profile_handler pp_handler;
static struct platform_driver platform_driver = {
.driver = {
@@ -500,7 +549,7 @@ static void alienware_zone_exit(struct platform_device *dev)
kfree(zone_attrs);
}
-static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
+static acpi_status alienware_wmax_command(void *in_args, size_t insize,
u32 command, int *out_data)
{
acpi_status status;
@@ -508,7 +557,7 @@ static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
struct acpi_buffer input;
struct acpi_buffer output;
- input.length = (acpi_size) sizeof(*in_args);
+ input.length = (acpi_size) insize;
input.pointer = in_args;
if (out_data) {
output.length = ACPI_ALLOCATE_BUFFER;
@@ -541,8 +590,8 @@ static ssize_t show_hdmi_cable(struct device *dev,
.arg = 0,
};
status =
- alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE,
- (u32 *) &out_data);
+ alienware_wmax_command(&in_args, sizeof(in_args),
+ WMAX_METHOD_HDMI_CABLE, (u32 *) &out_data);
if (ACPI_SUCCESS(status)) {
if (out_data == 0)
return sysfs_emit(buf, "[unconnected] connected unknown\n");
@@ -562,8 +611,8 @@ static ssize_t show_hdmi_source(struct device *dev,
.arg = 0,
};
status =
- alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS,
- (u32 *) &out_data);
+ alienware_wmax_command(&in_args, sizeof(in_args),
+ WMAX_METHOD_HDMI_STATUS, (u32 *) &out_data);
if (ACPI_SUCCESS(status)) {
if (out_data == 1)
@@ -589,7 +638,8 @@ static ssize_t toggle_hdmi_source(struct device *dev,
args.arg = 3;
pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf);
- status = alienware_wmax_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL);
+ status = alienware_wmax_command(&args, sizeof(args),
+ WMAX_METHOD_HDMI_SOURCE, NULL);
if (ACPI_FAILURE(status))
pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
@@ -642,8 +692,8 @@ static ssize_t show_amplifier_status(struct device *dev,
.arg = 0,
};
status =
- alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE,
- (u32 *) &out_data);
+ alienware_wmax_command(&in_args, sizeof(in_args),
+ WMAX_METHOD_AMPLIFIER_CABLE, (u32 *) &out_data);
if (ACPI_SUCCESS(status)) {
if (out_data == 0)
return sysfs_emit(buf, "[unconnected] connected unknown\n");
@@ -694,8 +744,8 @@ static ssize_t show_deepsleep_status(struct device *dev,
struct wmax_basic_args in_args = {
.arg = 0,
};
- status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS,
- (u32 *) &out_data);
+ status = alienware_wmax_command(&in_args, sizeof(in_args),
+ WMAX_METHOD_DEEP_SLEEP_STATUS, (u32 *) &out_data);
if (ACPI_SUCCESS(status)) {
if (out_data == 0)
return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
@@ -723,8 +773,8 @@ static ssize_t toggle_deepsleep(struct device *dev,
args.arg = 2;
pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf);
- status = alienware_wmax_command(&args, WMAX_METHOD_DEEP_SLEEP_CONTROL,
- NULL);
+ status = alienware_wmax_command(&args, sizeof(args),
+ WMAX_METHOD_DEEP_SLEEP_CONTROL, NULL);
if (ACPI_FAILURE(status))
pr_err("alienware-wmi: deep sleep control failed: results: %u\n",
@@ -760,6 +810,160 @@ static int create_deepsleep(struct platform_device *dev)
return ret;
}
+/*
+ * Thermal Profile control
+ * - Provides thermal profile control through the Platform Profile API
+ */
+#define PROFILE_MASK GENMASK(15,8)
+#define PROFILE_ACTIVATE BIT(0)
+
+static u32 profile_to_wmax_arg(enum WMAX_THERMAL_PROFILE prof)
+{
+ return FIELD_PREP(PROFILE_MASK, prof) | PROFILE_ACTIVATE;
+}
+
+static int thermal_profile_get(struct platform_profile_handler *pprof,
+ enum platform_profile_option *profile)
+{
+ acpi_status status;
+ u32 in_args = WMAX_ARG_GET_CURRENT_PROF;
+ u32 out_data;
+
+ status = alienware_wmax_command(&in_args, sizeof(in_args),
+ WMAX_METHOD_THERMAL_INFORMATION, &out_data);
+
+ if (ACPI_FAILURE(status))
+ return -EOPNOTSUPP;
+
+ if (out_data == 0xFFFFFFFF)
+ return -EBADRQC;
+
+ switch (out_data) {
+ case WMAX_THERMAL_LOW_POWER:
+ *profile = PLATFORM_PROFILE_LOW_POWER;
+ break;
+ case WMAX_THERMAL_QUIET:
+ *profile = PLATFORM_PROFILE_QUIET;
+ break;
+ case WMAX_THERMAL_BALANCED:
+ *profile = PLATFORM_PROFILE_BALANCED;
+ break;
+ case WMAX_THERMAL_BALANCED_PERFORMANCE:
+ *profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE;
+ break;
+ case WMAX_THERMAL_PERFORMANCE:
+ case WMAX_THERMAL_GMODE:
+ *profile = PLATFORM_PROFILE_PERFORMANCE;
+ break;
+ default:
+ return -ENODATA;
+ }
+
+ return 0;
+}
+
+static int thermal_profile_set(struct platform_profile_handler *pprof,
+ enum platform_profile_option profile)
+{
+ acpi_status status;
+ u32 in_args;
+ u32 out_data;
+
+ switch (profile) {
+ case PLATFORM_PROFILE_LOW_POWER:
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_LOW_POWER);
+ break;
+ case PLATFORM_PROFILE_QUIET:
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET);
+ break;
+ case PLATFORM_PROFILE_BALANCED:
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED);
+ break;
+ case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE);
+ break;
+ case PLATFORM_PROFILE_PERFORMANCE:
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_PERFORMANCE);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ status = alienware_wmax_command(&in_args, sizeof(in_args),
+ WMAX_METHOD_THERMAL_CONTROL, &out_data);
+
+ if (ACPI_FAILURE(status))
+ return -EOPNOTSUPP;
+
+ if (out_data == 0xFFFFFFFF)
+ return -EBADRQC;
+
+ return 0;
+}
+
+static int gmode_thermal_profile_set(struct platform_profile_handler *pprof,
+ enum platform_profile_option profile)
+{
+ acpi_status status;
+ u32 in_args;
+ u32 out_data;
+
+ switch (profile) {
+ case PLATFORM_PROFILE_LOW_POWER:
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_LOW_POWER);
+ break;
+ case PLATFORM_PROFILE_QUIET:
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET);
+ break;
+ case PLATFORM_PROFILE_BALANCED:
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED);
+ break;
+ case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE);
+ break;
+ case PLATFORM_PROFILE_PERFORMANCE:
+ in_args = profile_to_wmax_arg(WMAX_THERMAL_GMODE);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ status = alienware_wmax_command(&in_args, sizeof(in_args),
+ WMAX_METHOD_THERMAL_CONTROL, &out_data);
+
+ if (ACPI_FAILURE(status))
+ return -EOPNOTSUPP;
+
+ if (out_data == 0xFFFFFFFF)
+ return -EBADRQC;
+
+ return 0;
+}
+
+static int create_thermal_profile(void)
+{
+ pp_handler.profile_get = thermal_profile_get;
+
+ if (quirks->gmode > 0)
+ pp_handler.profile_set = gmode_thermal_profile_set;
+ else
+ pp_handler.profile_set = thermal_profile_set;
+
+ set_bit(PLATFORM_PROFILE_LOW_POWER, pp_handler.choices);
+ set_bit(PLATFORM_PROFILE_QUIET, pp_handler.choices);
+ set_bit(PLATFORM_PROFILE_BALANCED, pp_handler.choices);
+ set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, pp_handler.choices);
+ set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices);
+
+ return platform_profile_register(&pp_handler);
+}
+
+static void remove_thermal_profile(void)
+{
+ if (quirks->thermal > 0)
+ platform_profile_remove();
+}
+
static int __init alienware_wmi_init(void)
{
int ret;
@@ -807,6 +1011,12 @@ static int __init alienware_wmi_init(void)
goto fail_prep_deepsleep;
}
+ if (quirks->thermal > 0) {
+ ret = create_thermal_profile();
+ if (ret)
+ goto fail_prep_thermal_profile;
+ }
+
ret = alienware_zone_init(platform_device);
if (ret)
goto fail_prep_zones;
@@ -817,6 +1027,7 @@ static int __init alienware_wmi_init(void)
alienware_zone_exit(platform_device);
fail_prep_deepsleep:
fail_prep_amplifier:
+fail_prep_thermal_profile:
fail_prep_hdmi:
platform_device_del(platform_device);
fail_platform_device2:
@@ -834,6 +1045,7 @@ static void __exit alienware_wmi_exit(void)
if (platform_device) {
alienware_zone_exit(platform_device);
remove_hdmi(platform_device);
+ remove_thermal_profile();
platform_device_unregister(platform_device);
platform_driver_unregister(&platform_driver);
}
--
2.46.2
Hi Kurt, kernel test robot noticed the following build errors: [auto build test ERROR on linus/master] [also build test ERROR on v6.12-rc3 next-20241014] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/Kurt-Borja/alienware-wmi-Dell-AWCC-platform_profile-support/20241011-184337 base: linus/master patch link: https://lore.kernel.org/r/20241008195642.36677-2-kuurtb%40gmail.com patch subject: [PATCH v3] alienware-wmi: Dell AWCC platform_profile support config: x86_64-buildonly-randconfig-004-20241015 (https://download.01.org/0day-ci/archive/20241015/202410150939.BgH8WpE3-lkp@intel.com/config) compiler: gcc-12 (Debian 12.2.0-14) 12.2.0 reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20241015/202410150939.BgH8WpE3-lkp@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Closes: https://lore.kernel.org/oe-kbuild-all/202410150939.BgH8WpE3-lkp@intel.com/ All errors (new ones prefixed by >>): drivers/platform/x86/dell/alienware-wmi.c: In function 'profile_to_wmax_arg': >> drivers/platform/x86/dell/alienware-wmi.c:822:16: error: implicit declaration of function 'FIELD_PREP' [-Werror=implicit-function-declaration] 822 | return FIELD_PREP(PROFILE_MASK, prof) | PROFILE_ACTIVATE; | ^~~~~~~~~~ cc1: some warnings being treated as errors vim +/FIELD_PREP +822 drivers/platform/x86/dell/alienware-wmi.c 819 820 static u32 profile_to_wmax_arg(enum WMAX_THERMAL_PROFILE prof) 821 { > 822 return FIELD_PREP(PROFILE_MASK, prof) | PROFILE_ACTIVATE; 823 } 824 -- 0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki
Hi Kurt, kernel test robot noticed the following build errors: [auto build test ERROR on linus/master] [also build test ERROR on v6.12-rc2 next-20241009] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/Kurt-Borja/alienware-wmi-Dell-AWCC-platform_profile-support/20241009-040025 base: linus/master patch link: https://lore.kernel.org/r/20241008195642.36677-2-kuurtb%40gmail.com patch subject: [PATCH v3] alienware-wmi: Dell AWCC platform_profile support config: i386-randconfig-r051-20241010 (https://download.01.org/0day-ci/archive/20241010/202410101120.w4OLAnaI-lkp@intel.com/config) compiler: clang version 18.1.8 (https://github.com/llvm/llvm-project 3b5b5c1ec4a3095ab096dd780e84d7ab81f3d7ff) reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20241010/202410101120.w4OLAnaI-lkp@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Closes: https://lore.kernel.org/oe-kbuild-all/202410101120.w4OLAnaI-lkp@intel.com/ All errors (new ones prefixed by >>): >> drivers/platform/x86/dell/alienware-wmi.c:822:9: error: call to undeclared function 'FIELD_PREP'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] 822 | return FIELD_PREP(PROFILE_MASK, prof) | PROFILE_ACTIVATE; | ^ 1 error generated. vim +/FIELD_PREP +822 drivers/platform/x86/dell/alienware-wmi.c 819 820 static u32 profile_to_wmax_arg(enum WMAX_THERMAL_PROFILE prof) 821 { > 822 return FIELD_PREP(PROFILE_MASK, prof) | PROFILE_ACTIVATE; 823 } 824 -- 0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki
On Thu, 10 Oct 2024, kernel test robot wrote: > Hi Kurt, > > kernel test robot noticed the following build errors: > > [auto build test ERROR on linus/master] > [also build test ERROR on v6.12-rc2 next-20241009] > [If your patch is applied to the wrong git tree, kindly drop us a note. > And when submitting patch, we suggest to use '--base' as documented in > https://git-scm.com/docs/git-format-patch#_base_tree_information] > > url: https://github.com/intel-lab-lkp/linux/commits/Kurt-Borja/alienware-wmi-Dell-AWCC-platform_profile-support/20241009-040025 > base: linus/master > patch link: https://lore.kernel.org/r/20241008195642.36677-2-kuurtb%40gmail.com > patch subject: [PATCH v3] alienware-wmi: Dell AWCC platform_profile support > config: i386-randconfig-r051-20241010 (https://download.01.org/0day-ci/archive/20241010/202410101120.w4OLAnaI-lkp@intel.com/config) > compiler: clang version 18.1.8 (https://github.com/llvm/llvm-project 3b5b5c1ec4a3095ab096dd780e84d7ab81f3d7ff) > reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20241010/202410101120.w4OLAnaI-lkp@intel.com/reproduce) > > If you fix the issue in a separate patch/commit (i.e. not just a new version of > the same patch/commit), kindly add following tags > | Reported-by: kernel test robot <lkp@intel.com> > | Closes: https://lore.kernel.org/oe-kbuild-all/202410101120.w4OLAnaI-lkp@intel.com/ > > All errors (new ones prefixed by >>): > > >> drivers/platform/x86/dell/alienware-wmi.c:822:9: error: call to undeclared function 'FIELD_PREP'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] > 822 | return FIELD_PREP(PROFILE_MASK, prof) | PROFILE_ACTIVATE; > | ^ > 1 error generated. > > > vim +/FIELD_PREP +822 drivers/platform/x86/dell/alienware-wmi.c > > 819 > 820 static u32 profile_to_wmax_arg(enum WMAX_THERMAL_PROFILE prof) > 821 { > > 822 return FIELD_PREP(PROFILE_MASK, prof) | PROFILE_ACTIVATE; > 823 } > 824 For the one time I forget to mention that please add the necessary headers, it immediately bites (that was in my mind at one point but in the end I forgot to add it). So please, add the headers both for FIELD_PREP() and GENMASK(). -- i.
Thank you! I noticed the kernel thanks to the kernel robot too. I will be sure to add them in v4. Kurt
On Tue, 8 Oct 2024, Kurt Borja wrote: > This patch adds platform_profile support for Dell devices which implement > User Selectable Thermal Tables (USTT) that are meant to be controlled by > Alienware Command Center (AWCC). These devices may include newer Alienware > M-Series, Alienware X-Series and Dell's G-Series. This patch, was tested > by me on an Alienware x15 R1. > > It is suspected that Alienware Command Center manages thermal profiles > through the WMI interface, specifically through a device with identifier > \_SB_.AMW1.WMAX. This device was reverse engineered and the relevant > functionality is documented here [1]. This driver interacts with this > WMI device and thus is able to mimic AWCC's thermal profiles functionality > through the platform_profile API. In consequence the user would be able > to set and retrieve thermal profiles, which are just fan speed profiles. > > This driver was heavily inspired on inspur_platform_profile, special > thanks. > > Notes: > - Performance (FullSpeed) profile is a special profile which has it's own > entry in the Firmware Settings of the Alienware x15 R1. It also changes > the color of the F1 key. I suspect this behavior would be replicated in > other X-Series or M-Series laptops. > - G-Mode is a profile documented on [1] which mimics the behavior of > FullSpeed mode but it does not have an entry on the Firmware Settings of > the Alienware x15 R1, this may correspond to the G-Mode functionality on > G-Series laptops (activated by a special button) but I cannot test it. I > did not include this code in the driver as G-Mode causes unexpected > behavior on X-Series laptops. > > Signed-off-by: Kurt Borja <kuurtb@gmail.com> > > --- > v3: > - Removed extra empty line > - 0x0B named WMAX_ARG_GET_CURRENT_PROF > - Removed casts to the same type on functions added in this patch > - Thermal profile to WMAX argument is now an static function and makes > use of in-built kernel macros > - Platform profile is now removed only if it was created first > - create_platform_profile is now create_thermal_profile to avoid > confusion > - profile_get and profile_set functions renamed too to match the above > v2: > - Moved functionality to alienware-wmi driver > - Added thermal and gmode quirks to add support based on dmi match > - Performance profile is now GMODE for devices that support it > - alienware_wmax_command now is insize agnostic to support new thermal > methods > --- > drivers/platform/x86/dell/Kconfig | 1 + > drivers/platform/x86/dell/alienware-wmi.c | 238 ++++++++++++++++++++-- > 2 files changed, 226 insertions(+), 13 deletions(-) > > diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig > index 68a49788a..b06d634cd 100644 > --- a/drivers/platform/x86/dell/Kconfig > +++ b/drivers/platform/x86/dell/Kconfig > @@ -21,6 +21,7 @@ config ALIENWARE_WMI > depends on LEDS_CLASS > depends on NEW_LEDS > depends on ACPI_WMI > + select ACPI_PLATFORM_PROFILE > help > This is a driver for controlling Alienware BIOS driven > features. It exposes an interface for controlling the AlienFX > diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c > index f5ee62ce1..e3ef4b10b 100644 > --- a/drivers/platform/x86/dell/alienware-wmi.c > +++ b/drivers/platform/x86/dell/alienware-wmi.c > @@ -10,6 +10,7 @@ > #include <linux/acpi.h> > #include <linux/module.h> > #include <linux/platform_device.h> > +#include <linux/platform_profile.h> > #include <linux/dmi.h> > #include <linux/leds.h> > > @@ -25,6 +26,10 @@ > #define WMAX_METHOD_AMPLIFIER_CABLE 0x6 > #define WMAX_METHOD_DEEP_SLEEP_CONTROL 0x0B > #define WMAX_METHOD_DEEP_SLEEP_STATUS 0x0C > +#define WMAX_METHOD_THERMAL_INFORMATION 0x14 > +#define WMAX_METHOD_THERMAL_CONTROL 0x15 > + > +#define WMAX_ARG_GET_CURRENT_PROF 0x0B > > MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>"); > MODULE_DESCRIPTION("Alienware special feature control"); > @@ -49,11 +54,22 @@ enum WMAX_CONTROL_STATES { > WMAX_SUSPEND = 3, > }; > > +enum WMAX_THERMAL_PROFILE { > + WMAX_THERMAL_QUIET = 0xA3, > + WMAX_THERMAL_BALANCED = 0xA0, > + WMAX_THERMAL_BALANCED_PERFORMANCE = 0xA1, > + WMAX_THERMAL_PERFORMANCE = 0xA4, > + WMAX_THERMAL_GMODE = 0xAB, > + WMAX_THERMAL_LOW_POWER = 0xA5, > +}; > + > struct quirk_entry { > u8 num_zones; > u8 hdmi_mux; > u8 amplifier; > u8 deepslp; > + u8 thermal; > + u8 gmode; > }; > > static struct quirk_entry *quirks; > @@ -64,6 +80,8 @@ static struct quirk_entry quirk_inspiron5675 = { > .hdmi_mux = 0, > .amplifier = 0, > .deepslp = 0, > + .thermal = 0, > + .gmode = 0, > }; > > static struct quirk_entry quirk_unknown = { > @@ -71,6 +89,8 @@ static struct quirk_entry quirk_unknown = { > .hdmi_mux = 0, > .amplifier = 0, > .deepslp = 0, > + .thermal = 0, > + .gmode = 0, > }; > > static struct quirk_entry quirk_x51_r1_r2 = { > @@ -78,6 +98,8 @@ static struct quirk_entry quirk_x51_r1_r2 = { > .hdmi_mux = 0, > .amplifier = 0, > .deepslp = 0, > + .thermal = 0, > + .gmode = 0, > }; > > static struct quirk_entry quirk_x51_r3 = { > @@ -85,6 +107,8 @@ static struct quirk_entry quirk_x51_r3 = { > .hdmi_mux = 0, > .amplifier = 1, > .deepslp = 0, > + .thermal = 0, > + .gmode = 0, > }; > > static struct quirk_entry quirk_asm100 = { > @@ -92,6 +116,8 @@ static struct quirk_entry quirk_asm100 = { > .hdmi_mux = 1, > .amplifier = 0, > .deepslp = 0, > + .thermal = 0, > + .gmode = 0, > }; > > static struct quirk_entry quirk_asm200 = { > @@ -99,6 +125,8 @@ static struct quirk_entry quirk_asm200 = { > .hdmi_mux = 1, > .amplifier = 0, > .deepslp = 1, > + .thermal = 0, > + .gmode = 0, > }; > > static struct quirk_entry quirk_asm201 = { > @@ -106,6 +134,17 @@ static struct quirk_entry quirk_asm201 = { > .hdmi_mux = 1, > .amplifier = 1, > .deepslp = 1, > + .thermal = 0, > + .gmode = 0, > +}; > + > +static struct quirk_entry quirk_x15_r1 = { > + .num_zones = 2, > + .hdmi_mux = 0, > + .amplifier = 0, > + .deepslp = 0, > + .thermal = 1, > + .gmode = 0, > }; > > static int __init dmi_matched(const struct dmi_system_id *dmi) > @@ -169,6 +208,15 @@ static const struct dmi_system_id alienware_quirks[] __initconst = { > }, > .driver_data = &quirk_asm201, > }, > + { > + .callback = dmi_matched, > + .ident = "Alienware x15 R1", > + .matches = { > + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R1") > + }, > + .driver_data = &quirk_x15_r1, > + }, > { > .callback = dmi_matched, > .ident = "Dell Inc. Inspiron 5675", > @@ -218,6 +266,7 @@ static struct platform_device *platform_device; > static struct device_attribute *zone_dev_attrs; > static struct attribute **zone_attrs; > static struct platform_zone *zone_data; > +static struct platform_profile_handler pp_handler; > > static struct platform_driver platform_driver = { > .driver = { > @@ -500,7 +549,7 @@ static void alienware_zone_exit(struct platform_device *dev) > kfree(zone_attrs); > } > > -static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args, > +static acpi_status alienware_wmax_command(void *in_args, size_t insize, > u32 command, int *out_data) > { > acpi_status status; > @@ -508,7 +557,7 @@ static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args, > struct acpi_buffer input; > struct acpi_buffer output; > > - input.length = (acpi_size) sizeof(*in_args); > + input.length = (acpi_size) insize; > input.pointer = in_args; > if (out_data) { > output.length = ACPI_ALLOCATE_BUFFER; > @@ -541,8 +590,8 @@ static ssize_t show_hdmi_cable(struct device *dev, > .arg = 0, > }; > status = > - alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE, > - (u32 *) &out_data); > + alienware_wmax_command(&in_args, sizeof(in_args), > + WMAX_METHOD_HDMI_CABLE, (u32 *) &out_data); > if (ACPI_SUCCESS(status)) { > if (out_data == 0) > return sysfs_emit(buf, "[unconnected] connected unknown\n"); > @@ -562,8 +611,8 @@ static ssize_t show_hdmi_source(struct device *dev, > .arg = 0, > }; > status = > - alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS, > - (u32 *) &out_data); > + alienware_wmax_command(&in_args, sizeof(in_args), > + WMAX_METHOD_HDMI_STATUS, (u32 *) &out_data); > > if (ACPI_SUCCESS(status)) { > if (out_data == 1) > @@ -589,7 +638,8 @@ static ssize_t toggle_hdmi_source(struct device *dev, > args.arg = 3; > pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf); > > - status = alienware_wmax_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL); > + status = alienware_wmax_command(&args, sizeof(args), > + WMAX_METHOD_HDMI_SOURCE, NULL); > > if (ACPI_FAILURE(status)) > pr_err("alienware-wmi: HDMI toggle failed: results: %u\n", > @@ -642,8 +692,8 @@ static ssize_t show_amplifier_status(struct device *dev, > .arg = 0, > }; > status = > - alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE, > - (u32 *) &out_data); > + alienware_wmax_command(&in_args, sizeof(in_args), > + WMAX_METHOD_AMPLIFIER_CABLE, (u32 *) &out_data); > if (ACPI_SUCCESS(status)) { > if (out_data == 0) > return sysfs_emit(buf, "[unconnected] connected unknown\n"); > @@ -694,8 +744,8 @@ static ssize_t show_deepsleep_status(struct device *dev, > struct wmax_basic_args in_args = { > .arg = 0, > }; > - status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS, > - (u32 *) &out_data); > + status = alienware_wmax_command(&in_args, sizeof(in_args), > + WMAX_METHOD_DEEP_SLEEP_STATUS, (u32 *) &out_data); > if (ACPI_SUCCESS(status)) { > if (out_data == 0) > return sysfs_emit(buf, "[disabled] s5 s5_s4\n"); > @@ -723,8 +773,8 @@ static ssize_t toggle_deepsleep(struct device *dev, > args.arg = 2; > pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf); > > - status = alienware_wmax_command(&args, WMAX_METHOD_DEEP_SLEEP_CONTROL, > - NULL); > + status = alienware_wmax_command(&args, sizeof(args), > + WMAX_METHOD_DEEP_SLEEP_CONTROL, NULL); > > if (ACPI_FAILURE(status)) > pr_err("alienware-wmi: deep sleep control failed: results: %u\n", > @@ -760,6 +810,160 @@ static int create_deepsleep(struct platform_device *dev) > return ret; > } > > +/* > + * Thermal Profile control > + * - Provides thermal profile control through the Platform Profile API > + */ > +#define PROFILE_MASK GENMASK(15,8) Space after comma. > +#define PROFILE_ACTIVATE BIT(0) Add driver specific prefix to the defines please. > + > +static u32 profile_to_wmax_arg(enum WMAX_THERMAL_PROFILE prof) > +{ > + return FIELD_PREP(PROFILE_MASK, prof) | PROFILE_ACTIVATE; > +} > + > +static int thermal_profile_get(struct platform_profile_handler *pprof, > + enum platform_profile_option *profile) > +{ > + acpi_status status; > + u32 in_args = WMAX_ARG_GET_CURRENT_PROF; > + u32 out_data; > + > + status = alienware_wmax_command(&in_args, sizeof(in_args), > + WMAX_METHOD_THERMAL_INFORMATION, &out_data); Are you sure you want to keep out_data as u32? I'm not very happy how alienware_wmax_command() takes int * but all callers seem to prefer u32 * (or pass NULL). Should the out_data parameter for alienware_wmax_command() be u32 * or int *? In general, if you find something that doesn't make sense, it often just is an indication that some cleanup is in order. We're more than happy to consider such patches along with the feature patches as then things are moving into the correct direction even if the progress would be slow. > + if (ACPI_FAILURE(status)) > + return -EOPNOTSUPP; > + > + if (out_data == 0xFFFFFFFF) This constant too should be named if the data really is u32 and not a negative error code in which case I'd be fine with < 0 (without naming the error code) like in the original but it would need int as the type for the compare to work. > + return -EBADRQC; > + > + switch (out_data) { > + case WMAX_THERMAL_LOW_POWER: > + *profile = PLATFORM_PROFILE_LOW_POWER; > + break; > + case WMAX_THERMAL_QUIET: > + *profile = PLATFORM_PROFILE_QUIET; > + break; > + case WMAX_THERMAL_BALANCED: > + *profile = PLATFORM_PROFILE_BALANCED; > + break; > + case WMAX_THERMAL_BALANCED_PERFORMANCE: > + *profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE; > + break; > + case WMAX_THERMAL_PERFORMANCE: > + case WMAX_THERMAL_GMODE: > + *profile = PLATFORM_PROFILE_PERFORMANCE; > + break; > + default: > + return -ENODATA; > + } > + > + return 0; > +} > + > +static int thermal_profile_set(struct platform_profile_handler *pprof, > + enum platform_profile_option profile) > +{ > + acpi_status status; > + u32 in_args; > + u32 out_data; > + > + switch (profile) { > + case PLATFORM_PROFILE_LOW_POWER: > + in_args = profile_to_wmax_arg(WMAX_THERMAL_LOW_POWER); > + break; > + case PLATFORM_PROFILE_QUIET: > + in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET); > + break; > + case PLATFORM_PROFILE_BALANCED: > + in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED); > + break; > + case PLATFORM_PROFILE_BALANCED_PERFORMANCE: > + in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE); > + break; > + case PLATFORM_PROFILE_PERFORMANCE: > + in_args = profile_to_wmax_arg(WMAX_THERMAL_PERFORMANCE); > + break; > + default: > + return -EOPNOTSUPP; > + } > + > + status = alienware_wmax_command(&in_args, sizeof(in_args), > + WMAX_METHOD_THERMAL_CONTROL, &out_data); > + > + if (ACPI_FAILURE(status)) > + return -EOPNOTSUPP; > + > + if (out_data == 0xFFFFFFFF) Ditto. > + return -EBADRQC; > + > + return 0; > +} > + > +static int gmode_thermal_profile_set(struct platform_profile_handler *pprof, > + enum platform_profile_option profile) > +{ > + acpi_status status; > + u32 in_args; > + u32 out_data; > + > + switch (profile) { > + case PLATFORM_PROFILE_LOW_POWER: > + in_args = profile_to_wmax_arg(WMAX_THERMAL_LOW_POWER); > + break; > + case PLATFORM_PROFILE_QUIET: > + in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET); > + break; > + case PLATFORM_PROFILE_BALANCED: > + in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED); > + break; > + case PLATFORM_PROFILE_BALANCED_PERFORMANCE: > + in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE); > + break; > + case PLATFORM_PROFILE_PERFORMANCE: > + in_args = profile_to_wmax_arg(WMAX_THERMAL_GMODE); > + break; > + default: > + return -EOPNOTSUPP; > + } > + > + status = alienware_wmax_command(&in_args, sizeof(in_args), > + WMAX_METHOD_THERMAL_CONTROL, &out_data); > + > + if (ACPI_FAILURE(status)) > + return -EOPNOTSUPP; > + > + if (out_data == 0xFFFFFFFF) Ditto. -- i. > + return -EBADRQC; > + > + return 0; > +} > + > +static int create_thermal_profile(void) > +{ > + pp_handler.profile_get = thermal_profile_get; > + > + if (quirks->gmode > 0) > + pp_handler.profile_set = gmode_thermal_profile_set; > + else > + pp_handler.profile_set = thermal_profile_set; > + > + set_bit(PLATFORM_PROFILE_LOW_POWER, pp_handler.choices); > + set_bit(PLATFORM_PROFILE_QUIET, pp_handler.choices); > + set_bit(PLATFORM_PROFILE_BALANCED, pp_handler.choices); > + set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, pp_handler.choices); > + set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices); > + > + return platform_profile_register(&pp_handler); > +} > + > +static void remove_thermal_profile(void) > +{ > + if (quirks->thermal > 0) > + platform_profile_remove(); > +} > + > static int __init alienware_wmi_init(void) > { > int ret; > @@ -807,6 +1011,12 @@ static int __init alienware_wmi_init(void) > goto fail_prep_deepsleep; > } > > + if (quirks->thermal > 0) { > + ret = create_thermal_profile(); > + if (ret) > + goto fail_prep_thermal_profile; > + } > + > ret = alienware_zone_init(platform_device); > if (ret) > goto fail_prep_zones; > @@ -817,6 +1027,7 @@ static int __init alienware_wmi_init(void) > alienware_zone_exit(platform_device); > fail_prep_deepsleep: > fail_prep_amplifier: > +fail_prep_thermal_profile: > fail_prep_hdmi: > platform_device_del(platform_device); > fail_platform_device2: > @@ -834,6 +1045,7 @@ static void __exit alienware_wmi_exit(void) > if (platform_device) { > alienware_zone_exit(platform_device); > remove_hdmi(platform_device); > + remove_thermal_profile(); > platform_device_unregister(platform_device); > platform_driver_unregister(&platform_driver); > } >
Ok. > > +#define PROFILE_ACTIVATE BIT(0) > > Add driver specific prefix to the defines please. > Ok. > > + > > +static u32 profile_to_wmax_arg(enum WMAX_THERMAL_PROFILE prof) > > +{ > > + return FIELD_PREP(PROFILE_MASK, prof) | PROFILE_ACTIVATE; > > +} > > + > > +static int thermal_profile_get(struct platform_profile_handler *pprof, > > + enum platform_profile_option *profile) > > +{ > > + acpi_status status; > > + u32 in_args = WMAX_ARG_GET_CURRENT_PROF; > > + u32 out_data; > > + > > + status = alienware_wmax_command(&in_args, sizeof(in_args), > > + WMAX_METHOD_THERMAL_INFORMATION, &out_data); > > Are you sure you want to keep out_data as u32? I'm not very happy how > alienware_wmax_command() takes int * but all callers seem to prefer u32 * > (or pass NULL). > I don't have the old WMAX interface but the new one specifies an [out] uint32 argument for all methods, I guess it's the same for the old one. > Should the out_data parameter for alienware_wmax_command() > be u32 * or int *? I'd say we go with u32 * for everything to resemble as much as possible the WMAX interface description. That way there won't be confusion in the future. I will name 0xFFFFFFFF appropriately. > In general, if you find something that doesn't make sense, it often just > is an indication that some cleanup is in order. We're more than happy to > consider such patches along with the feature patches as then things are > moving into the correct direction even if the progress would be slow. All right. I will keep it in mind. I will send a clean up patch together with the split Armin requested together with v4. > > + if (ACPI_FAILURE(status)) > > + return -EOPNOTSUPP; > > + > > + if (out_data == 0xFFFFFFFF) > > This constant too should be named if the data really is u32 and not a > negative error code in which case I'd be fine with < 0 (without naming > the error code) like in the original but it would need int as the type for > the compare to work. > > > + return -EBADRQC; > > + > > + switch (out_data) { > > + case WMAX_THERMAL_LOW_POWER: > > + *profile = PLATFORM_PROFILE_LOW_POWER; > > + break; > > + case WMAX_THERMAL_QUIET: > > + *profile = PLATFORM_PROFILE_QUIET; > > + break; > > + case WMAX_THERMAL_BALANCED: > > + *profile = PLATFORM_PROFILE_BALANCED; > > + break; > > + case WMAX_THERMAL_BALANCED_PERFORMANCE: > > + *profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE; > > + break; > > + case WMAX_THERMAL_PERFORMANCE: > > + case WMAX_THERMAL_GMODE: > > + *profile = PLATFORM_PROFILE_PERFORMANCE; > > + break; > > + default: > > + return -ENODATA; > > + } > > + > > + return 0; > > +} > > + > > +static int thermal_profile_set(struct platform_profile_handler *pprof, > > + enum platform_profile_option profile) > > +{ > > + acpi_status status; > > + u32 in_args; > > + u32 out_data; > > + > > + switch (profile) { > > + case PLATFORM_PROFILE_LOW_POWER: > > + in_args = profile_to_wmax_arg(WMAX_THERMAL_LOW_POWER); > > + break; > > + case PLATFORM_PROFILE_QUIET: > > + in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET); > > + break; > > + case PLATFORM_PROFILE_BALANCED: > > + in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED); > > + break; > > + case PLATFORM_PROFILE_BALANCED_PERFORMANCE: > > + in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE); > > + break; > > + case PLATFORM_PROFILE_PERFORMANCE: > > + in_args = profile_to_wmax_arg(WMAX_THERMAL_PERFORMANCE); > > + break; > > + default: > > + return -EOPNOTSUPP; > > + } > > + > > + status = alienware_wmax_command(&in_args, sizeof(in_args), > > + WMAX_METHOD_THERMAL_CONTROL, &out_data); > > + > > + if (ACPI_FAILURE(status)) > > + return -EOPNOTSUPP; > > + > > + if (out_data == 0xFFFFFFFF) > > Ditto. > > > + return -EBADRQC; > > + > > + return 0; > > +} > > + > > +static int gmode_thermal_profile_set(struct platform_profile_handler *pprof, > > + enum platform_profile_option profile) > > +{ > > + acpi_status status; > > + u32 in_args; > > + u32 out_data; > > + > > + switch (profile) { > > + case PLATFORM_PROFILE_LOW_POWER: > > + in_args = profile_to_wmax_arg(WMAX_THERMAL_LOW_POWER); > > + break; > > + case PLATFORM_PROFILE_QUIET: > > + in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET); > > + break; > > + case PLATFORM_PROFILE_BALANCED: > > + in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED); > > + break; > > + case PLATFORM_PROFILE_BALANCED_PERFORMANCE: > > + in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE); > > + break; > > + case PLATFORM_PROFILE_PERFORMANCE: > > + in_args = profile_to_wmax_arg(WMAX_THERMAL_GMODE); > > + break; > > + default: > > + return -EOPNOTSUPP; > > + } > > + > > + status = alienware_wmax_command(&in_args, sizeof(in_args), > > + WMAX_METHOD_THERMAL_CONTROL, &out_data); > > + > > + if (ACPI_FAILURE(status)) > > + return -EOPNOTSUPP; > > + > > + if (out_data == 0xFFFFFFFF) > > Ditto. > > -- > i. > Thank you for your feedback! Kurt > > + return -EBADRQC; > > + > > + return 0; > > +} > > + > > +static int create_thermal_profile(void) > > +{ > > + pp_handler.profile_get = thermal_profile_get; > > + > > + if (quirks->gmode > 0) > > + pp_handler.profile_set = gmode_thermal_profile_set; > > + else > > + pp_handler.profile_set = thermal_profile_set; > > + > > + set_bit(PLATFORM_PROFILE_LOW_POWER, pp_handler.choices); > > + set_bit(PLATFORM_PROFILE_QUIET, pp_handler.choices); > > + set_bit(PLATFORM_PROFILE_BALANCED, pp_handler.choices); > > + set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, pp_handler.choices); > > + set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices); > > + > > + return platform_profile_register(&pp_handler); > > +} > > + > > +static void remove_thermal_profile(void) > > +{ > > + if (quirks->thermal > 0) > > + platform_profile_remove(); > > +} > > + > > static int __init alienware_wmi_init(void) > > { > > int ret; > > @@ -807,6 +1011,12 @@ static int __init alienware_wmi_init(void) > > goto fail_prep_deepsleep; > > } > > > > + if (quirks->thermal > 0) { > > + ret = create_thermal_profile(); > > + if (ret) > > + goto fail_prep_thermal_profile; > > + } > > + > > ret = alienware_zone_init(platform_device); > > if (ret) > > goto fail_prep_zones; > > @@ -817,6 +1027,7 @@ static int __init alienware_wmi_init(void) > > alienware_zone_exit(platform_device); > > fail_prep_deepsleep: > > fail_prep_amplifier: > > +fail_prep_thermal_profile: > > fail_prep_hdmi: > > platform_device_del(platform_device); > > fail_platform_device2: > > @@ -834,6 +1045,7 @@ static void __exit alienware_wmi_exit(void) > > if (platform_device) { > > alienware_zone_exit(platform_device); > > remove_hdmi(platform_device); > > + remove_thermal_profile(); > > platform_device_unregister(platform_device); > > platform_driver_unregister(&platform_driver); > > } > >
Am 08.10.24 um 21:56 schrieb Kurt Borja: > This patch adds platform_profile support for Dell devices which implement > User Selectable Thermal Tables (USTT) that are meant to be controlled by > Alienware Command Center (AWCC). These devices may include newer Alienware > M-Series, Alienware X-Series and Dell's G-Series. This patch, was tested > by me on an Alienware x15 R1. > > It is suspected that Alienware Command Center manages thermal profiles > through the WMI interface, specifically through a device with identifier > \_SB_.AMW1.WMAX. This device was reverse engineered and the relevant > functionality is documented here [1]. This driver interacts with this > WMI device and thus is able to mimic AWCC's thermal profiles functionality > through the platform_profile API. In consequence the user would be able > to set and retrieve thermal profiles, which are just fan speed profiles. Can you write a short piece of documentation at Documentation/wmi/devices/ to describe the Alienware WMI interface? This would be helpful for future developers. > This driver was heavily inspired on inspur_platform_profile, special > thanks. > > Notes: > - Performance (FullSpeed) profile is a special profile which has it's own > entry in the Firmware Settings of the Alienware x15 R1. It also changes > the color of the F1 key. I suspect this behavior would be replicated in > other X-Series or M-Series laptops. > - G-Mode is a profile documented on [1] which mimics the behavior of > FullSpeed mode but it does not have an entry on the Firmware Settings of > the Alienware x15 R1, this may correspond to the G-Mode functionality on > G-Series laptops (activated by a special button) but I cannot test it. I > did not include this code in the driver as G-Mode causes unexpected > behavior on X-Series laptops. > > Signed-off-by: Kurt Borja <kuurtb@gmail.com> > > --- > v3: > - Removed extra empty line > - 0x0B named WMAX_ARG_GET_CURRENT_PROF > - Removed casts to the same type on functions added in this patch > - Thermal profile to WMAX argument is now an static function and makes > use of in-built kernel macros > - Platform profile is now removed only if it was created first > - create_platform_profile is now create_thermal_profile to avoid > confusion > - profile_get and profile_set functions renamed too to match the above > v2: > - Moved functionality to alienware-wmi driver > - Added thermal and gmode quirks to add support based on dmi match > - Performance profile is now GMODE for devices that support it > - alienware_wmax_command now is insize agnostic to support new thermal > methods > --- > drivers/platform/x86/dell/Kconfig | 1 + > drivers/platform/x86/dell/alienware-wmi.c | 238 ++++++++++++++++++++-- > 2 files changed, 226 insertions(+), 13 deletions(-) > > diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig > index 68a49788a..b06d634cd 100644 > --- a/drivers/platform/x86/dell/Kconfig > +++ b/drivers/platform/x86/dell/Kconfig > @@ -21,6 +21,7 @@ config ALIENWARE_WMI > depends on LEDS_CLASS > depends on NEW_LEDS > depends on ACPI_WMI > + select ACPI_PLATFORM_PROFILE > help > This is a driver for controlling Alienware BIOS driven > features. It exposes an interface for controlling the AlienFX > diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c > index f5ee62ce1..e3ef4b10b 100644 > --- a/drivers/platform/x86/dell/alienware-wmi.c > +++ b/drivers/platform/x86/dell/alienware-wmi.c > @@ -10,6 +10,7 @@ > #include <linux/acpi.h> > #include <linux/module.h> > #include <linux/platform_device.h> > +#include <linux/platform_profile.h> > #include <linux/dmi.h> > #include <linux/leds.h> > > @@ -25,6 +26,10 @@ > #define WMAX_METHOD_AMPLIFIER_CABLE 0x6 > #define WMAX_METHOD_DEEP_SLEEP_CONTROL 0x0B > #define WMAX_METHOD_DEEP_SLEEP_STATUS 0x0C > +#define WMAX_METHOD_THERMAL_INFORMATION 0x14 > +#define WMAX_METHOD_THERMAL_CONTROL 0x15 > + > +#define WMAX_ARG_GET_CURRENT_PROF 0x0B > > MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>"); > MODULE_DESCRIPTION("Alienware special feature control"); > @@ -49,11 +54,22 @@ enum WMAX_CONTROL_STATES { > WMAX_SUSPEND = 3, > }; > > +enum WMAX_THERMAL_PROFILE { > + WMAX_THERMAL_QUIET = 0xA3, > + WMAX_THERMAL_BALANCED = 0xA0, > + WMAX_THERMAL_BALANCED_PERFORMANCE = 0xA1, > + WMAX_THERMAL_PERFORMANCE = 0xA4, > + WMAX_THERMAL_GMODE = 0xAB, > + WMAX_THERMAL_LOW_POWER = 0xA5, > +}; > + > struct quirk_entry { > u8 num_zones; > u8 hdmi_mux; > u8 amplifier; > u8 deepslp; > + u8 thermal; > + u8 gmode; > }; > > static struct quirk_entry *quirks; > @@ -64,6 +80,8 @@ static struct quirk_entry quirk_inspiron5675 = { > .hdmi_mux = 0, > .amplifier = 0, > .deepslp = 0, > + .thermal = 0, > + .gmode = 0, > }; > > static struct quirk_entry quirk_unknown = { > @@ -71,6 +89,8 @@ static struct quirk_entry quirk_unknown = { > .hdmi_mux = 0, > .amplifier = 0, > .deepslp = 0, > + .thermal = 0, > + .gmode = 0, > }; > > static struct quirk_entry quirk_x51_r1_r2 = { > @@ -78,6 +98,8 @@ static struct quirk_entry quirk_x51_r1_r2 = { > .hdmi_mux = 0, > .amplifier = 0, > .deepslp = 0, > + .thermal = 0, > + .gmode = 0, > }; > > static struct quirk_entry quirk_x51_r3 = { > @@ -85,6 +107,8 @@ static struct quirk_entry quirk_x51_r3 = { > .hdmi_mux = 0, > .amplifier = 1, > .deepslp = 0, > + .thermal = 0, > + .gmode = 0, > }; > > static struct quirk_entry quirk_asm100 = { > @@ -92,6 +116,8 @@ static struct quirk_entry quirk_asm100 = { > .hdmi_mux = 1, > .amplifier = 0, > .deepslp = 0, > + .thermal = 0, > + .gmode = 0, > }; > > static struct quirk_entry quirk_asm200 = { > @@ -99,6 +125,8 @@ static struct quirk_entry quirk_asm200 = { > .hdmi_mux = 1, > .amplifier = 0, > .deepslp = 1, > + .thermal = 0, > + .gmode = 0, > }; > > static struct quirk_entry quirk_asm201 = { > @@ -106,6 +134,17 @@ static struct quirk_entry quirk_asm201 = { > .hdmi_mux = 1, > .amplifier = 1, > .deepslp = 1, > + .thermal = 0, > + .gmode = 0, > +}; > + > +static struct quirk_entry quirk_x15_r1 = { > + .num_zones = 2, > + .hdmi_mux = 0, > + .amplifier = 0, > + .deepslp = 0, > + .thermal = 1, > + .gmode = 0, > }; > > static int __init dmi_matched(const struct dmi_system_id *dmi) > @@ -169,6 +208,15 @@ static const struct dmi_system_id alienware_quirks[] __initconst = { > }, > .driver_data = &quirk_asm201, > }, > + { > + .callback = dmi_matched, > + .ident = "Alienware x15 R1", > + .matches = { > + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R1") > + }, > + .driver_data = &quirk_x15_r1, > + }, > { > .callback = dmi_matched, > .ident = "Dell Inc. Inspiron 5675", > @@ -218,6 +266,7 @@ static struct platform_device *platform_device; > static struct device_attribute *zone_dev_attrs; > static struct attribute **zone_attrs; > static struct platform_zone *zone_data; > +static struct platform_profile_handler pp_handler; > > static struct platform_driver platform_driver = { > .driver = { > @@ -500,7 +549,7 @@ static void alienware_zone_exit(struct platform_device *dev) > kfree(zone_attrs); > } > > -static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args, > +static acpi_status alienware_wmax_command(void *in_args, size_t insize, > u32 command, int *out_data) > { Can you split this change into a separate patch? This would make review a bit easier. > acpi_status status; > @@ -508,7 +557,7 @@ static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args, > struct acpi_buffer input; > struct acpi_buffer output; > > - input.length = (acpi_size) sizeof(*in_args); > + input.length = (acpi_size) insize; Please drop the cast to acpi_size. > input.pointer = in_args; > if (out_data) { > output.length = ACPI_ALLOCATE_BUFFER; > @@ -541,8 +590,8 @@ static ssize_t show_hdmi_cable(struct device *dev, > .arg = 0, > }; > status = > - alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE, > - (u32 *) &out_data); > + alienware_wmax_command(&in_args, sizeof(in_args), > + WMAX_METHOD_HDMI_CABLE, (u32 *) &out_data); > if (ACPI_SUCCESS(status)) { > if (out_data == 0) > return sysfs_emit(buf, "[unconnected] connected unknown\n"); > @@ -562,8 +611,8 @@ static ssize_t show_hdmi_source(struct device *dev, > .arg = 0, > }; > status = > - alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS, > - (u32 *) &out_data); > + alienware_wmax_command(&in_args, sizeof(in_args), > + WMAX_METHOD_HDMI_STATUS, (u32 *) &out_data); > > if (ACPI_SUCCESS(status)) { > if (out_data == 1) > @@ -589,7 +638,8 @@ static ssize_t toggle_hdmi_source(struct device *dev, > args.arg = 3; > pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf); > > - status = alienware_wmax_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL); > + status = alienware_wmax_command(&args, sizeof(args), > + WMAX_METHOD_HDMI_SOURCE, NULL); > > if (ACPI_FAILURE(status)) > pr_err("alienware-wmi: HDMI toggle failed: results: %u\n", > @@ -642,8 +692,8 @@ static ssize_t show_amplifier_status(struct device *dev, > .arg = 0, > }; > status = > - alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE, > - (u32 *) &out_data); > + alienware_wmax_command(&in_args, sizeof(in_args), > + WMAX_METHOD_AMPLIFIER_CABLE, (u32 *) &out_data); > if (ACPI_SUCCESS(status)) { > if (out_data == 0) > return sysfs_emit(buf, "[unconnected] connected unknown\n"); > @@ -694,8 +744,8 @@ static ssize_t show_deepsleep_status(struct device *dev, > struct wmax_basic_args in_args = { > .arg = 0, > }; > - status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS, > - (u32 *) &out_data); > + status = alienware_wmax_command(&in_args, sizeof(in_args), > + WMAX_METHOD_DEEP_SLEEP_STATUS, (u32 *) &out_data); > if (ACPI_SUCCESS(status)) { > if (out_data == 0) > return sysfs_emit(buf, "[disabled] s5 s5_s4\n"); > @@ -723,8 +773,8 @@ static ssize_t toggle_deepsleep(struct device *dev, > args.arg = 2; > pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf); > > - status = alienware_wmax_command(&args, WMAX_METHOD_DEEP_SLEEP_CONTROL, > - NULL); > + status = alienware_wmax_command(&args, sizeof(args), > + WMAX_METHOD_DEEP_SLEEP_CONTROL, NULL); > > if (ACPI_FAILURE(status)) > pr_err("alienware-wmi: deep sleep control failed: results: %u\n", > @@ -760,6 +810,160 @@ static int create_deepsleep(struct platform_device *dev) > return ret; > } > > +/* > + * Thermal Profile control > + * - Provides thermal profile control through the Platform Profile API > + */ > +#define PROFILE_MASK GENMASK(15,8) > +#define PROFILE_ACTIVATE BIT(0) > + > +static u32 profile_to_wmax_arg(enum WMAX_THERMAL_PROFILE prof) > +{ > + return FIELD_PREP(PROFILE_MASK, prof) | PROFILE_ACTIVATE; > +} > + > +static int thermal_profile_get(struct platform_profile_handler *pprof, > + enum platform_profile_option *profile) > +{ > + acpi_status status; > + u32 in_args = WMAX_ARG_GET_CURRENT_PROF; > + u32 out_data; > + > + status = alienware_wmax_command(&in_args, sizeof(in_args), > + WMAX_METHOD_THERMAL_INFORMATION, &out_data); > + > + if (ACPI_FAILURE(status)) > + return -EOPNOTSUPP; Please return -EIO. > + > + if (out_data == 0xFFFFFFFF) > + return -EBADRQC; > + > + switch (out_data) { > + case WMAX_THERMAL_LOW_POWER: > + *profile = PLATFORM_PROFILE_LOW_POWER; > + break; > + case WMAX_THERMAL_QUIET: > + *profile = PLATFORM_PROFILE_QUIET; > + break; > + case WMAX_THERMAL_BALANCED: > + *profile = PLATFORM_PROFILE_BALANCED; > + break; > + case WMAX_THERMAL_BALANCED_PERFORMANCE: > + *profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE; > + break; > + case WMAX_THERMAL_PERFORMANCE: > + case WMAX_THERMAL_GMODE: > + *profile = PLATFORM_PROFILE_PERFORMANCE; > + break; > + default: > + return -ENODATA; > + } > + > + return 0; > +} > + > +static int thermal_profile_set(struct platform_profile_handler *pprof, > + enum platform_profile_option profile) > +{ > + acpi_status status; > + u32 in_args; > + u32 out_data; > + > + switch (profile) { > + case PLATFORM_PROFILE_LOW_POWER: > + in_args = profile_to_wmax_arg(WMAX_THERMAL_LOW_POWER); > + break; > + case PLATFORM_PROFILE_QUIET: > + in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET); > + break; > + case PLATFORM_PROFILE_BALANCED: > + in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED); > + break; > + case PLATFORM_PROFILE_BALANCED_PERFORMANCE: > + in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE); > + break; > + case PLATFORM_PROFILE_PERFORMANCE: > + in_args = profile_to_wmax_arg(WMAX_THERMAL_PERFORMANCE); > + break; > + default: > + return -EOPNOTSUPP; > + } > + > + status = alienware_wmax_command(&in_args, sizeof(in_args), > + WMAX_METHOD_THERMAL_CONTROL, &out_data); > + > + if (ACPI_FAILURE(status)) > + return -EOPNOTSUPP; Return -EIO. > + > + if (out_data == 0xFFFFFFFF) > + return -EBADRQC; > + > + return 0; > +} > + > +static int gmode_thermal_profile_set(struct platform_profile_handler *pprof, > + enum platform_profile_option profile) > +{ > + acpi_status status; > + u32 in_args; > + u32 out_data; > + > + switch (profile) { > + case PLATFORM_PROFILE_LOW_POWER: > + in_args = profile_to_wmax_arg(WMAX_THERMAL_LOW_POWER); > + break; > + case PLATFORM_PROFILE_QUIET: > + in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET); > + break; > + case PLATFORM_PROFILE_BALANCED: > + in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED); > + break; > + case PLATFORM_PROFILE_BALANCED_PERFORMANCE: > + in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE); > + break; > + case PLATFORM_PROFILE_PERFORMANCE: > + in_args = profile_to_wmax_arg(WMAX_THERMAL_GMODE); > + break; > + default: > + return -EOPNOTSUPP; > + } > + > + status = alienware_wmax_command(&in_args, sizeof(in_args), > + WMAX_METHOD_THERMAL_CONTROL, &out_data); > + > + if (ACPI_FAILURE(status)) > + return -EOPNOTSUPP; Return -EIO. Otherwise the patch looks quite good. Thanks, Armin Wolf > + > + if (out_data == 0xFFFFFFFF) > + return -EBADRQC; > + > + return 0; > +} > + > +static int create_thermal_profile(void) > +{ > + pp_handler.profile_get = thermal_profile_get; > + > + if (quirks->gmode > 0) > + pp_handler.profile_set = gmode_thermal_profile_set; > + else > + pp_handler.profile_set = thermal_profile_set; > + > + set_bit(PLATFORM_PROFILE_LOW_POWER, pp_handler.choices); > + set_bit(PLATFORM_PROFILE_QUIET, pp_handler.choices); > + set_bit(PLATFORM_PROFILE_BALANCED, pp_handler.choices); > + set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, pp_handler.choices); > + set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices); > + > + return platform_profile_register(&pp_handler); > +} > + > +static void remove_thermal_profile(void) > +{ > + if (quirks->thermal > 0) > + platform_profile_remove(); > +} > + > static int __init alienware_wmi_init(void) > { > int ret; > @@ -807,6 +1011,12 @@ static int __init alienware_wmi_init(void) > goto fail_prep_deepsleep; > } > > + if (quirks->thermal > 0) { > + ret = create_thermal_profile(); > + if (ret) > + goto fail_prep_thermal_profile; > + } > + > ret = alienware_zone_init(platform_device); > if (ret) > goto fail_prep_zones; > @@ -817,6 +1027,7 @@ static int __init alienware_wmi_init(void) > alienware_zone_exit(platform_device); > fail_prep_deepsleep: > fail_prep_amplifier: > +fail_prep_thermal_profile: > fail_prep_hdmi: > platform_device_del(platform_device); > fail_platform_device2: > @@ -834,6 +1045,7 @@ static void __exit alienware_wmi_exit(void) > if (platform_device) { > alienware_zone_exit(platform_device); > remove_hdmi(platform_device); > + remove_thermal_profile(); > platform_device_unregister(platform_device); > platform_driver_unregister(&platform_driver); > }
Yes, of course. > > This driver was heavily inspired on inspur_platform_profile, special > > thanks. > > > > Notes: > > - Performance (FullSpeed) profile is a special profile which has it's own > > entry in the Firmware Settings of the Alienware x15 R1. It also changes > > the color of the F1 key. I suspect this behavior would be replicated in > > other X-Series or M-Series laptops. > > - G-Mode is a profile documented on [1] which mimics the behavior of > > FullSpeed mode but it does not have an entry on the Firmware Settings of > > the Alienware x15 R1, this may correspond to the G-Mode functionality on > > G-Series laptops (activated by a special button) but I cannot test it. I > > did not include this code in the driver as G-Mode causes unexpected > > behavior on X-Series laptops. > > > > Signed-off-by: Kurt Borja <kuurtb@gmail.com> > > > > --- > > v3: > > - Removed extra empty line > > - 0x0B named WMAX_ARG_GET_CURRENT_PROF > > - Removed casts to the same type on functions added in this patch > > - Thermal profile to WMAX argument is now an static function and makes > > use of in-built kernel macros > > - Platform profile is now removed only if it was created first > > - create_platform_profile is now create_thermal_profile to avoid > > confusion > > - profile_get and profile_set functions renamed too to match the above > > v2: > > - Moved functionality to alienware-wmi driver > > - Added thermal and gmode quirks to add support based on dmi match > > - Performance profile is now GMODE for devices that support it > > - alienware_wmax_command now is insize agnostic to support new thermal > > methods > > --- > > drivers/platform/x86/dell/Kconfig | 1 + > > drivers/platform/x86/dell/alienware-wmi.c | 238 ++++++++++++++++++++-- > > 2 files changed, 226 insertions(+), 13 deletions(-) > > > > diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig > > index 68a49788a..b06d634cd 100644 > > --- a/drivers/platform/x86/dell/Kconfig > > +++ b/drivers/platform/x86/dell/Kconfig > > @@ -21,6 +21,7 @@ config ALIENWARE_WMI > > depends on LEDS_CLASS > > depends on NEW_LEDS > > depends on ACPI_WMI > > + select ACPI_PLATFORM_PROFILE > > help > > This is a driver for controlling Alienware BIOS driven > > features. It exposes an interface for controlling the AlienFX > > diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c > > index f5ee62ce1..e3ef4b10b 100644 > > --- a/drivers/platform/x86/dell/alienware-wmi.c > > +++ b/drivers/platform/x86/dell/alienware-wmi.c > > @@ -10,6 +10,7 @@ > > #include <linux/acpi.h> > > #include <linux/module.h> > > #include <linux/platform_device.h> > > +#include <linux/platform_profile.h> > > #include <linux/dmi.h> > > #include <linux/leds.h> > > > > @@ -25,6 +26,10 @@ > > #define WMAX_METHOD_AMPLIFIER_CABLE 0x6 > > #define WMAX_METHOD_DEEP_SLEEP_CONTROL 0x0B > > #define WMAX_METHOD_DEEP_SLEEP_STATUS 0x0C > > +#define WMAX_METHOD_THERMAL_INFORMATION 0x14 > > +#define WMAX_METHOD_THERMAL_CONTROL 0x15 > > + > > +#define WMAX_ARG_GET_CURRENT_PROF 0x0B > > > > MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>"); > > MODULE_DESCRIPTION("Alienware special feature control"); > > @@ -49,11 +54,22 @@ enum WMAX_CONTROL_STATES { > > WMAX_SUSPEND = 3, > > }; > > > > +enum WMAX_THERMAL_PROFILE { > > + WMAX_THERMAL_QUIET = 0xA3, > > + WMAX_THERMAL_BALANCED = 0xA0, > > + WMAX_THERMAL_BALANCED_PERFORMANCE = 0xA1, > > + WMAX_THERMAL_PERFORMANCE = 0xA4, > > + WMAX_THERMAL_GMODE = 0xAB, > > + WMAX_THERMAL_LOW_POWER = 0xA5, > > +}; > > + > > struct quirk_entry { > > u8 num_zones; > > u8 hdmi_mux; > > u8 amplifier; > > u8 deepslp; > > + u8 thermal; > > + u8 gmode; > > }; > > > > static struct quirk_entry *quirks; > > @@ -64,6 +80,8 @@ static struct quirk_entry quirk_inspiron5675 = { > > .hdmi_mux = 0, > > .amplifier = 0, > > .deepslp = 0, > > + .thermal = 0, > > + .gmode = 0, > > }; > > > > static struct quirk_entry quirk_unknown = { > > @@ -71,6 +89,8 @@ static struct quirk_entry quirk_unknown = { > > .hdmi_mux = 0, > > .amplifier = 0, > > .deepslp = 0, > > + .thermal = 0, > > + .gmode = 0, > > }; > > > > static struct quirk_entry quirk_x51_r1_r2 = { > > @@ -78,6 +98,8 @@ static struct quirk_entry quirk_x51_r1_r2 = { > > .hdmi_mux = 0, > > .amplifier = 0, > > .deepslp = 0, > > + .thermal = 0, > > + .gmode = 0, > > }; > > > > static struct quirk_entry quirk_x51_r3 = { > > @@ -85,6 +107,8 @@ static struct quirk_entry quirk_x51_r3 = { > > .hdmi_mux = 0, > > .amplifier = 1, > > .deepslp = 0, > > + .thermal = 0, > > + .gmode = 0, > > }; > > > > static struct quirk_entry quirk_asm100 = { > > @@ -92,6 +116,8 @@ static struct quirk_entry quirk_asm100 = { > > .hdmi_mux = 1, > > .amplifier = 0, > > .deepslp = 0, > > + .thermal = 0, > > + .gmode = 0, > > }; > > > > static struct quirk_entry quirk_asm200 = { > > @@ -99,6 +125,8 @@ static struct quirk_entry quirk_asm200 = { > > .hdmi_mux = 1, > > .amplifier = 0, > > .deepslp = 1, > > + .thermal = 0, > > + .gmode = 0, > > }; > > > > static struct quirk_entry quirk_asm201 = { > > @@ -106,6 +134,17 @@ static struct quirk_entry quirk_asm201 = { > > .hdmi_mux = 1, > > .amplifier = 1, > > .deepslp = 1, > > + .thermal = 0, > > + .gmode = 0, > > +}; > > + > > +static struct quirk_entry quirk_x15_r1 = { > > + .num_zones = 2, > > + .hdmi_mux = 0, > > + .amplifier = 0, > > + .deepslp = 0, > > + .thermal = 1, > > + .gmode = 0, > > }; > > > > static int __init dmi_matched(const struct dmi_system_id *dmi) > > @@ -169,6 +208,15 @@ static const struct dmi_system_id alienware_quirks[] __initconst = { > > }, > > .driver_data = &quirk_asm201, > > }, > > + { > > + .callback = dmi_matched, > > + .ident = "Alienware x15 R1", > > + .matches = { > > + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > > + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R1") > > + }, > > + .driver_data = &quirk_x15_r1, > > + }, > > { > > .callback = dmi_matched, > > .ident = "Dell Inc. Inspiron 5675", > > @@ -218,6 +266,7 @@ static struct platform_device *platform_device; > > static struct device_attribute *zone_dev_attrs; > > static struct attribute **zone_attrs; > > static struct platform_zone *zone_data; > > +static struct platform_profile_handler pp_handler; > > > > static struct platform_driver platform_driver = { > > .driver = { > > @@ -500,7 +549,7 @@ static void alienware_zone_exit(struct platform_device *dev) > > kfree(zone_attrs); > > } > > > > -static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args, > > +static acpi_status alienware_wmax_command(void *in_args, size_t insize, > > u32 command, int *out_data) > > { > > Can you split this change into a separate patch? This would make review a bit easier. > Yes, sure. In that case should I mention this patch depends on that one in v4? > > acpi_status status; > > @@ -508,7 +557,7 @@ static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args, > > struct acpi_buffer input; > > struct acpi_buffer output; > > > > - input.length = (acpi_size) sizeof(*in_args); > > + input.length = (acpi_size) insize; > > Please drop the cast to acpi_size. > Ok. > > input.pointer = in_args; > > if (out_data) { > > output.length = ACPI_ALLOCATE_BUFFER; > > @@ -541,8 +590,8 @@ static ssize_t show_hdmi_cable(struct device *dev, > > .arg = 0, > > }; > > status = > > - alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE, > > - (u32 *) &out_data); > > + alienware_wmax_command(&in_args, sizeof(in_args), > > + WMAX_METHOD_HDMI_CABLE, (u32 *) &out_data); > > if (ACPI_SUCCESS(status)) { > > if (out_data == 0) > > return sysfs_emit(buf, "[unconnected] connected unknown\n"); > > @@ -562,8 +611,8 @@ static ssize_t show_hdmi_source(struct device *dev, > > .arg = 0, > > }; > > status = > > - alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS, > > - (u32 *) &out_data); > > + alienware_wmax_command(&in_args, sizeof(in_args), > > + WMAX_METHOD_HDMI_STATUS, (u32 *) &out_data); > > > > if (ACPI_SUCCESS(status)) { > > if (out_data == 1) > > @@ -589,7 +638,8 @@ static ssize_t toggle_hdmi_source(struct device *dev, > > args.arg = 3; > > pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf); > > > > - status = alienware_wmax_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL); > > + status = alienware_wmax_command(&args, sizeof(args), > > + WMAX_METHOD_HDMI_SOURCE, NULL); > > > > if (ACPI_FAILURE(status)) > > pr_err("alienware-wmi: HDMI toggle failed: results: %u\n", > > @@ -642,8 +692,8 @@ static ssize_t show_amplifier_status(struct device *dev, > > .arg = 0, > > }; > > status = > > - alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE, > > - (u32 *) &out_data); > > + alienware_wmax_command(&in_args, sizeof(in_args), > > + WMAX_METHOD_AMPLIFIER_CABLE, (u32 *) &out_data); > > if (ACPI_SUCCESS(status)) { > > if (out_data == 0) > > return sysfs_emit(buf, "[unconnected] connected unknown\n"); > > @@ -694,8 +744,8 @@ static ssize_t show_deepsleep_status(struct device *dev, > > struct wmax_basic_args in_args = { > > .arg = 0, > > }; > > - status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS, > > - (u32 *) &out_data); > > + status = alienware_wmax_command(&in_args, sizeof(in_args), > > + WMAX_METHOD_DEEP_SLEEP_STATUS, (u32 *) &out_data); > > if (ACPI_SUCCESS(status)) { > > if (out_data == 0) > > return sysfs_emit(buf, "[disabled] s5 s5_s4\n"); > > @@ -723,8 +773,8 @@ static ssize_t toggle_deepsleep(struct device *dev, > > args.arg = 2; > > pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf); > > > > - status = alienware_wmax_command(&args, WMAX_METHOD_DEEP_SLEEP_CONTROL, > > - NULL); > > + status = alienware_wmax_command(&args, sizeof(args), > > + WMAX_METHOD_DEEP_SLEEP_CONTROL, NULL); > > > > if (ACPI_FAILURE(status)) > > pr_err("alienware-wmi: deep sleep control failed: results: %u\n", > > @@ -760,6 +810,160 @@ static int create_deepsleep(struct platform_device *dev) > > return ret; > > } > > > > +/* > > + * Thermal Profile control > > + * - Provides thermal profile control through the Platform Profile API > > + */ > > +#define PROFILE_MASK GENMASK(15,8) > > +#define PROFILE_ACTIVATE BIT(0) > > + > > +static u32 profile_to_wmax_arg(enum WMAX_THERMAL_PROFILE prof) > > +{ > > + return FIELD_PREP(PROFILE_MASK, prof) | PROFILE_ACTIVATE; > > +} > > + > > +static int thermal_profile_get(struct platform_profile_handler *pprof, > > + enum platform_profile_option *profile) > > +{ > > + acpi_status status; > > + u32 in_args = WMAX_ARG_GET_CURRENT_PROF; > > + u32 out_data; > > + > > + status = alienware_wmax_command(&in_args, sizeof(in_args), > > + WMAX_METHOD_THERMAL_INFORMATION, &out_data); > > + > > + if (ACPI_FAILURE(status)) > > + return -EOPNOTSUPP; > > Please return -EIO. > Ok. > > + > > + if (out_data == 0xFFFFFFFF) > > + return -EBADRQC; > > + > > + switch (out_data) { > > + case WMAX_THERMAL_LOW_POWER: > > + *profile = PLATFORM_PROFILE_LOW_POWER; > > + break; > > + case WMAX_THERMAL_QUIET: > > + *profile = PLATFORM_PROFILE_QUIET; > > + break; > > + case WMAX_THERMAL_BALANCED: > > + *profile = PLATFORM_PROFILE_BALANCED; > > + break; > > + case WMAX_THERMAL_BALANCED_PERFORMANCE: > > + *profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE; > > + break; > > + case WMAX_THERMAL_PERFORMANCE: > > + case WMAX_THERMAL_GMODE: > > + *profile = PLATFORM_PROFILE_PERFORMANCE; > > + break; > > + default: > > + return -ENODATA; > > + } > > + > > + return 0; > > +} > > + > > +static int thermal_profile_set(struct platform_profile_handler *pprof, > > + enum platform_profile_option profile) > > +{ > > + acpi_status status; > > + u32 in_args; > > + u32 out_data; > > + > > + switch (profile) { > > + case PLATFORM_PROFILE_LOW_POWER: > > + in_args = profile_to_wmax_arg(WMAX_THERMAL_LOW_POWER); > > + break; > > + case PLATFORM_PROFILE_QUIET: > > + in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET); > > + break; > > + case PLATFORM_PROFILE_BALANCED: > > + in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED); > > + break; > > + case PLATFORM_PROFILE_BALANCED_PERFORMANCE: > > + in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE); > > + break; > > + case PLATFORM_PROFILE_PERFORMANCE: > > + in_args = profile_to_wmax_arg(WMAX_THERMAL_PERFORMANCE); > > + break; > > + default: > > + return -EOPNOTSUPP; > > + } > > + > > + status = alienware_wmax_command(&in_args, sizeof(in_args), > > + WMAX_METHOD_THERMAL_CONTROL, &out_data); > > + > > + if (ACPI_FAILURE(status)) > > + return -EOPNOTSUPP; > > Return -EIO. > > > + > > + if (out_data == 0xFFFFFFFF) > > + return -EBADRQC; > > + > > + return 0; > > +} > > + > > +static int gmode_thermal_profile_set(struct platform_profile_handler *pprof, > > + enum platform_profile_option profile) > > +{ > > + acpi_status status; > > + u32 in_args; > > + u32 out_data; > > + > > + switch (profile) { > > + case PLATFORM_PROFILE_LOW_POWER: > > + in_args = profile_to_wmax_arg(WMAX_THERMAL_LOW_POWER); > > + break; > > + case PLATFORM_PROFILE_QUIET: > > + in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET); > > + break; > > + case PLATFORM_PROFILE_BALANCED: > > + in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED); > > + break; > > + case PLATFORM_PROFILE_BALANCED_PERFORMANCE: > > + in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE); > > + break; > > + case PLATFORM_PROFILE_PERFORMANCE: > > + in_args = profile_to_wmax_arg(WMAX_THERMAL_GMODE); > > + break; > > + default: > > + return -EOPNOTSUPP; > > + } > > + > > + status = alienware_wmax_command(&in_args, sizeof(in_args), > > + WMAX_METHOD_THERMAL_CONTROL, &out_data); > > + > > + if (ACPI_FAILURE(status)) > > + return -EOPNOTSUPP; > > Return -EIO. > > Otherwise the patch looks quite good. > Thank you! It's my first time working on the kernel. > Thanks, > Armin Wolf Your feedback is appreciated. Kurt > > + > > + if (out_data == 0xFFFFFFFF) > > + return -EBADRQC; > > + > > + return 0; > > +} > > + > > +static int create_thermal_profile(void) > > +{ > > + pp_handler.profile_get = thermal_profile_get; > > + > > + if (quirks->gmode > 0) > > + pp_handler.profile_set = gmode_thermal_profile_set; > > + else > > + pp_handler.profile_set = thermal_profile_set; > > + > > + set_bit(PLATFORM_PROFILE_LOW_POWER, pp_handler.choices); > > + set_bit(PLATFORM_PROFILE_QUIET, pp_handler.choices); > > + set_bit(PLATFORM_PROFILE_BALANCED, pp_handler.choices); > > + set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, pp_handler.choices); > > + set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices); > > + > > + return platform_profile_register(&pp_handler); > > +} > > + > > +static void remove_thermal_profile(void) > > +{ > > + if (quirks->thermal > 0) > > + platform_profile_remove(); > > +} > > + > > static int __init alienware_wmi_init(void) > > { > > int ret; > > @@ -807,6 +1011,12 @@ static int __init alienware_wmi_init(void) > > goto fail_prep_deepsleep; > > } > > > > + if (quirks->thermal > 0) { > > + ret = create_thermal_profile(); > > + if (ret) > > + goto fail_prep_thermal_profile; > > + } > > + > > ret = alienware_zone_init(platform_device); > > if (ret) > > goto fail_prep_zones; > > @@ -817,6 +1027,7 @@ static int __init alienware_wmi_init(void) > > alienware_zone_exit(platform_device); > > fail_prep_deepsleep: > > fail_prep_amplifier: > > +fail_prep_thermal_profile: > > fail_prep_hdmi: > > platform_device_del(platform_device); > > fail_platform_device2: > > @@ -834,6 +1045,7 @@ static void __exit alienware_wmi_exit(void) > > if (platform_device) { > > alienware_zone_exit(platform_device); > > remove_hdmi(platform_device); > > + remove_thermal_profile(); > > platform_device_unregister(platform_device); > > platform_driver_unregister(&platform_driver); > > }
Am 09.10.24 um 16:48 schrieb Kurt Borja: > Yes, of course. > >>> This driver was heavily inspired on inspur_platform_profile, special >>> thanks. >>> >>> Notes: >>> - Performance (FullSpeed) profile is a special profile which has it's own >>> entry in the Firmware Settings of the Alienware x15 R1. It also changes >>> the color of the F1 key. I suspect this behavior would be replicated in >>> other X-Series or M-Series laptops. >>> - G-Mode is a profile documented on [1] which mimics the behavior of >>> FullSpeed mode but it does not have an entry on the Firmware Settings of >>> the Alienware x15 R1, this may correspond to the G-Mode functionality on >>> G-Series laptops (activated by a special button) but I cannot test it. I >>> did not include this code in the driver as G-Mode causes unexpected >>> behavior on X-Series laptops. >>> >>> Signed-off-by: Kurt Borja <kuurtb@gmail.com> >>> >>> --- >>> v3: >>> - Removed extra empty line >>> - 0x0B named WMAX_ARG_GET_CURRENT_PROF >>> - Removed casts to the same type on functions added in this patch >>> - Thermal profile to WMAX argument is now an static function and makes >>> use of in-built kernel macros >>> - Platform profile is now removed only if it was created first >>> - create_platform_profile is now create_thermal_profile to avoid >>> confusion >>> - profile_get and profile_set functions renamed too to match the above >>> v2: >>> - Moved functionality to alienware-wmi driver >>> - Added thermal and gmode quirks to add support based on dmi match >>> - Performance profile is now GMODE for devices that support it >>> - alienware_wmax_command now is insize agnostic to support new thermal >>> methods >>> --- >>> drivers/platform/x86/dell/Kconfig | 1 + >>> drivers/platform/x86/dell/alienware-wmi.c | 238 ++++++++++++++++++++-- >>> 2 files changed, 226 insertions(+), 13 deletions(-) >>> >>> diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig >>> index 68a49788a..b06d634cd 100644 >>> --- a/drivers/platform/x86/dell/Kconfig >>> +++ b/drivers/platform/x86/dell/Kconfig >>> @@ -21,6 +21,7 @@ config ALIENWARE_WMI >>> depends on LEDS_CLASS >>> depends on NEW_LEDS >>> depends on ACPI_WMI >>> + select ACPI_PLATFORM_PROFILE >>> help >>> This is a driver for controlling Alienware BIOS driven >>> features. It exposes an interface for controlling the AlienFX >>> diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c >>> index f5ee62ce1..e3ef4b10b 100644 >>> --- a/drivers/platform/x86/dell/alienware-wmi.c >>> +++ b/drivers/platform/x86/dell/alienware-wmi.c >>> @@ -10,6 +10,7 @@ >>> #include <linux/acpi.h> >>> #include <linux/module.h> >>> #include <linux/platform_device.h> >>> +#include <linux/platform_profile.h> >>> #include <linux/dmi.h> >>> #include <linux/leds.h> >>> >>> @@ -25,6 +26,10 @@ >>> #define WMAX_METHOD_AMPLIFIER_CABLE 0x6 >>> #define WMAX_METHOD_DEEP_SLEEP_CONTROL 0x0B >>> #define WMAX_METHOD_DEEP_SLEEP_STATUS 0x0C >>> +#define WMAX_METHOD_THERMAL_INFORMATION 0x14 >>> +#define WMAX_METHOD_THERMAL_CONTROL 0x15 >>> + >>> +#define WMAX_ARG_GET_CURRENT_PROF 0x0B >>> >>> MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>"); >>> MODULE_DESCRIPTION("Alienware special feature control"); >>> @@ -49,11 +54,22 @@ enum WMAX_CONTROL_STATES { >>> WMAX_SUSPEND = 3, >>> }; >>> >>> +enum WMAX_THERMAL_PROFILE { >>> + WMAX_THERMAL_QUIET = 0xA3, >>> + WMAX_THERMAL_BALANCED = 0xA0, >>> + WMAX_THERMAL_BALANCED_PERFORMANCE = 0xA1, >>> + WMAX_THERMAL_PERFORMANCE = 0xA4, >>> + WMAX_THERMAL_GMODE = 0xAB, >>> + WMAX_THERMAL_LOW_POWER = 0xA5, >>> +}; >>> + >>> struct quirk_entry { >>> u8 num_zones; >>> u8 hdmi_mux; >>> u8 amplifier; >>> u8 deepslp; >>> + u8 thermal; >>> + u8 gmode; >>> }; >>> >>> static struct quirk_entry *quirks; >>> @@ -64,6 +80,8 @@ static struct quirk_entry quirk_inspiron5675 = { >>> .hdmi_mux = 0, >>> .amplifier = 0, >>> .deepslp = 0, >>> + .thermal = 0, >>> + .gmode = 0, >>> }; >>> >>> static struct quirk_entry quirk_unknown = { >>> @@ -71,6 +89,8 @@ static struct quirk_entry quirk_unknown = { >>> .hdmi_mux = 0, >>> .amplifier = 0, >>> .deepslp = 0, >>> + .thermal = 0, >>> + .gmode = 0, >>> }; >>> >>> static struct quirk_entry quirk_x51_r1_r2 = { >>> @@ -78,6 +98,8 @@ static struct quirk_entry quirk_x51_r1_r2 = { >>> .hdmi_mux = 0, >>> .amplifier = 0, >>> .deepslp = 0, >>> + .thermal = 0, >>> + .gmode = 0, >>> }; >>> >>> static struct quirk_entry quirk_x51_r3 = { >>> @@ -85,6 +107,8 @@ static struct quirk_entry quirk_x51_r3 = { >>> .hdmi_mux = 0, >>> .amplifier = 1, >>> .deepslp = 0, >>> + .thermal = 0, >>> + .gmode = 0, >>> }; >>> >>> static struct quirk_entry quirk_asm100 = { >>> @@ -92,6 +116,8 @@ static struct quirk_entry quirk_asm100 = { >>> .hdmi_mux = 1, >>> .amplifier = 0, >>> .deepslp = 0, >>> + .thermal = 0, >>> + .gmode = 0, >>> }; >>> >>> static struct quirk_entry quirk_asm200 = { >>> @@ -99,6 +125,8 @@ static struct quirk_entry quirk_asm200 = { >>> .hdmi_mux = 1, >>> .amplifier = 0, >>> .deepslp = 1, >>> + .thermal = 0, >>> + .gmode = 0, >>> }; >>> >>> static struct quirk_entry quirk_asm201 = { >>> @@ -106,6 +134,17 @@ static struct quirk_entry quirk_asm201 = { >>> .hdmi_mux = 1, >>> .amplifier = 1, >>> .deepslp = 1, >>> + .thermal = 0, >>> + .gmode = 0, >>> +}; >>> + >>> +static struct quirk_entry quirk_x15_r1 = { >>> + .num_zones = 2, >>> + .hdmi_mux = 0, >>> + .amplifier = 0, >>> + .deepslp = 0, >>> + .thermal = 1, >>> + .gmode = 0, >>> }; >>> >>> static int __init dmi_matched(const struct dmi_system_id *dmi) >>> @@ -169,6 +208,15 @@ static const struct dmi_system_id alienware_quirks[] __initconst = { >>> }, >>> .driver_data = &quirk_asm201, >>> }, >>> + { >>> + .callback = dmi_matched, >>> + .ident = "Alienware x15 R1", >>> + .matches = { >>> + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), >>> + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R1") >>> + }, >>> + .driver_data = &quirk_x15_r1, >>> + }, >>> { >>> .callback = dmi_matched, >>> .ident = "Dell Inc. Inspiron 5675", >>> @@ -218,6 +266,7 @@ static struct platform_device *platform_device; >>> static struct device_attribute *zone_dev_attrs; >>> static struct attribute **zone_attrs; >>> static struct platform_zone *zone_data; >>> +static struct platform_profile_handler pp_handler; >>> >>> static struct platform_driver platform_driver = { >>> .driver = { >>> @@ -500,7 +549,7 @@ static void alienware_zone_exit(struct platform_device *dev) >>> kfree(zone_attrs); >>> } >>> >>> -static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args, >>> +static acpi_status alienware_wmax_command(void *in_args, size_t insize, >>> u32 command, int *out_data) >>> { >> Can you split this change into a separate patch? This would make review a bit easier. >> > Yes, sure. In that case should I mention this patch depends on that one > in v4? Yes, but only in the description of the resulting patch series. Thanks, Armin Wolf >>> acpi_status status; >>> @@ -508,7 +557,7 @@ static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args, >>> struct acpi_buffer input; >>> struct acpi_buffer output; >>> >>> - input.length = (acpi_size) sizeof(*in_args); >>> + input.length = (acpi_size) insize; >> Please drop the cast to acpi_size. >> > Ok. > >>> input.pointer = in_args; >>> if (out_data) { >>> output.length = ACPI_ALLOCATE_BUFFER; >>> @@ -541,8 +590,8 @@ static ssize_t show_hdmi_cable(struct device *dev, >>> .arg = 0, >>> }; >>> status = >>> - alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE, >>> - (u32 *) &out_data); >>> + alienware_wmax_command(&in_args, sizeof(in_args), >>> + WMAX_METHOD_HDMI_CABLE, (u32 *) &out_data); >>> if (ACPI_SUCCESS(status)) { >>> if (out_data == 0) >>> return sysfs_emit(buf, "[unconnected] connected unknown\n"); >>> @@ -562,8 +611,8 @@ static ssize_t show_hdmi_source(struct device *dev, >>> .arg = 0, >>> }; >>> status = >>> - alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS, >>> - (u32 *) &out_data); >>> + alienware_wmax_command(&in_args, sizeof(in_args), >>> + WMAX_METHOD_HDMI_STATUS, (u32 *) &out_data); >>> >>> if (ACPI_SUCCESS(status)) { >>> if (out_data == 1) >>> @@ -589,7 +638,8 @@ static ssize_t toggle_hdmi_source(struct device *dev, >>> args.arg = 3; >>> pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf); >>> >>> - status = alienware_wmax_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL); >>> + status = alienware_wmax_command(&args, sizeof(args), >>> + WMAX_METHOD_HDMI_SOURCE, NULL); >>> >>> if (ACPI_FAILURE(status)) >>> pr_err("alienware-wmi: HDMI toggle failed: results: %u\n", >>> @@ -642,8 +692,8 @@ static ssize_t show_amplifier_status(struct device *dev, >>> .arg = 0, >>> }; >>> status = >>> - alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE, >>> - (u32 *) &out_data); >>> + alienware_wmax_command(&in_args, sizeof(in_args), >>> + WMAX_METHOD_AMPLIFIER_CABLE, (u32 *) &out_data); >>> if (ACPI_SUCCESS(status)) { >>> if (out_data == 0) >>> return sysfs_emit(buf, "[unconnected] connected unknown\n"); >>> @@ -694,8 +744,8 @@ static ssize_t show_deepsleep_status(struct device *dev, >>> struct wmax_basic_args in_args = { >>> .arg = 0, >>> }; >>> - status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS, >>> - (u32 *) &out_data); >>> + status = alienware_wmax_command(&in_args, sizeof(in_args), >>> + WMAX_METHOD_DEEP_SLEEP_STATUS, (u32 *) &out_data); >>> if (ACPI_SUCCESS(status)) { >>> if (out_data == 0) >>> return sysfs_emit(buf, "[disabled] s5 s5_s4\n"); >>> @@ -723,8 +773,8 @@ static ssize_t toggle_deepsleep(struct device *dev, >>> args.arg = 2; >>> pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf); >>> >>> - status = alienware_wmax_command(&args, WMAX_METHOD_DEEP_SLEEP_CONTROL, >>> - NULL); >>> + status = alienware_wmax_command(&args, sizeof(args), >>> + WMAX_METHOD_DEEP_SLEEP_CONTROL, NULL); >>> >>> if (ACPI_FAILURE(status)) >>> pr_err("alienware-wmi: deep sleep control failed: results: %u\n", >>> @@ -760,6 +810,160 @@ static int create_deepsleep(struct platform_device *dev) >>> return ret; >>> } >>> >>> +/* >>> + * Thermal Profile control >>> + * - Provides thermal profile control through the Platform Profile API >>> + */ >>> +#define PROFILE_MASK GENMASK(15,8) >>> +#define PROFILE_ACTIVATE BIT(0) >>> + >>> +static u32 profile_to_wmax_arg(enum WMAX_THERMAL_PROFILE prof) >>> +{ >>> + return FIELD_PREP(PROFILE_MASK, prof) | PROFILE_ACTIVATE; >>> +} >>> + >>> +static int thermal_profile_get(struct platform_profile_handler *pprof, >>> + enum platform_profile_option *profile) >>> +{ >>> + acpi_status status; >>> + u32 in_args = WMAX_ARG_GET_CURRENT_PROF; >>> + u32 out_data; >>> + >>> + status = alienware_wmax_command(&in_args, sizeof(in_args), >>> + WMAX_METHOD_THERMAL_INFORMATION, &out_data); >>> + >>> + if (ACPI_FAILURE(status)) >>> + return -EOPNOTSUPP; >> Please return -EIO. >> > Ok. > >>> + >>> + if (out_data == 0xFFFFFFFF) >>> + return -EBADRQC; >>> + >>> + switch (out_data) { >>> + case WMAX_THERMAL_LOW_POWER: >>> + *profile = PLATFORM_PROFILE_LOW_POWER; >>> + break; >>> + case WMAX_THERMAL_QUIET: >>> + *profile = PLATFORM_PROFILE_QUIET; >>> + break; >>> + case WMAX_THERMAL_BALANCED: >>> + *profile = PLATFORM_PROFILE_BALANCED; >>> + break; >>> + case WMAX_THERMAL_BALANCED_PERFORMANCE: >>> + *profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE; >>> + break; >>> + case WMAX_THERMAL_PERFORMANCE: >>> + case WMAX_THERMAL_GMODE: >>> + *profile = PLATFORM_PROFILE_PERFORMANCE; >>> + break; >>> + default: >>> + return -ENODATA; >>> + } >>> + >>> + return 0; >>> +} >>> + >>> +static int thermal_profile_set(struct platform_profile_handler *pprof, >>> + enum platform_profile_option profile) >>> +{ >>> + acpi_status status; >>> + u32 in_args; >>> + u32 out_data; >>> + >>> + switch (profile) { >>> + case PLATFORM_PROFILE_LOW_POWER: >>> + in_args = profile_to_wmax_arg(WMAX_THERMAL_LOW_POWER); >>> + break; >>> + case PLATFORM_PROFILE_QUIET: >>> + in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET); >>> + break; >>> + case PLATFORM_PROFILE_BALANCED: >>> + in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED); >>> + break; >>> + case PLATFORM_PROFILE_BALANCED_PERFORMANCE: >>> + in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE); >>> + break; >>> + case PLATFORM_PROFILE_PERFORMANCE: >>> + in_args = profile_to_wmax_arg(WMAX_THERMAL_PERFORMANCE); >>> + break; >>> + default: >>> + return -EOPNOTSUPP; >>> + } >>> + >>> + status = alienware_wmax_command(&in_args, sizeof(in_args), >>> + WMAX_METHOD_THERMAL_CONTROL, &out_data); >>> + >>> + if (ACPI_FAILURE(status)) >>> + return -EOPNOTSUPP; >> Return -EIO. >> >>> + >>> + if (out_data == 0xFFFFFFFF) >>> + return -EBADRQC; >>> + >>> + return 0; >>> +} >>> + >>> +static int gmode_thermal_profile_set(struct platform_profile_handler *pprof, >>> + enum platform_profile_option profile) >>> +{ >>> + acpi_status status; >>> + u32 in_args; >>> + u32 out_data; >>> + >>> + switch (profile) { >>> + case PLATFORM_PROFILE_LOW_POWER: >>> + in_args = profile_to_wmax_arg(WMAX_THERMAL_LOW_POWER); >>> + break; >>> + case PLATFORM_PROFILE_QUIET: >>> + in_args = profile_to_wmax_arg(WMAX_THERMAL_QUIET); >>> + break; >>> + case PLATFORM_PROFILE_BALANCED: >>> + in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED); >>> + break; >>> + case PLATFORM_PROFILE_BALANCED_PERFORMANCE: >>> + in_args = profile_to_wmax_arg(WMAX_THERMAL_BALANCED_PERFORMANCE); >>> + break; >>> + case PLATFORM_PROFILE_PERFORMANCE: >>> + in_args = profile_to_wmax_arg(WMAX_THERMAL_GMODE); >>> + break; >>> + default: >>> + return -EOPNOTSUPP; >>> + } >>> + >>> + status = alienware_wmax_command(&in_args, sizeof(in_args), >>> + WMAX_METHOD_THERMAL_CONTROL, &out_data); >>> + >>> + if (ACPI_FAILURE(status)) >>> + return -EOPNOTSUPP; >> Return -EIO. >> >> Otherwise the patch looks quite good. >> > Thank you! It's my first time working on the kernel. > >> Thanks, >> Armin Wolf > Your feedback is appreciated. > > Kurt > >>> + >>> + if (out_data == 0xFFFFFFFF) >>> + return -EBADRQC; >>> + >>> + return 0; >>> +} >>> + >>> +static int create_thermal_profile(void) >>> +{ >>> + pp_handler.profile_get = thermal_profile_get; >>> + >>> + if (quirks->gmode > 0) >>> + pp_handler.profile_set = gmode_thermal_profile_set; >>> + else >>> + pp_handler.profile_set = thermal_profile_set; >>> + >>> + set_bit(PLATFORM_PROFILE_LOW_POWER, pp_handler.choices); >>> + set_bit(PLATFORM_PROFILE_QUIET, pp_handler.choices); >>> + set_bit(PLATFORM_PROFILE_BALANCED, pp_handler.choices); >>> + set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, pp_handler.choices); >>> + set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices); >>> + >>> + return platform_profile_register(&pp_handler); >>> +} >>> + >>> +static void remove_thermal_profile(void) >>> +{ >>> + if (quirks->thermal > 0) >>> + platform_profile_remove(); >>> +} >>> + >>> static int __init alienware_wmi_init(void) >>> { >>> int ret; >>> @@ -807,6 +1011,12 @@ static int __init alienware_wmi_init(void) >>> goto fail_prep_deepsleep; >>> } >>> >>> + if (quirks->thermal > 0) { >>> + ret = create_thermal_profile(); >>> + if (ret) >>> + goto fail_prep_thermal_profile; >>> + } >>> + >>> ret = alienware_zone_init(platform_device); >>> if (ret) >>> goto fail_prep_zones; >>> @@ -817,6 +1027,7 @@ static int __init alienware_wmi_init(void) >>> alienware_zone_exit(platform_device); >>> fail_prep_deepsleep: >>> fail_prep_amplifier: >>> +fail_prep_thermal_profile: >>> fail_prep_hdmi: >>> platform_device_del(platform_device); >>> fail_platform_device2: >>> @@ -834,6 +1045,7 @@ static void __exit alienware_wmi_exit(void) >>> if (platform_device) { >>> alienware_zone_exit(platform_device); >>> remove_hdmi(platform_device); >>> + remove_thermal_profile(); >>> platform_device_unregister(platform_device); >>> platform_driver_unregister(&platform_driver); >>> }
This patch adds platform_profile support for Dell devices which implement
User Selectable Thermal Tables (USTT) that are meant to be controlled by
Alienware Command Center (AWCC). These devices may include newer Alienware
M-Series, Alienware X-Series and Dell's G-Series. This patch, was tested
by me on an Alienware x15 R1.
It is suspected that Alienware Command Center manages thermal profiles
through the WMI interface, specifically through a device with identifier
\_SB_.AMW1.WMAX. This device was reverse engineered and the relevant
functionality is documented here [1]. This driver interacts with this
WMI device and thus is able to mimic AWCC's thermal profiles functionality
through the platform_profile API. In consequence the user would be able
to set and retrieve thermal profiles, which are just fan speed profiles.
This driver was heavily inspired on inspur_platform_profile, special
thanks.
Notes:
- Performance (FullSpeed) profile is a special profile which has it's own
entry in the Firmware Settings of the Alienware x15 R1. It also changes
the color of the F1 key. I suspect this behavior would be replicated in
other X-Series or M-Series laptops.
- G-Mode is a profile documented on [1] which mimics the behavior of
FullSpeed mode but it does not have an entry on the Firmware Settings of
the Alienware x15 R1, this may correspond to the G-Mode functionality on
G-Series laptops (activated by a special button) but I cannot test it. I
did not include this code in the driver as G-Mode causes unexpected
behavior on X-Series laptops.
---
v2:
- Moved functionality to alienware-wmi driver
- Added thermal and gmode quirks to add support based on dmi match
- Performance profile is now GMODE for devices that support it
- alienware_wmax_command now is insize agnostic to support new thermal
methods
---
drivers/platform/x86/dell/Kconfig | 1 +
drivers/platform/x86/dell/alienware-wmi.c | 214 ++++++++++++++++++++--
2 files changed, 202 insertions(+), 13 deletions(-)
diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig
index 68a49788a..b06d634cd 100644
--- a/drivers/platform/x86/dell/Kconfig
+++ b/drivers/platform/x86/dell/Kconfig
@@ -21,6 +21,7 @@ config ALIENWARE_WMI
depends on LEDS_CLASS
depends on NEW_LEDS
depends on ACPI_WMI
+ select ACPI_PLATFORM_PROFILE
help
This is a driver for controlling Alienware BIOS driven
features. It exposes an interface for controlling the AlienFX
diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
index f5ee62ce1..47abb533c 100644
--- a/drivers/platform/x86/dell/alienware-wmi.c
+++ b/drivers/platform/x86/dell/alienware-wmi.c
@@ -10,6 +10,7 @@
#include <linux/acpi.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/platform_profile.h>
#include <linux/dmi.h>
#include <linux/leds.h>
@@ -25,6 +26,15 @@
#define WMAX_METHOD_AMPLIFIER_CABLE 0x6
#define WMAX_METHOD_DEEP_SLEEP_CONTROL 0x0B
#define WMAX_METHOD_DEEP_SLEEP_STATUS 0x0C
+#define WMAX_METHOD_THERMAL_INFORMATION 0x14
+#define WMAX_METHOD_THERMAL_CONTROL 0x15
+
+#define WMAX_THERMAL_QUIET 0xA3
+#define WMAX_THERMAL_BALANCED 0xA0
+#define WMAX_THERMAL_BALANCED_PERFORMANCE 0xA1
+#define WMAX_THERMAL_PERFORMANCE 0xA4
+#define WMAX_THERMAL_GMODE 0xAB
+#define WMAX_THERMAL_LOW_POWER 0xA5
MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
MODULE_DESCRIPTION("Alienware special feature control");
@@ -54,6 +64,8 @@ struct quirk_entry {
u8 hdmi_mux;
u8 amplifier;
u8 deepslp;
+ u8 thermal;
+ u8 gmode;
};
static struct quirk_entry *quirks;
@@ -64,6 +76,8 @@ static struct quirk_entry quirk_inspiron5675 = {
.hdmi_mux = 0,
.amplifier = 0,
.deepslp = 0,
+ .thermal = 0,
+ .gmode = 0,
};
static struct quirk_entry quirk_unknown = {
@@ -71,6 +85,8 @@ static struct quirk_entry quirk_unknown = {
.hdmi_mux = 0,
.amplifier = 0,
.deepslp = 0,
+ .thermal = 0,
+ .gmode = 0,
};
static struct quirk_entry quirk_x51_r1_r2 = {
@@ -78,6 +94,8 @@ static struct quirk_entry quirk_x51_r1_r2 = {
.hdmi_mux = 0,
.amplifier = 0,
.deepslp = 0,
+ .thermal = 0,
+ .gmode = 0,
};
static struct quirk_entry quirk_x51_r3 = {
@@ -85,6 +103,8 @@ static struct quirk_entry quirk_x51_r3 = {
.hdmi_mux = 0,
.amplifier = 1,
.deepslp = 0,
+ .thermal = 0,
+ .gmode = 0,
};
static struct quirk_entry quirk_asm100 = {
@@ -92,6 +112,8 @@ static struct quirk_entry quirk_asm100 = {
.hdmi_mux = 1,
.amplifier = 0,
.deepslp = 0,
+ .thermal = 0,
+ .gmode = 0,
};
static struct quirk_entry quirk_asm200 = {
@@ -99,6 +121,8 @@ static struct quirk_entry quirk_asm200 = {
.hdmi_mux = 1,
.amplifier = 0,
.deepslp = 1,
+ .thermal = 0,
+ .gmode = 0,
};
static struct quirk_entry quirk_asm201 = {
@@ -106,6 +130,17 @@ static struct quirk_entry quirk_asm201 = {
.hdmi_mux = 1,
.amplifier = 1,
.deepslp = 1,
+ .thermal = 0,
+ .gmode = 0,
+};
+
+static struct quirk_entry quirk_x15_r1 = {
+ .num_zones = 2,
+ .hdmi_mux = 0,
+ .amplifier = 0,
+ .deepslp = 0,
+ .thermal = 1,
+ .gmode = 0,
};
static int __init dmi_matched(const struct dmi_system_id *dmi)
@@ -169,6 +204,15 @@ static const struct dmi_system_id alienware_quirks[] __initconst = {
},
.driver_data = &quirk_asm201,
},
+ {
+ .callback = dmi_matched,
+ .ident = "Alienware x15 R1",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R1")
+ },
+ .driver_data = &quirk_x15_r1,
+ },
{
.callback = dmi_matched,
.ident = "Dell Inc. Inspiron 5675",
@@ -218,6 +262,7 @@ static struct platform_device *platform_device;
static struct device_attribute *zone_dev_attrs;
static struct attribute **zone_attrs;
static struct platform_zone *zone_data;
+static struct platform_profile_handler pp_handler;
static struct platform_driver platform_driver = {
.driver = {
@@ -500,7 +545,7 @@ static void alienware_zone_exit(struct platform_device *dev)
kfree(zone_attrs);
}
-static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
+static acpi_status alienware_wmax_command(void *in_args, size_t insize,
u32 command, int *out_data)
{
acpi_status status;
@@ -508,7 +553,7 @@ static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
struct acpi_buffer input;
struct acpi_buffer output;
- input.length = (acpi_size) sizeof(*in_args);
+ input.length = (acpi_size) insize;
input.pointer = in_args;
if (out_data) {
output.length = ACPI_ALLOCATE_BUFFER;
@@ -541,8 +586,8 @@ static ssize_t show_hdmi_cable(struct device *dev,
.arg = 0,
};
status =
- alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE,
- (u32 *) &out_data);
+ alienware_wmax_command(&in_args, sizeof(in_args),
+ WMAX_METHOD_HDMI_CABLE, (u32 *) &out_data);
if (ACPI_SUCCESS(status)) {
if (out_data == 0)
return sysfs_emit(buf, "[unconnected] connected unknown\n");
@@ -562,8 +607,8 @@ static ssize_t show_hdmi_source(struct device *dev,
.arg = 0,
};
status =
- alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS,
- (u32 *) &out_data);
+ alienware_wmax_command(&in_args, sizeof(in_args),
+ WMAX_METHOD_HDMI_STATUS, (u32 *) &out_data);
if (ACPI_SUCCESS(status)) {
if (out_data == 1)
@@ -589,7 +634,8 @@ static ssize_t toggle_hdmi_source(struct device *dev,
args.arg = 3;
pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf);
- status = alienware_wmax_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL);
+ status = alienware_wmax_command(&args, sizeof(args),
+ WMAX_METHOD_HDMI_SOURCE, NULL);
if (ACPI_FAILURE(status))
pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
@@ -642,8 +688,8 @@ static ssize_t show_amplifier_status(struct device *dev,
.arg = 0,
};
status =
- alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE,
- (u32 *) &out_data);
+ alienware_wmax_command(&in_args, sizeof(in_args),
+ WMAX_METHOD_AMPLIFIER_CABLE, (u32 *) &out_data);
if (ACPI_SUCCESS(status)) {
if (out_data == 0)
return sysfs_emit(buf, "[unconnected] connected unknown\n");
@@ -694,8 +740,8 @@ static ssize_t show_deepsleep_status(struct device *dev,
struct wmax_basic_args in_args = {
.arg = 0,
};
- status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS,
- (u32 *) &out_data);
+ status = alienware_wmax_command(&in_args, sizeof(in_args),
+ WMAX_METHOD_DEEP_SLEEP_STATUS, (u32 *) &out_data);
if (ACPI_SUCCESS(status)) {
if (out_data == 0)
return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
@@ -723,8 +769,8 @@ static ssize_t toggle_deepsleep(struct device *dev,
args.arg = 2;
pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf);
- status = alienware_wmax_command(&args, WMAX_METHOD_DEEP_SLEEP_CONTROL,
- NULL);
+ status = alienware_wmax_command(&args, sizeof(args),
+ WMAX_METHOD_DEEP_SLEEP_CONTROL, NULL);
if (ACPI_FAILURE(status))
pr_err("alienware-wmi: deep sleep control failed: results: %u\n",
@@ -760,6 +806,140 @@ static int create_deepsleep(struct platform_device *dev)
return ret;
}
+/*
+ * Thermal Profile control
+ * - Provides thermal profile control through the Platform Profile API
+ */
+
+static int platform_profile_get(struct platform_profile_handler *pprof,
+ enum platform_profile_option *profile)
+{
+ acpi_status status;
+ u32 in_args = 0x0B;
+ u32 out_data;
+
+ status = alienware_wmax_command(&in_args, sizeof(in_args),
+ WMAX_METHOD_THERMAL_INFORMATION, (u32 *) &out_data);
+
+ if (ACPI_FAILURE(status) || out_data < 0)
+ return -EOPNOTSUPP;
+
+ switch (out_data) {
+ case WMAX_THERMAL_LOW_POWER:
+ *profile = PLATFORM_PROFILE_LOW_POWER;
+ break;
+ case WMAX_THERMAL_QUIET:
+ *profile = PLATFORM_PROFILE_QUIET;
+ break;
+ case WMAX_THERMAL_BALANCED:
+ *profile = PLATFORM_PROFILE_BALANCED;
+ break;
+ case WMAX_THERMAL_BALANCED_PERFORMANCE:
+ *profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE;
+ break;
+ case WMAX_THERMAL_PERFORMANCE:
+ case WMAX_THERMAL_GMODE:
+ *profile = PLATFORM_PROFILE_PERFORMANCE;
+ break;
+ default:
+ return -ENODATA;
+ }
+
+ return 0;
+}
+
+#define SET_MASK(prof) ((prof << 8) | 1)
+
+static int platform_profile_set(struct platform_profile_handler *pprof,
+ enum platform_profile_option profile)
+{
+ acpi_status status;
+ u32 in_args;
+ u32 out_data;
+
+ switch (profile) {
+ case PLATFORM_PROFILE_LOW_POWER:
+ in_args = SET_MASK(WMAX_THERMAL_LOW_POWER);
+ break;
+ case PLATFORM_PROFILE_QUIET:
+ in_args = SET_MASK(WMAX_THERMAL_QUIET);
+ break;
+ case PLATFORM_PROFILE_BALANCED:
+ in_args = SET_MASK(WMAX_THERMAL_BALANCED);
+ break;
+ case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
+ in_args = SET_MASK(WMAX_THERMAL_BALANCED_PERFORMANCE);
+ break;
+ case PLATFORM_PROFILE_PERFORMANCE:
+ in_args = SET_MASK(WMAX_THERMAL_PERFORMANCE);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ status = alienware_wmax_command(&in_args, sizeof(in_args),
+ WMAX_METHOD_THERMAL_CONTROL, (u32 *) &out_data);
+
+ if (ACPI_FAILURE(status) || out_data < 0)
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+static int gmode_platform_profile_set(struct platform_profile_handler *pprof,
+ enum platform_profile_option profile)
+{
+ acpi_status status;
+ u32 in_args;
+ u32 out_data;
+
+ switch (profile) {
+ case PLATFORM_PROFILE_LOW_POWER:
+ in_args = SET_MASK(WMAX_THERMAL_LOW_POWER);
+ break;
+ case PLATFORM_PROFILE_QUIET:
+ in_args = SET_MASK(WMAX_THERMAL_QUIET);
+ break;
+ case PLATFORM_PROFILE_BALANCED:
+ in_args = SET_MASK(WMAX_THERMAL_BALANCED);
+ break;
+ case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
+ in_args = SET_MASK(WMAX_THERMAL_BALANCED_PERFORMANCE);
+ break;
+ case PLATFORM_PROFILE_PERFORMANCE:
+ in_args = SET_MASK(WMAX_THERMAL_GMODE);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ status = alienware_wmax_command(&in_args, sizeof(in_args),
+ WMAX_METHOD_THERMAL_CONTROL, (u32 *) &out_data);
+
+ if (ACPI_FAILURE(status) || out_data < 0)
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+static int create_platform_profile(void)
+{
+ pp_handler.profile_get = platform_profile_get;
+
+ if (quirks->gmode > 0)
+ pp_handler.profile_set = gmode_platform_profile_set;
+ else
+ pp_handler.profile_set = platform_profile_set;
+
+ set_bit(PLATFORM_PROFILE_LOW_POWER, pp_handler.choices);
+ set_bit(PLATFORM_PROFILE_QUIET, pp_handler.choices);
+ set_bit(PLATFORM_PROFILE_BALANCED, pp_handler.choices);
+ set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, pp_handler.choices);
+ set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices);
+
+ return platform_profile_register(&pp_handler);
+}
+
static int __init alienware_wmi_init(void)
{
int ret;
@@ -807,6 +987,12 @@ static int __init alienware_wmi_init(void)
goto fail_prep_deepsleep;
}
+ if (quirks->thermal > 0) {
+ ret = create_platform_profile();
+ if (ret)
+ goto fail_prep_thermal_profile;
+ }
+
ret = alienware_zone_init(platform_device);
if (ret)
goto fail_prep_zones;
@@ -817,6 +1003,7 @@ static int __init alienware_wmi_init(void)
alienware_zone_exit(platform_device);
fail_prep_deepsleep:
fail_prep_amplifier:
+fail_prep_thermal_profile:
fail_prep_hdmi:
platform_device_del(platform_device);
fail_platform_device2:
@@ -836,6 +1023,7 @@ static void __exit alienware_wmi_exit(void)
remove_hdmi(platform_device);
platform_device_unregister(platform_device);
platform_driver_unregister(&platform_driver);
+ platform_profile_remove();
}
}
--
2.46.2
On Tue, 8 Oct 2024, Kurt Borja wrote: > This patch adds platform_profile support for Dell devices which implement > User Selectable Thermal Tables (USTT) that are meant to be controlled by > Alienware Command Center (AWCC). These devices may include newer Alienware > M-Series, Alienware X-Series and Dell's G-Series. This patch, was tested > by me on an Alienware x15 R1. > > It is suspected that Alienware Command Center manages thermal profiles > through the WMI interface, specifically through a device with identifier > \_SB_.AMW1.WMAX. This device was reverse engineered and the relevant > functionality is documented here [1]. This driver interacts with this > WMI device and thus is able to mimic AWCC's thermal profiles functionality > through the platform_profile API. In consequence the user would be able > to set and retrieve thermal profiles, which are just fan speed profiles. > > This driver was heavily inspired on inspur_platform_profile, special > thanks. > > Notes: > - Performance (FullSpeed) profile is a special profile which has it's own > entry in the Firmware Settings of the Alienware x15 R1. It also changes > the color of the F1 key. I suspect this behavior would be replicated in > other X-Series or M-Series laptops. > - G-Mode is a profile documented on [1] which mimics the behavior of > FullSpeed mode but it does not have an entry on the Firmware Settings of > the Alienware x15 R1, this may correspond to the G-Mode functionality on > G-Series laptops (activated by a special button) but I cannot test it. I > did not include this code in the driver as G-Mode causes unexpected > behavior on X-Series laptops. > > --- > v2: > - Moved functionality to alienware-wmi driver > - Added thermal and gmode quirks to add support based on dmi match > - Performance profile is now GMODE for devices that support it > - alienware_wmax_command now is insize agnostic to support new thermal > methods > --- > drivers/platform/x86/dell/Kconfig | 1 + > drivers/platform/x86/dell/alienware-wmi.c | 214 ++++++++++++++++++++-- > 2 files changed, 202 insertions(+), 13 deletions(-) > > diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig > index 68a49788a..b06d634cd 100644 > --- a/drivers/platform/x86/dell/Kconfig > +++ b/drivers/platform/x86/dell/Kconfig > @@ -21,6 +21,7 @@ config ALIENWARE_WMI > depends on LEDS_CLASS > depends on NEW_LEDS > depends on ACPI_WMI > + select ACPI_PLATFORM_PROFILE > help > This is a driver for controlling Alienware BIOS driven > features. It exposes an interface for controlling the AlienFX > diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c > index f5ee62ce1..47abb533c 100644 > --- a/drivers/platform/x86/dell/alienware-wmi.c > +++ b/drivers/platform/x86/dell/alienware-wmi.c > @@ -10,6 +10,7 @@ > #include <linux/acpi.h> > #include <linux/module.h> > #include <linux/platform_device.h> > +#include <linux/platform_profile.h> > #include <linux/dmi.h> > #include <linux/leds.h> > > @@ -25,6 +26,15 @@ > #define WMAX_METHOD_AMPLIFIER_CABLE 0x6 > #define WMAX_METHOD_DEEP_SLEEP_CONTROL 0x0B > #define WMAX_METHOD_DEEP_SLEEP_STATUS 0x0C > +#define WMAX_METHOD_THERMAL_INFORMATION 0x14 > +#define WMAX_METHOD_THERMAL_CONTROL 0x15 > + > +#define WMAX_THERMAL_QUIET 0xA3 > +#define WMAX_THERMAL_BALANCED 0xA0 > +#define WMAX_THERMAL_BALANCED_PERFORMANCE 0xA1 > +#define WMAX_THERMAL_PERFORMANCE 0xA4 > +#define WMAX_THERMAL_GMODE 0xAB > +#define WMAX_THERMAL_LOW_POWER 0xA5 > > MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>"); > MODULE_DESCRIPTION("Alienware special feature control"); > @@ -54,6 +64,8 @@ struct quirk_entry { > u8 hdmi_mux; > u8 amplifier; > u8 deepslp; > + u8 thermal; > + u8 gmode; > }; > > static struct quirk_entry *quirks; > @@ -64,6 +76,8 @@ static struct quirk_entry quirk_inspiron5675 = { > .hdmi_mux = 0, > .amplifier = 0, > .deepslp = 0, > + .thermal = 0, > + .gmode = 0, > }; > > static struct quirk_entry quirk_unknown = { > @@ -71,6 +85,8 @@ static struct quirk_entry quirk_unknown = { > .hdmi_mux = 0, > .amplifier = 0, > .deepslp = 0, > + .thermal = 0, > + .gmode = 0, > }; > > static struct quirk_entry quirk_x51_r1_r2 = { > @@ -78,6 +94,8 @@ static struct quirk_entry quirk_x51_r1_r2 = { > .hdmi_mux = 0, > .amplifier = 0, > .deepslp = 0, > + .thermal = 0, > + .gmode = 0, > }; > > static struct quirk_entry quirk_x51_r3 = { > @@ -85,6 +103,8 @@ static struct quirk_entry quirk_x51_r3 = { > .hdmi_mux = 0, > .amplifier = 1, > .deepslp = 0, > + .thermal = 0, > + .gmode = 0, > }; > > static struct quirk_entry quirk_asm100 = { > @@ -92,6 +112,8 @@ static struct quirk_entry quirk_asm100 = { > .hdmi_mux = 1, > .amplifier = 0, > .deepslp = 0, > + .thermal = 0, > + .gmode = 0, > }; > > static struct quirk_entry quirk_asm200 = { > @@ -99,6 +121,8 @@ static struct quirk_entry quirk_asm200 = { > .hdmi_mux = 1, > .amplifier = 0, > .deepslp = 1, > + .thermal = 0, > + .gmode = 0, > }; > > static struct quirk_entry quirk_asm201 = { > @@ -106,6 +130,17 @@ static struct quirk_entry quirk_asm201 = { > .hdmi_mux = 1, > .amplifier = 1, > .deepslp = 1, > + .thermal = 0, > + .gmode = 0, > +}; > + > +static struct quirk_entry quirk_x15_r1 = { > + .num_zones = 2, > + .hdmi_mux = 0, > + .amplifier = 0, > + .deepslp = 0, > + .thermal = 1, > + .gmode = 0, > }; > > static int __init dmi_matched(const struct dmi_system_id *dmi) > @@ -169,6 +204,15 @@ static const struct dmi_system_id alienware_quirks[] __initconst = { > }, > .driver_data = &quirk_asm201, > }, > + { > + .callback = dmi_matched, > + .ident = "Alienware x15 R1", > + .matches = { > + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), > + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R1") > + }, > + .driver_data = &quirk_x15_r1, > + }, > { > .callback = dmi_matched, > .ident = "Dell Inc. Inspiron 5675", > @@ -218,6 +262,7 @@ static struct platform_device *platform_device; > static struct device_attribute *zone_dev_attrs; > static struct attribute **zone_attrs; > static struct platform_zone *zone_data; > +static struct platform_profile_handler pp_handler; > > static struct platform_driver platform_driver = { > .driver = { > @@ -500,7 +545,7 @@ static void alienware_zone_exit(struct platform_device *dev) > kfree(zone_attrs); > } > > -static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args, > +static acpi_status alienware_wmax_command(void *in_args, size_t insize, > u32 command, int *out_data) > { > acpi_status status; > @@ -508,7 +553,7 @@ static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args, > struct acpi_buffer input; > struct acpi_buffer output; > > - input.length = (acpi_size) sizeof(*in_args); > + input.length = (acpi_size) insize; > input.pointer = in_args; > if (out_data) { > output.length = ACPI_ALLOCATE_BUFFER; > @@ -541,8 +586,8 @@ static ssize_t show_hdmi_cable(struct device *dev, > .arg = 0, > }; > status = > - alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE, > - (u32 *) &out_data); > + alienware_wmax_command(&in_args, sizeof(in_args), > + WMAX_METHOD_HDMI_CABLE, (u32 *) &out_data); > if (ACPI_SUCCESS(status)) { > if (out_data == 0) > return sysfs_emit(buf, "[unconnected] connected unknown\n"); > @@ -562,8 +607,8 @@ static ssize_t show_hdmi_source(struct device *dev, > .arg = 0, > }; > status = > - alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS, > - (u32 *) &out_data); > + alienware_wmax_command(&in_args, sizeof(in_args), > + WMAX_METHOD_HDMI_STATUS, (u32 *) &out_data); > > if (ACPI_SUCCESS(status)) { > if (out_data == 1) > @@ -589,7 +634,8 @@ static ssize_t toggle_hdmi_source(struct device *dev, > args.arg = 3; > pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf); > > - status = alienware_wmax_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL); > + status = alienware_wmax_command(&args, sizeof(args), > + WMAX_METHOD_HDMI_SOURCE, NULL); > > if (ACPI_FAILURE(status)) > pr_err("alienware-wmi: HDMI toggle failed: results: %u\n", > @@ -642,8 +688,8 @@ static ssize_t show_amplifier_status(struct device *dev, > .arg = 0, > }; > status = > - alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE, > - (u32 *) &out_data); > + alienware_wmax_command(&in_args, sizeof(in_args), > + WMAX_METHOD_AMPLIFIER_CABLE, (u32 *) &out_data); > if (ACPI_SUCCESS(status)) { > if (out_data == 0) > return sysfs_emit(buf, "[unconnected] connected unknown\n"); > @@ -694,8 +740,8 @@ static ssize_t show_deepsleep_status(struct device *dev, > struct wmax_basic_args in_args = { > .arg = 0, > }; > - status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS, > - (u32 *) &out_data); > + status = alienware_wmax_command(&in_args, sizeof(in_args), > + WMAX_METHOD_DEEP_SLEEP_STATUS, (u32 *) &out_data); > if (ACPI_SUCCESS(status)) { > if (out_data == 0) > return sysfs_emit(buf, "[disabled] s5 s5_s4\n"); > @@ -723,8 +769,8 @@ static ssize_t toggle_deepsleep(struct device *dev, > args.arg = 2; > pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf); > > - status = alienware_wmax_command(&args, WMAX_METHOD_DEEP_SLEEP_CONTROL, > - NULL); > + status = alienware_wmax_command(&args, sizeof(args), > + WMAX_METHOD_DEEP_SLEEP_CONTROL, NULL); > > if (ACPI_FAILURE(status)) > pr_err("alienware-wmi: deep sleep control failed: results: %u\n", > @@ -760,6 +806,140 @@ static int create_deepsleep(struct platform_device *dev) > return ret; > } > > +/* > + * Thermal Profile control > + * - Provides thermal profile control through the Platform Profile API > + */ > + Extra newline. > +static int platform_profile_get(struct platform_profile_handler *pprof, > + enum platform_profile_option *profile) > +{ > + acpi_status status; > + u32 in_args = 0x0B; Use a named define instead of a magic number. > + u32 out_data; > + > + status = alienware_wmax_command(&in_args, sizeof(in_args), > + WMAX_METHOD_THERMAL_INFORMATION, (u32 *) &out_data); Casting to the same type?? Also, alienware_wmax_command() inputs int * which makes the cast look even more odd?!? I can see there are pre-existing bogus (u32 *) casts too which would be nice to clean up in another patch. > + > + if (ACPI_FAILURE(status) || out_data < 0) u32 cannot be < 0?? Is this an indication of a problem in the error handling? > + return -EOPNOTSUPP; > + > + switch (out_data) { > + case WMAX_THERMAL_LOW_POWER: > + *profile = PLATFORM_PROFILE_LOW_POWER; > + break; > + case WMAX_THERMAL_QUIET: > + *profile = PLATFORM_PROFILE_QUIET; > + break; > + case WMAX_THERMAL_BALANCED: > + *profile = PLATFORM_PROFILE_BALANCED; > + break; > + case WMAX_THERMAL_BALANCED_PERFORMANCE: > + *profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE; > + break; > + case WMAX_THERMAL_PERFORMANCE: > + case WMAX_THERMAL_GMODE: > + *profile = PLATFORM_PROFILE_PERFORMANCE; > + break; > + default: > + return -ENODATA; > + } > + > + return 0; > +} > + > +#define SET_MASK(prof) ((prof << 8) | 1) Name these with two defines. One define for naming that magic 1 and another for the profile field, use GENMASK() + FIELD_PREP() for it. Also, there's no need for this to be macro so change it into a static function. > +static int platform_profile_set(struct platform_profile_handler *pprof, > + enum platform_profile_option profile) > +{ > + acpi_status status; > + u32 in_args; > + u32 out_data; > + > + switch (profile) { > + case PLATFORM_PROFILE_LOW_POWER: > + in_args = SET_MASK(WMAX_THERMAL_LOW_POWER); > + break; > + case PLATFORM_PROFILE_QUIET: > + in_args = SET_MASK(WMAX_THERMAL_QUIET); > + break; > + case PLATFORM_PROFILE_BALANCED: > + in_args = SET_MASK(WMAX_THERMAL_BALANCED); > + break; > + case PLATFORM_PROFILE_BALANCED_PERFORMANCE: > + in_args = SET_MASK(WMAX_THERMAL_BALANCED_PERFORMANCE); > + break; > + case PLATFORM_PROFILE_PERFORMANCE: > + in_args = SET_MASK(WMAX_THERMAL_PERFORMANCE); > + break; > + default: > + return -EOPNOTSUPP; > + } > + > + status = alienware_wmax_command(&in_args, sizeof(in_args), > + WMAX_METHOD_THERMAL_CONTROL, (u32 *) &out_data); Cast to same type. > + > + if (ACPI_FAILURE(status) || out_data < 0) u32 cannot be < 0. > + return -EOPNOTSUPP; > + > + return 0; > +} > + > +static int gmode_platform_profile_set(struct platform_profile_handler *pprof, > + enum platform_profile_option profile) > +{ > + acpi_status status; > + u32 in_args; > + u32 out_data; > + > + switch (profile) { > + case PLATFORM_PROFILE_LOW_POWER: > + in_args = SET_MASK(WMAX_THERMAL_LOW_POWER); > + break; > + case PLATFORM_PROFILE_QUIET: > + in_args = SET_MASK(WMAX_THERMAL_QUIET); > + break; > + case PLATFORM_PROFILE_BALANCED: > + in_args = SET_MASK(WMAX_THERMAL_BALANCED); > + break; > + case PLATFORM_PROFILE_BALANCED_PERFORMANCE: > + in_args = SET_MASK(WMAX_THERMAL_BALANCED_PERFORMANCE); > + break; > + case PLATFORM_PROFILE_PERFORMANCE: > + in_args = SET_MASK(WMAX_THERMAL_GMODE); > + break; > + default: > + return -EOPNOTSUPP; > + } > + > + status = alienware_wmax_command(&in_args, sizeof(in_args), > + WMAX_METHOD_THERMAL_CONTROL, (u32 *) &out_data); > + if (ACPI_FAILURE(status) || out_data < 0) The same two issues here with the types. > + return -EOPNOTSUPP; > + > + return 0; > +} > + > +static int create_platform_profile(void) > +{ > + pp_handler.profile_get = platform_profile_get; > + > + if (quirks->gmode > 0) > + pp_handler.profile_set = gmode_platform_profile_set; > + else > + pp_handler.profile_set = platform_profile_set; > + > + set_bit(PLATFORM_PROFILE_LOW_POWER, pp_handler.choices); > + set_bit(PLATFORM_PROFILE_QUIET, pp_handler.choices); > + set_bit(PLATFORM_PROFILE_BALANCED, pp_handler.choices); > + set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, pp_handler.choices); > + set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices); > + > + return platform_profile_register(&pp_handler); > +} > + > static int __init alienware_wmi_init(void) > { > int ret; > @@ -807,6 +987,12 @@ static int __init alienware_wmi_init(void) > goto fail_prep_deepsleep; > } > > + if (quirks->thermal > 0) { > + ret = create_platform_profile(); > + if (ret) > + goto fail_prep_thermal_profile; > + } > + > ret = alienware_zone_init(platform_device); > if (ret) > goto fail_prep_zones; > @@ -817,6 +1003,7 @@ static int __init alienware_wmi_init(void) > alienware_zone_exit(platform_device); > fail_prep_deepsleep: > fail_prep_amplifier: > +fail_prep_thermal_profile: > fail_prep_hdmi: > platform_device_del(platform_device); > fail_platform_device2: > @@ -836,6 +1023,7 @@ static void __exit alienware_wmi_exit(void) > remove_hdmi(platform_device); > platform_device_unregister(platform_device); > platform_driver_unregister(&platform_driver); > + platform_profile_remove(); IIRC, I once checked that this will lead to a crash if it wasn't created first (which in your driver isn't always the case). -- i.
Removed. > > +static int platform_profile_get(struct platform_profile_handler *pprof, > > + enum platform_profile_option *profile) > > +{ > > + acpi_status status; > > + u32 in_args = 0x0B; > > Use a named define instead of a magic number. > Done > > + u32 out_data; > > + > > + status = alienware_wmax_command(&in_args, sizeof(in_args), > > + WMAX_METHOD_THERMAL_INFORMATION, (u32 *) &out_data); > > Casting to the same type?? > > Also, alienware_wmax_command() inputs int * which makes the cast look even > more odd?!? > > I can see there are pre-existing bogus (u32 *) casts too which would be > nice to clean up in another patch. > Yes I thought it was odd but added them just in case. I can submit a patch cleaning those up after this one. > > + > > + if (ACPI_FAILURE(status) || out_data < 0) > > u32 cannot be < 0?? > > Is this an indication of a problem in the error handling? > Yes it was, thank you for catching it. > > + return -EOPNOTSUPP; > > + > > + switch (out_data) { > > + case WMAX_THERMAL_LOW_POWER: > > + *profile = PLATFORM_PROFILE_LOW_POWER; > > + break; > > + case WMAX_THERMAL_QUIET: > > + *profile = PLATFORM_PROFILE_QUIET; > > + break; > > + case WMAX_THERMAL_BALANCED: > > + *profile = PLATFORM_PROFILE_BALANCED; > > + break; > > + case WMAX_THERMAL_BALANCED_PERFORMANCE: > > + *profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE; > > + break; > > + case WMAX_THERMAL_PERFORMANCE: > > + case WMAX_THERMAL_GMODE: > > + *profile = PLATFORM_PROFILE_PERFORMANCE; > > + break; > > + default: > > + return -ENODATA; > > + } > > + > > + return 0; > > +} > > + > > +#define SET_MASK(prof) ((prof << 8) | 1) > > Name these with two defines. One define for naming that magic 1 and > another for the profile field, use GENMASK() + FIELD_PREP() for it. > > Also, there's no need for this to be macro so change it into a static > function. > Done. > > +static int platform_profile_set(struct platform_profile_handler *pprof, > > + enum platform_profile_option profile) > > +{ > > + acpi_status status; > > + u32 in_args; > > + u32 out_data; > > + > > + switch (profile) { > > + case PLATFORM_PROFILE_LOW_POWER: > > + in_args = SET_MASK(WMAX_THERMAL_LOW_POWER); > > + break; > > + case PLATFORM_PROFILE_QUIET: > > + in_args = SET_MASK(WMAX_THERMAL_QUIET); > > + break; > > + case PLATFORM_PROFILE_BALANCED: > > + in_args = SET_MASK(WMAX_THERMAL_BALANCED); > > + break; > > + case PLATFORM_PROFILE_BALANCED_PERFORMANCE: > > + in_args = SET_MASK(WMAX_THERMAL_BALANCED_PERFORMANCE); > > + break; > > + case PLATFORM_PROFILE_PERFORMANCE: > > + in_args = SET_MASK(WMAX_THERMAL_PERFORMANCE); > > + break; > > + default: > > + return -EOPNOTSUPP; > > + } > > + > > + status = alienware_wmax_command(&in_args, sizeof(in_args), > > + WMAX_METHOD_THERMAL_CONTROL, (u32 *) &out_data); > > Cast to same type. > > > + > > + if (ACPI_FAILURE(status) || out_data < 0) > > u32 cannot be < 0. > > > + return -EOPNOTSUPP; > > + > > + return 0; > > +} > > + > > +static int gmode_platform_profile_set(struct platform_profile_handler *pprof, > > + enum platform_profile_option profile) > > +{ > > + acpi_status status; > > + u32 in_args; > > + u32 out_data; > > + > > + switch (profile) { > > + case PLATFORM_PROFILE_LOW_POWER: > > + in_args = SET_MASK(WMAX_THERMAL_LOW_POWER); > > + break; > > + case PLATFORM_PROFILE_QUIET: > > + in_args = SET_MASK(WMAX_THERMAL_QUIET); > > + break; > > + case PLATFORM_PROFILE_BALANCED: > > + in_args = SET_MASK(WMAX_THERMAL_BALANCED); > > + break; > > + case PLATFORM_PROFILE_BALANCED_PERFORMANCE: > > + in_args = SET_MASK(WMAX_THERMAL_BALANCED_PERFORMANCE); > > + break; > > + case PLATFORM_PROFILE_PERFORMANCE: > > + in_args = SET_MASK(WMAX_THERMAL_GMODE); > > + break; > > + default: > > + return -EOPNOTSUPP; > > + } > > + > > + status = alienware_wmax_command(&in_args, sizeof(in_args), > > > + WMAX_METHOD_THERMAL_CONTROL, (u32 *) &out_data); > > + if (ACPI_FAILURE(status) || out_data < 0) > > The same two issues here with the types. > > > + return -EOPNOTSUPP; > > + > > + return 0; > > +} > > + > > +static int create_platform_profile(void) > > +{ > > + pp_handler.profile_get = platform_profile_get; > > + > > + if (quirks->gmode > 0) > > + pp_handler.profile_set = gmode_platform_profile_set; > > + else > > + pp_handler.profile_set = platform_profile_set; > > + > > + set_bit(PLATFORM_PROFILE_LOW_POWER, pp_handler.choices); > > + set_bit(PLATFORM_PROFILE_QUIET, pp_handler.choices); > > + set_bit(PLATFORM_PROFILE_BALANCED, pp_handler.choices); > > + set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, pp_handler.choices); > > + set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices); > > + > > + return platform_profile_register(&pp_handler); > > +} > > + > > static int __init alienware_wmi_init(void) > > { > > int ret; > > @@ -807,6 +987,12 @@ static int __init alienware_wmi_init(void) > > goto fail_prep_deepsleep; > > } > > > > + if (quirks->thermal > 0) { > > + ret = create_platform_profile(); > > + if (ret) > > + goto fail_prep_thermal_profile; > > + } > > + > > ret = alienware_zone_init(platform_device); > > if (ret) > > goto fail_prep_zones; > > @@ -817,6 +1003,7 @@ static int __init alienware_wmi_init(void) > > alienware_zone_exit(platform_device); > > fail_prep_deepsleep: > > fail_prep_amplifier: > > +fail_prep_thermal_profile: > > fail_prep_hdmi: > > platform_device_del(platform_device); > > fail_platform_device2: > > @@ -836,6 +1023,7 @@ static void __exit alienware_wmi_exit(void) > > remove_hdmi(platform_device); > > platform_device_unregister(platform_device); > > platform_driver_unregister(&platform_driver); > > + platform_profile_remove(); > > IIRC, I once checked that this will lead to a crash if it wasn't created > first (which in your driver isn't always the case). > Fixed. > -- > i. Thank you for your feedback. I will submit v3 with the corrections soon. Kurt
© 2016 - 2024 Red Hat, Inc.