* [PATCH 09/11] platform/x86: asus-wmi: Control RGB keyboard backlight
[not found] <a8dd77f3-1e28-6416-5f04-aff44d5bb6d6@gmail.com>
@ 2019-04-10 20:33 ` Yurii Pavlovskyi
2019-04-11 5:46 ` [PATCH v2 " Yurii Pavlovskyi
2019-04-10 20:34 ` [PATCH 10/11] platform/x86: asus-wmi: Switch fan boost mode Yurii Pavlovskyi
1 sibling, 1 reply; 6+ messages in thread
From: Yurii Pavlovskyi @ 2019-04-10 20:33 UTC (permalink / raw)
Cc: Corentin Chary, Darren Hart, Andy Shevchenko, Daniel Drake,
acpi4asus-user, platform-driver-x86, linux-kernel, linux-api
The WMI exposes two methods for controlling RGB keyboard backlight which
allow to control:
* RGB components in range 00 - ff,
* Switch between 4 effects,
* Switch between 3 effect speed modes,
* Separately enable the backlight on boot, in awake state (after driver
load), in sleep mode, and probably in something called shutdown mode
(no observable effects of enabling it are known so far).
The configuration should be written to several sysfs parameter buffers
which are then written via WMI by writing either 1 or 2 to the "kbbl_set"
parameter. When reading the buffers the last written value is returned.
If the 2 is written to "kbbl_set", the parameters will be reset on reboot
(temporary mode), 1 is permanent mode, parameters are retained.
The calls use new 3-dword input buffer method call.
The functionality is only enabled if corresponding DSTS methods return
exact valid values.
The following script demonstrates usage:
echo Red [00 - ff]
echo 33 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_red
echo Green [00 - ff]
echo ff > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_green
echo Blue [00 - ff]
echo 0 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_blue
echo Mode: 0 - static color, 1 - blink, 2 - rainbow, 3 - strobe
echo 0 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_mode
echo Speed for modes 1 and 2: 0 - slow, 1 - medium, 2 - fast
echo 0 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_speed
echo Enable: 02 - on boot, before module load, 08 - awake, 20 - sleep,
echo 2a or ff to set all
echo 2a > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_flags
echo Save: 1 - permanently, 2 - temporarily, reset after reboot
echo 1 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_set
Signed-off-by: Yurii Pavlovskyi <yurii.pavlovskyi@gmail.com>
---
.../ABI/testing/sysfs-platform-asus-wmi | 61 ++++
drivers/platform/x86/asus-wmi.c | 329 ++++++++++++++++++
include/linux/platform_data/x86/asus-wmi.h | 2 +
3 files changed, 392 insertions(+)
diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi b/Documentation/ABI/testing/sysfs-platform-asus-wmi
index 019e1e29370e..300a40519695 100644
--- a/Documentation/ABI/testing/sysfs-platform-asus-wmi
+++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi
@@ -36,3 +36,64 @@ KernelVersion: 3.5
Contact: "AceLan Kao" <acelan.kao@canonical.com>
Description:
Resume on lid open. 1 means on, 0 means off.
+
+What: /sys/devices/platform/<platform>/kbbl/kbbl_red
+Date: Apr 2019
+KernelVersion: 5.1
+Contact: "Yurii Pavlovskyi" <yurii.pavlovskyi@gmail.com>
+Description:
+ RGB keyboard backlight red component: 00 .. ff.
+
+What: /sys/devices/platform/<platform>/kbbl/kbbl_green
+Date: Apr 2019
+KernelVersion: 5.1
+Contact: "Yurii Pavlovskyi" <yurii.pavlovskyi@gmail.com>
+Description:
+ RGB keyboard backlight green component: 00 .. ff.
+
+What: /sys/devices/platform/<platform>/kbbl/kbbl_blue
+Date: Apr 2019
+KernelVersion: 5.1
+Contact: "Yurii Pavlovskyi" <yurii.pavlovskyi@gmail.com>
+Description:
+ RGB keyboard backlight blue component: 00 .. ff.
+
+What: /sys/devices/platform/<platform>/kbbl/kbbl_mode
+Date: Apr 2019
+KernelVersion: 5.1
+Contact: "Yurii Pavlovskyi" <yurii.pavlovskyi@gmail.com>
+Description:
+ RGB keyboard backlight mode:
+ * 0 - static color,
+ * 1 - blink,
+ * 2 - rainbow,
+ * 3 - strobe.
+
+What: /sys/devices/platform/<platform>/kbbl/kbbl_speed
+Date: Apr 2019
+KernelVersion: 5.1
+Contact: "Yurii Pavlovskyi" <yurii.pavlovskyi@gmail.com>
+Description:
+ RGB keyboard backlight speed for modes 1 and 2:
+ * 0 - slow,
+ * 1 - medium,
+ * 2 - fast.
+
+What: /sys/devices/platform/<platform>/kbbl/kbbl_flags
+Date: Apr 2019
+KernelVersion: 5.1
+Contact: "Yurii Pavlovskyi" <yurii.pavlovskyi@gmail.com>
+Description:
+ RGB keyboard backlight enable flags (2a to enable everything), OR of:
+ * 02 - on boot (until module load),
+ * 08 - awake,
+ * 20 - sleep.
+
+What: /sys/devices/platform/<platform>/kbbl/kbbl_set
+Date: Apr 2019
+KernelVersion: 5.1
+Contact: "Yurii Pavlovskyi" <yurii.pavlovskyi@gmail.com>
+Description:
+ Write changed RGB keyboard backlight parameters:
+ * 1 - permanently,
+ * 2 - temporarily.
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 175ecd5b7c51..f4323a57f22f 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -145,6 +145,21 @@ struct asus_rfkill {
u32 dev_id;
};
+struct asus_kbbl_rgb {
+ u8 kbbl_red;
+ u8 kbbl_green;
+ u8 kbbl_blue;
+ u8 kbbl_mode;
+ u8 kbbl_speed;
+
+ u8 kbbl_set_red;
+ u8 kbbl_set_green;
+ u8 kbbl_set_blue;
+ u8 kbbl_set_mode;
+ u8 kbbl_set_speed;
+ u8 kbbl_set_flags;
+};
+
struct asus_wmi {
int dsts_id;
int spec;
@@ -181,6 +196,9 @@ struct asus_wmi {
int asus_hwmon_num_fans;
int asus_hwmon_pwm;
+ bool kbbl_rgb_available;
+ struct asus_kbbl_rgb kbbl_rgb;
+
struct hotplug_slot hotplug_slot;
struct mutex hotplug_lock;
struct mutex wmi_lock;
@@ -656,6 +674,310 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
return rv;
}
+/* RGB keyboard backlight *****************************************************/
+
+static ssize_t show_u8(u8 value, char *buf)
+{
+ return scnprintf(buf, PAGE_SIZE, "%02x\n", value);
+}
+
+static ssize_t store_u8(u8 *value, const char *buf, int count)
+{
+ int err;
+ u8 result;
+
+ err = kstrtou8(buf, 16, &result);
+ if (err < 0) {
+ pr_warn(PR "Trying to store invalid value\n");
+ return err;
+ }
+
+ *value = result;
+
+ return count;
+}
+
+static ssize_t kbbl_red_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return show_u8(asus->kbbl_rgb.kbbl_red, buf);
+}
+
+static ssize_t kbbl_red_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return store_u8(&asus->kbbl_rgb.kbbl_set_red, buf, count);
+}
+
+static ssize_t kbbl_green_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return show_u8(asus->kbbl_rgb.kbbl_green, buf);
+}
+
+static ssize_t kbbl_green_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return store_u8(&asus->kbbl_rgb.kbbl_set_green, buf, count);
+}
+
+static ssize_t kbbl_blue_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return show_u8(asus->kbbl_rgb.kbbl_blue, buf);
+}
+
+static ssize_t kbbl_blue_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return store_u8(&asus->kbbl_rgb.kbbl_set_blue, buf, count);
+}
+
+static ssize_t kbbl_mode_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return show_u8(asus->kbbl_rgb.kbbl_mode, buf);
+}
+
+static ssize_t kbbl_mode_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return store_u8(&asus->kbbl_rgb.kbbl_set_mode, buf, count);
+}
+
+static ssize_t kbbl_speed_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return show_u8(asus->kbbl_rgb.kbbl_speed, buf);
+}
+
+static ssize_t kbbl_speed_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return store_u8(&asus->kbbl_rgb.kbbl_set_speed, buf, count);
+}
+
+static ssize_t kbbl_flags_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return show_u8(asus->kbbl_rgb.kbbl_set_flags, buf);
+}
+
+static ssize_t kbbl_flags_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return store_u8(&asus->kbbl_rgb.kbbl_set_flags, buf, count);
+}
+
+static ssize_t kbbl_set_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return scnprintf(buf, PAGE_SIZE,
+ "Write to configure RGB keyboard backlight\n");
+}
+
+static int kbbl_rgb_write(struct asus_wmi *asus, int persistent)
+{
+ int err;
+ u32 retval;
+ u8 speed_byte;
+ u8 mode_byte;
+ u8 speed;
+ u8 mode;
+
+ speed = asus->kbbl_rgb.kbbl_set_speed;
+ switch (speed) {
+ case 0:
+ default:
+ speed_byte = 0xe1; // slow
+ speed = 0;
+ break;
+ case 1:
+ speed_byte = 0xeb; // medium
+ break;
+ case 2:
+ speed_byte = 0xf5; // fast
+ break;
+ }
+
+ mode = asus->kbbl_rgb.kbbl_set_mode;
+ switch (mode) {
+ case 0:
+ default:
+ mode_byte = 0x00; // static color
+ mode = 0;
+ break;
+ case 1:
+ mode_byte = 0x01; // blink
+ break;
+ case 2:
+ mode_byte = 0x02; // rainbow
+ break;
+ case 3:
+ mode_byte = 0x0a; // strobe
+ break;
+ }
+
+ err = asus_wmi_evaluate_method_3dw(ASUS_WMI_METHODID_DEVS,
+ ASUS_WMI_DEVID_KBD_RGB,
+ (persistent ? 0xb4 : 0xb3) |
+ (mode_byte << 8) |
+ (asus->kbbl_rgb.kbbl_set_red << 16) |
+ (asus->kbbl_rgb.kbbl_set_green << 24),
+ (asus->kbbl_rgb.kbbl_set_blue) |
+ (speed_byte << 8), &retval);
+ if (err) {
+ pr_warn(PR "RGB keyboard device 1, write error: %d\n", err);
+ return err;
+ }
+
+ if (retval != 1) {
+ pr_warn(PR "RGB keyboard device 1, write error (retval): %x\n",
+ retval);
+ return -EIO;
+ }
+
+ err = asus_wmi_evaluate_method_3dw(ASUS_WMI_METHODID_DEVS,
+ ASUS_WMI_DEVID_KBD_RGB2,
+ (0xbd) |
+ (asus->kbbl_rgb.kbbl_set_flags << 16) |
+ (persistent ? 0x0100 : 0x0000), 0, &retval);
+ if (err) {
+ pr_warn(PR "RGB keyboard device 2, write error: %d\n", err);
+ return err;
+ }
+
+ if (retval != 1) {
+ pr_warn(PR "RGB keyboard device 2, write error (retval): %x\n",
+ retval);
+ return -EIO;
+ }
+
+ asus->kbbl_rgb.kbbl_red = asus->kbbl_rgb.kbbl_set_red;
+ asus->kbbl_rgb.kbbl_green = asus->kbbl_rgb.kbbl_set_green;
+ asus->kbbl_rgb.kbbl_blue = asus->kbbl_rgb.kbbl_set_blue;
+ asus->kbbl_rgb.kbbl_mode = mode;
+ asus->kbbl_rgb.kbbl_speed = speed;
+
+ return 0;
+}
+
+static ssize_t kbbl_set_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ u8 value;
+ struct asus_wmi *asus;
+ int result;
+
+ asus = dev_get_drvdata(dev);
+ result = store_u8(&value, buf, count);
+ if (result < 0)
+ return result;
+
+ if (value == 1)
+ kbbl_rgb_write(asus, 1);
+ else if (value == 2)
+ kbbl_rgb_write(asus, 0);
+
+ return count;
+}
+
+/* RGB values: 00 .. ff */
+static DEVICE_ATTR_RW(kbbl_red);
+static DEVICE_ATTR_RW(kbbl_green);
+static DEVICE_ATTR_RW(kbbl_blue);
+
+/* Color modes: 0 - static color, 1 - blink, 2 - rainbow, 3 - strobe */
+static DEVICE_ATTR_RW(kbbl_mode);
+
+/* Speed for modes 1 and 2: 0 - slow, 1 - medium, 2 - fast */
+static DEVICE_ATTR_RW(kbbl_speed);
+
+/*
+ * Enable: 02 - on boot (until module load) | 08 - awake | 20 - sleep
+ * (2a or ff to enable everything)
+ *
+ * Logically 80 would be shutdown, but no visible effects of this option
+ * were observed so far
+ */
+static DEVICE_ATTR_RW(kbbl_flags);
+
+/* Write data: 1 - permanently, 2 - temporarily (reset after reboot) */
+static DEVICE_ATTR_RW(kbbl_set);
+
+static struct attribute *rgbkb_sysfs_attributes[] = {
+ &dev_attr_kbbl_red.attr,
+ &dev_attr_kbbl_green.attr,
+ &dev_attr_kbbl_blue.attr,
+ &dev_attr_kbbl_mode.attr,
+ &dev_attr_kbbl_speed.attr,
+ &dev_attr_kbbl_flags.attr,
+ &dev_attr_kbbl_set.attr,
+ NULL,
+};
+
+static const struct attribute_group kbbl_attribute_group = {
+ .name = "kbbl",
+ .attrs = rgbkb_sysfs_attributes
+};
+
+static int kbbl_rgb_init(struct asus_wmi *asus)
+{
+ int err;
+
+ err = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_KBD_RGB);
+ if (err) {
+ if (err == -ENODEV)
+ return 0;
+ else
+ return err;
+ }
+
+ err = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_KBD_RGB2);
+ if (err) {
+ if (err == -ENODEV)
+ return 0;
+ else
+ return err;
+ }
+
+ asus->kbbl_rgb_available = true;
+ return sysfs_create_group(&asus->platform_device->dev.kobj,
+ &kbbl_attribute_group);
+}
+
+static void kbbl_rgb_exit(struct asus_wmi *asus)
+{
+ if (asus->kbbl_rgb_available) {
+ sysfs_remove_group(&asus->platform_device->dev.kobj,
+ &kbbl_attribute_group);
+ }
+}
+
/* RF *************************************************************************/
/*
@@ -2212,6 +2534,10 @@ static int asus_wmi_add(struct platform_device *pdev)
if (err)
goto fail_leds;
+ err = kbbl_rgb_init(asus);
+ if (err)
+ goto fail_rgbkb;
+
asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WLAN, &result);
if (result & (ASUS_WMI_DSTS_PRESENCE_BIT | ASUS_WMI_DSTS_USER_BIT))
asus->driver->wlan_ctrl_by_user = 1;
@@ -2278,6 +2604,8 @@ static int asus_wmi_add(struct platform_device *pdev)
fail_backlight:
asus_wmi_rfkill_exit(asus);
fail_rfkill:
+ kbbl_rgb_exit(asus);
+fail_rgbkb:
asus_wmi_led_exit(asus);
fail_leds:
asus_wmi_hwmon_exit(asus);
@@ -2299,6 +2627,7 @@ static int asus_wmi_remove(struct platform_device *device)
asus_wmi_backlight_exit(asus);
asus_wmi_input_exit(asus);
asus_wmi_led_exit(asus);
+ kbbl_rgb_exit(asus);
asus_wmi_rfkill_exit(asus);
asus_wmi_debugfs_exit(asus);
asus_wmi_platform_exit(asus);
diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
index 53dfc2541960..25b7b653e6d2 100644
--- a/include/linux/platform_data/x86/asus-wmi.h
+++ b/include/linux/platform_data/x86/asus-wmi.h
@@ -57,6 +57,8 @@
#define ASUS_WMI_DEVID_KBD_BACKLIGHT 0x00050021
#define ASUS_WMI_DEVID_LIGHT_SENSOR 0x00050022 /* ?? */
#define ASUS_WMI_DEVID_LIGHTBAR 0x00050025
+#define ASUS_WMI_DEVID_KBD_RGB 0x00100056
+#define ASUS_WMI_DEVID_KBD_RGB2 0x00100057
/* Misc */
#define ASUS_WMI_DEVID_CAMERA 0x00060013
--
2.17.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 10/11] platform/x86: asus-wmi: Switch fan boost mode
[not found] <a8dd77f3-1e28-6416-5f04-aff44d5bb6d6@gmail.com>
2019-04-10 20:33 ` [PATCH 09/11] platform/x86: asus-wmi: Control RGB keyboard backlight Yurii Pavlovskyi
@ 2019-04-10 20:34 ` Yurii Pavlovskyi
2019-04-11 5:47 ` [PATCH v2 " Yurii Pavlovskyi
1 sibling, 1 reply; 6+ messages in thread
From: Yurii Pavlovskyi @ 2019-04-10 20:34 UTC (permalink / raw)
Cc: Corentin Chary, Darren Hart, Andy Shevchenko, Daniel Drake,
acpi4asus-user, platform-driver-x86, linux-kernel, linux-api
The WMI exposes a write-only device ID where three modes can be switched
on some laptops (TUF Gaming FX505GM). There is a hotkey combination Fn-F5
that does have a fan icon which is designed to toggle between these 3
modes.
Add a SysFS entry that reads the last written value and updates value in
WMI on write and a hotkey handler that toggles the modes. The
corresponding DEVS device handler does obviously take 3 possible
argument values.
Method (SFBM, 1, NotSerialized)
{
If ((Arg0 == Zero) { .. }
If ((Arg0 == One)) { .. }
If ((Arg0 == 0x02)) { .. }
}
... // DEVS
If ((IIA0 == 0x00110018))
{
SFBM (IIA1)
Return (One)
}
* 0x00 - is normal,
* 0x01 - is obviously turbo by the amount of noise, might be useful to
avoid CPU frequency throttling on high load,
* 0x02 - the meaning is unknown at the time as modes are not named
in the vendor documentation, but it does look like a quiet mode as CPU
temperature does increase about 10 degrees on maximum load.
Signed-off-by: Yurii Pavlovskyi <yurii.pavlovskyi@gmail.com>
---
.../ABI/testing/sysfs-platform-asus-wmi | 10 ++
drivers/platform/x86/asus-wmi.c | 119 ++++++++++++++++--
include/linux/platform_data/x86/asus-wmi.h | 1 +
3 files changed, 117 insertions(+), 13 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi b/Documentation/ABI/testing/sysfs-platform-asus-wmi
index 300a40519695..2b3184e297a7 100644
--- a/Documentation/ABI/testing/sysfs-platform-asus-wmi
+++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi
@@ -97,3 +97,13 @@ Description:
Write changed RGB keyboard backlight parameters:
* 1 - permanently,
* 2 - temporarily.
+
+What: /sys/devices/platform/<platform>/fan_mode
+Date: Apr 2019
+KernelVersion: 5.1
+Contact: "Yurii Pavlovskyi" <yurii.pavlovskyi@gmail.com>
+Description:
+ Fan boost mode:
+ * 0 - normal,
+ * 1 - turbo,
+ * 2 - quiet?
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index f4323a57f22f..941c628945ac 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -69,6 +69,7 @@ MODULE_LICENSE("GPL");
#define NOTIFY_KBD_BRTUP 0xc4
#define NOTIFY_KBD_BRTDWN 0xc5
#define NOTIFY_KBD_BRTTOGGLE 0xc7
+#define NOTIFY_KBD_FBM 0x99
#define ASUS_FAN_DESC "cpu_fan"
#define ASUS_FAN_MFUN 0x13
@@ -77,6 +78,8 @@ MODULE_LICENSE("GPL");
#define ASUS_FAN_CTRL_MANUAL 1
#define ASUS_FAN_CTRL_AUTO 2
+#define ASUS_FAN_MODE_COUNT 3
+
#define USB_INTEL_XUSB2PR 0xD0
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31
@@ -196,6 +199,9 @@ struct asus_wmi {
int asus_hwmon_num_fans;
int asus_hwmon_pwm;
+ bool fan_mode_available;
+ u8 fan_mode;
+
bool kbbl_rgb_available;
struct asus_kbbl_rgb kbbl_rgb;
@@ -1833,6 +1839,87 @@ static int asus_wmi_fan_init(struct asus_wmi *asus)
return 0;
}
+/* Fan mode *******************************************************************/
+
+static int fan_mode_check_present(struct asus_wmi *asus)
+{
+ u32 result;
+ int err;
+
+ asus->fan_mode_available = false;
+
+ err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_MODE, &result);
+ if (err) {
+ if (err == -ENODEV)
+ return 0;
+ else
+ return err;
+ }
+
+ if (result & ASUS_WMI_DSTS_PRESENCE_BIT)
+ asus->fan_mode_available = true;
+
+ return 0;
+}
+
+static int fan_mode_write(struct asus_wmi *asus)
+{
+ int err;
+ u8 value;
+ u32 retval;
+
+ value = asus->fan_mode % ASUS_FAN_MODE_COUNT;
+ pr_info(PR "Set fan mode: %u\n", value);
+ err = asus_wmi_set_devstate(ASUS_WMI_DEVID_FAN_MODE, value, &retval);
+
+ if (err) {
+ pr_warn(PR "Failed to set fan mode: %d\n", err);
+ return err;
+ }
+
+ if (retval != 1) {
+ pr_warn(PR "Failed to set fan mode (retval): 0x%x\n", retval);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int fan_mode_switch_next(struct asus_wmi *asus)
+{
+ asus->fan_mode = (asus->fan_mode + 1) % ASUS_FAN_MODE_COUNT;
+ return fan_mode_write(asus);
+}
+
+static ssize_t fan_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return show_u8(asus->fan_mode, buf);
+}
+
+static ssize_t fan_mode_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int result;
+ u8 new_mode;
+
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ result = store_u8(&new_mode, buf, count);
+ if (result < 0)
+ return result;
+
+ asus->fan_mode = new_mode % ASUS_FAN_MODE_COUNT;
+ fan_mode_write(asus);
+
+ return result;
+}
+
+// Fan mode: 0 - normal, 1 - turbo, 2 - quiet?
+static DEVICE_ATTR_RW(fan_mode);
+
/* Backlight ******************************************************************/
static int read_backlight_power(struct asus_wmi *asus)
@@ -2084,6 +2171,9 @@ static void asus_wmi_handle_notify(int code, struct asus_wmi *asus)
return;
}
+ if (asus->fan_mode_available && code == NOTIFY_KBD_FBM)
+ fan_mode_switch_next(asus);
+
if (is_display_toggle(code) && asus->driver->quirks->no_display_toggle)
return;
@@ -2237,6 +2327,7 @@ static struct attribute *platform_attributes[] = {
&dev_attr_touchpad.attr,
&dev_attr_lid_resume.attr,
&dev_attr_als_enable.attr,
+ &dev_attr_fan_mode.attr,
NULL
};
@@ -2258,6 +2349,8 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj,
devid = ASUS_WMI_DEVID_LID_RESUME;
else if (attr == &dev_attr_als_enable.attr)
devid = ASUS_WMI_DEVID_ALS_ENABLE;
+ else if (attr == &dev_attr_fan_mode.attr)
+ ok = asus->fan_mode_available;
if (devid != -1)
ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0);
@@ -2282,7 +2375,7 @@ static int asus_wmi_sysfs_init(struct platform_device *device)
/* Platform device ************************************************************/
-static int asus_wmi_platform_init(struct asus_wmi *asus)
+static void asus_wmi_platform_init(struct asus_wmi *asus)
{
int rv;
@@ -2334,13 +2427,6 @@ static int asus_wmi_platform_init(struct asus_wmi *asus)
if (asus->driver->quirks->wapf >= 0)
asus_wmi_set_devstate(ASUS_WMI_DEVID_CWAP,
asus->driver->quirks->wapf, NULL);
-
- return asus_wmi_sysfs_init(asus->platform_device);
-}
-
-static void asus_wmi_platform_exit(struct asus_wmi *asus)
-{
- asus_wmi_sysfs_exit(asus->platform_device);
}
/* debugfs ********************************************************************/
@@ -2515,9 +2601,15 @@ static int asus_wmi_add(struct platform_device *pdev)
if (wdrv->detect_quirks)
wdrv->detect_quirks(asus->driver);
- err = asus_wmi_platform_init(asus);
+ asus_wmi_platform_init(asus);
+
+ err = fan_mode_check_present(asus);
if (err)
- goto fail_platform;
+ goto fail_fan_mode;
+
+ err = asus_wmi_sysfs_init(asus->platform_device);
+ if (err)
+ goto fail_sysfs;
err = asus_wmi_input_init(asus);
if (err)
@@ -2612,8 +2704,9 @@ static int asus_wmi_add(struct platform_device *pdev)
fail_hwmon:
asus_wmi_input_exit(asus);
fail_input:
- asus_wmi_platform_exit(asus);
-fail_platform:
+ asus_wmi_sysfs_exit(asus->platform_device);
+fail_sysfs:
+fail_fan_mode:
kfree(asus);
return err;
}
@@ -2630,7 +2723,7 @@ static int asus_wmi_remove(struct platform_device *device)
kbbl_rgb_exit(asus);
asus_wmi_rfkill_exit(asus);
asus_wmi_debugfs_exit(asus);
- asus_wmi_platform_exit(asus);
+ asus_wmi_sysfs_exit(asus->platform_device);
asus_wmi_hwmon_exit(asus);
kfree(asus);
diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
index 25b7b653e6d2..0f3654b7b8a8 100644
--- a/include/linux/platform_data/x86/asus-wmi.h
+++ b/include/linux/platform_data/x86/asus-wmi.h
@@ -59,6 +59,7 @@
#define ASUS_WMI_DEVID_LIGHTBAR 0x00050025
#define ASUS_WMI_DEVID_KBD_RGB 0x00100056
#define ASUS_WMI_DEVID_KBD_RGB2 0x00100057
+#define ASUS_WMI_DEVID_FAN_MODE 0x00110018
/* Misc */
#define ASUS_WMI_DEVID_CAMERA 0x00060013
--
2.17.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v2 09/11] platform/x86: asus-wmi: Control RGB keyboard backlight
2019-04-10 20:33 ` [PATCH 09/11] platform/x86: asus-wmi: Control RGB keyboard backlight Yurii Pavlovskyi
@ 2019-04-11 5:46 ` Yurii Pavlovskyi
0 siblings, 0 replies; 6+ messages in thread
From: Yurii Pavlovskyi @ 2019-04-11 5:46 UTC (permalink / raw)
Cc: Corentin Chary, Darren Hart, Andy Shevchenko, Daniel Drake,
acpi4asus-user, platform-driver-x86, linux-kernel, linux-api
The WMI exposes two methods for controlling RGB keyboard backlight which
allow to control:
* RGB components in range 00 - ff,
* Switch between 4 effects,
* Switch between 3 effect speed modes,
* Separately enable the backlight on boot, in awake state (after driver
load), in sleep mode, and probably in something called shutdown mode
(no observable effects of enabling it are known so far).
The configuration should be written to several sysfs parameter buffers
which are then written via WMI by writing either 1 or 2 to the "kbbl_set"
parameter. When reading the buffers the last written value is returned.
If the 2 is written to "kbbl_set", the parameters will be reset on reboot
(temporary mode), 1 is permanent mode, parameters are retained.
The calls use new 3-dword input buffer method call.
The functionality is only enabled if corresponding DSTS methods return
exact valid values.
The following script demonstrates usage:
echo Red [00 - ff]
echo 33 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_red
echo Green [00 - ff]
echo ff > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_green
echo Blue [00 - ff]
echo 0 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_blue
echo Mode: 0 - static color, 1 - blink, 2 - rainbow, 3 - strobe
echo 0 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_mode
echo Speed for modes 1 and 2: 0 - slow, 1 - medium, 2 - fast
echo 0 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_speed
echo Enable: 02 - on boot, before module load, 08 - awake, 20 - sleep,
echo 2a or ff to set all
echo 2a > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_flags
echo Save: 1 - permanently, 2 - temporarily, reset after reboot
echo 1 > /sys/devices/platform/asus-nb-wmi/kbbl/kbbl_set
Signed-off-by: Yurii Pavlovskyi <yurii.pavlovskyi@gmail.com>
---
.../ABI/testing/sysfs-platform-asus-wmi | 61 ++++
drivers/platform/x86/asus-wmi.c | 329 ++++++++++++++++++
include/linux/platform_data/x86/asus-wmi.h | 2 +
3 files changed, 392 insertions(+)
diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi b/Documentation/ABI/testing/sysfs-platform-asus-wmi
index 019e1e29370e..300a40519695 100644
--- a/Documentation/ABI/testing/sysfs-platform-asus-wmi
+++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi
@@ -36,3 +36,64 @@ KernelVersion: 3.5
Contact: "AceLan Kao" <acelan.kao@canonical.com>
Description:
Resume on lid open. 1 means on, 0 means off.
+
+What: /sys/devices/platform/<platform>/kbbl/kbbl_red
+Date: Apr 2019
+KernelVersion: 5.1
+Contact: "Yurii Pavlovskyi" <yurii.pavlovskyi@gmail.com>
+Description:
+ RGB keyboard backlight red component: 00 .. ff.
+
+What: /sys/devices/platform/<platform>/kbbl/kbbl_green
+Date: Apr 2019
+KernelVersion: 5.1
+Contact: "Yurii Pavlovskyi" <yurii.pavlovskyi@gmail.com>
+Description:
+ RGB keyboard backlight green component: 00 .. ff.
+
+What: /sys/devices/platform/<platform>/kbbl/kbbl_blue
+Date: Apr 2019
+KernelVersion: 5.1
+Contact: "Yurii Pavlovskyi" <yurii.pavlovskyi@gmail.com>
+Description:
+ RGB keyboard backlight blue component: 00 .. ff.
+
+What: /sys/devices/platform/<platform>/kbbl/kbbl_mode
+Date: Apr 2019
+KernelVersion: 5.1
+Contact: "Yurii Pavlovskyi" <yurii.pavlovskyi@gmail.com>
+Description:
+ RGB keyboard backlight mode:
+ * 0 - static color,
+ * 1 - blink,
+ * 2 - rainbow,
+ * 3 - strobe.
+
+What: /sys/devices/platform/<platform>/kbbl/kbbl_speed
+Date: Apr 2019
+KernelVersion: 5.1
+Contact: "Yurii Pavlovskyi" <yurii.pavlovskyi@gmail.com>
+Description:
+ RGB keyboard backlight speed for modes 1 and 2:
+ * 0 - slow,
+ * 1 - medium,
+ * 2 - fast.
+
+What: /sys/devices/platform/<platform>/kbbl/kbbl_flags
+Date: Apr 2019
+KernelVersion: 5.1
+Contact: "Yurii Pavlovskyi" <yurii.pavlovskyi@gmail.com>
+Description:
+ RGB keyboard backlight enable flags (2a to enable everything), OR of:
+ * 02 - on boot (until module load),
+ * 08 - awake,
+ * 20 - sleep.
+
+What: /sys/devices/platform/<platform>/kbbl/kbbl_set
+Date: Apr 2019
+KernelVersion: 5.1
+Contact: "Yurii Pavlovskyi" <yurii.pavlovskyi@gmail.com>
+Description:
+ Write changed RGB keyboard backlight parameters:
+ * 1 - permanently,
+ * 2 - temporarily.
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index de0a8f61d4a1..b4fd200e8335 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -145,6 +145,21 @@ struct asus_rfkill {
u32 dev_id;
};
+struct asus_kbbl_rgb {
+ u8 kbbl_red;
+ u8 kbbl_green;
+ u8 kbbl_blue;
+ u8 kbbl_mode;
+ u8 kbbl_speed;
+
+ u8 kbbl_set_red;
+ u8 kbbl_set_green;
+ u8 kbbl_set_blue;
+ u8 kbbl_set_mode;
+ u8 kbbl_set_speed;
+ u8 kbbl_set_flags;
+};
+
struct asus_wmi {
int dsts_id;
int spec;
@@ -181,6 +196,9 @@ struct asus_wmi {
int asus_hwmon_num_fans;
int asus_hwmon_pwm;
+ bool kbbl_rgb_available;
+ struct asus_kbbl_rgb kbbl_rgb;
+
struct hotplug_slot hotplug_slot;
struct mutex hotplug_lock;
struct mutex wmi_lock;
@@ -656,6 +674,310 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
return rv;
}
+/* RGB keyboard backlight *****************************************************/
+
+static ssize_t show_u8(u8 value, char *buf)
+{
+ return scnprintf(buf, PAGE_SIZE, "%02x\n", value);
+}
+
+static ssize_t store_u8(u8 *value, const char *buf, int count)
+{
+ int err;
+ u8 result;
+
+ err = kstrtou8(buf, 16, &result);
+ if (err < 0) {
+ pr_warn("Trying to store invalid value\n");
+ return err;
+ }
+
+ *value = result;
+
+ return count;
+}
+
+static ssize_t kbbl_red_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return show_u8(asus->kbbl_rgb.kbbl_red, buf);
+}
+
+static ssize_t kbbl_red_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return store_u8(&asus->kbbl_rgb.kbbl_set_red, buf, count);
+}
+
+static ssize_t kbbl_green_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return show_u8(asus->kbbl_rgb.kbbl_green, buf);
+}
+
+static ssize_t kbbl_green_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return store_u8(&asus->kbbl_rgb.kbbl_set_green, buf, count);
+}
+
+static ssize_t kbbl_blue_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return show_u8(asus->kbbl_rgb.kbbl_blue, buf);
+}
+
+static ssize_t kbbl_blue_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return store_u8(&asus->kbbl_rgb.kbbl_set_blue, buf, count);
+}
+
+static ssize_t kbbl_mode_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return show_u8(asus->kbbl_rgb.kbbl_mode, buf);
+}
+
+static ssize_t kbbl_mode_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return store_u8(&asus->kbbl_rgb.kbbl_set_mode, buf, count);
+}
+
+static ssize_t kbbl_speed_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return show_u8(asus->kbbl_rgb.kbbl_speed, buf);
+}
+
+static ssize_t kbbl_speed_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return store_u8(&asus->kbbl_rgb.kbbl_set_speed, buf, count);
+}
+
+static ssize_t kbbl_flags_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return show_u8(asus->kbbl_rgb.kbbl_set_flags, buf);
+}
+
+static ssize_t kbbl_flags_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return store_u8(&asus->kbbl_rgb.kbbl_set_flags, buf, count);
+}
+
+static ssize_t kbbl_set_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return scnprintf(buf, PAGE_SIZE,
+ "Write to configure RGB keyboard backlight\n");
+}
+
+static int kbbl_rgb_write(struct asus_wmi *asus, int persistent)
+{
+ int err;
+ u32 retval;
+ u8 speed_byte;
+ u8 mode_byte;
+ u8 speed;
+ u8 mode;
+
+ speed = asus->kbbl_rgb.kbbl_set_speed;
+ switch (speed) {
+ case 0:
+ default:
+ speed_byte = 0xe1; // slow
+ speed = 0;
+ break;
+ case 1:
+ speed_byte = 0xeb; // medium
+ break;
+ case 2:
+ speed_byte = 0xf5; // fast
+ break;
+ }
+
+ mode = asus->kbbl_rgb.kbbl_set_mode;
+ switch (mode) {
+ case 0:
+ default:
+ mode_byte = 0x00; // static color
+ mode = 0;
+ break;
+ case 1:
+ mode_byte = 0x01; // blink
+ break;
+ case 2:
+ mode_byte = 0x02; // rainbow
+ break;
+ case 3:
+ mode_byte = 0x0a; // strobe
+ break;
+ }
+
+ err = asus_wmi_evaluate_method_3dw(ASUS_WMI_METHODID_DEVS,
+ ASUS_WMI_DEVID_KBD_RGB,
+ (persistent ? 0xb4 : 0xb3) |
+ (mode_byte << 8) |
+ (asus->kbbl_rgb.kbbl_set_red << 16) |
+ (asus->kbbl_rgb.kbbl_set_green << 24),
+ (asus->kbbl_rgb.kbbl_set_blue) |
+ (speed_byte << 8), &retval);
+ if (err) {
+ pr_warn("RGB keyboard device 1, write error: %d\n", err);
+ return err;
+ }
+
+ if (retval != 1) {
+ pr_warn("RGB keyboard device 1, write error (retval): %x\n",
+ retval);
+ return -EIO;
+ }
+
+ err = asus_wmi_evaluate_method_3dw(ASUS_WMI_METHODID_DEVS,
+ ASUS_WMI_DEVID_KBD_RGB2,
+ (0xbd) |
+ (asus->kbbl_rgb.kbbl_set_flags << 16) |
+ (persistent ? 0x0100 : 0x0000), 0, &retval);
+ if (err) {
+ pr_warn("RGB keyboard device 2, write error: %d\n", err);
+ return err;
+ }
+
+ if (retval != 1) {
+ pr_warn("RGB keyboard device 2, write error (retval): %x\n",
+ retval);
+ return -EIO;
+ }
+
+ asus->kbbl_rgb.kbbl_red = asus->kbbl_rgb.kbbl_set_red;
+ asus->kbbl_rgb.kbbl_green = asus->kbbl_rgb.kbbl_set_green;
+ asus->kbbl_rgb.kbbl_blue = asus->kbbl_rgb.kbbl_set_blue;
+ asus->kbbl_rgb.kbbl_mode = mode;
+ asus->kbbl_rgb.kbbl_speed = speed;
+
+ return 0;
+}
+
+static ssize_t kbbl_set_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ u8 value;
+ struct asus_wmi *asus;
+ int result;
+
+ asus = dev_get_drvdata(dev);
+ result = store_u8(&value, buf, count);
+ if (result < 0)
+ return result;
+
+ if (value == 1)
+ kbbl_rgb_write(asus, 1);
+ else if (value == 2)
+ kbbl_rgb_write(asus, 0);
+
+ return count;
+}
+
+/* RGB values: 00 .. ff */
+static DEVICE_ATTR_RW(kbbl_red);
+static DEVICE_ATTR_RW(kbbl_green);
+static DEVICE_ATTR_RW(kbbl_blue);
+
+/* Color modes: 0 - static color, 1 - blink, 2 - rainbow, 3 - strobe */
+static DEVICE_ATTR_RW(kbbl_mode);
+
+/* Speed for modes 1 and 2: 0 - slow, 1 - medium, 2 - fast */
+static DEVICE_ATTR_RW(kbbl_speed);
+
+/*
+ * Enable: 02 - on boot (until module load) | 08 - awake | 20 - sleep
+ * (2a or ff to enable everything)
+ *
+ * Logically 80 would be shutdown, but no visible effects of this option
+ * were observed so far
+ */
+static DEVICE_ATTR_RW(kbbl_flags);
+
+/* Write data: 1 - permanently, 2 - temporarily (reset after reboot) */
+static DEVICE_ATTR_RW(kbbl_set);
+
+static struct attribute *rgbkb_sysfs_attributes[] = {
+ &dev_attr_kbbl_red.attr,
+ &dev_attr_kbbl_green.attr,
+ &dev_attr_kbbl_blue.attr,
+ &dev_attr_kbbl_mode.attr,
+ &dev_attr_kbbl_speed.attr,
+ &dev_attr_kbbl_flags.attr,
+ &dev_attr_kbbl_set.attr,
+ NULL,
+};
+
+static const struct attribute_group kbbl_attribute_group = {
+ .name = "kbbl",
+ .attrs = rgbkb_sysfs_attributes
+};
+
+static int kbbl_rgb_init(struct asus_wmi *asus)
+{
+ int err;
+
+ err = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_KBD_RGB);
+ if (err) {
+ if (err == -ENODEV)
+ return 0;
+ else
+ return err;
+ }
+
+ err = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_KBD_RGB2);
+ if (err) {
+ if (err == -ENODEV)
+ return 0;
+ else
+ return err;
+ }
+
+ asus->kbbl_rgb_available = true;
+ return sysfs_create_group(&asus->platform_device->dev.kobj,
+ &kbbl_attribute_group);
+}
+
+static void kbbl_rgb_exit(struct asus_wmi *asus)
+{
+ if (asus->kbbl_rgb_available) {
+ sysfs_remove_group(&asus->platform_device->dev.kobj,
+ &kbbl_attribute_group);
+ }
+}
+
/* RF *************************************************************************/
/*
@@ -2211,6 +2533,10 @@ static int asus_wmi_add(struct platform_device *pdev)
if (err)
goto fail_leds;
+ err = kbbl_rgb_init(asus);
+ if (err)
+ goto fail_rgbkb;
+
asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WLAN, &result);
if (result & (ASUS_WMI_DSTS_PRESENCE_BIT | ASUS_WMI_DSTS_USER_BIT))
asus->driver->wlan_ctrl_by_user = 1;
@@ -2277,6 +2603,8 @@ static int asus_wmi_add(struct platform_device *pdev)
fail_backlight:
asus_wmi_rfkill_exit(asus);
fail_rfkill:
+ kbbl_rgb_exit(asus);
+fail_rgbkb:
asus_wmi_led_exit(asus);
fail_leds:
asus_wmi_hwmon_exit(asus);
@@ -2298,6 +2626,7 @@ static int asus_wmi_remove(struct platform_device *device)
asus_wmi_backlight_exit(asus);
asus_wmi_input_exit(asus);
asus_wmi_led_exit(asus);
+ kbbl_rgb_exit(asus);
asus_wmi_rfkill_exit(asus);
asus_wmi_debugfs_exit(asus);
asus_wmi_platform_exit(asus);
diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
index 53dfc2541960..25b7b653e6d2 100644
--- a/include/linux/platform_data/x86/asus-wmi.h
+++ b/include/linux/platform_data/x86/asus-wmi.h
@@ -57,6 +57,8 @@
#define ASUS_WMI_DEVID_KBD_BACKLIGHT 0x00050021
#define ASUS_WMI_DEVID_LIGHT_SENSOR 0x00050022 /* ?? */
#define ASUS_WMI_DEVID_LIGHTBAR 0x00050025
+#define ASUS_WMI_DEVID_KBD_RGB 0x00100056
+#define ASUS_WMI_DEVID_KBD_RGB2 0x00100057
/* Misc */
#define ASUS_WMI_DEVID_CAMERA 0x00060013
--
2.17.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v2 10/11] platform/x86: asus-wmi: Switch fan boost mode
2019-04-10 20:34 ` [PATCH 10/11] platform/x86: asus-wmi: Switch fan boost mode Yurii Pavlovskyi
@ 2019-04-11 5:47 ` Yurii Pavlovskyi
2019-04-12 8:03 ` Daniel Drake
0 siblings, 1 reply; 6+ messages in thread
From: Yurii Pavlovskyi @ 2019-04-11 5:47 UTC (permalink / raw)
Cc: Corentin Chary, Darren Hart, Andy Shevchenko, Daniel Drake,
acpi4asus-user, platform-driver-x86, linux-kernel, linux-api
The WMI exposes a write-only device ID where three modes can be switched
on some laptops (TUF Gaming FX505GM). There is a hotkey combination Fn-F5
that does have a fan icon which is designed to toggle between these 3
modes.
Add a SysFS entry that reads the last written value and updates value in
WMI on write and a hotkey handler that toggles the modes. The
corresponding DEVS device handler does obviously take 3 possible
argument values.
Method (SFBM, 1, NotSerialized)
{
If ((Arg0 == Zero) { .. }
If ((Arg0 == One)) { .. }
If ((Arg0 == 0x02)) { .. }
}
... // DEVS
If ((IIA0 == 0x00110018))
{
SFBM (IIA1)
Return (One)
}
* 0x00 - is normal,
* 0x01 - is obviously turbo by the amount of noise, might be useful to
avoid CPU frequency throttling on high load,
* 0x02 - the meaning is unknown at the time as modes are not named
in the vendor documentation, but it does look like a quiet mode as CPU
temperature does increase about 10 degrees on maximum load.
Signed-off-by: Yurii Pavlovskyi <yurii.pavlovskyi@gmail.com>
---
.../ABI/testing/sysfs-platform-asus-wmi | 10 ++
drivers/platform/x86/asus-wmi.c | 119 ++++++++++++++++--
include/linux/platform_data/x86/asus-wmi.h | 1 +
3 files changed, 117 insertions(+), 13 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi b/Documentation/ABI/testing/sysfs-platform-asus-wmi
index 300a40519695..2b3184e297a7 100644
--- a/Documentation/ABI/testing/sysfs-platform-asus-wmi
+++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi
@@ -97,3 +97,13 @@ Description:
Write changed RGB keyboard backlight parameters:
* 1 - permanently,
* 2 - temporarily.
+
+What: /sys/devices/platform/<platform>/fan_mode
+Date: Apr 2019
+KernelVersion: 5.1
+Contact: "Yurii Pavlovskyi" <yurii.pavlovskyi@gmail.com>
+Description:
+ Fan boost mode:
+ * 0 - normal,
+ * 1 - turbo,
+ * 2 - quiet?
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index b4fd200e8335..f0e506feb924 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -69,6 +69,7 @@ MODULE_LICENSE("GPL");
#define NOTIFY_KBD_BRTUP 0xc4
#define NOTIFY_KBD_BRTDWN 0xc5
#define NOTIFY_KBD_BRTTOGGLE 0xc7
+#define NOTIFY_KBD_FBM 0x99
#define ASUS_FAN_DESC "cpu_fan"
#define ASUS_FAN_MFUN 0x13
@@ -77,6 +78,8 @@ MODULE_LICENSE("GPL");
#define ASUS_FAN_CTRL_MANUAL 1
#define ASUS_FAN_CTRL_AUTO 2
+#define ASUS_FAN_MODE_COUNT 3
+
#define USB_INTEL_XUSB2PR 0xD0
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31
@@ -196,6 +199,9 @@ struct asus_wmi {
int asus_hwmon_num_fans;
int asus_hwmon_pwm;
+ bool fan_mode_available;
+ u8 fan_mode;
+
bool kbbl_rgb_available;
struct asus_kbbl_rgb kbbl_rgb;
@@ -1832,6 +1838,87 @@ static int asus_wmi_fan_init(struct asus_wmi *asus)
return 0;
}
+/* Fan mode *******************************************************************/
+
+static int fan_mode_check_present(struct asus_wmi *asus)
+{
+ u32 result;
+ int err;
+
+ asus->fan_mode_available = false;
+
+ err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_MODE, &result);
+ if (err) {
+ if (err == -ENODEV)
+ return 0;
+ else
+ return err;
+ }
+
+ if (result & ASUS_WMI_DSTS_PRESENCE_BIT)
+ asus->fan_mode_available = true;
+
+ return 0;
+}
+
+static int fan_mode_write(struct asus_wmi *asus)
+{
+ int err;
+ u8 value;
+ u32 retval;
+
+ value = asus->fan_mode % ASUS_FAN_MODE_COUNT;
+ pr_info("Set fan mode: %u\n", value);
+ err = asus_wmi_set_devstate(ASUS_WMI_DEVID_FAN_MODE, value, &retval);
+
+ if (err) {
+ pr_warn("Failed to set fan mode: %d\n", err);
+ return err;
+ }
+
+ if (retval != 1) {
+ pr_warn("Failed to set fan mode (retval): 0x%x\n", retval);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int fan_mode_switch_next(struct asus_wmi *asus)
+{
+ asus->fan_mode = (asus->fan_mode + 1) % ASUS_FAN_MODE_COUNT;
+ return fan_mode_write(asus);
+}
+
+static ssize_t fan_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return show_u8(asus->fan_mode, buf);
+}
+
+static ssize_t fan_mode_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int result;
+ u8 new_mode;
+
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ result = store_u8(&new_mode, buf, count);
+ if (result < 0)
+ return result;
+
+ asus->fan_mode = new_mode % ASUS_FAN_MODE_COUNT;
+ fan_mode_write(asus);
+
+ return result;
+}
+
+// Fan mode: 0 - normal, 1 - turbo, 2 - quiet?
+static DEVICE_ATTR_RW(fan_mode);
+
/* Backlight ******************************************************************/
static int read_backlight_power(struct asus_wmi *asus)
@@ -2083,6 +2170,9 @@ static void asus_wmi_handle_notify(int code, struct asus_wmi *asus)
return;
}
+ if (asus->fan_mode_available && code == NOTIFY_KBD_FBM)
+ fan_mode_switch_next(asus);
+
if (is_display_toggle(code) && asus->driver->quirks->no_display_toggle)
return;
@@ -2236,6 +2326,7 @@ static struct attribute *platform_attributes[] = {
&dev_attr_touchpad.attr,
&dev_attr_lid_resume.attr,
&dev_attr_als_enable.attr,
+ &dev_attr_fan_mode.attr,
NULL
};
@@ -2257,6 +2348,8 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj,
devid = ASUS_WMI_DEVID_LID_RESUME;
else if (attr == &dev_attr_als_enable.attr)
devid = ASUS_WMI_DEVID_ALS_ENABLE;
+ else if (attr == &dev_attr_fan_mode.attr)
+ ok = asus->fan_mode_available;
if (devid != -1)
ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0);
@@ -2281,7 +2374,7 @@ static int asus_wmi_sysfs_init(struct platform_device *device)
/* Platform device ************************************************************/
-static int asus_wmi_platform_init(struct asus_wmi *asus)
+static void asus_wmi_platform_init(struct asus_wmi *asus)
{
int rv;
@@ -2333,13 +2426,6 @@ static int asus_wmi_platform_init(struct asus_wmi *asus)
if (asus->driver->quirks->wapf >= 0)
asus_wmi_set_devstate(ASUS_WMI_DEVID_CWAP,
asus->driver->quirks->wapf, NULL);
-
- return asus_wmi_sysfs_init(asus->platform_device);
-}
-
-static void asus_wmi_platform_exit(struct asus_wmi *asus)
-{
- asus_wmi_sysfs_exit(asus->platform_device);
}
/* debugfs ********************************************************************/
@@ -2514,9 +2600,15 @@ static int asus_wmi_add(struct platform_device *pdev)
if (wdrv->detect_quirks)
wdrv->detect_quirks(asus->driver);
- err = asus_wmi_platform_init(asus);
+ asus_wmi_platform_init(asus);
+
+ err = fan_mode_check_present(asus);
if (err)
- goto fail_platform;
+ goto fail_fan_mode;
+
+ err = asus_wmi_sysfs_init(asus->platform_device);
+ if (err)
+ goto fail_sysfs;
err = asus_wmi_input_init(asus);
if (err)
@@ -2611,8 +2703,9 @@ static int asus_wmi_add(struct platform_device *pdev)
fail_hwmon:
asus_wmi_input_exit(asus);
fail_input:
- asus_wmi_platform_exit(asus);
-fail_platform:
+ asus_wmi_sysfs_exit(asus->platform_device);
+fail_sysfs:
+fail_fan_mode:
kfree(asus);
return err;
}
@@ -2629,7 +2722,7 @@ static int asus_wmi_remove(struct platform_device *device)
kbbl_rgb_exit(asus);
asus_wmi_rfkill_exit(asus);
asus_wmi_debugfs_exit(asus);
- asus_wmi_platform_exit(asus);
+ asus_wmi_sysfs_exit(asus->platform_device);
asus_wmi_hwmon_exit(asus);
kfree(asus);
diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
index 25b7b653e6d2..0f3654b7b8a8 100644
--- a/include/linux/platform_data/x86/asus-wmi.h
+++ b/include/linux/platform_data/x86/asus-wmi.h
@@ -59,6 +59,7 @@
#define ASUS_WMI_DEVID_LIGHTBAR 0x00050025
#define ASUS_WMI_DEVID_KBD_RGB 0x00100056
#define ASUS_WMI_DEVID_KBD_RGB2 0x00100057
+#define ASUS_WMI_DEVID_FAN_MODE 0x00110018
/* Misc */
#define ASUS_WMI_DEVID_CAMERA 0x00060013
--
2.17.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v2 10/11] platform/x86: asus-wmi: Switch fan boost mode
2019-04-11 5:47 ` [PATCH v2 " Yurii Pavlovskyi
@ 2019-04-12 8:03 ` Daniel Drake
2019-04-12 20:50 ` Yurii Pavlovskyi
0 siblings, 1 reply; 6+ messages in thread
From: Daniel Drake @ 2019-04-12 8:03 UTC (permalink / raw)
To: Yurii Pavlovskyi
Cc: Corentin Chary, Darren Hart, Andy Shevchenko, acpi4asus-user,
Platform Driver, Linux Kernel, linux-api,
Endless Linux Upstreaming Team
On Thu, Apr 11, 2019 at 1:47 PM Yurii Pavlovskyi
<yurii.pavlovskyi@gmail.com> wrote:
> * 0x00 - is normal,
> * 0x01 - is obviously turbo by the amount of noise, might be useful to
> avoid CPU frequency throttling on high load,
> * 0x02 - the meaning is unknown at the time as modes are not named
> in the vendor documentation, but it does look like a quiet mode as CPU
> temperature does increase about 10 degrees on maximum load.
I'm curious which vendor documentation you're working with here.
>From the spec,
0 = normal
1 = overboost
2 = silent
Also you can use DSTS on the 0x00110018 device to check the exact
capabilities supported, which you should use to refine which modes can
be cycled through.
Bit 0 = overboost supported
Bit 1 = silent supported
Thanks
Daniel
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v2 10/11] platform/x86: asus-wmi: Switch fan boost mode
2019-04-12 8:03 ` Daniel Drake
@ 2019-04-12 20:50 ` Yurii Pavlovskyi
0 siblings, 0 replies; 6+ messages in thread
From: Yurii Pavlovskyi @ 2019-04-12 20:50 UTC (permalink / raw)
To: Daniel Drake
Cc: Corentin Chary, Darren Hart, Andy Shevchenko, acpi4asus-user,
Platform Driver, Linux Kernel, linux-api,
Endless Linux Upstreaming Team
On 12.04.19 10:03, Daniel Drake wrote:
> On Thu, Apr 11, 2019 at 1:47 PM Yurii Pavlovskyi
> <yurii.pavlovskyi@gmail.com> wrote:
>> * 0x00 - is normal,
>> * 0x01 - is obviously turbo by the amount of noise, might be useful to
>> avoid CPU frequency throttling on high load,
>> * 0x02 - the meaning is unknown at the time as modes are not named
>> in the vendor documentation, but it does look like a quiet mode as CPU
>> temperature does increase about 10 degrees on maximum load.
>
> I'm curious which vendor documentation you're working with here.
That would be user manual for FX505 series, which is pretty minimalistic.
It says it has a fan mode switch key and that's it. Following up on your
comment, I've searched more and actually did found their names hidden in
marketing web page on the website. You're right, they call them balanced,
overboost and silent there.
> From the spec,
> 0 = normal
> 1 = overboost
> 2 = silent
> Also you can use DSTS on the 0x00110018 device to check the exact
> capabilities supported, which you should use to refine which modes can
> be cycled through.
> Bit 0 = overboost supported
> Bit 1 = silent supported
>
> Thanks
> Daniel
>
Thanks! I was guessing that the 3 in DSTS must've meant something.
Appreciate your comments! Will definitely implement them. I'm going to post
the v3 patch series approximately middle of next week.
Best regards,
Yurii
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2019-04-12 20:50 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <a8dd77f3-1e28-6416-5f04-aff44d5bb6d6@gmail.com>
2019-04-10 20:33 ` [PATCH 09/11] platform/x86: asus-wmi: Control RGB keyboard backlight Yurii Pavlovskyi
2019-04-11 5:46 ` [PATCH v2 " Yurii Pavlovskyi
2019-04-10 20:34 ` [PATCH 10/11] platform/x86: asus-wmi: Switch fan boost mode Yurii Pavlovskyi
2019-04-11 5:47 ` [PATCH v2 " Yurii Pavlovskyi
2019-04-12 8:03 ` Daniel Drake
2019-04-12 20:50 ` Yurii Pavlovskyi
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).