summaryrefslogtreecommitdiffstats
path: root/drivers/platform/x86/hp-wmi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/platform/x86/hp-wmi.c')
-rw-r--r--drivers/platform/x86/hp-wmi.c230
1 files changed, 164 insertions, 66 deletions
diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c
index 48a46466f086..0e9a25b56e0e 100644
--- a/drivers/platform/x86/hp-wmi.c
+++ b/drivers/platform/x86/hp-wmi.c
@@ -35,10 +35,6 @@ MODULE_LICENSE("GPL");
MODULE_ALIAS("wmi:95F24279-4D7B-4334-9387-ACCDC67EF61C");
MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4");
-static int enable_tablet_mode_sw = -1;
-module_param(enable_tablet_mode_sw, int, 0444);
-MODULE_PARM_DESC(enable_tablet_mode_sw, "Enable SW_TABLET_MODE reporting (-1=auto, 0=no, 1=yes)");
-
#define HPWMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C"
#define HPWMI_BIOS_GUID "5FB7F034-2C63-45e9-BE91-3D44E2C707E4"
#define HP_OMEN_EC_THERMAL_PROFILE_OFFSET 0x95
@@ -61,6 +57,14 @@ static const char * const omen_thermal_profile_boards[] = {
"8917", "8918", "8949", "894A", "89EB"
};
+/* DMI Board names of Omen laptops that are specifically set to be thermal
+ * profile version 0 by the Omen Command Center app, regardless of what
+ * the get system design information WMI call returns
+ */
+static const char *const omen_thermal_profile_force_v0_boards[] = {
+ "8607", "8746", "8747", "8749", "874A", "8748"
+};
+
enum hp_wmi_radio {
HPWMI_WIFI = 0x0,
HPWMI_BLUETOOTH = 0x1,
@@ -86,12 +90,17 @@ enum hp_wmi_event_ids {
HPWMI_BATTERY_CHARGE_PERIOD = 0x10,
};
+/*
+ * struct bios_args buffer is dynamically allocated. New WMI command types
+ * were introduced that exceeds 128-byte data size. Changes to handle
+ * the data size allocation scheme were kept in hp_wmi_perform_qurey function.
+ */
struct bios_args {
u32 signature;
u32 command;
u32 commandtype;
u32 datasize;
- u8 data[128];
+ u8 data[];
};
enum hp_wmi_commandtype {
@@ -107,6 +116,7 @@ enum hp_wmi_commandtype {
HPWMI_FEATURE2_QUERY = 0x0d,
HPWMI_WIRELESS2_QUERY = 0x1b,
HPWMI_POSTCODEERROR_QUERY = 0x2a,
+ HPWMI_SYSTEM_DEVICE_MODE = 0x40,
HPWMI_THERMAL_PROFILE_QUERY = 0x4c,
};
@@ -115,6 +125,7 @@ enum hp_wmi_gm_commandtype {
HPWMI_SET_PERFORMANCE_MODE = 0x1A,
HPWMI_FAN_SPEED_MAX_GET_QUERY = 0x26,
HPWMI_FAN_SPEED_MAX_SET_QUERY = 0x27,
+ HPWMI_GET_SYSTEM_DESIGN_DATA = 0x28,
};
enum hp_wmi_command {
@@ -149,10 +160,16 @@ enum hp_wireless2_bits {
HPWMI_POWER_FW_OR_HW = HPWMI_POWER_BIOS | HPWMI_POWER_HARD,
};
-enum hp_thermal_profile_omen {
- HP_OMEN_THERMAL_PROFILE_DEFAULT = 0x00,
- HP_OMEN_THERMAL_PROFILE_PERFORMANCE = 0x01,
- HP_OMEN_THERMAL_PROFILE_COOL = 0x02,
+enum hp_thermal_profile_omen_v0 {
+ HP_OMEN_V0_THERMAL_PROFILE_DEFAULT = 0x00,
+ HP_OMEN_V0_THERMAL_PROFILE_PERFORMANCE = 0x01,
+ HP_OMEN_V0_THERMAL_PROFILE_COOL = 0x02,
+};
+
+enum hp_thermal_profile_omen_v1 {
+ HP_OMEN_V1_THERMAL_PROFILE_DEFAULT = 0x30,
+ HP_OMEN_V1_THERMAL_PROFILE_PERFORMANCE = 0x31,
+ HP_OMEN_V1_THERMAL_PROFILE_COOL = 0x50,
};
enum hp_thermal_profile {
@@ -217,6 +234,19 @@ struct rfkill2_device {
static int rfkill2_count;
static struct rfkill2_device rfkill2[HPWMI_MAX_RFKILL2_DEVICES];
+/*
+ * Chassis Types values were obtained from SMBIOS reference
+ * specification version 3.00. A complete list of system enclosures
+ * and chassis types is available on Table 17.
+ */
+static const char * const tablet_chassis_types[] = {
+ "30", /* Tablet*/
+ "31", /* Convertible */
+ "32" /* Detachable */
+};
+
+#define DEVICE_MODE_TABLET 0x06
+
/* map output size to the corresponding WMI method id */
static inline int encode_outsize_for_pvsz(int outsize)
{
@@ -256,37 +286,43 @@ static inline int encode_outsize_for_pvsz(int outsize)
static int hp_wmi_perform_query(int query, enum hp_wmi_command command,
void *buffer, int insize, int outsize)
{
- int mid;
+ struct acpi_buffer input, output = { ACPI_ALLOCATE_BUFFER, NULL };
struct bios_return *bios_return;
- int actual_outsize;
- union acpi_object *obj;
- struct bios_args args = {
- .signature = 0x55434553,
- .command = command,
- .commandtype = query,
- .datasize = insize,
- .data = { 0 },
- };
- struct acpi_buffer input = { sizeof(struct bios_args), &args };
- struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
- int ret = 0;
+ union acpi_object *obj = NULL;
+ struct bios_args *args = NULL;
+ int mid, actual_outsize, ret;
+ size_t bios_args_size;
mid = encode_outsize_for_pvsz(outsize);
if (WARN_ON(mid < 0))
return mid;
- if (WARN_ON(insize > sizeof(args.data)))
- return -EINVAL;
- memcpy(&args.data[0], buffer, insize);
+ bios_args_size = struct_size(args, data, insize);
+ args = kmalloc(bios_args_size, GFP_KERNEL);
+ if (!args)
+ return -ENOMEM;
- wmi_evaluate_method(HPWMI_BIOS_GUID, 0, mid, &input, &output);
+ input.length = bios_args_size;
+ input.pointer = args;
- obj = output.pointer;
+ args->signature = 0x55434553;
+ args->command = command;
+ args->commandtype = query;
+ args->datasize = insize;
+ memcpy(args->data, buffer, flex_array_size(args, data, insize));
- if (!obj)
- return -EINVAL;
+ ret = wmi_evaluate_method(HPWMI_BIOS_GUID, 0, mid, &input, &output);
+ if (ret)
+ goto out_free;
+
+ obj = output.pointer;
+ if (!obj) {
+ ret = -EINVAL;
+ goto out_free;
+ }
if (obj->type != ACPI_TYPE_BUFFER) {
+ pr_warn("query 0x%x returned an invalid object 0x%x\n", query, ret);
ret = -EINVAL;
goto out_free;
}
@@ -311,6 +347,7 @@ static int hp_wmi_perform_query(int query, enum hp_wmi_command command,
out_free:
kfree(obj);
+ kfree(args);
return ret;
}
@@ -320,7 +357,7 @@ static int hp_wmi_get_fan_speed(int fan)
char fan_data[4] = { fan, 0, 0, 0 };
int ret = hp_wmi_perform_query(HPWMI_FAN_SPEED_GET_QUERY, HPWMI_GM,
- &fan_data, sizeof(fan_data),
+ &fan_data, sizeof(char),
sizeof(fan_data));
if (ret != 0)
@@ -337,7 +374,7 @@ static int hp_wmi_read_int(int query)
int val = 0, ret;
ret = hp_wmi_perform_query(query, HPWMI_READ, &val,
- sizeof(val), sizeof(val));
+ 0, sizeof(val));
if (ret)
return ret < 0 ? ret : -EINVAL;
@@ -345,14 +382,39 @@ static int hp_wmi_read_int(int query)
return val;
}
-static int hp_wmi_hw_state(int mask)
+static int hp_wmi_get_dock_state(void)
{
int state = hp_wmi_read_int(HPWMI_HARDWARE_QUERY);
if (state < 0)
return state;
- return !!(state & mask);
+ return !!(state & HPWMI_DOCK_MASK);
+}
+
+static int hp_wmi_get_tablet_mode(void)
+{
+ char system_device_mode[4] = { 0 };
+ const char *chassis_type;
+ bool tablet_found;
+ int ret;
+
+ chassis_type = dmi_get_system_info(DMI_CHASSIS_TYPE);
+ if (!chassis_type)
+ return -ENODEV;
+
+ tablet_found = match_string(tablet_chassis_types,
+ ARRAY_SIZE(tablet_chassis_types),
+ chassis_type) >= 0;
+ if (!tablet_found)
+ return -ENODEV;
+
+ ret = hp_wmi_perform_query(HPWMI_SYSTEM_DEVICE_MODE, HPWMI_READ,
+ system_device_mode, 0, sizeof(system_device_mode));
+ if (ret < 0)
+ return ret;
+
+ return system_device_mode[0] == DEVICE_MODE_TABLET;
}
static int omen_thermal_profile_set(int mode)
@@ -360,11 +422,8 @@ static int omen_thermal_profile_set(int mode)
char buffer[2] = {0, mode};
int ret;
- if (mode < 0 || mode > 2)
- return -EINVAL;
-
ret = hp_wmi_perform_query(HPWMI_SET_PERFORMANCE_MODE, HPWMI_GM,
- &buffer, sizeof(buffer), sizeof(buffer));
+ &buffer, sizeof(buffer), 0);
if (ret)
return ret < 0 ? ret : -EINVAL;
@@ -384,6 +443,30 @@ static bool is_omen_thermal_profile(void)
board_name) >= 0;
}
+static int omen_get_thermal_policy_version(void)
+{
+ unsigned char buffer[8] = { 0 };
+ int ret;
+
+ const char *board_name = dmi_get_system_info(DMI_BOARD_NAME);
+
+ if (board_name) {
+ int matches = match_string(omen_thermal_profile_force_v0_boards,
+ ARRAY_SIZE(omen_thermal_profile_force_v0_boards),
+ board_name);
+ if (matches >= 0)
+ return 0;
+ }
+
+ ret = hp_wmi_perform_query(HPWMI_GET_SYSTEM_DESIGN_DATA, HPWMI_GM,
+ &buffer, sizeof(buffer), sizeof(buffer));
+
+ if (ret)
+ return ret < 0 ? ret : -EINVAL;
+
+ return buffer[3];
+}
+
static int omen_thermal_profile_get(void)
{
u8 data;
@@ -401,7 +484,7 @@ static int hp_wmi_fan_speed_max_set(int enabled)
int ret;
ret = hp_wmi_perform_query(HPWMI_FAN_SPEED_MAX_SET_QUERY, HPWMI_GM,
- &enabled, sizeof(enabled), sizeof(enabled));
+ &enabled, sizeof(enabled), 0);
if (ret)
return ret < 0 ? ret : -EINVAL;
@@ -414,7 +497,7 @@ static int hp_wmi_fan_speed_max_get(void)
int val = 0, ret;
ret = hp_wmi_perform_query(HPWMI_FAN_SPEED_MAX_GET_QUERY, HPWMI_GM,
- &val, sizeof(val), sizeof(val));
+ &val, 0, sizeof(val));
if (ret)
return ret < 0 ? ret : -EINVAL;
@@ -426,7 +509,7 @@ static int __init hp_wmi_bios_2008_later(void)
{
int state = 0;
int ret = hp_wmi_perform_query(HPWMI_FEATURE_QUERY, HPWMI_READ, &state,
- sizeof(state), sizeof(state));
+ 0, sizeof(state));
if (!ret)
return 1;
@@ -437,7 +520,7 @@ static int __init hp_wmi_bios_2009_later(void)
{
u8 state[128];
int ret = hp_wmi_perform_query(HPWMI_FEATURE2_QUERY, HPWMI_READ, &state,
- sizeof(state), sizeof(state));
+ 0, sizeof(state));
if (!ret)
return 1;
@@ -515,7 +598,7 @@ static int hp_wmi_rfkill2_refresh(void)
int err, i;
err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, HPWMI_READ, &state,
- sizeof(state), sizeof(state));
+ 0, sizeof(state));
if (err)
return err;
@@ -568,7 +651,7 @@ static ssize_t als_show(struct device *dev, struct device_attribute *attr,
static ssize_t dock_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- int value = hp_wmi_hw_state(HPWMI_DOCK_MASK);
+ int value = hp_wmi_get_dock_state();
if (value < 0)
return value;
return sprintf(buf, "%d\n", value);
@@ -577,7 +660,7 @@ static ssize_t dock_show(struct device *dev, struct device_attribute *attr,
static ssize_t tablet_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- int value = hp_wmi_hw_state(HPWMI_TABLET_MASK);
+ int value = hp_wmi_get_tablet_mode();
if (value < 0)
return value;
return sprintf(buf, "%d\n", value);
@@ -604,7 +687,7 @@ static ssize_t als_store(struct device *dev, struct device_attribute *attr,
return ret;
ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, HPWMI_WRITE, &tmp,
- sizeof(tmp), sizeof(tmp));
+ sizeof(tmp), 0);
if (ret)
return ret < 0 ? ret : -EINVAL;
@@ -625,9 +708,9 @@ static ssize_t postcode_store(struct device *dev, struct device_attribute *attr,
if (clear == false)
return -EINVAL;
- /* Clear the POST error code. It is kept until until cleared. */
+ /* Clear the POST error code. It is kept until cleared. */
ret = hp_wmi_perform_query(HPWMI_POSTCODEERROR_QUERY, HPWMI_WRITE, &tmp,
- sizeof(tmp), sizeof(tmp));
+ sizeof(tmp), 0);
if (ret)
return ret < 0 ? ret : -EINVAL;
@@ -699,10 +782,10 @@ static void hp_wmi_notify(u32 value, void *context)
case HPWMI_DOCK_EVENT:
if (test_bit(SW_DOCK, hp_wmi_input_dev->swbit))
input_report_switch(hp_wmi_input_dev, SW_DOCK,
- hp_wmi_hw_state(HPWMI_DOCK_MASK));
+ hp_wmi_get_dock_state());
if (test_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit))
input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
- hp_wmi_hw_state(HPWMI_TABLET_MASK));
+ hp_wmi_get_tablet_mode());
input_sync(hp_wmi_input_dev);
break;
case HPWMI_PARK_HDD:
@@ -780,19 +863,17 @@ static int __init hp_wmi_input_setup(void)
__set_bit(EV_SW, hp_wmi_input_dev->evbit);
/* Dock */
- val = hp_wmi_hw_state(HPWMI_DOCK_MASK);
+ val = hp_wmi_get_dock_state();
if (!(val < 0)) {
__set_bit(SW_DOCK, hp_wmi_input_dev->swbit);
input_report_switch(hp_wmi_input_dev, SW_DOCK, val);
}
/* Tablet mode */
- if (enable_tablet_mode_sw > 0) {
- val = hp_wmi_hw_state(HPWMI_TABLET_MASK);
- if (val >= 0) {
- __set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit);
- input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, val);
- }
+ val = hp_wmi_get_tablet_mode();
+ if (!(val < 0)) {
+ __set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit);
+ input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, val);
}
err = sparse_keymap_setup(hp_wmi_input_dev, hp_wmi_keymap, NULL);
@@ -919,7 +1000,7 @@ static int __init hp_wmi_rfkill2_setup(struct platform_device *device)
int err, i;
err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, HPWMI_READ, &state,
- sizeof(state), sizeof(state));
+ 0, sizeof(state));
if (err)
return err < 0 ? err : -EINVAL;
@@ -1008,13 +1089,16 @@ static int platform_profile_omen_get(struct platform_profile_handler *pprof,
return tp;
switch (tp) {
- case HP_OMEN_THERMAL_PROFILE_PERFORMANCE:
+ case HP_OMEN_V0_THERMAL_PROFILE_PERFORMANCE:
+ case HP_OMEN_V1_THERMAL_PROFILE_PERFORMANCE:
*profile = PLATFORM_PROFILE_PERFORMANCE;
break;
- case HP_OMEN_THERMAL_PROFILE_DEFAULT:
+ case HP_OMEN_V0_THERMAL_PROFILE_DEFAULT:
+ case HP_OMEN_V1_THERMAL_PROFILE_DEFAULT:
*profile = PLATFORM_PROFILE_BALANCED;
break;
- case HP_OMEN_THERMAL_PROFILE_COOL:
+ case HP_OMEN_V0_THERMAL_PROFILE_COOL:
+ case HP_OMEN_V1_THERMAL_PROFILE_COOL:
*profile = PLATFORM_PROFILE_COOL;
break;
default:
@@ -1027,17 +1111,31 @@ static int platform_profile_omen_get(struct platform_profile_handler *pprof,
static int platform_profile_omen_set(struct platform_profile_handler *pprof,
enum platform_profile_option profile)
{
- int err, tp;
+ int err, tp, tp_version;
+
+ tp_version = omen_get_thermal_policy_version();
+
+ if (tp_version < 0 || tp_version > 1)
+ return -EOPNOTSUPP;
switch (profile) {
case PLATFORM_PROFILE_PERFORMANCE:
- tp = HP_OMEN_THERMAL_PROFILE_PERFORMANCE;
+ if (tp_version == 0)
+ tp = HP_OMEN_V0_THERMAL_PROFILE_PERFORMANCE;
+ else
+ tp = HP_OMEN_V1_THERMAL_PROFILE_PERFORMANCE;
break;
case PLATFORM_PROFILE_BALANCED:
- tp = HP_OMEN_THERMAL_PROFILE_DEFAULT;
+ if (tp_version == 0)
+ tp = HP_OMEN_V0_THERMAL_PROFILE_DEFAULT;
+ else
+ tp = HP_OMEN_V1_THERMAL_PROFILE_DEFAULT;
break;
case PLATFORM_PROFILE_COOL:
- tp = HP_OMEN_THERMAL_PROFILE_COOL;
+ if (tp_version == 0)
+ tp = HP_OMEN_V0_THERMAL_PROFILE_COOL;
+ else
+ tp = HP_OMEN_V1_THERMAL_PROFILE_COOL;
break;
default:
return -EOPNOTSUPP;
@@ -1227,10 +1325,10 @@ static int hp_wmi_resume_handler(struct device *device)
if (hp_wmi_input_dev) {
if (test_bit(SW_DOCK, hp_wmi_input_dev->swbit))
input_report_switch(hp_wmi_input_dev, SW_DOCK,
- hp_wmi_hw_state(HPWMI_DOCK_MASK));
+ hp_wmi_get_dock_state());
if (test_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit))
input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
- hp_wmi_hw_state(HPWMI_TABLET_MASK));
+ hp_wmi_get_tablet_mode());
input_sync(hp_wmi_input_dev);
}