diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index 8005c088e9eee..c89f492f889a1 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -155,6 +155,10 @@ static struct quirk_entry quirk_asus_z13 = { .tablet_switch_mode = asus_wmi_kbd_dock_devid, }; +static struct quirk_entry quirk_asus_fa401 = { + .kbd_rgb_state_available = true, +}; + static int dmi_matched(const struct dmi_system_id *dmi) { pr_info("Identified laptop model '%s'\n", dmi->ident); @@ -562,6 +566,15 @@ static const struct dmi_system_id asus_quirks[] = { }, .driver_data = &quirk_asus_z13, }, + { + .callback = dmi_matched, + .ident = "ASUSTeK COMPUTER INC. FA401", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_BOARD_NAME, "FA401"), + }, + .driver_data = &quirk_asus_fa401, + }, {}, }; diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index fd5fbdb0cd8b8..2fc208e91ee5e 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -256,6 +257,7 @@ struct asus_wmi { int tpd_led_wk; struct led_classdev kbd_led; int kbd_led_wk; + int kbd_led_last_level; bool kbd_led_notify; bool kbd_led_avail; bool kbd_led_registered; @@ -309,6 +311,7 @@ struct asus_wmi { u32 kbd_rgb_dev; bool kbd_rgb_state_available; + u32 kbd_rgb_state_flags; bool oobe_state_available; u8 throttle_thermal_policy_mode; @@ -343,6 +346,7 @@ struct asus_wmi { /* Global to allow setting externally without requiring driver data */ static enum asus_ally_mcu_hack use_ally_mcu_hack = ASUS_WMI_ALLY_MCU_HACK_INIT; +static struct asus_wmi *asus_hotk; #if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) static void asus_wmi_show_deprecated(void) @@ -1047,6 +1051,27 @@ static DEVICE_ATTR_RW(gpu_mux_mode); #endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */ /* TUF Laptop Keyboard RGB Modes **********************************************/ + +/* Command IDs passed in arg0 byte 0 for TUF RGB WMI methods */ +#define ASUS_WMI_TUF_RGB_STATE_CMD_ID 0xbd + +/* Bit mask for the save-to-BIOS command flag in kbd_rgb_state_store (arg0 bit 10) */ +#define TUF_RGB_STATE_SAVE GENMASK(10, 10) + +/* Bit masks for kbd_rgb_state_store flags field (arg0 bits [23:16]) */ +#define TUF_RGB_STATE_BOOT GENMASK(17, 17) +#define TUF_RGB_STATE_AWAKE GENMASK(19, 19) +#define TUF_RGB_STATE_SLEEP GENMASK(21, 21) +#define TUF_RGB_STATE_KEYBOARD GENMASK(23, 23) + +/* Bit masks for kbd_rgb_mode_store fields */ +#define TUF_RGB_MODE_CMD GENMASK(7, 0) +#define TUF_RGB_MODE_MODE GENMASK(15, 8) +#define TUF_RGB_MODE_RED GENMASK(23, 16) +#define TUF_RGB_MODE_GREEN GENMASK(31, 24) +#define TUF_RGB_MODE_BLUE GENMASK(7, 0) +#define TUF_RGB_MODE_SPEED GENMASK(15, 8) + static ssize_t kbd_rgb_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -1093,7 +1118,12 @@ static ssize_t kbd_rgb_mode_store(struct device *dev, } err = asus_wmi_evaluate_method3(ASUS_WMI_METHODID_DEVS, asus->kbd_rgb_dev, - cmd | (mode << 8) | (r << 16) | (g << 24), b | (speed << 8), NULL); + FIELD_PREP(TUF_RGB_MODE_CMD, cmd) | + FIELD_PREP(TUF_RGB_MODE_MODE, mode) | + FIELD_PREP(TUF_RGB_MODE_RED, r) | + FIELD_PREP(TUF_RGB_MODE_GREEN, g), + FIELD_PREP(TUF_RGB_MODE_BLUE, b) | + FIELD_PREP(TUF_RGB_MODE_SPEED, speed), NULL); if (err) return err; @@ -1119,31 +1149,36 @@ static ssize_t kbd_rgb_state_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - u32 flags, cmd, boot, awake, sleep, keyboard; + struct led_classdev *led = dev_get_drvdata(dev); + struct asus_wmi *asus = container_of(led, struct asus_wmi, kbd_led); + u32 cmd, boot, awake, sleep, keyboard; + u32 arg0; int err; if (sscanf(buf, "%d %d %d %d %d", &cmd, &boot, &awake, &sleep, &keyboard) != 5) return -EINVAL; + arg0 = ASUS_WMI_TUF_RGB_STATE_CMD_ID; + if (cmd) - cmd = BIT(2); + arg0 |= FIELD_PREP(TUF_RGB_STATE_SAVE, 1); - flags = 0; if (boot) - flags |= BIT(1); + arg0 |= FIELD_PREP(TUF_RGB_STATE_BOOT, 1); if (awake) - flags |= BIT(3); + arg0 |= FIELD_PREP(TUF_RGB_STATE_AWAKE, 1); if (sleep) - flags |= BIT(5); + arg0 |= FIELD_PREP(TUF_RGB_STATE_SLEEP, 1); if (keyboard) - flags |= BIT(7); + arg0 |= FIELD_PREP(TUF_RGB_STATE_KEYBOARD, 1); - /* 0xbd is the required default arg0 for the method. Nothing happens otherwise */ err = asus_wmi_evaluate_method3(ASUS_WMI_METHODID_DEVS, - ASUS_WMI_DEVID_TUF_RGB_STATE, 0xbd | cmd << 8 | (flags << 16), 0, NULL); + ASUS_WMI_DEVID_TUF_RGB_STATE, arg0, 0, NULL); if (err) return err; + asus->kbd_rgb_state_flags = arg0; + return count; } static DEVICE_ATTR_WO(kbd_rgb_state); @@ -1916,8 +1951,11 @@ static void do_kbd_led_set(struct led_classdev *led_cdev, int value) asus = container_of(led_cdev, struct asus_wmi, kbd_led); - scoped_guard(spinlock_irqsave, &asus_ref.lock) + scoped_guard(spinlock_irqsave, &asus_ref.lock) { asus->kbd_led_wk = clamp_val(value, 0, ASUS_EV_MAX_BRIGHTNESS); + if (asus->kbd_led_wk > 0) + asus->kbd_led_last_level = asus->kbd_led_wk; + } if (asus->kbd_led_avail) kbd_led_update(asus); @@ -2127,6 +2165,7 @@ static int asus_wmi_led_init(struct asus_wmi *asus) if (asus->kbd_led_avail) { asus->kbd_led_wk = led_val; + asus->kbd_led_last_level = led_val > 0 ? led_val : ASUS_EV_MAX_BRIGHTNESS; if (num_rgb_groups != 0) asus->kbd_led.groups = kbd_rgb_mode_groups; } else { @@ -5150,6 +5189,7 @@ static int asus_wmi_add(struct platform_device *pdev) asus->platform_device = pdev; wdrv->platform_device = pdev; platform_set_drvdata(asus->platform_device, asus); + asus_hotk = asus; if (wdrv->detect_quirks) wdrv->detect_quirks(asus->driver); @@ -5185,7 +5225,17 @@ static int asus_wmi_add(struct platform_device *pdev) asus->egpu_enable_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_EGPU); asus->dgpu_disable_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_DGPU); - asus->kbd_rgb_state_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_STATE); + asus->kbd_rgb_state_available = + asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_STATE) || + asus->driver->quirks->kbd_rgb_state_available; + if (asus->kbd_rgb_state_available) { + asus->kbd_rgb_state_flags = + ASUS_WMI_TUF_RGB_STATE_CMD_ID | + FIELD_PREP(TUF_RGB_STATE_BOOT, 1) | + FIELD_PREP(TUF_RGB_STATE_AWAKE, 1) | + FIELD_PREP(TUF_RGB_STATE_SLEEP, 1) | + FIELD_PREP(TUF_RGB_STATE_KEYBOARD, 1); + } if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_MINI_LED_MODE)) asus->mini_led_dev_id = ASUS_WMI_DEVID_MINI_LED_MODE; @@ -5326,6 +5376,7 @@ static void asus_wmi_remove(struct platform_device *device) struct asus_wmi *asus; asus = platform_get_drvdata(device); + asus_hotk = NULL; if (asus->driver->i8042_filter) i8042_remove_filter(asus->driver->i8042_filter); wmi_remove_notify_handler(asus->driver->event_guid); @@ -5426,17 +5477,54 @@ static int asus_hotk_restore(struct device *device) return 0; } +static void asus_tuf_reassert_sleep_rgb_state(struct asus_wmi *asus) +{ + if (asus && asus->driver->quirks->kbd_rgb_state_available && + asus->kbd_rgb_state_available && + (asus->kbd_rgb_state_flags & TUF_RGB_STATE_SLEEP)) { + int level; + u32 arg0; + + /* + * Re-assert keyboard backlight using the last user-configured + * brightness level (falling back to max brightness) with the + * light-on bit (0x80) set. + */ + level = asus->kbd_led_last_level ? + asus->kbd_led_last_level : ASUS_EV_MAX_BRIGHTNESS; + asus_wmi_set_devstate(ASUS_WMI_DEVID_KBD_BACKLIGHT, + 0x80 | (level & 0x7f), NULL); + + /* Re-assert the last user-configured TUF RGB power state */ + arg0 = asus->kbd_rgb_state_flags | + FIELD_PREP(TUF_RGB_STATE_SAVE, 1); + asus_wmi_evaluate_method3(ASUS_WMI_METHODID_DEVS, + ASUS_WMI_DEVID_TUF_RGB_STATE, + arg0, 0, NULL); + } +} + static int asus_hotk_prepare(struct device *device) { + struct asus_wmi *asus = dev_get_drvdata(device); + if (use_ally_mcu_hack == ASUS_WMI_ALLY_MCU_HACK_ENABLED) { acpi_execute_simple_method(NULL, ASUS_USB0_PWR_EC0_CSEE, ASUS_USB0_PWR_EC0_CSEE_OFF); msleep(ASUS_USB0_PWR_EC0_CSEE_WAIT); } + + asus_tuf_reassert_sleep_rgb_state(asus); + return 0; } #if defined(CONFIG_SUSPEND) +static void asus_s2idle_prepare(void) +{ + asus_tuf_reassert_sleep_rgb_state(asus_hotk); +} + static void asus_ally_s2idle_restore(void) { if (use_ally_mcu_hack == ASUS_WMI_ALLY_MCU_HACK_ENABLED) { @@ -5446,20 +5534,20 @@ static void asus_ally_s2idle_restore(void) } } -/* Use only for Ally devices due to the wake_on_ac */ -static struct acpi_s2idle_dev_ops asus_ally_s2idle_dev_ops = { +static struct acpi_s2idle_dev_ops asus_s2idle_dev_ops = { + .prepare = asus_s2idle_prepare, .restore = asus_ally_s2idle_restore, }; static void asus_s2idle_check_register(void) { - if (acpi_register_lps0_dev(&asus_ally_s2idle_dev_ops)) + if (acpi_register_lps0_dev(&asus_s2idle_dev_ops)) pr_warn("failed to register LPS0 sleep handler in asus-wmi\n"); } static void asus_s2idle_check_unregister(void) { - acpi_unregister_lps0_dev(&asus_ally_s2idle_dev_ops); + acpi_unregister_lps0_dev(&asus_s2idle_dev_ops); } #else static void asus_s2idle_check_register(void) {} diff --git a/drivers/platform/x86/asus-wmi.h b/drivers/platform/x86/asus-wmi.h index 5cd4392b964eb..9bc0b6145fed6 100644 --- a/drivers/platform/x86/asus-wmi.h +++ b/drivers/platform/x86/asus-wmi.h @@ -52,6 +52,7 @@ struct quirk_entry { */ int no_display_toggle; u32 xusb2pr; + bool kbd_rgb_state_available; }; struct asus_wmi_driver {