From: Chris Taraszka <chris@miget.com>
To: "Hans de Goede" <hansg@kernel.org>,
"Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>
Cc: platform-driver-x86@vger.kernel.org,
linux-kernel@vger.kernel.org, Mingyou Chen <qby140326@gmail.com>,
Ilya Gladyshev <ilya.gladyshev@linux.dev>,
Armin Wolf <W_Armin@gmx.de>, KentoNion <kento@kekto.ru>,
Matias Civadda <matias.civadda2342001@gmail.com>,
Arsenova <7z0423arsenova@proton.me>,
Chris Taraszka <chris@miget.com>
Subject: [PATCH v4 2/4] platform/x86: bitland-mifs-wmi: Support a separate profile value on battery
Date: Fri, 6 Nov 2026 12:42:58 +0100 [thread overview]
Message-ID: <20261106114300.936782-3-chris@miget.com> (raw)
In-Reply-To: <20261106114300.936782-1-chris@miget.com>
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:
v4: no longer edits the TM2424 entry. Patch 3 now adds the quirk with
0x03 as the battery entry (Mingyou).
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 d092dc696293..97dafd510ba8 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
next prev parent reply other threads:[~2026-11-06 11:43 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-06 11:42 [PATCH v4 0/4] platform/x86: bitland-mifs-wmi: Xiaomi Book Pro 14 support Chris Taraszka
2026-11-06 11:42 ` [PATCH v4 1/4] platform/x86: bitland-mifs-wmi: Don't fail suspend or resume on profile errors Chris Taraszka
2026-11-06 11:42 ` Chris Taraszka [this message]
2026-11-06 11:42 ` [PATCH v4 3/4] platform/x86: bitland-mifs-wmi: Add Xiaomi Book Pro 14 quirk Chris Taraszka
2026-11-06 13:25 ` Mingyou Chen
2026-11-06 11:43 ` [PATCH v4 4/4] platform/x86: bitland-mifs-wmi: Cycle the platform profile on Fn+K Chris Taraszka
2026-11-06 13:19 ` Mingyou Chen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261106114300.936782-3-chris@miget.com \
--to=chris@miget.com \
--cc=7z0423arsenova@proton.me \
--cc=W_Armin@gmx.de \
--cc=hansg@kernel.org \
--cc=ilpo.jarvinen@linux.intel.com \
--cc=ilya.gladyshev@linux.dev \
--cc=kento@kekto.ru \
--cc=linux-kernel@vger.kernel.org \
--cc=matias.civadda2342001@gmail.com \
--cc=platform-driver-x86@vger.kernel.org \
--cc=qby140326@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®