#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>
 #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
        { 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;
 
        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;
 
        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);
        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;
        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);
                            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;
 
        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;
 
        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;
 
 
                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;
 
                        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:
 
 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();