162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Alienware AlienFX control 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2014 Dell Inc <Dell.Client.Kernel@dell.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/acpi.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/platform_device.h> 1362306a36Sopenharmony_ci#include <linux/dmi.h> 1462306a36Sopenharmony_ci#include <linux/leds.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define LEGACY_CONTROL_GUID "A90597CE-A997-11DA-B012-B622A1EF5492" 1762306a36Sopenharmony_ci#define LEGACY_POWER_CONTROL_GUID "A80593CE-A997-11DA-B012-B622A1EF5492" 1862306a36Sopenharmony_ci#define WMAX_CONTROL_GUID "A70591CE-A997-11DA-B012-B622A1EF5492" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define WMAX_METHOD_HDMI_SOURCE 0x1 2162306a36Sopenharmony_ci#define WMAX_METHOD_HDMI_STATUS 0x2 2262306a36Sopenharmony_ci#define WMAX_METHOD_BRIGHTNESS 0x3 2362306a36Sopenharmony_ci#define WMAX_METHOD_ZONE_CONTROL 0x4 2462306a36Sopenharmony_ci#define WMAX_METHOD_HDMI_CABLE 0x5 2562306a36Sopenharmony_ci#define WMAX_METHOD_AMPLIFIER_CABLE 0x6 2662306a36Sopenharmony_ci#define WMAX_METHOD_DEEP_SLEEP_CONTROL 0x0B 2762306a36Sopenharmony_ci#define WMAX_METHOD_DEEP_SLEEP_STATUS 0x0C 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ciMODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>"); 3062306a36Sopenharmony_ciMODULE_DESCRIPTION("Alienware special feature control"); 3162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3262306a36Sopenharmony_ciMODULE_ALIAS("wmi:" LEGACY_CONTROL_GUID); 3362306a36Sopenharmony_ciMODULE_ALIAS("wmi:" WMAX_CONTROL_GUID); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cienum INTERFACE_FLAGS { 3662306a36Sopenharmony_ci LEGACY, 3762306a36Sopenharmony_ci WMAX, 3862306a36Sopenharmony_ci}; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cienum LEGACY_CONTROL_STATES { 4162306a36Sopenharmony_ci LEGACY_RUNNING = 1, 4262306a36Sopenharmony_ci LEGACY_BOOTING = 0, 4362306a36Sopenharmony_ci LEGACY_SUSPEND = 3, 4462306a36Sopenharmony_ci}; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cienum WMAX_CONTROL_STATES { 4762306a36Sopenharmony_ci WMAX_RUNNING = 0xFF, 4862306a36Sopenharmony_ci WMAX_BOOTING = 0, 4962306a36Sopenharmony_ci WMAX_SUSPEND = 3, 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistruct quirk_entry { 5362306a36Sopenharmony_ci u8 num_zones; 5462306a36Sopenharmony_ci u8 hdmi_mux; 5562306a36Sopenharmony_ci u8 amplifier; 5662306a36Sopenharmony_ci u8 deepslp; 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic struct quirk_entry *quirks; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic struct quirk_entry quirk_inspiron5675 = { 6362306a36Sopenharmony_ci .num_zones = 2, 6462306a36Sopenharmony_ci .hdmi_mux = 0, 6562306a36Sopenharmony_ci .amplifier = 0, 6662306a36Sopenharmony_ci .deepslp = 0, 6762306a36Sopenharmony_ci}; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic struct quirk_entry quirk_unknown = { 7062306a36Sopenharmony_ci .num_zones = 2, 7162306a36Sopenharmony_ci .hdmi_mux = 0, 7262306a36Sopenharmony_ci .amplifier = 0, 7362306a36Sopenharmony_ci .deepslp = 0, 7462306a36Sopenharmony_ci}; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic struct quirk_entry quirk_x51_r1_r2 = { 7762306a36Sopenharmony_ci .num_zones = 3, 7862306a36Sopenharmony_ci .hdmi_mux = 0, 7962306a36Sopenharmony_ci .amplifier = 0, 8062306a36Sopenharmony_ci .deepslp = 0, 8162306a36Sopenharmony_ci}; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic struct quirk_entry quirk_x51_r3 = { 8462306a36Sopenharmony_ci .num_zones = 4, 8562306a36Sopenharmony_ci .hdmi_mux = 0, 8662306a36Sopenharmony_ci .amplifier = 1, 8762306a36Sopenharmony_ci .deepslp = 0, 8862306a36Sopenharmony_ci}; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic struct quirk_entry quirk_asm100 = { 9162306a36Sopenharmony_ci .num_zones = 2, 9262306a36Sopenharmony_ci .hdmi_mux = 1, 9362306a36Sopenharmony_ci .amplifier = 0, 9462306a36Sopenharmony_ci .deepslp = 0, 9562306a36Sopenharmony_ci}; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic struct quirk_entry quirk_asm200 = { 9862306a36Sopenharmony_ci .num_zones = 2, 9962306a36Sopenharmony_ci .hdmi_mux = 1, 10062306a36Sopenharmony_ci .amplifier = 0, 10162306a36Sopenharmony_ci .deepslp = 1, 10262306a36Sopenharmony_ci}; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic struct quirk_entry quirk_asm201 = { 10562306a36Sopenharmony_ci .num_zones = 2, 10662306a36Sopenharmony_ci .hdmi_mux = 1, 10762306a36Sopenharmony_ci .amplifier = 1, 10862306a36Sopenharmony_ci .deepslp = 1, 10962306a36Sopenharmony_ci}; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic int __init dmi_matched(const struct dmi_system_id *dmi) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci quirks = dmi->driver_data; 11462306a36Sopenharmony_ci return 1; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic const struct dmi_system_id alienware_quirks[] __initconst = { 11862306a36Sopenharmony_ci { 11962306a36Sopenharmony_ci .callback = dmi_matched, 12062306a36Sopenharmony_ci .ident = "Alienware X51 R3", 12162306a36Sopenharmony_ci .matches = { 12262306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), 12362306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"), 12462306a36Sopenharmony_ci }, 12562306a36Sopenharmony_ci .driver_data = &quirk_x51_r3, 12662306a36Sopenharmony_ci }, 12762306a36Sopenharmony_ci { 12862306a36Sopenharmony_ci .callback = dmi_matched, 12962306a36Sopenharmony_ci .ident = "Alienware X51 R2", 13062306a36Sopenharmony_ci .matches = { 13162306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), 13262306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"), 13362306a36Sopenharmony_ci }, 13462306a36Sopenharmony_ci .driver_data = &quirk_x51_r1_r2, 13562306a36Sopenharmony_ci }, 13662306a36Sopenharmony_ci { 13762306a36Sopenharmony_ci .callback = dmi_matched, 13862306a36Sopenharmony_ci .ident = "Alienware X51 R1", 13962306a36Sopenharmony_ci .matches = { 14062306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), 14162306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"), 14262306a36Sopenharmony_ci }, 14362306a36Sopenharmony_ci .driver_data = &quirk_x51_r1_r2, 14462306a36Sopenharmony_ci }, 14562306a36Sopenharmony_ci { 14662306a36Sopenharmony_ci .callback = dmi_matched, 14762306a36Sopenharmony_ci .ident = "Alienware ASM100", 14862306a36Sopenharmony_ci .matches = { 14962306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), 15062306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"), 15162306a36Sopenharmony_ci }, 15262306a36Sopenharmony_ci .driver_data = &quirk_asm100, 15362306a36Sopenharmony_ci }, 15462306a36Sopenharmony_ci { 15562306a36Sopenharmony_ci .callback = dmi_matched, 15662306a36Sopenharmony_ci .ident = "Alienware ASM200", 15762306a36Sopenharmony_ci .matches = { 15862306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), 15962306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"), 16062306a36Sopenharmony_ci }, 16162306a36Sopenharmony_ci .driver_data = &quirk_asm200, 16262306a36Sopenharmony_ci }, 16362306a36Sopenharmony_ci { 16462306a36Sopenharmony_ci .callback = dmi_matched, 16562306a36Sopenharmony_ci .ident = "Alienware ASM201", 16662306a36Sopenharmony_ci .matches = { 16762306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), 16862306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"), 16962306a36Sopenharmony_ci }, 17062306a36Sopenharmony_ci .driver_data = &quirk_asm201, 17162306a36Sopenharmony_ci }, 17262306a36Sopenharmony_ci { 17362306a36Sopenharmony_ci .callback = dmi_matched, 17462306a36Sopenharmony_ci .ident = "Dell Inc. Inspiron 5675", 17562306a36Sopenharmony_ci .matches = { 17662306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 17762306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"), 17862306a36Sopenharmony_ci }, 17962306a36Sopenharmony_ci .driver_data = &quirk_inspiron5675, 18062306a36Sopenharmony_ci }, 18162306a36Sopenharmony_ci {} 18262306a36Sopenharmony_ci}; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistruct color_platform { 18562306a36Sopenharmony_ci u8 blue; 18662306a36Sopenharmony_ci u8 green; 18762306a36Sopenharmony_ci u8 red; 18862306a36Sopenharmony_ci} __packed; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistruct platform_zone { 19162306a36Sopenharmony_ci u8 location; 19262306a36Sopenharmony_ci struct device_attribute *attr; 19362306a36Sopenharmony_ci struct color_platform colors; 19462306a36Sopenharmony_ci}; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistruct wmax_brightness_args { 19762306a36Sopenharmony_ci u32 led_mask; 19862306a36Sopenharmony_ci u32 percentage; 19962306a36Sopenharmony_ci}; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistruct wmax_basic_args { 20262306a36Sopenharmony_ci u8 arg; 20362306a36Sopenharmony_ci}; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistruct legacy_led_args { 20662306a36Sopenharmony_ci struct color_platform colors; 20762306a36Sopenharmony_ci u8 brightness; 20862306a36Sopenharmony_ci u8 state; 20962306a36Sopenharmony_ci} __packed; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistruct wmax_led_args { 21262306a36Sopenharmony_ci u32 led_mask; 21362306a36Sopenharmony_ci struct color_platform colors; 21462306a36Sopenharmony_ci u8 state; 21562306a36Sopenharmony_ci} __packed; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic struct platform_device *platform_device; 21862306a36Sopenharmony_cistatic struct device_attribute *zone_dev_attrs; 21962306a36Sopenharmony_cistatic struct attribute **zone_attrs; 22062306a36Sopenharmony_cistatic struct platform_zone *zone_data; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic struct platform_driver platform_driver = { 22362306a36Sopenharmony_ci .driver = { 22462306a36Sopenharmony_ci .name = "alienware-wmi", 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci}; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic struct attribute_group zone_attribute_group = { 22962306a36Sopenharmony_ci .name = "rgb_zones", 23062306a36Sopenharmony_ci}; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic u8 interface; 23362306a36Sopenharmony_cistatic u8 lighting_control_state; 23462306a36Sopenharmony_cistatic u8 global_brightness; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci/* 23762306a36Sopenharmony_ci * Helpers used for zone control 23862306a36Sopenharmony_ci */ 23962306a36Sopenharmony_cistatic int parse_rgb(const char *buf, struct platform_zone *zone) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci long unsigned int rgb; 24262306a36Sopenharmony_ci int ret; 24362306a36Sopenharmony_ci union color_union { 24462306a36Sopenharmony_ci struct color_platform cp; 24562306a36Sopenharmony_ci int package; 24662306a36Sopenharmony_ci } repackager; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci ret = kstrtoul(buf, 16, &rgb); 24962306a36Sopenharmony_ci if (ret) 25062306a36Sopenharmony_ci return ret; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* RGB triplet notation is 24-bit hexadecimal */ 25362306a36Sopenharmony_ci if (rgb > 0xFFFFFF) 25462306a36Sopenharmony_ci return -EINVAL; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci repackager.package = rgb & 0x0f0f0f0f; 25762306a36Sopenharmony_ci pr_debug("alienware-wmi: r: %d g:%d b: %d\n", 25862306a36Sopenharmony_ci repackager.cp.red, repackager.cp.green, repackager.cp.blue); 25962306a36Sopenharmony_ci zone->colors = repackager.cp; 26062306a36Sopenharmony_ci return 0; 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic struct platform_zone *match_zone(struct device_attribute *attr) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci u8 zone; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci for (zone = 0; zone < quirks->num_zones; zone++) { 26862306a36Sopenharmony_ci if ((struct device_attribute *)zone_data[zone].attr == attr) { 26962306a36Sopenharmony_ci pr_debug("alienware-wmi: matched zone location: %d\n", 27062306a36Sopenharmony_ci zone_data[zone].location); 27162306a36Sopenharmony_ci return &zone_data[zone]; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci return NULL; 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci/* 27862306a36Sopenharmony_ci * Individual RGB zone control 27962306a36Sopenharmony_ci */ 28062306a36Sopenharmony_cistatic int alienware_update_led(struct platform_zone *zone) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci int method_id; 28362306a36Sopenharmony_ci acpi_status status; 28462306a36Sopenharmony_ci char *guid; 28562306a36Sopenharmony_ci struct acpi_buffer input; 28662306a36Sopenharmony_ci struct legacy_led_args legacy_args; 28762306a36Sopenharmony_ci struct wmax_led_args wmax_basic_args; 28862306a36Sopenharmony_ci if (interface == WMAX) { 28962306a36Sopenharmony_ci wmax_basic_args.led_mask = 1 << zone->location; 29062306a36Sopenharmony_ci wmax_basic_args.colors = zone->colors; 29162306a36Sopenharmony_ci wmax_basic_args.state = lighting_control_state; 29262306a36Sopenharmony_ci guid = WMAX_CONTROL_GUID; 29362306a36Sopenharmony_ci method_id = WMAX_METHOD_ZONE_CONTROL; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci input.length = (acpi_size) sizeof(wmax_basic_args); 29662306a36Sopenharmony_ci input.pointer = &wmax_basic_args; 29762306a36Sopenharmony_ci } else { 29862306a36Sopenharmony_ci legacy_args.colors = zone->colors; 29962306a36Sopenharmony_ci legacy_args.brightness = global_brightness; 30062306a36Sopenharmony_ci legacy_args.state = 0; 30162306a36Sopenharmony_ci if (lighting_control_state == LEGACY_BOOTING || 30262306a36Sopenharmony_ci lighting_control_state == LEGACY_SUSPEND) { 30362306a36Sopenharmony_ci guid = LEGACY_POWER_CONTROL_GUID; 30462306a36Sopenharmony_ci legacy_args.state = lighting_control_state; 30562306a36Sopenharmony_ci } else 30662306a36Sopenharmony_ci guid = LEGACY_CONTROL_GUID; 30762306a36Sopenharmony_ci method_id = zone->location + 1; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci input.length = (acpi_size) sizeof(legacy_args); 31062306a36Sopenharmony_ci input.pointer = &legacy_args; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci pr_debug("alienware-wmi: guid %s method %d\n", guid, method_id); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci status = wmi_evaluate_method(guid, 0, method_id, &input, NULL); 31562306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 31662306a36Sopenharmony_ci pr_err("alienware-wmi: zone set failure: %u\n", status); 31762306a36Sopenharmony_ci return ACPI_FAILURE(status); 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic ssize_t zone_show(struct device *dev, struct device_attribute *attr, 32162306a36Sopenharmony_ci char *buf) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci struct platform_zone *target_zone; 32462306a36Sopenharmony_ci target_zone = match_zone(attr); 32562306a36Sopenharmony_ci if (target_zone == NULL) 32662306a36Sopenharmony_ci return sprintf(buf, "red: -1, green: -1, blue: -1\n"); 32762306a36Sopenharmony_ci return sprintf(buf, "red: %d, green: %d, blue: %d\n", 32862306a36Sopenharmony_ci target_zone->colors.red, 32962306a36Sopenharmony_ci target_zone->colors.green, target_zone->colors.blue); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistatic ssize_t zone_set(struct device *dev, struct device_attribute *attr, 33462306a36Sopenharmony_ci const char *buf, size_t count) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci struct platform_zone *target_zone; 33762306a36Sopenharmony_ci int ret; 33862306a36Sopenharmony_ci target_zone = match_zone(attr); 33962306a36Sopenharmony_ci if (target_zone == NULL) { 34062306a36Sopenharmony_ci pr_err("alienware-wmi: invalid target zone\n"); 34162306a36Sopenharmony_ci return 1; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci ret = parse_rgb(buf, target_zone); 34462306a36Sopenharmony_ci if (ret) 34562306a36Sopenharmony_ci return ret; 34662306a36Sopenharmony_ci ret = alienware_update_led(target_zone); 34762306a36Sopenharmony_ci return ret ? ret : count; 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci/* 35162306a36Sopenharmony_ci * LED Brightness (Global) 35262306a36Sopenharmony_ci */ 35362306a36Sopenharmony_cistatic int wmax_brightness(int brightness) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci acpi_status status; 35662306a36Sopenharmony_ci struct acpi_buffer input; 35762306a36Sopenharmony_ci struct wmax_brightness_args args = { 35862306a36Sopenharmony_ci .led_mask = 0xFF, 35962306a36Sopenharmony_ci .percentage = brightness, 36062306a36Sopenharmony_ci }; 36162306a36Sopenharmony_ci input.length = (acpi_size) sizeof(args); 36262306a36Sopenharmony_ci input.pointer = &args; 36362306a36Sopenharmony_ci status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0, 36462306a36Sopenharmony_ci WMAX_METHOD_BRIGHTNESS, &input, NULL); 36562306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 36662306a36Sopenharmony_ci pr_err("alienware-wmi: brightness set failure: %u\n", status); 36762306a36Sopenharmony_ci return ACPI_FAILURE(status); 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistatic void global_led_set(struct led_classdev *led_cdev, 37162306a36Sopenharmony_ci enum led_brightness brightness) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci int ret; 37462306a36Sopenharmony_ci global_brightness = brightness; 37562306a36Sopenharmony_ci if (interface == WMAX) 37662306a36Sopenharmony_ci ret = wmax_brightness(brightness); 37762306a36Sopenharmony_ci else 37862306a36Sopenharmony_ci ret = alienware_update_led(&zone_data[0]); 37962306a36Sopenharmony_ci if (ret) 38062306a36Sopenharmony_ci pr_err("LED brightness update failed\n"); 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic enum led_brightness global_led_get(struct led_classdev *led_cdev) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci return global_brightness; 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistatic struct led_classdev global_led = { 38962306a36Sopenharmony_ci .brightness_set = global_led_set, 39062306a36Sopenharmony_ci .brightness_get = global_led_get, 39162306a36Sopenharmony_ci .name = "alienware::global_brightness", 39262306a36Sopenharmony_ci}; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci/* 39562306a36Sopenharmony_ci * Lighting control state device attribute (Global) 39662306a36Sopenharmony_ci */ 39762306a36Sopenharmony_cistatic ssize_t show_control_state(struct device *dev, 39862306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci if (lighting_control_state == LEGACY_BOOTING) 40162306a36Sopenharmony_ci return sysfs_emit(buf, "[booting] running suspend\n"); 40262306a36Sopenharmony_ci else if (lighting_control_state == LEGACY_SUSPEND) 40362306a36Sopenharmony_ci return sysfs_emit(buf, "booting running [suspend]\n"); 40462306a36Sopenharmony_ci return sysfs_emit(buf, "booting [running] suspend\n"); 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistatic ssize_t store_control_state(struct device *dev, 40862306a36Sopenharmony_ci struct device_attribute *attr, 40962306a36Sopenharmony_ci const char *buf, size_t count) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci long unsigned int val; 41262306a36Sopenharmony_ci if (strcmp(buf, "booting\n") == 0) 41362306a36Sopenharmony_ci val = LEGACY_BOOTING; 41462306a36Sopenharmony_ci else if (strcmp(buf, "suspend\n") == 0) 41562306a36Sopenharmony_ci val = LEGACY_SUSPEND; 41662306a36Sopenharmony_ci else if (interface == LEGACY) 41762306a36Sopenharmony_ci val = LEGACY_RUNNING; 41862306a36Sopenharmony_ci else 41962306a36Sopenharmony_ci val = WMAX_RUNNING; 42062306a36Sopenharmony_ci lighting_control_state = val; 42162306a36Sopenharmony_ci pr_debug("alienware-wmi: updated control state to %d\n", 42262306a36Sopenharmony_ci lighting_control_state); 42362306a36Sopenharmony_ci return count; 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic DEVICE_ATTR(lighting_control_state, 0644, show_control_state, 42762306a36Sopenharmony_ci store_control_state); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cistatic int alienware_zone_init(struct platform_device *dev) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci u8 zone; 43262306a36Sopenharmony_ci char buffer[10]; 43362306a36Sopenharmony_ci char *name; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (interface == WMAX) { 43662306a36Sopenharmony_ci lighting_control_state = WMAX_RUNNING; 43762306a36Sopenharmony_ci } else if (interface == LEGACY) { 43862306a36Sopenharmony_ci lighting_control_state = LEGACY_RUNNING; 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci global_led.max_brightness = 0x0F; 44162306a36Sopenharmony_ci global_brightness = global_led.max_brightness; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci /* 44462306a36Sopenharmony_ci * - zone_dev_attrs num_zones + 1 is for individual zones and then 44562306a36Sopenharmony_ci * null terminated 44662306a36Sopenharmony_ci * - zone_attrs num_zones + 2 is for all attrs in zone_dev_attrs + 44762306a36Sopenharmony_ci * the lighting control + null terminated 44862306a36Sopenharmony_ci * - zone_data num_zones is for the distinct zones 44962306a36Sopenharmony_ci */ 45062306a36Sopenharmony_ci zone_dev_attrs = 45162306a36Sopenharmony_ci kcalloc(quirks->num_zones + 1, sizeof(struct device_attribute), 45262306a36Sopenharmony_ci GFP_KERNEL); 45362306a36Sopenharmony_ci if (!zone_dev_attrs) 45462306a36Sopenharmony_ci return -ENOMEM; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci zone_attrs = 45762306a36Sopenharmony_ci kcalloc(quirks->num_zones + 2, sizeof(struct attribute *), 45862306a36Sopenharmony_ci GFP_KERNEL); 45962306a36Sopenharmony_ci if (!zone_attrs) 46062306a36Sopenharmony_ci return -ENOMEM; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci zone_data = 46362306a36Sopenharmony_ci kcalloc(quirks->num_zones, sizeof(struct platform_zone), 46462306a36Sopenharmony_ci GFP_KERNEL); 46562306a36Sopenharmony_ci if (!zone_data) 46662306a36Sopenharmony_ci return -ENOMEM; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci for (zone = 0; zone < quirks->num_zones; zone++) { 46962306a36Sopenharmony_ci sprintf(buffer, "zone%02hhX", zone); 47062306a36Sopenharmony_ci name = kstrdup(buffer, GFP_KERNEL); 47162306a36Sopenharmony_ci if (name == NULL) 47262306a36Sopenharmony_ci return 1; 47362306a36Sopenharmony_ci sysfs_attr_init(&zone_dev_attrs[zone].attr); 47462306a36Sopenharmony_ci zone_dev_attrs[zone].attr.name = name; 47562306a36Sopenharmony_ci zone_dev_attrs[zone].attr.mode = 0644; 47662306a36Sopenharmony_ci zone_dev_attrs[zone].show = zone_show; 47762306a36Sopenharmony_ci zone_dev_attrs[zone].store = zone_set; 47862306a36Sopenharmony_ci zone_data[zone].location = zone; 47962306a36Sopenharmony_ci zone_attrs[zone] = &zone_dev_attrs[zone].attr; 48062306a36Sopenharmony_ci zone_data[zone].attr = &zone_dev_attrs[zone]; 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci zone_attrs[quirks->num_zones] = &dev_attr_lighting_control_state.attr; 48362306a36Sopenharmony_ci zone_attribute_group.attrs = zone_attrs; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci led_classdev_register(&dev->dev, &global_led); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci return sysfs_create_group(&dev->dev.kobj, &zone_attribute_group); 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_cistatic void alienware_zone_exit(struct platform_device *dev) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci u8 zone; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci sysfs_remove_group(&dev->dev.kobj, &zone_attribute_group); 49562306a36Sopenharmony_ci led_classdev_unregister(&global_led); 49662306a36Sopenharmony_ci if (zone_dev_attrs) { 49762306a36Sopenharmony_ci for (zone = 0; zone < quirks->num_zones; zone++) 49862306a36Sopenharmony_ci kfree(zone_dev_attrs[zone].attr.name); 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci kfree(zone_dev_attrs); 50162306a36Sopenharmony_ci kfree(zone_data); 50262306a36Sopenharmony_ci kfree(zone_attrs); 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_cistatic acpi_status alienware_wmax_command(struct wmax_basic_args *in_args, 50662306a36Sopenharmony_ci u32 command, int *out_data) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci acpi_status status; 50962306a36Sopenharmony_ci union acpi_object *obj; 51062306a36Sopenharmony_ci struct acpi_buffer input; 51162306a36Sopenharmony_ci struct acpi_buffer output; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci input.length = (acpi_size) sizeof(*in_args); 51462306a36Sopenharmony_ci input.pointer = in_args; 51562306a36Sopenharmony_ci if (out_data) { 51662306a36Sopenharmony_ci output.length = ACPI_ALLOCATE_BUFFER; 51762306a36Sopenharmony_ci output.pointer = NULL; 51862306a36Sopenharmony_ci status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0, 51962306a36Sopenharmony_ci command, &input, &output); 52062306a36Sopenharmony_ci if (ACPI_SUCCESS(status)) { 52162306a36Sopenharmony_ci obj = (union acpi_object *)output.pointer; 52262306a36Sopenharmony_ci if (obj && obj->type == ACPI_TYPE_INTEGER) 52362306a36Sopenharmony_ci *out_data = (u32)obj->integer.value; 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci kfree(output.pointer); 52662306a36Sopenharmony_ci } else { 52762306a36Sopenharmony_ci status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0, 52862306a36Sopenharmony_ci command, &input, NULL); 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci return status; 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci/* 53462306a36Sopenharmony_ci * The HDMI mux sysfs node indicates the status of the HDMI input mux. 53562306a36Sopenharmony_ci * It can toggle between standard system GPU output and HDMI input. 53662306a36Sopenharmony_ci */ 53762306a36Sopenharmony_cistatic ssize_t show_hdmi_cable(struct device *dev, 53862306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci acpi_status status; 54162306a36Sopenharmony_ci u32 out_data; 54262306a36Sopenharmony_ci struct wmax_basic_args in_args = { 54362306a36Sopenharmony_ci .arg = 0, 54462306a36Sopenharmony_ci }; 54562306a36Sopenharmony_ci status = 54662306a36Sopenharmony_ci alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE, 54762306a36Sopenharmony_ci (u32 *) &out_data); 54862306a36Sopenharmony_ci if (ACPI_SUCCESS(status)) { 54962306a36Sopenharmony_ci if (out_data == 0) 55062306a36Sopenharmony_ci return sysfs_emit(buf, "[unconnected] connected unknown\n"); 55162306a36Sopenharmony_ci else if (out_data == 1) 55262306a36Sopenharmony_ci return sysfs_emit(buf, "unconnected [connected] unknown\n"); 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci pr_err("alienware-wmi: unknown HDMI cable status: %d\n", status); 55562306a36Sopenharmony_ci return sysfs_emit(buf, "unconnected connected [unknown]\n"); 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_cistatic ssize_t show_hdmi_source(struct device *dev, 55962306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 56062306a36Sopenharmony_ci{ 56162306a36Sopenharmony_ci acpi_status status; 56262306a36Sopenharmony_ci u32 out_data; 56362306a36Sopenharmony_ci struct wmax_basic_args in_args = { 56462306a36Sopenharmony_ci .arg = 0, 56562306a36Sopenharmony_ci }; 56662306a36Sopenharmony_ci status = 56762306a36Sopenharmony_ci alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS, 56862306a36Sopenharmony_ci (u32 *) &out_data); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci if (ACPI_SUCCESS(status)) { 57162306a36Sopenharmony_ci if (out_data == 1) 57262306a36Sopenharmony_ci return sysfs_emit(buf, "[input] gpu unknown\n"); 57362306a36Sopenharmony_ci else if (out_data == 2) 57462306a36Sopenharmony_ci return sysfs_emit(buf, "input [gpu] unknown\n"); 57562306a36Sopenharmony_ci } 57662306a36Sopenharmony_ci pr_err("alienware-wmi: unknown HDMI source status: %u\n", status); 57762306a36Sopenharmony_ci return sysfs_emit(buf, "input gpu [unknown]\n"); 57862306a36Sopenharmony_ci} 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_cistatic ssize_t toggle_hdmi_source(struct device *dev, 58162306a36Sopenharmony_ci struct device_attribute *attr, 58262306a36Sopenharmony_ci const char *buf, size_t count) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci acpi_status status; 58562306a36Sopenharmony_ci struct wmax_basic_args args; 58662306a36Sopenharmony_ci if (strcmp(buf, "gpu\n") == 0) 58762306a36Sopenharmony_ci args.arg = 1; 58862306a36Sopenharmony_ci else if (strcmp(buf, "input\n") == 0) 58962306a36Sopenharmony_ci args.arg = 2; 59062306a36Sopenharmony_ci else 59162306a36Sopenharmony_ci args.arg = 3; 59262306a36Sopenharmony_ci pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci status = alienware_wmax_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 59762306a36Sopenharmony_ci pr_err("alienware-wmi: HDMI toggle failed: results: %u\n", 59862306a36Sopenharmony_ci status); 59962306a36Sopenharmony_ci return count; 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic DEVICE_ATTR(cable, S_IRUGO, show_hdmi_cable, NULL); 60362306a36Sopenharmony_cistatic DEVICE_ATTR(source, S_IRUGO | S_IWUSR, show_hdmi_source, 60462306a36Sopenharmony_ci toggle_hdmi_source); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_cistatic struct attribute *hdmi_attrs[] = { 60762306a36Sopenharmony_ci &dev_attr_cable.attr, 60862306a36Sopenharmony_ci &dev_attr_source.attr, 60962306a36Sopenharmony_ci NULL, 61062306a36Sopenharmony_ci}; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_cistatic const struct attribute_group hdmi_attribute_group = { 61362306a36Sopenharmony_ci .name = "hdmi", 61462306a36Sopenharmony_ci .attrs = hdmi_attrs, 61562306a36Sopenharmony_ci}; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_cistatic void remove_hdmi(struct platform_device *dev) 61862306a36Sopenharmony_ci{ 61962306a36Sopenharmony_ci if (quirks->hdmi_mux > 0) 62062306a36Sopenharmony_ci sysfs_remove_group(&dev->dev.kobj, &hdmi_attribute_group); 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_cistatic int create_hdmi(struct platform_device *dev) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci int ret; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci ret = sysfs_create_group(&dev->dev.kobj, &hdmi_attribute_group); 62862306a36Sopenharmony_ci if (ret) 62962306a36Sopenharmony_ci remove_hdmi(dev); 63062306a36Sopenharmony_ci return ret; 63162306a36Sopenharmony_ci} 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci/* 63462306a36Sopenharmony_ci * Alienware GFX amplifier support 63562306a36Sopenharmony_ci * - Currently supports reading cable status 63662306a36Sopenharmony_ci * - Leaving expansion room to possibly support dock/undock events later 63762306a36Sopenharmony_ci */ 63862306a36Sopenharmony_cistatic ssize_t show_amplifier_status(struct device *dev, 63962306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci acpi_status status; 64262306a36Sopenharmony_ci u32 out_data; 64362306a36Sopenharmony_ci struct wmax_basic_args in_args = { 64462306a36Sopenharmony_ci .arg = 0, 64562306a36Sopenharmony_ci }; 64662306a36Sopenharmony_ci status = 64762306a36Sopenharmony_ci alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE, 64862306a36Sopenharmony_ci (u32 *) &out_data); 64962306a36Sopenharmony_ci if (ACPI_SUCCESS(status)) { 65062306a36Sopenharmony_ci if (out_data == 0) 65162306a36Sopenharmony_ci return sysfs_emit(buf, "[unconnected] connected unknown\n"); 65262306a36Sopenharmony_ci else if (out_data == 1) 65362306a36Sopenharmony_ci return sysfs_emit(buf, "unconnected [connected] unknown\n"); 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci pr_err("alienware-wmi: unknown amplifier cable status: %d\n", status); 65662306a36Sopenharmony_ci return sysfs_emit(buf, "unconnected connected [unknown]\n"); 65762306a36Sopenharmony_ci} 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_cistatic DEVICE_ATTR(status, S_IRUGO, show_amplifier_status, NULL); 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_cistatic struct attribute *amplifier_attrs[] = { 66262306a36Sopenharmony_ci &dev_attr_status.attr, 66362306a36Sopenharmony_ci NULL, 66462306a36Sopenharmony_ci}; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_cistatic const struct attribute_group amplifier_attribute_group = { 66762306a36Sopenharmony_ci .name = "amplifier", 66862306a36Sopenharmony_ci .attrs = amplifier_attrs, 66962306a36Sopenharmony_ci}; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_cistatic void remove_amplifier(struct platform_device *dev) 67262306a36Sopenharmony_ci{ 67362306a36Sopenharmony_ci if (quirks->amplifier > 0) 67462306a36Sopenharmony_ci sysfs_remove_group(&dev->dev.kobj, &lifier_attribute_group); 67562306a36Sopenharmony_ci} 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_cistatic int create_amplifier(struct platform_device *dev) 67862306a36Sopenharmony_ci{ 67962306a36Sopenharmony_ci int ret; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci ret = sysfs_create_group(&dev->dev.kobj, &lifier_attribute_group); 68262306a36Sopenharmony_ci if (ret) 68362306a36Sopenharmony_ci remove_amplifier(dev); 68462306a36Sopenharmony_ci return ret; 68562306a36Sopenharmony_ci} 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci/* 68862306a36Sopenharmony_ci * Deep Sleep Control support 68962306a36Sopenharmony_ci * - Modifies BIOS setting for deep sleep control allowing extra wakeup events 69062306a36Sopenharmony_ci */ 69162306a36Sopenharmony_cistatic ssize_t show_deepsleep_status(struct device *dev, 69262306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 69362306a36Sopenharmony_ci{ 69462306a36Sopenharmony_ci acpi_status status; 69562306a36Sopenharmony_ci u32 out_data; 69662306a36Sopenharmony_ci struct wmax_basic_args in_args = { 69762306a36Sopenharmony_ci .arg = 0, 69862306a36Sopenharmony_ci }; 69962306a36Sopenharmony_ci status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS, 70062306a36Sopenharmony_ci (u32 *) &out_data); 70162306a36Sopenharmony_ci if (ACPI_SUCCESS(status)) { 70262306a36Sopenharmony_ci if (out_data == 0) 70362306a36Sopenharmony_ci return sysfs_emit(buf, "[disabled] s5 s5_s4\n"); 70462306a36Sopenharmony_ci else if (out_data == 1) 70562306a36Sopenharmony_ci return sysfs_emit(buf, "disabled [s5] s5_s4\n"); 70662306a36Sopenharmony_ci else if (out_data == 2) 70762306a36Sopenharmony_ci return sysfs_emit(buf, "disabled s5 [s5_s4]\n"); 70862306a36Sopenharmony_ci } 70962306a36Sopenharmony_ci pr_err("alienware-wmi: unknown deep sleep status: %d\n", status); 71062306a36Sopenharmony_ci return sysfs_emit(buf, "disabled s5 s5_s4 [unknown]\n"); 71162306a36Sopenharmony_ci} 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_cistatic ssize_t toggle_deepsleep(struct device *dev, 71462306a36Sopenharmony_ci struct device_attribute *attr, 71562306a36Sopenharmony_ci const char *buf, size_t count) 71662306a36Sopenharmony_ci{ 71762306a36Sopenharmony_ci acpi_status status; 71862306a36Sopenharmony_ci struct wmax_basic_args args; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci if (strcmp(buf, "disabled\n") == 0) 72162306a36Sopenharmony_ci args.arg = 0; 72262306a36Sopenharmony_ci else if (strcmp(buf, "s5\n") == 0) 72362306a36Sopenharmony_ci args.arg = 1; 72462306a36Sopenharmony_ci else 72562306a36Sopenharmony_ci args.arg = 2; 72662306a36Sopenharmony_ci pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci status = alienware_wmax_command(&args, WMAX_METHOD_DEEP_SLEEP_CONTROL, 72962306a36Sopenharmony_ci NULL); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 73262306a36Sopenharmony_ci pr_err("alienware-wmi: deep sleep control failed: results: %u\n", 73362306a36Sopenharmony_ci status); 73462306a36Sopenharmony_ci return count; 73562306a36Sopenharmony_ci} 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_cistatic DEVICE_ATTR(deepsleep, S_IRUGO | S_IWUSR, show_deepsleep_status, toggle_deepsleep); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_cistatic struct attribute *deepsleep_attrs[] = { 74062306a36Sopenharmony_ci &dev_attr_deepsleep.attr, 74162306a36Sopenharmony_ci NULL, 74262306a36Sopenharmony_ci}; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_cistatic const struct attribute_group deepsleep_attribute_group = { 74562306a36Sopenharmony_ci .name = "deepsleep", 74662306a36Sopenharmony_ci .attrs = deepsleep_attrs, 74762306a36Sopenharmony_ci}; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_cistatic void remove_deepsleep(struct platform_device *dev) 75062306a36Sopenharmony_ci{ 75162306a36Sopenharmony_ci if (quirks->deepslp > 0) 75262306a36Sopenharmony_ci sysfs_remove_group(&dev->dev.kobj, &deepsleep_attribute_group); 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_cistatic int create_deepsleep(struct platform_device *dev) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci int ret; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci ret = sysfs_create_group(&dev->dev.kobj, &deepsleep_attribute_group); 76062306a36Sopenharmony_ci if (ret) 76162306a36Sopenharmony_ci remove_deepsleep(dev); 76262306a36Sopenharmony_ci return ret; 76362306a36Sopenharmony_ci} 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_cistatic int __init alienware_wmi_init(void) 76662306a36Sopenharmony_ci{ 76762306a36Sopenharmony_ci int ret; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci if (wmi_has_guid(LEGACY_CONTROL_GUID)) 77062306a36Sopenharmony_ci interface = LEGACY; 77162306a36Sopenharmony_ci else if (wmi_has_guid(WMAX_CONTROL_GUID)) 77262306a36Sopenharmony_ci interface = WMAX; 77362306a36Sopenharmony_ci else { 77462306a36Sopenharmony_ci pr_warn("alienware-wmi: No known WMI GUID found\n"); 77562306a36Sopenharmony_ci return -ENODEV; 77662306a36Sopenharmony_ci } 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci dmi_check_system(alienware_quirks); 77962306a36Sopenharmony_ci if (quirks == NULL) 78062306a36Sopenharmony_ci quirks = &quirk_unknown; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci ret = platform_driver_register(&platform_driver); 78362306a36Sopenharmony_ci if (ret) 78462306a36Sopenharmony_ci goto fail_platform_driver; 78562306a36Sopenharmony_ci platform_device = platform_device_alloc("alienware-wmi", PLATFORM_DEVID_NONE); 78662306a36Sopenharmony_ci if (!platform_device) { 78762306a36Sopenharmony_ci ret = -ENOMEM; 78862306a36Sopenharmony_ci goto fail_platform_device1; 78962306a36Sopenharmony_ci } 79062306a36Sopenharmony_ci ret = platform_device_add(platform_device); 79162306a36Sopenharmony_ci if (ret) 79262306a36Sopenharmony_ci goto fail_platform_device2; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci if (quirks->hdmi_mux > 0) { 79562306a36Sopenharmony_ci ret = create_hdmi(platform_device); 79662306a36Sopenharmony_ci if (ret) 79762306a36Sopenharmony_ci goto fail_prep_hdmi; 79862306a36Sopenharmony_ci } 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci if (quirks->amplifier > 0) { 80162306a36Sopenharmony_ci ret = create_amplifier(platform_device); 80262306a36Sopenharmony_ci if (ret) 80362306a36Sopenharmony_ci goto fail_prep_amplifier; 80462306a36Sopenharmony_ci } 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci if (quirks->deepslp > 0) { 80762306a36Sopenharmony_ci ret = create_deepsleep(platform_device); 80862306a36Sopenharmony_ci if (ret) 80962306a36Sopenharmony_ci goto fail_prep_deepsleep; 81062306a36Sopenharmony_ci } 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci ret = alienware_zone_init(platform_device); 81362306a36Sopenharmony_ci if (ret) 81462306a36Sopenharmony_ci goto fail_prep_zones; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci return 0; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_cifail_prep_zones: 81962306a36Sopenharmony_ci alienware_zone_exit(platform_device); 82062306a36Sopenharmony_cifail_prep_deepsleep: 82162306a36Sopenharmony_cifail_prep_amplifier: 82262306a36Sopenharmony_cifail_prep_hdmi: 82362306a36Sopenharmony_ci platform_device_del(platform_device); 82462306a36Sopenharmony_cifail_platform_device2: 82562306a36Sopenharmony_ci platform_device_put(platform_device); 82662306a36Sopenharmony_cifail_platform_device1: 82762306a36Sopenharmony_ci platform_driver_unregister(&platform_driver); 82862306a36Sopenharmony_cifail_platform_driver: 82962306a36Sopenharmony_ci return ret; 83062306a36Sopenharmony_ci} 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_cimodule_init(alienware_wmi_init); 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_cistatic void __exit alienware_wmi_exit(void) 83562306a36Sopenharmony_ci{ 83662306a36Sopenharmony_ci if (platform_device) { 83762306a36Sopenharmony_ci alienware_zone_exit(platform_device); 83862306a36Sopenharmony_ci remove_hdmi(platform_device); 83962306a36Sopenharmony_ci platform_device_unregister(platform_device); 84062306a36Sopenharmony_ci platform_driver_unregister(&platform_driver); 84162306a36Sopenharmony_ci } 84262306a36Sopenharmony_ci} 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_cimodule_exit(alienware_wmi_exit); 845