* [PATCH v5 1/4] platform/x86: bitland-mifs-wmi: Don't fail suspend or resume on profile errors
2026-11-11 17:04 [PATCH v5 0/4] platform/x86: bitland-mifs-wmi: Xiaomi Book Pro 14 support Chris Taraszka
@ 2026-11-11 17:04 ` Chris Taraszka
2026-11-11 17:04 ` [PATCH v5 2/4] platform/x86: bitland-mifs-wmi: Support a separate profile value on battery Chris Taraszka
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Chris Taraszka @ 2026-11-11 17:04 UTC (permalink / raw)
To: Hans de Goede, Ilpo Järvinen
Cc: platform-driver-x86, linux-kernel, Mingyou Chen, Ilya Gladyshev,
Armin Wolf, KentoNion, Matias Civadda, Yun Wang, Oleg Rybalov,
Chris Taraszka
bitland_mifs_wmi_suspend() returns the laptop_profile_get() error to
the PM core, which aborts the whole system suspend. This happens on any
machine whose firmware reports a SystemPerMode value that is missing
from its profile table, and on such a laptop with the lid closed logind
keeps retrying the suspend, so the machine stays awake and drains its
battery.
bitland_mifs_wmi_resume() likewise returns any error other than
-EOPNOTSUPP from restoring the profile, so the resume is reported as
failed although the machine is fully usable.
Saving and restoring the platform profile is best effort. Warn and
continue instead of failing the system transition, and skip the restore
when nothing was saved, so resume does not write a stale or default
profile.
Fixes: dc1ec4fa86b2 ("platform/x86: bitland-mifs-wmi: Add new Bitland MIFS WMI driver")
Reviewed-by: Mingyou Chen <qby140326@gmail.com>
Signed-off-by: Chris Taraszka <chris@miget.com>
---
Notes:
Unchanged since v4.
drivers/platform/x86/bitland-mifs-wmi.c | 22 +++++++++++++++-------
1 file changed, 15 insertions(+), 7 deletions(-)
diff --git a/drivers/platform/x86/bitland-mifs-wmi.c b/drivers/platform/x86/bitland-mifs-wmi.c
index 517bc241cdef..6217486fece8 100644
--- a/drivers/platform/x86/bitland-mifs-wmi.c
+++ b/drivers/platform/x86/bitland-mifs-wmi.c
@@ -213,6 +213,7 @@ struct bitland_mifs_wmi_data {
struct device *pp_dev;
const struct bitland_mifs_quirks *quirks;
enum platform_profile_option saved_profile;
+ bool profile_saved;
};
static int bitland_mifs_wmi_call(struct bitland_mifs_wmi_data *data,
@@ -383,10 +384,15 @@ static int bitland_mifs_wmi_suspend(struct device *dev)
return 0;
ret = laptop_profile_get(data->pp_dev, &profile);
- if (ret == 0)
- data->saved_profile = profile;
+ data->profile_saved = !ret;
+ if (ret) {
+ dev_warn(dev, "Failed to save platform profile: %d\n", ret);
+ return 0;
+ }
- return ret;
+ data->saved_profile = profile;
+
+ return 0;
}
static int bitland_mifs_wmi_resume(struct device *dev)
@@ -394,8 +400,8 @@ static int bitland_mifs_wmi_resume(struct device *dev)
struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev);
int ret;
- /* Skip event device */
- if (!data->pp_dev)
+ /* Skip event device, or nothing was saved on suspend */
+ if (!data->pp_dev || !data->profile_saved)
return 0;
ret = laptop_profile_set(dev, data->saved_profile);
@@ -411,8 +417,10 @@ static int bitland_mifs_wmi_resume(struct device *dev)
}
}
- dev_dbg(dev, "Resuming, restoring profile %d\n", data->saved_profile);
- return ret;
+ if (ret)
+ dev_warn(dev, "Failed to restore platform profile: %d\n", ret);
+
+ return 0;
}
static DEFINE_SIMPLE_DEV_PM_OPS(bitland_mifs_wmi_pm_ops,
--
2.56.0
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH v5 2/4] platform/x86: bitland-mifs-wmi: Support a separate profile value on battery
2026-11-11 17:04 [PATCH v5 0/4] platform/x86: bitland-mifs-wmi: Xiaomi Book Pro 14 support Chris Taraszka
2026-11-11 17:04 ` [PATCH v5 1/4] platform/x86: bitland-mifs-wmi: Don't fail suspend or resume on profile errors Chris Taraszka
@ 2026-11-11 17:04 ` Chris Taraszka
2026-11-11 17:04 ` [PATCH v5 3/4] platform/x86: bitland-mifs-wmi: Add Xiaomi Book Pro 14 quirk Chris Taraszka
2026-11-11 17:04 ` [PATCH v5 4/4] platform/x86: bitland-mifs-wmi: Cycle the platform profile on Fn+K Chris Taraszka
3 siblings, 0 replies; 5+ messages in thread
From: Chris Taraszka @ 2026-11-11 17:04 UTC (permalink / raw)
To: Hans de Goede, Ilpo Järvinen
Cc: platform-driver-x86, linux-kernel, Mingyou Chen, Ilya Gladyshev,
Armin Wolf, KentoNion, Matias Civadda, Yun Wang, Oleg Rybalov,
Chris Taraszka
Some firmware uses a different SystemPerMode value for a profile
depending on the power source. The Xiaomi Book Pro 14 uses 0x04 for
performance on AC and 0x03 on battery, both with a 50-58 W sustained
power limit. Its DTT tables have no battery entry for 0x04, and BIOS
0707 refuses 0x04 on battery.
Let a profile entry be marked for battery use. It is set in place of
the AC entry for the same profile while the system runs on battery,
without the AC check. The firmware does not change the mode when the
power source changes, so re-apply the current profile from an ACPI AC
notifier, taking the AC state from the event. A mutex keeps this from
racing with profile changes from userspace.
The notifier is only registered when the quirks have a battery entry,
so models without one are not affected.
Suggested-by: KentoNion <kento@kekto.ru>
Signed-off-by: Chris Taraszka <chris@miget.com>
---
Notes:
Unchanged since v4.
Tested together with patch 3, with ftrace kprobes on the AC notifier
and on the WMI calls:
AC, performance (0x04), unplug: the notifier sets 0x03 within 1 ms
battery, performance: set 0x03, PL1 58 W
battery, performance (0x03), plug in: the notifier sets 0x04
s2idle from performance on AC: resume restores 0x04
Battery class events are ignored. Here Omarchy also switches to its
saved per-source profile about a second later, which goes through
laptop_profile_set() after the notifier.
drivers/platform/x86/bitland-mifs-wmi.c | 120 +++++++++++++++++++++---
1 file changed, 107 insertions(+), 13 deletions(-)
diff --git a/drivers/platform/x86/bitland-mifs-wmi.c b/drivers/platform/x86/bitland-mifs-wmi.c
index 6217486fece8..4bbf38651ed7 100644
--- a/drivers/platform/x86/bitland-mifs-wmi.c
+++ b/drivers/platform/x86/bitland-mifs-wmi.c
@@ -91,6 +91,7 @@ struct bitland_profile_entry {
enum platform_profile_option profile;
u8 wmi_val;
bool no_set;
+ bool battery; /* Set in place of the AC entry while on battery */
};
struct bitland_mifs_quirks {
@@ -206,8 +207,10 @@ struct bitland_fan_notify_data {
struct bitland_mifs_wmi_data {
struct wmi_device *wdev;
struct mutex lock; /* Protects WMI calls */
+ struct mutex profile_lock; /* Serializes profile changes */
struct led_classdev kbd_led;
struct notifier_block notifier;
+ struct notifier_block ac_notifier;
struct input_dev *input_dev;
struct device *hwmon_dev;
struct device *pp_dev;
@@ -329,34 +332,108 @@ static int bitland_pp_check_capability(struct bitland_mifs_wmi_data *data)
return 0;
}
-static int laptop_profile_set(struct device *dev,
- enum platform_profile_option profile)
+static const struct bitland_profile_entry *
+bitland_profile_set_entry(const struct bitland_mifs_quirks *quirks,
+ enum platform_profile_option profile, bool on_battery)
+{
+ const struct bitland_profile_entry *entry = NULL;
+ size_t i;
+
+ for (i = 0; i < quirks->num_profile_entries; i++) {
+ const struct bitland_profile_entry *e = &quirks->profile_entries[i];
+
+ if (e->profile != profile || e->no_set)
+ continue;
+
+ if (e->battery && on_battery)
+ return e;
+
+ if (!e->battery && !entry)
+ entry = e;
+ }
+
+ return entry;
+}
+
+static int bitland_profile_set(struct bitland_mifs_wmi_data *data,
+ enum platform_profile_option profile,
+ bool on_battery)
{
- struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev);
- const struct bitland_mifs_quirks *quirks = data->quirks;
struct bitland_mifs_input input = {
.operation = cpu_to_le16(WMI_METHOD_SET),
.function = cpu_to_le16(WMI_FN_SYSTEM_PER_MODE),
};
- size_t i;
+ const struct bitland_profile_entry *entry;
int ret;
- if (profile == PLATFORM_PROFILE_PERFORMANCE ||
- profile == PLATFORM_PROFILE_BALANCED_PERFORMANCE) {
+ entry = bitland_profile_set_entry(data->quirks, profile, on_battery);
+ if (!entry)
+ return -EOPNOTSUPP;
+
+ if (!entry->battery &&
+ (profile == PLATFORM_PROFILE_PERFORMANCE ||
+ profile == PLATFORM_PROFILE_BALANCED_PERFORMANCE)) {
ret = bitland_pp_check_capability(data);
if (ret)
return ret;
}
+ input.payload[0] = entry->wmi_val;
+
+ return bitland_mifs_wmi_call(data, &input, NULL);
+}
+
+static int laptop_profile_set(struct device *dev,
+ enum platform_profile_option profile)
+{
+ struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev);
+
+ guard(mutex)(&data->profile_lock);
+
+ return bitland_profile_set(data, profile,
+ !power_supply_is_system_supplied());
+}
+
+static int bitland_ac_notify(struct notifier_block *nb, unsigned long action,
+ void *data)
+{
+ struct bitland_mifs_wmi_data *drv_data =
+ container_of(nb, struct bitland_mifs_wmi_data, ac_notifier);
+ struct acpi_bus_event *event = data;
+ enum platform_profile_option profile;
+ int ret;
+
+ if (strcmp(event->device_class, ACPI_AC_CLASS))
+ return NOTIFY_DONE;
+
+ guard(mutex)(&drv_data->profile_lock);
+
+ /* Switch to the AC or battery value of the current profile */
+ ret = laptop_profile_get(drv_data->pp_dev, &profile);
+ if (!ret)
+ ret = bitland_profile_set(drv_data, profile, !event->data);
+ if (ret)
+ dev_warn(&drv_data->wdev->dev,
+ "Failed to update platform profile on AC change: %d\n", ret);
+
+ return NOTIFY_DONE;
+}
+
+static void bitland_ac_notifier_unregister(void *data)
+{
+ unregister_acpi_notifier(data);
+}
+
+static bool bitland_has_battery_entries(const struct bitland_mifs_quirks *quirks)
+{
+ size_t i;
+
for (i = 0; i < quirks->num_profile_entries; i++) {
- if (quirks->profile_entries[i].profile == profile &&
- !quirks->profile_entries[i].no_set) {
- input.payload[0] = quirks->profile_entries[i].wmi_val;
- return bitland_mifs_wmi_call(data, &input, NULL);
- }
+ if (quirks->profile_entries[i].battery)
+ return true;
}
- return -EOPNOTSUPP;
+ return false;
}
static int platform_profile_probe(void *drvdata, unsigned long *choices)
@@ -805,6 +882,10 @@ static int bitland_mifs_wmi_probe(struct wmi_device *wdev, const void *context)
if (ret)
return ret;
+ ret = devm_mutex_init(&wdev->dev, &drv_data->profile_lock);
+ if (ret)
+ return ret;
+
dev_set_drvdata(&wdev->dev, drv_data);
drv_data->quirks = &default_quirks;
@@ -837,6 +918,19 @@ static int bitland_mifs_wmi_probe(struct wmi_device *wdev, const void *context)
if (IS_ERR(drv_data->pp_dev))
return PTR_ERR(drv_data->pp_dev);
+ if (bitland_has_battery_entries(drv_data->quirks)) {
+ drv_data->ac_notifier.notifier_call = bitland_ac_notify;
+ ret = register_acpi_notifier(&drv_data->ac_notifier);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&wdev->dev,
+ bitland_ac_notifier_unregister,
+ &drv_data->ac_notifier);
+ if (ret)
+ return ret;
+ }
+
/* Register hwmon */
drv_data->hwmon_dev = devm_hwmon_device_register_with_info(&wdev->dev,
"bitland_mifs",
--
2.56.0
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH v5 3/4] platform/x86: bitland-mifs-wmi: Add Xiaomi Book Pro 14 quirk
2026-11-11 17:04 [PATCH v5 0/4] platform/x86: bitland-mifs-wmi: Xiaomi Book Pro 14 support Chris Taraszka
2026-11-11 17:04 ` [PATCH v5 1/4] platform/x86: bitland-mifs-wmi: Don't fail suspend or resume on profile errors Chris Taraszka
2026-11-11 17:04 ` [PATCH v5 2/4] platform/x86: bitland-mifs-wmi: Support a separate profile value on battery Chris Taraszka
@ 2026-11-11 17:04 ` Chris Taraszka
2026-11-11 17:04 ` [PATCH v5 4/4] platform/x86: bitland-mifs-wmi: Cycle the platform profile on Fn+K Chris Taraszka
3 siblings, 0 replies; 5+ messages in thread
From: Chris Taraszka @ 2026-11-11 17:04 UTC (permalink / raw)
To: Hans de Goede, Ilpo Järvinen
Cc: platform-driver-x86, linux-kernel, Mingyou Chen, Ilya Gladyshev,
Armin Wolf, KentoNion, Matias Civadda, Yun Wang, Oleg Rybalov,
Chris Taraszka
The Xiaomi Book Pro 14 (board TM2424) implements SystemPerMode with
its own values. Its firmware ignores 0x00 and 0x01, and GET returns one
of 0x02, 0x03, 0x04, 0x09 or 0x0a, so with the default table balanced
and balanced-performance are no-ops and reading the profile fails with
-EINVAL whenever the firmware is in 0x04, 0x09 or 0x0a. The failed read
also aborts system suspend.
The firmware passes the mode to Intel DTT as an OEM design variable.
The DTT tables in its data vault name the modes and set the sustained
package power limit (PL1) for each, by power source. For the H484 CPU
variant (H404 has the same layout with lower limits):
value AC battery
0x02 Silent 15-25 W Silent 15-25 W
0x0a Silent 15-25 W Silent 15-25 W
0x09 Balance 35-45 W Balance 32-40 W
0x03 Smart 36-37 W Turbo 50-58 W
0x04 Crazy 50-58 W no entry
Map low-power, balanced and performance to 0x02, 0x09 and 0x04, and
use 0x03 for performance on battery, where it gives what 0x04 gives on
AC. 0x0a selects the same tables as 0x02, so report it as low-power
without offering it for SET. There is no balanced-performance mode.
The machine is USB-C only and WMI_FN_SYSTEM_AC_TYPE is not implemented,
so skip the AC type check.
Reviewed-by: Mingyou Chen <qby140326@gmail.com>
Signed-off-by: Chris Taraszka <chris@miget.com>
---
Notes:
v5: add Mingyou's Reviewed-by, otherwise unchanged.
Tested on the Xiaomi Book Pro 14 (TM2424, BIOS XMAPT4B0P0A0A, H484)
with this series on top of v15. Firmware mode (EC 0x68), DTT OEM
variable 1 and PL1 a few seconds after each set:
AC low-power 0x02 ODV1 2 25 W
balanced 0x09 ODV1 0 45 W
performance 0x04 ODV1 4 58 W
battery low-power 0x02 ODV1 2 25 W
performance 0x03 ODV1 1 58 W
drivers/platform/x86/bitland-mifs-wmi.c | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/drivers/platform/x86/bitland-mifs-wmi.c b/drivers/platform/x86/bitland-mifs-wmi.c
index 4bbf38651ed7..c25b4e4c9b3c 100644
--- a/drivers/platform/x86/bitland-mifs-wmi.c
+++ b/drivers/platform/x86/bitland-mifs-wmi.c
@@ -114,7 +114,28 @@ static const struct bitland_mifs_quirks default_quirks = {
.num_profile_entries = ARRAY_SIZE(default_profile_entries),
};
+static const struct bitland_profile_entry tm2424_profile_entries[] = {
+ { PLATFORM_PROFILE_LOW_POWER, 0x02 },
+ { PLATFORM_PROFILE_LOW_POWER, 0x0a, true },
+ { PLATFORM_PROFILE_BALANCED, 0x09 },
+ { PLATFORM_PROFILE_PERFORMANCE, 0x04 },
+ { PLATFORM_PROFILE_PERFORMANCE, 0x03, .battery = true },
+};
+
+static const struct bitland_mifs_quirks tm2424_quirks = {
+ .profile_entries = tm2424_profile_entries,
+ .num_profile_entries = ARRAY_SIZE(tm2424_profile_entries),
+ .skip_ac_type_check = true,
+};
+
static const struct dmi_system_id bitland_mifs_dmi_table[] = {
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "XIAOMI"),
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "TM2424"),
+ },
+ .driver_data = (void *)&tm2424_quirks,
+ },
{}
};
--
2.56.0
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH v5 4/4] platform/x86: bitland-mifs-wmi: Cycle the platform profile on Fn+K
2026-11-11 17:04 [PATCH v5 0/4] platform/x86: bitland-mifs-wmi: Xiaomi Book Pro 14 support Chris Taraszka
` (2 preceding siblings ...)
2026-11-11 17:04 ` [PATCH v5 3/4] platform/x86: bitland-mifs-wmi: Add Xiaomi Book Pro 14 quirk Chris Taraszka
@ 2026-11-11 17:04 ` Chris Taraszka
3 siblings, 0 replies; 5+ messages in thread
From: Chris Taraszka @ 2026-11-11 17:04 UTC (permalink / raw)
To: Hans de Goede, Ilpo Järvinen
Cc: platform-driver-x86, linux-kernel, Mingyou Chen, Ilya Gladyshev,
Armin Wolf, KentoNion, Matias Civadda, Yun Wang, Oleg Rybalov,
Chris Taraszka
On the Xiaomi Book Pro 14 (TM2424), Fn+K sends hotkey event 0x28 and
the firmware does not change SystemPerMode itself. The event is a
request that the vendor software acts on; without that software Fn+K
does nothing on Windows either, and on Linux it is currently logged as
an unknown event.
Call platform_profile_cycle() from the control device when the event
arrives, so Fn+K cycles through the profiles that all platform profile
handlers support. 0x28 is not used for anything else, so no quirk is
needed.
Suggested-by: Hans de Goede <hansg@kernel.org>
Signed-off-by: Chris Taraszka <chris@miget.com>
---
Notes:
v5: cycle on 0x28 for every model, without the quirk flag (Mingyou).
Retested on v7.4-rc1, on battery: three Fn+K presses from low-power
set 0x09, 0x03 and 0x02, each followed by a single 0x16 from the
firmware.
drivers/platform/x86/bitland-mifs-wmi.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/drivers/platform/x86/bitland-mifs-wmi.c b/drivers/platform/x86/bitland-mifs-wmi.c
index c25b4e4c9b3c..d8341683819e 100644
--- a/drivers/platform/x86/bitland-mifs-wmi.c
+++ b/drivers/platform/x86/bitland-mifs-wmi.c
@@ -169,6 +169,7 @@ enum bitland_mifs_event_id {
WMI_EVENT_MIC_MUTE = 33,
WMI_EVENT_RESERVED_23 = 34,
WMI_EVENT_OPEN_APP = 35,
+ WMI_EVENT_PERFORMANCE_CYCLE = 40,
};
enum bitland_mifs_event_type {
@@ -217,6 +218,7 @@ static BLOCKING_NOTIFIER_HEAD(bitland_notifier_list);
enum bitland_notifier_actions {
BITLAND_NOTIFY_KBD_BRIGHTNESS,
BITLAND_NOTIFY_PLATFORM_PROFILE,
+ BITLAND_NOTIFY_PLATFORM_PROFILE_CYCLE,
BITLAND_NOTIFY_HWMON,
};
@@ -869,6 +871,10 @@ static int bitland_notifier_callback(struct notifier_block *nb,
case BITLAND_NOTIFY_PLATFORM_PROFILE:
platform_profile_notify(data_ctx->pp_dev);
break;
+ case BITLAND_NOTIFY_PLATFORM_PROFILE_CYCLE:
+ if (data_ctx->pp_dev)
+ platform_profile_cycle();
+ break;
case BITLAND_NOTIFY_HWMON:
fan_info = data;
@@ -1043,6 +1049,12 @@ static void bitland_mifs_wmi_notify(struct wmi_device *wdev,
NULL);
return;
+ case WMI_EVENT_PERFORMANCE_CYCLE:
+ blocking_notifier_call_chain(&bitland_notifier_list,
+ BITLAND_NOTIFY_PLATFORM_PROFILE_CYCLE,
+ NULL);
+ return;
+
/*
* The device has 3 fans (CPU, GPU, SYS),
* but there are only the CPU and GPU fan has events
--
2.56.0
^ permalink raw reply [flat|nested] 5+ messages in thread