* [PATCH v3 1/6] MAINTAINERS: Add maintainer entry of bitland-mifs-wmi driver
2026-08-15 13:25 [PATCH v3 0/6] Merge redmi-wmi into bitland-mifs-wmi Mingyou Chen
@ 2026-08-15 13:25 ` Mingyou Chen
2026-08-15 13:25 ` [PATCH v3 2/6] platform/x86: bitland-mifs-wmi: Merge the function of redmi-wmi into the bitland driver Mingyou Chen
` (4 subsequent siblings)
5 siblings, 0 replies; 8+ messages in thread
From: Mingyou Chen @ 2026-08-15 13:25 UTC (permalink / raw)
To: qby140326
Cc: W_Armin, foxido, hansg, ilpo.jarvinen, linux-kernel, nika,
platform-driver-x86, vlku.milos.fun, i, wolf109909
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 [flat|nested] 8+ messages in thread* [PATCH v3 2/6] platform/x86: bitland-mifs-wmi: Merge the function of redmi-wmi into the bitland driver
2026-08-15 13:25 [PATCH v3 0/6] Merge redmi-wmi into bitland-mifs-wmi Mingyou Chen
2026-08-15 13:25 ` [PATCH v3 1/6] MAINTAINERS: Add maintainer entry of bitland-mifs-wmi driver Mingyou Chen
@ 2026-08-15 13:25 ` Mingyou Chen
2026-08-15 13:25 ` [PATCH v3 3/6] platform/x86: bitland-mifs-wmi: Add Redmi mic-mute key entries Mingyou Chen
` (3 subsequent siblings)
5 siblings, 0 replies; 8+ messages in thread
From: Mingyou Chen @ 2026-08-15 13:25 UTC (permalink / raw)
To: qby140326
Cc: W_Armin, foxido, hansg, ilpo.jarvinen, linux-kernel, nika,
platform-driver-x86, vlku.milos.fun, i, wolf109909
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 | 141 ++++++++++++++++--------
1 file changed, 97 insertions(+), 44 deletions(-)
diff --git a/drivers/platform/x86/bitland-mifs-wmi.c b/drivers/platform/x86/bitland-mifs-wmi.c
index b0d06a80e89e..677e08430473 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,31 +821,28 @@ 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),
* but there are only the CPU and GPU fan has events
*/
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 [flat|nested] 8+ messages in thread* [PATCH v3 3/6] platform/x86: bitland-mifs-wmi: Add Redmi mic-mute key entries
2026-08-15 13:25 [PATCH v3 0/6] Merge redmi-wmi into bitland-mifs-wmi Mingyou Chen
2026-08-15 13:25 ` [PATCH v3 1/6] MAINTAINERS: Add maintainer entry of bitland-mifs-wmi driver Mingyou Chen
2026-08-15 13:25 ` [PATCH v3 2/6] platform/x86: bitland-mifs-wmi: Merge the function of redmi-wmi into the bitland driver Mingyou Chen
@ 2026-08-15 13:25 ` Mingyou Chen
2026-08-15 13:25 ` [PATCH v3 4/6] platform/x86: redmi-wmi: Drop redmi-wmi driver Mingyou Chen
` (2 subsequent siblings)
5 siblings, 0 replies; 8+ messages in thread
From: Mingyou Chen @ 2026-08-15 13:25 UTC (permalink / raw)
To: qby140326
Cc: W_Armin, foxido, hansg, ilpo.jarvinen, linux-kernel, nika,
platform-driver-x86, vlku.milos.fun, i, wolf109909
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 677e08430473..179e72516c71 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 [flat|nested] 8+ messages in thread* [PATCH v3 4/6] platform/x86: redmi-wmi: Drop redmi-wmi driver
2026-08-15 13:25 [PATCH v3 0/6] Merge redmi-wmi into bitland-mifs-wmi Mingyou Chen
` (2 preceding siblings ...)
2026-08-15 13:25 ` [PATCH v3 3/6] platform/x86: bitland-mifs-wmi: Add Redmi mic-mute key entries Mingyou Chen
@ 2026-08-15 13:25 ` Mingyou Chen
2026-08-15 13:25 ` [PATCH v3 5/6] platform/x86: bitland-mifs-wmi: Add per-machine ops table Mingyou Chen
2026-08-15 13:25 ` [PATCH v3 6/6] platform/x86: bitland-mifs-wmi: Add Redmi laptop support Mingyou Chen
5 siblings, 0 replies; 8+ messages in thread
From: Mingyou Chen @ 2026-08-15 13:25 UTC (permalink / raw)
To: qby140326
Cc: W_Armin, foxido, hansg, ilpo.jarvinen, linux-kernel, nika,
platform-driver-x86, vlku.milos.fun, i, wolf109909
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 [flat|nested] 8+ messages in thread* [PATCH v3 5/6] platform/x86: bitland-mifs-wmi: Add per-machine ops table
2026-08-15 13:25 [PATCH v3 0/6] Merge redmi-wmi into bitland-mifs-wmi Mingyou Chen
` (3 preceding siblings ...)
2026-08-15 13:25 ` [PATCH v3 4/6] platform/x86: redmi-wmi: Drop redmi-wmi driver Mingyou Chen
@ 2026-08-15 13:25 ` Mingyou Chen
2026-08-15 16:00 ` Rsplwe
2026-08-15 13:25 ` [PATCH v3 6/6] platform/x86: bitland-mifs-wmi: Add Redmi laptop support Mingyou Chen
5 siblings, 1 reply; 8+ messages in thread
From: Mingyou Chen @ 2026-08-15 13:25 UTC (permalink / raw)
To: qby140326
Cc: W_Armin, foxido, hansg, ilpo.jarvinen, linux-kernel, nika,
platform-driver-x86, vlku.milos.fun, i, wolf109909
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 179e72516c71..39824bba49fe 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 [flat|nested] 8+ messages in thread* Re: [PATCH v3 5/6] platform/x86: bitland-mifs-wmi: Add per-machine ops table
2026-08-15 13:25 ` [PATCH v3 5/6] platform/x86: bitland-mifs-wmi: Add per-machine ops table Mingyou Chen
@ 2026-08-15 16:00 ` Rsplwe
0 siblings, 0 replies; 8+ messages in thread
From: Rsplwe @ 2026-08-15 16:00 UTC (permalink / raw)
To: qby140326
Cc: W_Armin, foxido, hansg, i, ilpo.jarvinen, linux-kernel, nika,
platform-driver-x86, vlku.milos.fun, wolf109909
Hi,
Regarding the handling of `has_full_speed`: when a machine does not have a Full Speed mode, `PLATFORM_PROFILE_PERFORMANCE` should still be exposed if the machine supports the Performance mode.
In other words, the absence of Full Speed should not make `BALANCED_PERFORMANCE` the highest available profile. For machines without Full Speed, the expected profile choices should be: `low-power` / `balanced` / `performance`
where `PLATFORM_PROFILE_PERFORMANCE` maps to the machine's highest available performance mode.
Therefore, `has_full_speed` should probably distinguish whether the machine has a separate Full Speed mode, while the profile choice logic should ensure that `PLATFORM_PROFILE_PERFORMANCE` remains available when Performance is the highest supported mode.
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v3 6/6] platform/x86: bitland-mifs-wmi: Add Redmi laptop support
2026-08-15 13:25 [PATCH v3 0/6] Merge redmi-wmi into bitland-mifs-wmi Mingyou Chen
` (4 preceding siblings ...)
2026-08-15 13:25 ` [PATCH v3 5/6] platform/x86: bitland-mifs-wmi: Add per-machine ops table Mingyou Chen
@ 2026-08-15 13:25 ` Mingyou Chen
5 siblings, 0 replies; 8+ messages in thread
From: Mingyou Chen @ 2026-08-15 13:25 UTC (permalink / raw)
To: qby140326
Cc: W_Armin, foxido, hansg, ilpo.jarvinen, linux-kernel, nika,
platform-driver-x86, vlku.milos.fun, i, wolf109909
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 | 329 +++++++++++++++++++++++-
1 file changed, 316 insertions(+), 13 deletions(-)
diff --git a/drivers/platform/x86/bitland-mifs-wmi.c b/drivers/platform/x86/bitland-mifs-wmi.c
index 39824bba49fe..d5b53008bff0 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,21 @@ static const struct bitland_profile_mode_map bitland_mode_map = {
.full_speed = WMI_PP_FULL_SPEED,
};
+/*
+ * Redmi laptops encode the performance mode values differently, as
+ * reverse engineered from the Windows GCommandCenter application: the
+ * app sends value 0 to select the performance mode and value 4 to
+ * select the full-speed mode.
+ */
+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 +129,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 +140,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, "Xiaomi"),
+ },
+ .driver_data = (void *)&redmi_book_ops,
+ },
+ {}
+};
+
enum bitland_mifs_event_id {
WMI_EVENT_RESERVED_1 = 1,
WMI_EVENT_RESERVED_2 = 2,
@@ -203,6 +273,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;
};
@@ -273,10 +345,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 +360,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)
{
@@ -337,6 +419,7 @@ static int platform_profile_probe(void *drvdata, unsigned long *choices)
set_bit(PLATFORM_PROFILE_BALANCED, choices);
set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, choices);
+ /* Full-speed mode is not available on regular Redmi Books */
if (data->ops->has_full_speed)
set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
@@ -418,6 +501,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 +534,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 +554,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 +602,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 +831,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 +922,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 +995,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 +1042,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 +1053,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 +1080,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 +1153,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 +1188,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 +1227,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] 8+ messages in thread