#include <acpi/video.h>
 #include <linux/hwmon.h>
 #include <linux/units.h>
+#include <linux/unaligned.h>
 #include <linux/bitfield.h>
 
 MODULE_AUTHOR("Carlos Corbacho");
 #define ACER_WMID_GET_GAMING_SYS_INFO_METHODID 5
 #define ACER_WMID_SET_GAMING_FAN_BEHAVIOR 14
 #define ACER_WMID_SET_GAMING_MISC_SETTING_METHODID 22
+#define ACER_WMID_GET_GAMING_MISC_SETTING_METHODID 23
 
-#define ACER_PREDATOR_V4_THERMAL_PROFILE_EC_OFFSET 0x54
+#define ACER_GAMING_MISC_SETTING_STATUS_MASK GENMASK_ULL(7, 0)
+#define ACER_GAMING_MISC_SETTING_INDEX_MASK GENMASK_ULL(7, 0)
+#define ACER_GAMING_MISC_SETTING_VALUE_MASK GENMASK_ULL(15, 8)
 
 #define ACER_PREDATOR_V4_RETURN_STATUS_BIT_MASK GENMASK_ULL(7, 0)
 #define ACER_PREDATOR_V4_SENSOR_INDEX_BIT_MASK GENMASK_ULL(15, 8)
        ACER_WMID_SENSOR_GPU_TEMPERATURE        = 0x0A,
 };
 
+enum acer_wmi_gaming_misc_setting {
+       ACER_WMID_MISC_SETTING_PLATFORM_PROFILE         = 0x000B,
+};
+
 static const struct key_entry acer_wmi_keymap[] __initconst = {
        {KE_KEY, 0x01, {KEY_WLAN} },     /* WiFi */
        {KE_KEY, 0x03, {KEY_WLAN} },     /* WiFi */
  */
 static int last_non_turbo_profile;
 
-enum acer_predator_v4_thermal_profile_ec {
-       ACER_PREDATOR_V4_THERMAL_PROFILE_ECO = 0x04,
-       ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO = 0x03,
-       ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE = 0x02,
-       ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET = 0x01,
-       ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED = 0x00,
-};
-
-enum acer_predator_v4_thermal_profile_wmi {
-       ACER_PREDATOR_V4_THERMAL_PROFILE_ECO_WMI = 0x060B,
-       ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI = 0x050B,
-       ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE_WMI = 0x040B,
-       ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET_WMI = 0x0B,
-       ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI = 0x010B,
+enum acer_predator_v4_thermal_profile {
+       ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET          = 0x00,
+       ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED       = 0x01,
+       ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE    = 0x04,
+       ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO          = 0x05,
+       ACER_PREDATOR_V4_THERMAL_PROFILE_ECO            = 0x06,
 };
 
 /* Find which quirks are needed for a particular vendor/ model pair */
        return status;
 }
 
+static int WMI_gaming_execute_u32_u64(u32 method_id, u32 in, u64 *out)
+{
+       struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL };
+       struct acpi_buffer input = {
+               .length = sizeof(in),
+               .pointer = &in,
+       };
+       union acpi_object *obj;
+       acpi_status status;
+       int ret = 0;
+
+       status = wmi_evaluate_method(WMID_GUID4, 0, method_id, &input, &result);
+       if (ACPI_FAILURE(status))
+               return -EIO;
+
+       obj = result.pointer;
+       if (obj && out) {
+               switch (obj->type) {
+               case ACPI_TYPE_INTEGER:
+                       *out = obj->integer.value;
+                       break;
+               case ACPI_TYPE_BUFFER:
+                       if (obj->buffer.length < sizeof(*out))
+                               ret = -ENOMSG;
+                       else
+                               *out = get_unaligned_le64(obj->buffer.pointer);
+
+                       break;
+               default:
+                       ret = -ENOMSG;
+                       break;
+               }
+       }
+
+       kfree(obj);
+
+       return ret;
+}
+
 static acpi_status WMID_gaming_set_u64(u64 value, u32 cap)
 {
        u32 method_id = 0;
        WMID_gaming_set_u64(gpu_fan_config2 | gpu_fan_config1 << 16, ACER_CAP_TURBO_FAN);
 }
 
+static int WMID_gaming_set_misc_setting(enum acer_wmi_gaming_misc_setting setting, u8 value)
+{
+       acpi_status status;
+       u64 input = 0;
+       u64 result;
+
+       input |= FIELD_PREP(ACER_GAMING_MISC_SETTING_INDEX_MASK, setting);
+       input |= FIELD_PREP(ACER_GAMING_MISC_SETTING_VALUE_MASK, value);
+
+       status = WMI_gaming_execute_u64(ACER_WMID_SET_GAMING_MISC_SETTING_METHODID, input, &result);
+       if (ACPI_FAILURE(status))
+               return -EIO;
+
+       /* The return status must be zero for the operation to have succeeded */
+       if (FIELD_GET(ACER_GAMING_MISC_SETTING_STATUS_MASK, result))
+               return -EIO;
+
+       return 0;
+}
+
+static int WMID_gaming_get_misc_setting(enum acer_wmi_gaming_misc_setting setting, u8 *value)
+{
+       u64 input = 0;
+       u64 result;
+       int ret;
+
+       input |= FIELD_PREP(ACER_GAMING_MISC_SETTING_INDEX_MASK, setting);
+
+       ret = WMI_gaming_execute_u32_u64(ACER_WMID_GET_GAMING_MISC_SETTING_METHODID, input,
+                                        &result);
+       if (ret < 0)
+               return ret;
+
+       /* The return status must be zero for the operation to have succeeded */
+       if (FIELD_GET(ACER_GAMING_MISC_SETTING_STATUS_MASK, result))
+               return -EIO;
+
+       *value = FIELD_GET(ACER_GAMING_MISC_SETTING_VALUE_MASK, result);
+
+       return 0;
+}
+
 /*
  * Generic Device (interface-independent)
  */
        u8 tp;
        int err;
 
-       err = ec_read(ACER_PREDATOR_V4_THERMAL_PROFILE_EC_OFFSET, &tp);
-
-       if (err < 0)
+       err = WMID_gaming_get_misc_setting(ACER_WMID_MISC_SETTING_PLATFORM_PROFILE, &tp);
+       if (err)
                return err;
 
        switch (tp) {
 acer_predator_v4_platform_profile_set(struct platform_profile_handler *pprof,
                                      enum platform_profile_option profile)
 {
-       int tp;
-       acpi_status status;
+       int err, tp;
 
        switch (profile) {
        case PLATFORM_PROFILE_PERFORMANCE:
-               tp = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI;
+               tp = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO;
                break;
        case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
-               tp = ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE_WMI;
+               tp = ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE;
                break;
        case PLATFORM_PROFILE_BALANCED:
-               tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI;
+               tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED;
                break;
        case PLATFORM_PROFILE_QUIET:
-               tp = ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET_WMI;
+               tp = ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET;
                break;
        case PLATFORM_PROFILE_LOW_POWER:
-               tp = ACER_PREDATOR_V4_THERMAL_PROFILE_ECO_WMI;
+               tp = ACER_PREDATOR_V4_THERMAL_PROFILE_ECO;
                break;
        default:
                return -EOPNOTSUPP;
        }
 
-       status = WMI_gaming_execute_u64(
-               ACER_WMID_SET_GAMING_MISC_SETTING_METHODID, tp, NULL);
-
-       if (ACPI_FAILURE(status))
-               return -EIO;
+       err = WMID_gaming_set_misc_setting(ACER_WMID_MISC_SETTING_PLATFORM_PROFILE, tp);
+       if (err)
+               return err;
 
-       if (tp != ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI)
+       if (tp != ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO)
                last_non_turbo_profile = tp;
 
        return 0;
 
                /* Set default non-turbo profile  */
                last_non_turbo_profile =
-                       ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI;
+                       ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED;
        }
        return 0;
 }
                u8 current_tp;
                int tp, err;
                u64 on_AC;
-               acpi_status status;
-
-               err = ec_read(ACER_PREDATOR_V4_THERMAL_PROFILE_EC_OFFSET,
-                             ¤t_tp);
 
-               if (err < 0)
+               err = WMID_gaming_get_misc_setting(ACER_WMID_MISC_SETTING_PLATFORM_PROFILE,
+                                                  ¤t_tp);
+               if (err)
                        return err;
 
                /* Check power source */
                switch (current_tp) {
                case ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO:
                        if (!on_AC)
-                               tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI;
+                               tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED;
                        else if (cycle_gaming_thermal_profile)
-                               tp = ACER_PREDATOR_V4_THERMAL_PROFILE_ECO_WMI;
+                               tp = ACER_PREDATOR_V4_THERMAL_PROFILE_ECO;
                        else
                                tp = last_non_turbo_profile;
                        break;
                case ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE:
                        if (!on_AC)
-                               tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI;
+                               tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED;
                        else
-                               tp = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI;
+                               tp = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO;
                        break;
                case ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED:
                        if (!on_AC)
-                               tp = ACER_PREDATOR_V4_THERMAL_PROFILE_ECO_WMI;
+                               tp = ACER_PREDATOR_V4_THERMAL_PROFILE_ECO;
                        else if (cycle_gaming_thermal_profile)
-                               tp = ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE_WMI;
+                               tp = ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE;
                        else
-                               tp = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI;
+                               tp = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO;
                        break;
                case ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET:
                        if (!on_AC)
-                               tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI;
+                               tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED;
                        else if (cycle_gaming_thermal_profile)
-                               tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI;
+                               tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED;
                        else
-                               tp = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI;
+                               tp = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO;
                        break;
                case ACER_PREDATOR_V4_THERMAL_PROFILE_ECO:
                        if (!on_AC)
-                               tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI;
+                               tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED;
                        else if (cycle_gaming_thermal_profile)
-                               tp = ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET_WMI;
+                               tp = ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET;
                        else
-                               tp = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI;
+                               tp = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO;
                        break;
                default:
                        return -EOPNOTSUPP;
                }
 
-               status = WMI_gaming_execute_u64(
-                       ACER_WMID_SET_GAMING_MISC_SETTING_METHODID, tp, NULL);
-
-               if (ACPI_FAILURE(status))
-                       return -EIO;
+               err = WMID_gaming_set_misc_setting(ACER_WMID_MISC_SETTING_PLATFORM_PROFILE, tp);
+               if (err)
+                       return err;
 
                /* Store non-turbo profile for turbo mode toggle*/
-               if (tp != ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI)
+               if (tp != ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO)
                        last_non_turbo_profile = tp;
 
                platform_profile_notify(&platform_profile_handler);