summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/platform/x86/hp/Kconfig1
-rw-r--r--drivers/platform/x86/hp/hp-wmi.c190
2 files changed, 174 insertions, 17 deletions
diff --git a/drivers/platform/x86/hp/Kconfig b/drivers/platform/x86/hp/Kconfig
index 7fef4f12e498..d776761cd5fd 100644
--- a/drivers/platform/x86/hp/Kconfig
+++ b/drivers/platform/x86/hp/Kconfig
@@ -40,6 +40,7 @@ config HP_WMI
depends on ACPI_WMI
depends on INPUT
depends on RFKILL || RFKILL = n
+ select POWER_SUPPLY
select INPUT_SPARSEKMAP
select ACPI_PLATFORM_PROFILE
select HWMON
diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c
index 5fa553023842..c8bcb3e2b344 100644
--- a/drivers/platform/x86/hp/hp-wmi.c
+++ b/drivers/platform/x86/hp/hp-wmi.c
@@ -24,6 +24,9 @@
#include <linux/platform_profile.h>
#include <linux/hwmon.h>
#include <linux/acpi.h>
+#include <linux/mutex.h>
+#include <linux/cleanup.h>
+#include <linux/power_supply.h>
#include <linux/rfkill.h>
#include <linux/string.h>
#include <linux/dmi.h>
@@ -42,6 +45,8 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45E9-BE91-3D44E2C707E4");
#define HP_OMEN_EC_THERMAL_PROFILE_TIMER_OFFSET 0x63
#define HP_OMEN_EC_THERMAL_PROFILE_OFFSET 0x95
+#define ACPI_AC_CLASS "ac_adapter"
+
#define zero_if_sup(tmp) (zero_insize_support?0:sizeof(tmp)) // use when zero insize is required
/* DMI board names of devices that should use the omen specific path for
@@ -259,10 +264,18 @@ static const struct key_entry hp_wmi_keymap[] = {
{ KE_END, 0 }
};
+/*
+ * Mutex for the active_platform_profile variable,
+ * see omen_powersource_event.
+ */
+static DEFINE_MUTEX(active_platform_profile_lock);
+
static struct input_dev *hp_wmi_input_dev;
static struct input_dev *camera_shutter_input_dev;
static struct platform_device *hp_wmi_platform_dev;
static struct platform_profile_handler platform_profile_handler;
+static struct notifier_block platform_power_source_nb;
+static enum platform_profile_option active_platform_profile;
static bool platform_profile_support;
static bool zero_insize_support;
@@ -1194,8 +1207,7 @@ fail:
return err;
}
-static int platform_profile_omen_get(struct platform_profile_handler *pprof,
- enum platform_profile_option *profile)
+static int platform_profile_omen_get_ec(enum platform_profile_option *profile)
{
int tp;
@@ -1223,6 +1235,29 @@ static int platform_profile_omen_get(struct platform_profile_handler *pprof,
return 0;
}
+static int platform_profile_omen_get(struct platform_profile_handler *pprof,
+ enum platform_profile_option *profile)
+{
+ enum platform_profile_option selected_platform_profile;
+
+ /*
+ * We directly return the stored platform profile, as the embedded
+ * controller will not accept switching to the performance option when
+ * the conditions are not met (e.g. the laptop is not plugged in).
+ *
+ * If we directly return what the EC reports, the platform profile will
+ * immediately "switch back" to normal mode, which is against the
+ * expected behaviour from a userspace point of view, as described in
+ * the Platform Profile Section page of the kernel documentation.
+ *
+ * See also omen_powersource_event.
+ */
+ guard(mutex)(&active_platform_profile_lock);
+ selected_platform_profile = active_platform_profile;
+
+ return selected_platform_profile;
+}
+
static bool has_omen_thermal_profile_ec_timer(void)
{
const char *board_name = dmi_get_system_info(DMI_BOARD_NAME);
@@ -1245,8 +1280,7 @@ inline int omen_thermal_profile_ec_timer_set(u8 value)
return ec_write(HP_OMEN_EC_THERMAL_PROFILE_TIMER_OFFSET, value);
}
-static int platform_profile_omen_set(struct platform_profile_handler *pprof,
- enum platform_profile_option profile)
+static int platform_profile_omen_set_ec(enum platform_profile_option profile)
{
int err, tp, tp_version;
enum hp_thermal_profile_omen_flags flags = 0;
@@ -1300,6 +1334,22 @@ static int platform_profile_omen_set(struct platform_profile_handler *pprof,
return 0;
}
+static int platform_profile_omen_set(struct platform_profile_handler *pprof,
+ enum platform_profile_option profile)
+{
+ int err;
+
+ guard(mutex)(&active_platform_profile_lock);
+
+ err = platform_profile_omen_set_ec(profile);
+ if (err < 0)
+ return err;
+
+ active_platform_profile = profile;
+
+ return 0;
+}
+
static int thermal_profile_get(void)
{
return hp_wmi_read_int(HPWMI_THERMAL_PROFILE_QUERY);
@@ -1381,8 +1431,7 @@ static bool is_victus_thermal_profile(void)
board_name) >= 0;
}
-static int platform_profile_victus_get(struct platform_profile_handler *pprof,
- enum platform_profile_option *profile)
+static int platform_profile_victus_get_ec(enum platform_profile_option *profile)
{
int tp;
@@ -1407,8 +1456,14 @@ static int platform_profile_victus_get(struct platform_profile_handler *pprof,
return 0;
}
-static int platform_profile_victus_set(struct platform_profile_handler *pprof,
- enum platform_profile_option profile)
+static int platform_profile_victus_get(struct platform_profile_handler *pprof,
+ enum platform_profile_option *profile)
+{
+ /* Same behaviour as platform_profile_omen_get */
+ return platform_profile_omen_get(pprof, profile);
+}
+
+static int platform_profile_victus_set_ec(enum platform_profile_option profile)
{
int err, tp;
@@ -1433,21 +1488,113 @@ static int platform_profile_victus_set(struct platform_profile_handler *pprof,
return 0;
}
+static int platform_profile_victus_set(struct platform_profile_handler *pprof,
+ enum platform_profile_option profile)
+{
+ int err;
+
+ guard(mutex)(&active_platform_profile_lock);
+
+ err = platform_profile_victus_set_ec(profile);
+ if (err < 0)
+ return err;
+
+ active_platform_profile = profile;
+
+ return 0;
+}
+
+static int omen_powersource_event(struct notifier_block *nb,
+ unsigned long value,
+ void *data)
+{
+ struct acpi_bus_event *event_entry = data;
+ enum platform_profile_option actual_profile;
+ int err;
+
+ if (strcmp(event_entry->device_class, ACPI_AC_CLASS) != 0)
+ return NOTIFY_DONE;
+
+ pr_debug("Received power source device event\n");
+
+ guard(mutex)(&active_platform_profile_lock);
+
+ /*
+ * This handler can only be called on Omen and Victus models, so
+ * there's no need to call is_victus_thermal_profile() here.
+ */
+ if (is_omen_thermal_profile())
+ err = platform_profile_omen_get_ec(&actual_profile);
+ else
+ err = platform_profile_victus_get_ec(&actual_profile);
+
+ if (err < 0) {
+ /*
+ * Although we failed to get the current platform profile, we
+ * still want the other event consumers to process it.
+ */
+ pr_warn("Failed to read current platform profile (%d)\n", err);
+ return NOTIFY_DONE;
+ }
+
+ /*
+ * If we're back on AC and that the user-chosen power profile is
+ * different from what the EC reports, we restore the user-chosen
+ * one.
+ */
+ if (power_supply_is_system_supplied() <= 0 ||
+ active_platform_profile == actual_profile) {
+ pr_debug("Platform profile update skipped, conditions unmet\n");
+ return NOTIFY_DONE;
+ }
+
+ if (is_omen_thermal_profile())
+ err = platform_profile_omen_set_ec(active_platform_profile);
+ else
+ err = platform_profile_victus_set_ec(active_platform_profile);
+
+ if (err < 0) {
+ pr_warn("Failed to restore platform profile (%d)\n", err);
+ return NOTIFY_DONE;
+ }
+
+ return NOTIFY_OK;
+}
+
+static int omen_register_powersource_event_handler(void)
+{
+ int err;
+
+ platform_power_source_nb.notifier_call = omen_powersource_event;
+ err = register_acpi_notifier(&platform_power_source_nb);
+
+ if (err < 0) {
+ pr_warn("Failed to install ACPI power source notify handler\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static inline void omen_unregister_powersource_event_handler(void)
+{
+ unregister_acpi_notifier(&platform_power_source_nb);
+}
+
static int thermal_profile_setup(void)
{
int err, tp;
if (is_omen_thermal_profile()) {
- tp = omen_thermal_profile_get();
- if (tp < 0)
- return tp;
+ err = platform_profile_omen_get_ec(&active_platform_profile);
+ if (err < 0)
+ return err;
/*
* call thermal profile write command to ensure that the
* firmware correctly sets the OEM variables
*/
-
- err = omen_thermal_profile_set(tp);
+ err = platform_profile_omen_set_ec(active_platform_profile);
if (err < 0)
return err;
@@ -1456,15 +1603,15 @@ static int thermal_profile_setup(void)
set_bit(PLATFORM_PROFILE_COOL, platform_profile_handler.choices);
} else if (is_victus_thermal_profile()) {
- tp = omen_thermal_profile_get();
- if (tp < 0)
- return tp;
+ err = platform_profile_victus_get_ec(&active_platform_profile);
+ if (err < 0)
+ return err;
/*
* call thermal profile write command to ensure that the
* firmware correctly sets the OEM variables
*/
- err = omen_thermal_profile_set(tp);
+ err = platform_profile_victus_set_ec(active_platform_profile);
if (err < 0)
return err;
@@ -1758,6 +1905,12 @@ static int __init hp_wmi_init(void)
goto err_unregister_device;
}
+ if (is_omen_thermal_profile() || is_victus_thermal_profile()) {
+ err = omen_register_powersource_event_handler();
+ if (err)
+ goto err_unregister_device;
+ }
+
return 0;
err_unregister_device:
@@ -1772,6 +1925,9 @@ module_init(hp_wmi_init);
static void __exit hp_wmi_exit(void)
{
+ if (is_omen_thermal_profile() || is_victus_thermal_profile())
+ omen_unregister_powersource_event_handler();
+
if (wmi_has_guid(HPWMI_EVENT_GUID))
hp_wmi_input_destroy();