[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20241205004730.2187107-2-kuurtb@gmail.com>
Date: Wed, 4 Dec 2024 21:47:31 -0300
From: Kurt Borja <kuurtb@...il.com>
To: kuurtb@...il.com
Cc: Dell.Client.Kernel@...l.com,
hdegoede@...hat.com,
ilpo.jarvinen@...ux.intel.com,
linux-kernel@...r.kernel.org,
mario.limonciello@....com,
platform-driver-x86@...r.kernel.org,
w_armin@....de
Subject: [RFC PATCH 20/21] platform-x86: Split the alienware-wmi module
Split the alienware-wmi module into three files, which correspond to the
different interfaces this module exposed. The new structure roughly
works like this:
- alienware-wmi-base.c: Manages DMI quirks and will initialize the
preferred WMI driver
- alienware-wmi-alienfx.c: Manages the sysfs interface exposed by the
"alienware-wmi" platform device
- alienware-wmi-awcc.c: Manages thermal control methods present on new
Alienware and Dell devices
These three parts already worked completely independently, so the split
is done seamlessly by copying and pasting.
Signed-off-by: Kurt Borja <kuurtb@...il.com>
---
drivers/platform/x86/dell/Makefile | 2 +
.../platform/x86/dell/alienware-wmi-alienfx.c | 531 ++++++++++++
.../platform/x86/dell/alienware-wmi-awcc.c | 282 +++++++
.../platform/x86/dell/alienware-wmi-base.c | 794 ------------------
4 files changed, 815 insertions(+), 794 deletions(-)
create mode 100644 drivers/platform/x86/dell/alienware-wmi-alienfx.c
create mode 100644 drivers/platform/x86/dell/alienware-wmi-awcc.c
diff --git a/drivers/platform/x86/dell/Makefile b/drivers/platform/x86/dell/Makefile
index 5e7496aeb070..54a592fd6ae6 100644
--- a/drivers/platform/x86/dell/Makefile
+++ b/drivers/platform/x86/dell/Makefile
@@ -6,6 +6,8 @@
obj-$(CONFIG_ALIENWARE_WMI) += alienware-wmi.o
alienware-wmi-objs := alienware-wmi-base.o
+alienware-wmi-y += alienware-wmi-alienfx.o
+alienware-wmi-y += alienware-wmi-awcc.o
obj-$(CONFIG_DCDBAS) += dcdbas.o
obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o
obj-$(CONFIG_DELL_RBTN) += dell-rbtn.o
diff --git a/drivers/platform/x86/dell/alienware-wmi-alienfx.c b/drivers/platform/x86/dell/alienware-wmi-alienfx.c
new file mode 100644
index 000000000000..d0291d1ba9bb
--- /dev/null
+++ b/drivers/platform/x86/dell/alienware-wmi-alienfx.c
@@ -0,0 +1,531 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Alienware AlienFX control
+ *
+ * Copyright (C) 2014 Dell Inc <Dell.Client.Kernel@...l.com>
+ */
+
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/wmi.h>
+#include "alienware-wmi.h"
+
+struct wmax_basic_args {
+ u8 arg;
+};
+
+/*
+ * Helpers used for zone control
+ */
+static int parse_rgb(const char *buf, struct color_platform *colors)
+{
+ long unsigned int rgb;
+ int ret;
+ union color_union {
+ struct color_platform cp;
+ int package;
+ } repackager;
+
+ ret = kstrtoul(buf, 16, &rgb);
+ if (ret)
+ return ret;
+
+ /* RGB triplet notation is 24-bit hexadecimal */
+ if (rgb > 0xFFFFFF)
+ return -EINVAL;
+
+ repackager.package = rgb & 0x0f0f0f0f;
+ pr_debug("alienware-wmi: r: %d g:%d b: %d\n",
+ repackager.cp.red, repackager.cp.green, repackager.cp.blue);
+ *colors = repackager.cp;
+ return 0;
+}
+
+/*
+ * Individual RGB zone control
+ */
+static ssize_t zone_show(struct device *dev, struct device_attribute *attr,
+ char *buf, u8 location)
+{
+ struct alienfx_priv *priv;
+ struct color_platform *colors;
+
+ priv = dev_get_drvdata(dev);
+ colors = &priv->colors[location];
+
+ return sprintf(buf, "red: %d, green: %d, blue: %d\n",
+ colors->red, colors->green, colors->blue);
+
+}
+
+static ssize_t zone_set(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count, u8 location)
+{
+ struct alienfx_priv *priv;
+ struct alienfx_platdata *pdata;
+ struct color_platform *colors;
+ int ret;
+
+ priv = dev_get_drvdata(dev);
+ pdata = dev_get_platdata(dev);
+
+ colors = &priv->colors[location];
+ ret = parse_rgb(buf, colors);
+ if (ret)
+ return ret;
+
+ ret = pdata->ops.upd_led(priv, pdata->wdev, location);
+
+ return ret ? ret : count;
+}
+
+#define ALIENWARE_ZONE_SHOW_FUNC(_num) \
+ static ssize_t zone0##_num##_show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+ { \
+ return zone_show(dev, attr, buf, _num); \
+ }
+
+#define ALIENWARE_ZONE_STORE_FUNC(_num) \
+ static ssize_t zone0##_num##_store(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+ { \
+ return zone_set(dev, attr, buf, count, _num); \
+ }
+
+#define ALIENWARE_ZONE_ATTR(_num) \
+ ALIENWARE_ZONE_SHOW_FUNC(_num) \
+ ALIENWARE_ZONE_STORE_FUNC(_num) \
+ static DEVICE_ATTR_RW(zone0##_num)
+
+ALIENWARE_ZONE_ATTR(0);
+ALIENWARE_ZONE_ATTR(1);
+ALIENWARE_ZONE_ATTR(2);
+ALIENWARE_ZONE_ATTR(3);
+
+/*
+ * Lighting control state device attribute (Global)
+ */
+static ssize_t lighting_control_state_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct alienfx_priv *priv;
+
+ priv = dev_get_drvdata(dev);
+
+ if (priv->lighting_control_state == LEGACY_BOOTING)
+ return sysfs_emit(buf, "[booting] running suspend\n");
+ else if (priv->lighting_control_state == LEGACY_SUSPEND)
+ return sysfs_emit(buf, "booting running [suspend]\n");
+ return sysfs_emit(buf, "booting [running] suspend\n");
+}
+
+static ssize_t lighting_control_state_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct alienfx_priv *priv;
+ struct alienfx_platdata *pdata;
+ u8 val;
+
+ priv = dev_get_drvdata(dev);
+ pdata = dev_get_platdata(dev);
+
+ if (strcmp(buf, "booting\n") == 0)
+ val = LEGACY_BOOTING;
+ else if (strcmp(buf, "suspend\n") == 0)
+ val = LEGACY_SUSPEND;
+ else
+ val = pdata->running_code;
+
+ priv->lighting_control_state = val;
+ pr_debug("alienware-wmi: updated control state to %d\n",
+ priv->lighting_control_state);
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(lighting_control_state);
+
+static umode_t zone_attr_visible(struct kobject *kobj,
+ struct attribute *attr, int n)
+{
+ struct device *dev;
+ struct alienfx_platdata *pdata;
+
+ dev = container_of(kobj, struct device, kobj);
+ pdata = dev_get_platdata(dev);
+
+ return n < pdata->num_zones + 1 ? 0644 : 0;
+}
+
+static bool zone_group_visible(struct kobject *kobj)
+{
+ struct device *dev;
+ struct alienfx_platdata *pdata;
+
+ dev = container_of(kobj, struct device, kobj);
+ pdata = dev_get_platdata(dev);
+
+ return pdata->num_zones > 0;
+}
+DEFINE_SYSFS_GROUP_VISIBLE(zone);
+
+static struct attribute *zone_attrs[] = {
+ &dev_attr_lighting_control_state.attr,
+ &dev_attr_zone00.attr,
+ &dev_attr_zone01.attr,
+ &dev_attr_zone02.attr,
+ &dev_attr_zone03.attr,
+ NULL
+};
+
+static struct attribute_group zone_attribute_group = {
+ .name = "rgb_zones",
+ .is_visible = SYSFS_GROUP_VISIBLE(zone),
+ .attrs = zone_attrs,
+};
+
+/*
+ * LED Brightness (Global)
+ */
+static void global_led_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct alienfx_priv *priv;
+ struct alienfx_platdata *pdata;
+ int ret;
+
+ priv = container_of(led_cdev, struct alienfx_priv, global_led);
+ pdata = dev_get_platdata(&priv->pdev->dev);
+
+ priv->global_brightness = brightness;
+
+ ret = pdata->ops.upd_brightness(priv, pdata->wdev, brightness);
+ if (ret)
+ pr_err("LED brightness update failed\n");
+}
+
+static enum led_brightness global_led_get(struct led_classdev *led_cdev)
+{
+ struct alienfx_priv *priv;
+
+ priv = container_of(led_cdev, struct alienfx_priv, global_led);
+
+ return priv->global_brightness;
+}
+
+/*
+ * The HDMI mux sysfs node indicates the status of the HDMI input mux.
+ * It can toggle between standard system GPU output and HDMI input.
+ */
+static ssize_t show_hdmi_cable(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct alienfx_platdata *pdata;
+ acpi_status status;
+ u32 out_data;
+ struct wmax_basic_args in_args = {
+ .arg = 0,
+ };
+
+ pdata = dev_get_platdata(dev);
+
+ status = alienware_wmi_command(pdata->wdev, WMAX_METHOD_HDMI_CABLE,
+ &in_args, sizeof(in_args), &out_data);
+
+ if (ACPI_SUCCESS(status)) {
+ if (out_data == 0)
+ return sysfs_emit(buf, "[unconnected] connected unknown\n");
+ else if (out_data == 1)
+ return sysfs_emit(buf, "unconnected [connected] unknown\n");
+ }
+ pr_err("alienware-wmi: unknown HDMI cable status: %d\n", status);
+ return sysfs_emit(buf, "unconnected connected [unknown]\n");
+}
+
+static ssize_t show_hdmi_source(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct alienfx_platdata *pdata;
+ acpi_status status;
+ u32 out_data;
+ struct wmax_basic_args in_args = {
+ .arg = 0,
+ };
+
+ pdata = dev_get_platdata(dev);
+
+ status = alienware_wmi_command(pdata->wdev, WMAX_METHOD_HDMI_STATUS,
+ &in_args, sizeof(in_args), &out_data);
+
+ if (ACPI_SUCCESS(status)) {
+ if (out_data == 1)
+ return sysfs_emit(buf, "[input] gpu unknown\n");
+ else if (out_data == 2)
+ return sysfs_emit(buf, "input [gpu] unknown\n");
+ }
+ pr_err("alienware-wmi: unknown HDMI source status: %u\n", status);
+ return sysfs_emit(buf, "input gpu [unknown]\n");
+}
+
+static ssize_t toggle_hdmi_source(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct alienfx_platdata *pdata;
+ acpi_status status;
+ struct wmax_basic_args args;
+
+ pdata = dev_get_platdata(dev);
+
+ if (strcmp(buf, "gpu\n") == 0)
+ args.arg = 1;
+ else if (strcmp(buf, "input\n") == 0)
+ args.arg = 2;
+ else
+ args.arg = 3;
+ pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf);
+
+ status = alienware_wmi_command(pdata->wdev, WMAX_METHOD_HDMI_SOURCE,
+ &args, sizeof(args), NULL);
+
+ if (ACPI_FAILURE(status))
+ pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
+ status);
+ return count;
+}
+
+static DEVICE_ATTR(cable, S_IRUGO, show_hdmi_cable, NULL);
+static DEVICE_ATTR(source, S_IRUGO | S_IWUSR, show_hdmi_source,
+ toggle_hdmi_source);
+
+static bool hdmi_group_visible(struct kobject *kobj)
+{
+ struct device *dev;
+ struct alienfx_platdata *pdata;
+
+ dev = container_of(kobj, struct device, kobj);
+ pdata = dev_get_platdata(dev);
+
+ return pdata->hdmi_mux;
+}
+DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(hdmi);
+
+static struct attribute *hdmi_attrs[] = {
+ &dev_attr_cable.attr,
+ &dev_attr_source.attr,
+ NULL,
+};
+
+static const struct attribute_group hdmi_attribute_group = {
+ .name = "hdmi",
+ .is_visible = SYSFS_GROUP_VISIBLE(hdmi),
+ .attrs = hdmi_attrs,
+};
+
+/*
+ * Alienware GFX amplifier support
+ * - Currently supports reading cable status
+ * - Leaving expansion room to possibly support dock/undock events later
+ */
+static ssize_t show_amplifier_status(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct alienfx_platdata *pdata;
+ acpi_status status;
+ u32 out_data;
+ struct wmax_basic_args in_args = {
+ .arg = 0,
+ };
+
+ pdata = dev_get_platdata(dev);
+
+ status = alienware_wmi_command(pdata->wdev, WMAX_METHOD_AMPLIFIER_CABLE,
+ &in_args, sizeof(in_args), &out_data);
+ if (ACPI_SUCCESS(status)) {
+ if (out_data == 0)
+ return sysfs_emit(buf, "[unconnected] connected unknown\n");
+ else if (out_data == 1)
+ return sysfs_emit(buf, "unconnected [connected] unknown\n");
+ }
+ pr_err("alienware-wmi: unknown amplifier cable status: %d\n", status);
+ return sysfs_emit(buf, "unconnected connected [unknown]\n");
+}
+
+static DEVICE_ATTR(status, S_IRUGO, show_amplifier_status, NULL);
+
+static bool amplifier_group_visible(struct kobject *kobj)
+{
+ struct device *dev;
+ struct alienfx_platdata *pdata;
+
+ dev = container_of(kobj, struct device, kobj);
+ pdata = dev_get_platdata(dev);
+
+ return pdata->amplifier;
+}
+DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(amplifier);
+
+static struct attribute *amplifier_attrs[] = {
+ &dev_attr_status.attr,
+ NULL,
+};
+
+static const struct attribute_group amplifier_attribute_group = {
+ .name = "amplifier",
+ .is_visible = SYSFS_GROUP_VISIBLE(amplifier),
+ .attrs = amplifier_attrs,
+};
+
+/*
+ * Deep Sleep Control support
+ * - Modifies BIOS setting for deep sleep control allowing extra wakeup events
+ */
+static ssize_t show_deepsleep_status(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct alienfx_platdata *pdata;
+ acpi_status status;
+ u32 out_data;
+ struct wmax_basic_args in_args = {
+ .arg = 0,
+ };
+
+ pdata = dev_get_platdata(dev);
+
+ status = alienware_wmi_command(pdata->wdev, WMAX_METHOD_DEEP_SLEEP_STATUS,
+ &in_args, sizeof(in_args), &out_data);
+ if (ACPI_SUCCESS(status)) {
+ if (out_data == 0)
+ return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
+ else if (out_data == 1)
+ return sysfs_emit(buf, "disabled [s5] s5_s4\n");
+ else if (out_data == 2)
+ return sysfs_emit(buf, "disabled s5 [s5_s4]\n");
+ }
+ pr_err("alienware-wmi: unknown deep sleep status: %d\n", status);
+ return sysfs_emit(buf, "disabled s5 s5_s4 [unknown]\n");
+}
+
+static ssize_t toggle_deepsleep(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct alienfx_platdata *pdata;
+ acpi_status status;
+ struct wmax_basic_args args;
+
+ pdata = dev_get_platdata(dev);
+
+ if (strcmp(buf, "disabled\n") == 0)
+ args.arg = 0;
+ else if (strcmp(buf, "s5\n") == 0)
+ args.arg = 1;
+ else
+ args.arg = 2;
+ pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf);
+
+ status = alienware_wmi_command(pdata->wdev, WMAX_METHOD_DEEP_SLEEP_CONTROL,
+ &args, sizeof(args), NULL);
+
+ if (ACPI_FAILURE(status))
+ pr_err("alienware-wmi: deep sleep control failed: results: %u\n",
+ status);
+ return count;
+}
+
+static DEVICE_ATTR(deepsleep, S_IRUGO | S_IWUSR, show_deepsleep_status, toggle_deepsleep);
+
+static bool deepsleep_group_visible(struct kobject *kobj)
+{
+ struct device *dev;
+ struct alienfx_platdata *pdata;
+
+ dev = container_of(kobj, struct device, kobj);
+ pdata = dev_get_platdata(dev);
+
+ return pdata->deepslp;
+}
+DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(deepsleep);
+
+static struct attribute *deepsleep_attrs[] = {
+ &dev_attr_deepsleep.attr,
+ NULL,
+};
+
+static const struct attribute_group deepsleep_attribute_group = {
+ .name = "deepsleep",
+ .is_visible = SYSFS_GROUP_VISIBLE(deepsleep),
+ .attrs = deepsleep_attrs,
+};
+
+/*
+ * Platform Driver
+ */
+static int alienfx_probe(struct platform_device *pdev)
+{
+ struct alienfx_priv *priv;
+ struct alienfx_platdata *pdata;
+ struct led_classdev *leds;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ platform_set_drvdata(pdev, priv);
+
+ priv->pdev = pdev;
+
+ pdata = dev_get_platdata(&pdev->dev);
+
+ priv->lighting_control_state = pdata->running_code;
+
+ leds = &priv->global_led;
+ leds->name = "alienware::global_brightness";
+ leds->brightness_set = global_led_set;
+ leds->brightness_get = global_led_get;
+ leds->max_brightness = 0x0F;
+
+ priv->global_brightness = priv->global_led.max_brightness;
+
+ return devm_led_classdev_register(&pdev->dev, &priv->global_led);
+}
+
+static const struct attribute_group *alienfx_groups[] = {
+ &zone_attribute_group,
+ &hdmi_attribute_group,
+ &lifier_attribute_group,
+ &deepsleep_attribute_group,
+ NULL
+};
+
+static struct platform_driver platform_driver = {
+ .driver = {
+ .name = "alienware-wmi",
+ .dev_groups = alienfx_groups,
+ },
+ .probe = alienfx_probe,
+};
+
+int alienfx_wmi_init(struct alienfx_platdata *pdata)
+{
+ struct platform_device *pdev;
+
+ pdev = platform_create_bundle(&platform_driver, alienfx_probe, NULL, 0,
+ pdata, sizeof(*pdata));
+
+ dev_set_drvdata(&pdata->wdev->dev, pdev);
+
+ return PTR_ERR_OR_ZERO(pdev);
+}
+
+void alienfx_wmi_exit(struct wmi_device *wdev)
+{
+ struct platform_device *pdev;
+
+ pdev = dev_get_drvdata(&wdev->dev);
+
+ platform_device_unregister(pdev);
+ platform_driver_unregister(&platform_driver);
+}
diff --git a/drivers/platform/x86/dell/alienware-wmi-awcc.c b/drivers/platform/x86/dell/alienware-wmi-awcc.c
new file mode 100644
index 000000000000..115ef8436ae3
--- /dev/null
+++ b/drivers/platform/x86/dell/alienware-wmi-awcc.c
@@ -0,0 +1,282 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Alienware AlienFX control
+ *
+ * Copyright (C) 2024 Kurt Borja <kuurtb@...il.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/platform_device.h>
+#include <linux/platform_profile.h>
+#include <linux/wmi.h>
+#include "alienware-wmi.h"
+
+#define WMAX_METHOD_THERMAL_INFORMATION 0x14
+#define WMAX_METHOD_THERMAL_CONTROL 0x15
+#define WMAX_METHOD_GAME_SHIFT_STATUS 0x25
+
+#define WMAX_THERMAL_MODE_GMODE 0xAB
+
+#define WMAX_FAILURE_CODE 0xFFFFFFFF
+#define WMAX_THERMAL_TABLE_MASK GENMASK(7, 4)
+#define WMAX_THERMAL_MODE_MASK GENMASK(3, 0)
+#define WMAX_SENSOR_ID_MASK BIT(8)
+
+enum WMAX_THERMAL_INFORMATION_OPERATIONS {
+ WMAX_OPERATION_SYS_DESCRIPTION = 0x02,
+ WMAX_OPERATION_LIST_IDS = 0x03,
+ WMAX_OPERATION_CURRENT_PROFILE = 0x0B,
+};
+
+enum WMAX_THERMAL_CONTROL_OPERATIONS {
+ WMAX_OPERATION_ACTIVATE_PROFILE = 0x01,
+};
+
+enum WMAX_GAME_SHIFT_STATUS_OPERATIONS {
+ WMAX_OPERATION_TOGGLE_GAME_SHIFT = 0x01,
+ WMAX_OPERATION_GET_GAME_SHIFT_STATUS = 0x02,
+};
+
+enum WMAX_THERMAL_TABLES {
+ WMAX_THERMAL_TABLE_BASIC = 0x90,
+ WMAX_THERMAL_TABLE_USTT = 0xA0,
+};
+
+static const enum platform_profile_option wmax_mode_to_platform_profile[THERMAL_MODE_LAST] = {
+ [THERMAL_MODE_USTT_BALANCED] = PLATFORM_PROFILE_BALANCED,
+ [THERMAL_MODE_USTT_BALANCED_PERFORMANCE] = PLATFORM_PROFILE_BALANCED_PERFORMANCE,
+ [THERMAL_MODE_USTT_COOL] = PLATFORM_PROFILE_COOL,
+ [THERMAL_MODE_USTT_QUIET] = PLATFORM_PROFILE_QUIET,
+ [THERMAL_MODE_USTT_PERFORMANCE] = PLATFORM_PROFILE_PERFORMANCE,
+ [THERMAL_MODE_USTT_LOW_POWER] = PLATFORM_PROFILE_LOW_POWER,
+ [THERMAL_MODE_BASIC_QUIET] = PLATFORM_PROFILE_QUIET,
+ [THERMAL_MODE_BASIC_BALANCED] = PLATFORM_PROFILE_BALANCED,
+ [THERMAL_MODE_BASIC_BALANCED_PERFORMANCE] = PLATFORM_PROFILE_BALANCED_PERFORMANCE,
+ [THERMAL_MODE_BASIC_PERFORMANCE] = PLATFORM_PROFILE_PERFORMANCE,
+};
+
+struct wmax_u32_args {
+ u8 operation;
+ u8 arg1;
+ u8 arg2;
+ u8 arg3;
+};
+
+/*
+ * Thermal Profile control
+ * - Provides thermal profile control through the Platform Profile API
+ */
+static bool is_wmax_thermal_code(u32 code)
+{
+ if (code & WMAX_SENSOR_ID_MASK)
+ return false;
+
+ if ((code & WMAX_THERMAL_MODE_MASK) >= THERMAL_MODE_LAST)
+ return false;
+
+ if ((code & WMAX_THERMAL_TABLE_MASK) == WMAX_THERMAL_TABLE_BASIC &&
+ (code & WMAX_THERMAL_MODE_MASK) >= THERMAL_MODE_BASIC_QUIET)
+ return true;
+
+ if ((code & WMAX_THERMAL_TABLE_MASK) == WMAX_THERMAL_TABLE_USTT &&
+ (code & WMAX_THERMAL_MODE_MASK) <= THERMAL_MODE_USTT_LOW_POWER)
+ return true;
+
+ return false;
+}
+
+static int wmax_thermal_information(struct wmi_device *wdev, u8 operation,
+ u8 arg, u32 *out_data)
+{
+ acpi_status status;
+ struct wmax_u32_args in_args = {
+ .operation = operation,
+ .arg1 = arg,
+ .arg2 = 0,
+ .arg3 = 0,
+ };
+
+ status = alienware_wmi_command(wdev, WMAX_METHOD_THERMAL_INFORMATION,
+ &in_args, sizeof(in_args), out_data);
+
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ if (*out_data == WMAX_FAILURE_CODE)
+ return -EBADRQC;
+
+ return 0;
+}
+
+static int wmax_thermal_control(struct wmi_device *wdev, u8 profile)
+{
+ acpi_status status;
+ struct wmax_u32_args in_args = {
+ .operation = WMAX_OPERATION_ACTIVATE_PROFILE,
+ .arg1 = profile,
+ .arg2 = 0,
+ .arg3 = 0,
+ };
+ u32 out_data;
+
+ status = alienware_wmi_command(wdev, WMAX_METHOD_THERMAL_CONTROL,
+ &in_args, sizeof(in_args), &out_data);
+
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ if (out_data == WMAX_FAILURE_CODE)
+ return -EBADRQC;
+
+ return 0;
+}
+
+static int wmax_game_shift_status(struct wmi_device *wdev, u8 operation,
+ u32 *out_data)
+{
+ acpi_status status;
+ struct wmax_u32_args in_args = {
+ .operation = operation,
+ .arg1 = 0,
+ .arg2 = 0,
+ .arg3 = 0,
+ };
+
+ status = alienware_wmi_command(wdev, WMAX_METHOD_GAME_SHIFT_STATUS,
+ &in_args, sizeof(in_args), out_data);
+
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ if (*out_data == WMAX_FAILURE_CODE)
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+static int thermal_profile_get(struct platform_profile_handler *pprof,
+ enum platform_profile_option *profile)
+{
+ struct awcc_priv *priv;
+ u32 out_data;
+ int ret;
+
+ priv = container_of(pprof, struct awcc_priv, pp_handler);
+
+ ret = wmax_thermal_information(priv->wdev, WMAX_OPERATION_CURRENT_PROFILE,
+ 0, &out_data);
+
+ if (ret < 0)
+ return ret;
+
+ if (out_data == WMAX_THERMAL_MODE_GMODE) {
+ *profile = PLATFORM_PROFILE_PERFORMANCE;
+ return 0;
+ }
+
+ if (!is_wmax_thermal_code(out_data))
+ return -ENODATA;
+
+ out_data &= WMAX_THERMAL_MODE_MASK;
+ *profile = wmax_mode_to_platform_profile[out_data];
+
+ return 0;
+}
+
+static int thermal_profile_set(struct platform_profile_handler *pprof,
+ enum platform_profile_option profile)
+{
+ struct awcc_priv *priv;
+
+ priv = container_of(pprof, struct awcc_priv, pp_handler);
+
+ if (priv->has_gmode) {
+ u32 gmode_status;
+ int ret;
+
+ ret = wmax_game_shift_status(priv->wdev,
+ WMAX_OPERATION_GET_GAME_SHIFT_STATUS,
+ &gmode_status);
+
+ if (ret < 0)
+ return ret;
+
+ if ((profile == PLATFORM_PROFILE_PERFORMANCE && !gmode_status) ||
+ (profile != PLATFORM_PROFILE_PERFORMANCE && gmode_status)) {
+ ret = wmax_game_shift_status(priv->wdev,
+ WMAX_OPERATION_TOGGLE_GAME_SHIFT,
+ &gmode_status);
+
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ return wmax_thermal_control(priv->wdev,
+ priv->supported_thermal_profiles[profile]);
+}
+
+int create_thermal_profile(struct wmi_device *wdev, bool has_gmode)
+{
+ struct awcc_priv *priv;
+ u32 out_data;
+ u8 sys_desc[4];
+ u32 first_mode;
+ enum wmax_thermal_mode mode;
+ enum platform_profile_option profile;
+ int ret;
+
+ priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
+ dev_set_drvdata(&wdev->dev, priv);
+
+ priv->wdev = wdev;
+
+ ret = wmax_thermal_information(wdev, WMAX_OPERATION_SYS_DESCRIPTION,
+ 0, (u32 *) &sys_desc);
+ if (ret < 0)
+ return ret;
+
+ first_mode = sys_desc[0] + sys_desc[1];
+
+ for (u32 i = 0; i < sys_desc[3]; i++) {
+ ret = wmax_thermal_information(wdev, WMAX_OPERATION_LIST_IDS,
+ i + first_mode, &out_data);
+
+ if (ret == -EIO)
+ return ret;
+
+ if (ret == -EBADRQC)
+ break;
+
+ if (!is_wmax_thermal_code(out_data))
+ continue;
+
+ mode = out_data & WMAX_THERMAL_MODE_MASK;
+ profile = wmax_mode_to_platform_profile[mode];
+ priv->supported_thermal_profiles[profile] = out_data;
+
+ set_bit(profile, priv->pp_handler.choices);
+ }
+
+ if (bitmap_empty(priv->pp_handler.choices, PLATFORM_PROFILE_LAST))
+ return -ENODEV;
+
+ if (has_gmode) {
+ priv->has_gmode = true;
+ priv->supported_thermal_profiles[PLATFORM_PROFILE_PERFORMANCE] =
+ WMAX_THERMAL_MODE_GMODE;
+
+ set_bit(PLATFORM_PROFILE_PERFORMANCE, priv->pp_handler.choices);
+ }
+
+ priv->pp_handler.profile_get = thermal_profile_get;
+ priv->pp_handler.profile_set = thermal_profile_set;
+
+ return platform_profile_register(&priv->pp_handler);
+}
+
+void remove_thermal_profile(void)
+{
+ platform_profile_remove();
+}
diff --git a/drivers/platform/x86/dell/alienware-wmi-base.c b/drivers/platform/x86/dell/alienware-wmi-base.c
index 4165eb0d0bf5..7cc6bb3dc0d7 100644
--- a/drivers/platform/x86/dell/alienware-wmi-base.c
+++ b/drivers/platform/x86/dell/alienware-wmi-base.c
@@ -8,24 +8,12 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/acpi.h>
-#include <linux/bitfield.h>
-#include <linux/bits.h>
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/platform_profile.h>
#include <linux/dmi.h>
-#include <linux/leds.h>
#include <linux/wmi.h>
#include "alienware-wmi.h"
-#define WMAX_METHOD_THERMAL_INFORMATION 0x14
-#define WMAX_METHOD_THERMAL_CONTROL 0x15
-#define WMAX_METHOD_GAME_SHIFT_STATUS 0x25
-
-#define WMAX_THERMAL_MODE_GMODE 0xAB
-
-#define WMAX_FAILURE_CODE 0xFFFFFFFF
-
MODULE_AUTHOR("Mario Limonciello <mario.limonciello@...look.com>");
MODULE_DESCRIPTION("Alienware special feature control");
MODULE_LICENSE("GPL");
@@ -43,39 +31,6 @@ enum INTERFACE_FLAGS {
WMAX,
};
-enum WMAX_THERMAL_INFORMATION_OPERATIONS {
- WMAX_OPERATION_SYS_DESCRIPTION = 0x02,
- WMAX_OPERATION_LIST_IDS = 0x03,
- WMAX_OPERATION_CURRENT_PROFILE = 0x0B,
-};
-
-enum WMAX_THERMAL_CONTROL_OPERATIONS {
- WMAX_OPERATION_ACTIVATE_PROFILE = 0x01,
-};
-
-enum WMAX_GAME_SHIFT_STATUS_OPERATIONS {
- WMAX_OPERATION_TOGGLE_GAME_SHIFT = 0x01,
- WMAX_OPERATION_GET_GAME_SHIFT_STATUS = 0x02,
-};
-
-enum WMAX_THERMAL_TABLES {
- WMAX_THERMAL_TABLE_BASIC = 0x90,
- WMAX_THERMAL_TABLE_USTT = 0xA0,
-};
-
-static const enum platform_profile_option wmax_mode_to_platform_profile[THERMAL_MODE_LAST] = {
- [THERMAL_MODE_USTT_BALANCED] = PLATFORM_PROFILE_BALANCED,
- [THERMAL_MODE_USTT_BALANCED_PERFORMANCE] = PLATFORM_PROFILE_BALANCED_PERFORMANCE,
- [THERMAL_MODE_USTT_COOL] = PLATFORM_PROFILE_COOL,
- [THERMAL_MODE_USTT_QUIET] = PLATFORM_PROFILE_QUIET,
- [THERMAL_MODE_USTT_PERFORMANCE] = PLATFORM_PROFILE_PERFORMANCE,
- [THERMAL_MODE_USTT_LOW_POWER] = PLATFORM_PROFILE_LOW_POWER,
- [THERMAL_MODE_BASIC_QUIET] = PLATFORM_PROFILE_QUIET,
- [THERMAL_MODE_BASIC_BALANCED] = PLATFORM_PROFILE_BALANCED,
- [THERMAL_MODE_BASIC_BALANCED_PERFORMANCE] = PLATFORM_PROFILE_BALANCED_PERFORMANCE,
- [THERMAL_MODE_BASIC_PERFORMANCE] = PLATFORM_PROFILE_PERFORMANCE,
-};
-
struct quirk_entry {
u8 num_zones;
u8 hdmi_mux;
@@ -332,17 +287,6 @@ static const struct dmi_system_id alienware_quirks[] __initconst = {
{}
};
-struct wmax_basic_args {
- u8 arg;
-};
-
-struct wmax_u32_args {
- u8 operation;
- u8 arg1;
- u8 arg2;
- u8 arg3;
-};
-
static u8 interface;
static struct wmi_driver *preferred_wmi_driver;
@@ -372,744 +316,6 @@ acpi_status alienware_wmi_command(struct wmi_device *wdev, u32 method_id,
return ret;
}
-/*
- * Helpers used for zone control
- */
-static int parse_rgb(const char *buf, struct color_platform *colors)
-{
- long unsigned int rgb;
- int ret;
- union color_union {
- struct color_platform cp;
- int package;
- } repackager;
-
- ret = kstrtoul(buf, 16, &rgb);
- if (ret)
- return ret;
-
- /* RGB triplet notation is 24-bit hexadecimal */
- if (rgb > 0xFFFFFF)
- return -EINVAL;
-
- repackager.package = rgb & 0x0f0f0f0f;
- pr_debug("alienware-wmi: r: %d g:%d b: %d\n",
- repackager.cp.red, repackager.cp.green, repackager.cp.blue);
- *colors = repackager.cp;
- return 0;
-}
-
-/*
- * Individual RGB zone control
- */
-static ssize_t zone_show(struct device *dev, struct device_attribute *attr,
- char *buf, u8 location)
-{
- struct alienfx_priv *priv;
- struct color_platform *colors;
-
- priv = dev_get_drvdata(dev);
- colors = &priv->colors[location];
-
- return sprintf(buf, "red: %d, green: %d, blue: %d\n",
- colors->red, colors->green, colors->blue);
-
-}
-
-static ssize_t zone_set(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count, u8 location)
-{
- struct alienfx_priv *priv;
- struct alienfx_platdata *pdata;
- struct color_platform *colors;
- int ret;
-
- priv = dev_get_drvdata(dev);
- pdata = dev_get_platdata(dev);
-
- colors = &priv->colors[location];
- ret = parse_rgb(buf, colors);
- if (ret)
- return ret;
-
- ret = pdata->ops.upd_led(priv, pdata->wdev, location);
-
- return ret ? ret : count;
-}
-
-#define ALIENWARE_ZONE_SHOW_FUNC(_num) \
- static ssize_t zone0##_num##_show(struct device *dev, \
- struct device_attribute *attr, \
- char *buf) \
- { \
- return zone_show(dev, attr, buf, _num); \
- }
-
-#define ALIENWARE_ZONE_STORE_FUNC(_num) \
- static ssize_t zone0##_num##_store(struct device *dev, \
- struct device_attribute *attr, \
- const char *buf, size_t count) \
- { \
- return zone_set(dev, attr, buf, count, _num); \
- }
-
-#define ALIENWARE_ZONE_ATTR(_num) \
- ALIENWARE_ZONE_SHOW_FUNC(_num) \
- ALIENWARE_ZONE_STORE_FUNC(_num) \
- static DEVICE_ATTR_RW(zone0##_num)
-
-ALIENWARE_ZONE_ATTR(0);
-ALIENWARE_ZONE_ATTR(1);
-ALIENWARE_ZONE_ATTR(2);
-ALIENWARE_ZONE_ATTR(3);
-
-/*
- * Lighting control state device attribute (Global)
- */
-static ssize_t lighting_control_state_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct alienfx_priv *priv;
-
- priv = dev_get_drvdata(dev);
-
- if (priv->lighting_control_state == LEGACY_BOOTING)
- return sysfs_emit(buf, "[booting] running suspend\n");
- else if (priv->lighting_control_state == LEGACY_SUSPEND)
- return sysfs_emit(buf, "booting running [suspend]\n");
- return sysfs_emit(buf, "booting [running] suspend\n");
-}
-
-static ssize_t lighting_control_state_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct alienfx_priv *priv;
- struct alienfx_platdata *pdata;
- u8 val;
-
- priv = dev_get_drvdata(dev);
- pdata = dev_get_platdata(dev);
-
- if (strcmp(buf, "booting\n") == 0)
- val = LEGACY_BOOTING;
- else if (strcmp(buf, "suspend\n") == 0)
- val = LEGACY_SUSPEND;
- else
- val = pdata->running_code;
-
- priv->lighting_control_state = val;
- pr_debug("alienware-wmi: updated control state to %d\n",
- priv->lighting_control_state);
-
- return count;
-}
-
-static DEVICE_ATTR_RW(lighting_control_state);
-
-static umode_t zone_attr_visible(struct kobject *kobj,
- struct attribute *attr, int n)
-{
- struct device *dev;
- struct alienfx_platdata *pdata;
-
- dev = container_of(kobj, struct device, kobj);
- pdata = dev_get_platdata(dev);
-
- return n < pdata->num_zones + 1 ? 0644 : 0;
-}
-
-static bool zone_group_visible(struct kobject *kobj)
-{
- struct device *dev;
- struct alienfx_platdata *pdata;
-
- dev = container_of(kobj, struct device, kobj);
- pdata = dev_get_platdata(dev);
-
- return pdata->num_zones > 0;
-}
-DEFINE_SYSFS_GROUP_VISIBLE(zone);
-
-static struct attribute *zone_attrs[] = {
- &dev_attr_lighting_control_state.attr,
- &dev_attr_zone00.attr,
- &dev_attr_zone01.attr,
- &dev_attr_zone02.attr,
- &dev_attr_zone03.attr,
- NULL
-};
-
-static struct attribute_group zone_attribute_group = {
- .name = "rgb_zones",
- .is_visible = SYSFS_GROUP_VISIBLE(zone),
- .attrs = zone_attrs,
-};
-
-/*
- * LED Brightness (Global)
- */
-static void global_led_set(struct led_classdev *led_cdev,
- enum led_brightness brightness)
-{
- struct alienfx_priv *priv;
- struct alienfx_platdata *pdata;
- int ret;
-
- priv = container_of(led_cdev, struct alienfx_priv, global_led);
- pdata = dev_get_platdata(&priv->pdev->dev);
-
- priv->global_brightness = brightness;
-
- ret = pdata->ops.upd_brightness(priv, pdata->wdev, brightness);
- if (ret)
- pr_err("LED brightness update failed\n");
-}
-
-static enum led_brightness global_led_get(struct led_classdev *led_cdev)
-{
- struct alienfx_priv *priv;
-
- priv = container_of(led_cdev, struct alienfx_priv, global_led);
-
- return priv->global_brightness;
-}
-
-/*
- * The HDMI mux sysfs node indicates the status of the HDMI input mux.
- * It can toggle between standard system GPU output and HDMI input.
- */
-static ssize_t show_hdmi_cable(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct alienfx_platdata *pdata;
- acpi_status status;
- u32 out_data;
- struct wmax_basic_args in_args = {
- .arg = 0,
- };
-
- pdata = dev_get_platdata(dev);
-
- status = alienware_wmi_command(pdata->wdev, WMAX_METHOD_HDMI_CABLE,
- &in_args, sizeof(in_args), &out_data);
-
- if (ACPI_SUCCESS(status)) {
- if (out_data == 0)
- return sysfs_emit(buf, "[unconnected] connected unknown\n");
- else if (out_data == 1)
- return sysfs_emit(buf, "unconnected [connected] unknown\n");
- }
- pr_err("alienware-wmi: unknown HDMI cable status: %d\n", status);
- return sysfs_emit(buf, "unconnected connected [unknown]\n");
-}
-
-static ssize_t show_hdmi_source(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct alienfx_platdata *pdata;
- acpi_status status;
- u32 out_data;
- struct wmax_basic_args in_args = {
- .arg = 0,
- };
-
- pdata = dev_get_platdata(dev);
-
- status = alienware_wmi_command(pdata->wdev, WMAX_METHOD_HDMI_STATUS,
- &in_args, sizeof(in_args), &out_data);
-
- if (ACPI_SUCCESS(status)) {
- if (out_data == 1)
- return sysfs_emit(buf, "[input] gpu unknown\n");
- else if (out_data == 2)
- return sysfs_emit(buf, "input [gpu] unknown\n");
- }
- pr_err("alienware-wmi: unknown HDMI source status: %u\n", status);
- return sysfs_emit(buf, "input gpu [unknown]\n");
-}
-
-static ssize_t toggle_hdmi_source(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct alienfx_platdata *pdata;
- acpi_status status;
- struct wmax_basic_args args;
-
- pdata = dev_get_platdata(dev);
-
- if (strcmp(buf, "gpu\n") == 0)
- args.arg = 1;
- else if (strcmp(buf, "input\n") == 0)
- args.arg = 2;
- else
- args.arg = 3;
- pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf);
-
- status = alienware_wmi_command(pdata->wdev, WMAX_METHOD_HDMI_SOURCE,
- &args, sizeof(args), NULL);
-
- if (ACPI_FAILURE(status))
- pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
- status);
- return count;
-}
-
-static DEVICE_ATTR(cable, S_IRUGO, show_hdmi_cable, NULL);
-static DEVICE_ATTR(source, S_IRUGO | S_IWUSR, show_hdmi_source,
- toggle_hdmi_source);
-
-static bool hdmi_group_visible(struct kobject *kobj)
-{
- struct device *dev;
- struct alienfx_platdata *pdata;
-
- dev = container_of(kobj, struct device, kobj);
- pdata = dev_get_platdata(dev);
-
- return pdata->hdmi_mux;
-}
-DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(hdmi);
-
-static struct attribute *hdmi_attrs[] = {
- &dev_attr_cable.attr,
- &dev_attr_source.attr,
- NULL,
-};
-
-static const struct attribute_group hdmi_attribute_group = {
- .name = "hdmi",
- .is_visible = SYSFS_GROUP_VISIBLE(hdmi),
- .attrs = hdmi_attrs,
-};
-
-/*
- * Alienware GFX amplifier support
- * - Currently supports reading cable status
- * - Leaving expansion room to possibly support dock/undock events later
- */
-static ssize_t show_amplifier_status(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct alienfx_platdata *pdata;
- acpi_status status;
- u32 out_data;
- struct wmax_basic_args in_args = {
- .arg = 0,
- };
-
- pdata = dev_get_platdata(dev);
-
- status = alienware_wmi_command(pdata->wdev, WMAX_METHOD_AMPLIFIER_CABLE,
- &in_args, sizeof(in_args), &out_data);
- if (ACPI_SUCCESS(status)) {
- if (out_data == 0)
- return sysfs_emit(buf, "[unconnected] connected unknown\n");
- else if (out_data == 1)
- return sysfs_emit(buf, "unconnected [connected] unknown\n");
- }
- pr_err("alienware-wmi: unknown amplifier cable status: %d\n", status);
- return sysfs_emit(buf, "unconnected connected [unknown]\n");
-}
-
-static DEVICE_ATTR(status, S_IRUGO, show_amplifier_status, NULL);
-
-static bool amplifier_group_visible(struct kobject *kobj)
-{
- struct device *dev;
- struct alienfx_platdata *pdata;
-
- dev = container_of(kobj, struct device, kobj);
- pdata = dev_get_platdata(dev);
-
- return pdata->amplifier;
-}
-DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(amplifier);
-
-static struct attribute *amplifier_attrs[] = {
- &dev_attr_status.attr,
- NULL,
-};
-
-static const struct attribute_group amplifier_attribute_group = {
- .name = "amplifier",
- .is_visible = SYSFS_GROUP_VISIBLE(amplifier),
- .attrs = amplifier_attrs,
-};
-
-/*
- * Deep Sleep Control support
- * - Modifies BIOS setting for deep sleep control allowing extra wakeup events
- */
-static ssize_t show_deepsleep_status(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct alienfx_platdata *pdata;
- acpi_status status;
- u32 out_data;
- struct wmax_basic_args in_args = {
- .arg = 0,
- };
-
- pdata = dev_get_platdata(dev);
-
- status = alienware_wmi_command(pdata->wdev, WMAX_METHOD_DEEP_SLEEP_STATUS,
- &in_args, sizeof(in_args), &out_data);
- if (ACPI_SUCCESS(status)) {
- if (out_data == 0)
- return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
- else if (out_data == 1)
- return sysfs_emit(buf, "disabled [s5] s5_s4\n");
- else if (out_data == 2)
- return sysfs_emit(buf, "disabled s5 [s5_s4]\n");
- }
- pr_err("alienware-wmi: unknown deep sleep status: %d\n", status);
- return sysfs_emit(buf, "disabled s5 s5_s4 [unknown]\n");
-}
-
-static ssize_t toggle_deepsleep(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct alienfx_platdata *pdata;
- acpi_status status;
- struct wmax_basic_args args;
-
- pdata = dev_get_platdata(dev);
-
- if (strcmp(buf, "disabled\n") == 0)
- args.arg = 0;
- else if (strcmp(buf, "s5\n") == 0)
- args.arg = 1;
- else
- args.arg = 2;
- pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf);
-
- status = alienware_wmi_command(pdata->wdev, WMAX_METHOD_DEEP_SLEEP_CONTROL,
- &args, sizeof(args), NULL);
-
- if (ACPI_FAILURE(status))
- pr_err("alienware-wmi: deep sleep control failed: results: %u\n",
- status);
- return count;
-}
-
-static DEVICE_ATTR(deepsleep, S_IRUGO | S_IWUSR, show_deepsleep_status, toggle_deepsleep);
-
-static bool deepsleep_group_visible(struct kobject *kobj)
-{
- struct device *dev;
- struct alienfx_platdata *pdata;
-
- dev = container_of(kobj, struct device, kobj);
- pdata = dev_get_platdata(dev);
-
- return pdata->deepslp;
-}
-DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(deepsleep);
-
-static struct attribute *deepsleep_attrs[] = {
- &dev_attr_deepsleep.attr,
- NULL,
-};
-
-static const struct attribute_group deepsleep_attribute_group = {
- .name = "deepsleep",
- .is_visible = SYSFS_GROUP_VISIBLE(deepsleep),
- .attrs = deepsleep_attrs,
-};
-
-/*
- * Thermal Profile control
- * - Provides thermal profile control through the Platform Profile API
- */
-#define WMAX_THERMAL_TABLE_MASK GENMASK(7, 4)
-#define WMAX_THERMAL_MODE_MASK GENMASK(3, 0)
-#define WMAX_SENSOR_ID_MASK BIT(8)
-
-static bool is_wmax_thermal_code(u32 code)
-{
- if (code & WMAX_SENSOR_ID_MASK)
- return false;
-
- if ((code & WMAX_THERMAL_MODE_MASK) >= THERMAL_MODE_LAST)
- return false;
-
- if ((code & WMAX_THERMAL_TABLE_MASK) == WMAX_THERMAL_TABLE_BASIC &&
- (code & WMAX_THERMAL_MODE_MASK) >= THERMAL_MODE_BASIC_QUIET)
- return true;
-
- if ((code & WMAX_THERMAL_TABLE_MASK) == WMAX_THERMAL_TABLE_USTT &&
- (code & WMAX_THERMAL_MODE_MASK) <= THERMAL_MODE_USTT_LOW_POWER)
- return true;
-
- return false;
-}
-
-static int wmax_thermal_information(struct wmi_device *wdev, u8 operation,
- u8 arg, u32 *out_data)
-{
- acpi_status status;
- struct wmax_u32_args in_args = {
- .operation = operation,
- .arg1 = arg,
- .arg2 = 0,
- .arg3 = 0,
- };
-
- status = alienware_wmi_command(wdev, WMAX_METHOD_THERMAL_INFORMATION,
- &in_args, sizeof(in_args), out_data);
-
- if (ACPI_FAILURE(status))
- return -EIO;
-
- if (*out_data == WMAX_FAILURE_CODE)
- return -EBADRQC;
-
- return 0;
-}
-
-static int wmax_thermal_control(struct wmi_device *wdev, u8 profile)
-{
- acpi_status status;
- struct wmax_u32_args in_args = {
- .operation = WMAX_OPERATION_ACTIVATE_PROFILE,
- .arg1 = profile,
- .arg2 = 0,
- .arg3 = 0,
- };
- u32 out_data;
-
- status = alienware_wmi_command(wdev, WMAX_METHOD_THERMAL_CONTROL,
- &in_args, sizeof(in_args), &out_data);
-
- if (ACPI_FAILURE(status))
- return -EIO;
-
- if (out_data == WMAX_FAILURE_CODE)
- return -EBADRQC;
-
- return 0;
-}
-
-static int wmax_game_shift_status(struct wmi_device *wdev, u8 operation,
- u32 *out_data)
-{
- acpi_status status;
- struct wmax_u32_args in_args = {
- .operation = operation,
- .arg1 = 0,
- .arg2 = 0,
- .arg3 = 0,
- };
-
- status = alienware_wmi_command(wdev, WMAX_METHOD_GAME_SHIFT_STATUS,
- &in_args, sizeof(in_args), out_data);
-
- if (ACPI_FAILURE(status))
- return -EIO;
-
- if (*out_data == WMAX_FAILURE_CODE)
- return -EOPNOTSUPP;
-
- return 0;
-}
-
-static int thermal_profile_get(struct platform_profile_handler *pprof,
- enum platform_profile_option *profile)
-{
- struct awcc_priv *priv;
- u32 out_data;
- int ret;
-
- priv = container_of(pprof, struct awcc_priv, pp_handler);
-
- ret = wmax_thermal_information(priv->wdev, WMAX_OPERATION_CURRENT_PROFILE,
- 0, &out_data);
-
- if (ret < 0)
- return ret;
-
- if (out_data == WMAX_THERMAL_MODE_GMODE) {
- *profile = PLATFORM_PROFILE_PERFORMANCE;
- return 0;
- }
-
- if (!is_wmax_thermal_code(out_data))
- return -ENODATA;
-
- out_data &= WMAX_THERMAL_MODE_MASK;
- *profile = wmax_mode_to_platform_profile[out_data];
-
- return 0;
-}
-
-static int thermal_profile_set(struct platform_profile_handler *pprof,
- enum platform_profile_option profile)
-{
- struct awcc_priv *priv;
-
- priv = container_of(pprof, struct awcc_priv, pp_handler);
-
- if (priv->has_gmode) {
- u32 gmode_status;
- int ret;
-
- ret = wmax_game_shift_status(priv->wdev,
- WMAX_OPERATION_GET_GAME_SHIFT_STATUS,
- &gmode_status);
-
- if (ret < 0)
- return ret;
-
- if ((profile == PLATFORM_PROFILE_PERFORMANCE && !gmode_status) ||
- (profile != PLATFORM_PROFILE_PERFORMANCE && gmode_status)) {
- ret = wmax_game_shift_status(priv->wdev,
- WMAX_OPERATION_TOGGLE_GAME_SHIFT,
- &gmode_status);
-
- if (ret < 0)
- return ret;
- }
- }
-
- return wmax_thermal_control(priv->wdev,
- priv->supported_thermal_profiles[profile]);
-}
-
-int create_thermal_profile(struct wmi_device *wdev, bool has_gmode)
-{
- struct awcc_priv *priv;
- u32 out_data;
- u8 sys_desc[4];
- u32 first_mode;
- enum wmax_thermal_mode mode;
- enum platform_profile_option profile;
- int ret;
-
- priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
- dev_set_drvdata(&wdev->dev, priv);
-
- priv->wdev = wdev;
-
- ret = wmax_thermal_information(wdev, WMAX_OPERATION_SYS_DESCRIPTION,
- 0, (u32 *) &sys_desc);
- if (ret < 0)
- return ret;
-
- first_mode = sys_desc[0] + sys_desc[1];
-
- for (u32 i = 0; i < sys_desc[3]; i++) {
- ret = wmax_thermal_information(wdev, WMAX_OPERATION_LIST_IDS,
- i + first_mode, &out_data);
-
- if (ret == -EIO)
- return ret;
-
- if (ret == -EBADRQC)
- break;
-
- if (!is_wmax_thermal_code(out_data))
- continue;
-
- mode = out_data & WMAX_THERMAL_MODE_MASK;
- profile = wmax_mode_to_platform_profile[mode];
- priv->supported_thermal_profiles[profile] = out_data;
-
- set_bit(profile, priv->pp_handler.choices);
- }
-
- if (bitmap_empty(priv->pp_handler.choices, PLATFORM_PROFILE_LAST))
- return -ENODEV;
-
- if (has_gmode) {
- priv->has_gmode = true;
- priv->supported_thermal_profiles[PLATFORM_PROFILE_PERFORMANCE] =
- WMAX_THERMAL_MODE_GMODE;
-
- set_bit(PLATFORM_PROFILE_PERFORMANCE, priv->pp_handler.choices);
- }
-
- priv->pp_handler.profile_get = thermal_profile_get;
- priv->pp_handler.profile_set = thermal_profile_set;
-
- return platform_profile_register(&priv->pp_handler);
-}
-
-void remove_thermal_profile(void)
-{
- platform_profile_remove();
-}
-
-/*
- * Platform Driver
- */
-static int alienfx_probe(struct platform_device *pdev)
-{
- struct alienfx_priv *priv;
- struct led_classdev *leds;
-
- priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
- platform_set_drvdata(pdev, priv);
-
- priv->pdev = pdev;
-
- if (interface == WMAX)
- priv->lighting_control_state = WMAX_RUNNING;
- else if (interface == LEGACY)
- priv->lighting_control_state = LEGACY_RUNNING;
-
- leds = &priv->global_led;
- leds->name = "alienware::global_brightness";
- leds->brightness_set = global_led_set;
- leds->brightness_get = global_led_get;
- leds->max_brightness = 0x0F;
-
- priv->global_brightness = priv->global_led.max_brightness;
-
- return devm_led_classdev_register(&pdev->dev, &priv->global_led);
-}
-
-static const struct attribute_group *alienfx_groups[] = {
- &zone_attribute_group,
- &hdmi_attribute_group,
- &lifier_attribute_group,
- &deepsleep_attribute_group,
- NULL
-};
-
-static struct platform_driver platform_driver = {
- .driver = {
- .name = "alienware-wmi",
- .dev_groups = alienfx_groups,
- },
- .probe = alienfx_probe,
-};
-
-int alienfx_wmi_init(struct alienfx_platdata *pdata)
-{
- struct platform_device *pdev;
-
- pdev = platform_create_bundle(&platform_driver, alienfx_probe, NULL, 0,
- pdata, sizeof(*pdata));
-
- dev_set_drvdata(&pdata->wdev->dev, pdev);
-
- return PTR_ERR_OR_ZERO(pdev);
-}
-
-void alienfx_wmi_exit(struct wmi_device *wdev)
-{
- struct platform_device *pdev;
-
- pdev = dev_get_drvdata(&wdev->dev);
-
- platform_device_unregister(pdev);
- platform_driver_unregister(&platform_driver);
-}
-
/*
* Legacy WMI device
*/
--
2.47.1
Powered by blists - more mailing lists