Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions drivers/platform/x86/asus-nb-wmi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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,
},
{},
};

Expand Down
120 changes: 104 additions & 16 deletions drivers/platform/x86/asus-wmi.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

#include <linux/acpi.h>
#include <linux/backlight.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;

Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Expand All @@ -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) {}
Expand Down
1 change: 1 addition & 0 deletions drivers/platform/x86/asus-wmi.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ struct quirk_entry {
*/
int no_display_toggle;
u32 xusb2pr;
bool kbd_rgb_state_available;
};

struct asus_wmi_driver {
Expand Down