This patch series touches quite a few things along with adding support for some
new features.
- Add support for mini-LED on 2024 ROG lpatops
- Add support for the gpu MUX WMI call on Vivobook laptops
- Add support for the POST boot sound on ROG laptops
- Add support for MCU power-save (ROG Ally only, saves more power on suspend)
- Store written values for ppt_* features
- Small formatting cleanup
- Small fixes to cleanup struct holes found with pahole
Obsoletes:
- https://lore.kernel.org/all/[email protected]/
- https://lore.kernel.org/all/[email protected]/
- https://lore.kernel.org/all/[email protected]/
- https://lore.kernel.org/all/[email protected]/
- https://lore.kernel.org/all/[email protected]/
Changelog:
- V1
- Mini-LED: use asus_wmi_get_devstate() and not asus_wmi_get_devstate_simple()
- Fix dates in Documentation/ABI/testing/sysfs-platform-asus-wmi
- Remove <name>_available bools and rely on devid for:
- gpu_mux
- mini_led
- kbd_rgb (TUF RGB LED)
- V2
- Fix formating on select if/else blocks shown by checkpatch.pl
- V3
- mini-led patch:
- Add error handling
- Add defines inplace of "magic" numbers
- Remove outdated comment
- Clarify existing comments
- post-sound patch:
- Add missing newline
- min-default-ppt patch
Use `%u` instead of `%d` in sysfs_emit()
Luke D. Jones (9):
platform/x86: asus-wmi: add support for 2024 ROG Mini-LED
platform/x86: asus-wmi: add support for Vivobook GPU MUX
platform/x86: asus-wmi: add support variant of TUF RGB
platform/x86: asus-wmi: support toggling POST sound
platform/x86: asus-wmi: store a min default for ppt options
platform/x86: asus-wmi: adjust formatting of ppt-<name>() functions
platform/x86: asus-wmi: ROG Ally increase wait time, allow MCU
powersave
platform/x86: asus-wmi: Add support for MCU powersave
platform/x86: asus-wmi: cleanup main struct to avoid some holes
.../ABI/testing/sysfs-platform-asus-wmi | 26 ++
drivers/platform/x86/asus-wmi.c | 407 ++++++++++++++----
include/linux/platform_data/x86/asus-wmi.h | 6 +
3 files changed, 353 insertions(+), 86 deletions(-)
--
2.44.0
Laptops with any of the ppt or nv tunables default to the minimum setting
on boot so we can safely assume a stored value is correct.
This patch adds storing of those values in the local struct, and enables
reading of those values back. To prevent creating a series of byte holes
in the struct the "<name>_available" bool is removed and
`asus_sysfs_is_visible()` uses the `ASUS_WMI_DEVID_<name>` directly.
Reviewed-by: Ilpo Järvinen <[email protected]>
Signed-off-by: Luke D. Jones <[email protected]>
---
drivers/platform/x86/asus-wmi.c | 127 +++++++++++++++++++++++++-------
1 file changed, 99 insertions(+), 28 deletions(-)
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 3cb907ee07c5..f87500d87f56 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -273,13 +273,13 @@ struct asus_wmi {
u32 gpu_mux_dev;
/* Tunables provided by ASUS for gaming laptops */
- bool ppt_pl2_sppt_available;
- bool ppt_pl1_spl_available;
- bool ppt_apu_sppt_available;
- bool ppt_plat_sppt_available;
- bool ppt_fppt_available;
- bool nv_dyn_boost_available;
- bool nv_temp_tgt_available;
+ u32 ppt_pl2_sppt;
+ u32 ppt_pl1_spl;
+ u32 ppt_apu_sppt;
+ u32 ppt_platform_sppt;
+ u32 ppt_fppt;
+ u32 nv_dynamic_boost;
+ u32 nv_temp_target;
u32 kbd_rgb_dev;
bool kbd_rgb_state_available;
@@ -1031,11 +1031,21 @@ static ssize_t ppt_pl2_sppt_store(struct device *dev,
return -EIO;
}
+ asus->ppt_pl2_sppt = value;
sysfs_notify(&asus->platform_device->dev.kobj, NULL, "ppt_pl2_sppt");
return count;
}
-static DEVICE_ATTR_WO(ppt_pl2_sppt);
+
+static ssize_t ppt_pl2_sppt_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%u\n", asus->ppt_pl2_sppt);
+}
+static DEVICE_ATTR_RW(ppt_pl2_sppt);
/* Tunable: PPT, Intel=PL1, AMD=SPL ******************************************/
static ssize_t ppt_pl1_spl_store(struct device *dev,
@@ -1065,11 +1075,20 @@ static ssize_t ppt_pl1_spl_store(struct device *dev,
return -EIO;
}
+ asus->ppt_pl1_spl = value;
sysfs_notify(&asus->platform_device->dev.kobj, NULL, "ppt_pl1_spl");
return count;
}
-static DEVICE_ATTR_WO(ppt_pl1_spl);
+static ssize_t ppt_pl1_spl_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%u\n", asus->ppt_pl1_spl);
+}
+static DEVICE_ATTR_RW(ppt_pl1_spl);
/* Tunable: PPT APU FPPT ******************************************************/
static ssize_t ppt_fppt_store(struct device *dev,
@@ -1099,11 +1118,21 @@ static ssize_t ppt_fppt_store(struct device *dev,
return -EIO;
}
+ asus->ppt_fppt = value;
sysfs_notify(&asus->platform_device->dev.kobj, NULL, "ppt_fpu_sppt");
return count;
}
-static DEVICE_ATTR_WO(ppt_fppt);
+
+static ssize_t ppt_fppt_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%u\n", asus->ppt_fppt);
+}
+static DEVICE_ATTR_RW(ppt_fppt);
/* Tunable: PPT APU SPPT *****************************************************/
static ssize_t ppt_apu_sppt_store(struct device *dev,
@@ -1133,11 +1162,21 @@ static ssize_t ppt_apu_sppt_store(struct device *dev,
return -EIO;
}
+ asus->ppt_apu_sppt = value;
sysfs_notify(&asus->platform_device->dev.kobj, NULL, "ppt_apu_sppt");
return count;
}
-static DEVICE_ATTR_WO(ppt_apu_sppt);
+
+static ssize_t ppt_apu_sppt_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%u\n", asus->ppt_apu_sppt);
+}
+static DEVICE_ATTR_RW(ppt_apu_sppt);
/* Tunable: PPT platform SPPT ************************************************/
static ssize_t ppt_platform_sppt_store(struct device *dev,
@@ -1167,11 +1206,21 @@ static ssize_t ppt_platform_sppt_store(struct device *dev,
return -EIO;
}
+ asus->ppt_platform_sppt = value;
sysfs_notify(&asus->platform_device->dev.kobj, NULL, "ppt_platform_sppt");
return count;
}
-static DEVICE_ATTR_WO(ppt_platform_sppt);
+
+static ssize_t ppt_platform_sppt_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%u\n", asus->ppt_platform_sppt);
+}
+static DEVICE_ATTR_RW(ppt_platform_sppt);
/* Tunable: NVIDIA dynamic boost *********************************************/
static ssize_t nv_dynamic_boost_store(struct device *dev,
@@ -1201,11 +1250,21 @@ static ssize_t nv_dynamic_boost_store(struct device *dev,
return -EIO;
}
+ asus->nv_dynamic_boost = value;
sysfs_notify(&asus->platform_device->dev.kobj, NULL, "nv_dynamic_boost");
return count;
}
-static DEVICE_ATTR_WO(nv_dynamic_boost);
+
+static ssize_t nv_dynamic_boost_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%u\n", asus->nv_dynamic_boost);
+}
+static DEVICE_ATTR_RW(nv_dynamic_boost);
/* Tunable: NVIDIA temperature target ****************************************/
static ssize_t nv_temp_target_store(struct device *dev,
@@ -1235,11 +1294,21 @@ static ssize_t nv_temp_target_store(struct device *dev,
return -EIO;
}
+ asus->nv_temp_target = value;
sysfs_notify(&asus->platform_device->dev.kobj, NULL, "nv_temp_target");
return count;
}
-static DEVICE_ATTR_WO(nv_temp_target);
+
+static ssize_t nv_temp_target_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%u\n", asus->nv_temp_target);
+}
+static DEVICE_ATTR_RW(nv_temp_target);
/* Battery ********************************************************************/
@@ -4293,19 +4362,19 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj,
else if (attr == &dev_attr_throttle_thermal_policy.attr)
ok = asus->throttle_thermal_policy_available;
else if (attr == &dev_attr_ppt_pl2_sppt.attr)
- ok = asus->ppt_pl2_sppt_available;
+ devid = ASUS_WMI_DEVID_PPT_PL2_SPPT;
else if (attr == &dev_attr_ppt_pl1_spl.attr)
- ok = asus->ppt_pl1_spl_available;
+ devid = ASUS_WMI_DEVID_PPT_PL1_SPL;
else if (attr == &dev_attr_ppt_fppt.attr)
- ok = asus->ppt_fppt_available;
+ devid = ASUS_WMI_DEVID_PPT_FPPT;
else if (attr == &dev_attr_ppt_apu_sppt.attr)
- ok = asus->ppt_apu_sppt_available;
+ devid = ASUS_WMI_DEVID_PPT_APU_SPPT;
else if (attr == &dev_attr_ppt_platform_sppt.attr)
- ok = asus->ppt_plat_sppt_available;
+ devid = ASUS_WMI_DEVID_PPT_PLAT_SPPT;
else if (attr == &dev_attr_nv_dynamic_boost.attr)
- ok = asus->nv_dyn_boost_available;
+ devid = ASUS_WMI_DEVID_NV_DYN_BOOST;
else if (attr == &dev_attr_nv_temp_target.attr)
- ok = asus->nv_temp_tgt_available;
+ devid = ASUS_WMI_DEVID_NV_THERM_TARGET;
else if (attr == &dev_attr_boot_sound.attr)
devid = ASUS_WMI_DEVID_BOOT_SOUND;
else if (attr == &dev_attr_panel_od.attr)
@@ -4551,18 +4620,20 @@ static int asus_wmi_add(struct platform_device *pdev)
if (err)
goto fail_platform;
+ /* ensure defaults for tunables */
+ asus->ppt_pl2_sppt = 5;
+ asus->ppt_pl1_spl = 5;
+ asus->ppt_apu_sppt = 5;
+ asus->ppt_platform_sppt = 5;
+ asus->ppt_fppt = 5;
+ asus->nv_dynamic_boost = 5;
+ asus->nv_temp_target = 75;
+
asus->charge_mode_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_CHARGE_MODE);
asus->egpu_enable_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_EGPU);
asus->egpu_connect_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_EGPU_CONNECTED);
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->ppt_pl2_sppt_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_PPT_PL2_SPPT);
- asus->ppt_pl1_spl_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_PPT_PL1_SPL);
- asus->ppt_fppt_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_PPT_FPPT);
- asus->ppt_apu_sppt_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_PPT_APU_SPPT);
- asus->ppt_plat_sppt_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_PPT_PLAT_SPPT);
- asus->nv_dyn_boost_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_NV_DYN_BOOST);
- asus->nv_temp_tgt_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_NV_THERM_TARGET);
asus->panel_overdrive_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_PANEL_OD);
asus->ally_mcu_usb_switch = acpi_has_method(NULL, ASUS_USB0_PWR_EC0_CSEE)
&& dmi_match(DMI_BOARD_NAME, "RC71L");
--
2.44.0
Shift the call to dev_get_drvdata() up to top of the function block
in all of the ppt_<name>() functions as part of a minor cleanup.
Reviewed-by: Ilpo Järvinen <[email protected]>
Signed-off-by: Luke D. Jones <[email protected]>
---
drivers/platform/x86/asus-wmi.c | 21 +++++++--------------
1 file changed, 7 insertions(+), 14 deletions(-)
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index f87500d87f56..aad4f94cfdba 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -1008,11 +1008,10 @@ static ssize_t ppt_pl2_sppt_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;
u32 value;
- struct asus_wmi *asus = dev_get_drvdata(dev);
-
result = kstrtou32(buf, 10, &value);
if (result)
return result;
@@ -1052,11 +1051,10 @@ static ssize_t ppt_pl1_spl_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;
u32 value;
- struct asus_wmi *asus = dev_get_drvdata(dev);
-
result = kstrtou32(buf, 10, &value);
if (result)
return result;
@@ -1095,11 +1093,10 @@ static ssize_t ppt_fppt_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;
u32 value;
- struct asus_wmi *asus = dev_get_drvdata(dev);
-
result = kstrtou32(buf, 10, &value);
if (result)
return result;
@@ -1139,11 +1136,10 @@ static ssize_t ppt_apu_sppt_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;
u32 value;
- struct asus_wmi *asus = dev_get_drvdata(dev);
-
result = kstrtou32(buf, 10, &value);
if (result)
return result;
@@ -1183,11 +1179,10 @@ static ssize_t ppt_platform_sppt_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;
u32 value;
- struct asus_wmi *asus = dev_get_drvdata(dev);
-
result = kstrtou32(buf, 10, &value);
if (result)
return result;
@@ -1227,11 +1222,10 @@ static ssize_t nv_dynamic_boost_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;
u32 value;
- struct asus_wmi *asus = dev_get_drvdata(dev);
-
result = kstrtou32(buf, 10, &value);
if (result)
return result;
@@ -1271,11 +1265,10 @@ static ssize_t nv_temp_target_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;
u32 value;
- struct asus_wmi *asus = dev_get_drvdata(dev);
-
result = kstrtou32(buf, 10, &value);
if (result)
return result;
--
2.44.0
Add support for an MCU powersave WMI call. This is intended to set the
MCU in to a low-power mode when sleeping. This mode can cut sleep power
use by around half.
Signed-off-by: Luke D. Jones <[email protected]>
---
.../ABI/testing/sysfs-platform-asus-wmi | 9 ++++
drivers/platform/x86/asus-wmi.c | 50 +++++++++++++++++++
2 files changed, 59 insertions(+)
diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi b/Documentation/ABI/testing/sysfs-platform-asus-wmi
index 72933527d2e4..28144371a0f1 100644
--- a/Documentation/ABI/testing/sysfs-platform-asus-wmi
+++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi
@@ -203,3 +203,12 @@ Description:
Set if the BIOS POST sound is played on boot.
* 0 - False,
* 1 - True
+
+What: /sys/devices/platform/<platform>/mcu_powersave
+Date: Apr 2024
+KernelVersion: 6.10
+Contact: "Luke Jones" <[email protected]>
+Description:
+ Set if the MCU can go in to low-power mode on system sleep
+ * 0 - False,
+ * 1 - True
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 7867178a9121..bf36ac11ad02 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -1303,6 +1303,53 @@ static ssize_t nv_temp_target_show(struct device *dev,
}
static DEVICE_ATTR_RW(nv_temp_target);
+/* Ally MCU Powersave ********************************************************/
+static ssize_t mcu_powersave_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_MCU_POWERSAVE);
+ if (result < 0)
+ return result;
+
+ return sysfs_emit(buf, "%d\n", result);
+}
+
+static ssize_t mcu_powersave_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int result, err;
+ u32 enable;
+
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ result = kstrtou32(buf, 10, &enable);
+ if (result)
+ return result;
+
+ if (enable > 1)
+ return -EINVAL;
+
+ err = asus_wmi_set_devstate(ASUS_WMI_DEVID_MCU_POWERSAVE, enable, &result);
+ if (err) {
+ pr_warn("Failed to set MCU powersave: %d\n", err);
+ return err;
+ }
+
+ if (result > 1) {
+ pr_warn("Failed to set MCU powersave (result): 0x%x\n", result);
+ return -EIO;
+ }
+
+ sysfs_notify(&asus->platform_device->dev.kobj, NULL, "mcu_powersave");
+
+ return count;
+}
+static DEVICE_ATTR_RW(mcu_powersave);
+
/* Battery ********************************************************************/
/* The battery maximum charging percentage */
@@ -4315,6 +4362,7 @@ static struct attribute *platform_attributes[] = {
&dev_attr_ppt_platform_sppt.attr,
&dev_attr_nv_dynamic_boost.attr,
&dev_attr_nv_temp_target.attr,
+ &dev_attr_mcu_powersave.attr,
&dev_attr_boot_sound.attr,
&dev_attr_panel_od.attr,
&dev_attr_mini_led_mode.attr,
@@ -4368,6 +4416,8 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj,
devid = ASUS_WMI_DEVID_NV_DYN_BOOST;
else if (attr == &dev_attr_nv_temp_target.attr)
devid = ASUS_WMI_DEVID_NV_THERM_TARGET;
+ else if (attr == &dev_attr_mcu_powersave.attr)
+ devid = ASUS_WMI_DEVID_MCU_POWERSAVE;
else if (attr == &dev_attr_boot_sound.attr)
devid = ASUS_WMI_DEVID_BOOT_SOUND;
else if (attr == &dev_attr_panel_od.attr)
--
2.44.0
The previous work to allow the MCU to be resumed correctly after sleep
and resume tried to take the shortest possible time. However as work
continues in various other parts of the s2idle subsystems it has shown
that it wasn't entirely reliable.
If the MCU disable/enable call is done correctly the MCU fully removes
its USB endpoints, and this shows as a full USB device reconnection on
resume. When we tried to short this as much as possible sometimes the
MCU doesn't get to complete what it needs to do before going to low-power
and this affected the reconnection.
Through trial it is found that the minimum time required is approx 1200ms
to allow a proper disconnect and disable, and the same amount of time on
resume is required to prevent a rapid disconnect/reconnect happening on
seemingly random occasions. To be safe the time is now 1500ms for msleep.
Reviewed-by: Ilpo Järvinen <[email protected]>
Signed-off-by: Luke D. Jones <[email protected]>
---
drivers/platform/x86/asus-wmi.c | 12 ++----------
1 file changed, 2 insertions(+), 10 deletions(-)
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index aad4f94cfdba..7867178a9121 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -140,7 +140,7 @@ module_param(fnlock_default, bool, 0444);
/* Controls the power state of the USB0 hub on ROG Ally which input is on */
#define ASUS_USB0_PWR_EC0_CSEE "\\_SB.PCI0.SBRG.EC0.CSEE"
/* 300ms so far seems to produce a reliable result on AC and battery */
-#define ASUS_USB0_PWR_EC0_CSEE_WAIT 300
+#define ASUS_USB0_PWR_EC0_CSEE_WAIT 1500
static const char * const ashs_ids[] = { "ATK4001", "ATK4002", NULL };
@@ -4827,6 +4827,7 @@ static int asus_hotk_resume_early(struct device *device)
struct asus_wmi *asus = dev_get_drvdata(device);
if (asus->ally_mcu_usb_switch) {
+ /* sleep required to prevent USB0 being yanked then reappearing rapidly */
if (ACPI_FAILURE(acpi_execute_simple_method(NULL, ASUS_USB0_PWR_EC0_CSEE, 0xB8)))
dev_err(device, "ROG Ally MCU failed to connect USB dev\n");
else
@@ -4838,17 +4839,8 @@ static int asus_hotk_resume_early(struct device *device)
static int asus_hotk_prepare(struct device *device)
{
struct asus_wmi *asus = dev_get_drvdata(device);
- int result, err;
if (asus->ally_mcu_usb_switch) {
- /* When powersave is enabled it causes many issues with resume of USB hub */
- result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_MCU_POWERSAVE);
- if (result == 1) {
- dev_warn(device, "MCU powersave enabled, disabling to prevent resume issues");
- err = asus_wmi_set_devstate(ASUS_WMI_DEVID_MCU_POWERSAVE, 0, &result);
- if (err || result != 1)
- dev_err(device, "Failed to set MCU powersave mode: %d\n", err);
- }
/* sleep required to ensure USB0 is disabled before sleep continues */
if (ACPI_FAILURE(acpi_execute_simple_method(NULL, ASUS_USB0_PWR_EC0_CSEE, 0xB7)))
dev_err(device, "ROG Ally MCU failed to disconnect USB dev\n");
--
2.44.0
Support the 2024 mini-led backlight and adjust the related functions
to select the relevant dev-id. Also add `available_mini_led_mode` to the
platform sysfs since the available mini-led levels can be different.
signed-off-by: Luke D. Jones <[email protected]>
---
.../ABI/testing/sysfs-platform-asus-wmi | 8 ++
drivers/platform/x86/asus-wmi.c | 94 +++++++++++++++++--
include/linux/platform_data/x86/asus-wmi.h | 1 +
3 files changed, 93 insertions(+), 10 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi b/Documentation/ABI/testing/sysfs-platform-asus-wmi
index 8a7e25bde085..ef1ac1a20a71 100644
--- a/Documentation/ABI/testing/sysfs-platform-asus-wmi
+++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi
@@ -126,6 +126,14 @@ Description:
Change the mini-LED mode:
* 0 - Single-zone,
* 1 - Multi-zone
+ * 2 - Multi-zone strong (available on newer generation mini-led)
+
+What: /sys/devices/platform/<platform>/available_mini_led_mode
+Date: Apr 2024
+KernelVersion: 6.10
+Contact: "Luke Jones" <[email protected]>
+Description:
+ List the available mini-led modes.
What: /sys/devices/platform/<platform>/ppt_pl1_spl
Date: Jun 2023
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 3f07bbf809ef..588c099103de 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -126,6 +126,17 @@ module_param(fnlock_default, bool, 0444);
#define ASUS_SCREENPAD_BRIGHT_MAX 255
#define ASUS_SCREENPAD_BRIGHT_DEFAULT 60
+#define ASUS_MINI_LED_MODE_MASK 0x03
+/* Standard modes for devices with only on/off */
+#define ASUS_MINI_LED_OFF 0x00
+#define ASUS_MINI_LED_ON 0x01
+/* New mode on some devices, define here to clarify remapping later */
+#define ASUS_MINI_LED_STRONG_MODE 0x02
+/* New modes for devices with 3 mini-led mode types */
+#define ASUS_MINI_LED_2024_WEAK 0x00
+#define ASUS_MINI_LED_2024_STRONG 0x01
+#define ASUS_MINI_LED_2024_OFF 0x02
+
/* Controls the power state of the USB0 hub on ROG Ally which input is on */
#define ASUS_USB0_PWR_EC0_CSEE "\\_SB.PCI0.SBRG.EC0.CSEE"
/* 300ms so far seems to produce a reliable result on AC and battery */
@@ -288,7 +299,7 @@ struct asus_wmi {
bool battery_rsoc_available;
bool panel_overdrive_available;
- bool mini_led_mode_available;
+ u32 mini_led_dev_id;
struct hotplug_slot hotplug_slot;
struct mutex hotplug_lock;
@@ -2108,13 +2119,33 @@ static ssize_t mini_led_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct asus_wmi *asus = dev_get_drvdata(dev);
- int result;
+ int err;
+ u32 value;
- result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_MINI_LED_MODE);
- if (result < 0)
- return result;
+ err = asus_wmi_get_devstate(asus, asus->mini_led_dev_id, &value);
+ if (err < 0)
+ return err;
+ value = value & ASUS_MINI_LED_MODE_MASK;
- return sysfs_emit(buf, "%d\n", result);
+ /*
+ * Remap the mode values to match previous generation mini-led. The last gen
+ * WMI 0 == off, while on this version WMI 2 ==off (flipped).
+ */
+ if (asus->mini_led_dev_id == ASUS_WMI_DEVID_MINI_LED_MODE2) {
+ switch (value) {
+ case ASUS_MINI_LED_2024_WEAK:
+ value = ASUS_MINI_LED_ON;
+ break;
+ case ASUS_MINI_LED_2024_STRONG:
+ value = ASUS_MINI_LED_STRONG_MODE;
+ break;
+ case ASUS_MINI_LED_2024_OFF:
+ value = ASUS_MINI_LED_OFF;
+ break;
+ }
+ }
+
+ return sysfs_emit(buf, "%d\n", value);
}
static ssize_t mini_led_mode_store(struct device *dev,
@@ -2130,11 +2161,30 @@ static ssize_t mini_led_mode_store(struct device *dev,
if (result)
return result;
- if (mode > 1)
+ if (mode > 1 && asus->mini_led_dev_id == ASUS_WMI_DEVID_MINI_LED_MODE)
+ return -EINVAL;
+ if (mode > 2 && asus->mini_led_dev_id == ASUS_WMI_DEVID_MINI_LED_MODE2)
return -EINVAL;
- err = asus_wmi_set_devstate(ASUS_WMI_DEVID_MINI_LED_MODE, mode, &result);
+ /*
+ * Remap the mode values so expected behaviour is the same as the last
+ * generation of mini-LED with 0 == off, 1 == on.
+ */
+ if (asus->mini_led_dev_id == ASUS_WMI_DEVID_MINI_LED_MODE2) {
+ switch (mode) {
+ case ASUS_MINI_LED_OFF:
+ mode = ASUS_MINI_LED_2024_OFF;
+ break;
+ case ASUS_MINI_LED_ON:
+ mode = ASUS_MINI_LED_2024_WEAK;
+ break;
+ case ASUS_MINI_LED_STRONG_MODE:
+ mode = ASUS_MINI_LED_2024_STRONG;
+ break;
+ }
+ }
+ err = asus_wmi_set_devstate(asus->mini_led_dev_id, mode, &result);
if (err) {
pr_warn("Failed to set mini-LED: %d\n", err);
return err;
@@ -2151,6 +2201,23 @@ static ssize_t mini_led_mode_store(struct device *dev,
}
static DEVICE_ATTR_RW(mini_led_mode);
+static ssize_t available_mini_led_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ switch (asus->mini_led_dev_id) {
+ case ASUS_WMI_DEVID_MINI_LED_MODE:
+ return sysfs_emit(buf, "0 1\n");
+ case ASUS_WMI_DEVID_MINI_LED_MODE2:
+ return sysfs_emit(buf, "0 1 2\n");
+ }
+
+ return sysfs_emit(buf, "0\n");
+}
+
+static DEVICE_ATTR_RO(available_mini_led_mode);
+
/* Quirks *********************************************************************/
static void asus_wmi_set_xusb2pr(struct asus_wmi *asus)
@@ -4139,6 +4206,7 @@ static struct attribute *platform_attributes[] = {
&dev_attr_nv_temp_target.attr,
&dev_attr_panel_od.attr,
&dev_attr_mini_led_mode.attr,
+ &dev_attr_available_mini_led_mode.attr,
NULL
};
@@ -4191,7 +4259,9 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj,
else if (attr == &dev_attr_panel_od.attr)
ok = asus->panel_overdrive_available;
else if (attr == &dev_attr_mini_led_mode.attr)
- ok = asus->mini_led_mode_available;
+ ok = asus->mini_led_dev_id != 0;
+ else if (attr == &dev_attr_available_mini_led_mode.attr)
+ ok = asus->mini_led_dev_id != 0;
if (devid != -1)
ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0);
@@ -4444,10 +4514,14 @@ static int asus_wmi_add(struct platform_device *pdev)
asus->nv_dyn_boost_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_NV_DYN_BOOST);
asus->nv_temp_tgt_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_NV_THERM_TARGET);
asus->panel_overdrive_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_PANEL_OD);
- asus->mini_led_mode_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_MINI_LED_MODE);
asus->ally_mcu_usb_switch = acpi_has_method(NULL, ASUS_USB0_PWR_EC0_CSEE)
&& dmi_match(DMI_BOARD_NAME, "RC71L");
+ if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_MINI_LED_MODE))
+ asus->mini_led_dev_id = ASUS_WMI_DEVID_MINI_LED_MODE;
+ else if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_MINI_LED_MODE2))
+ asus->mini_led_dev_id = ASUS_WMI_DEVID_MINI_LED_MODE2;
+
err = fan_boost_mode_check_present(asus);
if (err)
goto fail_fan_boost_mode;
diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
index ab1c7deff118..9cadce10ad9a 100644
--- a/include/linux/platform_data/x86/asus-wmi.h
+++ b/include/linux/platform_data/x86/asus-wmi.h
@@ -71,6 +71,7 @@
#define ASUS_WMI_DEVID_LID_FLIP 0x00060062
#define ASUS_WMI_DEVID_LID_FLIP_ROG 0x00060077
#define ASUS_WMI_DEVID_MINI_LED_MODE 0x0005001E
+#define ASUS_WMI_DEVID_MINI_LED_MODE2 0x0005002E
/* Storage */
#define ASUS_WMI_DEVID_CARDREADER 0x00080013
--
2.44.0
On Wed, 3 Apr 2024, Luke D. Jones wrote:
> Laptops with any of the ppt or nv tunables default to the minimum setting
> on boot so we can safely assume a stored value is correct.
>
> This patch adds storing of those values in the local struct, and enables
> reading of those values back. To prevent creating a series of byte holes
> in the struct the "<name>_available" bool is removed and
> `asus_sysfs_is_visible()` uses the `ASUS_WMI_DEVID_<name>` directly.
>
> Reviewed-by: Ilpo Järvinen <[email protected]>
> Signed-off-by: Luke D. Jones <[email protected]>
Reviewed-by: Ilpo Järvinen <[email protected]>
(I know it is already among the tags but I had not given it until now.)
--
i.
On Wed, 3 Apr 2024, at 9:23 PM, Ilpo Järvinen wrote:
> On Wed, 3 Apr 2024, Luke D. Jones wrote:
>
> > Laptops with any of the ppt or nv tunables default to the minimum setting
> > on boot so we can safely assume a stored value is correct.
> >
> > This patch adds storing of those values in the local struct, and enables
> > reading of those values back. To prevent creating a series of byte holes
> > in the struct the "<name>_available" bool is removed and
> > `asus_sysfs_is_visible()` uses the `ASUS_WMI_DEVID_<name>` directly.
> >
> > Reviewed-by: Ilpo Järvinen <[email protected]>
> > Signed-off-by: Luke D. Jones <[email protected]>
>
> Reviewed-by: Ilpo Järvinen <[email protected]>
>
> (I know it is already among the tags but I had not given it until now.)
>
> --
> i.
>
Understood, very sorry about making that assumption.
Luke.
On Wed, 3 Apr 2024, Luke D. Jones wrote:
> Add support for an MCU powersave WMI call. This is intended to set the
> MCU in to a low-power mode when sleeping. This mode can cut sleep power
> use by around half.
>
> Signed-off-by: Luke D. Jones <[email protected]>
Reviewed-by: Ilpo J?rvinen <[email protected]>
--
i.
On Wed, 3 Apr 2024, Luke D. Jones wrote:
> Support the 2024 mini-led backlight and adjust the related functions
> to select the relevant dev-id. Also add `available_mini_led_mode` to the
> platform sysfs since the available mini-led levels can be different.
>
> signed-off-by: Luke D. Jones <[email protected]>
A few small things still below, but in general this is now in much better
shape!
> ---
> .../ABI/testing/sysfs-platform-asus-wmi | 8 ++
> drivers/platform/x86/asus-wmi.c | 94 +++++++++++++++++--
> include/linux/platform_data/x86/asus-wmi.h | 1 +
> 3 files changed, 93 insertions(+), 10 deletions(-)
>
> diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi b/Documentation/ABI/testing/sysfs-platform-asus-wmi
> index 8a7e25bde085..ef1ac1a20a71 100644
> --- a/Documentation/ABI/testing/sysfs-platform-asus-wmi
> +++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi
> @@ -126,6 +126,14 @@ Description:
> Change the mini-LED mode:
> * 0 - Single-zone,
> * 1 - Multi-zone
> + * 2 - Multi-zone strong (available on newer generation mini-led)
> +
> +What: /sys/devices/platform/<platform>/available_mini_led_mode
> +Date: Apr 2024
> +KernelVersion: 6.10
> +Contact: "Luke Jones" <[email protected]>
> +Description:
> + List the available mini-led modes.
>
> What: /sys/devices/platform/<platform>/ppt_pl1_spl
> Date: Jun 2023
> diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
> index 3f07bbf809ef..588c099103de 100644
> --- a/drivers/platform/x86/asus-wmi.c
> +++ b/drivers/platform/x86/asus-wmi.c
> @@ -126,6 +126,17 @@ module_param(fnlock_default, bool, 0444);
> #define ASUS_SCREENPAD_BRIGHT_MAX 255
> #define ASUS_SCREENPAD_BRIGHT_DEFAULT 60
>
> +#define ASUS_MINI_LED_MODE_MASK 0x03
> +/* Standard modes for devices with only on/off */
> +#define ASUS_MINI_LED_OFF 0x00
> +#define ASUS_MINI_LED_ON 0x01
> +/* New mode on some devices, define here to clarify remapping later */
> +#define ASUS_MINI_LED_STRONG_MODE 0x02
> +/* New modes for devices with 3 mini-led mode types */
> +#define ASUS_MINI_LED_2024_WEAK 0x00
> +#define ASUS_MINI_LED_2024_STRONG 0x01
> +#define ASUS_MINI_LED_2024_OFF 0x02
> +
> /* Controls the power state of the USB0 hub on ROG Ally which input is on */
> #define ASUS_USB0_PWR_EC0_CSEE "\\_SB.PCI0.SBRG.EC0.CSEE"
> /* 300ms so far seems to produce a reliable result on AC and battery */
> @@ -288,7 +299,7 @@ struct asus_wmi {
> bool battery_rsoc_available;
>
> bool panel_overdrive_available;
> - bool mini_led_mode_available;
> + u32 mini_led_dev_id;
>
> struct hotplug_slot hotplug_slot;
> struct mutex hotplug_lock;
> @@ -2108,13 +2119,33 @@ static ssize_t mini_led_mode_show(struct device *dev,
> struct device_attribute *attr, char *buf)
> {
> struct asus_wmi *asus = dev_get_drvdata(dev);
> - int result;
> + int err;
> + u32 value;
reverse xmas order.
>
> - result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_MINI_LED_MODE);
> - if (result < 0)
> - return result;
> + err = asus_wmi_get_devstate(asus, asus->mini_led_dev_id, &value);
> + if (err < 0)
> + return err;
> + value = value & ASUS_MINI_LED_MODE_MASK;
>
> - return sysfs_emit(buf, "%d\n", result);
> + /*
> + * Remap the mode values to match previous generation mini-led. The last gen
> + * WMI 0 == off, while on this version WMI 2 ==off (flipped).
> + */
> + if (asus->mini_led_dev_id == ASUS_WMI_DEVID_MINI_LED_MODE2) {
> + switch (value) {
> + case ASUS_MINI_LED_2024_WEAK:
> + value = ASUS_MINI_LED_ON;
> + break;
> + case ASUS_MINI_LED_2024_STRONG:
> + value = ASUS_MINI_LED_STRONG_MODE;
> + break;
> + case ASUS_MINI_LED_2024_OFF:
> + value = ASUS_MINI_LED_OFF;
> + break;
> + }
> + }
> +
> + return sysfs_emit(buf, "%d\n", value);
> }
>
> static ssize_t mini_led_mode_store(struct device *dev,
> @@ -2130,11 +2161,30 @@ static ssize_t mini_led_mode_store(struct device *dev,
> if (result)
> return result;
>
> - if (mode > 1)
> + if (mode > 1 && asus->mini_led_dev_id == ASUS_WMI_DEVID_MINI_LED_MODE)
> + return -EINVAL;
> + if (mode > 2 && asus->mini_led_dev_id == ASUS_WMI_DEVID_MINI_LED_MODE2)
These literals should use the new defines I think.
With those small things fixed,
Reviewed-by: Ilpo J?rvinen <[email protected]>
--
i.
> return -EINVAL;
>
> - err = asus_wmi_set_devstate(ASUS_WMI_DEVID_MINI_LED_MODE, mode, &result);
> + /*
> + * Remap the mode values so expected behaviour is the same as the last
> + * generation of mini-LED with 0 == off, 1 == on.
> + */
> + if (asus->mini_led_dev_id == ASUS_WMI_DEVID_MINI_LED_MODE2) {
> + switch (mode) {
> + case ASUS_MINI_LED_OFF:
> + mode = ASUS_MINI_LED_2024_OFF;
> + break;
> + case ASUS_MINI_LED_ON:
> + mode = ASUS_MINI_LED_2024_WEAK;
> + break;
> + case ASUS_MINI_LED_STRONG_MODE:
> + mode = ASUS_MINI_LED_2024_STRONG;
> + break;
> + }
> + }
>
> + err = asus_wmi_set_devstate(asus->mini_led_dev_id, mode, &result);
> if (err) {
> pr_warn("Failed to set mini-LED: %d\n", err);
> return err;
> @@ -2151,6 +2201,23 @@ static ssize_t mini_led_mode_store(struct device *dev,
> }
> static DEVICE_ATTR_RW(mini_led_mode);
>
> +static ssize_t available_mini_led_mode_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct asus_wmi *asus = dev_get_drvdata(dev);
> +
> + switch (asus->mini_led_dev_id) {
> + case ASUS_WMI_DEVID_MINI_LED_MODE:
> + return sysfs_emit(buf, "0 1\n");
> + case ASUS_WMI_DEVID_MINI_LED_MODE2:
> + return sysfs_emit(buf, "0 1 2\n");
> + }
> +
> + return sysfs_emit(buf, "0\n");
> +}
> +
> +static DEVICE_ATTR_RO(available_mini_led_mode);
> +
> /* Quirks *********************************************************************/
>
> static void asus_wmi_set_xusb2pr(struct asus_wmi *asus)
> @@ -4139,6 +4206,7 @@ static struct attribute *platform_attributes[] = {
> &dev_attr_nv_temp_target.attr,
> &dev_attr_panel_od.attr,
> &dev_attr_mini_led_mode.attr,
> + &dev_attr_available_mini_led_mode.attr,
> NULL
> };
>
> @@ -4191,7 +4259,9 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj,
> else if (attr == &dev_attr_panel_od.attr)
> ok = asus->panel_overdrive_available;
> else if (attr == &dev_attr_mini_led_mode.attr)
> - ok = asus->mini_led_mode_available;
> + ok = asus->mini_led_dev_id != 0;
> + else if (attr == &dev_attr_available_mini_led_mode.attr)
> + ok = asus->mini_led_dev_id != 0;
>
> if (devid != -1)
> ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0);
> @@ -4444,10 +4514,14 @@ static int asus_wmi_add(struct platform_device *pdev)
> asus->nv_dyn_boost_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_NV_DYN_BOOST);
> asus->nv_temp_tgt_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_NV_THERM_TARGET);
> asus->panel_overdrive_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_PANEL_OD);
> - asus->mini_led_mode_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_MINI_LED_MODE);
> asus->ally_mcu_usb_switch = acpi_has_method(NULL, ASUS_USB0_PWR_EC0_CSEE)
> && dmi_match(DMI_BOARD_NAME, "RC71L");
>
> + if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_MINI_LED_MODE))
> + asus->mini_led_dev_id = ASUS_WMI_DEVID_MINI_LED_MODE;
> + else if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_MINI_LED_MODE2))
> + asus->mini_led_dev_id = ASUS_WMI_DEVID_MINI_LED_MODE2;
> +
> err = fan_boost_mode_check_present(asus);
> if (err)
> goto fail_fan_boost_mode;
> diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
> index ab1c7deff118..9cadce10ad9a 100644
> --- a/include/linux/platform_data/x86/asus-wmi.h
> +++ b/include/linux/platform_data/x86/asus-wmi.h
> @@ -71,6 +71,7 @@
> #define ASUS_WMI_DEVID_LID_FLIP 0x00060062
> #define ASUS_WMI_DEVID_LID_FLIP_ROG 0x00060077
> #define ASUS_WMI_DEVID_MINI_LED_MODE 0x0005001E
> +#define ASUS_WMI_DEVID_MINI_LED_MODE2 0x0005002E
>
> /* Storage */
> #define ASUS_WMI_DEVID_CARDREADER 0x00080013
>