Skip to content

Commit d234302

Browse files
alexis-belmonteij-intel
authored andcommitted
platform/x86: hp-wmi: Fix platform profile option switch bug on Omen and Victus laptops
Fix a platform profile option switch/getter bug on some Omen and Victus laptops dismissing userspace choice when selecting performance mode in inadequate conditions (e.g. by being disconnected from the AC power plug) by - hooking an ACPI notify handler through the omen_register_powersource_event_handler method that listens to AC power source changes (plugging in/out the AC power plug) - keeping an intermediate active_platform_profile variable that is set when userspace changes the platform profile setting - restoring the selected platform profile kept in active_platform_profile when AC power is plugged back into the laptop through the power_supply_is_system_supplied call check, unless if the user decided to alter the platform profile mid-way Add a dependency to the POWER_SUPPLY subsystem to be able to check for power_supply_is_system_supplied. Introduce intermediary functions to leverage platform profile <-> internal thermal profile translation code from platform_profile_omen_set and callers. These changes makes the module compliant with the principles defined in the Platform Profile Selection page of the Kernel documentation on those kind of laptops; which is to not "(...) let userspace know about any sub-optimal conditions which are impeding reaching the requested performance level". Since the Omen and Victus laptops share the same embedded controller system, the fix is applicable to both categories of laptops. Signed-off-by: Alexis Belmonte <[email protected]> Link: https://lore.kernel.org/r/Zovh22-7B1jI7sfF@alexis-pc [ij: Made active_platform_profile_lock static] Reviewed-by: Ilpo Järvinen <[email protected]> Signed-off-by: Ilpo Järvinen <[email protected]>
1 parent 356eda9 commit d234302

File tree

2 files changed

+174
-17
lines changed

2 files changed

+174
-17
lines changed

drivers/platform/x86/hp/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ config HP_WMI
4040
depends on ACPI_WMI
4141
depends on INPUT
4242
depends on RFKILL || RFKILL = n
43+
select POWER_SUPPLY
4344
select INPUT_SPARSEKMAP
4445
select ACPI_PLATFORM_PROFILE
4546
select HWMON

drivers/platform/x86/hp/hp-wmi.c

Lines changed: 173 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
#include <linux/platform_profile.h>
2525
#include <linux/hwmon.h>
2626
#include <linux/acpi.h>
27+
#include <linux/mutex.h>
28+
#include <linux/cleanup.h>
29+
#include <linux/power_supply.h>
2730
#include <linux/rfkill.h>
2831
#include <linux/string.h>
2932
#include <linux/dmi.h>
@@ -42,6 +45,8 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45E9-BE91-3D44E2C707E4");
4245
#define HP_OMEN_EC_THERMAL_PROFILE_TIMER_OFFSET 0x63
4346
#define HP_OMEN_EC_THERMAL_PROFILE_OFFSET 0x95
4447

48+
#define ACPI_AC_CLASS "ac_adapter"
49+
4550
#define zero_if_sup(tmp) (zero_insize_support?0:sizeof(tmp)) // use when zero insize is required
4651

4752
/* 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[] = {
259264
{ KE_END, 0 }
260265
};
261266

267+
/*
268+
* Mutex for the active_platform_profile variable,
269+
* see omen_powersource_event.
270+
*/
271+
static DEFINE_MUTEX(active_platform_profile_lock);
272+
262273
static struct input_dev *hp_wmi_input_dev;
263274
static struct input_dev *camera_shutter_input_dev;
264275
static struct platform_device *hp_wmi_platform_dev;
265276
static struct platform_profile_handler platform_profile_handler;
277+
static struct notifier_block platform_power_source_nb;
278+
static enum platform_profile_option active_platform_profile;
266279
static bool platform_profile_support;
267280
static bool zero_insize_support;
268281

@@ -1194,8 +1207,7 @@ static int __init hp_wmi_rfkill2_setup(struct platform_device *device)
11941207
return err;
11951208
}
11961209

1197-
static int platform_profile_omen_get(struct platform_profile_handler *pprof,
1198-
enum platform_profile_option *profile)
1210+
static int platform_profile_omen_get_ec(enum platform_profile_option *profile)
11991211
{
12001212
int tp;
12011213

@@ -1223,6 +1235,29 @@ static int platform_profile_omen_get(struct platform_profile_handler *pprof,
12231235
return 0;
12241236
}
12251237

1238+
static int platform_profile_omen_get(struct platform_profile_handler *pprof,
1239+
enum platform_profile_option *profile)
1240+
{
1241+
enum platform_profile_option selected_platform_profile;
1242+
1243+
/*
1244+
* We directly return the stored platform profile, as the embedded
1245+
* controller will not accept switching to the performance option when
1246+
* the conditions are not met (e.g. the laptop is not plugged in).
1247+
*
1248+
* If we directly return what the EC reports, the platform profile will
1249+
* immediately "switch back" to normal mode, which is against the
1250+
* expected behaviour from a userspace point of view, as described in
1251+
* the Platform Profile Section page of the kernel documentation.
1252+
*
1253+
* See also omen_powersource_event.
1254+
*/
1255+
guard(mutex)(&active_platform_profile_lock);
1256+
selected_platform_profile = active_platform_profile;
1257+
1258+
return selected_platform_profile;
1259+
}
1260+
12261261
static bool has_omen_thermal_profile_ec_timer(void)
12271262
{
12281263
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)
12451280
return ec_write(HP_OMEN_EC_THERMAL_PROFILE_TIMER_OFFSET, value);
12461281
}
12471282

1248-
static int platform_profile_omen_set(struct platform_profile_handler *pprof,
1249-
enum platform_profile_option profile)
1283+
static int platform_profile_omen_set_ec(enum platform_profile_option profile)
12501284
{
12511285
int err, tp, tp_version;
12521286
enum hp_thermal_profile_omen_flags flags = 0;
@@ -1300,6 +1334,22 @@ static int platform_profile_omen_set(struct platform_profile_handler *pprof,
13001334
return 0;
13011335
}
13021336

1337+
static int platform_profile_omen_set(struct platform_profile_handler *pprof,
1338+
enum platform_profile_option profile)
1339+
{
1340+
int err;
1341+
1342+
guard(mutex)(&active_platform_profile_lock);
1343+
1344+
err = platform_profile_omen_set_ec(profile);
1345+
if (err < 0)
1346+
return err;
1347+
1348+
active_platform_profile = profile;
1349+
1350+
return 0;
1351+
}
1352+
13031353
static int thermal_profile_get(void)
13041354
{
13051355
return hp_wmi_read_int(HPWMI_THERMAL_PROFILE_QUERY);
@@ -1381,8 +1431,7 @@ static bool is_victus_thermal_profile(void)
13811431
board_name) >= 0;
13821432
}
13831433

1384-
static int platform_profile_victus_get(struct platform_profile_handler *pprof,
1385-
enum platform_profile_option *profile)
1434+
static int platform_profile_victus_get_ec(enum platform_profile_option *profile)
13861435
{
13871436
int tp;
13881437

@@ -1407,8 +1456,14 @@ static int platform_profile_victus_get(struct platform_profile_handler *pprof,
14071456
return 0;
14081457
}
14091458

1410-
static int platform_profile_victus_set(struct platform_profile_handler *pprof,
1411-
enum platform_profile_option profile)
1459+
static int platform_profile_victus_get(struct platform_profile_handler *pprof,
1460+
enum platform_profile_option *profile)
1461+
{
1462+
/* Same behaviour as platform_profile_omen_get */
1463+
return platform_profile_omen_get(pprof, profile);
1464+
}
1465+
1466+
static int platform_profile_victus_set_ec(enum platform_profile_option profile)
14121467
{
14131468
int err, tp;
14141469

@@ -1433,21 +1488,113 @@ static int platform_profile_victus_set(struct platform_profile_handler *pprof,
14331488
return 0;
14341489
}
14351490

1491+
static int platform_profile_victus_set(struct platform_profile_handler *pprof,
1492+
enum platform_profile_option profile)
1493+
{
1494+
int err;
1495+
1496+
guard(mutex)(&active_platform_profile_lock);
1497+
1498+
err = platform_profile_victus_set_ec(profile);
1499+
if (err < 0)
1500+
return err;
1501+
1502+
active_platform_profile = profile;
1503+
1504+
return 0;
1505+
}
1506+
1507+
static int omen_powersource_event(struct notifier_block *nb,
1508+
unsigned long value,
1509+
void *data)
1510+
{
1511+
struct acpi_bus_event *event_entry = data;
1512+
enum platform_profile_option actual_profile;
1513+
int err;
1514+
1515+
if (strcmp(event_entry->device_class, ACPI_AC_CLASS) != 0)
1516+
return NOTIFY_DONE;
1517+
1518+
pr_debug("Received power source device event\n");
1519+
1520+
guard(mutex)(&active_platform_profile_lock);
1521+
1522+
/*
1523+
* This handler can only be called on Omen and Victus models, so
1524+
* there's no need to call is_victus_thermal_profile() here.
1525+
*/
1526+
if (is_omen_thermal_profile())
1527+
err = platform_profile_omen_get_ec(&actual_profile);
1528+
else
1529+
err = platform_profile_victus_get_ec(&actual_profile);
1530+
1531+
if (err < 0) {
1532+
/*
1533+
* Although we failed to get the current platform profile, we
1534+
* still want the other event consumers to process it.
1535+
*/
1536+
pr_warn("Failed to read current platform profile (%d)\n", err);
1537+
return NOTIFY_DONE;
1538+
}
1539+
1540+
/*
1541+
* If we're back on AC and that the user-chosen power profile is
1542+
* different from what the EC reports, we restore the user-chosen
1543+
* one.
1544+
*/
1545+
if (power_supply_is_system_supplied() <= 0 ||
1546+
active_platform_profile == actual_profile) {
1547+
pr_debug("Platform profile update skipped, conditions unmet\n");
1548+
return NOTIFY_DONE;
1549+
}
1550+
1551+
if (is_omen_thermal_profile())
1552+
err = platform_profile_omen_set_ec(active_platform_profile);
1553+
else
1554+
err = platform_profile_victus_set_ec(active_platform_profile);
1555+
1556+
if (err < 0) {
1557+
pr_warn("Failed to restore platform profile (%d)\n", err);
1558+
return NOTIFY_DONE;
1559+
}
1560+
1561+
return NOTIFY_OK;
1562+
}
1563+
1564+
static int omen_register_powersource_event_handler(void)
1565+
{
1566+
int err;
1567+
1568+
platform_power_source_nb.notifier_call = omen_powersource_event;
1569+
err = register_acpi_notifier(&platform_power_source_nb);
1570+
1571+
if (err < 0) {
1572+
pr_warn("Failed to install ACPI power source notify handler\n");
1573+
return err;
1574+
}
1575+
1576+
return 0;
1577+
}
1578+
1579+
static inline void omen_unregister_powersource_event_handler(void)
1580+
{
1581+
unregister_acpi_notifier(&platform_power_source_nb);
1582+
}
1583+
14361584
static int thermal_profile_setup(void)
14371585
{
14381586
int err, tp;
14391587

14401588
if (is_omen_thermal_profile()) {
1441-
tp = omen_thermal_profile_get();
1442-
if (tp < 0)
1443-
return tp;
1589+
err = platform_profile_omen_get_ec(&active_platform_profile);
1590+
if (err < 0)
1591+
return err;
14441592

14451593
/*
14461594
* call thermal profile write command to ensure that the
14471595
* firmware correctly sets the OEM variables
14481596
*/
1449-
1450-
err = omen_thermal_profile_set(tp);
1597+
err = platform_profile_omen_set_ec(active_platform_profile);
14511598
if (err < 0)
14521599
return err;
14531600

@@ -1456,15 +1603,15 @@ static int thermal_profile_setup(void)
14561603

14571604
set_bit(PLATFORM_PROFILE_COOL, platform_profile_handler.choices);
14581605
} else if (is_victus_thermal_profile()) {
1459-
tp = omen_thermal_profile_get();
1460-
if (tp < 0)
1461-
return tp;
1606+
err = platform_profile_victus_get_ec(&active_platform_profile);
1607+
if (err < 0)
1608+
return err;
14621609

14631610
/*
14641611
* call thermal profile write command to ensure that the
14651612
* firmware correctly sets the OEM variables
14661613
*/
1467-
err = omen_thermal_profile_set(tp);
1614+
err = platform_profile_victus_set_ec(active_platform_profile);
14681615
if (err < 0)
14691616
return err;
14701617

@@ -1758,6 +1905,12 @@ static int __init hp_wmi_init(void)
17581905
goto err_unregister_device;
17591906
}
17601907

1908+
if (is_omen_thermal_profile() || is_victus_thermal_profile()) {
1909+
err = omen_register_powersource_event_handler();
1910+
if (err)
1911+
goto err_unregister_device;
1912+
}
1913+
17611914
return 0;
17621915

17631916
err_unregister_device:
@@ -1772,6 +1925,9 @@ module_init(hp_wmi_init);
17721925

17731926
static void __exit hp_wmi_exit(void)
17741927
{
1928+
if (is_omen_thermal_profile() || is_victus_thermal_profile())
1929+
omen_unregister_powersource_event_handler();
1930+
17751931
if (wmi_has_guid(HPWMI_EVENT_GUID))
17761932
hp_wmi_input_destroy();
17771933

0 commit comments

Comments
 (0)