* [PATCH v5 1/6] MAINTAINERS: Add maintainer entry of bitland-mifs-wmi driver
2026-08-16 10:08 [PATCH v5 0/6] Merge redmi-wmi into bitland-mifs-wmi Mingyou Chen
@ 2026-08-16 10:08 ` Mingyou Chen
2026-08-26 20:23 ` MAINTAINERS: Add maintainer entry for " Ilya Gladyshev
2026-08-16 10:08 ` [PATCH v5 2/6] platform/x86: bitland-mifs-wmi: Merge the function of redmi-wmi into the bitland driver Mingyou Chen
` (6 subsequent siblings)
7 siblings, 1 reply; 13+ messages in thread
From: Mingyou Chen @ 2026-08-16 10:08 UTC (permalink / raw)
To: qby140326
Cc: W_Armin, foxido, hansg, ilpo.jarvinen, linux-kernel, nika,
platform-driver-x86, vlku.milos.fun, i, wolf109909,
rahulbheda131313
Add a new maintainer entry for the bitland-mifs-wmi platform driver.
Signed-off-by: Mingyou Chen <qby140326@gmail.com>
---
MAINTAINERS | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index f37a81950e25..af86b1df1a94 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22774,6 +22774,13 @@ L: platform-driver-x86@vger.kernel.org
S: Maintained
F: drivers/platform/x86/redmi-wmi.c
+BITLAND LAPTOP DRIVER
+M: Mingyou Chen <qby140326@gmail.com>
+M: Gladyshev Ilya <foxido@foxido.dev>
+L: platform-driver-x86@vger.kernel.org
+S: Maintained
+F: drivers/platform/x86/bitland-mifs-wmi.c
+
REDPINE WIRELESS DRIVER
L: linux-wireless@vger.kernel.org
S: Orphan
--
2.55.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: MAINTAINERS: Add maintainer entry for bitland-mifs-wmi driver
2026-08-16 10:08 ` [PATCH v5 1/6] MAINTAINERS: Add maintainer entry of bitland-mifs-wmi driver Mingyou Chen
@ 2026-08-26 20:23 ` Ilya Gladyshev
0 siblings, 0 replies; 13+ messages in thread
From: Ilya Gladyshev @ 2026-08-26 20:23 UTC (permalink / raw)
To: qby140326
Cc: W_Armin, foxido, hansg, i, ilpo.jarvinen, linux-kernel, nika,
platform-driver-x86, rahulbheda131313, vlku.milos.fun, wolf109909,
Ilya Gladyshev
Sorry, I had trouble with my @foxido.dev email and missed your patch.
Could you please update my email address to my[1] linux.dev one?
I was considering submitting a separate patch, but including this change
here seems more convenient and less noisy.
Reviewed-by: Ilya Gladyshev <ilya.gladyshev@linux.dev>
[1]: https://foxido.dev/verify_email/
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v5 2/6] platform/x86: bitland-mifs-wmi: Merge the function of redmi-wmi into the bitland driver
2026-08-16 10:08 [PATCH v5 0/6] Merge redmi-wmi into bitland-mifs-wmi Mingyou Chen
2026-08-16 10:08 ` [PATCH v5 1/6] MAINTAINERS: Add maintainer entry of bitland-mifs-wmi driver Mingyou Chen
@ 2026-08-16 10:08 ` Mingyou Chen
2026-08-26 20:45 ` Ilya Gladyshev
2026-08-16 10:08 ` [PATCH v5 3/6] platform/x86: bitland-mifs-wmi: Add Redmi mic-mute key entries Mingyou Chen
` (5 subsequent siblings)
7 siblings, 1 reply; 13+ messages in thread
From: Mingyou Chen @ 2026-08-16 10:08 UTC (permalink / raw)
To: qby140326
Cc: W_Armin, foxido, hansg, ilpo.jarvinen, linux-kernel, nika,
platform-driver-x86, vlku.milos.fun, i, wolf109909,
rahulbheda131313
The bitland-mifs-wmi and legacy redmi-wmi drivers both attempt to bind
to the same WMI GUID (46C93E13-EE9B-4262-8488-563BCA757FEF). This
overlap causes a device registration conflict, preventing one of the
drivers from loading properly depending on the module initialization
order.
Merge the event handling logic from redmi-wmi into bitland-mifs-wmi. By
handling both device layouts within a single driver, we eliminate the
GUID ownership conflict.
Tested-by: Nika Krasnova <nika@nikableh.moe>
Signed-off-by: Mingyou Chen <qby140326@gmail.com>
---
drivers/platform/x86/bitland-mifs-wmi.c | 139 ++++++++++++++++--------
1 file changed, 96 insertions(+), 43 deletions(-)
diff --git a/drivers/platform/x86/bitland-mifs-wmi.c b/drivers/platform/x86/bitland-mifs-wmi.c
index b0d06a80e89e..8b6476881de6 100644
--- a/drivers/platform/x86/bitland-mifs-wmi.c
+++ b/drivers/platform/x86/bitland-mifs-wmi.c
@@ -34,6 +34,11 @@
#include <linux/units.h>
#include <linux/wmi.h>
+#define BI_HOTKEY_CODE(id, low, high) \
+ (((u32)(high) << 24) | ((u32)(low) << 16) | ((u32)(id) << 8) | WMI_EVENT_TYPE_HOTKEY)
+
+#define AI_KEY_VALUE_MASK BIT(8)
+
#define DRV_NAME "bitland-mifs-wmi"
#define BITLAND_MIFS_GUID "B60BFB48-3E5B-49E4-A0E9-8CFFE1B3434B"
#define BITLAND_EVENT_GUID "46C93E13-EE9B-4262-8488-563BCA757FEF"
@@ -601,18 +606,59 @@ static const struct attribute *const laptop_attrs[] = {
ATTRIBUTE_GROUPS(laptop);
static const struct key_entry bitland_mifs_wmi_keymap[] = {
- { KE_KEY, WMI_EVENT_OPEN_APP, { KEY_PROG1 } },
- { KE_KEY, WMI_EVENT_CALCULATOR_START, { KEY_CALC } },
- { KE_KEY, WMI_EVENT_BROWSER_START, { KEY_WWW } },
- { KE_IGNORE, WMI_EVENT_FN_J, { KEY_RESERVED } },
- { KE_IGNORE, WMI_EVENT_FN_F, { KEY_RESERVED } },
- { KE_IGNORE, WMI_EVENT_FN_0, { KEY_RESERVED } },
- { KE_IGNORE, WMI_EVENT_FN_1, { KEY_RESERVED } },
- { KE_IGNORE, WMI_EVENT_FN_2, { KEY_RESERVED } },
- { KE_IGNORE, WMI_EVENT_FN_3, { KEY_RESERVED } },
- { KE_IGNORE, WMI_EVENT_FN_4, { KEY_RESERVED } },
- { KE_IGNORE, WMI_EVENT_FN_5, { KEY_RESERVED } },
- { KE_END, 0 }
+ { KE_KEY, BI_HOTKEY_CODE(WMI_EVENT_OPEN_APP, 1, 0), { KEY_PROG1 } },
+
+ { KE_KEY, BI_HOTKEY_CODE(WMI_EVENT_CALCULATOR_START, 1, 0), { KEY_CALC } },
+
+ { KE_KEY, BI_HOTKEY_CODE(WMI_EVENT_BROWSER_START, 1, 0), { KEY_WWW } },
+
+ { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_FN_J, 0, 0), {} },
+ { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_FN_F, 0, 0), {} },
+ { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_FN_0, 0, 0), {} },
+ { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_FN_1, 0, 0), {} },
+ { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_FN_2, 0, 0), {} },
+ { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_FN_3, 0, 0), {} },
+ { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_FN_4, 0, 0), {} },
+ { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_FN_5, 0, 0), {} },
+
+ { KE_KEY, BI_HOTKEY_CODE(WMI_EVENT_RESERVED_2, 0, 0), { KEY_SELECTIVE_SCREENSHOT } },
+ { KE_KEY, BI_HOTKEY_CODE(WMI_EVENT_RESERVED_3, 0, 0), { KEY_ALL_APPLICATIONS } },
+ { KE_KEY, BI_HOTKEY_CODE(0x1b, 0, 0), { KEY_CONFIG } },
+ { KE_KEY, BI_HOTKEY_CODE(0x1b, 1, 0), { KEY_CONFIG } },
+ { KE_KEY, BI_HOTKEY_CODE(WMI_EVENT_RESERVED_1, 1, 0), { KEY_SWITCHVIDEOMODE } },
+ { KE_KEY, BI_HOTKEY_CODE(WMI_EVENT_REFRESH_RATE, 0, 0), { KEY_REFRESH_RATE_TOGGLE } },
+
+ /* AI button has code for each position */
+ { KE_KEY, BI_HOTKEY_CODE(WMI_EVENT_FN_5, 1, 0), { KEY_ASSISTANT } },
+
+ { KE_KEY, BI_HOTKEY_CODE(0x19, 1, 0), { KEY_ASSISTANT } },
+
+ { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_KBD_BRIGHTNESS, 0, 0), {} },
+ { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_KBD_BRIGHTNESS, 0, 0x80), {} },
+ { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_KBD_BRIGHTNESS, 5, 0), {} },
+ { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_KBD_BRIGHTNESS, 0x0a, 0), {} },
+
+ /* Xiaomi G Command Center */
+ { KE_KEY, BI_HOTKEY_CODE(0x0a, 1, 0), { KEY_VENDOR } },
+
+ /* OEM preset power mode */
+ { KE_IGNORE, BI_HOTKEY_CODE(0x16, 1, 0), {} },
+ { KE_IGNORE, BI_HOTKEY_CODE(0x16, 2, 0), {} },
+ { KE_IGNORE, BI_HOTKEY_CODE(0x16, 3, 0), {} },
+ { KE_IGNORE, BI_HOTKEY_CODE(0x16, 4, 0), {} },
+
+ /* Fn Lock state */
+ { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_FNLOCK_STATE, 0, 0), {} },
+ { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_FNLOCK_STATE, 1, 0), {} },
+
+ /* Fn+`/1/2/3/4 */
+ { KE_KEY, BI_HOTKEY_CODE(WMI_EVENT_FN_F, 1, 0), { KEY_F13 } },
+ { KE_KEY, BI_HOTKEY_CODE(WMI_EVENT_FN_0, 1, 0), { KEY_F14 } },
+ { KE_KEY, BI_HOTKEY_CODE(WMI_EVENT_FN_1, 1, 0), { KEY_F15 } },
+ { KE_KEY, BI_HOTKEY_CODE(WMI_EVENT_FN_2, 1, 0), { KEY_F16 } },
+ { KE_KEY, BI_HOTKEY_CODE(WMI_EVENT_FN_3, 1, 0), { KEY_F17 } },
+
+ { KE_END }
};
static void bitland_notifier_unregister(void *data)
@@ -730,12 +776,35 @@ static int bitland_mifs_wmi_probe(struct wmi_device *wdev, const void *context)
&drv_data->notifier);
}
+static void bitland_mifs_wmi_report_key(struct wmi_device *wdev, u32 payload)
+{
+ struct bitland_mifs_wmi_data *data = dev_get_drvdata(&wdev->dev);
+ struct key_entry *entry;
+ bool autorelease = true;
+ int value = 1;
+
+ entry = sparse_keymap_entry_from_scancode(data->input_dev, payload);
+ if (!entry) {
+ dev_dbg(&wdev->dev, "Unknown WMI hotkey with payload 0x%08x\n", payload);
+ return;
+ }
+
+ /* AI key quirk */
+ if (entry->keycode == KEY_ASSISTANT) {
+ value = !(payload & AI_KEY_VALUE_MASK);
+ autorelease = false;
+ }
+
+ guard(mutex)(&data->lock);
+ sparse_keymap_report_entry(data->input_dev, entry, value, autorelease);
+}
+
static void bitland_mifs_wmi_notify(struct wmi_device *wdev,
const struct wmi_buffer *buffer)
{
- struct bitland_mifs_wmi_data *data = dev_get_drvdata(&wdev->dev);
const struct bitland_mifs_event *event = buffer->data;
struct bitland_fan_notify_data fan_data;
+ u32 payload;
u8 brightness;
/* Validate event type */
@@ -752,24 +821,13 @@ static void bitland_mifs_wmi_notify(struct wmi_device *wdev,
blocking_notifier_call_chain(&bitland_notifier_list,
BITLAND_NOTIFY_KBD_BRIGHTNESS,
&brightness);
- break;
+ return;
case WMI_EVENT_PERFORMANCE_PLAN:
blocking_notifier_call_chain(&bitland_notifier_list,
BITLAND_NOTIFY_PLATFORM_PROFILE,
NULL);
- break;
-
- case WMI_EVENT_OPEN_APP:
- case WMI_EVENT_CALCULATOR_START:
- case WMI_EVENT_BROWSER_START: {
- guard(mutex)(&data->lock);
- if (!sparse_keymap_report_event(data->input_dev,
- event->event_id, 1, true))
- dev_warn(&wdev->dev, "Unknown key pressed: 0x%02x\n",
- event->event_id);
- break;
- }
+ return;
/*
* The device has 3 fans (CPU, GPU, SYS),
@@ -777,6 +835,14 @@ static void bitland_mifs_wmi_notify(struct wmi_device *wdev,
*/
case WMI_EVENT_CPU_FAN_SPEED:
case WMI_EVENT_GPU_FAN_SPEED:
+ /* Redmi refresh rate toggle quirk */
+ if (event->event_id == WMI_EVENT_CPU_FAN_SPEED &&
+ event->value_low == 0 && event->value_high == 0) {
+ payload = BI_HOTKEY_CODE(WMI_EVENT_REFRESH_RATE, 0, 0);
+ bitland_mifs_wmi_report_key(wdev, payload);
+ return;
+ }
+
if (event->event_id == WMI_EVENT_CPU_FAN_SPEED)
fan_data.channel = 0;
else
@@ -787,27 +853,14 @@ static void bitland_mifs_wmi_notify(struct wmi_device *wdev,
blocking_notifier_call_chain(&bitland_notifier_list,
BITLAND_NOTIFY_HWMON,
&fan_data);
- break;
-
- case WMI_EVENT_AIRPLANE_MODE:
- case WMI_EVENT_TOUCHPAD_STATE:
- case WMI_EVENT_FNLOCK_STATE:
- case WMI_EVENT_KBD_MODE:
- case WMI_EVENT_CAPSLOCK_STATE:
- case WMI_EVENT_NUMLOCK_STATE:
- case WMI_EVENT_SCROLLLOCK_STATE:
- case WMI_EVENT_REFRESH_RATE:
- case WMI_EVENT_WIN_KEY_LOCK:
- /* These events are informational or handled by firmware */
- dev_dbg(&wdev->dev, "State change event: id=%d value=%d\n",
- event->event_id, event->value_low);
- break;
+ return;
default:
- dev_dbg(&wdev->dev, "Unknown event: id=0x%02x value=0x%02x\n",
- event->event_id, event->value_low);
break;
}
+
+ payload = get_unaligned_le32(buffer->data);
+ bitland_mifs_wmi_report_key(wdev, payload);
}
static const struct wmi_device_id bitland_mifs_wmi_id_table[] = {
--
2.55.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH v5 2/6] platform/x86: bitland-mifs-wmi: Merge the function of redmi-wmi into the bitland driver
2026-08-16 10:08 ` [PATCH v5 2/6] platform/x86: bitland-mifs-wmi: Merge the function of redmi-wmi into the bitland driver Mingyou Chen
@ 2026-08-26 20:45 ` Ilya Gladyshev
0 siblings, 0 replies; 13+ messages in thread
From: Ilya Gladyshev @ 2026-08-26 20:45 UTC (permalink / raw)
To: Mingyou Chen
Cc: W_Armin, hansg, ilpo.jarvinen, linux-kernel, nika,
platform-driver-x86, vlku.milos.fun, i, wolf109909,
rahulbheda131313
Thank you for your patch. I have tested it on my Redmi Book Pro 15 2022,
and everything works as expected, so
Tested-by: Ilya Gladyshev <ilya.gladyshev@linux.dev>
On 8/16/26 13:08, Mingyou Chen wrote:
> The bitland-mifs-wmi and legacy redmi-wmi drivers both attempt to bind
> to the same WMI GUID (46C93E13-EE9B-4262-8488-563BCA757FEF). This
> overlap causes a device registration conflict, preventing one of the
> drivers from loading properly depending on the module initialization
> order.
>
> Merge the event handling logic from redmi-wmi into bitland-mifs-wmi. By
> handling both device layouts within a single driver, we eliminate the
> GUID ownership conflict.
>
> Tested-by: Nika Krasnova <nika@nikableh.moe>
> Signed-off-by: Mingyou Chen <qby140326@gmail.com>
> ---
> drivers/platform/x86/bitland-mifs-wmi.c | 139 ++++++++++++++++--------
> 1 file changed, 96 insertions(+), 43 deletions(-)
>
> diff --git a/drivers/platform/x86/bitland-mifs-wmi.c b/drivers/platform/x86/bitland-mifs-wmi.c
> index b0d06a80e89e..8b6476881de6 100644
> --- a/drivers/platform/x86/bitland-mifs-wmi.c
> +++ b/drivers/platform/x86/bitland-mifs-wmi.c
> @@ -34,6 +34,11 @@
> #include <linux/units.h>
> #include <linux/wmi.h>
>
> +#define BI_HOTKEY_CODE(id, low, high) \
> + (((u32)(high) << 24) | ((u32)(low) << 16) | ((u32)(id) << 8) | WMI_EVENT_TYPE_HOTKEY)
> +
> ...> +
> + /* AI button has code for each position */
> + { KE_KEY, BI_HOTKEY_CODE(WMI_EVENT_FN_5, 1, 0), { KEY_ASSISTANT } },
> +
> + { KE_KEY, BI_HOTKEY_CODE(0x19, 1, 0), { KEY_ASSISTANT } },
Why empty line between two KEY_ASSISTANT mappings?
> static void bitland_mifs_wmi_notify(struct wmi_device *wdev,
> const struct wmi_buffer *buffer)
> {
> - struct bitland_mifs_wmi_data *data = dev_get_drvdata(&wdev->dev);
> const struct bitland_mifs_event *event = buffer->data;
> struct bitland_fan_notify_data fan_data;
> + u32 payload;
> u8 brightness;
>
> /* Validate event type */
> @@ -752,24 +821,13 @@ static void bitland_mifs_wmi_notify(struct wmi_device *wdev,
> blocking_notifier_call_chain(&bitland_notifier_list,
> BITLAND_NOTIFY_KBD_BRIGHTNESS,
> &brightness);
> - break;
> + return;
>
> case WMI_EVENT_PERFORMANCE_PLAN:
> blocking_notifier_call_chain(&bitland_notifier_list,
> BITLAND_NOTIFY_PLATFORM_PROFILE,
> NULL);
> - break;
> -
> - case WMI_EVENT_OPEN_APP:
> - case WMI_EVENT_CALCULATOR_START:
> - case WMI_EVENT_BROWSER_START: {
> - guard(mutex)(&data->lock);
> - if (!sparse_keymap_report_event(data->input_dev,
> - event->event_id, 1, true))
> - dev_warn(&wdev->dev, "Unknown key pressed: 0x%02x\n",
> - event->event_id);
> - break;
> - }
> + return;
>
> /*
> * The device has 3 fans (CPU, GPU, SYS),
> @@ -777,6 +835,14 @@ static void bitland_mifs_wmi_notify(struct wmi_device *wdev,
> */
> case WMI_EVENT_CPU_FAN_SPEED:
> case WMI_EVENT_GPU_FAN_SPEED:
> + /* Redmi refresh rate toggle quirk */
> + if (event->event_id == WMI_EVENT_CPU_FAN_SPEED &&
> + event->value_low == 0 && event->value_high == 0) {
> + payload = BI_HOTKEY_CODE(WMI_EVENT_REFRESH_RATE, 0, 0);
> + bitland_mifs_wmi_report_key(wdev, payload);
> + return;
> + }
> +
> if (event->event_id == WMI_EVENT_CPU_FAN_SPEED)
> fan_data.channel = 0;
> else
> @@ -787,27 +853,14 @@ static void bitland_mifs_wmi_notify(struct wmi_device *wdev,
> blocking_notifier_call_chain(&bitland_notifier_list,
> BITLAND_NOTIFY_HWMON,
> &fan_data);
> - break;
> -
> - case WMI_EVENT_AIRPLANE_MODE:
> - case WMI_EVENT_TOUCHPAD_STATE:
> - case WMI_EVENT_FNLOCK_STATE:
> - case WMI_EVENT_KBD_MODE:
> - case WMI_EVENT_CAPSLOCK_STATE:
> - case WMI_EVENT_NUMLOCK_STATE:
> - case WMI_EVENT_SCROLLLOCK_STATE:
> - case WMI_EVENT_REFRESH_RATE:
> - case WMI_EVENT_WIN_KEY_LOCK:
> - /* These events are informational or handled by firmware */
> - dev_dbg(&wdev->dev, "State change event: id=%d value=%d\n",
> - event->event_id, event->value_low);
> - break;
> + return;
>
> default:
> - dev_dbg(&wdev->dev, "Unknown event: id=0x%02x value=0x%02x\n",
> - event->event_id, event->value_low);
> break;
> }
> +
> + payload = get_unaligned_le32(buffer->data);
> + bitland_mifs_wmi_report_key(wdev, payload);
> }
[nitpick]
Would it make sense to place this inside the `default` case? That might
make the control flow simpler (no break->return changes, everything
still happens in the switch).
> static const struct wmi_device_id bitland_mifs_wmi_id_table[] = {
---
Ilya Gladyshev // foxido.dev
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v5 3/6] platform/x86: bitland-mifs-wmi: Add Redmi mic-mute key entries
2026-08-16 10:08 [PATCH v5 0/6] Merge redmi-wmi into bitland-mifs-wmi Mingyou Chen
2026-08-16 10:08 ` [PATCH v5 1/6] MAINTAINERS: Add maintainer entry of bitland-mifs-wmi driver Mingyou Chen
2026-08-16 10:08 ` [PATCH v5 2/6] platform/x86: bitland-mifs-wmi: Merge the function of redmi-wmi into the bitland driver Mingyou Chen
@ 2026-08-16 10:08 ` Mingyou Chen
2026-08-16 10:08 ` [PATCH v5 4/6] platform/x86: redmi-wmi: Drop redmi-wmi driver Mingyou Chen
` (4 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Mingyou Chen @ 2026-08-16 10:08 UTC (permalink / raw)
To: qby140326
Cc: W_Armin, foxido, hansg, ilpo.jarvinen, linux-kernel, nika,
platform-driver-x86, vlku.milos.fun, i, wolf109909,
rahulbheda131313
Add the missing Redmibook mic-mute hotkey definitions to the shared
sparse keymap table:
- 0x00002101 (Fn-lock disabled state)
- 0x00012101 (Fn-lock enabled state)
Reported-by: Nika Krasnova <nika@nikableh.moe>
Link:
https://lore.kernel.org/platform-driver-x86/20260531123345.237521-1-nika@nikableh.moe/
Signed-off-by: Mingyou Chen <qby140326@gmail.com>
---
drivers/platform/x86/bitland-mifs-wmi.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/platform/x86/bitland-mifs-wmi.c b/drivers/platform/x86/bitland-mifs-wmi.c
index 8b6476881de6..81dad2b65f4f 100644
--- a/drivers/platform/x86/bitland-mifs-wmi.c
+++ b/drivers/platform/x86/bitland-mifs-wmi.c
@@ -628,6 +628,10 @@ static const struct key_entry bitland_mifs_wmi_keymap[] = {
{ KE_KEY, BI_HOTKEY_CODE(WMI_EVENT_RESERVED_1, 1, 0), { KEY_SWITCHVIDEOMODE } },
{ KE_KEY, BI_HOTKEY_CODE(WMI_EVENT_REFRESH_RATE, 0, 0), { KEY_REFRESH_RATE_TOGGLE } },
+ /* Mic-mute key */
+ { KE_KEY, BI_HOTKEY_CODE(0x21, 0, 0), { KEY_MICMUTE } },
+ { KE_KEY, BI_HOTKEY_CODE(0x21, 1, 0), { KEY_MICMUTE } },
+
/* AI button has code for each position */
{ KE_KEY, BI_HOTKEY_CODE(WMI_EVENT_FN_5, 1, 0), { KEY_ASSISTANT } },
--
2.55.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v5 4/6] platform/x86: redmi-wmi: Drop redmi-wmi driver
2026-08-16 10:08 [PATCH v5 0/6] Merge redmi-wmi into bitland-mifs-wmi Mingyou Chen
` (2 preceding siblings ...)
2026-08-16 10:08 ` [PATCH v5 3/6] platform/x86: bitland-mifs-wmi: Add Redmi mic-mute key entries Mingyou Chen
@ 2026-08-16 10:08 ` Mingyou Chen
2026-08-26 20:50 ` Ilya Gladyshev
2026-08-16 10:08 ` [PATCH v5 5/6] platform/x86: bitland-mifs-wmi: Add per-machine ops table Mingyou Chen
` (3 subsequent siblings)
7 siblings, 1 reply; 13+ messages in thread
From: Mingyou Chen @ 2026-08-16 10:08 UTC (permalink / raw)
To: qby140326
Cc: W_Armin, foxido, hansg, ilpo.jarvinen, linux-kernel, nika,
platform-driver-x86, vlku.milos.fun, i, wolf109909,
rahulbheda131313
The redmi-wmi driver is no longer needed because its functionality
has been superseded by the new generic bitland platform driver.
All supported Redmi laptop models are now handled properly via the
updated WMI interfaces in bitland-mifs-wmi.
Remove the obsolete code, Kconfig entries, and Makefile references
to clean up the platform directory.
Signed-off-by: Mingyou Chen <qby140326@gmail.com>
---
MAINTAINERS | 6 --
drivers/platform/x86/Kconfig | 12 ---
drivers/platform/x86/Makefile | 1 -
drivers/platform/x86/redmi-wmi.c | 154 -------------------------------
4 files changed, 173 deletions(-)
delete mode 100644 drivers/platform/x86/redmi-wmi.c
diff --git a/MAINTAINERS b/MAINTAINERS
index af86b1df1a94..b92bc4816fa6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22768,12 +22768,6 @@ S: Maintained
T: git https://github.com/pkshih/rtw.git
F: drivers/net/wireless/realtek/rtw89/
-REDMIBOOK WMI DRIVERS
-M: Gladyshev Ilya <foxido@foxido.dev>
-L: platform-driver-x86@vger.kernel.org
-S: Maintained
-F: drivers/platform/x86/redmi-wmi.c
-
BITLAND LAPTOP DRIVER
M: Mingyou Chen <qby140326@gmail.com>
M: Gladyshev Ilya <foxido@foxido.dev>
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index b54b5212b204..036fc76f62df 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -90,18 +90,6 @@ config XIAOMI_WMI
To compile this driver as a module, choose M here: the module will
be called xiaomi-wmi.
-config REDMI_WMI
- tristate "Redmibook WMI key driver"
- depends on ACPI_WMI
- depends on INPUT
- select INPUT_SPARSEKMAP
- help
- Say Y here if you want support for WMI-based hotkey events on
- Xiaomi Redmibook devices.
-
- To compile this driver as a module, choose M here: the module will
- be called redmi-wmi.
-
config GIGABYTE_WMI
tristate "Gigabyte WMI temperature driver"
depends on ACPI_WMI
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 872ac3842391..0da4876f766e 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -12,7 +12,6 @@ obj-$(CONFIG_HUAWEI_WMI) += huawei-wmi.o
obj-$(CONFIG_MXM_WMI) += mxm-wmi.o
obj-$(CONFIG_NVIDIA_WMI_EC_BACKLIGHT) += nvidia-wmi-ec-backlight.o
obj-$(CONFIG_XIAOMI_WMI) += xiaomi-wmi.o
-obj-$(CONFIG_REDMI_WMI) += redmi-wmi.o
obj-$(CONFIG_GIGABYTE_WMI) += gigabyte-wmi.o
obj-$(CONFIG_BITLAND_MIFS_WMI) += bitland-mifs-wmi.o
diff --git a/drivers/platform/x86/redmi-wmi.c b/drivers/platform/x86/redmi-wmi.c
deleted file mode 100644
index 58898630eda6..000000000000
--- a/drivers/platform/x86/redmi-wmi.c
+++ /dev/null
@@ -1,154 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/* WMI driver for Xiaomi Redmibooks */
-
-#include <linux/acpi.h>
-#include <linux/bits.h>
-#include <linux/device.h>
-#include <linux/input.h>
-#include <linux/input/sparse-keymap.h>
-#include <linux/module.h>
-#include <linux/mutex.h>
-#include <linux/unaligned.h>
-#include <linux/wmi.h>
-
-#include <uapi/linux/input-event-codes.h>
-
-#define WMI_REDMIBOOK_KEYBOARD_EVENT_GUID "46C93E13-EE9B-4262-8488-563BCA757FEF"
-
-#define AI_KEY_VALUE_MASK BIT(8)
-
-static const struct key_entry redmi_wmi_keymap[] = {
- {KE_KEY, 0x00000201, {KEY_SELECTIVE_SCREENSHOT}},
- {KE_KEY, 0x00000301, {KEY_ALL_APPLICATIONS}},
- {KE_KEY, 0x00001b01, {KEY_CONFIG}},
- {KE_KEY, 0x00011b01, {KEY_CONFIG}},
- {KE_KEY, 0x00010101, {KEY_SWITCHVIDEOMODE}},
- {KE_KEY, 0x00001a01, {KEY_REFRESH_RATE_TOGGLE}},
-
- /* AI button has code for each position */
- {KE_KEY, 0x00011801, {KEY_ASSISTANT}},
- {KE_KEY, 0x00011901, {KEY_ASSISTANT}},
-
- /* Keyboard backlight */
- {KE_IGNORE, 0x00000501, {}},
- {KE_IGNORE, 0x00800501, {}},
- {KE_IGNORE, 0x00050501, {}},
- {KE_IGNORE, 0x000a0501, {}},
-
- /* Xiaomi G Command Center */
- {KE_KEY, 0x00010a01, {KEY_VENDOR}},
-
- /* OEM preset power mode */
- {KE_IGNORE, 0x00011601, {}},
- {KE_IGNORE, 0x00021601, {}},
- {KE_IGNORE, 0x00031601, {}},
- {KE_IGNORE, 0x00041601, {}},
-
- /* Fn Lock state */
- {KE_IGNORE, 0x00000701, {}},
- {KE_IGNORE, 0x00010701, {}},
-
- /* Fn+`/1/2/3/4 */
- {KE_KEY, 0x00011101, {KEY_F13}},
- {KE_KEY, 0x00011201, {KEY_F14}},
- {KE_KEY, 0x00011301, {KEY_F15}},
- {KE_KEY, 0x00011401, {KEY_F16}},
- {KE_KEY, 0x00011501, {KEY_F17}},
-
- {KE_END}
-};
-
-struct redmi_wmi {
- struct input_dev *input_dev;
- /* Protects the key event sequence */
- struct mutex key_lock;
-};
-
-static int redmi_wmi_probe(struct wmi_device *wdev, const void *context)
-{
- struct redmi_wmi *data;
- int err;
-
- /* Init dev */
- data = devm_kzalloc(&wdev->dev, sizeof(*data), GFP_KERNEL);
- if (!data)
- return -ENOMEM;
-
- dev_set_drvdata(&wdev->dev, data);
-
- err = devm_mutex_init(&wdev->dev, &data->key_lock);
- if (err)
- return err;
-
- data->input_dev = devm_input_allocate_device(&wdev->dev);
- if (!data->input_dev)
- return -ENOMEM;
-
- data->input_dev->name = "Redmibook WMI keys";
- data->input_dev->phys = "wmi/input0";
-
- err = sparse_keymap_setup(data->input_dev, redmi_wmi_keymap, NULL);
- if (err)
- return err;
-
- return input_register_device(data->input_dev);
-}
-
-static void redmi_wmi_notify(struct wmi_device *wdev, union acpi_object *obj)
-{
- struct key_entry *entry;
- struct redmi_wmi *data = dev_get_drvdata(&wdev->dev);
- bool autorelease = true;
- u32 payload;
- int value = 1;
-
- if (obj->type != ACPI_TYPE_BUFFER) {
- dev_err(&wdev->dev, "Bad response type %u\n", obj->type);
- return;
- }
-
- if (obj->buffer.length < 32) {
- dev_err(&wdev->dev, "Invalid buffer length %u\n", obj->buffer.length);
- return;
- }
-
- payload = get_unaligned_le32(obj->buffer.pointer);
- entry = sparse_keymap_entry_from_scancode(data->input_dev, payload);
-
- if (!entry) {
- dev_dbg(&wdev->dev, "Unknown WMI event with payload %u", payload);
- return;
- }
-
- /* AI key quirk */
- if (entry->keycode == KEY_ASSISTANT) {
- value = !(payload & AI_KEY_VALUE_MASK);
- autorelease = false;
- }
-
- guard(mutex)(&data->key_lock);
- sparse_keymap_report_entry(data->input_dev, entry, value, autorelease);
-}
-
-static const struct wmi_device_id redmi_wmi_id_table[] = {
- { WMI_REDMIBOOK_KEYBOARD_EVENT_GUID, NULL },
- { }
-};
-
-static struct wmi_driver redmi_wmi_driver = {
- .driver = {
- .name = "redmi-wmi",
- .probe_type = PROBE_PREFER_ASYNCHRONOUS,
- },
- .id_table = redmi_wmi_id_table,
- .min_event_size = 32,
- .probe = redmi_wmi_probe,
- .notify = redmi_wmi_notify,
- .no_singleton = true,
-};
-module_wmi_driver(redmi_wmi_driver);
-
-MODULE_DEVICE_TABLE(wmi, redmi_wmi_id_table);
-MODULE_AUTHOR("Gladyshev Ilya <foxido@foxido.dev>");
-MODULE_DESCRIPTION("Redmibook WMI driver");
-MODULE_LICENSE("GPL");
--
2.55.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH v5 4/6] platform/x86: redmi-wmi: Drop redmi-wmi driver
2026-08-16 10:08 ` [PATCH v5 4/6] platform/x86: redmi-wmi: Drop redmi-wmi driver Mingyou Chen
@ 2026-08-26 20:50 ` Ilya Gladyshev
0 siblings, 0 replies; 13+ messages in thread
From: Ilya Gladyshev @ 2026-08-26 20:50 UTC (permalink / raw)
To: Mingyou Chen
Cc: W_Armin, hansg, ilpo.jarvinen, linux-kernel, nika,
platform-driver-x86, vlku.milos.fun, i, wolf109909,
rahulbheda131313
Reviewed-by: Ilya Gladyshev <ilya.gladyshev@linux.dev>
On 8/16/26 13:08, Mingyou Chen wrote:
> The redmi-wmi driver is no longer needed because its functionality
> has been superseded by the new generic bitland platform driver.
> All supported Redmi laptop models are now handled properly via the
> updated WMI interfaces in bitland-mifs-wmi.
>
> Remove the obsolete code, Kconfig entries, and Makefile references
> to clean up the platform directory.
>
> Signed-off-by: Mingyou Chen <qby140326@gmail.com>
> ---
> MAINTAINERS | 6 --
> drivers/platform/x86/Kconfig | 12 ---
> drivers/platform/x86/Makefile | 1 -
> drivers/platform/x86/redmi-wmi.c | 154 -------------------------------
> 4 files changed, 173 deletions(-)
> delete mode 100644 drivers/platform/x86/redmi-wmi.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index af86b1df1a94..b92bc4816fa6 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -22768,12 +22768,6 @@ S: Maintained
> T: git https://github.com/pkshih/rtw.git
> F: drivers/net/wireless/realtek/rtw89/
>
> -REDMIBOOK WMI DRIVERS
> -M: Gladyshev Ilya <foxido@foxido.dev>
> -L: platform-driver-x86@vger.kernel.org
> -S: Maintained
> -F: drivers/platform/x86/redmi-wmi.c
> -
> BITLAND LAPTOP DRIVER
> M: Mingyou Chen <qby140326@gmail.com>
> M: Gladyshev Ilya <foxido@foxido.dev>
> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
> index b54b5212b204..036fc76f62df 100644
> --- a/drivers/platform/x86/Kconfig
> +++ b/drivers/platform/x86/Kconfig
> @@ -90,18 +90,6 @@ config XIAOMI_WMI
> To compile this driver as a module, choose M here: the module will
> be called xiaomi-wmi.
>
> -config REDMI_WMI
> - tristate "Redmibook WMI key driver"
> - depends on ACPI_WMI
> - depends on INPUT
> - select INPUT_SPARSEKMAP
> - help
> - Say Y here if you want support for WMI-based hotkey events on
> - Xiaomi Redmibook devices.
> -
> - To compile this driver as a module, choose M here: the module will
> - be called redmi-wmi.
> -
> config GIGABYTE_WMI
> tristate "Gigabyte WMI temperature driver"
> depends on ACPI_WMI
> diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
> index 872ac3842391..0da4876f766e 100644
> --- a/drivers/platform/x86/Makefile
> +++ b/drivers/platform/x86/Makefile
> @@ -12,7 +12,6 @@ obj-$(CONFIG_HUAWEI_WMI) += huawei-wmi.o
> obj-$(CONFIG_MXM_WMI) += mxm-wmi.o
> obj-$(CONFIG_NVIDIA_WMI_EC_BACKLIGHT) += nvidia-wmi-ec-backlight.o
> obj-$(CONFIG_XIAOMI_WMI) += xiaomi-wmi.o
> -obj-$(CONFIG_REDMI_WMI) += redmi-wmi.o
> obj-$(CONFIG_GIGABYTE_WMI) += gigabyte-wmi.o
> obj-$(CONFIG_BITLAND_MIFS_WMI) += bitland-mifs-wmi.o
>
> diff --git a/drivers/platform/x86/redmi-wmi.c b/drivers/platform/x86/redmi-wmi.c
> deleted file mode 100644
> index 58898630eda6..000000000000
> --- a/drivers/platform/x86/redmi-wmi.c
> +++ /dev/null
> @@ -1,154 +0,0 @@
> -// SPDX-License-Identifier: GPL-2.0
> -/* WMI driver for Xiaomi Redmibooks */
> -
> -#include <linux/acpi.h>
> -#include <linux/bits.h>
> -#include <linux/device.h>
> -#include <linux/input.h>
> -#include <linux/input/sparse-keymap.h>
> -#include <linux/module.h>
> -#include <linux/mutex.h>
> -#include <linux/unaligned.h>
> -#include <linux/wmi.h>
> -
> -#include <uapi/linux/input-event-codes.h>
> -
> -#define WMI_REDMIBOOK_KEYBOARD_EVENT_GUID "46C93E13-EE9B-4262-8488-563BCA757FEF"
> -
> -#define AI_KEY_VALUE_MASK BIT(8)
> -
> -static const struct key_entry redmi_wmi_keymap[] = {
> - {KE_KEY, 0x00000201, {KEY_SELECTIVE_SCREENSHOT}},
> - {KE_KEY, 0x00000301, {KEY_ALL_APPLICATIONS}},
> - {KE_KEY, 0x00001b01, {KEY_CONFIG}},
> - {KE_KEY, 0x00011b01, {KEY_CONFIG}},
> - {KE_KEY, 0x00010101, {KEY_SWITCHVIDEOMODE}},
> - {KE_KEY, 0x00001a01, {KEY_REFRESH_RATE_TOGGLE}},
> -
> - /* AI button has code for each position */
> - {KE_KEY, 0x00011801, {KEY_ASSISTANT}},
> - {KE_KEY, 0x00011901, {KEY_ASSISTANT}},
> -
> - /* Keyboard backlight */
> - {KE_IGNORE, 0x00000501, {}},
> - {KE_IGNORE, 0x00800501, {}},
> - {KE_IGNORE, 0x00050501, {}},
> - {KE_IGNORE, 0x000a0501, {}},
> -
> - /* Xiaomi G Command Center */
> - {KE_KEY, 0x00010a01, {KEY_VENDOR}},
> -
> - /* OEM preset power mode */
> - {KE_IGNORE, 0x00011601, {}},
> - {KE_IGNORE, 0x00021601, {}},
> - {KE_IGNORE, 0x00031601, {}},
> - {KE_IGNORE, 0x00041601, {}},
> -
> - /* Fn Lock state */
> - {KE_IGNORE, 0x00000701, {}},
> - {KE_IGNORE, 0x00010701, {}},
> -
> - /* Fn+`/1/2/3/4 */
> - {KE_KEY, 0x00011101, {KEY_F13}},
> - {KE_KEY, 0x00011201, {KEY_F14}},
> - {KE_KEY, 0x00011301, {KEY_F15}},
> - {KE_KEY, 0x00011401, {KEY_F16}},
> - {KE_KEY, 0x00011501, {KEY_F17}},
> -
> - {KE_END}
> -};
> -
> -struct redmi_wmi {
> - struct input_dev *input_dev;
> - /* Protects the key event sequence */
> - struct mutex key_lock;
> -};
> -
> -static int redmi_wmi_probe(struct wmi_device *wdev, const void *context)
> -{
> - struct redmi_wmi *data;
> - int err;
> -
> - /* Init dev */
> - data = devm_kzalloc(&wdev->dev, sizeof(*data), GFP_KERNEL);
> - if (!data)
> - return -ENOMEM;
> -
> - dev_set_drvdata(&wdev->dev, data);
> -
> - err = devm_mutex_init(&wdev->dev, &data->key_lock);
> - if (err)
> - return err;
> -
> - data->input_dev = devm_input_allocate_device(&wdev->dev);
> - if (!data->input_dev)
> - return -ENOMEM;
> -
> - data->input_dev->name = "Redmibook WMI keys";
> - data->input_dev->phys = "wmi/input0";
> -
> - err = sparse_keymap_setup(data->input_dev, redmi_wmi_keymap, NULL);
> - if (err)
> - return err;
> -
> - return input_register_device(data->input_dev);
> -}
> -
> -static void redmi_wmi_notify(struct wmi_device *wdev, union acpi_object *obj)
> -{
> - struct key_entry *entry;
> - struct redmi_wmi *data = dev_get_drvdata(&wdev->dev);
> - bool autorelease = true;
> - u32 payload;
> - int value = 1;
> -
> - if (obj->type != ACPI_TYPE_BUFFER) {
> - dev_err(&wdev->dev, "Bad response type %u\n", obj->type);
> - return;
> - }
> -
> - if (obj->buffer.length < 32) {
> - dev_err(&wdev->dev, "Invalid buffer length %u\n", obj->buffer.length);
> - return;
> - }
> -
> - payload = get_unaligned_le32(obj->buffer.pointer);
> - entry = sparse_keymap_entry_from_scancode(data->input_dev, payload);
> -
> - if (!entry) {
> - dev_dbg(&wdev->dev, "Unknown WMI event with payload %u", payload);
> - return;
> - }
> -
> - /* AI key quirk */
> - if (entry->keycode == KEY_ASSISTANT) {
> - value = !(payload & AI_KEY_VALUE_MASK);
> - autorelease = false;
> - }
> -
> - guard(mutex)(&data->key_lock);
> - sparse_keymap_report_entry(data->input_dev, entry, value, autorelease);
> -}
> -
> -static const struct wmi_device_id redmi_wmi_id_table[] = {
> - { WMI_REDMIBOOK_KEYBOARD_EVENT_GUID, NULL },
> - { }
> -};
> -
> -static struct wmi_driver redmi_wmi_driver = {
> - .driver = {
> - .name = "redmi-wmi",
> - .probe_type = PROBE_PREFER_ASYNCHRONOUS,
> - },
> - .id_table = redmi_wmi_id_table,
> - .min_event_size = 32,
> - .probe = redmi_wmi_probe,
> - .notify = redmi_wmi_notify,
> - .no_singleton = true,
> -};
> -module_wmi_driver(redmi_wmi_driver);
> -
> -MODULE_DEVICE_TABLE(wmi, redmi_wmi_id_table);
> -MODULE_AUTHOR("Gladyshev Ilya <foxido@foxido.dev>");
> -MODULE_DESCRIPTION("Redmibook WMI driver");
> -MODULE_LICENSE("GPL");
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v5 5/6] platform/x86: bitland-mifs-wmi: Add per-machine ops table
2026-08-16 10:08 [PATCH v5 0/6] Merge redmi-wmi into bitland-mifs-wmi Mingyou Chen
` (3 preceding siblings ...)
2026-08-16 10:08 ` [PATCH v5 4/6] platform/x86: redmi-wmi: Drop redmi-wmi driver Mingyou Chen
@ 2026-08-16 10:08 ` Mingyou Chen
2026-08-16 10:08 ` [PATCH v5 6/6] platform/x86: bitland-mifs-wmi: Add Redmi laptop support Mingyou Chen
` (2 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Mingyou Chen @ 2026-08-16 10:08 UTC (permalink / raw)
To: qby140326
Cc: W_Armin, foxido, hansg, ilpo.jarvinen, linux-kernel, nika,
platform-driver-x86, vlku.milos.fun, i, wolf109909,
rahulbheda131313
The MIFS WMI interface is shared by several Bitland ODM laptops which
differ in the layout of the performance mode values of
WMI_FN_SYSTEM_PER_MODE and in the capability checks.
Introduce a machine ops table to support multiple machine families
without sprinkling model checks over the driver:
struct bitland_mifs_ops {
const char *name;
const struct bitland_profile_mode_map *mode_map;
int (*check_performance_capability)(
struct bitland_mifs_wmi_data *data);
bool has_full_speed;
};
The default ops instance keeps the current Bitland behavior, so this
change is a pure refactor without any functional changes.
Signed-off-by: Mingyou Chen <qby140326@gmail.com>
---
drivers/platform/x86/bitland-mifs-wmi.c | 89 +++++++++++++++++++------
1 file changed, 67 insertions(+), 22 deletions(-)
diff --git a/drivers/platform/x86/bitland-mifs-wmi.c b/drivers/platform/x86/bitland-mifs-wmi.c
index 81dad2b65f4f..17792d8b7c29 100644
--- a/drivers/platform/x86/bitland-mifs-wmi.c
+++ b/drivers/platform/x86/bitland-mifs-wmi.c
@@ -78,6 +78,46 @@ enum bitland_mifs_power_profile {
WMI_PP_FULL_SPEED = 3,
};
+struct bitland_mifs_wmi_data;
+
+/*
+ * Value layout of WMI_FN_SYSTEM_PER_MODE.
+ */
+struct bitland_profile_mode_map {
+ u8 balanced;
+ u8 performance;
+ u8 quiet;
+ u8 full_speed;
+};
+
+static const struct bitland_profile_mode_map bitland_mode_map = {
+ .balanced = WMI_PP_BALANCED,
+ .performance = WMI_PP_PERFORMANCE,
+ .quiet = WMI_PP_QUIET,
+ .full_speed = WMI_PP_FULL_SPEED,
+};
+
+static int bitland_pp_check_capability(struct bitland_mifs_wmi_data *data);
+
+/*
+ * Machine-specific operations. The MIFS WMI interface is shared by
+ * several Bitland ODM laptops which differ in the layout of the
+ * performance mode values and in the capability checks.
+ */
+struct bitland_mifs_ops {
+ const char *name;
+ const struct bitland_profile_mode_map *mode_map;
+ int (*check_performance_capability)(struct bitland_mifs_wmi_data *data);
+ bool has_full_speed;
+};
+
+static const struct bitland_mifs_ops bitland_ops = {
+ .name = "Bitland",
+ .mode_map = &bitland_mode_map,
+ .check_performance_capability = bitland_pp_check_capability,
+ .has_full_speed = true,
+};
+
enum bitland_mifs_event_id {
WMI_EVENT_RESERVED_1 = 1,
WMI_EVENT_RESERVED_2 = 2,
@@ -163,6 +203,7 @@ struct bitland_mifs_wmi_data {
struct input_dev *input_dev;
struct device *hwmon_dev;
struct device *pp_dev;
+ const struct bitland_mifs_ops *ops;
enum platform_profile_option saved_profile;
};
@@ -193,6 +234,7 @@ static int laptop_profile_get(struct device *dev,
enum platform_profile_option *profile)
{
struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev);
+ const struct bitland_profile_mode_map *map = data->ops->mode_map;
struct bitland_mifs_input input = {
.reserved1 = 0,
.operation = WMI_METHOD_GET,
@@ -200,32 +242,29 @@ static int laptop_profile_get(struct device *dev,
.function = WMI_FN_SYSTEM_PER_MODE,
};
struct bitland_mifs_output result;
+ u8 mode;
int ret;
ret = bitland_mifs_wmi_call(data, &input, &result);
if (ret)
return ret;
- switch (result.data[0]) {
- case WMI_PP_BALANCED:
+ mode = result.data[0];
+ if (mode == map->quiet)
+ *profile = PLATFORM_PROFILE_LOW_POWER;
+ else if (mode == map->balanced)
*profile = PLATFORM_PROFILE_BALANCED;
- break;
- case WMI_PP_PERFORMANCE:
+ else if (mode == map->performance)
*profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE;
- break;
- case WMI_PP_QUIET:
- *profile = PLATFORM_PROFILE_LOW_POWER;
- break;
- case WMI_PP_FULL_SPEED:
+ else if (mode == map->full_speed)
*profile = PLATFORM_PROFILE_PERFORMANCE;
- break;
- default:
+ else
return -EINVAL;
- }
+
return 0;
}
-static int bitland_check_performance_capability(struct bitland_mifs_wmi_data *data)
+static int bitland_pp_check_capability(struct bitland_mifs_wmi_data *data)
{
struct bitland_mifs_input input = {
.operation = WMI_METHOD_GET,
@@ -252,6 +291,7 @@ static int laptop_profile_set(struct device *dev,
enum platform_profile_option profile)
{
struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev);
+ const struct bitland_profile_mode_map *map = data->ops->mode_map;
struct bitland_mifs_input input = {
.reserved1 = 0,
.operation = WMI_METHOD_SET,
@@ -259,42 +299,46 @@ static int laptop_profile_set(struct device *dev,
.function = WMI_FN_SYSTEM_PER_MODE,
};
int ret;
- u8 val;
+ u8 mode;
switch (profile) {
case PLATFORM_PROFILE_LOW_POWER:
- val = WMI_PP_QUIET;
+ mode = map->quiet;
break;
case PLATFORM_PROFILE_BALANCED:
- val = WMI_PP_BALANCED;
+ mode = map->balanced;
break;
case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
- ret = bitland_check_performance_capability(data);
+ ret = data->ops->check_performance_capability(data);
if (ret)
return ret;
- val = WMI_PP_PERFORMANCE;
+ mode = map->performance;
break;
case PLATFORM_PROFILE_PERFORMANCE:
- ret = bitland_check_performance_capability(data);
+ ret = data->ops->check_performance_capability(data);
if (ret)
return ret;
- val = WMI_PP_FULL_SPEED;
+ mode = map->full_speed;
break;
default:
return -EOPNOTSUPP;
}
- input.payload[0] = val;
+ input.payload[0] = mode;
return bitland_mifs_wmi_call(data, &input, NULL);
}
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);
- set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
+
+ if (data->ops->has_full_speed)
+ set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
return 0;
}
@@ -717,6 +761,7 @@ static int bitland_mifs_wmi_probe(struct wmi_device *wdev, const void *context)
return -ENOMEM;
drv_data->wdev = wdev;
+ drv_data->ops = &bitland_ops;
ret = devm_mutex_init(&wdev->dev, &drv_data->lock);
if (ret)
--
2.55.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v5 6/6] platform/x86: bitland-mifs-wmi: Add Redmi laptop support
2026-08-16 10:08 [PATCH v5 0/6] Merge redmi-wmi into bitland-mifs-wmi Mingyou Chen
` (4 preceding siblings ...)
2026-08-16 10:08 ` [PATCH v5 5/6] platform/x86: bitland-mifs-wmi: Add per-machine ops table Mingyou Chen
@ 2026-08-16 10:08 ` Mingyou Chen
2026-08-17 23:17 ` Miloš Vlku
2026-08-26 13:26 ` [PATCH] platform/x86: bitland-mifs-wmi: add TM2424 ops and hotkeys KentoNion
2026-08-26 13:29 ` KentoNion
7 siblings, 1 reply; 13+ messages in thread
From: Mingyou Chen @ 2026-08-16 10:08 UTC (permalink / raw)
To: qby140326
Cc: W_Armin, foxido, hansg, ilpo.jarvinen, linux-kernel, nika,
platform-driver-x86, vlku.milos.fun, i, wolf109909,
rahulbheda131313
Redmi laptops (Redmi Book and Redmi G series) use the same MIFS WMI
interface but encode the performance mode values of
WMI_FN_SYSTEM_PER_MODE differently, as reverse engineered from the
Windows GCommandCenter application:
0 = performance, 1 = balanced, 2 = quiet, 4 = full speed
Add "Redmi Book" and "Redmi G" ops entries selected via DMI:
- Redmi G (board TM2135/TM2137): the full-speed mode is available
- Redmi Book/Xiaomi Book: no full-speed mode
- The performance modes only require AC power (any source); the
barrel-jack requirement is Bitland-specific
The GCommandCenter application further showed that:
- the CPU power sensor (WMI_FN_CPU_POWER) is only available on Redmi
machines, gate the hwmon power channel on the ops table
- the refresh-rate-toggle quirk in the CPU fan speed event is
Redmi-specific, gate it on the ops table instead of applying it
unconditionally
Also add Redmi support for Fn lock and touchpad lock control
(WMI_FN_FN_LOCK / WMI_FN_TP_LOCK sysfs attributes), keyboard type
detection (hide the RGB kb_mode attribute on white-only backlit
keyboards) and hotkeys (airplane mode -> KEY_RFKILL, ignore the
lock-state and keyboard mode indicator events).
The sysfs attribute group is now registered in probe (instead of via
dev_groups) so that the is_visible callback can access the probe
populated driver data.
Signed-off-by: Mingyou Chen <qby140326@gmail.com>
---
drivers/platform/x86/bitland-mifs-wmi.c | 336 ++++++++++++++++++++++--
1 file changed, 318 insertions(+), 18 deletions(-)
diff --git a/drivers/platform/x86/bitland-mifs-wmi.c b/drivers/platform/x86/bitland-mifs-wmi.c
index 17792d8b7c29..1eb9f92740e0 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>
@@ -71,6 +72,11 @@ enum bitland_system_ac_mode {
WMI_SYSTEM_AC_CIRCULARHOLE = 2,
};
+enum bitland_kbd_type {
+ WMI_KBD_TYPE_WHITE = 0,
+ WMI_KBD_TYPE_RGB = 1,
+};
+
enum bitland_mifs_power_profile {
WMI_PP_BALANCED = 0,
WMI_PP_PERFORMANCE = 1,
@@ -97,7 +103,15 @@ static const struct bitland_profile_mode_map bitland_mode_map = {
.full_speed = WMI_PP_FULL_SPEED,
};
+static const struct bitland_profile_mode_map redmi_mode_map = {
+ .balanced = 1,
+ .performance = 0,
+ .quiet = 2,
+ .full_speed = 4,
+};
+
static int bitland_pp_check_capability(struct bitland_mifs_wmi_data *data);
+static int redmi_pp_check_capability(struct bitland_mifs_wmi_data *data);
/*
* Machine-specific operations. The MIFS WMI interface is shared by
@@ -109,6 +123,8 @@ struct bitland_mifs_ops {
const struct bitland_profile_mode_map *mode_map;
int (*check_performance_capability)(struct bitland_mifs_wmi_data *data);
bool has_full_speed;
+ bool has_cpu_power;
+ bool quirk_refresh_rate_toggle;
};
static const struct bitland_mifs_ops bitland_ops = {
@@ -118,6 +134,54 @@ static const struct bitland_mifs_ops bitland_ops = {
.has_full_speed = true,
};
+static const struct bitland_mifs_ops redmi_book_ops = {
+ .name = "Redmi Book",
+ .mode_map = &redmi_mode_map,
+ .check_performance_capability = redmi_pp_check_capability,
+ .has_cpu_power = true,
+ .quirk_refresh_rate_toggle = true,
+};
+
+static const struct bitland_mifs_ops redmi_g_ops = {
+ .name = "Redmi G",
+ .mode_map = &redmi_mode_map,
+ .check_performance_capability = redmi_pp_check_capability,
+ .has_full_speed = true,
+ .has_cpu_power = true,
+ .quirk_refresh_rate_toggle = true,
+};
+
+static const struct dmi_system_id bitland_mifs_dmi_table[] = {
+ {
+ /* Redmi G */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "TM2135"),
+ },
+ .driver_data = (void *)&redmi_g_ops,
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "TM2137"),
+ },
+ .driver_data = (void *)&redmi_g_ops,
+ },
+ {
+ /* Redmi Book series */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Redmi"),
+ },
+ .driver_data = (void *)&redmi_book_ops,
+ },
+ {
+ /* Xiaomi Book series */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "TIMI"),
+ },
+ .driver_data = (void *)&redmi_book_ops,
+ },
+ {}
+};
+
enum bitland_mifs_event_id {
WMI_EVENT_RESERVED_1 = 1,
WMI_EVENT_RESERVED_2 = 2,
@@ -203,6 +267,8 @@ struct bitland_mifs_wmi_data {
struct input_dev *input_dev;
struct device *hwmon_dev;
struct device *pp_dev;
+ enum bitland_wmi_device_type dev_type;
+ u8 kbd_type;
const struct bitland_mifs_ops *ops;
enum platform_profile_option saved_profile;
};
@@ -255,8 +321,10 @@ static int laptop_profile_get(struct device *dev,
else if (mode == map->balanced)
*profile = PLATFORM_PROFILE_BALANCED;
else if (mode == map->performance)
- *profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE;
- else if (mode == map->full_speed)
+ *profile = data->ops->has_full_speed ?
+ PLATFORM_PROFILE_BALANCED_PERFORMANCE :
+ PLATFORM_PROFILE_PERFORMANCE;
+ else if (data->ops->has_full_speed && mode == map->full_speed)
*profile = PLATFORM_PROFILE_PERFORMANCE;
else
return -EINVAL;
@@ -273,10 +341,11 @@ static int bitland_pp_check_capability(struct bitland_mifs_wmi_data *data)
struct bitland_mifs_output output;
int ret;
- /* Full-speed/performance mode requires DC power (not USB-C) */
+ /* The performance modes require AC power */
if (!power_supply_is_system_supplied())
return -EOPNOTSUPP;
+ /* Additionally require the barrel-jack power supply (not USB-C) */
ret = bitland_mifs_wmi_call(data, &input, &output);
if (ret)
return ret;
@@ -287,6 +356,15 @@ static int bitland_pp_check_capability(struct bitland_mifs_wmi_data *data)
return 0;
}
+static int redmi_pp_check_capability(struct bitland_mifs_wmi_data *data)
+{
+ /* The performance modes require AC power (any source) */
+ if (!power_supply_is_system_supplied())
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
static int laptop_profile_set(struct device *dev,
enum platform_profile_option profile)
{
@@ -309,6 +387,8 @@ static int laptop_profile_set(struct device *dev,
mode = map->balanced;
break;
case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
+ if (!data->ops->has_full_speed)
+ return -EOPNOTSUPP;
ret = data->ops->check_performance_capability(data);
if (ret)
return ret;
@@ -318,7 +398,7 @@ static int laptop_profile_set(struct device *dev,
ret = data->ops->check_performance_capability(data);
if (ret)
return ret;
- mode = map->full_speed;
+ mode = data->ops->has_full_speed ? map->full_speed : map->performance;
break;
default:
return -EOPNOTSUPP;
@@ -335,10 +415,10 @@ static int platform_profile_probe(void *drvdata, unsigned long *choices)
set_bit(PLATFORM_PROFILE_LOW_POWER, choices);
set_bit(PLATFORM_PROFILE_BALANCED, choices);
- set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, choices);
+ set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
if (data->ops->has_full_speed)
- set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
+ set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, choices);
return 0;
}
@@ -418,6 +498,13 @@ static int laptop_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
default:
return -EINVAL;
}
+ case hwmon_power:
+ /* WMI_FN_CPU_POWER returns the CPU power draw in watts */
+ input.function = WMI_FN_CPU_POWER;
+ ret = bitland_mifs_wmi_call(data, &input, &res);
+ if (!ret)
+ *val = res.data[0] * MICROWATT_PER_WATT;
+ return ret;
default:
return -EINVAL;
}
@@ -444,6 +531,15 @@ static const struct hwmon_channel_info *laptop_hwmon_info[] = {
NULL
};
+static const struct hwmon_channel_info *laptop_hwmon_info_power[] = {
+ HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
+ HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_LABEL,
+ HWMON_F_INPUT | HWMON_F_LABEL,
+ HWMON_F_INPUT | HWMON_F_LABEL),
+ HWMON_CHANNEL_INFO(power, HWMON_P_INPUT),
+ NULL
+};
+
static const struct hwmon_ops laptop_hwmon_ops = {
.visible = 0444,
.read = laptop_hwmon_read,
@@ -455,6 +551,11 @@ static const struct hwmon_chip_info laptop_chip_info = {
.info = laptop_hwmon_info,
};
+static const struct hwmon_chip_info laptop_chip_info_power = {
+ .ops = &laptop_hwmon_ops,
+ .info = laptop_hwmon_info_power,
+};
+
static int laptop_kbd_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
@@ -498,6 +599,96 @@ static const char *const gpu_mode_strings[] = {
"uma",
};
+static ssize_t fn_lock_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev);
+ struct bitland_mifs_input input = {
+ .reserved1 = 0,
+ .operation = WMI_METHOD_GET,
+ .reserved2 = 0,
+ .function = WMI_FN_FN_LOCK,
+ };
+ struct bitland_mifs_output res;
+ int ret;
+
+ ret = bitland_mifs_wmi_call(data, &input, &res);
+ if (ret)
+ return ret;
+
+ return sysfs_emit(buf, "%u\n", res.data[0]);
+}
+
+static ssize_t fn_lock_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev);
+ struct bitland_mifs_input input = {
+ .reserved1 = 0,
+ .operation = WMI_METHOD_SET,
+ .reserved2 = 0,
+ .function = WMI_FN_FN_LOCK,
+ };
+ bool val;
+ int ret;
+
+ if (kstrtobool(buf, &val))
+ return -EINVAL;
+
+ input.payload[0] = val;
+
+ ret = bitland_mifs_wmi_call(data, &input, NULL);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static ssize_t touchpad_lock_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev);
+ struct bitland_mifs_input input = {
+ .reserved1 = 0,
+ .operation = WMI_METHOD_GET,
+ .reserved2 = 0,
+ .function = WMI_FN_TP_LOCK,
+ };
+ struct bitland_mifs_output res;
+ int ret;
+
+ ret = bitland_mifs_wmi_call(data, &input, &res);
+ if (ret)
+ return ret;
+
+ return sysfs_emit(buf, "%u\n", res.data[0]);
+}
+
+static ssize_t touchpad_lock_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev);
+ struct bitland_mifs_input input = {
+ .reserved1 = 0,
+ .operation = WMI_METHOD_SET,
+ .reserved2 = 0,
+ .function = WMI_FN_TP_LOCK,
+ };
+ bool val;
+ int ret;
+
+ if (kstrtobool(buf, &val))
+ return -EINVAL;
+
+ input.payload[0] = val;
+
+ ret = bitland_mifs_wmi_call(data, &input, NULL);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
/* GPU Mode: 0:Hybrid, 1:Discrete, 2:UMA */
static ssize_t gpu_mode_show(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -637,17 +828,46 @@ static ssize_t fan_boost_store(struct device *dev,
return count;
}
-static const DEVICE_ATTR_RW(gpu_mode);
-static const DEVICE_ATTR_RW(kb_mode);
-static const DEVICE_ATTR_WO(fan_boost);
+static DEVICE_ATTR_RW(gpu_mode);
+static DEVICE_ATTR_RW(kb_mode);
+static DEVICE_ATTR_WO(fan_boost);
+static DEVICE_ATTR_RW(fn_lock);
+static DEVICE_ATTR_RW(touchpad_lock);
-static const struct attribute *const laptop_attrs[] = {
+static struct attribute *laptop_attrs[] = {
&dev_attr_gpu_mode.attr,
&dev_attr_kb_mode.attr,
&dev_attr_fan_boost.attr,
+ &dev_attr_fn_lock.attr,
+ &dev_attr_touchpad_lock.attr,
NULL,
};
-ATTRIBUTE_GROUPS(laptop);
+
+static umode_t laptop_attr_is_visible(struct kobject *kobj,
+ struct attribute *attr, int idx)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev);
+
+ /* The sysfs attributes are only supported on the control device */
+ if (data->dev_type != BITLAND_WMI_CONTROL)
+ return 0;
+
+ /*
+ * White-only backlit keyboards (e.g. Redmi Book series) do not
+ * support the RGB backlight modes.
+ */
+ if (attr == &dev_attr_kb_mode.attr &&
+ data->kbd_type == WMI_KBD_TYPE_WHITE)
+ return 0;
+
+ return attr->mode;
+}
+
+static const struct attribute_group laptop_group = {
+ .attrs = laptop_attrs,
+ .is_visible = laptop_attr_is_visible,
+};
static const struct key_entry bitland_mifs_wmi_keymap[] = {
{ KE_KEY, BI_HOTKEY_CODE(WMI_EVENT_OPEN_APP, 1, 0), { KEY_PROG1 } },
@@ -699,6 +919,34 @@ static const struct key_entry bitland_mifs_wmi_keymap[] = {
{ KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_FNLOCK_STATE, 0, 0), {} },
{ KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_FNLOCK_STATE, 1, 0), {} },
+ /* Airplane mode toggle (Redmi) */
+ { KE_KEY, BI_HOTKEY_CODE(WMI_EVENT_AIRPLANE_MODE, 0, 0), { KEY_RFKILL } },
+ { KE_KEY, BI_HOTKEY_CODE(WMI_EVENT_AIRPLANE_MODE, 1, 0), { KEY_RFKILL } },
+
+ /*
+ * Touchpad state change (Redmi Fn+F7). The EC performs the actual
+ * toggle itself; userspace can control the state via the
+ * touchpad_lock sysfs attribute.
+ */
+ { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_TOUCHPAD_STATE, 0, 0), {} },
+ { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_TOUCHPAD_STATE, 1, 0), {} },
+
+ /* Keyboard backlight mode change */
+ { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_KBD_MODE, 0, 0), {} },
+ { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_KBD_MODE, 1, 0), {} },
+
+ /* Lock-state indicator events */
+ { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_CAPSLOCK_STATE, 0, 0), {} },
+ { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_CAPSLOCK_STATE, 1, 0), {} },
+ { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_NUMLOCK_STATE, 0, 0), {} },
+ { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_NUMLOCK_STATE, 1, 0), {} },
+ { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_SCROLLLOCK_STATE, 0, 0), {} },
+ { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_SCROLLLOCK_STATE, 1, 0), {} },
+
+ /* Win key lock (Redmi gaming mode) */
+ { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_WIN_KEY_LOCK, 0, 0), {} },
+ { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_WIN_KEY_LOCK, 1, 0), {} },
+
/* Fn+`/1/2/3/4 */
{ KE_KEY, BI_HOTKEY_CODE(WMI_EVENT_FN_F, 1, 0), { KEY_F13 } },
{ KE_KEY, BI_HOTKEY_CODE(WMI_EVENT_FN_0, 1, 0), { KEY_F14 } },
@@ -744,6 +992,36 @@ static int bitland_notifier_callback(struct notifier_block *nb,
return NOTIFY_OK;
}
+static int bitland_detect_kbd_type(struct bitland_mifs_wmi_data *data)
+{
+ struct bitland_mifs_input input = {
+ .reserved1 = 0,
+ .operation = WMI_METHOD_GET,
+ .reserved2 = 0,
+ .function = WMI_FN_KBD_TYPE,
+ };
+ struct bitland_mifs_output res;
+ int ret;
+
+ ret = bitland_mifs_wmi_call(data, &input, &res);
+ if (ret)
+ return ret;
+
+ data->kbd_type = res.data[0];
+ return 0;
+}
+
+static void bitland_detect_ops(struct bitland_mifs_wmi_data *data)
+{
+ const struct dmi_system_id *id;
+
+ id = dmi_first_match(bitland_mifs_dmi_table);
+ if (id)
+ data->ops = id->driver_data;
+
+ dev_dbg(&data->wdev->dev, "Detected machine ops: %s\n", data->ops->name);
+}
+
static int bitland_mifs_wmi_probe(struct wmi_device *wdev, const void *context)
{
struct bitland_mifs_wmi_data *drv_data;
@@ -761,6 +1039,9 @@ static int bitland_mifs_wmi_probe(struct wmi_device *wdev, const void *context)
return -ENOMEM;
drv_data->wdev = wdev;
+ drv_data->dev_type = dev_type;
+ /* Default to RGB so the RGB attributes stay visible if detection fails */
+ drv_data->kbd_type = WMI_KBD_TYPE_RGB;
drv_data->ops = &bitland_ops;
ret = devm_mutex_init(&wdev->dev, &drv_data->lock);
@@ -769,6 +1050,8 @@ static int bitland_mifs_wmi_probe(struct wmi_device *wdev, const void *context)
dev_set_drvdata(&wdev->dev, drv_data);
+ bitland_detect_ops(drv_data);
+
if (dev_type == BITLAND_WMI_EVENT) {
/* Register input device for hotkeys */
drv_data->input_dev = devm_input_allocate_device(&wdev->dev);
@@ -794,12 +1077,28 @@ 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);
+ /* Detect the keyboard type */
+ ret = bitland_detect_kbd_type(drv_data);
+ if (ret)
+ dev_dbg(&wdev->dev, "Failed to detect keyboard type, assuming RGB: %d\n", ret);
+
+ ret = devm_device_add_group(&wdev->dev, &laptop_group);
+ if (ret)
+ return ret;
+
/* Register hwmon */
- drv_data->hwmon_dev = devm_hwmon_device_register_with_info(&wdev->dev,
- "bitland_mifs",
- drv_data,
- &laptop_chip_info,
- NULL);
+ if (drv_data->ops->has_cpu_power)
+ drv_data->hwmon_dev = devm_hwmon_device_register_with_info(&wdev->dev,
+ "bitland_mifs",
+ drv_data,
+ &laptop_chip_info_power,
+ NULL);
+ else
+ drv_data->hwmon_dev = devm_hwmon_device_register_with_info(&wdev->dev,
+ "bitland_mifs",
+ drv_data,
+ &laptop_chip_info,
+ NULL);
if (IS_ERR(drv_data->hwmon_dev))
return PTR_ERR(drv_data->hwmon_dev);
@@ -851,6 +1150,7 @@ static void bitland_mifs_wmi_report_key(struct wmi_device *wdev, u32 payload)
static void bitland_mifs_wmi_notify(struct wmi_device *wdev,
const struct wmi_buffer *buffer)
{
+ struct bitland_mifs_wmi_data *data = dev_get_drvdata(&wdev->dev);
const struct bitland_mifs_event *event = buffer->data;
struct bitland_fan_notify_data fan_data;
u32 payload;
@@ -885,7 +1185,8 @@ static void bitland_mifs_wmi_notify(struct wmi_device *wdev,
case WMI_EVENT_CPU_FAN_SPEED:
case WMI_EVENT_GPU_FAN_SPEED:
/* Redmi refresh rate toggle quirk */
- if (event->event_id == WMI_EVENT_CPU_FAN_SPEED &&
+ if (data->ops->quirk_refresh_rate_toggle &&
+ event->event_id == WMI_EVENT_CPU_FAN_SPEED &&
event->value_low == 0 && event->value_high == 0) {
payload = BI_HOTKEY_CODE(WMI_EVENT_REFRESH_RATE, 0, 0);
bitland_mifs_wmi_report_key(wdev, payload);
@@ -923,7 +1224,6 @@ static struct wmi_driver bitland_mifs_wmi_driver = {
.no_singleton = true,
.driver = {
.name = DRV_NAME,
- .dev_groups = laptop_groups,
.pm = pm_sleep_ptr(&bitland_mifs_wmi_pm_ops),
},
.id_table = bitland_mifs_wmi_id_table,
--
2.55.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH v5 6/6] platform/x86: bitland-mifs-wmi: Add Redmi laptop support
2026-08-16 10:08 ` [PATCH v5 6/6] platform/x86: bitland-mifs-wmi: Add Redmi laptop support Mingyou Chen
@ 2026-08-17 23:17 ` Miloš Vlku
0 siblings, 0 replies; 13+ messages in thread
From: Miloš Vlku @ 2026-08-17 23:17 UTC (permalink / raw)
To: Mingyou Chen
Cc: W_Armin, foxido, hansg, ilpo.jarvinen, linux-kernel, nika,
platform-driver-x86, i, wolf109909, rahulbheda131313
> + {
> + /* Redmi Book series */
> + .matches = {
> + DMI_MATCH(DMI_SYS_VENDOR, "Redmi"),
> + },
> + .driver_data = (void *)&redmi_book_ops,
> + },
> + {
> + /* Xiaomi Book series */
> + .matches = {
> + DMI_MATCH(DMI_SYS_VENDOR, "TIMI"),
> + },
> + .driver_data = (void *)&redmi_book_ops,
> + },
Neither entry matches my machine. I have a Redmi Book Pro 14 2024, and
it reports:
DMI_SYS_VENDOR XIAOMI
DMI_BOARD_NAME TM2307
DMI_PRODUCT_NAME Redmi Book Pro 14 2024
Since DMI_MATCH() is a substring test, neither "Redmi" nor "TIMI"
matches "XIAOMI", so dmi_first_match() returns NULL and data->ops stays
at &bitland_ops. The series has no effect on this model.
A board-name match would cover it:
{
/* Redmi Book Pro 14 2024 */
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "TM2307"),
},
.driver_data = (void *)&redmi_book_ops,
},
I don't know how widely the "XIAOMI" vendor string is used across the
range, so matching DMI_SYS_VENDOR "XIAOMI" may cover more machines at
once if that encoding applies to all of them.
Thanks,
Milos
On Sun, Aug 16, 2026 at 12:08 PM Mingyou Chen <qby140326@gmail.com> wrote:
>
> Redmi laptops (Redmi Book and Redmi G series) use the same MIFS WMI
> interface but encode the performance mode values of
> WMI_FN_SYSTEM_PER_MODE differently, as reverse engineered from the
> Windows GCommandCenter application:
>
> 0 = performance, 1 = balanced, 2 = quiet, 4 = full speed
>
> Add "Redmi Book" and "Redmi G" ops entries selected via DMI:
> - Redmi G (board TM2135/TM2137): the full-speed mode is available
> - Redmi Book/Xiaomi Book: no full-speed mode
> - The performance modes only require AC power (any source); the
> barrel-jack requirement is Bitland-specific
>
> The GCommandCenter application further showed that:
> - the CPU power sensor (WMI_FN_CPU_POWER) is only available on Redmi
> machines, gate the hwmon power channel on the ops table
> - the refresh-rate-toggle quirk in the CPU fan speed event is
> Redmi-specific, gate it on the ops table instead of applying it
> unconditionally
>
> Also add Redmi support for Fn lock and touchpad lock control
> (WMI_FN_FN_LOCK / WMI_FN_TP_LOCK sysfs attributes), keyboard type
> detection (hide the RGB kb_mode attribute on white-only backlit
> keyboards) and hotkeys (airplane mode -> KEY_RFKILL, ignore the
> lock-state and keyboard mode indicator events).
>
> The sysfs attribute group is now registered in probe (instead of via
> dev_groups) so that the is_visible callback can access the probe
> populated driver data.
>
> Signed-off-by: Mingyou Chen <qby140326@gmail.com>
> ---
> drivers/platform/x86/bitland-mifs-wmi.c | 336 ++++++++++++++++++++++--
> 1 file changed, 318 insertions(+), 18 deletions(-)
>
> diff --git a/drivers/platform/x86/bitland-mifs-wmi.c b/drivers/platform/x86/bitland-mifs-wmi.c
> index 17792d8b7c29..1eb9f92740e0 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>
> @@ -71,6 +72,11 @@ enum bitland_system_ac_mode {
> WMI_SYSTEM_AC_CIRCULARHOLE = 2,
> };
>
> +enum bitland_kbd_type {
> + WMI_KBD_TYPE_WHITE = 0,
> + WMI_KBD_TYPE_RGB = 1,
> +};
> +
> enum bitland_mifs_power_profile {
> WMI_PP_BALANCED = 0,
> WMI_PP_PERFORMANCE = 1,
> @@ -97,7 +103,15 @@ static const struct bitland_profile_mode_map bitland_mode_map = {
> .full_speed = WMI_PP_FULL_SPEED,
> };
>
> +static const struct bitland_profile_mode_map redmi_mode_map = {
> + .balanced = 1,
> + .performance = 0,
> + .quiet = 2,
> + .full_speed = 4,
> +};
> +
> static int bitland_pp_check_capability(struct bitland_mifs_wmi_data *data);
> +static int redmi_pp_check_capability(struct bitland_mifs_wmi_data *data);
>
> /*
> * Machine-specific operations. The MIFS WMI interface is shared by
> @@ -109,6 +123,8 @@ struct bitland_mifs_ops {
> const struct bitland_profile_mode_map *mode_map;
> int (*check_performance_capability)(struct bitland_mifs_wmi_data *data);
> bool has_full_speed;
> + bool has_cpu_power;
> + bool quirk_refresh_rate_toggle;
> };
>
> static const struct bitland_mifs_ops bitland_ops = {
> @@ -118,6 +134,54 @@ static const struct bitland_mifs_ops bitland_ops = {
> .has_full_speed = true,
> };
>
> +static const struct bitland_mifs_ops redmi_book_ops = {
> + .name = "Redmi Book",
> + .mode_map = &redmi_mode_map,
> + .check_performance_capability = redmi_pp_check_capability,
> + .has_cpu_power = true,
> + .quirk_refresh_rate_toggle = true,
> +};
> +
> +static const struct bitland_mifs_ops redmi_g_ops = {
> + .name = "Redmi G",
> + .mode_map = &redmi_mode_map,
> + .check_performance_capability = redmi_pp_check_capability,
> + .has_full_speed = true,
> + .has_cpu_power = true,
> + .quirk_refresh_rate_toggle = true,
> +};
> +
> +static const struct dmi_system_id bitland_mifs_dmi_table[] = {
> + {
> + /* Redmi G */
> + .matches = {
> + DMI_MATCH(DMI_BOARD_NAME, "TM2135"),
> + },
> + .driver_data = (void *)&redmi_g_ops,
> + },
> + {
> + .matches = {
> + DMI_MATCH(DMI_BOARD_NAME, "TM2137"),
> + },
> + .driver_data = (void *)&redmi_g_ops,
> + },
> + {
> + /* Redmi Book series */
> + .matches = {
> + DMI_MATCH(DMI_SYS_VENDOR, "Redmi"),
> + },
> + .driver_data = (void *)&redmi_book_ops,
> + },
> + {
> + /* Xiaomi Book series */
> + .matches = {
> + DMI_MATCH(DMI_SYS_VENDOR, "TIMI"),
> + },
> + .driver_data = (void *)&redmi_book_ops,
> + },
> + {}
> +};
> +
> enum bitland_mifs_event_id {
> WMI_EVENT_RESERVED_1 = 1,
> WMI_EVENT_RESERVED_2 = 2,
> @@ -203,6 +267,8 @@ struct bitland_mifs_wmi_data {
> struct input_dev *input_dev;
> struct device *hwmon_dev;
> struct device *pp_dev;
> + enum bitland_wmi_device_type dev_type;
> + u8 kbd_type;
> const struct bitland_mifs_ops *ops;
> enum platform_profile_option saved_profile;
> };
> @@ -255,8 +321,10 @@ static int laptop_profile_get(struct device *dev,
> else if (mode == map->balanced)
> *profile = PLATFORM_PROFILE_BALANCED;
> else if (mode == map->performance)
> - *profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE;
> - else if (mode == map->full_speed)
> + *profile = data->ops->has_full_speed ?
> + PLATFORM_PROFILE_BALANCED_PERFORMANCE :
> + PLATFORM_PROFILE_PERFORMANCE;
> + else if (data->ops->has_full_speed && mode == map->full_speed)
> *profile = PLATFORM_PROFILE_PERFORMANCE;
> else
> return -EINVAL;
> @@ -273,10 +341,11 @@ static int bitland_pp_check_capability(struct bitland_mifs_wmi_data *data)
> struct bitland_mifs_output output;
> int ret;
>
> - /* Full-speed/performance mode requires DC power (not USB-C) */
> + /* The performance modes require AC power */
> if (!power_supply_is_system_supplied())
> return -EOPNOTSUPP;
>
> + /* Additionally require the barrel-jack power supply (not USB-C) */
> ret = bitland_mifs_wmi_call(data, &input, &output);
> if (ret)
> return ret;
> @@ -287,6 +356,15 @@ static int bitland_pp_check_capability(struct bitland_mifs_wmi_data *data)
> return 0;
> }
>
> +static int redmi_pp_check_capability(struct bitland_mifs_wmi_data *data)
> +{
> + /* The performance modes require AC power (any source) */
> + if (!power_supply_is_system_supplied())
> + return -EOPNOTSUPP;
> +
> + return 0;
> +}
> +
> static int laptop_profile_set(struct device *dev,
> enum platform_profile_option profile)
> {
> @@ -309,6 +387,8 @@ static int laptop_profile_set(struct device *dev,
> mode = map->balanced;
> break;
> case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
> + if (!data->ops->has_full_speed)
> + return -EOPNOTSUPP;
> ret = data->ops->check_performance_capability(data);
> if (ret)
> return ret;
> @@ -318,7 +398,7 @@ static int laptop_profile_set(struct device *dev,
> ret = data->ops->check_performance_capability(data);
> if (ret)
> return ret;
> - mode = map->full_speed;
> + mode = data->ops->has_full_speed ? map->full_speed : map->performance;
> break;
> default:
> return -EOPNOTSUPP;
> @@ -335,10 +415,10 @@ static int platform_profile_probe(void *drvdata, unsigned long *choices)
>
> set_bit(PLATFORM_PROFILE_LOW_POWER, choices);
> set_bit(PLATFORM_PROFILE_BALANCED, choices);
> - set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, choices);
> + set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
>
> if (data->ops->has_full_speed)
> - set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
> + set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, choices);
>
> return 0;
> }
> @@ -418,6 +498,13 @@ static int laptop_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
> default:
> return -EINVAL;
> }
> + case hwmon_power:
> + /* WMI_FN_CPU_POWER returns the CPU power draw in watts */
> + input.function = WMI_FN_CPU_POWER;
> + ret = bitland_mifs_wmi_call(data, &input, &res);
> + if (!ret)
> + *val = res.data[0] * MICROWATT_PER_WATT;
> + return ret;
> default:
> return -EINVAL;
> }
> @@ -444,6 +531,15 @@ static const struct hwmon_channel_info *laptop_hwmon_info[] = {
> NULL
> };
>
> +static const struct hwmon_channel_info *laptop_hwmon_info_power[] = {
> + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
> + HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_LABEL,
> + HWMON_F_INPUT | HWMON_F_LABEL,
> + HWMON_F_INPUT | HWMON_F_LABEL),
> + HWMON_CHANNEL_INFO(power, HWMON_P_INPUT),
> + NULL
> +};
> +
> static const struct hwmon_ops laptop_hwmon_ops = {
> .visible = 0444,
> .read = laptop_hwmon_read,
> @@ -455,6 +551,11 @@ static const struct hwmon_chip_info laptop_chip_info = {
> .info = laptop_hwmon_info,
> };
>
> +static const struct hwmon_chip_info laptop_chip_info_power = {
> + .ops = &laptop_hwmon_ops,
> + .info = laptop_hwmon_info_power,
> +};
> +
> static int laptop_kbd_led_set(struct led_classdev *led_cdev,
> enum led_brightness value)
> {
> @@ -498,6 +599,96 @@ static const char *const gpu_mode_strings[] = {
> "uma",
> };
>
> +static ssize_t fn_lock_show(struct device *dev, struct device_attribute *attr,
> + char *buf)
> +{
> + struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev);
> + struct bitland_mifs_input input = {
> + .reserved1 = 0,
> + .operation = WMI_METHOD_GET,
> + .reserved2 = 0,
> + .function = WMI_FN_FN_LOCK,
> + };
> + struct bitland_mifs_output res;
> + int ret;
> +
> + ret = bitland_mifs_wmi_call(data, &input, &res);
> + if (ret)
> + return ret;
> +
> + return sysfs_emit(buf, "%u\n", res.data[0]);
> +}
> +
> +static ssize_t fn_lock_store(struct device *dev, struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev);
> + struct bitland_mifs_input input = {
> + .reserved1 = 0,
> + .operation = WMI_METHOD_SET,
> + .reserved2 = 0,
> + .function = WMI_FN_FN_LOCK,
> + };
> + bool val;
> + int ret;
> +
> + if (kstrtobool(buf, &val))
> + return -EINVAL;
> +
> + input.payload[0] = val;
> +
> + ret = bitland_mifs_wmi_call(data, &input, NULL);
> + if (ret)
> + return ret;
> +
> + return count;
> +}
> +
> +static ssize_t touchpad_lock_show(struct device *dev, struct device_attribute *attr,
> + char *buf)
> +{
> + struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev);
> + struct bitland_mifs_input input = {
> + .reserved1 = 0,
> + .operation = WMI_METHOD_GET,
> + .reserved2 = 0,
> + .function = WMI_FN_TP_LOCK,
> + };
> + struct bitland_mifs_output res;
> + int ret;
> +
> + ret = bitland_mifs_wmi_call(data, &input, &res);
> + if (ret)
> + return ret;
> +
> + return sysfs_emit(buf, "%u\n", res.data[0]);
> +}
> +
> +static ssize_t touchpad_lock_store(struct device *dev, struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev);
> + struct bitland_mifs_input input = {
> + .reserved1 = 0,
> + .operation = WMI_METHOD_SET,
> + .reserved2 = 0,
> + .function = WMI_FN_TP_LOCK,
> + };
> + bool val;
> + int ret;
> +
> + if (kstrtobool(buf, &val))
> + return -EINVAL;
> +
> + input.payload[0] = val;
> +
> + ret = bitland_mifs_wmi_call(data, &input, NULL);
> + if (ret)
> + return ret;
> +
> + return count;
> +}
> +
> /* GPU Mode: 0:Hybrid, 1:Discrete, 2:UMA */
> static ssize_t gpu_mode_show(struct device *dev, struct device_attribute *attr,
> char *buf)
> @@ -637,17 +828,46 @@ static ssize_t fan_boost_store(struct device *dev,
> return count;
> }
>
> -static const DEVICE_ATTR_RW(gpu_mode);
> -static const DEVICE_ATTR_RW(kb_mode);
> -static const DEVICE_ATTR_WO(fan_boost);
> +static DEVICE_ATTR_RW(gpu_mode);
> +static DEVICE_ATTR_RW(kb_mode);
> +static DEVICE_ATTR_WO(fan_boost);
> +static DEVICE_ATTR_RW(fn_lock);
> +static DEVICE_ATTR_RW(touchpad_lock);
>
> -static const struct attribute *const laptop_attrs[] = {
> +static struct attribute *laptop_attrs[] = {
> &dev_attr_gpu_mode.attr,
> &dev_attr_kb_mode.attr,
> &dev_attr_fan_boost.attr,
> + &dev_attr_fn_lock.attr,
> + &dev_attr_touchpad_lock.attr,
> NULL,
> };
> -ATTRIBUTE_GROUPS(laptop);
> +
> +static umode_t laptop_attr_is_visible(struct kobject *kobj,
> + struct attribute *attr, int idx)
> +{
> + struct device *dev = kobj_to_dev(kobj);
> + struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev);
> +
> + /* The sysfs attributes are only supported on the control device */
> + if (data->dev_type != BITLAND_WMI_CONTROL)
> + return 0;
> +
> + /*
> + * White-only backlit keyboards (e.g. Redmi Book series) do not
> + * support the RGB backlight modes.
> + */
> + if (attr == &dev_attr_kb_mode.attr &&
> + data->kbd_type == WMI_KBD_TYPE_WHITE)
> + return 0;
> +
> + return attr->mode;
> +}
> +
> +static const struct attribute_group laptop_group = {
> + .attrs = laptop_attrs,
> + .is_visible = laptop_attr_is_visible,
> +};
>
> static const struct key_entry bitland_mifs_wmi_keymap[] = {
> { KE_KEY, BI_HOTKEY_CODE(WMI_EVENT_OPEN_APP, 1, 0), { KEY_PROG1 } },
> @@ -699,6 +919,34 @@ static const struct key_entry bitland_mifs_wmi_keymap[] = {
> { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_FNLOCK_STATE, 0, 0), {} },
> { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_FNLOCK_STATE, 1, 0), {} },
>
> + /* Airplane mode toggle (Redmi) */
> + { KE_KEY, BI_HOTKEY_CODE(WMI_EVENT_AIRPLANE_MODE, 0, 0), { KEY_RFKILL } },
> + { KE_KEY, BI_HOTKEY_CODE(WMI_EVENT_AIRPLANE_MODE, 1, 0), { KEY_RFKILL } },
> +
> + /*
> + * Touchpad state change (Redmi Fn+F7). The EC performs the actual
> + * toggle itself; userspace can control the state via the
> + * touchpad_lock sysfs attribute.
> + */
> + { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_TOUCHPAD_STATE, 0, 0), {} },
> + { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_TOUCHPAD_STATE, 1, 0), {} },
> +
> + /* Keyboard backlight mode change */
> + { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_KBD_MODE, 0, 0), {} },
> + { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_KBD_MODE, 1, 0), {} },
> +
> + /* Lock-state indicator events */
> + { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_CAPSLOCK_STATE, 0, 0), {} },
> + { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_CAPSLOCK_STATE, 1, 0), {} },
> + { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_NUMLOCK_STATE, 0, 0), {} },
> + { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_NUMLOCK_STATE, 1, 0), {} },
> + { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_SCROLLLOCK_STATE, 0, 0), {} },
> + { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_SCROLLLOCK_STATE, 1, 0), {} },
> +
> + /* Win key lock (Redmi gaming mode) */
> + { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_WIN_KEY_LOCK, 0, 0), {} },
> + { KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_WIN_KEY_LOCK, 1, 0), {} },
> +
> /* Fn+`/1/2/3/4 */
> { KE_KEY, BI_HOTKEY_CODE(WMI_EVENT_FN_F, 1, 0), { KEY_F13 } },
> { KE_KEY, BI_HOTKEY_CODE(WMI_EVENT_FN_0, 1, 0), { KEY_F14 } },
> @@ -744,6 +992,36 @@ static int bitland_notifier_callback(struct notifier_block *nb,
> return NOTIFY_OK;
> }
>
> +static int bitland_detect_kbd_type(struct bitland_mifs_wmi_data *data)
> +{
> + struct bitland_mifs_input input = {
> + .reserved1 = 0,
> + .operation = WMI_METHOD_GET,
> + .reserved2 = 0,
> + .function = WMI_FN_KBD_TYPE,
> + };
> + struct bitland_mifs_output res;
> + int ret;
> +
> + ret = bitland_mifs_wmi_call(data, &input, &res);
> + if (ret)
> + return ret;
> +
> + data->kbd_type = res.data[0];
> + return 0;
> +}
> +
> +static void bitland_detect_ops(struct bitland_mifs_wmi_data *data)
> +{
> + const struct dmi_system_id *id;
> +
> + id = dmi_first_match(bitland_mifs_dmi_table);
> + if (id)
> + data->ops = id->driver_data;
> +
> + dev_dbg(&data->wdev->dev, "Detected machine ops: %s\n", data->ops->name);
> +}
> +
> static int bitland_mifs_wmi_probe(struct wmi_device *wdev, const void *context)
> {
> struct bitland_mifs_wmi_data *drv_data;
> @@ -761,6 +1039,9 @@ static int bitland_mifs_wmi_probe(struct wmi_device *wdev, const void *context)
> return -ENOMEM;
>
> drv_data->wdev = wdev;
> + drv_data->dev_type = dev_type;
> + /* Default to RGB so the RGB attributes stay visible if detection fails */
> + drv_data->kbd_type = WMI_KBD_TYPE_RGB;
> drv_data->ops = &bitland_ops;
>
> ret = devm_mutex_init(&wdev->dev, &drv_data->lock);
> @@ -769,6 +1050,8 @@ static int bitland_mifs_wmi_probe(struct wmi_device *wdev, const void *context)
>
> dev_set_drvdata(&wdev->dev, drv_data);
>
> + bitland_detect_ops(drv_data);
> +
> if (dev_type == BITLAND_WMI_EVENT) {
> /* Register input device for hotkeys */
> drv_data->input_dev = devm_input_allocate_device(&wdev->dev);
> @@ -794,12 +1077,28 @@ 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);
>
> + /* Detect the keyboard type */
> + ret = bitland_detect_kbd_type(drv_data);
> + if (ret)
> + dev_dbg(&wdev->dev, "Failed to detect keyboard type, assuming RGB: %d\n", ret);
> +
> + ret = devm_device_add_group(&wdev->dev, &laptop_group);
> + if (ret)
> + return ret;
> +
> /* Register hwmon */
> - drv_data->hwmon_dev = devm_hwmon_device_register_with_info(&wdev->dev,
> - "bitland_mifs",
> - drv_data,
> - &laptop_chip_info,
> - NULL);
> + if (drv_data->ops->has_cpu_power)
> + drv_data->hwmon_dev = devm_hwmon_device_register_with_info(&wdev->dev,
> + "bitland_mifs",
> + drv_data,
> + &laptop_chip_info_power,
> + NULL);
> + else
> + drv_data->hwmon_dev = devm_hwmon_device_register_with_info(&wdev->dev,
> + "bitland_mifs",
> + drv_data,
> + &laptop_chip_info,
> + NULL);
> if (IS_ERR(drv_data->hwmon_dev))
> return PTR_ERR(drv_data->hwmon_dev);
>
> @@ -851,6 +1150,7 @@ static void bitland_mifs_wmi_report_key(struct wmi_device *wdev, u32 payload)
> static void bitland_mifs_wmi_notify(struct wmi_device *wdev,
> const struct wmi_buffer *buffer)
> {
> + struct bitland_mifs_wmi_data *data = dev_get_drvdata(&wdev->dev);
> const struct bitland_mifs_event *event = buffer->data;
> struct bitland_fan_notify_data fan_data;
> u32 payload;
> @@ -885,7 +1185,8 @@ static void bitland_mifs_wmi_notify(struct wmi_device *wdev,
> case WMI_EVENT_CPU_FAN_SPEED:
> case WMI_EVENT_GPU_FAN_SPEED:
> /* Redmi refresh rate toggle quirk */
> - if (event->event_id == WMI_EVENT_CPU_FAN_SPEED &&
> + if (data->ops->quirk_refresh_rate_toggle &&
> + event->event_id == WMI_EVENT_CPU_FAN_SPEED &&
> event->value_low == 0 && event->value_high == 0) {
> payload = BI_HOTKEY_CODE(WMI_EVENT_REFRESH_RATE, 0, 0);
> bitland_mifs_wmi_report_key(wdev, payload);
> @@ -923,7 +1224,6 @@ static struct wmi_driver bitland_mifs_wmi_driver = {
> .no_singleton = true,
> .driver = {
> .name = DRV_NAME,
> - .dev_groups = laptop_groups,
> .pm = pm_sleep_ptr(&bitland_mifs_wmi_pm_ops),
> },
> .id_table = bitland_mifs_wmi_id_table,
> --
> 2.55.0
>
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH] platform/x86: bitland-mifs-wmi: add TM2424 ops and hotkeys
2026-08-16 10:08 [PATCH v5 0/6] Merge redmi-wmi into bitland-mifs-wmi Mingyou Chen
` (5 preceding siblings ...)
2026-08-16 10:08 ` [PATCH v5 6/6] platform/x86: bitland-mifs-wmi: Add Redmi laptop support Mingyou Chen
@ 2026-08-26 13:26 ` KentoNion
2026-08-26 13:29 ` KentoNion
7 siblings, 0 replies; 13+ messages in thread
From: KentoNion @ 2026-08-26 13:26 UTC (permalink / raw)
To: platform-driver-x86, Mingyou Chen
Cc: Hans de Goede, Ilpo Järvinen, Armin Wolf, Gladyshev Ilya,
linux-kernel, Yuming Sun, Nabil Danial, Nika Krasnova, linux-doc,
Jonathan Corbet, Shuah Khan
Xiaomi Book Pro 14 2026 (DMI sys_vendor=XIAOMI, board_name=TM2424)
uses five cmd 0x08 bytes that do not fit the four-field mode_map:
Quiet=0x02, Turbo=0x03, Full-speed=0x04, Auto=0x09, Eco=0x0A
Add a TM2424 entry to the per-machine ops table. SET maps the three
platform_profile choices to Eco / Auto / Full-speed; GET aliases Quiet
and Turbo. Full-speed falls back to Turbo when firmware NACKs.
The reduced firmware does not implement hwmon, keyboard LED or the
gpu_mode/kb_mode/fan_boost attributes.
Map remaining TM2424 HID_EVENT20 keys (projection value 0, AI break
0x24, Mi button 0x25/0x26). Mic-mute 0x21 and settings 0x1B are
already in the merged keymap.
This supersedes the standalone TM2424 quirk
<20260816215825.289356-1-kento@kekto.ru>.
Tested on TM2424-77080: performance on AC holds ~50W package power.
acpi_listen shows HID_EVENT20 as wmi PNP0C14:00 notify 0x20 for the
mic, AI, projection, settings and Mi keys (see cover letter).
Signed-off-by: KentoNion <kento@kekto.ru>
---
This is a follow-up to Mingyou's v5, not a competing rewrite.
v5's four-field mode_map cannot express TM2424: five firmware bytes
(Quiet/Turbo/Full-speed/Auto/Eco) collapse to three platform_profile
choices, GET has aliases, and SET of performance must try Full-speed
then Turbo when firmware NACKs. The new ops callbacks stay NULL on
the Bitland/Redmi paths.
DMI is XIAOMI + TM2424. Mingyou's Xiaomi Book match is TIMI, so this
SKU would otherwise keep the Bitland 0..3 defaults and stay broken.
Yuming's 2/5 names on this machine are inverted (0x03 is Turbo, 0x09
is Auto, 0x0A is Eco). Please do not take that SET mapping for TM2424.
I did not include lid polling or EC fan tachs. On this BIOS the ACPI
EC still starts and Lid Switch already binds; ECON is an unresolved
External used only by _STA (not defined in DSDT or any SSDT). That
is an ACPI firmware bug, not a WMI one. Decoded excerpts and dmesg
are a reply on Yuming's 0/5, In-Reply-To Armin.
I also left the v5 suspend hook alone. Ilpo already nacked the
profile_valid pattern on Yuming 1/5. With this mapping GET no longer
returns unmapped cmd 0x08 bytes on TM2424, so the existing hook
should not abort sleep here. A global never-abort plus a status-word
check still belongs in a separate patch; please take Ilpo's union +
endianness nits there. This patch only looks at output.operation ==
0x80 on TM2424 SET (the accepted byte; same field Yuming packs as
0x8000) so Full-speed can fall back to Turbo.
One regression to watch when v5 is rebased: e0d6312578e1 turned some
redmi-wmi KE_IGNORE entries into KEY_KBDILLUMTOGGLE / KEY_PERFORMANCE
/ KEY_FN_ESC. v5 2/6 still KE_IGNOREs those; please carry Musaev's
mappings into the merged keymap in v6.
The LKP const-qualifier error on the old standalone quirk is obsolete
once v5's non-const DEVICE_ATTR is the base.
Tested on Xiaomi Book Pro 14 2026, SKU TM2424-77080, BIOS XMAPT4B0P0707.
acpi_listen on that machine (HID_EVENT20 is notify 0x20 on PNP0C14:00).
Volume and panel brightness are standard ACPI and do not go through
this driver. The WMI-backed keys with this keymap:
FN+F4 mic:
wmi PNP0C14:00 00000020 00000000
button/micmute MICMUTE 00000080 00000000 K
FN+F7 AI (make KEY_PROG1; break is KE_IGNORE):
wmi PNP0C14:00 00000020 00000000
button/prog1 PROG1 00000080 00000000 K
wmi PNP0C14:00 00000020 00000000
FN+F8 projection:
wmi PNP0C14:00 00000020 00000000
video/switchmode VMOD 00000080 00000000 K
FN+F9 settings (KEY_CONFIG, evdev only):
wmi PNP0C14:00 00000020 00000000
Mi button (make KEY_VENDOR; break is KE_IGNORE):
wmi PNP0C14:00 00000020 00000000
button/vendor VNDR 00000080 00000000 K
wmi PNP0C14:00 00000020 00000000
Raw session (including power/mute/volume/brightness):
button/power PBTN 00000080 00000000
button/power LNXPWRBN:00 00000080 00000001
button/mute MUTE 00000080 00000000 K
button/volumeup VOLUP 00000080 00000000 K
button/volumeup VOLUP 00000080 00000000 K
button/volumedown VOLDN 00000080 00000000 K
wmi PNP0C14:00 00000020 00000000
button/micmute MICMUTE 00000080 00000000 K
wmi PNP0C14:00 00000020 00000000
button/micmute MICMUTE 00000080 00000000 K
video/brightnessdown BRTDN 00000087 00000000
video/brightnessup BRTUP 00000086 00000000
wmi PNP0C14:00 00000020 00000000
button/prog1 PROG1 00000080 00000000 K
wmi PNP0C14:00 00000020 00000000
wmi PNP0C14:00 00000020 00000000
video/switchmode VMOD 00000080 00000000 K
wmi PNP0C14:00 00000020 00000000
wmi PNP0C14:00 00000020 00000000
wmi PNP0C14:00 00000020 00000000
wmi PNP0C14:00 00000020 00000000
button/vendor VNDR 00000080 00000000 K
wmi PNP0C14:00 00000020 00000000
diff --git a/Documentation/wmi/devices/bitland-mifs-wmi.rst b/Documentation/wmi/devices/bitland-mifs-wmi.rst
index 9e86ecc..a89f872 100644
--- a/Documentation/wmi/devices/bitland-mifs-wmi.rst
+++ b/Documentation/wmi/devices/bitland-mifs-wmi.rst
@@ -93,8 +93,13 @@ The following Command IDs are used in the third byte of the buffer:
+----------+-----------------------+------------------------------------------+
| ID | Name | Values / Description |
+==========+=======================+==========================================+
-| 8 | SystemPerMode | 0: Balance, 1: Performance, 2: Quiet, |
-| | | 3: Full-speed |
+| 8 | SystemPerMode | Bitland gaming: 0 Balance, 1 |
+| | | Performance, 2 Quiet, 3 Full-speed. |
+| | | Redmi: 0 Performance, 1 Balanced, 2 |
+| | | Quiet, 4 Full-speed. |
+| | | TM2424 (Xiaomi Book Pro 14): 0x02 |
+| | | Quiet, 0x03 Turbo, 0x04 Full-speed, |
+| | | 0x09 Auto, 0x0A Eco. |
+----------+-----------------------+------------------------------------------+
| 9 | GPUMode | 0: Hybrid, 1: Discrete, 2: UMA |
+----------+-----------------------+------------------------------------------+
@@ -188,11 +193,8 @@ Performance Modes
-----------------
Changing the performance mode via Command ID 0x08 (SystemPerMode) affects the
power limits (PL1/PL2) and fan curves managed by the Embedded Controller (EC).
-Note that the "Full-speed" and "Performance" mode (1, 3) is typically only
-available when the system is connected to a DC power source (not USB-C/PD).
-
-In the driver implementation, switch to performance/full-speed mode without
-DC power connected will throw the EOPNOTSUPP error.
+The value encoding and the AC-power requirement are machine-specific (see
+Command ID 8 and the driver ops table).
Graphics Switching
------------------
diff --git a/drivers/platform/x86/bitland-mifs-wmi.c b/drivers/platform/x86/bitland-mifs-wmi.c
index 6031b1c..b49a6fb 100644
--- a/drivers/platform/x86/bitland-mifs-wmi.c
+++ b/drivers/platform/x86/bitland-mifs-wmi.c
@@ -44,6 +44,9 @@
#define BITLAND_MIFS_GUID "B60BFB48-3E5B-49E4-A0E9-8CFFE1B3434B"
#define BITLAND_EVENT_GUID "46C93E13-EE9B-4262-8488-563BCA757FEF"
+/* Firmware status in output.operation (wire OutData[1]): request accepted. */
+#define MIFS_STATUS_OK 0x80
+
enum bitland_mifs_operation {
WMI_METHOD_GET = 250,
WMI_METHOD_SET = 251,
@@ -110,8 +113,21 @@ static const struct bitland_profile_mode_map redmi_mode_map = {
.full_speed = 4,
};
+/* TM2424 SystemPerMode bytes (Eco/Auto/Full-speed; Quiet/Turbo are GET aliases). */
+enum bitland_mifs_tm2424_perf_mode {
+ WMI_TM2424_QUIET = 0x02,
+ WMI_TM2424_TURBO = 0x03,
+ WMI_TM2424_FULL_SPEED = 0x04,
+ WMI_TM2424_AUTO = 0x09,
+ WMI_TM2424_ECO = 0x0A,
+};
+
static int bitland_pp_check_capability(struct bitland_mifs_wmi_data *data);
static int redmi_pp_check_capability(struct bitland_mifs_wmi_data *data);
+static int tm2424_profile_get(struct bitland_mifs_wmi_data *data,
+ enum platform_profile_option *profile);
+static int tm2424_profile_set(struct bitland_mifs_wmi_data *data,
+ enum platform_profile_option profile);
/*
* Machine-specific operations. The MIFS WMI interface is shared by
@@ -122,9 +138,15 @@ struct bitland_mifs_ops {
const char *name;
const struct bitland_profile_mode_map *mode_map;
int (*check_performance_capability)(struct bitland_mifs_wmi_data *data);
+ int (*profile_get)(struct bitland_mifs_wmi_data *data,
+ enum platform_profile_option *profile);
+ int (*profile_set)(struct bitland_mifs_wmi_data *data,
+ enum platform_profile_option profile);
bool has_full_speed;
bool has_cpu_power;
bool quirk_refresh_rate_toggle;
+ /* hwmon, keyboard LED and gpu/kb/fan sysfs are unimplemented */
+ bool reduced_fw;
};
static const struct bitland_mifs_ops bitland_ops = {
@@ -151,7 +173,22 @@ static const struct bitland_mifs_ops redmi_g_ops = {
.quirk_refresh_rate_toggle = true,
};
+static const struct bitland_mifs_ops tm2424_ops = {
+ .name = "Xiaomi Book Pro 14",
+ .profile_get = tm2424_profile_get,
+ .profile_set = tm2424_profile_set,
+ .reduced_fw = true,
+};
+
static const struct dmi_system_id bitland_mifs_dmi_table[] = {
+ {
+ /* Xiaomi Book Pro 14. Vendor is XIAOMI, not TIMI. */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "XIAOMI"),
+ DMI_MATCH(DMI_BOARD_NAME, "TM2424"),
+ },
+ .driver_data = (void *)&tm2424_ops,
+ },
{
/* Redmi G */
.matches = {
@@ -296,11 +333,89 @@ static int bitland_mifs_wmi_call(struct bitland_mifs_wmi_data *data,
return 0;
}
+static int tm2424_wmi_perf_set(struct bitland_mifs_wmi_data *data, u8 val)
+{
+ struct bitland_mifs_input input = {
+ .operation = WMI_METHOD_SET,
+ .function = WMI_FN_SYSTEM_PER_MODE,
+ };
+ struct bitland_mifs_output output;
+ int ret;
+
+ input.payload[0] = val;
+
+ ret = bitland_mifs_wmi_call(data, &input, &output);
+ if (ret)
+ return ret;
+
+ if (output.operation != MIFS_STATUS_OK) {
+ dev_dbg(&data->wdev->dev,
+ "perf mode %#x rejected by firmware (status=%#x)\n",
+ val, output.operation);
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int tm2424_profile_get(struct bitland_mifs_wmi_data *data,
+ enum platform_profile_option *profile)
+{
+ struct bitland_mifs_input input = {
+ .operation = WMI_METHOD_GET,
+ .function = WMI_FN_SYSTEM_PER_MODE,
+ };
+ struct bitland_mifs_output output;
+ int ret;
+
+ ret = bitland_mifs_wmi_call(data, &input, &output);
+ if (ret)
+ return ret;
+
+ switch (output.data[0]) {
+ case WMI_TM2424_ECO:
+ case WMI_TM2424_QUIET:
+ *profile = PLATFORM_PROFILE_LOW_POWER;
+ break;
+ case WMI_TM2424_AUTO:
+ *profile = PLATFORM_PROFILE_BALANCED;
+ break;
+ case WMI_TM2424_FULL_SPEED:
+ case WMI_TM2424_TURBO:
+ *profile = PLATFORM_PROFILE_PERFORMANCE;
+ break;
+ default:
+ dev_dbg(&data->wdev->dev,
+ "unrecognized TM2424 perf mode %#x\n", output.data[0]);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tm2424_profile_set(struct bitland_mifs_wmi_data *data,
+ enum platform_profile_option profile)
+{
+ switch (profile) {
+ case PLATFORM_PROFILE_LOW_POWER:
+ return tm2424_wmi_perf_set(data, WMI_TM2424_ECO);
+ case PLATFORM_PROFILE_BALANCED:
+ return tm2424_wmi_perf_set(data, WMI_TM2424_AUTO);
+ case PLATFORM_PROFILE_PERFORMANCE:
+ /* Full-speed is rejected on battery; fall back to Turbo. */
+ if (tm2424_wmi_perf_set(data, WMI_TM2424_FULL_SPEED) == 0)
+ return 0;
+ return tm2424_wmi_perf_set(data, WMI_TM2424_TURBO);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
static int laptop_profile_get(struct device *dev,
enum platform_profile_option *profile)
{
struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev);
- const struct bitland_profile_mode_map *map = data->ops->mode_map;
+ const struct bitland_profile_mode_map *map;
struct bitland_mifs_input input = {
.reserved1 = 0,
.operation = WMI_METHOD_GET,
@@ -311,6 +426,10 @@ static int laptop_profile_get(struct device *dev,
u8 mode;
int ret;
+ if (data->ops->profile_get)
+ return data->ops->profile_get(data, profile);
+
+ map = data->ops->mode_map;
ret = bitland_mifs_wmi_call(data, &input, &result);
if (ret)
return ret;
@@ -369,7 +488,7 @@ static int laptop_profile_set(struct device *dev,
enum platform_profile_option profile)
{
struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev);
- const struct bitland_profile_mode_map *map = data->ops->mode_map;
+ const struct bitland_profile_mode_map *map;
struct bitland_mifs_input input = {
.reserved1 = 0,
.operation = WMI_METHOD_SET,
@@ -379,6 +498,11 @@ static int laptop_profile_set(struct device *dev,
int ret;
u8 mode;
+ if (data->ops->profile_set)
+ return data->ops->profile_set(data, profile);
+
+ map = data->ops->mode_map;
+
switch (profile) {
case PLATFORM_PROFILE_LOW_POWER:
mode = map->quiet;
@@ -897,6 +1021,7 @@ static const struct key_entry bitland_mifs_wmi_keymap[] = {
{ KE_KEY, BI_HOTKEY_CODE(WMI_EVENT_RESERVED_3, 0, 0), { KEY_ALL_APPLICATIONS } },
{ KE_KEY, BI_HOTKEY_CODE(0x1b, 0, 0), { KEY_CONFIG } },
{ KE_KEY, BI_HOTKEY_CODE(0x1b, 1, 0), { KEY_CONFIG } },
+ { KE_KEY, BI_HOTKEY_CODE(WMI_EVENT_RESERVED_1, 0, 0), { KEY_SWITCHVIDEOMODE } },
{ KE_KEY, BI_HOTKEY_CODE(WMI_EVENT_RESERVED_1, 1, 0), { KEY_SWITCHVIDEOMODE } },
{ KE_KEY, BI_HOTKEY_CODE(WMI_EVENT_REFRESH_RATE, 0, 0), { KEY_REFRESH_RATE_TOGGLE } },
@@ -908,6 +1033,12 @@ static const struct key_entry bitland_mifs_wmi_keymap[] = {
{ KE_KEY, BI_HOTKEY_CODE(WMI_EVENT_FN_5, 1, 0), { KEY_ASSISTANT } },
{ KE_KEY, BI_HOTKEY_CODE(0x19, 1, 0), { KEY_ASSISTANT } },
+ /* TM2424 AI break event (make is 0x23 / WMI_EVENT_OPEN_APP) */
+ { KE_IGNORE, BI_HOTKEY_CODE(0x24, 1, 0), {} },
+
+ /* TM2424 Mi button: make/break pair */
+ { KE_KEY, BI_HOTKEY_CODE(0x25, 1, 0), { KEY_VENDOR } },
+ { KE_IGNORE, BI_HOTKEY_CODE(0x26, 1, 0), {} },
{ KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_KBD_BRIGHTNESS, 0, 0), {} },
{ KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_KBD_BRIGHTNESS, 0, 0x80), {} },
@@ -1085,6 +1216,9 @@ 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 (drv_data->ops->reduced_fw)
+ return 0;
+
/* Detect the keyboard type */
ret = bitland_detect_kbd_type(drv_data);
if (ret)
@@ -1243,5 +1377,6 @@ static struct wmi_driver bitland_mifs_wmi_driver = {
module_wmi_driver(bitland_mifs_wmi_driver);
MODULE_AUTHOR("Mingyou Chen <qby140326@gmail.com>");
+MODULE_AUTHOR("KentoNion <kento@kekto.ru>");
MODULE_DESCRIPTION("Bitland MIFS (MiInterface) WMI driver");
MODULE_LICENSE("GPL");
--
2.55.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH] platform/x86: bitland-mifs-wmi: add TM2424 ops and hotkeys
2026-08-16 10:08 [PATCH v5 0/6] Merge redmi-wmi into bitland-mifs-wmi Mingyou Chen
` (6 preceding siblings ...)
2026-08-26 13:26 ` [PATCH] platform/x86: bitland-mifs-wmi: add TM2424 ops and hotkeys KentoNion
@ 2026-08-26 13:29 ` KentoNion
7 siblings, 0 replies; 13+ messages in thread
From: KentoNion @ 2026-08-26 13:29 UTC (permalink / raw)
To: platform-driver-x86, Mingyou Chen
Cc: Hans de Goede, Ilpo Järvinen, Armin Wolf, Gladyshev Ilya,
linux-kernel, Nabil Danial, Nika Krasnova, linux-doc,
Jonathan Corbet, Shuah Khan
Xiaomi Book Pro 14 2026 (DMI sys_vendor=XIAOMI, board_name=TM2424)
uses five cmd 0x08 bytes that do not fit the four-field mode_map:
Quiet=0x02, Turbo=0x03, Full-speed=0x04, Auto=0x09, Eco=0x0A
Add a TM2424 entry to the per-machine ops table. SET maps the three
platform_profile choices to Eco / Auto / Full-speed; GET aliases Quiet
and Turbo. Full-speed falls back to Turbo when firmware NACKs.
The reduced firmware does not implement hwmon, keyboard LED or the
gpu_mode/kb_mode/fan_boost attributes.
Map remaining TM2424 HID_EVENT20 keys (projection value 0, AI break
0x24, Mi button 0x25/0x26). Mic-mute 0x21 and settings 0x1B are
already in the merged keymap.
This supersedes the standalone TM2424 quirk
<20260816215825.289356-1-kento@kekto.ru>.
Tested on TM2424-77080: performance on AC holds ~50W package power.
acpi_listen shows HID_EVENT20 as wmi PNP0C14:00 notify 0x20 for the
mic, AI, projection, settings and Mi keys (see cover letter).
Signed-off-by: KentoNion <kento@kekto.ru>
---
This is a follow-up to Mingyou's v5, not a competing rewrite.
v5's four-field mode_map cannot express TM2424: five firmware bytes
(Quiet/Turbo/Full-speed/Auto/Eco) collapse to three platform_profile
choices, GET has aliases, and SET of performance must try Full-speed
then Turbo when firmware NACKs. The new ops callbacks stay NULL on
the Bitland/Redmi paths.
DMI is XIAOMI + TM2424. Mingyou's Xiaomi Book match is TIMI, so this
SKU would otherwise keep the Bitland 0..3 defaults and stay broken.
Yuming's 2/5 names on this machine are inverted (0x03 is Turbo, 0x09
is Auto, 0x0A is Eco). Please do not take that SET mapping for TM2424.
I did not include lid polling or EC fan tachs. On this BIOS the ACPI
EC still starts and Lid Switch already binds; ECON is an unresolved
External used only by _STA (not defined in DSDT or any SSDT). That
is an ACPI firmware bug, not a WMI one. Decoded excerpts and dmesg
are a reply on Yuming's 0/5, In-Reply-To Armin.
I also left the v5 suspend hook alone. Ilpo already nacked the
profile_valid pattern on Yuming 1/5. With this mapping GET no longer
returns unmapped cmd 0x08 bytes on TM2424, so the existing hook
should not abort sleep here. A global never-abort plus a status-word
check still belongs in a separate patch; please take Ilpo's union +
endianness nits there. This patch only looks at output.operation ==
0x80 on TM2424 SET (the accepted byte; same field Yuming packs as
0x8000) so Full-speed can fall back to Turbo.
One regression to watch when v5 is rebased: e0d6312578e1 turned some
redmi-wmi KE_IGNORE entries into KEY_KBDILLUMTOGGLE / KEY_PERFORMANCE
/ KEY_FN_ESC. v5 2/6 still KE_IGNOREs those; please carry Musaev's
mappings into the merged keymap in v6.
The LKP const-qualifier error on the old standalone quirk is obsolete
once v5's non-const DEVICE_ATTR is the base.
Tested on Xiaomi Book Pro 14 2026, SKU TM2424-77080, BIOS XMAPT4B0P0707.
acpi_listen on that machine (HID_EVENT20 is notify 0x20 on PNP0C14:00).
Volume and panel brightness are standard ACPI and do not go through
this driver. The WMI-backed keys with this keymap:
FN+F4 mic:
wmi PNP0C14:00 00000020 00000000
button/micmute MICMUTE 00000080 00000000 K
FN+F7 AI (make KEY_PROG1; break is KE_IGNORE):
wmi PNP0C14:00 00000020 00000000
button/prog1 PROG1 00000080 00000000 K
wmi PNP0C14:00 00000020 00000000
FN+F8 projection:
wmi PNP0C14:00 00000020 00000000
video/switchmode VMOD 00000080 00000000 K
FN+F9 settings (KEY_CONFIG, evdev only):
wmi PNP0C14:00 00000020 00000000
Mi button (make KEY_VENDOR; break is KE_IGNORE):
wmi PNP0C14:00 00000020 00000000
button/vendor VNDR 00000080 00000000 K
wmi PNP0C14:00 00000020 00000000
Raw session (including power/mute/volume/brightness):
button/power PBTN 00000080 00000000
button/power LNXPWRBN:00 00000080 00000001
button/mute MUTE 00000080 00000000 K
button/volumeup VOLUP 00000080 00000000 K
button/volumeup VOLUP 00000080 00000000 K
button/volumedown VOLDN 00000080 00000000 K
wmi PNP0C14:00 00000020 00000000
button/micmute MICMUTE 00000080 00000000 K
wmi PNP0C14:00 00000020 00000000
button/micmute MICMUTE 00000080 00000000 K
video/brightnessdown BRTDN 00000087 00000000
video/brightnessup BRTUP 00000086 00000000
wmi PNP0C14:00 00000020 00000000
button/prog1 PROG1 00000080 00000000 K
wmi PNP0C14:00 00000020 00000000
wmi PNP0C14:00 00000020 00000000
video/switchmode VMOD 00000080 00000000 K
wmi PNP0C14:00 00000020 00000000
wmi PNP0C14:00 00000020 00000000
wmi PNP0C14:00 00000020 00000000
wmi PNP0C14:00 00000020 00000000
button/vendor VNDR 00000080 00000000 K
wmi PNP0C14:00 00000020 00000000
diff --git a/Documentation/wmi/devices/bitland-mifs-wmi.rst b/Documentation/wmi/devices/bitland-mifs-wmi.rst
index 9e86ecc..a89f872 100644
--- a/Documentation/wmi/devices/bitland-mifs-wmi.rst
+++ b/Documentation/wmi/devices/bitland-mifs-wmi.rst
@@ -93,8 +93,13 @@ The following Command IDs are used in the third byte of the buffer:
+----------+-----------------------+------------------------------------------+
| ID | Name | Values / Description |
+==========+=======================+==========================================+
-| 8 | SystemPerMode | 0: Balance, 1: Performance, 2: Quiet, |
-| | | 3: Full-speed |
+| 8 | SystemPerMode | Bitland gaming: 0 Balance, 1 |
+| | | Performance, 2 Quiet, 3 Full-speed. |
+| | | Redmi: 0 Performance, 1 Balanced, 2 |
+| | | Quiet, 4 Full-speed. |
+| | | TM2424 (Xiaomi Book Pro 14): 0x02 |
+| | | Quiet, 0x03 Turbo, 0x04 Full-speed, |
+| | | 0x09 Auto, 0x0A Eco. |
+----------+-----------------------+------------------------------------------+
| 9 | GPUMode | 0: Hybrid, 1: Discrete, 2: UMA |
+----------+-----------------------+------------------------------------------+
@@ -188,11 +193,8 @@ Performance Modes
-----------------
Changing the performance mode via Command ID 0x08 (SystemPerMode) affects the
power limits (PL1/PL2) and fan curves managed by the Embedded Controller (EC).
-Note that the "Full-speed" and "Performance" mode (1, 3) is typically only
-available when the system is connected to a DC power source (not USB-C/PD).
-
-In the driver implementation, switch to performance/full-speed mode without
-DC power connected will throw the EOPNOTSUPP error.
+The value encoding and the AC-power requirement are machine-specific (see
+Command ID 8 and the driver ops table).
Graphics Switching
------------------
diff --git a/drivers/platform/x86/bitland-mifs-wmi.c b/drivers/platform/x86/bitland-mifs-wmi.c
index 6031b1c..b49a6fb 100644
--- a/drivers/platform/x86/bitland-mifs-wmi.c
+++ b/drivers/platform/x86/bitland-mifs-wmi.c
@@ -44,6 +44,9 @@
#define BITLAND_MIFS_GUID "B60BFB48-3E5B-49E4-A0E9-8CFFE1B3434B"
#define BITLAND_EVENT_GUID "46C93E13-EE9B-4262-8488-563BCA757FEF"
+/* Firmware status in output.operation (wire OutData[1]): request accepted. */
+#define MIFS_STATUS_OK 0x80
+
enum bitland_mifs_operation {
WMI_METHOD_GET = 250,
WMI_METHOD_SET = 251,
@@ -110,8 +113,21 @@ static const struct bitland_profile_mode_map redmi_mode_map = {
.full_speed = 4,
};
+/* TM2424 SystemPerMode bytes (Eco/Auto/Full-speed; Quiet/Turbo are GET aliases). */
+enum bitland_mifs_tm2424_perf_mode {
+ WMI_TM2424_QUIET = 0x02,
+ WMI_TM2424_TURBO = 0x03,
+ WMI_TM2424_FULL_SPEED = 0x04,
+ WMI_TM2424_AUTO = 0x09,
+ WMI_TM2424_ECO = 0x0A,
+};
+
static int bitland_pp_check_capability(struct bitland_mifs_wmi_data *data);
static int redmi_pp_check_capability(struct bitland_mifs_wmi_data *data);
+static int tm2424_profile_get(struct bitland_mifs_wmi_data *data,
+ enum platform_profile_option *profile);
+static int tm2424_profile_set(struct bitland_mifs_wmi_data *data,
+ enum platform_profile_option profile);
/*
* Machine-specific operations. The MIFS WMI interface is shared by
@@ -122,9 +138,15 @@ struct bitland_mifs_ops {
const char *name;
const struct bitland_profile_mode_map *mode_map;
int (*check_performance_capability)(struct bitland_mifs_wmi_data *data);
+ int (*profile_get)(struct bitland_mifs_wmi_data *data,
+ enum platform_profile_option *profile);
+ int (*profile_set)(struct bitland_mifs_wmi_data *data,
+ enum platform_profile_option profile);
bool has_full_speed;
bool has_cpu_power;
bool quirk_refresh_rate_toggle;
+ /* hwmon, keyboard LED and gpu/kb/fan sysfs are unimplemented */
+ bool reduced_fw;
};
static const struct bitland_mifs_ops bitland_ops = {
@@ -151,7 +173,22 @@ static const struct bitland_mifs_ops redmi_g_ops = {
.quirk_refresh_rate_toggle = true,
};
+static const struct bitland_mifs_ops tm2424_ops = {
+ .name = "Xiaomi Book Pro 14",
+ .profile_get = tm2424_profile_get,
+ .profile_set = tm2424_profile_set,
+ .reduced_fw = true,
+};
+
static const struct dmi_system_id bitland_mifs_dmi_table[] = {
+ {
+ /* Xiaomi Book Pro 14. Vendor is XIAOMI, not TIMI. */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "XIAOMI"),
+ DMI_MATCH(DMI_BOARD_NAME, "TM2424"),
+ },
+ .driver_data = (void *)&tm2424_ops,
+ },
{
/* Redmi G */
.matches = {
@@ -296,11 +333,89 @@ static int bitland_mifs_wmi_call(struct bitland_mifs_wmi_data *data,
return 0;
}
+static int tm2424_wmi_perf_set(struct bitland_mifs_wmi_data *data, u8 val)
+{
+ struct bitland_mifs_input input = {
+ .operation = WMI_METHOD_SET,
+ .function = WMI_FN_SYSTEM_PER_MODE,
+ };
+ struct bitland_mifs_output output;
+ int ret;
+
+ input.payload[0] = val;
+
+ ret = bitland_mifs_wmi_call(data, &input, &output);
+ if (ret)
+ return ret;
+
+ if (output.operation != MIFS_STATUS_OK) {
+ dev_dbg(&data->wdev->dev,
+ "perf mode %#x rejected by firmware (status=%#x)\n",
+ val, output.operation);
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int tm2424_profile_get(struct bitland_mifs_wmi_data *data,
+ enum platform_profile_option *profile)
+{
+ struct bitland_mifs_input input = {
+ .operation = WMI_METHOD_GET,
+ .function = WMI_FN_SYSTEM_PER_MODE,
+ };
+ struct bitland_mifs_output output;
+ int ret;
+
+ ret = bitland_mifs_wmi_call(data, &input, &output);
+ if (ret)
+ return ret;
+
+ switch (output.data[0]) {
+ case WMI_TM2424_ECO:
+ case WMI_TM2424_QUIET:
+ *profile = PLATFORM_PROFILE_LOW_POWER;
+ break;
+ case WMI_TM2424_AUTO:
+ *profile = PLATFORM_PROFILE_BALANCED;
+ break;
+ case WMI_TM2424_FULL_SPEED:
+ case WMI_TM2424_TURBO:
+ *profile = PLATFORM_PROFILE_PERFORMANCE;
+ break;
+ default:
+ dev_dbg(&data->wdev->dev,
+ "unrecognized TM2424 perf mode %#x\n", output.data[0]);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tm2424_profile_set(struct bitland_mifs_wmi_data *data,
+ enum platform_profile_option profile)
+{
+ switch (profile) {
+ case PLATFORM_PROFILE_LOW_POWER:
+ return tm2424_wmi_perf_set(data, WMI_TM2424_ECO);
+ case PLATFORM_PROFILE_BALANCED:
+ return tm2424_wmi_perf_set(data, WMI_TM2424_AUTO);
+ case PLATFORM_PROFILE_PERFORMANCE:
+ /* Full-speed is rejected on battery; fall back to Turbo. */
+ if (tm2424_wmi_perf_set(data, WMI_TM2424_FULL_SPEED) == 0)
+ return 0;
+ return tm2424_wmi_perf_set(data, WMI_TM2424_TURBO);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
static int laptop_profile_get(struct device *dev,
enum platform_profile_option *profile)
{
struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev);
- const struct bitland_profile_mode_map *map = data->ops->mode_map;
+ const struct bitland_profile_mode_map *map;
struct bitland_mifs_input input = {
.reserved1 = 0,
.operation = WMI_METHOD_GET,
@@ -311,6 +426,10 @@ static int laptop_profile_get(struct device *dev,
u8 mode;
int ret;
+ if (data->ops->profile_get)
+ return data->ops->profile_get(data, profile);
+
+ map = data->ops->mode_map;
ret = bitland_mifs_wmi_call(data, &input, &result);
if (ret)
return ret;
@@ -369,7 +488,7 @@ static int laptop_profile_set(struct device *dev,
enum platform_profile_option profile)
{
struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev);
- const struct bitland_profile_mode_map *map = data->ops->mode_map;
+ const struct bitland_profile_mode_map *map;
struct bitland_mifs_input input = {
.reserved1 = 0,
.operation = WMI_METHOD_SET,
@@ -379,6 +498,11 @@ static int laptop_profile_set(struct device *dev,
int ret;
u8 mode;
+ if (data->ops->profile_set)
+ return data->ops->profile_set(data, profile);
+
+ map = data->ops->mode_map;
+
switch (profile) {
case PLATFORM_PROFILE_LOW_POWER:
mode = map->quiet;
@@ -897,6 +1021,7 @@ static const struct key_entry bitland_mifs_wmi_keymap[] = {
{ KE_KEY, BI_HOTKEY_CODE(WMI_EVENT_RESERVED_3, 0, 0), { KEY_ALL_APPLICATIONS } },
{ KE_KEY, BI_HOTKEY_CODE(0x1b, 0, 0), { KEY_CONFIG } },
{ KE_KEY, BI_HOTKEY_CODE(0x1b, 1, 0), { KEY_CONFIG } },
+ { KE_KEY, BI_HOTKEY_CODE(WMI_EVENT_RESERVED_1, 0, 0), { KEY_SWITCHVIDEOMODE } },
{ KE_KEY, BI_HOTKEY_CODE(WMI_EVENT_RESERVED_1, 1, 0), { KEY_SWITCHVIDEOMODE } },
{ KE_KEY, BI_HOTKEY_CODE(WMI_EVENT_REFRESH_RATE, 0, 0), { KEY_REFRESH_RATE_TOGGLE } },
@@ -908,6 +1033,12 @@ static const struct key_entry bitland_mifs_wmi_keymap[] = {
{ KE_KEY, BI_HOTKEY_CODE(WMI_EVENT_FN_5, 1, 0), { KEY_ASSISTANT } },
{ KE_KEY, BI_HOTKEY_CODE(0x19, 1, 0), { KEY_ASSISTANT } },
+ /* TM2424 AI break event (make is 0x23 / WMI_EVENT_OPEN_APP) */
+ { KE_IGNORE, BI_HOTKEY_CODE(0x24, 1, 0), {} },
+
+ /* TM2424 Mi button: make/break pair */
+ { KE_KEY, BI_HOTKEY_CODE(0x25, 1, 0), { KEY_VENDOR } },
+ { KE_IGNORE, BI_HOTKEY_CODE(0x26, 1, 0), {} },
{ KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_KBD_BRIGHTNESS, 0, 0), {} },
{ KE_IGNORE, BI_HOTKEY_CODE(WMI_EVENT_KBD_BRIGHTNESS, 0, 0x80), {} },
@@ -1085,6 +1216,9 @@ 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 (drv_data->ops->reduced_fw)
+ return 0;
+
/* Detect the keyboard type */
ret = bitland_detect_kbd_type(drv_data);
if (ret)
@@ -1243,5 +1377,6 @@ static struct wmi_driver bitland_mifs_wmi_driver = {
module_wmi_driver(bitland_mifs_wmi_driver);
MODULE_AUTHOR("Mingyou Chen <qby140326@gmail.com>");
+MODULE_AUTHOR("KentoNion <kento@kekto.ru>");
MODULE_DESCRIPTION("Bitland MIFS (MiInterface) WMI driver");
MODULE_LICENSE("GPL");
--
2.55.0
^ permalink raw reply related [flat|nested] 13+ messages in thread