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+
262273static struct input_dev * hp_wmi_input_dev ;
263274static struct input_dev * camera_shutter_input_dev ;
264275static struct platform_device * hp_wmi_platform_dev ;
265276static 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 ;
266279static bool platform_profile_support ;
267280static 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+
12261261static 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+
13031353static 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+
14361584static 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
17631916err_unregister_device :
@@ -1772,6 +1925,9 @@ module_init(hp_wmi_init);
17721925
17731926static 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