Re: [BUG] bitland-mifs-wmi: Power profiles not working on Xiaomi Mi NoteBook Ultra

Bheda Rahul <[email protected]>
Newsgroups org.kernel.vger.platform-driver-x86
Message-ID <CAEX=4377O9ce6r8OOo5r_9Pyf4TRfMWga4oHSsnH02npqgEjJw@mail.gmail.com>
so this patch works for me this model only support EV20() method.
timi_2017_fix.patch (text/x-patch, 6.2 KB)
diff --git a/drivers/platform/x86/bitland-mifs-wmi.c b/drivers/platform/x86/bitland-mifs-wmi.c
index 6031b1cef..e8d2059ed 100644
--- a/drivers/platform/x86/bitland-mifs-wmi.c
+++ b/drivers/platform/x86/bitland-mifs-wmi.c
@@ -11,6 +11,7 @@
 #include <linux/array_size.h>
 #include <linux/bits.h>
 #include <linux/container_of.h>
+#include <linux/delay.h>
 #include <linux/dev_printk.h>
 #include <linux/device.h>
 #include <linux/device/devres.h>
@@ -125,6 +126,8 @@ struct bitland_mifs_ops {
 	bool has_full_speed;
 	bool has_cpu_power;
 	bool quirk_refresh_rate_toggle;
+	int (*custom_profile_get)(struct bitland_mifs_wmi_data *data, u8 *mode);
+	int (*custom_profile_set)(struct bitland_mifs_wmi_data *data, u8 mode);
 };
 
 static const struct bitland_mifs_ops bitland_ops = {
@@ -151,7 +154,101 @@ static const struct bitland_mifs_ops redmi_g_ops = {
 	.quirk_refresh_rate_toggle = true,
 };
 
+/*
+ * TM2017 quirk: The WMI method 0x08 is a dummy stub on this firmware.
+ * Instead, use the native ACPI methods QV20/EV20/FUNR to control
+ * performance profiles via the Embedded Controller.
+ *
+ * ACPI method signatures (from ssdt12.dsl):
+ *   FUNR(0x16)  → reads EC register STNM, returns current mode (0/1/2)
+ *   QV20(0x16)  → sets EVFN=0x16 and fires Notify(WMID, 0x20)
+ *   EV20()      → reads FUNR(0x16), computes next state, writes ECWT(val, RefOf(STNM))
+ *
+ * State machine (EV20):
+ *   0 (Balanced)    → 2 (Quiet)
+ *   1 (Performance) → 2 (Quiet) [if MITE==0] or 0 (Balanced) [if MITE==1]
+ *   2 (Quiet)       → 1 (Performance)
+ */
+
+/* ACPI paths */
+#define TM2017_ACPI_QV20	"\\_SB.PC00.WMID.QV20"
+#define TM2017_ACPI_EV20	"\\_SB.PC00.WMID.EV20"
+#define TM2017_ACPI_FUNR	"\\_SB.PC00.LPCB.H_EC.FUNR"
+
+/* Maximum cycle iterations to prevent infinite loops */
+#define TM2017_MAX_CYCLES	4
+
+static int tm2017_acpi_read_mode(void)
+{
+	acpi_status status;
+	unsigned long long result;
+	struct acpi_object_list params;
+	union acpi_object arg;
+
+	arg.type = ACPI_TYPE_INTEGER;
+	arg.integer.value = 0x16;
+
+	params.count = 1;
+	params.pointer = &arg;
+
+	status = acpi_evaluate_integer(NULL, TM2017_ACPI_FUNR, &params, &result);
+	if (ACPI_FAILURE(status))
+		return -EIO;
+
+	return (int)result;
+}
+
+static int tm2017_acpi_cycle_mode(void)
+{
+	acpi_status status;
+	struct acpi_object_list params;
+	union acpi_object arg;
+
+	/* Step 1: QV20(0x16) — queue the Fn+K event */
+	arg.type = ACPI_TYPE_INTEGER;
+	arg.integer.value = 0x16;
+
+	params.count = 1;
+	params.pointer = &arg;
+
+	status = acpi_evaluate_object(NULL, TM2017_ACPI_QV20, &params, NULL);
+	if (ACPI_FAILURE(status))
+		return -EIO;
+
+	/* Step 2: EV20() — execute the event handler */
+	status = acpi_evaluate_object(NULL, TM2017_ACPI_EV20, NULL, NULL);
+	if (ACPI_FAILURE(status))
+		return -EIO;
+
+	return 0;
+}
+
+static int tm2017_pp_check_capability(struct bitland_mifs_wmi_data *data)
+{
+	/* TM2017 supports all modes regardless of AC type */
+	return 0;
+}
+
+static int tm2017_profile_get(struct bitland_mifs_wmi_data *data, u8 *mode);
+static int tm2017_profile_set(struct bitland_mifs_wmi_data *data, u8 mode);
+
+static const struct bitland_mifs_ops tm2017_ops = {
+	.name = "Mi NoteBook Ultra TM2017",
+	.mode_map = &bitland_mode_map,  /* same 0/1/2 mapping */
+	.check_performance_capability = tm2017_pp_check_capability,
+	.has_full_speed = false,        /* TM2017 only has 3 modes */
+	.custom_profile_get = tm2017_profile_get,
+	.custom_profile_set = tm2017_profile_set,
+};
+
 static const struct dmi_system_id bitland_mifs_dmi_table[] = {
+	{
+		/* Xiaomi Mi NoteBook Ultra — WMI profile is a dummy */
+		.matches = {
+			DMI_MATCH(DMI_BOARD_NAME, "TM2017"),
+		},
+		.driver_data = (void *)&tm2017_ops,
+	},
 	{
 		/* Redmi G */
 		.matches = {
@@ -296,26 +393,80 @@ static int bitland_mifs_wmi_call(struct bitland_mifs_wmi_data *data,
 	return 0;
 }
 
+static int tm2017_profile_get(struct bitland_mifs_wmi_data *data, u8 *mode)
+{
+	int ret = tm2017_acpi_read_mode();
+	if (ret < 0)
+		return ret;
+
+	*mode = (u8)ret;
+	return 0;
+}
+
+static int tm2017_profile_set(struct bitland_mifs_wmi_data *data, u8 mode)
+{
+	int current_mode, i, ret;
+
+	current_mode = tm2017_acpi_read_mode();
+	if (current_mode < 0)
+		return current_mode;
+
+	/* Already at target? */
+	if ((u8)current_mode == mode)
+		return 0;
+
+	/* Cycle until we reach the target (max TM2017_MAX_CYCLES) */
+	for (i = 0; i < TM2017_MAX_CYCLES; i++) {
+		ret = tm2017_acpi_cycle_mode();
+		if (ret)
+			return ret;
+
+		/* Give the EC time to settle before reading back or cycling again */
+		msleep(50);
+
+		current_mode = tm2017_acpi_read_mode();
+		if (current_mode < 0)
+			return current_mode;
+
+		if ((u8)current_mode == mode)
+			return 0;
+	}
+
+	/* Could not reach target mode */
+	dev_warn(&data->wdev->dev,
+		 "TM2017: failed to reach target mode %u after %d cycles\n",
+		 mode, TM2017_MAX_CYCLES);
+	return -EIO;
+}
+
 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,
-		.reserved2 = 0,
-		.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;
+	if (data->ops->custom_profile_get) {
+		int ret = data->ops->custom_profile_get(data, &mode);
+		if (ret)
+			return ret;
+	} else {
+		struct bitland_mifs_input input = {
+			.reserved1 = 0,
+			.operation = WMI_METHOD_GET,
+			.reserved2 = 0,
+			.function = WMI_FN_SYSTEM_PER_MODE,
+		};
+		struct bitland_mifs_output result;
+		int ret;
+
+		ret = bitland_mifs_wmi_call(data, &input, &result);
+		if (ret)
+			return ret;
+
+		mode = result.data[0];
+	}
 
-	mode = result.data[0];
 	if (mode == map->quiet)
 		*profile = PLATFORM_PROFILE_LOW_POWER;
 	else if (mode == map->balanced)
@@ -404,6 +555,9 @@ static int laptop_profile_set(struct device *dev,
 		return -EOPNOTSUPP;
 	}
 
+	if (data->ops->custom_profile_set)
+		return data->ops->custom_profile_set(data, mode);
+
 	input.payload[0] = mode;
 
 	return bitland_mifs_wmi_call(data, &input, NULL);
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.