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