From: Kurt Borja Date: Fri, 7 Feb 2025 15:46:05 +0000 (-0500) Subject: platform/x86: Rename alienware-wmi.c X-Git-Tag: io_uring-6.15-20250403~94^2~56 X-Git-Url: https://git.kernel.dk/?a=commitdiff_plain;h=2e56ac8c27240a5c14f314776baba02aea1357c7;p=linux-block.git platform/x86: Rename alienware-wmi.c Rename alienware-wmi to support upcoming split. Reviewed-by: Armin Wolf Reviewed-by: Mario Limonciello Signed-off-by: Kurt Borja Link: https://lore.kernel.org/r/20250207154610.13675-10-kuurtb@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- diff --git a/drivers/platform/x86/dell/Makefile b/drivers/platform/x86/dell/Makefile index bb3cbd470a46..f8aec8502c2f 100644 --- a/drivers/platform/x86/dell/Makefile +++ b/drivers/platform/x86/dell/Makefile @@ -5,6 +5,7 @@ # obj-$(CONFIG_ALIENWARE_WMI) += alienware-wmi.o +alienware-wmi-objs := alienware-wmi-base.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-base.c b/drivers/platform/x86/dell/alienware-wmi-base.c new file mode 100644 index 000000000000..c575b82f11df --- /dev/null +++ b/drivers/platform/x86/dell/alienware-wmi-base.c @@ -0,0 +1,1384 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Alienware AlienFX control + * + * Copyright (C) 2014 Dell Inc + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LEGACY_CONTROL_GUID "A90597CE-A997-11DA-B012-B622A1EF5492" +#define LEGACY_POWER_CONTROL_GUID "A80593CE-A997-11DA-B012-B622A1EF5492" +#define WMAX_CONTROL_GUID "A70591CE-A997-11DA-B012-B622A1EF5492" + +#define WMAX_METHOD_HDMI_SOURCE 0x1 +#define WMAX_METHOD_HDMI_STATUS 0x2 +#define WMAX_METHOD_BRIGHTNESS 0x3 +#define WMAX_METHOD_ZONE_CONTROL 0x4 +#define WMAX_METHOD_HDMI_CABLE 0x5 +#define WMAX_METHOD_AMPLIFIER_CABLE 0x6 +#define WMAX_METHOD_DEEP_SLEEP_CONTROL 0x0B +#define WMAX_METHOD_DEEP_SLEEP_STATUS 0x0C +#define WMAX_METHOD_THERMAL_INFORMATION 0x14 +#define WMAX_METHOD_THERMAL_CONTROL 0x15 +#define WMAX_METHOD_GAME_SHIFT_STATUS 0x25 + +#define WMAX_THERMAL_MODE_GMODE 0xAB + +#define WMAX_FAILURE_CODE 0xFFFFFFFF + +MODULE_AUTHOR("Mario Limonciello "); +MODULE_DESCRIPTION("Alienware special feature control"); +MODULE_LICENSE("GPL"); + +static bool force_platform_profile; +module_param_unsafe(force_platform_profile, bool, 0); +MODULE_PARM_DESC(force_platform_profile, "Forces auto-detecting thermal profiles without checking if WMI thermal backend is available"); + +static bool force_gmode; +module_param_unsafe(force_gmode, bool, 0); +MODULE_PARM_DESC(force_gmode, "Forces G-Mode when performance profile is selected"); + +enum INTERFACE_FLAGS { + LEGACY, + WMAX, +}; + +enum LEGACY_CONTROL_STATES { + LEGACY_RUNNING = 1, + LEGACY_BOOTING = 0, + LEGACY_SUSPEND = 3, +}; + +enum WMAX_CONTROL_STATES { + WMAX_RUNNING = 0xFF, + WMAX_BOOTING = 0, + WMAX_SUSPEND = 3, +}; + +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, +}; + +enum wmax_thermal_mode { + THERMAL_MODE_USTT_BALANCED, + THERMAL_MODE_USTT_BALANCED_PERFORMANCE, + THERMAL_MODE_USTT_COOL, + THERMAL_MODE_USTT_QUIET, + THERMAL_MODE_USTT_PERFORMANCE, + THERMAL_MODE_USTT_LOW_POWER, + THERMAL_MODE_BASIC_QUIET, + THERMAL_MODE_BASIC_BALANCED, + THERMAL_MODE_BASIC_BALANCED_PERFORMANCE, + THERMAL_MODE_BASIC_PERFORMANCE, + THERMAL_MODE_LAST, +}; + +static const enum platform_profile_option wmax_mode_to_platform_profile[THERMAL_MODE_LAST] = { + [THERMAL_MODE_USTT_BALANCED] = PLATFORM_PROFILE_BALANCED, + [THERMAL_MODE_USTT_BALANCED_PERFORMANCE] = PLATFORM_PROFILE_BALANCED_PERFORMANCE, + [THERMAL_MODE_USTT_COOL] = PLATFORM_PROFILE_COOL, + [THERMAL_MODE_USTT_QUIET] = PLATFORM_PROFILE_QUIET, + [THERMAL_MODE_USTT_PERFORMANCE] = PLATFORM_PROFILE_PERFORMANCE, + [THERMAL_MODE_USTT_LOW_POWER] = PLATFORM_PROFILE_LOW_POWER, + [THERMAL_MODE_BASIC_QUIET] = PLATFORM_PROFILE_QUIET, + [THERMAL_MODE_BASIC_BALANCED] = PLATFORM_PROFILE_BALANCED, + [THERMAL_MODE_BASIC_BALANCED_PERFORMANCE] = PLATFORM_PROFILE_BALANCED_PERFORMANCE, + [THERMAL_MODE_BASIC_PERFORMANCE] = PLATFORM_PROFILE_PERFORMANCE, +}; + +struct alienfx_quirks { + u8 num_zones; + bool hdmi_mux; + bool amplifier; + bool deepslp; +}; + +static struct alienfx_quirks *alienfx; + + +static struct alienfx_quirks quirk_inspiron5675 = { + .num_zones = 2, + .hdmi_mux = false, + .amplifier = false, + .deepslp = false, +}; + +static struct alienfx_quirks quirk_unknown = { + .num_zones = 2, + .hdmi_mux = false, + .amplifier = false, + .deepslp = false, +}; + +static struct alienfx_quirks quirk_x51_r1_r2 = { + .num_zones = 3, + .hdmi_mux = false, + .amplifier = false, + .deepslp = false, +}; + +static struct alienfx_quirks quirk_x51_r3 = { + .num_zones = 4, + .hdmi_mux = false, + .amplifier = true, + .deepslp = false, +}; + +static struct alienfx_quirks quirk_asm100 = { + .num_zones = 2, + .hdmi_mux = true, + .amplifier = false, + .deepslp = false, +}; + +static struct alienfx_quirks quirk_asm200 = { + .num_zones = 2, + .hdmi_mux = true, + .amplifier = false, + .deepslp = true, +}; + +static struct alienfx_quirks quirk_asm201 = { + .num_zones = 2, + .hdmi_mux = true, + .amplifier = true, + .deepslp = true, +}; + +static int __init dmi_matched(const struct dmi_system_id *dmi) +{ + alienfx = dmi->driver_data; + return 1; +} + +static const struct dmi_system_id alienware_quirks[] __initconst = { + { + .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 ASM201", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), + DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"), + }, + .driver_data = &quirk_asm201, + }, + { + .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 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 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 = "Dell Inc. Inspiron 5675", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"), + }, + .driver_data = &quirk_inspiron5675, + }, + {} +}; + +struct color_platform { + u8 blue; + u8 green; + u8 red; +} __packed; + +struct wmax_brightness_args { + u32 led_mask; + u32 percentage; +}; + +struct wmax_basic_args { + u8 arg; +}; + +struct legacy_led_args { + struct color_platform colors; + u8 brightness; + u8 state; +} __packed; + +struct wmax_led_args { + u32 led_mask; + struct color_platform colors; + u8 state; +} __packed; + +struct wmax_u32_args { + u8 operation; + u8 arg1; + u8 arg2; + u8 arg3; +}; + +struct awcc_priv { + struct wmi_device *wdev; + struct device *ppdev; + enum wmax_thermal_mode supported_thermal_profiles[PLATFORM_PROFILE_LAST]; +}; + +struct alienfx_priv { + struct platform_device *pdev; + struct led_classdev global_led; + struct color_platform colors[4]; + u8 global_brightness; + u8 lighting_control_state; +}; + +struct alienfx_ops { + int (*upd_led)(struct alienfx_priv *priv, struct wmi_device *wdev, + u8 location); + int (*upd_brightness)(struct alienfx_priv *priv, struct wmi_device *wdev, + u8 brightness); +}; + +struct alienfx_platdata { + struct wmi_device *wdev; + struct alienfx_ops ops; +}; + +static u8 interface; + +struct awcc_quirks { + bool pprof; + bool gmode; +}; + +static struct awcc_quirks g_series_quirks = { + .pprof = true, + .gmode = true, +}; + +static struct awcc_quirks generic_quirks = { + .pprof = true, + .gmode = false, +}; + +static struct awcc_quirks empty_quirks; + +static const struct dmi_system_id awcc_dmi_table[] __initconst = { + { + .ident = "Alienware m16 R1 AMD", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m16 R1 AMD"), + }, + .driver_data = &generic_quirks, + }, + { + .ident = "Alienware m17 R5", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m17 R5 AMD"), + }, + .driver_data = &generic_quirks, + }, + { + .ident = "Alienware m18 R2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m18 R2"), + }, + .driver_data = &generic_quirks, + }, + { + .ident = "Alienware x15 R1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R1"), + }, + .driver_data = &generic_quirks, + }, + { + .ident = "Alienware x17 R2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x17 R2"), + }, + .driver_data = &generic_quirks, + }, + { + .ident = "Dell Inc. G15 5510", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5510"), + }, + .driver_data = &g_series_quirks, + }, + { + .ident = "Dell Inc. G15 5511", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5511"), + }, + .driver_data = &g_series_quirks, + }, + { + .ident = "Dell Inc. G15 5515", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5515"), + }, + .driver_data = &g_series_quirks, + }, + { + .ident = "Dell Inc. G3 3500", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "G3 3500"), + }, + .driver_data = &g_series_quirks, + }, + { + .ident = "Dell Inc. G3 3590", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "G3 3590"), + }, + .driver_data = &g_series_quirks, + }, + { + .ident = "Dell Inc. G5 5500", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "G5 5500"), + }, + .driver_data = &g_series_quirks, + }, +}; + +static struct awcc_quirks *awcc; + +static int alienware_wmi_command(struct wmi_device *wdev, u32 method_id, + void *in_args, size_t in_size, u32 *out_data) +{ + struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; + struct acpi_buffer in = {in_size, in_args}; + acpi_status ret; + + ret = wmidev_evaluate_method(wdev, 0, method_id, &in, out_data ? &out : NULL); + if (ACPI_FAILURE(ret)) + return -EIO; + + union acpi_object *obj __free(kfree) = out.pointer; + + if (out_data) { + if (obj && obj->type == ACPI_TYPE_INTEGER) + *out_data = (u32)obj->integer.value; + else + return -ENOMSG; + } + + return 0; +} + +/* + * 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 = dev_get_drvdata(dev); + struct color_platform *colors = &priv->colors[location]; + + return sprintf(buf, "red: %d, green: %d, blue: %d\n", + colors->red, colors->green, colors->blue); + +} + +static ssize_t zone_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count, u8 location) +{ + struct alienfx_priv *priv = dev_get_drvdata(dev); + struct color_platform *colors = &priv->colors[location]; + struct alienfx_platdata *pdata = dev_get_platdata(dev); + int ret; + + ret = parse_rgb(buf, colors); + if (ret) + return ret; + + ret = pdata->ops.upd_led(priv, pdata->wdev, location); + + return ret ? ret : count; +} + +static ssize_t zone00_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return zone_show(dev, attr, buf, 0); +} + +static ssize_t zone00_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + return zone_store(dev, attr, buf, count, 0); +} + +static DEVICE_ATTR_RW(zone00); + +static ssize_t zone01_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return zone_show(dev, attr, buf, 1); +} + +static ssize_t zone01_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + return zone_store(dev, attr, buf, count, 1); +} + +static DEVICE_ATTR_RW(zone01); + +static ssize_t zone02_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return zone_show(dev, attr, buf, 2); +} + +static ssize_t zone02_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + return zone_store(dev, attr, buf, count, 2); +} + +static DEVICE_ATTR_RW(zone02); + +static ssize_t zone03_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return zone_show(dev, attr, buf, 3); +} + +static ssize_t zone03_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + return zone_store(dev, attr, buf, count, 3); +} + +static DEVICE_ATTR_RW(zone03); + +/* + * 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 = 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 = dev_get_drvdata(dev); + u8 val; + + if (strcmp(buf, "booting\n") == 0) + val = LEGACY_BOOTING; + else if (strcmp(buf, "suspend\n") == 0) + val = LEGACY_SUSPEND; + else if (interface == LEGACY) + val = LEGACY_RUNNING; + else + val = WMAX_RUNNING; + + 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) +{ + if (n < alienfx->num_zones + 1) + return attr->mode; + + return 0; +} + +static bool zone_group_visible(struct kobject *kobj) +{ + return alienfx->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 = container_of(led_cdev, struct alienfx_priv, + global_led); + struct alienfx_platdata *pdata = dev_get_platdata(&priv->pdev->dev); + int ret; + + 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 = 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 cable_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct alienfx_platdata *pdata = dev_get_platdata(dev); + struct wmax_basic_args in_args = { + .arg = 0, + }; + u32 out_data; + int ret; + + ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_HDMI_CABLE, + &in_args, sizeof(in_args), &out_data); + if (!ret) { + 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", ret); + return sysfs_emit(buf, "unconnected connected [unknown]\n"); +} + +static ssize_t source_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct alienfx_platdata *pdata = dev_get_platdata(dev); + struct wmax_basic_args in_args = { + .arg = 0, + }; + u32 out_data; + int ret; + + ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_HDMI_STATUS, + &in_args, sizeof(in_args), &out_data); + if (!ret) { + 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", ret); + return sysfs_emit(buf, "input gpu [unknown]\n"); +} + +static ssize_t source_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct alienfx_platdata *pdata = dev_get_platdata(dev); + struct wmax_basic_args args; + int ret; + + 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); + + ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_HDMI_SOURCE, &args, + sizeof(args), NULL); + if (ret < 0) + pr_err("alienware-wmi: HDMI toggle failed: results: %u\n", ret); + + return count; +} + +static DEVICE_ATTR_RO(cable); +static DEVICE_ATTR_RW(source); + +static bool hdmi_group_visible(struct kobject *kobj) +{ + return interface == WMAX && alienfx->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 status_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct alienfx_platdata *pdata = dev_get_platdata(dev); + struct wmax_basic_args in_args = { + .arg = 0, + }; + u32 out_data; + int ret; + + ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_AMPLIFIER_CABLE, + &in_args, sizeof(in_args), &out_data); + if (!ret) { + 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", ret); + return sysfs_emit(buf, "unconnected connected [unknown]\n"); +} + +static DEVICE_ATTR_RO(status); + +static bool amplifier_group_visible(struct kobject *kobj) +{ + return interface == WMAX && alienfx->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 deepsleep_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct alienfx_platdata *pdata = dev_get_platdata(dev); + struct wmax_basic_args in_args = { + .arg = 0, + }; + u32 out_data; + int ret; + + ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_DEEP_SLEEP_STATUS, + &in_args, sizeof(in_args), &out_data); + if (!ret) { + 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", ret); + return sysfs_emit(buf, "disabled s5 s5_s4 [unknown]\n"); +} + +static ssize_t deepsleep_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct alienfx_platdata *pdata = dev_get_platdata(dev); + struct wmax_basic_args args; + int ret; + + 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); + + ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_DEEP_SLEEP_CONTROL, + &args, sizeof(args), NULL); + if (!ret) + pr_err("alienware-wmi: deep sleep control failed: results: %u\n", ret); + + return count; +} + +static DEVICE_ATTR_RW(deepsleep); + +static bool deepsleep_group_visible(struct kobject *kobj) +{ + return interface == WMAX && alienfx->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) +{ + struct wmax_u32_args in_args = { + .operation = operation, + .arg1 = arg, + .arg2 = 0, + .arg3 = 0, + }; + int ret; + + ret = alienware_wmi_command(wdev, WMAX_METHOD_THERMAL_INFORMATION, + &in_args, sizeof(in_args), out_data); + if (ret < 0) + return ret; + + if (*out_data == WMAX_FAILURE_CODE) + return -EBADRQC; + + return 0; +} + +static int wmax_thermal_control(struct wmi_device *wdev, u8 profile) +{ + struct wmax_u32_args in_args = { + .operation = WMAX_OPERATION_ACTIVATE_PROFILE, + .arg1 = profile, + .arg2 = 0, + .arg3 = 0, + }; + u32 out_data; + int ret; + + ret = alienware_wmi_command(wdev, WMAX_METHOD_THERMAL_CONTROL, + &in_args, sizeof(in_args), &out_data); + if (ret) + return ret; + + 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) +{ + struct wmax_u32_args in_args = { + .operation = operation, + .arg1 = 0, + .arg2 = 0, + .arg3 = 0, + }; + int ret; + + ret = alienware_wmi_command(wdev, WMAX_METHOD_GAME_SHIFT_STATUS, + &in_args, sizeof(in_args), out_data); + if (ret < 0) + return ret; + + if (*out_data == WMAX_FAILURE_CODE) + return -EOPNOTSUPP; + + return 0; +} + +static int thermal_profile_get(struct device *dev, + enum platform_profile_option *profile) +{ + struct awcc_priv *priv = dev_get_drvdata(dev); + u32 out_data; + int ret; + + 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 device *dev, + enum platform_profile_option profile) +{ + struct awcc_priv *priv = dev_get_drvdata(dev); + + if (awcc->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]); +} + +static int thermal_profile_probe(void *drvdata, unsigned long *choices) +{ + enum platform_profile_option profile; + struct awcc_priv *priv = drvdata; + enum wmax_thermal_mode mode; + u8 sys_desc[4]; + u32 first_mode; + u32 out_data; + int ret; + + ret = wmax_thermal_information(priv->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(priv->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, choices); + } + + if (bitmap_empty(choices, PLATFORM_PROFILE_LAST)) + return -ENODEV; + + if (awcc->gmode) { + priv->supported_thermal_profiles[PLATFORM_PROFILE_PERFORMANCE] = + WMAX_THERMAL_MODE_GMODE; + + set_bit(PLATFORM_PROFILE_PERFORMANCE, choices); + } + + return 0; +} + +static const struct platform_profile_ops awcc_platform_profile_ops = { + .probe = thermal_profile_probe, + .profile_get = thermal_profile_get, + .profile_set = thermal_profile_set, +}; + +static int awcc_platform_profile_init(struct wmi_device *wdev) +{ + struct awcc_priv *priv = dev_get_drvdata(&wdev->dev); + + priv->ppdev = devm_platform_profile_register(&wdev->dev, "alienware-wmi", + priv, &awcc_platform_profile_ops); + + return PTR_ERR_OR_ZERO(priv->ppdev); +} + +static int alienware_awcc_setup(struct wmi_device *wdev) +{ + struct awcc_priv *priv; + int ret; + + priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->wdev = wdev; + dev_set_drvdata(&wdev->dev, priv); + + if (awcc->pprof) { + ret = awcc_platform_profile_init(wdev); + if (ret) + return ret; + } + + return 0; +} + +/* + * Platform Driver + */ +static int alienfx_probe(struct platform_device *pdev) +{ + struct alienfx_priv *priv; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + if (interface == WMAX) + priv->lighting_control_state = WMAX_RUNNING; + else + priv->lighting_control_state = LEGACY_RUNNING; + + priv->pdev = pdev; + priv->global_led.name = "alienware::global_brightness"; + priv->global_led.brightness_set = global_led_set; + priv->global_led.brightness_get = global_led_get; + priv->global_led.max_brightness = 0x0F; + priv->global_brightness = priv->global_led.max_brightness; + platform_set_drvdata(pdev, priv); + + 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, +}; + +static void alienware_alienfx_remove(void *data) +{ + struct platform_device *pdev = data; + + platform_device_unregister(pdev); +} + +static int alienware_alienfx_setup(struct alienfx_platdata *pdata) +{ + struct device *dev = &pdata->wdev->dev; + struct platform_device *pdev; + int ret; + + pdev = platform_device_register_data(NULL, "alienware-wmi", + PLATFORM_DEVID_NONE, pdata, + sizeof(*pdata)); + if (IS_ERR(pdev)) + return PTR_ERR(pdev); + + dev_set_drvdata(dev, pdev); + ret = devm_add_action_or_reset(dev, alienware_alienfx_remove, pdev); + if (ret) + return ret; + + return 0; +} + +/* + * Legacy WMI driver + */ +static int legacy_wmi_update_led(struct alienfx_priv *priv, + struct wmi_device *wdev, u8 location) +{ + struct legacy_led_args legacy_args = { + .colors = priv->colors[location], + .brightness = priv->global_brightness, + .state = 0, + }; + struct acpi_buffer input; + acpi_status status; + + if (legacy_args.state != LEGACY_RUNNING) { + legacy_args.state = priv->lighting_control_state; + + input.length = sizeof(legacy_args); + input.pointer = &legacy_args; + + status = wmi_evaluate_method(LEGACY_POWER_CONTROL_GUID, 0, + location + 1, &input, NULL); + if (ACPI_FAILURE(status)) + return -EIO; + + return 0; + } + + return alienware_wmi_command(wdev, location + 1, &legacy_args, + sizeof(legacy_args), NULL); +} + +static int legacy_wmi_update_brightness(struct alienfx_priv *priv, + struct wmi_device *wdev, u8 brightness) +{ + return legacy_wmi_update_led(priv, wdev, 0); +} + +static int legacy_wmi_probe(struct wmi_device *wdev, const void *context) +{ + struct alienfx_platdata pdata = { + .wdev = wdev, + .ops = { + .upd_led = legacy_wmi_update_led, + .upd_brightness = legacy_wmi_update_brightness, + }, + }; + + return alienware_alienfx_setup(&pdata); +} + +static const struct wmi_device_id alienware_legacy_device_id_table[] = { + { LEGACY_CONTROL_GUID, NULL }, + { }, +}; +MODULE_DEVICE_TABLE(wmi, alienware_legacy_device_id_table); + +static struct wmi_driver alienware_legacy_wmi_driver = { + .driver = { + .name = "alienware-wmi-alienfx", + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, + .id_table = alienware_legacy_device_id_table, + .probe = legacy_wmi_probe, + .no_singleton = true, +}; + +static int __init alienware_legacy_wmi_init(void) +{ + return wmi_driver_register(&alienware_legacy_wmi_driver); +} + +static void __exit alienware_legacy_wmi_exit(void) +{ + wmi_driver_unregister(&alienware_legacy_wmi_driver); +} + +/* + * WMAX WMI driver + */ +static int wmax_wmi_update_led(struct alienfx_priv *priv, + struct wmi_device *wdev, u8 location) +{ + struct wmax_led_args in_args = { + .led_mask = 1 << location, + .colors = priv->colors[location], + .state = priv->lighting_control_state, + }; + + return alienware_wmi_command(wdev, WMAX_METHOD_ZONE_CONTROL, &in_args, + sizeof(in_args), NULL); +} + +static int wmax_wmi_update_brightness(struct alienfx_priv *priv, + struct wmi_device *wdev, u8 brightness) +{ + struct wmax_brightness_args in_args = { + .led_mask = 0xFF, + .percentage = brightness, + }; + + return alienware_wmi_command(wdev, WMAX_METHOD_BRIGHTNESS, &in_args, + sizeof(in_args), NULL); +} + +static int wmax_wmi_probe(struct wmi_device *wdev, const void *context) +{ + struct alienfx_platdata pdata = { + .wdev = wdev, + .ops = { + .upd_led = wmax_wmi_update_led, + .upd_brightness = wmax_wmi_update_brightness, + }, + }; + int ret; + + if (awcc) + ret = alienware_awcc_setup(wdev); + else + ret = alienware_alienfx_setup(&pdata); + + return ret; +} + +static const struct wmi_device_id alienware_wmax_device_id_table[] = { + { WMAX_CONTROL_GUID, NULL }, + { }, +}; +MODULE_DEVICE_TABLE(wmi, alienware_wmax_device_id_table); + +static struct wmi_driver alienware_wmax_wmi_driver = { + .driver = { + .name = "alienware-wmi-wmax", + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, + .id_table = alienware_wmax_device_id_table, + .probe = wmax_wmi_probe, + .no_singleton = true, +}; + +static int __init alienware_wmax_wmi_init(void) +{ + const struct dmi_system_id *id; + + id = dmi_first_match(awcc_dmi_table); + if (id) + awcc = id->driver_data; + + if (force_platform_profile) { + if (!awcc) + awcc = &empty_quirks; + + awcc->pprof = true; + } + + if (force_gmode) { + if (awcc) + awcc->gmode = true; + else + pr_warn("force_gmode requires platform profile support\n"); + } + + return wmi_driver_register(&alienware_wmax_wmi_driver); +} + +static void __exit alienware_wmax_wmi_exit(void) +{ + wmi_driver_unregister(&alienware_wmax_wmi_driver); +} + +static int __init alienware_wmi_init(void) +{ + int ret; + + dmi_check_system(alienware_quirks); + if (!alienfx) + alienfx = &quirk_unknown; + + ret = platform_driver_register(&platform_driver); + if (ret < 0) + return ret; + + if (wmi_has_guid(WMAX_CONTROL_GUID)) { + interface = WMAX; + ret = alienware_wmax_wmi_init(); + } else { + interface = LEGACY; + ret = alienware_legacy_wmi_init(); + } + + if (ret < 0) + platform_driver_unregister(&platform_driver); + + return ret; +} + +module_init(alienware_wmi_init); + +static void __exit alienware_wmi_exit(void) +{ + if (interface == WMAX) + alienware_wmax_wmi_exit(); + else + alienware_legacy_wmi_exit(); + + platform_driver_unregister(&platform_driver); +} + +module_exit(alienware_wmi_exit); diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c deleted file mode 100644 index c575b82f11df..000000000000 --- a/drivers/platform/x86/dell/alienware-wmi.c +++ /dev/null @@ -1,1384 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Alienware AlienFX control - * - * Copyright (C) 2014 Dell Inc - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define LEGACY_CONTROL_GUID "A90597CE-A997-11DA-B012-B622A1EF5492" -#define LEGACY_POWER_CONTROL_GUID "A80593CE-A997-11DA-B012-B622A1EF5492" -#define WMAX_CONTROL_GUID "A70591CE-A997-11DA-B012-B622A1EF5492" - -#define WMAX_METHOD_HDMI_SOURCE 0x1 -#define WMAX_METHOD_HDMI_STATUS 0x2 -#define WMAX_METHOD_BRIGHTNESS 0x3 -#define WMAX_METHOD_ZONE_CONTROL 0x4 -#define WMAX_METHOD_HDMI_CABLE 0x5 -#define WMAX_METHOD_AMPLIFIER_CABLE 0x6 -#define WMAX_METHOD_DEEP_SLEEP_CONTROL 0x0B -#define WMAX_METHOD_DEEP_SLEEP_STATUS 0x0C -#define WMAX_METHOD_THERMAL_INFORMATION 0x14 -#define WMAX_METHOD_THERMAL_CONTROL 0x15 -#define WMAX_METHOD_GAME_SHIFT_STATUS 0x25 - -#define WMAX_THERMAL_MODE_GMODE 0xAB - -#define WMAX_FAILURE_CODE 0xFFFFFFFF - -MODULE_AUTHOR("Mario Limonciello "); -MODULE_DESCRIPTION("Alienware special feature control"); -MODULE_LICENSE("GPL"); - -static bool force_platform_profile; -module_param_unsafe(force_platform_profile, bool, 0); -MODULE_PARM_DESC(force_platform_profile, "Forces auto-detecting thermal profiles without checking if WMI thermal backend is available"); - -static bool force_gmode; -module_param_unsafe(force_gmode, bool, 0); -MODULE_PARM_DESC(force_gmode, "Forces G-Mode when performance profile is selected"); - -enum INTERFACE_FLAGS { - LEGACY, - WMAX, -}; - -enum LEGACY_CONTROL_STATES { - LEGACY_RUNNING = 1, - LEGACY_BOOTING = 0, - LEGACY_SUSPEND = 3, -}; - -enum WMAX_CONTROL_STATES { - WMAX_RUNNING = 0xFF, - WMAX_BOOTING = 0, - WMAX_SUSPEND = 3, -}; - -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, -}; - -enum wmax_thermal_mode { - THERMAL_MODE_USTT_BALANCED, - THERMAL_MODE_USTT_BALANCED_PERFORMANCE, - THERMAL_MODE_USTT_COOL, - THERMAL_MODE_USTT_QUIET, - THERMAL_MODE_USTT_PERFORMANCE, - THERMAL_MODE_USTT_LOW_POWER, - THERMAL_MODE_BASIC_QUIET, - THERMAL_MODE_BASIC_BALANCED, - THERMAL_MODE_BASIC_BALANCED_PERFORMANCE, - THERMAL_MODE_BASIC_PERFORMANCE, - THERMAL_MODE_LAST, -}; - -static const enum platform_profile_option wmax_mode_to_platform_profile[THERMAL_MODE_LAST] = { - [THERMAL_MODE_USTT_BALANCED] = PLATFORM_PROFILE_BALANCED, - [THERMAL_MODE_USTT_BALANCED_PERFORMANCE] = PLATFORM_PROFILE_BALANCED_PERFORMANCE, - [THERMAL_MODE_USTT_COOL] = PLATFORM_PROFILE_COOL, - [THERMAL_MODE_USTT_QUIET] = PLATFORM_PROFILE_QUIET, - [THERMAL_MODE_USTT_PERFORMANCE] = PLATFORM_PROFILE_PERFORMANCE, - [THERMAL_MODE_USTT_LOW_POWER] = PLATFORM_PROFILE_LOW_POWER, - [THERMAL_MODE_BASIC_QUIET] = PLATFORM_PROFILE_QUIET, - [THERMAL_MODE_BASIC_BALANCED] = PLATFORM_PROFILE_BALANCED, - [THERMAL_MODE_BASIC_BALANCED_PERFORMANCE] = PLATFORM_PROFILE_BALANCED_PERFORMANCE, - [THERMAL_MODE_BASIC_PERFORMANCE] = PLATFORM_PROFILE_PERFORMANCE, -}; - -struct alienfx_quirks { - u8 num_zones; - bool hdmi_mux; - bool amplifier; - bool deepslp; -}; - -static struct alienfx_quirks *alienfx; - - -static struct alienfx_quirks quirk_inspiron5675 = { - .num_zones = 2, - .hdmi_mux = false, - .amplifier = false, - .deepslp = false, -}; - -static struct alienfx_quirks quirk_unknown = { - .num_zones = 2, - .hdmi_mux = false, - .amplifier = false, - .deepslp = false, -}; - -static struct alienfx_quirks quirk_x51_r1_r2 = { - .num_zones = 3, - .hdmi_mux = false, - .amplifier = false, - .deepslp = false, -}; - -static struct alienfx_quirks quirk_x51_r3 = { - .num_zones = 4, - .hdmi_mux = false, - .amplifier = true, - .deepslp = false, -}; - -static struct alienfx_quirks quirk_asm100 = { - .num_zones = 2, - .hdmi_mux = true, - .amplifier = false, - .deepslp = false, -}; - -static struct alienfx_quirks quirk_asm200 = { - .num_zones = 2, - .hdmi_mux = true, - .amplifier = false, - .deepslp = true, -}; - -static struct alienfx_quirks quirk_asm201 = { - .num_zones = 2, - .hdmi_mux = true, - .amplifier = true, - .deepslp = true, -}; - -static int __init dmi_matched(const struct dmi_system_id *dmi) -{ - alienfx = dmi->driver_data; - return 1; -} - -static const struct dmi_system_id alienware_quirks[] __initconst = { - { - .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 ASM201", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), - DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"), - }, - .driver_data = &quirk_asm201, - }, - { - .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 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 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 = "Dell Inc. Inspiron 5675", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"), - }, - .driver_data = &quirk_inspiron5675, - }, - {} -}; - -struct color_platform { - u8 blue; - u8 green; - u8 red; -} __packed; - -struct wmax_brightness_args { - u32 led_mask; - u32 percentage; -}; - -struct wmax_basic_args { - u8 arg; -}; - -struct legacy_led_args { - struct color_platform colors; - u8 brightness; - u8 state; -} __packed; - -struct wmax_led_args { - u32 led_mask; - struct color_platform colors; - u8 state; -} __packed; - -struct wmax_u32_args { - u8 operation; - u8 arg1; - u8 arg2; - u8 arg3; -}; - -struct awcc_priv { - struct wmi_device *wdev; - struct device *ppdev; - enum wmax_thermal_mode supported_thermal_profiles[PLATFORM_PROFILE_LAST]; -}; - -struct alienfx_priv { - struct platform_device *pdev; - struct led_classdev global_led; - struct color_platform colors[4]; - u8 global_brightness; - u8 lighting_control_state; -}; - -struct alienfx_ops { - int (*upd_led)(struct alienfx_priv *priv, struct wmi_device *wdev, - u8 location); - int (*upd_brightness)(struct alienfx_priv *priv, struct wmi_device *wdev, - u8 brightness); -}; - -struct alienfx_platdata { - struct wmi_device *wdev; - struct alienfx_ops ops; -}; - -static u8 interface; - -struct awcc_quirks { - bool pprof; - bool gmode; -}; - -static struct awcc_quirks g_series_quirks = { - .pprof = true, - .gmode = true, -}; - -static struct awcc_quirks generic_quirks = { - .pprof = true, - .gmode = false, -}; - -static struct awcc_quirks empty_quirks; - -static const struct dmi_system_id awcc_dmi_table[] __initconst = { - { - .ident = "Alienware m16 R1 AMD", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), - DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m16 R1 AMD"), - }, - .driver_data = &generic_quirks, - }, - { - .ident = "Alienware m17 R5", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), - DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m17 R5 AMD"), - }, - .driver_data = &generic_quirks, - }, - { - .ident = "Alienware m18 R2", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), - DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m18 R2"), - }, - .driver_data = &generic_quirks, - }, - { - .ident = "Alienware x15 R1", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), - DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R1"), - }, - .driver_data = &generic_quirks, - }, - { - .ident = "Alienware x17 R2", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), - DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x17 R2"), - }, - .driver_data = &generic_quirks, - }, - { - .ident = "Dell Inc. G15 5510", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5510"), - }, - .driver_data = &g_series_quirks, - }, - { - .ident = "Dell Inc. G15 5511", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5511"), - }, - .driver_data = &g_series_quirks, - }, - { - .ident = "Dell Inc. G15 5515", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5515"), - }, - .driver_data = &g_series_quirks, - }, - { - .ident = "Dell Inc. G3 3500", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "G3 3500"), - }, - .driver_data = &g_series_quirks, - }, - { - .ident = "Dell Inc. G3 3590", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "G3 3590"), - }, - .driver_data = &g_series_quirks, - }, - { - .ident = "Dell Inc. G5 5500", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "G5 5500"), - }, - .driver_data = &g_series_quirks, - }, -}; - -static struct awcc_quirks *awcc; - -static int alienware_wmi_command(struct wmi_device *wdev, u32 method_id, - void *in_args, size_t in_size, u32 *out_data) -{ - struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; - struct acpi_buffer in = {in_size, in_args}; - acpi_status ret; - - ret = wmidev_evaluate_method(wdev, 0, method_id, &in, out_data ? &out : NULL); - if (ACPI_FAILURE(ret)) - return -EIO; - - union acpi_object *obj __free(kfree) = out.pointer; - - if (out_data) { - if (obj && obj->type == ACPI_TYPE_INTEGER) - *out_data = (u32)obj->integer.value; - else - return -ENOMSG; - } - - return 0; -} - -/* - * 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 = dev_get_drvdata(dev); - struct color_platform *colors = &priv->colors[location]; - - return sprintf(buf, "red: %d, green: %d, blue: %d\n", - colors->red, colors->green, colors->blue); - -} - -static ssize_t zone_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count, u8 location) -{ - struct alienfx_priv *priv = dev_get_drvdata(dev); - struct color_platform *colors = &priv->colors[location]; - struct alienfx_platdata *pdata = dev_get_platdata(dev); - int ret; - - ret = parse_rgb(buf, colors); - if (ret) - return ret; - - ret = pdata->ops.upd_led(priv, pdata->wdev, location); - - return ret ? ret : count; -} - -static ssize_t zone00_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - return zone_show(dev, attr, buf, 0); -} - -static ssize_t zone00_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - return zone_store(dev, attr, buf, count, 0); -} - -static DEVICE_ATTR_RW(zone00); - -static ssize_t zone01_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - return zone_show(dev, attr, buf, 1); -} - -static ssize_t zone01_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - return zone_store(dev, attr, buf, count, 1); -} - -static DEVICE_ATTR_RW(zone01); - -static ssize_t zone02_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - return zone_show(dev, attr, buf, 2); -} - -static ssize_t zone02_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - return zone_store(dev, attr, buf, count, 2); -} - -static DEVICE_ATTR_RW(zone02); - -static ssize_t zone03_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - return zone_show(dev, attr, buf, 3); -} - -static ssize_t zone03_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - return zone_store(dev, attr, buf, count, 3); -} - -static DEVICE_ATTR_RW(zone03); - -/* - * 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 = 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 = dev_get_drvdata(dev); - u8 val; - - if (strcmp(buf, "booting\n") == 0) - val = LEGACY_BOOTING; - else if (strcmp(buf, "suspend\n") == 0) - val = LEGACY_SUSPEND; - else if (interface == LEGACY) - val = LEGACY_RUNNING; - else - val = WMAX_RUNNING; - - 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) -{ - if (n < alienfx->num_zones + 1) - return attr->mode; - - return 0; -} - -static bool zone_group_visible(struct kobject *kobj) -{ - return alienfx->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 = container_of(led_cdev, struct alienfx_priv, - global_led); - struct alienfx_platdata *pdata = dev_get_platdata(&priv->pdev->dev); - int ret; - - 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 = 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 cable_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct alienfx_platdata *pdata = dev_get_platdata(dev); - struct wmax_basic_args in_args = { - .arg = 0, - }; - u32 out_data; - int ret; - - ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_HDMI_CABLE, - &in_args, sizeof(in_args), &out_data); - if (!ret) { - 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", ret); - return sysfs_emit(buf, "unconnected connected [unknown]\n"); -} - -static ssize_t source_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct alienfx_platdata *pdata = dev_get_platdata(dev); - struct wmax_basic_args in_args = { - .arg = 0, - }; - u32 out_data; - int ret; - - ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_HDMI_STATUS, - &in_args, sizeof(in_args), &out_data); - if (!ret) { - 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", ret); - return sysfs_emit(buf, "input gpu [unknown]\n"); -} - -static ssize_t source_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct alienfx_platdata *pdata = dev_get_platdata(dev); - struct wmax_basic_args args; - int ret; - - 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); - - ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_HDMI_SOURCE, &args, - sizeof(args), NULL); - if (ret < 0) - pr_err("alienware-wmi: HDMI toggle failed: results: %u\n", ret); - - return count; -} - -static DEVICE_ATTR_RO(cable); -static DEVICE_ATTR_RW(source); - -static bool hdmi_group_visible(struct kobject *kobj) -{ - return interface == WMAX && alienfx->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 status_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct alienfx_platdata *pdata = dev_get_platdata(dev); - struct wmax_basic_args in_args = { - .arg = 0, - }; - u32 out_data; - int ret; - - ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_AMPLIFIER_CABLE, - &in_args, sizeof(in_args), &out_data); - if (!ret) { - 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", ret); - return sysfs_emit(buf, "unconnected connected [unknown]\n"); -} - -static DEVICE_ATTR_RO(status); - -static bool amplifier_group_visible(struct kobject *kobj) -{ - return interface == WMAX && alienfx->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 deepsleep_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct alienfx_platdata *pdata = dev_get_platdata(dev); - struct wmax_basic_args in_args = { - .arg = 0, - }; - u32 out_data; - int ret; - - ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_DEEP_SLEEP_STATUS, - &in_args, sizeof(in_args), &out_data); - if (!ret) { - 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", ret); - return sysfs_emit(buf, "disabled s5 s5_s4 [unknown]\n"); -} - -static ssize_t deepsleep_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct alienfx_platdata *pdata = dev_get_platdata(dev); - struct wmax_basic_args args; - int ret; - - 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); - - ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_DEEP_SLEEP_CONTROL, - &args, sizeof(args), NULL); - if (!ret) - pr_err("alienware-wmi: deep sleep control failed: results: %u\n", ret); - - return count; -} - -static DEVICE_ATTR_RW(deepsleep); - -static bool deepsleep_group_visible(struct kobject *kobj) -{ - return interface == WMAX && alienfx->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) -{ - struct wmax_u32_args in_args = { - .operation = operation, - .arg1 = arg, - .arg2 = 0, - .arg3 = 0, - }; - int ret; - - ret = alienware_wmi_command(wdev, WMAX_METHOD_THERMAL_INFORMATION, - &in_args, sizeof(in_args), out_data); - if (ret < 0) - return ret; - - if (*out_data == WMAX_FAILURE_CODE) - return -EBADRQC; - - return 0; -} - -static int wmax_thermal_control(struct wmi_device *wdev, u8 profile) -{ - struct wmax_u32_args in_args = { - .operation = WMAX_OPERATION_ACTIVATE_PROFILE, - .arg1 = profile, - .arg2 = 0, - .arg3 = 0, - }; - u32 out_data; - int ret; - - ret = alienware_wmi_command(wdev, WMAX_METHOD_THERMAL_CONTROL, - &in_args, sizeof(in_args), &out_data); - if (ret) - return ret; - - 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) -{ - struct wmax_u32_args in_args = { - .operation = operation, - .arg1 = 0, - .arg2 = 0, - .arg3 = 0, - }; - int ret; - - ret = alienware_wmi_command(wdev, WMAX_METHOD_GAME_SHIFT_STATUS, - &in_args, sizeof(in_args), out_data); - if (ret < 0) - return ret; - - if (*out_data == WMAX_FAILURE_CODE) - return -EOPNOTSUPP; - - return 0; -} - -static int thermal_profile_get(struct device *dev, - enum platform_profile_option *profile) -{ - struct awcc_priv *priv = dev_get_drvdata(dev); - u32 out_data; - int ret; - - 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 device *dev, - enum platform_profile_option profile) -{ - struct awcc_priv *priv = dev_get_drvdata(dev); - - if (awcc->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]); -} - -static int thermal_profile_probe(void *drvdata, unsigned long *choices) -{ - enum platform_profile_option profile; - struct awcc_priv *priv = drvdata; - enum wmax_thermal_mode mode; - u8 sys_desc[4]; - u32 first_mode; - u32 out_data; - int ret; - - ret = wmax_thermal_information(priv->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(priv->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, choices); - } - - if (bitmap_empty(choices, PLATFORM_PROFILE_LAST)) - return -ENODEV; - - if (awcc->gmode) { - priv->supported_thermal_profiles[PLATFORM_PROFILE_PERFORMANCE] = - WMAX_THERMAL_MODE_GMODE; - - set_bit(PLATFORM_PROFILE_PERFORMANCE, choices); - } - - return 0; -} - -static const struct platform_profile_ops awcc_platform_profile_ops = { - .probe = thermal_profile_probe, - .profile_get = thermal_profile_get, - .profile_set = thermal_profile_set, -}; - -static int awcc_platform_profile_init(struct wmi_device *wdev) -{ - struct awcc_priv *priv = dev_get_drvdata(&wdev->dev); - - priv->ppdev = devm_platform_profile_register(&wdev->dev, "alienware-wmi", - priv, &awcc_platform_profile_ops); - - return PTR_ERR_OR_ZERO(priv->ppdev); -} - -static int alienware_awcc_setup(struct wmi_device *wdev) -{ - struct awcc_priv *priv; - int ret; - - priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - priv->wdev = wdev; - dev_set_drvdata(&wdev->dev, priv); - - if (awcc->pprof) { - ret = awcc_platform_profile_init(wdev); - if (ret) - return ret; - } - - return 0; -} - -/* - * Platform Driver - */ -static int alienfx_probe(struct platform_device *pdev) -{ - struct alienfx_priv *priv; - - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - if (interface == WMAX) - priv->lighting_control_state = WMAX_RUNNING; - else - priv->lighting_control_state = LEGACY_RUNNING; - - priv->pdev = pdev; - priv->global_led.name = "alienware::global_brightness"; - priv->global_led.brightness_set = global_led_set; - priv->global_led.brightness_get = global_led_get; - priv->global_led.max_brightness = 0x0F; - priv->global_brightness = priv->global_led.max_brightness; - platform_set_drvdata(pdev, priv); - - 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, -}; - -static void alienware_alienfx_remove(void *data) -{ - struct platform_device *pdev = data; - - platform_device_unregister(pdev); -} - -static int alienware_alienfx_setup(struct alienfx_platdata *pdata) -{ - struct device *dev = &pdata->wdev->dev; - struct platform_device *pdev; - int ret; - - pdev = platform_device_register_data(NULL, "alienware-wmi", - PLATFORM_DEVID_NONE, pdata, - sizeof(*pdata)); - if (IS_ERR(pdev)) - return PTR_ERR(pdev); - - dev_set_drvdata(dev, pdev); - ret = devm_add_action_or_reset(dev, alienware_alienfx_remove, pdev); - if (ret) - return ret; - - return 0; -} - -/* - * Legacy WMI driver - */ -static int legacy_wmi_update_led(struct alienfx_priv *priv, - struct wmi_device *wdev, u8 location) -{ - struct legacy_led_args legacy_args = { - .colors = priv->colors[location], - .brightness = priv->global_brightness, - .state = 0, - }; - struct acpi_buffer input; - acpi_status status; - - if (legacy_args.state != LEGACY_RUNNING) { - legacy_args.state = priv->lighting_control_state; - - input.length = sizeof(legacy_args); - input.pointer = &legacy_args; - - status = wmi_evaluate_method(LEGACY_POWER_CONTROL_GUID, 0, - location + 1, &input, NULL); - if (ACPI_FAILURE(status)) - return -EIO; - - return 0; - } - - return alienware_wmi_command(wdev, location + 1, &legacy_args, - sizeof(legacy_args), NULL); -} - -static int legacy_wmi_update_brightness(struct alienfx_priv *priv, - struct wmi_device *wdev, u8 brightness) -{ - return legacy_wmi_update_led(priv, wdev, 0); -} - -static int legacy_wmi_probe(struct wmi_device *wdev, const void *context) -{ - struct alienfx_platdata pdata = { - .wdev = wdev, - .ops = { - .upd_led = legacy_wmi_update_led, - .upd_brightness = legacy_wmi_update_brightness, - }, - }; - - return alienware_alienfx_setup(&pdata); -} - -static const struct wmi_device_id alienware_legacy_device_id_table[] = { - { LEGACY_CONTROL_GUID, NULL }, - { }, -}; -MODULE_DEVICE_TABLE(wmi, alienware_legacy_device_id_table); - -static struct wmi_driver alienware_legacy_wmi_driver = { - .driver = { - .name = "alienware-wmi-alienfx", - .probe_type = PROBE_PREFER_ASYNCHRONOUS, - }, - .id_table = alienware_legacy_device_id_table, - .probe = legacy_wmi_probe, - .no_singleton = true, -}; - -static int __init alienware_legacy_wmi_init(void) -{ - return wmi_driver_register(&alienware_legacy_wmi_driver); -} - -static void __exit alienware_legacy_wmi_exit(void) -{ - wmi_driver_unregister(&alienware_legacy_wmi_driver); -} - -/* - * WMAX WMI driver - */ -static int wmax_wmi_update_led(struct alienfx_priv *priv, - struct wmi_device *wdev, u8 location) -{ - struct wmax_led_args in_args = { - .led_mask = 1 << location, - .colors = priv->colors[location], - .state = priv->lighting_control_state, - }; - - return alienware_wmi_command(wdev, WMAX_METHOD_ZONE_CONTROL, &in_args, - sizeof(in_args), NULL); -} - -static int wmax_wmi_update_brightness(struct alienfx_priv *priv, - struct wmi_device *wdev, u8 brightness) -{ - struct wmax_brightness_args in_args = { - .led_mask = 0xFF, - .percentage = brightness, - }; - - return alienware_wmi_command(wdev, WMAX_METHOD_BRIGHTNESS, &in_args, - sizeof(in_args), NULL); -} - -static int wmax_wmi_probe(struct wmi_device *wdev, const void *context) -{ - struct alienfx_platdata pdata = { - .wdev = wdev, - .ops = { - .upd_led = wmax_wmi_update_led, - .upd_brightness = wmax_wmi_update_brightness, - }, - }; - int ret; - - if (awcc) - ret = alienware_awcc_setup(wdev); - else - ret = alienware_alienfx_setup(&pdata); - - return ret; -} - -static const struct wmi_device_id alienware_wmax_device_id_table[] = { - { WMAX_CONTROL_GUID, NULL }, - { }, -}; -MODULE_DEVICE_TABLE(wmi, alienware_wmax_device_id_table); - -static struct wmi_driver alienware_wmax_wmi_driver = { - .driver = { - .name = "alienware-wmi-wmax", - .probe_type = PROBE_PREFER_ASYNCHRONOUS, - }, - .id_table = alienware_wmax_device_id_table, - .probe = wmax_wmi_probe, - .no_singleton = true, -}; - -static int __init alienware_wmax_wmi_init(void) -{ - const struct dmi_system_id *id; - - id = dmi_first_match(awcc_dmi_table); - if (id) - awcc = id->driver_data; - - if (force_platform_profile) { - if (!awcc) - awcc = &empty_quirks; - - awcc->pprof = true; - } - - if (force_gmode) { - if (awcc) - awcc->gmode = true; - else - pr_warn("force_gmode requires platform profile support\n"); - } - - return wmi_driver_register(&alienware_wmax_wmi_driver); -} - -static void __exit alienware_wmax_wmi_exit(void) -{ - wmi_driver_unregister(&alienware_wmax_wmi_driver); -} - -static int __init alienware_wmi_init(void) -{ - int ret; - - dmi_check_system(alienware_quirks); - if (!alienfx) - alienfx = &quirk_unknown; - - ret = platform_driver_register(&platform_driver); - if (ret < 0) - return ret; - - if (wmi_has_guid(WMAX_CONTROL_GUID)) { - interface = WMAX; - ret = alienware_wmax_wmi_init(); - } else { - interface = LEGACY; - ret = alienware_legacy_wmi_init(); - } - - if (ret < 0) - platform_driver_unregister(&platform_driver); - - return ret; -} - -module_init(alienware_wmi_init); - -static void __exit alienware_wmi_exit(void) -{ - if (interface == WMAX) - alienware_wmax_wmi_exit(); - else - alienware_legacy_wmi_exit(); - - platform_driver_unregister(&platform_driver); -} - -module_exit(alienware_wmi_exit);