xref: /linux/drivers/platform/x86/dell/alienware-wmi-base.c (revision 4f9786035f9e519db41375818e1d0b5f20da2f10)
12e56ac8cSKurt Borja // SPDX-License-Identifier: GPL-2.0-or-later
22e56ac8cSKurt Borja /*
3*0738c302SKurt Borja  * Alienware special feature control
42e56ac8cSKurt Borja  *
52e56ac8cSKurt Borja  * Copyright (C) 2014 Dell Inc <Dell.Client.Kernel@dell.com>
6*0738c302SKurt Borja  * Copyright (C) 2025 Kurt Borja <kuurtb@gmail.com>
72e56ac8cSKurt Borja  */
82e56ac8cSKurt Borja 
92e56ac8cSKurt Borja #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
102e56ac8cSKurt Borja 
112e56ac8cSKurt Borja #include <linux/acpi.h>
122e56ac8cSKurt Borja #include <linux/cleanup.h>
132e56ac8cSKurt Borja #include <linux/module.h>
142e56ac8cSKurt Borja #include <linux/platform_device.h>
152e56ac8cSKurt Borja #include <linux/dmi.h>
162e56ac8cSKurt Borja #include <linux/leds.h>
17c5ebbaf1SKurt Borja #include "alienware-wmi.h"
182e56ac8cSKurt Borja 
192e56ac8cSKurt Borja MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
20*0738c302SKurt Borja MODULE_AUTHOR("Kurt Borja <kuurtb@gmail.com>");
212e56ac8cSKurt Borja MODULE_DESCRIPTION("Alienware special feature control");
222e56ac8cSKurt Borja MODULE_LICENSE("GPL");
232e56ac8cSKurt Borja 
24c5ebbaf1SKurt Borja struct alienfx_quirks *alienfx;
252e56ac8cSKurt Borja 
262e56ac8cSKurt Borja static struct alienfx_quirks quirk_inspiron5675 = {
272e56ac8cSKurt Borja 	.num_zones = 2,
282e56ac8cSKurt Borja 	.hdmi_mux = false,
292e56ac8cSKurt Borja 	.amplifier = false,
302e56ac8cSKurt Borja 	.deepslp = false,
312e56ac8cSKurt Borja };
322e56ac8cSKurt Borja 
332e56ac8cSKurt Borja static struct alienfx_quirks quirk_unknown = {
342e56ac8cSKurt Borja 	.num_zones = 2,
352e56ac8cSKurt Borja 	.hdmi_mux = false,
362e56ac8cSKurt Borja 	.amplifier = false,
372e56ac8cSKurt Borja 	.deepslp = false,
382e56ac8cSKurt Borja };
392e56ac8cSKurt Borja 
402e56ac8cSKurt Borja static struct alienfx_quirks quirk_x51_r1_r2 = {
412e56ac8cSKurt Borja 	.num_zones = 3,
422e56ac8cSKurt Borja 	.hdmi_mux = false,
432e56ac8cSKurt Borja 	.amplifier = false,
442e56ac8cSKurt Borja 	.deepslp = false,
452e56ac8cSKurt Borja };
462e56ac8cSKurt Borja 
472e56ac8cSKurt Borja static struct alienfx_quirks quirk_x51_r3 = {
482e56ac8cSKurt Borja 	.num_zones = 4,
492e56ac8cSKurt Borja 	.hdmi_mux = false,
502e56ac8cSKurt Borja 	.amplifier = true,
512e56ac8cSKurt Borja 	.deepslp = false,
522e56ac8cSKurt Borja };
532e56ac8cSKurt Borja 
542e56ac8cSKurt Borja static struct alienfx_quirks quirk_asm100 = {
552e56ac8cSKurt Borja 	.num_zones = 2,
562e56ac8cSKurt Borja 	.hdmi_mux = true,
572e56ac8cSKurt Borja 	.amplifier = false,
582e56ac8cSKurt Borja 	.deepslp = false,
592e56ac8cSKurt Borja };
602e56ac8cSKurt Borja 
612e56ac8cSKurt Borja static struct alienfx_quirks quirk_asm200 = {
622e56ac8cSKurt Borja 	.num_zones = 2,
632e56ac8cSKurt Borja 	.hdmi_mux = true,
642e56ac8cSKurt Borja 	.amplifier = false,
652e56ac8cSKurt Borja 	.deepslp = true,
662e56ac8cSKurt Borja };
672e56ac8cSKurt Borja 
682e56ac8cSKurt Borja static struct alienfx_quirks quirk_asm201 = {
692e56ac8cSKurt Borja 	.num_zones = 2,
702e56ac8cSKurt Borja 	.hdmi_mux = true,
712e56ac8cSKurt Borja 	.amplifier = true,
722e56ac8cSKurt Borja 	.deepslp = true,
732e56ac8cSKurt Borja };
742e56ac8cSKurt Borja 
752e56ac8cSKurt Borja static int __init dmi_matched(const struct dmi_system_id *dmi)
762e56ac8cSKurt Borja {
772e56ac8cSKurt Borja 	alienfx = dmi->driver_data;
782e56ac8cSKurt Borja 	return 1;
792e56ac8cSKurt Borja }
802e56ac8cSKurt Borja 
812e56ac8cSKurt Borja static const struct dmi_system_id alienware_quirks[] __initconst = {
822e56ac8cSKurt Borja 	{
832e56ac8cSKurt Borja 		.callback = dmi_matched,
842e56ac8cSKurt Borja 		.ident = "Alienware ASM100",
852e56ac8cSKurt Borja 		.matches = {
862e56ac8cSKurt Borja 			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
872e56ac8cSKurt Borja 			DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"),
882e56ac8cSKurt Borja 		},
892e56ac8cSKurt Borja 		.driver_data = &quirk_asm100,
902e56ac8cSKurt Borja 	},
912e56ac8cSKurt Borja 	{
922e56ac8cSKurt Borja 		.callback = dmi_matched,
932e56ac8cSKurt Borja 		.ident = "Alienware ASM200",
942e56ac8cSKurt Borja 		.matches = {
952e56ac8cSKurt Borja 			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
962e56ac8cSKurt Borja 			DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"),
972e56ac8cSKurt Borja 		},
982e56ac8cSKurt Borja 		.driver_data = &quirk_asm200,
992e56ac8cSKurt Borja 	},
1002e56ac8cSKurt Borja 	{
1012e56ac8cSKurt Borja 		.callback = dmi_matched,
1022e56ac8cSKurt Borja 		.ident = "Alienware ASM201",
1032e56ac8cSKurt Borja 		.matches = {
1042e56ac8cSKurt Borja 			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
1052e56ac8cSKurt Borja 			DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"),
1062e56ac8cSKurt Borja 		},
1072e56ac8cSKurt Borja 		.driver_data = &quirk_asm201,
1082e56ac8cSKurt Borja 	},
1092e56ac8cSKurt Borja 	{
1102e56ac8cSKurt Borja 		.callback = dmi_matched,
1112e56ac8cSKurt Borja 		.ident = "Alienware X51 R1",
1122e56ac8cSKurt Borja 		.matches = {
1132e56ac8cSKurt Borja 			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
1142e56ac8cSKurt Borja 			DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"),
1152e56ac8cSKurt Borja 		},
1162e56ac8cSKurt Borja 		.driver_data = &quirk_x51_r1_r2,
1172e56ac8cSKurt Borja 	},
1182e56ac8cSKurt Borja 	{
1192e56ac8cSKurt Borja 		.callback = dmi_matched,
1202e56ac8cSKurt Borja 		.ident = "Alienware X51 R2",
1212e56ac8cSKurt Borja 		.matches = {
1222e56ac8cSKurt Borja 			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
1232e56ac8cSKurt Borja 			DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"),
1242e56ac8cSKurt Borja 		},
1252e56ac8cSKurt Borja 		.driver_data = &quirk_x51_r1_r2,
1262e56ac8cSKurt Borja 	},
1272e56ac8cSKurt Borja 	{
1282e56ac8cSKurt Borja 		.callback = dmi_matched,
1292e56ac8cSKurt Borja 		.ident = "Alienware X51 R3",
1302e56ac8cSKurt Borja 		.matches = {
1312e56ac8cSKurt Borja 			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
1322e56ac8cSKurt Borja 			DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"),
1332e56ac8cSKurt Borja 		},
1342e56ac8cSKurt Borja 		.driver_data = &quirk_x51_r3,
1352e56ac8cSKurt Borja 	},
1362e56ac8cSKurt Borja 	{
1372e56ac8cSKurt Borja 		.callback = dmi_matched,
1382e56ac8cSKurt Borja 		.ident = "Dell Inc. Inspiron 5675",
1392e56ac8cSKurt Borja 		.matches = {
1402e56ac8cSKurt Borja 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
1412e56ac8cSKurt Borja 			DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"),
1422e56ac8cSKurt Borja 		},
1432e56ac8cSKurt Borja 		.driver_data = &quirk_inspiron5675,
1442e56ac8cSKurt Borja 	},
1452e56ac8cSKurt Borja 	{}
1462e56ac8cSKurt Borja };
1472e56ac8cSKurt Borja 
148c5ebbaf1SKurt Borja u8 alienware_interface;
1492e56ac8cSKurt Borja 
150c5ebbaf1SKurt Borja int alienware_wmi_command(struct wmi_device *wdev, u32 method_id,
1512e56ac8cSKurt Borja 			  void *in_args, size_t in_size, u32 *out_data)
1522e56ac8cSKurt Borja {
1532e56ac8cSKurt Borja 	struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
1542e56ac8cSKurt Borja 	struct acpi_buffer in = {in_size, in_args};
1552e56ac8cSKurt Borja 	acpi_status ret;
1562e56ac8cSKurt Borja 
1572e56ac8cSKurt Borja 	ret = wmidev_evaluate_method(wdev, 0, method_id, &in, out_data ? &out : NULL);
1582e56ac8cSKurt Borja 	if (ACPI_FAILURE(ret))
1592e56ac8cSKurt Borja 		return -EIO;
1602e56ac8cSKurt Borja 
1612e56ac8cSKurt Borja 	union acpi_object *obj __free(kfree) = out.pointer;
1622e56ac8cSKurt Borja 
1632e56ac8cSKurt Borja 	if (out_data) {
1642e56ac8cSKurt Borja 		if (obj && obj->type == ACPI_TYPE_INTEGER)
1652e56ac8cSKurt Borja 			*out_data = (u32)obj->integer.value;
1662e56ac8cSKurt Borja 		else
1672e56ac8cSKurt Borja 			return -ENOMSG;
1682e56ac8cSKurt Borja 	}
1692e56ac8cSKurt Borja 
1702e56ac8cSKurt Borja 	return 0;
1712e56ac8cSKurt Borja }
1722e56ac8cSKurt Borja 
1732e56ac8cSKurt Borja /*
1742e56ac8cSKurt Borja  * Helpers used for zone control
1752e56ac8cSKurt Borja  */
1762e56ac8cSKurt Borja static int parse_rgb(const char *buf, struct color_platform *colors)
1772e56ac8cSKurt Borja {
1782e56ac8cSKurt Borja 	long unsigned int rgb;
1792e56ac8cSKurt Borja 	int ret;
1802e56ac8cSKurt Borja 	union color_union {
1812e56ac8cSKurt Borja 		struct color_platform cp;
1822e56ac8cSKurt Borja 		int package;
1832e56ac8cSKurt Borja 	} repackager;
1842e56ac8cSKurt Borja 
1852e56ac8cSKurt Borja 	ret = kstrtoul(buf, 16, &rgb);
1862e56ac8cSKurt Borja 	if (ret)
1872e56ac8cSKurt Borja 		return ret;
1882e56ac8cSKurt Borja 
1892e56ac8cSKurt Borja 	/* RGB triplet notation is 24-bit hexadecimal */
1902e56ac8cSKurt Borja 	if (rgb > 0xFFFFFF)
1912e56ac8cSKurt Borja 		return -EINVAL;
1922e56ac8cSKurt Borja 
1932e56ac8cSKurt Borja 	repackager.package = rgb & 0x0f0f0f0f;
1942e56ac8cSKurt Borja 	pr_debug("alienware-wmi: r: %d g:%d b: %d\n",
1952e56ac8cSKurt Borja 		 repackager.cp.red, repackager.cp.green, repackager.cp.blue);
1962e56ac8cSKurt Borja 	*colors = repackager.cp;
1972e56ac8cSKurt Borja 	return 0;
1982e56ac8cSKurt Borja }
1992e56ac8cSKurt Borja 
2002e56ac8cSKurt Borja /*
2012e56ac8cSKurt Borja  * Individual RGB zone control
2022e56ac8cSKurt Borja  */
2032e56ac8cSKurt Borja static ssize_t zone_show(struct device *dev, struct device_attribute *attr,
2042e56ac8cSKurt Borja 			 char *buf, u8 location)
2052e56ac8cSKurt Borja {
2062e56ac8cSKurt Borja 	struct alienfx_priv *priv = dev_get_drvdata(dev);
2072e56ac8cSKurt Borja 	struct color_platform *colors = &priv->colors[location];
2082e56ac8cSKurt Borja 
2092e56ac8cSKurt Borja 	return sprintf(buf, "red: %d, green: %d, blue: %d\n",
2102e56ac8cSKurt Borja 		       colors->red, colors->green, colors->blue);
2112e56ac8cSKurt Borja 
2122e56ac8cSKurt Borja }
2132e56ac8cSKurt Borja 
2142e56ac8cSKurt Borja static ssize_t zone_store(struct device *dev, struct device_attribute *attr,
2152e56ac8cSKurt Borja 			  const char *buf, size_t count, u8 location)
2162e56ac8cSKurt Borja {
2172e56ac8cSKurt Borja 	struct alienfx_priv *priv = dev_get_drvdata(dev);
2182e56ac8cSKurt Borja 	struct color_platform *colors = &priv->colors[location];
2192e56ac8cSKurt Borja 	struct alienfx_platdata *pdata = dev_get_platdata(dev);
2202e56ac8cSKurt Borja 	int ret;
2212e56ac8cSKurt Borja 
2222e56ac8cSKurt Borja 	ret = parse_rgb(buf, colors);
2232e56ac8cSKurt Borja 	if (ret)
2242e56ac8cSKurt Borja 		return ret;
2252e56ac8cSKurt Borja 
2262e56ac8cSKurt Borja 	ret = pdata->ops.upd_led(priv, pdata->wdev, location);
2272e56ac8cSKurt Borja 
2282e56ac8cSKurt Borja 	return ret ? ret : count;
2292e56ac8cSKurt Borja }
2302e56ac8cSKurt Borja 
2312e56ac8cSKurt Borja static ssize_t zone00_show(struct device *dev, struct device_attribute *attr,
2322e56ac8cSKurt Borja 			   char *buf)
2332e56ac8cSKurt Borja {
2342e56ac8cSKurt Borja 	return zone_show(dev, attr, buf, 0);
2352e56ac8cSKurt Borja }
2362e56ac8cSKurt Borja 
2372e56ac8cSKurt Borja static ssize_t zone00_store(struct device *dev, struct device_attribute *attr,
2382e56ac8cSKurt Borja 			    const char *buf, size_t count)
2392e56ac8cSKurt Borja {
2402e56ac8cSKurt Borja 	return zone_store(dev, attr, buf, count, 0);
2412e56ac8cSKurt Borja }
2422e56ac8cSKurt Borja 
2432e56ac8cSKurt Borja static DEVICE_ATTR_RW(zone00);
2442e56ac8cSKurt Borja 
2452e56ac8cSKurt Borja static ssize_t zone01_show(struct device *dev, struct device_attribute *attr,
2462e56ac8cSKurt Borja 			   char *buf)
2472e56ac8cSKurt Borja {
2482e56ac8cSKurt Borja 	return zone_show(dev, attr, buf, 1);
2492e56ac8cSKurt Borja }
2502e56ac8cSKurt Borja 
2512e56ac8cSKurt Borja static ssize_t zone01_store(struct device *dev, struct device_attribute *attr,
2522e56ac8cSKurt Borja 			    const char *buf, size_t count)
2532e56ac8cSKurt Borja {
2542e56ac8cSKurt Borja 	return zone_store(dev, attr, buf, count, 1);
2552e56ac8cSKurt Borja }
2562e56ac8cSKurt Borja 
2572e56ac8cSKurt Borja static DEVICE_ATTR_RW(zone01);
2582e56ac8cSKurt Borja 
2592e56ac8cSKurt Borja static ssize_t zone02_show(struct device *dev, struct device_attribute *attr,
2602e56ac8cSKurt Borja 			   char *buf)
2612e56ac8cSKurt Borja {
2622e56ac8cSKurt Borja 	return zone_show(dev, attr, buf, 2);
2632e56ac8cSKurt Borja }
2642e56ac8cSKurt Borja 
2652e56ac8cSKurt Borja static ssize_t zone02_store(struct device *dev, struct device_attribute *attr,
2662e56ac8cSKurt Borja 			    const char *buf, size_t count)
2672e56ac8cSKurt Borja {
2682e56ac8cSKurt Borja 	return zone_store(dev, attr, buf, count, 2);
2692e56ac8cSKurt Borja }
2702e56ac8cSKurt Borja 
2712e56ac8cSKurt Borja static DEVICE_ATTR_RW(zone02);
2722e56ac8cSKurt Borja 
2732e56ac8cSKurt Borja static ssize_t zone03_show(struct device *dev, struct device_attribute *attr,
2742e56ac8cSKurt Borja 			   char *buf)
2752e56ac8cSKurt Borja {
2762e56ac8cSKurt Borja 	return zone_show(dev, attr, buf, 3);
2772e56ac8cSKurt Borja }
2782e56ac8cSKurt Borja 
2792e56ac8cSKurt Borja static ssize_t zone03_store(struct device *dev, struct device_attribute *attr,
2802e56ac8cSKurt Borja 			    const char *buf, size_t count)
2812e56ac8cSKurt Borja {
2822e56ac8cSKurt Borja 	return zone_store(dev, attr, buf, count, 3);
2832e56ac8cSKurt Borja }
2842e56ac8cSKurt Borja 
2852e56ac8cSKurt Borja static DEVICE_ATTR_RW(zone03);
2862e56ac8cSKurt Borja 
2872e56ac8cSKurt Borja /*
2882e56ac8cSKurt Borja  * Lighting control state device attribute (Global)
2892e56ac8cSKurt Borja  */
2902e56ac8cSKurt Borja static ssize_t lighting_control_state_show(struct device *dev,
2912e56ac8cSKurt Borja 					   struct device_attribute *attr,
2922e56ac8cSKurt Borja 					   char *buf)
2932e56ac8cSKurt Borja {
2942e56ac8cSKurt Borja 	struct alienfx_priv *priv = dev_get_drvdata(dev);
2952e56ac8cSKurt Borja 
2962e56ac8cSKurt Borja 	if (priv->lighting_control_state == LEGACY_BOOTING)
2972e56ac8cSKurt Borja 		return sysfs_emit(buf, "[booting] running suspend\n");
2982e56ac8cSKurt Borja 	else if (priv->lighting_control_state == LEGACY_SUSPEND)
2992e56ac8cSKurt Borja 		return sysfs_emit(buf, "booting running [suspend]\n");
3002e56ac8cSKurt Borja 
3012e56ac8cSKurt Borja 	return sysfs_emit(buf, "booting [running] suspend\n");
3022e56ac8cSKurt Borja }
3032e56ac8cSKurt Borja 
3042e56ac8cSKurt Borja static ssize_t lighting_control_state_store(struct device *dev,
3052e56ac8cSKurt Borja 					    struct device_attribute *attr,
3062e56ac8cSKurt Borja 					    const char *buf, size_t count)
3072e56ac8cSKurt Borja {
3082e56ac8cSKurt Borja 	struct alienfx_priv *priv = dev_get_drvdata(dev);
3092e56ac8cSKurt Borja 	u8 val;
3102e56ac8cSKurt Borja 
3112e56ac8cSKurt Borja 	if (strcmp(buf, "booting\n") == 0)
3122e56ac8cSKurt Borja 		val = LEGACY_BOOTING;
3132e56ac8cSKurt Borja 	else if (strcmp(buf, "suspend\n") == 0)
3142e56ac8cSKurt Borja 		val = LEGACY_SUSPEND;
315c5ebbaf1SKurt Borja 	else if (alienware_interface == LEGACY)
3162e56ac8cSKurt Borja 		val = LEGACY_RUNNING;
3172e56ac8cSKurt Borja 	else
3182e56ac8cSKurt Borja 		val = WMAX_RUNNING;
3192e56ac8cSKurt Borja 
3202e56ac8cSKurt Borja 	priv->lighting_control_state = val;
3212e56ac8cSKurt Borja 	pr_debug("alienware-wmi: updated control state to %d\n",
3222e56ac8cSKurt Borja 		 priv->lighting_control_state);
3232e56ac8cSKurt Borja 
3242e56ac8cSKurt Borja 	return count;
3252e56ac8cSKurt Borja }
3262e56ac8cSKurt Borja 
3272e56ac8cSKurt Borja static DEVICE_ATTR_RW(lighting_control_state);
3282e56ac8cSKurt Borja 
3292e56ac8cSKurt Borja static umode_t zone_attr_visible(struct kobject *kobj,
3302e56ac8cSKurt Borja 				 struct attribute *attr, int n)
3312e56ac8cSKurt Borja {
3322e56ac8cSKurt Borja 	if (n < alienfx->num_zones + 1)
3332e56ac8cSKurt Borja 		return attr->mode;
3342e56ac8cSKurt Borja 
3352e56ac8cSKurt Borja 	return 0;
3362e56ac8cSKurt Borja }
3372e56ac8cSKurt Borja 
3382e56ac8cSKurt Borja static bool zone_group_visible(struct kobject *kobj)
3392e56ac8cSKurt Borja {
3402e56ac8cSKurt Borja 	return alienfx->num_zones > 0;
3412e56ac8cSKurt Borja }
3422e56ac8cSKurt Borja DEFINE_SYSFS_GROUP_VISIBLE(zone);
3432e56ac8cSKurt Borja 
3442e56ac8cSKurt Borja static struct attribute *zone_attrs[] = {
3452e56ac8cSKurt Borja 	&dev_attr_lighting_control_state.attr,
3462e56ac8cSKurt Borja 	&dev_attr_zone00.attr,
3472e56ac8cSKurt Borja 	&dev_attr_zone01.attr,
3482e56ac8cSKurt Borja 	&dev_attr_zone02.attr,
3492e56ac8cSKurt Borja 	&dev_attr_zone03.attr,
3502e56ac8cSKurt Borja 	NULL
3512e56ac8cSKurt Borja };
3522e56ac8cSKurt Borja 
3532e56ac8cSKurt Borja static struct attribute_group zone_attribute_group = {
3542e56ac8cSKurt Borja 	.name = "rgb_zones",
3552e56ac8cSKurt Borja 	.is_visible = SYSFS_GROUP_VISIBLE(zone),
3562e56ac8cSKurt Borja 	.attrs = zone_attrs,
3572e56ac8cSKurt Borja };
3582e56ac8cSKurt Borja 
3592e56ac8cSKurt Borja /*
3602e56ac8cSKurt Borja  * LED Brightness (Global)
3612e56ac8cSKurt Borja  */
3622e56ac8cSKurt Borja static void global_led_set(struct led_classdev *led_cdev,
3632e56ac8cSKurt Borja 			   enum led_brightness brightness)
3642e56ac8cSKurt Borja {
3652e56ac8cSKurt Borja 	struct alienfx_priv *priv = container_of(led_cdev, struct alienfx_priv,
3662e56ac8cSKurt Borja 						 global_led);
3672e56ac8cSKurt Borja 	struct alienfx_platdata *pdata = dev_get_platdata(&priv->pdev->dev);
3682e56ac8cSKurt Borja 	int ret;
3692e56ac8cSKurt Borja 
3702e56ac8cSKurt Borja 	priv->global_brightness = brightness;
3712e56ac8cSKurt Borja 
3722e56ac8cSKurt Borja 	ret = pdata->ops.upd_brightness(priv, pdata->wdev, brightness);
3732e56ac8cSKurt Borja 	if (ret)
3742e56ac8cSKurt Borja 		pr_err("LED brightness update failed\n");
3752e56ac8cSKurt Borja }
3762e56ac8cSKurt Borja 
3772e56ac8cSKurt Borja static enum led_brightness global_led_get(struct led_classdev *led_cdev)
3782e56ac8cSKurt Borja {
3792e56ac8cSKurt Borja 	struct alienfx_priv *priv = container_of(led_cdev, struct alienfx_priv,
3802e56ac8cSKurt Borja 						 global_led);
3812e56ac8cSKurt Borja 
3822e56ac8cSKurt Borja 	return priv->global_brightness;
3832e56ac8cSKurt Borja }
3842e56ac8cSKurt Borja 
3852e56ac8cSKurt Borja /*
3862e56ac8cSKurt Borja  * Platform Driver
3872e56ac8cSKurt Borja  */
3882e56ac8cSKurt Borja static int alienfx_probe(struct platform_device *pdev)
3892e56ac8cSKurt Borja {
3902e56ac8cSKurt Borja 	struct alienfx_priv *priv;
3912e56ac8cSKurt Borja 
3922e56ac8cSKurt Borja 	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
3932e56ac8cSKurt Borja 	if (!priv)
3942e56ac8cSKurt Borja 		return -ENOMEM;
3952e56ac8cSKurt Borja 
396c5ebbaf1SKurt Borja 	if (alienware_interface == WMAX)
3972e56ac8cSKurt Borja 		priv->lighting_control_state = WMAX_RUNNING;
3982e56ac8cSKurt Borja 	else
3992e56ac8cSKurt Borja 		priv->lighting_control_state = LEGACY_RUNNING;
4002e56ac8cSKurt Borja 
4012e56ac8cSKurt Borja 	priv->pdev = pdev;
4022e56ac8cSKurt Borja 	priv->global_led.name = "alienware::global_brightness";
4032e56ac8cSKurt Borja 	priv->global_led.brightness_set = global_led_set;
4042e56ac8cSKurt Borja 	priv->global_led.brightness_get = global_led_get;
4052e56ac8cSKurt Borja 	priv->global_led.max_brightness = 0x0F;
4062e56ac8cSKurt Borja 	priv->global_brightness = priv->global_led.max_brightness;
4072e56ac8cSKurt Borja 	platform_set_drvdata(pdev, priv);
4082e56ac8cSKurt Borja 
4092e56ac8cSKurt Borja 	return devm_led_classdev_register(&pdev->dev, &priv->global_led);
4102e56ac8cSKurt Borja }
4112e56ac8cSKurt Borja 
4122e56ac8cSKurt Borja static const struct attribute_group *alienfx_groups[] = {
4132e56ac8cSKurt Borja 	&zone_attribute_group,
414c5ebbaf1SKurt Borja 	WMAX_DEV_GROUPS
4152e56ac8cSKurt Borja 	NULL
4162e56ac8cSKurt Borja };
4172e56ac8cSKurt Borja 
4182e56ac8cSKurt Borja static struct platform_driver platform_driver = {
4192e56ac8cSKurt Borja 	.driver = {
4202e56ac8cSKurt Borja 		.name = "alienware-wmi",
4212e56ac8cSKurt Borja 		.dev_groups = alienfx_groups,
4222e56ac8cSKurt Borja 	},
4232e56ac8cSKurt Borja 	.probe = alienfx_probe,
4242e56ac8cSKurt Borja };
4252e56ac8cSKurt Borja 
4262e56ac8cSKurt Borja static void alienware_alienfx_remove(void *data)
4272e56ac8cSKurt Borja {
4282e56ac8cSKurt Borja 	struct platform_device *pdev = data;
4292e56ac8cSKurt Borja 
4302e56ac8cSKurt Borja 	platform_device_unregister(pdev);
4312e56ac8cSKurt Borja }
4322e56ac8cSKurt Borja 
433c5ebbaf1SKurt Borja int alienware_alienfx_setup(struct alienfx_platdata *pdata)
4342e56ac8cSKurt Borja {
4352e56ac8cSKurt Borja 	struct device *dev = &pdata->wdev->dev;
4362e56ac8cSKurt Borja 	struct platform_device *pdev;
4372e56ac8cSKurt Borja 	int ret;
4382e56ac8cSKurt Borja 
4392e56ac8cSKurt Borja 	pdev = platform_device_register_data(NULL, "alienware-wmi",
4402e56ac8cSKurt Borja 					     PLATFORM_DEVID_NONE, pdata,
4412e56ac8cSKurt Borja 					     sizeof(*pdata));
4422e56ac8cSKurt Borja 	if (IS_ERR(pdev))
4432e56ac8cSKurt Borja 		return PTR_ERR(pdev);
4442e56ac8cSKurt Borja 
4452e56ac8cSKurt Borja 	dev_set_drvdata(dev, pdev);
4462e56ac8cSKurt Borja 	ret = devm_add_action_or_reset(dev, alienware_alienfx_remove, pdev);
4472e56ac8cSKurt Borja 	if (ret)
4482e56ac8cSKurt Borja 		return ret;
4492e56ac8cSKurt Borja 
4502e56ac8cSKurt Borja 	return 0;
4512e56ac8cSKurt Borja }
4522e56ac8cSKurt Borja 
4532e56ac8cSKurt Borja static int __init alienware_wmi_init(void)
4542e56ac8cSKurt Borja {
4552e56ac8cSKurt Borja 	int ret;
4562e56ac8cSKurt Borja 
4572e56ac8cSKurt Borja 	dmi_check_system(alienware_quirks);
4582e56ac8cSKurt Borja 	if (!alienfx)
4592e56ac8cSKurt Borja 		alienfx = &quirk_unknown;
4602e56ac8cSKurt Borja 
4612e56ac8cSKurt Borja 	ret = platform_driver_register(&platform_driver);
4622e56ac8cSKurt Borja 	if (ret < 0)
4632e56ac8cSKurt Borja 		return ret;
4642e56ac8cSKurt Borja 
4652e56ac8cSKurt Borja 	if (wmi_has_guid(WMAX_CONTROL_GUID)) {
466c5ebbaf1SKurt Borja 		alienware_interface = WMAX;
4672e56ac8cSKurt Borja 		ret = alienware_wmax_wmi_init();
4682e56ac8cSKurt Borja 	} else {
469c5ebbaf1SKurt Borja 		alienware_interface = LEGACY;
4702e56ac8cSKurt Borja 		ret = alienware_legacy_wmi_init();
4712e56ac8cSKurt Borja 	}
4722e56ac8cSKurt Borja 
4732e56ac8cSKurt Borja 	if (ret < 0)
4742e56ac8cSKurt Borja 		platform_driver_unregister(&platform_driver);
4752e56ac8cSKurt Borja 
4762e56ac8cSKurt Borja 	return ret;
4772e56ac8cSKurt Borja }
4782e56ac8cSKurt Borja 
4792e56ac8cSKurt Borja module_init(alienware_wmi_init);
4802e56ac8cSKurt Borja 
4812e56ac8cSKurt Borja static void __exit alienware_wmi_exit(void)
4822e56ac8cSKurt Borja {
483c5ebbaf1SKurt Borja 	if (alienware_interface == WMAX)
4842e56ac8cSKurt Borja 		alienware_wmax_wmi_exit();
4852e56ac8cSKurt Borja 	else
4862e56ac8cSKurt Borja 		alienware_legacy_wmi_exit();
4872e56ac8cSKurt Borja 
4882e56ac8cSKurt Borja 	platform_driver_unregister(&platform_driver);
4892e56ac8cSKurt Borja }
4902e56ac8cSKurt Borja 
4912e56ac8cSKurt Borja module_exit(alienware_wmi_exit);
492