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, ¶ms, &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, ¶ms, 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);