Add support for the WMI methods used to turn off and adjust the brightness of the secondary "screenpad" device found on some high-end ASUS laptops like the GX650P series and others. Signed-off-by: Luke D. Jones <luke@xxxxxxxxxx> --- .../ABI/testing/sysfs-platform-asus-wmi | 20 +++ drivers/platform/x86/asus-wmi.c | 121 ++++++++++++++++++ include/linux/platform_data/x86/asus-wmi.h | 4 + 3 files changed, 145 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi b/Documentation/ABI/testing/sysfs-platform-asus-wmi index a77a004a1baa..97028c919c03 100644 --- a/Documentation/ABI/testing/sysfs-platform-asus-wmi +++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi @@ -98,3 +98,23 @@ Description: Enable an LCD response-time boost to reduce or remove ghosting: * 0 - Disable, * 1 - Enable + +What: /sys/devices/platform/<platform>/screenpad_enable +Date: May 2023 +KernelVersion: 6.4 +Contact: "Luke Jones" <luke@xxxxxxxxxx> +Description: + Enable the secondary "screenpad" screen found on some ASUS + devices which is typically a short screen mounted above the + keyboard and below the main screen: + * 0 - Disable, + * 1 - Enable + +What: /sys/devices/platform/<platform>/screenpad_brightness +Date: May 2023 +KernelVersion: 6.4 +Contact: "Luke Jones" <luke@xxxxxxxxxx> +Description: + Control the brightness of the secondary "screenpad" screen + found on some ASUS devices: + * 0-255 where 255 is maximum brightness \ No newline at end of file diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 1038dfdcdd32..bd3975be9060 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -241,6 +241,10 @@ struct asus_wmi { bool dgpu_disable_available; bool gpu_mux_mode_available; + bool screenpad_enable_available; + bool screenpad_brightness_available; + u8 screenpad_last_brightness; + bool kbd_rgb_mode_available; bool kbd_rgb_state_available; @@ -733,6 +737,113 @@ static ssize_t gpu_mux_mode_store(struct device *dev, } static DEVICE_ATTR_RW(gpu_mux_mode); +/* screenpad switch *************************************************************/ +static ssize_t screenpad_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct asus_wmi *asus = dev_get_drvdata(dev); + int result; + + result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_SCREENPAD_DISABLE); + if (result < 0) + return result; + + return sysfs_emit(buf, "%d\n", result); +} + +static ssize_t screenpad_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct asus_wmi *asus = dev_get_drvdata(dev); + int result, err; + u8 enable; + + err = kstrtou8(buf, 10, &enable); + if (err) + return err; + + if (enable > 1) + return -EINVAL; + + if (enable == 0) { + /* Get and store brightness before disabling */ + asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_SCREENPAD_LIGHT, &result); + if (result < 0) + return result; + asus->screenpad_last_brightness = result & ASUS_WMI_DSTS_BRIGHTNESS_MASK; + + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_SCREENPAD_DISABLE, enable, &result); + } else { + /* Setting the brightness is what re-enables the screenpad */ + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_SCREENPAD_LIGHT, + asus->screenpad_last_brightness, + &result); + } + if (err) { + dev_err(dev, "Failed to set screenpad_enable: %d\n", err); + return err; + } + if (result != 1) { + dev_warn(dev, "Failed to set screenpad_enable (result): 0x%x\n", result); + return -EIO; + } + + sysfs_notify(&asus->platform_device->dev.kobj, NULL, "screenpad_enable"); + + return count; +} +static DEVICE_ATTR_RW(screenpad_enable); + +/* screenpad brightness *************************************************************/ +static ssize_t screenpad_brightness_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct asus_wmi *asus = dev_get_drvdata(dev); + int result; + + /* Check if the screen is enabled, if not, return last saved brightness */ + result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_SCREENPAD_DISABLE); + if (result < 0) + return result; + if (!result) + return asus->screenpad_last_brightness; + + asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_SCREENPAD_LIGHT, &result); + if (result < 0) + return result; + + return sysfs_emit(buf, "%d\n", result & ASUS_WMI_DSTS_BRIGHTNESS_MASK); +} + +static ssize_t screenpad_brightness_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct asus_wmi *asus = dev_get_drvdata(dev); + int result, err; + u8 brightness; + + err = kstrtou8(buf, 10, &brightness); + if (err) + return err; + + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_SCREENPAD_LIGHT, brightness, &result); + if (err) { + dev_err(dev, "Failed to set screenpad_brightness: %d\n", err); + return err; + } + if (result != 1) { + dev_warn(dev, "Failed to set screenpad_brightness (result): 0x%x\n", result); + return -EIO; + } + + sysfs_notify(&asus->platform_device->dev.kobj, NULL, "screenpad_brightness"); + + return count; +} +static DEVICE_ATTR_RW(screenpad_brightness); + /* TUF Laptop Keyboard RGB Modes **********************************************/ static ssize_t kbd_rgb_mode_store(struct device *dev, struct device_attribute *attr, @@ -3465,6 +3576,8 @@ static struct attribute *platform_attributes[] = { &dev_attr_egpu_enable.attr, &dev_attr_dgpu_disable.attr, &dev_attr_gpu_mux_mode.attr, + &dev_attr_screenpad_enable.attr, + &dev_attr_screenpad_brightness.attr, &dev_attr_lid_resume.attr, &dev_attr_als_enable.attr, &dev_attr_fan_boost_mode.attr, @@ -3497,6 +3610,10 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj, ok = asus->dgpu_disable_available; else if (attr == &dev_attr_gpu_mux_mode.attr) ok = asus->gpu_mux_mode_available; + else if (attr == &dev_attr_screenpad_enable.attr) + ok = asus->screenpad_enable_available; + else if (attr == &dev_attr_screenpad_brightness.attr) + ok = asus->screenpad_brightness_available; else if (attr == &dev_attr_fan_boost_mode.attr) ok = asus->fan_boost_mode_available; else if (attr == &dev_attr_throttle_thermal_policy.attr) @@ -3760,6 +3877,10 @@ 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->gpu_mux_mode_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_GPU_MUX); + asus->screenpad_enable_available = asus_wmi_dev_is_present(asus, + ASUS_WMI_DEVID_SCREENPAD_DISABLE); + asus->screenpad_brightness_available = asus_wmi_dev_is_present(asus, + ASUS_WMI_DEVID_SCREENPAD_LIGHT); asus->kbd_rgb_mode_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_MODE); asus->kbd_rgb_state_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_STATE); asus->panel_overdrive_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_PANEL_OD); diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index 28234dc9fa6a..978627aac87c 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -58,6 +58,10 @@ #define ASUS_WMI_DEVID_KBD_BACKLIGHT 0x00050021 #define ASUS_WMI_DEVID_LIGHT_SENSOR 0x00050022 /* ?? */ #define ASUS_WMI_DEVID_LIGHTBAR 0x00050025 +/* This can only be used to disable the screen, not re-enable */ +#define ASUS_WMI_DEVID_SCREENPAD_DISABLE 0x00050031 +/* Writing a brightness re-enables the screen if disabled */ +#define ASUS_WMI_DEVID_SCREENPAD_LIGHT 0x00050032 #define ASUS_WMI_DEVID_FAN_BOOST_MODE 0x00110018 #define ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY 0x00120075 -- 2.40.0