^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-or-later
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Alienware AlienFX control
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (C) 2014 Dell Inc <mario_limonciello@dell.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <linux/acpi.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/dmi.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/leds.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #define LEGACY_CONTROL_GUID "A90597CE-A997-11DA-B012-B622A1EF5492"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #define LEGACY_POWER_CONTROL_GUID "A80593CE-A997-11DA-B012-B622A1EF5492"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #define WMAX_CONTROL_GUID "A70591CE-A997-11DA-B012-B622A1EF5492"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #define WMAX_METHOD_HDMI_SOURCE 0x1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #define WMAX_METHOD_HDMI_STATUS 0x2
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) #define WMAX_METHOD_BRIGHTNESS 0x3
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) #define WMAX_METHOD_ZONE_CONTROL 0x4
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) #define WMAX_METHOD_HDMI_CABLE 0x5
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) #define WMAX_METHOD_AMPLIFIER_CABLE 0x6
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) #define WMAX_METHOD_DEEP_SLEEP_CONTROL 0x0B
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) #define WMAX_METHOD_DEEP_SLEEP_STATUS 0x0C
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) MODULE_AUTHOR("Mario Limonciello <mario_limonciello@dell.com>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) MODULE_DESCRIPTION("Alienware special feature control");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) MODULE_LICENSE("GPL");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) MODULE_ALIAS("wmi:" LEGACY_CONTROL_GUID);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) MODULE_ALIAS("wmi:" WMAX_CONTROL_GUID);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) enum INTERFACE_FLAGS {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) LEGACY,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) WMAX,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) enum LEGACY_CONTROL_STATES {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) LEGACY_RUNNING = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) LEGACY_BOOTING = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) LEGACY_SUSPEND = 3,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) enum WMAX_CONTROL_STATES {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) WMAX_RUNNING = 0xFF,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) WMAX_BOOTING = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) WMAX_SUSPEND = 3,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) struct quirk_entry {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) u8 num_zones;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) u8 hdmi_mux;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) u8 amplifier;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) u8 deepslp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) static struct quirk_entry *quirks;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) static struct quirk_entry quirk_inspiron5675 = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) .num_zones = 2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) .hdmi_mux = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) .amplifier = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) .deepslp = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) static struct quirk_entry quirk_unknown = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) .num_zones = 2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) .hdmi_mux = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) .amplifier = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) .deepslp = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) static struct quirk_entry quirk_x51_r1_r2 = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) .num_zones = 3,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) .hdmi_mux = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) .amplifier = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) .deepslp = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) static struct quirk_entry quirk_x51_r3 = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) .num_zones = 4,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) .hdmi_mux = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) .amplifier = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) .deepslp = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) static struct quirk_entry quirk_asm100 = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) .num_zones = 2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) .hdmi_mux = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) .amplifier = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) .deepslp = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) static struct quirk_entry quirk_asm200 = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) .num_zones = 2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) .hdmi_mux = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) .amplifier = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) .deepslp = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) static struct quirk_entry quirk_asm201 = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) .num_zones = 2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) .hdmi_mux = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) .amplifier = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) .deepslp = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) static int __init dmi_matched(const struct dmi_system_id *dmi)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) quirks = dmi->driver_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) return 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) static const struct dmi_system_id alienware_quirks[] __initconst = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) .callback = dmi_matched,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) .ident = "Alienware X51 R3",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) .matches = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) .driver_data = &quirk_x51_r3,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) .callback = dmi_matched,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) .ident = "Alienware X51 R2",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) .matches = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) .driver_data = &quirk_x51_r1_r2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) .callback = dmi_matched,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) .ident = "Alienware X51 R1",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) .matches = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) .driver_data = &quirk_x51_r1_r2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) .callback = dmi_matched,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) .ident = "Alienware ASM100",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) .matches = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) .driver_data = &quirk_asm100,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) .callback = dmi_matched,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) .ident = "Alienware ASM200",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) .matches = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) .driver_data = &quirk_asm200,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) .callback = dmi_matched,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) .ident = "Alienware ASM201",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) .matches = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) .driver_data = &quirk_asm201,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) .callback = dmi_matched,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) .ident = "Dell Inc. Inspiron 5675",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) .matches = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) .driver_data = &quirk_inspiron5675,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) {}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) struct color_platform {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) u8 blue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) u8 green;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) u8 red;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) } __packed;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) struct platform_zone {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) u8 location;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) struct device_attribute *attr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) struct color_platform colors;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) struct wmax_brightness_args {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) u32 led_mask;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) u32 percentage;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) struct wmax_basic_args {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) u8 arg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) struct legacy_led_args {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) struct color_platform colors;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) u8 brightness;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) u8 state;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) } __packed;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) struct wmax_led_args {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) u32 led_mask;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) struct color_platform colors;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) u8 state;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) } __packed;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) static struct platform_device *platform_device;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) static struct device_attribute *zone_dev_attrs;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) static struct attribute **zone_attrs;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) static struct platform_zone *zone_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) static struct platform_driver platform_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) .name = "alienware-wmi",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) static struct attribute_group zone_attribute_group = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) .name = "rgb_zones",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) static u8 interface;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) static u8 lighting_control_state;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) static u8 global_brightness;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) * Helpers used for zone control
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) static int parse_rgb(const char *buf, struct platform_zone *zone)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) long unsigned int rgb;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) union color_union {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) struct color_platform cp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) int package;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) } repackager;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) ret = kstrtoul(buf, 16, &rgb);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) /* RGB triplet notation is 24-bit hexadecimal */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) if (rgb > 0xFFFFFF)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) repackager.package = rgb & 0x0f0f0f0f;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) pr_debug("alienware-wmi: r: %d g:%d b: %d\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) repackager.cp.red, repackager.cp.green, repackager.cp.blue);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) zone->colors = repackager.cp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) static struct platform_zone *match_zone(struct device_attribute *attr)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) u8 zone;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) for (zone = 0; zone < quirks->num_zones; zone++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) if ((struct device_attribute *)zone_data[zone].attr == attr) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) pr_debug("alienware-wmi: matched zone location: %d\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) zone_data[zone].location);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) return &zone_data[zone];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) return NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) * Individual RGB zone control
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) static int alienware_update_led(struct platform_zone *zone)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) int method_id;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) acpi_status status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) char *guid;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) struct acpi_buffer input;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) struct legacy_led_args legacy_args;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) struct wmax_led_args wmax_basic_args;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) if (interface == WMAX) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) wmax_basic_args.led_mask = 1 << zone->location;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) wmax_basic_args.colors = zone->colors;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) wmax_basic_args.state = lighting_control_state;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) guid = WMAX_CONTROL_GUID;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) method_id = WMAX_METHOD_ZONE_CONTROL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) input.length = (acpi_size) sizeof(wmax_basic_args);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) input.pointer = &wmax_basic_args;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) legacy_args.colors = zone->colors;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) legacy_args.brightness = global_brightness;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) legacy_args.state = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) if (lighting_control_state == LEGACY_BOOTING ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) lighting_control_state == LEGACY_SUSPEND) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) guid = LEGACY_POWER_CONTROL_GUID;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) legacy_args.state = lighting_control_state;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) } else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) guid = LEGACY_CONTROL_GUID;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) method_id = zone->location + 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) input.length = (acpi_size) sizeof(legacy_args);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) input.pointer = &legacy_args;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) pr_debug("alienware-wmi: guid %s method %d\n", guid, method_id);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) status = wmi_evaluate_method(guid, 0, method_id, &input, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315) if (ACPI_FAILURE(status))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) pr_err("alienware-wmi: zone set failure: %u\n", status);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) return ACPI_FAILURE(status);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) static ssize_t zone_show(struct device *dev, struct device_attribute *attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) char *buf)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) struct platform_zone *target_zone;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) target_zone = match_zone(attr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) if (target_zone == NULL)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326) return sprintf(buf, "red: -1, green: -1, blue: -1\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) return sprintf(buf, "red: %d, green: %d, blue: %d\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) target_zone->colors.red,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) target_zone->colors.green, target_zone->colors.blue);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) static ssize_t zone_set(struct device *dev, struct device_attribute *attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334) const char *buf, size_t count)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336) struct platform_zone *target_zone;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338) target_zone = match_zone(attr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339) if (target_zone == NULL) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340) pr_err("alienware-wmi: invalid target zone\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341) return 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343) ret = parse_rgb(buf, target_zone);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346) ret = alienware_update_led(target_zone);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347) return ret ? ret : count;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351) * LED Brightness (Global)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353) static int wmax_brightness(int brightness)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355) acpi_status status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356) struct acpi_buffer input;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357) struct wmax_brightness_args args = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358) .led_mask = 0xFF,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359) .percentage = brightness,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361) input.length = (acpi_size) sizeof(args);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362) input.pointer = &args;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363) status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364) WMAX_METHOD_BRIGHTNESS, &input, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365) if (ACPI_FAILURE(status))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366) pr_err("alienware-wmi: brightness set failure: %u\n", status);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367) return ACPI_FAILURE(status);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 369)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 370) static void global_led_set(struct led_classdev *led_cdev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 371) enum led_brightness brightness)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 374) global_brightness = brightness;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 375) if (interface == WMAX)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 376) ret = wmax_brightness(brightness);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 377) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 378) ret = alienware_update_led(&zone_data[0]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 379) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 380) pr_err("LED brightness update failed\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 381) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 382)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 383) static enum led_brightness global_led_get(struct led_classdev *led_cdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 384) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 385) return global_brightness;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 386) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 387)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 388) static struct led_classdev global_led = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 389) .brightness_set = global_led_set,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 390) .brightness_get = global_led_get,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 391) .name = "alienware::global_brightness",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 392) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 393)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 394) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 395) * Lighting control state device attribute (Global)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 396) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 397) static ssize_t show_control_state(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 398) struct device_attribute *attr, char *buf)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 399) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 400) if (lighting_control_state == LEGACY_BOOTING)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 401) return scnprintf(buf, PAGE_SIZE, "[booting] running suspend\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 402) else if (lighting_control_state == LEGACY_SUSPEND)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 403) return scnprintf(buf, PAGE_SIZE, "booting running [suspend]\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 404) return scnprintf(buf, PAGE_SIZE, "booting [running] suspend\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 405) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 406)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 407) static ssize_t store_control_state(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 408) struct device_attribute *attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 409) const char *buf, size_t count)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 410) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 411) long unsigned int val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 412) if (strcmp(buf, "booting\n") == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 413) val = LEGACY_BOOTING;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 414) else if (strcmp(buf, "suspend\n") == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 415) val = LEGACY_SUSPEND;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 416) else if (interface == LEGACY)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 417) val = LEGACY_RUNNING;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 418) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 419) val = WMAX_RUNNING;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 420) lighting_control_state = val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 421) pr_debug("alienware-wmi: updated control state to %d\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 422) lighting_control_state);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 423) return count;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 424) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 425)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 426) static DEVICE_ATTR(lighting_control_state, 0644, show_control_state,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 427) store_control_state);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 428)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 429) static int alienware_zone_init(struct platform_device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 430) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 431) u8 zone;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 432) char buffer[10];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 433) char *name;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 434)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 435) if (interface == WMAX) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 436) lighting_control_state = WMAX_RUNNING;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 437) } else if (interface == LEGACY) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 438) lighting_control_state = LEGACY_RUNNING;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 439) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 440) global_led.max_brightness = 0x0F;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 441) global_brightness = global_led.max_brightness;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 442)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 443) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 444) * - zone_dev_attrs num_zones + 1 is for individual zones and then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 445) * null terminated
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 446) * - zone_attrs num_zones + 2 is for all attrs in zone_dev_attrs +
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 447) * the lighting control + null terminated
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 448) * - zone_data num_zones is for the distinct zones
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 449) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 450) zone_dev_attrs =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 451) kcalloc(quirks->num_zones + 1, sizeof(struct device_attribute),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 452) GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 453) if (!zone_dev_attrs)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 454) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 455)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 456) zone_attrs =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 457) kcalloc(quirks->num_zones + 2, sizeof(struct attribute *),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 458) GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 459) if (!zone_attrs)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 460) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 461)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 462) zone_data =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 463) kcalloc(quirks->num_zones, sizeof(struct platform_zone),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 464) GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 465) if (!zone_data)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 466) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 467)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 468) for (zone = 0; zone < quirks->num_zones; zone++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 469) sprintf(buffer, "zone%02hhX", zone);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 470) name = kstrdup(buffer, GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 471) if (name == NULL)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 472) return 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 473) sysfs_attr_init(&zone_dev_attrs[zone].attr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 474) zone_dev_attrs[zone].attr.name = name;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 475) zone_dev_attrs[zone].attr.mode = 0644;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 476) zone_dev_attrs[zone].show = zone_show;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 477) zone_dev_attrs[zone].store = zone_set;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 478) zone_data[zone].location = zone;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 479) zone_attrs[zone] = &zone_dev_attrs[zone].attr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 480) zone_data[zone].attr = &zone_dev_attrs[zone];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 481) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 482) zone_attrs[quirks->num_zones] = &dev_attr_lighting_control_state.attr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 483) zone_attribute_group.attrs = zone_attrs;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 484)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 485) led_classdev_register(&dev->dev, &global_led);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 486)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 487) return sysfs_create_group(&dev->dev.kobj, &zone_attribute_group);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 488) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 489)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 490) static void alienware_zone_exit(struct platform_device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 491) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 492) u8 zone;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 493)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 494) sysfs_remove_group(&dev->dev.kobj, &zone_attribute_group);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 495) led_classdev_unregister(&global_led);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 496) if (zone_dev_attrs) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 497) for (zone = 0; zone < quirks->num_zones; zone++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 498) kfree(zone_dev_attrs[zone].attr.name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 499) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 500) kfree(zone_dev_attrs);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 501) kfree(zone_data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 502) kfree(zone_attrs);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 503) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 504)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 505) static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 506) u32 command, int *out_data)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 507) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 508) acpi_status status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 509) union acpi_object *obj;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 510) struct acpi_buffer input;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 511) struct acpi_buffer output;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 512)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 513) input.length = (acpi_size) sizeof(*in_args);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 514) input.pointer = in_args;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 515) if (out_data) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 516) output.length = ACPI_ALLOCATE_BUFFER;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 517) output.pointer = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 518) status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 519) command, &input, &output);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 520) if (ACPI_SUCCESS(status)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 521) obj = (union acpi_object *)output.pointer;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 522) if (obj && obj->type == ACPI_TYPE_INTEGER)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 523) *out_data = (u32)obj->integer.value;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 524) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 525) kfree(output.pointer);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 526) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 527) status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 528) command, &input, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 529) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 530) return status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 531) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 532)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 533) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 534) * The HDMI mux sysfs node indicates the status of the HDMI input mux.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 535) * It can toggle between standard system GPU output and HDMI input.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 536) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 537) static ssize_t show_hdmi_cable(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 538) struct device_attribute *attr, char *buf)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 539) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 540) acpi_status status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 541) u32 out_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 542) struct wmax_basic_args in_args = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 543) .arg = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 544) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 545) status =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 546) alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 547) (u32 *) &out_data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 548) if (ACPI_SUCCESS(status)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 549) if (out_data == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 550) return scnprintf(buf, PAGE_SIZE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 551) "[unconnected] connected unknown\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 552) else if (out_data == 1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 553) return scnprintf(buf, PAGE_SIZE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 554) "unconnected [connected] unknown\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 555) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 556) pr_err("alienware-wmi: unknown HDMI cable status: %d\n", status);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 557) return scnprintf(buf, PAGE_SIZE, "unconnected connected [unknown]\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 558) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 559)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 560) static ssize_t show_hdmi_source(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 561) struct device_attribute *attr, char *buf)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 562) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 563) acpi_status status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 564) u32 out_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 565) struct wmax_basic_args in_args = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 566) .arg = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 567) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 568) status =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 569) alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 570) (u32 *) &out_data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 571)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 572) if (ACPI_SUCCESS(status)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 573) if (out_data == 1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 574) return scnprintf(buf, PAGE_SIZE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 575) "[input] gpu unknown\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 576) else if (out_data == 2)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 577) return scnprintf(buf, PAGE_SIZE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 578) "input [gpu] unknown\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 579) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 580) pr_err("alienware-wmi: unknown HDMI source status: %u\n", status);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 581) return scnprintf(buf, PAGE_SIZE, "input gpu [unknown]\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 582) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 583)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 584) static ssize_t toggle_hdmi_source(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 585) struct device_attribute *attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 586) const char *buf, size_t count)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 587) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 588) acpi_status status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 589) struct wmax_basic_args args;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 590) if (strcmp(buf, "gpu\n") == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 591) args.arg = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 592) else if (strcmp(buf, "input\n") == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 593) args.arg = 2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 594) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 595) args.arg = 3;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 596) pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 597)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 598) status = alienware_wmax_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 599)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 600) if (ACPI_FAILURE(status))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 601) pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 602) status);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 603) return count;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 604) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 605)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 606) static DEVICE_ATTR(cable, S_IRUGO, show_hdmi_cable, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 607) static DEVICE_ATTR(source, S_IRUGO | S_IWUSR, show_hdmi_source,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 608) toggle_hdmi_source);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 609)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 610) static struct attribute *hdmi_attrs[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 611) &dev_attr_cable.attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 612) &dev_attr_source.attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 613) NULL,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 614) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 615)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 616) static const struct attribute_group hdmi_attribute_group = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 617) .name = "hdmi",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 618) .attrs = hdmi_attrs,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 619) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 620)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 621) static void remove_hdmi(struct platform_device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 622) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 623) if (quirks->hdmi_mux > 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 624) sysfs_remove_group(&dev->dev.kobj, &hdmi_attribute_group);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 625) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 626)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 627) static int create_hdmi(struct platform_device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 628) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 629) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 630)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 631) ret = sysfs_create_group(&dev->dev.kobj, &hdmi_attribute_group);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 632) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 633) remove_hdmi(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 634) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 635) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 636)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 637) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 638) * Alienware GFX amplifier support
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 639) * - Currently supports reading cable status
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 640) * - Leaving expansion room to possibly support dock/undock events later
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 641) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 642) static ssize_t show_amplifier_status(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 643) struct device_attribute *attr, char *buf)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 644) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 645) acpi_status status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 646) u32 out_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 647) struct wmax_basic_args in_args = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 648) .arg = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 649) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 650) status =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 651) alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 652) (u32 *) &out_data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 653) if (ACPI_SUCCESS(status)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 654) if (out_data == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 655) return scnprintf(buf, PAGE_SIZE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 656) "[unconnected] connected unknown\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 657) else if (out_data == 1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 658) return scnprintf(buf, PAGE_SIZE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 659) "unconnected [connected] unknown\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 660) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 661) pr_err("alienware-wmi: unknown amplifier cable status: %d\n", status);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 662) return scnprintf(buf, PAGE_SIZE, "unconnected connected [unknown]\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 663) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 664)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 665) static DEVICE_ATTR(status, S_IRUGO, show_amplifier_status, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 666)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 667) static struct attribute *amplifier_attrs[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 668) &dev_attr_status.attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 669) NULL,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 670) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 671)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 672) static const struct attribute_group amplifier_attribute_group = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 673) .name = "amplifier",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 674) .attrs = amplifier_attrs,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 675) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 676)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 677) static void remove_amplifier(struct platform_device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 678) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 679) if (quirks->amplifier > 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 680) sysfs_remove_group(&dev->dev.kobj, &lifier_attribute_group);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 681) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 682)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 683) static int create_amplifier(struct platform_device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 684) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 685) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 686)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 687) ret = sysfs_create_group(&dev->dev.kobj, &lifier_attribute_group);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 688) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 689) remove_amplifier(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 690) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 691) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 692)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 693) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 694) * Deep Sleep Control support
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 695) * - Modifies BIOS setting for deep sleep control allowing extra wakeup events
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 696) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 697) static ssize_t show_deepsleep_status(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 698) struct device_attribute *attr, char *buf)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 699) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 700) acpi_status status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 701) u32 out_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 702) struct wmax_basic_args in_args = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 703) .arg = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 704) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 705) status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 706) (u32 *) &out_data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 707) if (ACPI_SUCCESS(status)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 708) if (out_data == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 709) return scnprintf(buf, PAGE_SIZE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 710) "[disabled] s5 s5_s4\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 711) else if (out_data == 1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 712) return scnprintf(buf, PAGE_SIZE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 713) "disabled [s5] s5_s4\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 714) else if (out_data == 2)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 715) return scnprintf(buf, PAGE_SIZE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 716) "disabled s5 [s5_s4]\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 717) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 718) pr_err("alienware-wmi: unknown deep sleep status: %d\n", status);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 719) return scnprintf(buf, PAGE_SIZE, "disabled s5 s5_s4 [unknown]\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 720) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 721)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 722) static ssize_t toggle_deepsleep(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 723) struct device_attribute *attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 724) const char *buf, size_t count)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 725) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 726) acpi_status status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 727) struct wmax_basic_args args;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 728)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 729) if (strcmp(buf, "disabled\n") == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 730) args.arg = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 731) else if (strcmp(buf, "s5\n") == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 732) args.arg = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 733) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 734) args.arg = 2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 735) pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 736)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 737) status = alienware_wmax_command(&args, WMAX_METHOD_DEEP_SLEEP_CONTROL,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 738) NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 739)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 740) if (ACPI_FAILURE(status))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 741) pr_err("alienware-wmi: deep sleep control failed: results: %u\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 742) status);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 743) return count;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 744) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 745)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 746) static DEVICE_ATTR(deepsleep, S_IRUGO | S_IWUSR, show_deepsleep_status, toggle_deepsleep);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 747)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 748) static struct attribute *deepsleep_attrs[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 749) &dev_attr_deepsleep.attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 750) NULL,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 751) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 752)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 753) static const struct attribute_group deepsleep_attribute_group = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 754) .name = "deepsleep",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 755) .attrs = deepsleep_attrs,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 756) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 757)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 758) static void remove_deepsleep(struct platform_device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 759) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 760) if (quirks->deepslp > 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 761) sysfs_remove_group(&dev->dev.kobj, &deepsleep_attribute_group);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 762) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 763)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 764) static int create_deepsleep(struct platform_device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 765) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 766) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 767)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 768) ret = sysfs_create_group(&dev->dev.kobj, &deepsleep_attribute_group);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 769) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 770) remove_deepsleep(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 771) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 772) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 773)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 774) static int __init alienware_wmi_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 775) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 776) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 777)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 778) if (wmi_has_guid(LEGACY_CONTROL_GUID))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 779) interface = LEGACY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 780) else if (wmi_has_guid(WMAX_CONTROL_GUID))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 781) interface = WMAX;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 782) else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 783) pr_warn("alienware-wmi: No known WMI GUID found\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 784) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 785) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 786)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 787) dmi_check_system(alienware_quirks);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 788) if (quirks == NULL)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 789) quirks = &quirk_unknown;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 790)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 791) ret = platform_driver_register(&platform_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 792) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 793) goto fail_platform_driver;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 794) platform_device = platform_device_alloc("alienware-wmi", -1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 795) if (!platform_device) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 796) ret = -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 797) goto fail_platform_device1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 798) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 799) ret = platform_device_add(platform_device);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 800) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 801) goto fail_platform_device2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 802)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 803) if (quirks->hdmi_mux > 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 804) ret = create_hdmi(platform_device);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 805) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 806) goto fail_prep_hdmi;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 807) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 808)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 809) if (quirks->amplifier > 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 810) ret = create_amplifier(platform_device);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 811) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 812) goto fail_prep_amplifier;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 813) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 814)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 815) if (quirks->deepslp > 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 816) ret = create_deepsleep(platform_device);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 817) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 818) goto fail_prep_deepsleep;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 819) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 820)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 821) ret = alienware_zone_init(platform_device);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 822) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 823) goto fail_prep_zones;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 824)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 825) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 826)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 827) fail_prep_zones:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 828) alienware_zone_exit(platform_device);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 829) fail_prep_deepsleep:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 830) fail_prep_amplifier:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 831) fail_prep_hdmi:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 832) platform_device_del(platform_device);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 833) fail_platform_device2:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 834) platform_device_put(platform_device);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 835) fail_platform_device1:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 836) platform_driver_unregister(&platform_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 837) fail_platform_driver:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 838) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 839) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 840)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 841) module_init(alienware_wmi_init);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 842)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 843) static void __exit alienware_wmi_exit(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 844) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 845) if (platform_device) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 846) alienware_zone_exit(platform_device);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 847) remove_hdmi(platform_device);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 848) platform_device_unregister(platform_device);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 849) platform_driver_unregister(&platform_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 850) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 851) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 852)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 853) module_exit(alienware_wmi_exit);