From: Bozhen Peng <bozhenpeng93@gmail.com>
To: "Hans de Goede" <hansg@kernel.org>,
"Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>
Cc: Armin Wolf <W_Armin@gmx.de>, Gladyshev Ilya <foxido@foxido.dev>,
Mingyou Chen <qby140326@gmail.com>,
Jonathan Corbet <corbet@lwn.net>,
Shuah Khan <skhan@linuxfoundation.org>,
Randy Dunlap <rdunlap@infradead.org>,
platform-driver-x86@vger.kernel.org, linux-doc@vger.kernel.org,
linux-kernel@vger.kernel.org
Subject: [PATCH] platform/x86: bitland-mifs-wmi: Support WUJIE15 PRO HPT
Date: Sun, 13 Sep 2026 18:12:08 -0700 [thread overview]
Message-ID: <0da43a8c-1755-49fe-b8e2-403489297f06@gmail.com> (raw)
I found that my WUJIE15 PRO with the HPT board uses three profiles:
performance (0), balanced (1), and quiet (2). The driver's default
mapping gets these wrong and exposes a fourth mode that the firmware
does not implement. My laptop only has USB-C power input, so the
circular-hole adapter requirement also rules out its supported external-
power configuration.
I added a check matching the vendor, product and board names to select
the correct profiles and power check. During testing, I saw
power_supply_is_system_supplied() return true even after I unplugged the
charger during sleep. I switched the check to the ACPI adapter's _PSR,
which reads the current power-present bit on this machine. I also made
resume select balanced if power was removed, rather than restore the
saved performance profile. If the power query fails, I return the error
instead of assuming the charger is connected.
I checked the firmware methods and found only two fan readings, in F2/F1
order, and no MIFS CPU temperature reading. Some unimplemented GET
commands return success with zero data, so I could not use a successful
query to establish sensor support. I register the two fans and leave out
the temperature input. I also hide the unsupported GPU, RGB mode and fan
boost controls, and check firmware replies to the remaining SET
commands. I left normal keyboard backlight control unchanged.
I found that the event GUID also matches redmi-wmi, so I made that
driver decline this machine and leave its notifications to bitland-mifs-
wmi. I kept the existing behavior for other machines and documented the
HPT differences.
Signed-off-by: Bozhen Peng <bozhenpeng93@gmail.com>
---
I tested this on my MECHREVO WUJIE15 PRO (WUJIE15 Series-HPT), comparing
the firmware methods and profile values with the Windows OEM controls.
I ran the hardware tests on Ubuntu 26.04.1, kernel 7.0.0-30-generic. I
used a small adaptation for that kernel's older WMI API in the test
modules. I have left that adaptation and the extra diagnostic logging
out of this patch.
I ran 22 automated profile, backlight, binding and sensor checks, all of
which passed, and tested the actual hotkeys. I recorded about 2280 and
2850-2970 RPM from the two fans while running, and zero when stopped. I
did not independently calibrate the RPM readings.
For the final _PSR change, I ran two s2idle tests. With the charger
connected, the driver read supplied=1 and restored performance; it
stayed in performance afterwards. With the charger unplugged during
sleep, it read supplied=0 and restored balanced; it stayed balanced
afterwards. Requesting performance on battery then returned EOPNOTSUPP.
The logs include the resume decision and firmware readbacks.
I also built a full x86_64 kernel and both modules against the for-next
base below, without the WMI API adaptation. I used x86_64_defconfig with
BITLAND_MIFS_WMI and REDMI_WMI enabled, GCC 15.2.0 and W=1. I saw no
compiler warnings from either changed driver.
.../wmi/devices/bitland-mifs-wmi.rst | 31 ++++
drivers/platform/x86/bitland-mifs-wmi.c | 170 +++++++++++++++++-
drivers/platform/x86/redmi-wmi.c | 7 +
3 files changed, 201 insertions(+), 7 deletions(-)
diff --git a/Documentation/wmi/devices/bitland-mifs-wmi.rst b/Documentation/wmi/devices/bitland-mifs-wmi.rst
index 9e86ecc29..fd966cf18 100644
--- a/Documentation/wmi/devices/bitland-mifs-wmi.rst
+++ b/Documentation/wmi/devices/bitland-mifs-wmi.rst
@@ -7,6 +7,37 @@ Bitland MIFS driver (bitland-mifs-wmi)
Introduction
============
+MECHREVO WUJIE15 PRO (HPT) firmware variant
+-----------------------------------------
+
+The MECHREVO WUJIE15 PRO with board WUJIE15 Series-HPT uses a reduced
+interface. The driver selects this variant using exact system vendor,
+product name and board name matches.
+
+The supported platform profiles are low-power (raw value 2), balanced
+(1), and performance (0). There is no separate full-speed mode.
+USB-C is the only external power input on this model. Performance is
+available on external power, but not on battery. The driver queries the
+ACPI adapter's _PSR for current power presence because USB-C connector
+ONLINE state may still be stale during resume. A failed query is not
+treated as external power being present.
+On resume, a saved performance profile becomes balanced if external
+power was removed, matching the firmware's AC-loss transition.
+
+Command 0x0d returns two little-endian tachometers, EC Fan 2 followed by
+Fan 1. No third fan or CPU-temperature input is provided. GPU switching,
+RGB modes and fan boost are not exposed. Keyboard backlight brightness
+uses the normal 0 through 3 values.
+
+Unsupported GET commands can return success (0x8000) with an empty
+payload. Successful queries alone therefore do not establish the
+presence of a sensor or control; the driver uses the known capabilities.
+Supported profile and backlight SET calls are also checked for a
+firmware acknowledgment.
+
+The event GUID is shared with redmi-wmi. On this exact DMI combination,
+redmi-wmi declines binding so bitland-mifs-wmi handles the notifications.
+
EC WMI interface description
============================
diff --git a/drivers/platform/x86/bitland-mifs-wmi.c b/drivers/platform/x86/bitland-mifs-wmi.c
index 3a3731845..3193ae4bd 100644
--- a/drivers/platform/x86/bitland-mifs-wmi.c
+++ b/drivers/platform/x86/bitland-mifs-wmi.c
@@ -14,6 +14,7 @@
#include <linux/dev_printk.h>
#include <linux/device.h>
#include <linux/device/devres.h>
+#include <linux/dmi.h>
#include <linux/err.h>
#include <linux/hwmon.h>
#include <linux/init.h>
@@ -159,6 +160,19 @@ struct bitland_mifs_wmi_data {
struct device *hwmon_dev;
struct device *pp_dev;
enum platform_profile_option saved_profile;
+ bool is_wujie15_hpt;
+};
+
+/* Profile mapping and capabilities for the WUJIE15 PRO HPT board. */
+static const struct dmi_system_id bitland_wujie15_hpt[] = {
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "MECHREVO"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "WUJIE15 PRO"),
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "WUJIE15 Series-HPT"),
+ },
+ },
+ { }
};
static int bitland_mifs_wmi_call(struct bitland_mifs_wmi_data *data,
@@ -167,12 +181,15 @@ static int bitland_mifs_wmi_call(struct bitland_mifs_wmi_data *data,
{
struct wmi_buffer in_buf = { .length = sizeof(*input), .data = (void *)input };
struct wmi_buffer out_buf = { 0 };
+ struct bitland_mifs_output reply;
int ret;
guard(mutex)(&data->lock);
- if (!output)
+ if (!output && !data->is_wujie15_hpt)
return wmidev_invoke_procedure(data->wdev, 0, 1, &in_buf);
+ if (!output)
+ output = &reply;
ret = wmidev_invoke_method(data->wdev, 0, 1, &in_buf, &out_buf, sizeof(*output));
if (ret)
@@ -181,6 +198,20 @@ static int bitland_mifs_wmi_call(struct bitland_mifs_wmi_data *data,
memcpy(output, out_buf.data, sizeof(*output));
kfree(out_buf.data);
+ /* HPT's supported profile/backlight calls acknowledge with 0x8000. */
+ if (data->is_wujie15_hpt) {
+ u16 status = get_unaligned_le16(output);
+
+ if (status == 0xe000)
+ return -EOPNOTSUPP;
+ if (status != 0x8000)
+ return -EIO;
+ dev_dbg(&data->wdev->dev,
+ "HPT op=%#x fn=%#x arg=%u value=%u status=%#x\n",
+ input->operation, input->function, input->payload[0],
+ output->data[0], status);
+ }
+
return 0;
}
@@ -201,6 +232,22 @@ static int laptop_profile_get(struct device *dev,
if (ret)
return ret;
+ if (data->is_wujie15_hpt) {
+ switch (result.data[0]) {
+ case 0:
+ *profile = PLATFORM_PROFILE_PERFORMANCE;
+ return 0;
+ case 1:
+ *profile = PLATFORM_PROFILE_BALANCED;
+ return 0;
+ case 2:
+ *profile = PLATFORM_PROFILE_LOW_POWER;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ }
+
switch (result.data[0]) {
case WMI_PP_BALANCED:
*profile = PLATFORM_PROFILE_BALANCED;
@@ -220,6 +267,26 @@ static int laptop_profile_get(struct device *dev,
return 0;
}
+static int bitland_wujie15_is_supplied(void)
+{
+ struct acpi_device *adapter;
+ unsigned long long online;
+ acpi_status status;
+
+ /* USB-C ONLINE can still contain the pre-suspend connector state. */
+ adapter = acpi_dev_get_first_match_dev("ACPI0003", NULL, -1);
+ if (!adapter)
+ return -ENODEV;
+
+ /* This machine's adapter _PSR reads the current EC power-present bit. */
+ status = acpi_evaluate_integer(adapter->handle, "_PSR", NULL, &online);
+ acpi_dev_put(adapter);
+ if (ACPI_FAILURE(status) || online > 1)
+ return -EIO;
+
+ return online;
+}
+
static int bitland_check_performance_capability(struct bitland_mifs_wmi_data *data)
{
struct bitland_mifs_input input = {
@@ -229,7 +296,15 @@ static int bitland_check_performance_capability(struct bitland_mifs_wmi_data *da
struct bitland_mifs_output output;
int ret;
- /* Full-speed/performance mode requires DC power (not USB-C) */
+ /* WUJIE15 PRO is powered through USB-C only. */
+ if (data->is_wujie15_hpt) {
+ ret = bitland_wujie15_is_supplied();
+ if (ret < 0)
+ return ret;
+ return ret ? 0 : -EOPNOTSUPP;
+ }
+
+ /* Other models require the firmware's circular-hole adapter type. */
if (!power_supply_is_system_supplied())
return -EOPNOTSUPP;
@@ -256,6 +331,27 @@ static int laptop_profile_set(struct device *dev,
int ret;
u8 val;
+ if (data->is_wujie15_hpt) {
+ switch (profile) {
+ case PLATFORM_PROFILE_LOW_POWER:
+ val = 2;
+ break;
+ case PLATFORM_PROFILE_BALANCED:
+ val = 1;
+ break;
+ case PLATFORM_PROFILE_PERFORMANCE:
+ ret = bitland_check_performance_capability(data);
+ if (ret)
+ return ret;
+ val = 0;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ input.payload[0] = val;
+ return bitland_mifs_wmi_call(data, &input, NULL);
+ }
+
switch (profile) {
case PLATFORM_PROFILE_LOW_POWER:
val = WMI_PP_QUIET;
@@ -286,9 +382,12 @@ static int laptop_profile_set(struct device *dev,
static int platform_profile_probe(void *drvdata, unsigned long *choices)
{
+ struct bitland_mifs_wmi_data *data = drvdata;
+
set_bit(PLATFORM_PROFILE_LOW_POWER, choices);
set_bit(PLATFORM_PROFILE_BALANCED, choices);
- set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, choices);
+ if (!data->is_wujie15_hpt)
+ set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, choices);
set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
return 0;
@@ -314,13 +413,25 @@ static int bitland_mifs_wmi_suspend(struct device *dev)
static int bitland_mifs_wmi_resume(struct device *dev)
{
struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev);
+ enum platform_profile_option profile = data->saved_profile;
+ int supplied;
/* Skip event device */
if (!data->pp_dev)
return 0;
- dev_dbg(dev, "Resuming, restoring profile %d\n", data->saved_profile);
- return laptop_profile_set(dev, data->saved_profile);
+ /* Match the firmware's performance-to-balanced transition on AC loss. */
+ if (data->is_wujie15_hpt &&
+ profile == PLATFORM_PROFILE_PERFORMANCE) {
+ supplied = bitland_wujie15_is_supplied();
+ if (supplied < 0)
+ return supplied;
+ if (!supplied)
+ profile = PLATFORM_PROFILE_BALANCED;
+ }
+
+ dev_dbg(dev, "Resuming, restoring profile %d\n", profile);
+ return laptop_profile_set(dev, profile);
}
static DEFINE_SIMPLE_DEV_PM_OPS(bitland_mifs_wmi_pm_ops,
@@ -386,7 +497,16 @@ static int laptop_hwmon_read_string(struct device *dev,
enum hwmon_sensor_types type, u32 attr,
int channel, const char **str)
{
+ struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev);
+ static const char *const hpt_fan_labels[] = { "Fan 2", "Fan 1" };
+
if (type == hwmon_fan && attr == hwmon_fan_label) {
+ if (data->is_wujie15_hpt) {
+ if (channel < 0 || channel >= ARRAY_SIZE(hpt_fan_labels))
+ return -EINVAL;
+ *str = hpt_fan_labels[channel];
+ return 0;
+ }
if (channel >= 0 && channel < ARRAY_SIZE(fan_labels)) {
*str = fan_labels[channel];
return 0;
@@ -414,6 +534,18 @@ static const struct hwmon_chip_info laptop_chip_info = {
.info = laptop_hwmon_info,
};
+/* HPT fills only the F2/F1 tachometers; the temperature GET is a no-op. */
+static const struct hwmon_channel_info *hpt_hwmon_info[] = {
+ HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_LABEL,
+ HWMON_F_INPUT | HWMON_F_LABEL),
+ NULL
+};
+
+static const struct hwmon_chip_info hpt_chip_info = {
+ .ops = &laptop_hwmon_ops,
+ .info = hpt_hwmon_info,
+};
+
static int laptop_kbd_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
@@ -606,7 +738,28 @@ static const struct attribute *const laptop_attrs[] = {
&dev_attr_fan_boost.attr,
NULL,
};
-ATTRIBUTE_GROUPS(laptop);
+
+static umode_t laptop_attr_is_visible(struct kobject *kobj,
+ const struct attribute *attr, int index)
+{
+ struct bitland_mifs_wmi_data *data = dev_get_drvdata(kobj_to_dev(kobj));
+
+ /* All three custom attributes require methods absent on HPT. */
+ if (data && data->is_wujie15_hpt)
+ return 0;
+
+ return attr->mode;
+}
+
+static const struct attribute_group laptop_group = {
+ .attrs_const = laptop_attrs,
+ .is_visible_const = laptop_attr_is_visible,
+};
+
+static const struct attribute_group *laptop_groups[] = {
+ &laptop_group,
+ NULL
+};
static const struct key_entry bitland_mifs_wmi_keymap[] = {
{ KE_KEY, WMI_EVENT_OPEN_APP, { KEY_PROG1 } },
@@ -661,6 +814,7 @@ static int bitland_notifier_callback(struct notifier_block *nb,
static int bitland_mifs_wmi_probe(struct wmi_device *wdev, const void *context)
{
struct bitland_mifs_wmi_data *drv_data;
+ const struct hwmon_chip_info *chip_info;
enum bitland_wmi_device_type dev_type =
(enum bitland_wmi_device_type)(unsigned long)context;
struct led_init_data init_data = {
@@ -675,6 +829,7 @@ static int bitland_mifs_wmi_probe(struct wmi_device *wdev, const void *context)
return -ENOMEM;
drv_data->wdev = wdev;
+ drv_data->is_wujie15_hpt = dmi_check_system(bitland_wujie15_hpt);
ret = devm_mutex_init(&wdev->dev, &drv_data->lock);
if (ret)
@@ -708,10 +863,11 @@ static int bitland_mifs_wmi_probe(struct wmi_device *wdev, const void *context)
return PTR_ERR(drv_data->pp_dev);
/* Register hwmon */
+ chip_info = drv_data->is_wujie15_hpt ? &hpt_chip_info : &laptop_chip_info;
drv_data->hwmon_dev = devm_hwmon_device_register_with_info(&wdev->dev,
"bitland_mifs",
drv_data,
- &laptop_chip_info,
+ chip_info,
NULL);
if (IS_ERR(drv_data->hwmon_dev))
return PTR_ERR(drv_data->hwmon_dev);
diff --git a/drivers/platform/x86/redmi-wmi.c b/drivers/platform/x86/redmi-wmi.c
index cc82ef502..de41642b7 100644
--- a/drivers/platform/x86/redmi-wmi.c
+++ b/drivers/platform/x86/redmi-wmi.c
@@ -4,6 +4,7 @@
#include <linux/acpi.h>
#include <linux/bits.h>
#include <linux/device.h>
+#include <linux/dmi.h>
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
#include <linux/module.h>
@@ -69,6 +70,12 @@ static int redmi_wmi_probe(struct wmi_device *wdev, const void *context)
struct redmi_wmi *data;
int err;
+ /* This shared GUID belongs to bitland-mifs-wmi on the WUJIE15 PRO HPT. */
+ if (dmi_match(DMI_SYS_VENDOR, "MECHREVO") &&
+ dmi_match(DMI_PRODUCT_NAME, "WUJIE15 PRO") &&
+ dmi_match(DMI_BOARD_NAME, "WUJIE15 Series-HPT"))
+ return -ENODEV;
+
/* Init dev */
data = devm_kzalloc(&wdev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
next reply other threads:[~2026-09-14 1:12 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-14 1:12 Bozhen Peng [this message]
2026-09-14 2:01 ` Randy Dunlap
2026-09-14 2:20 ` Bozhen Peng
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=0da43a8c-1755-49fe-b8e2-403489297f06@gmail.com \
--to=bozhenpeng93@gmail.com \
--cc=W_Armin@gmx.de \
--cc=corbet@lwn.net \
--cc=foxido@foxido.dev \
--cc=hansg@kernel.org \
--cc=ilpo.jarvinen@linux.intel.com \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=platform-driver-x86@vger.kernel.org \
--cc=qby140326@gmail.com \
--cc=rdunlap@infradead.org \
--cc=skhan@linuxfoundation.org \
/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®