mbox series

[RFC,00/13] acpi/x86: s2idle: implement Modern Standby transition states and expose to userspace

Message ID 20241121172239.119590-1-lkml@antheas.dev (mailing list archive)
Headers show
Series acpi/x86: s2idle: implement Modern Standby transition states and expose to userspace | expand

Message

Antheas Kapenekakis Nov. 21, 2024, 5:22 p.m. UTC
The following series moves the _DSM 3,4,7,8 firmware notifications outside
the suspend sequence, and makes them part of a transition function, where
the system can transition freely between them when it is not suspended.
This transition function is exposed to userspace, which now gains the
ability to control the presentation of the device (e.g., pulse the suspend
light) without forcing the kernel to suspend. In addition, it adds support
for the _DSM 9 call Turn Display On, which was introduced in Windows 22H2
and aims to speed up device wake-up while remaining in the "Sleep" state.
If userspace is not standby aware, the kernel will bring the system into
the "Sleep" state before beginning the suspend sequence.

This series requires a bit of background on how modern standby works in
Windows. Windows has a concept of "Modern Standby" [1], where it performs
an elaborate userspace and kernel suspend choreography while the device is
inactive in order to maintain fast wake-up times and connectivity while the
display of the device is off. This is done through 5 hardware states and
the OS takes the liberty of transitioning between them, by following a set
of rules (e.g., "Adaptive Hibernate").

```
                                 \/-> "Hibernate (S4)"
"Active" <-> "Screen Off" <-> "Sleep" <-> "DRIPS"
                  /\-  "Resume"  <-         <-
```

When the display is on and the user is interacting with the device, it is
in the "Active" state. The moment the display turns off, the device
transitions to the "Screen Off" state, where hardware and userspace are
fully active. Userspace will then decide when appropriate to freeze major
components (such as the DE) and transition into the "Sleep" state, where
the kernel is still active and connectivity is maintained. Finally, the
conventional "Suspend-to-idle" path can be used to bring the system into
the deepest runtime idle platform state (DRIPS) state, which is named
"s2idle" in the Linux kernel.

After wake-up, the system re-transitions into the "Sleep" state, where
userspace can run housekeeping and/or hibernate if the wake-up was not user
initiated (e.g., timer). If user-initiated, userspace can hasten the
transition out of the "Sleep" state by transitioning into the state
"Resume" that certain devices use to boost the Power Limit (PLx) while
remaining in sleep (support for this new notification is rare). Then, it
transitions back into "Screen Off" and "Active" to prepare for the user.

All transitions between these states feature unique firmware notifications
[3] that change the presentation of the device (e.g., pulse the suspend
light, turn off RGB). For more information, see the docs in [8]. Making
these transitions accessible from userspace moves them out of the suspend
sequence and has them happen while the kernel is fully active, mirroring
Windows.

As a side effect, this patch series completely fixes the ROG Ally
controller issue [5], which expects for .5s to lapse before its
controller's USB hub goes into D3 and otherwise malfunctions. It also fixes
an issue present in (allegedly only) older firmwares where they check the
USB subsystem is not in D3 before allowing the controller to wake up while
in powersave mode (for avoiding spurious wake-ups). As such, this patch
series is also a universal fix for the ROG Ally controller.

Moreover, this patch series allows turning off the controller and RGB of
most Windows handhelds (OneXPlayer, Lenovo Legion Go, GPD, and Asus ROG
Ally), opening the possibility of implementing suspend-then-hibernate and
other standby features, such as background downloads, without waking up the
RGB/controller of those devices. A Thinkpad T14 2021 was also tested, and
it pulses its suspend light during sleep.

There is still the question of where LSP0 entry/exit (_DSM 5,6) should be
fired or whether they should be fired in the path to hibernation. However,
as they cause no issues currently, and they fire when software activity has
seized, they are fine where they are.

It is important to note that the effects of these _DSMs persist during
reboots. I.e., if the Legion Go reboots while in the "Sleep" state, it will
boot into the "Sleep" state and have its controller disabled and suspend
light pulsing. The reboot persistence is undesirable, so the reboot path
will need to include a transition to active prior to reboot (not
included in this series). This is not the case after shutdown and
hibernation, where the device boots into the "Active" state.

The issue of DPMS is still present. Currently, gamescope and KDE (at least)
do not fire DPMS before suspending. This causes an undesirable frozen
screen while the system is suspending and looks quite ugly in general. This
is especially true if the firmware notifications fire earlier. Therefore,
should the kernel fire DPMS before forcing the transition to sleep for
backwards compat.? If yes, it will be quite the effort. Moreover, should
the kernel allow graphics drivers hook the transition function and block
transitions to "Screen Off" if there is an active CRTC? As that would be a
significant undertaking, there should be proof that there exists such a
device that has an issue firing the notifications with an active CRTC.

A variant of this series has been tested by thousands of users by now,
where the notifications fire around .5s before the CRTC is disabled and no
ill-effects have found in regard to this quirk. AFAIK, it is a visual
quirk. Making DPMS fire before the backwards compat. transition is a good
idea in any case, as it will sync the 200ms between Display Off/Sleep Entry
firing and the graphics driver turning off the display, but it might not be
worth the effort.

We are currently testing a DPMS patch for gamescope and it completely fixes
this visual quirk while allowing for e.g., hibernation without turning on
the screen. The DPMS gamescope patch + performing the transitions in
userspace in such a way where it blends the Ally's suspend delay halves the
user perceived delay to sleep and results in a very professional
presentation. This presentation extends to other devices as well, such as
the Legion Go.

Link: https://learn.microsoft.com/en-us/windows-hardware/design/device-experiences/prepare-hardware-for-modern-standby [1]
Link: https://learn.microsoft.com/en-us/windows-hardware/customize/power-settings/adaptive-hibernate [2]
Link: https://learn.microsoft.com/en-us/windows-hardware/design/device-experiences/modern-standby-firmware-notifications [3]
Link: https://github.com/hhd-dev/hwinfo/tree/master/devices [4]
Link: https://git.kernel.org/pub/scm/linux/kernel/git/superm1/linux.git/log/?h=superm1/dsm-screen-on-off [5]
Link: https://gitlab.freedesktop.org/drm/amd/-/issues/2719 [6]
Link: https://dl.dell.com/manuals/all-products/esuprt_solutions_int/esuprt_solutions_int_solutions_resources/client-mobile-solution-resources_white-papers45_en-us.pdf [7]
File: Documentation/admin-guide/pm/standby-states.rst [8]

Changes from previous series (`acpi/x86: s2idle: move Display off/on calls
  outside suspend (fixes ROG Ally suspend)`):
  - Separate Display On/Off rename into its own commit (suggested by Hans)
  - Move delay quirks into s2idle.c (suggested by Hans)
  - Add documentation on Documentation/admin-guide/pm/standby-states.rst
  - Callbacks are now static and a transition function is used
  - Fixed all checkpatch warnings
  - The rest of the series is completely re-written

Antheas Kapenekakis (13):
  Documentation: PM: Add documentation for S0ix Standby States
  acpi/x86: s2idle: add support for Display Off and Display On callbacks
  acpi/x86: s2idle: add support for Sleep Entry and Sleep Exit callbacks
  acpi/x86: s2idle: add support for Turn On Display callback
  acpi/x86: s2idle: add modern standby transition function
  acpi/x86: s2idle: rename Screen On/Off to Display On/Off
  acpi/x86: s2idle: call Display On/Off as part of callbacks
  acpi/x86: s2idle: rename MS Exit/Entry to Sleep Exit/Entry
  acpi/x86: s2idle: call Sleep Entry/Exit as part of callbacks
  acpi/x86: s2idle: add Turn On Display and call as part of callback
  acpi/x86: s2idle: add quirk table for modern standby delays
  platform/x86: asus-wmi: remove Ally (1st gen) and Ally X suspend quirk
  PM: standby: Add sysfs attribute for modern standby transitions

 Documentation/ABI/testing/sysfs-power         |  34 +++
 .../admin-guide/pm/standby-states.rst         | 133 ++++++++++
 Documentation/admin-guide/pm/system-wide.rst  |   1 +
 drivers/acpi/x86/s2idle.c                     | 249 ++++++++++++++----
 drivers/platform/x86/asus-wmi.c               |  54 ----
 include/linux/suspend.h                       |  16 ++
 kernel/power/main.c                           |  75 ++++++
 kernel/power/power.h                          |   1 +
 kernel/power/suspend.c                        | 154 +++++++++++
 9 files changed, 616 insertions(+), 101 deletions(-)
 create mode 100644 Documentation/admin-guide/pm/standby-states.rst

Comments

Rafael J. Wysocki Nov. 21, 2024, 5:41 p.m. UTC | #1
On Thu, Nov 21, 2024 at 6:28 PM Antheas Kapenekakis <lkml@antheas.dev> wrote:
>
> The following series moves the _DSM 3,4,7,8 firmware notifications outside
> the suspend sequence, and makes them part of a transition function, where
> the system can transition freely between them when it is not suspended.
> This transition function is exposed to userspace, which now gains the
> ability to control the presentation of the device (e.g., pulse the suspend
> light) without forcing the kernel to suspend. In addition, it adds support
> for the _DSM 9 call Turn Display On, which was introduced in Windows 22H2
> and aims to speed up device wake-up while remaining in the "Sleep" state.
> If userspace is not standby aware, the kernel will bring the system into
> the "Sleep" state before beginning the suspend sequence.

I'll get to this when 6.13-rc1 is out, but I can tell you right away
that some of the above cannot be done without breaking backwards
compatibility.

> This series requires a bit of background on how modern standby works in
> Windows. Windows has a concept of "Modern Standby" [1], where it performs
> an elaborate userspace and kernel suspend choreography while the device is
> inactive in order to maintain fast wake-up times and connectivity while the
> display of the device is off. This is done through 5 hardware states and
> the OS takes the liberty of transitioning between them, by following a set
> of rules (e.g., "Adaptive Hibernate").
>
> ```
>                                  \/-> "Hibernate (S4)"
> "Active" <-> "Screen Off" <-> "Sleep" <-> "DRIPS"
>                   /\-  "Resume"  <-         <-
> ```
>
> When the display is on and the user is interacting with the device, it is
> in the "Active" state. The moment the display turns off, the device
> transitions to the "Screen Off" state, where hardware and userspace are
> fully active. Userspace will then decide when appropriate to freeze major
> components (such as the DE) and transition into the "Sleep" state, where
> the kernel is still active and connectivity is maintained. Finally, the
> conventional "Suspend-to-idle" path can be used to bring the system into
> the deepest runtime idle platform state (DRIPS) state, which is named
> "s2idle" in the Linux kernel.
>
> After wake-up, the system re-transitions into the "Sleep" state, where
> userspace can run housekeeping and/or hibernate if the wake-up was not user
> initiated (e.g., timer). If user-initiated, userspace can hasten the
> transition out of the "Sleep" state by transitioning into the state
> "Resume" that certain devices use to boost the Power Limit (PLx) while
> remaining in sleep (support for this new notification is rare). Then, it
> transitions back into "Screen Off" and "Active" to prepare for the user.
>
> All transitions between these states feature unique firmware notifications
> [3] that change the presentation of the device (e.g., pulse the suspend
> light, turn off RGB). For more information, see the docs in [8]. Making
> these transitions accessible from userspace moves them out of the suspend
> sequence and has them happen while the kernel is fully active, mirroring
> Windows.
>
> As a side effect, this patch series completely fixes the ROG Ally
> controller issue [5], which expects for .5s to lapse before its
> controller's USB hub goes into D3 and otherwise malfunctions. It also fixes
> an issue present in (allegedly only) older firmwares where they check the
> USB subsystem is not in D3 before allowing the controller to wake up while
> in powersave mode (for avoiding spurious wake-ups). As such, this patch
> series is also a universal fix for the ROG Ally controller.
>
> Moreover, this patch series allows turning off the controller and RGB of
> most Windows handhelds (OneXPlayer, Lenovo Legion Go, GPD, and Asus ROG
> Ally), opening the possibility of implementing suspend-then-hibernate and
> other standby features, such as background downloads, without waking up the
> RGB/controller of those devices. A Thinkpad T14 2021 was also tested, and
> it pulses its suspend light during sleep.
>
> There is still the question of where LSP0 entry/exit (_DSM 5,6) should be
> fired or whether they should be fired in the path to hibernation. However,
> as they cause no issues currently, and they fire when software activity has
> seized, they are fine where they are.
>
> It is important to note that the effects of these _DSMs persist during
> reboots. I.e., if the Legion Go reboots while in the "Sleep" state, it will
> boot into the "Sleep" state and have its controller disabled and suspend
> light pulsing. The reboot persistence is undesirable, so the reboot path
> will need to include a transition to active prior to reboot (not
> included in this series). This is not the case after shutdown and
> hibernation, where the device boots into the "Active" state.
>
> The issue of DPMS is still present. Currently, gamescope and KDE (at least)
> do not fire DPMS before suspending. This causes an undesirable frozen
> screen while the system is suspending and looks quite ugly in general. This
> is especially true if the firmware notifications fire earlier. Therefore,
> should the kernel fire DPMS before forcing the transition to sleep for
> backwards compat.? If yes, it will be quite the effort. Moreover, should
> the kernel allow graphics drivers hook the transition function and block
> transitions to "Screen Off" if there is an active CRTC? As that would be a
> significant undertaking, there should be proof that there exists such a
> device that has an issue firing the notifications with an active CRTC.
>
> A variant of this series has been tested by thousands of users by now,
> where the notifications fire around .5s before the CRTC is disabled and no
> ill-effects have found in regard to this quirk. AFAIK, it is a visual
> quirk. Making DPMS fire before the backwards compat. transition is a good
> idea in any case, as it will sync the 200ms between Display Off/Sleep Entry
> firing and the graphics driver turning off the display, but it might not be
> worth the effort.
>
> We are currently testing a DPMS patch for gamescope and it completely fixes
> this visual quirk while allowing for e.g., hibernation without turning on
> the screen. The DPMS gamescope patch + performing the transitions in
> userspace in such a way where it blends the Ally's suspend delay halves the
> user perceived delay to sleep and results in a very professional
> presentation. This presentation extends to other devices as well, such as
> the Legion Go.
>
> Link: https://learn.microsoft.com/en-us/windows-hardware/design/device-experiences/prepare-hardware-for-modern-standby [1]
> Link: https://learn.microsoft.com/en-us/windows-hardware/customize/power-settings/adaptive-hibernate [2]
> Link: https://learn.microsoft.com/en-us/windows-hardware/design/device-experiences/modern-standby-firmware-notifications [3]
> Link: https://github.com/hhd-dev/hwinfo/tree/master/devices [4]
> Link: https://git.kernel.org/pub/scm/linux/kernel/git/superm1/linux.git/log/?h=superm1/dsm-screen-on-off [5]
> Link: https://gitlab.freedesktop.org/drm/amd/-/issues/2719 [6]
> Link: https://dl.dell.com/manuals/all-products/esuprt_solutions_int/esuprt_solutions_int_solutions_resources/client-mobile-solution-resources_white-papers45_en-us.pdf [7]
> File: Documentation/admin-guide/pm/standby-states.rst [8]
>
> Changes from previous series (`acpi/x86: s2idle: move Display off/on calls
>   outside suspend (fixes ROG Ally suspend)`):
>   - Separate Display On/Off rename into its own commit (suggested by Hans)
>   - Move delay quirks into s2idle.c (suggested by Hans)
>   - Add documentation on Documentation/admin-guide/pm/standby-states.rst
>   - Callbacks are now static and a transition function is used
>   - Fixed all checkpatch warnings
>   - The rest of the series is completely re-written
>
> Antheas Kapenekakis (13):
>   Documentation: PM: Add documentation for S0ix Standby States
>   acpi/x86: s2idle: add support for Display Off and Display On callbacks
>   acpi/x86: s2idle: add support for Sleep Entry and Sleep Exit callbacks
>   acpi/x86: s2idle: add support for Turn On Display callback
>   acpi/x86: s2idle: add modern standby transition function
>   acpi/x86: s2idle: rename Screen On/Off to Display On/Off
>   acpi/x86: s2idle: call Display On/Off as part of callbacks
>   acpi/x86: s2idle: rename MS Exit/Entry to Sleep Exit/Entry
>   acpi/x86: s2idle: call Sleep Entry/Exit as part of callbacks
>   acpi/x86: s2idle: add Turn On Display and call as part of callback
>   acpi/x86: s2idle: add quirk table for modern standby delays
>   platform/x86: asus-wmi: remove Ally (1st gen) and Ally X suspend quirk
>   PM: standby: Add sysfs attribute for modern standby transitions
>
>  Documentation/ABI/testing/sysfs-power         |  34 +++
>  .../admin-guide/pm/standby-states.rst         | 133 ++++++++++
>  Documentation/admin-guide/pm/system-wide.rst  |   1 +
>  drivers/acpi/x86/s2idle.c                     | 249 ++++++++++++++----
>  drivers/platform/x86/asus-wmi.c               |  54 ----
>  include/linux/suspend.h                       |  16 ++
>  kernel/power/main.c                           |  75 ++++++
>  kernel/power/power.h                          |   1 +
>  kernel/power/suspend.c                        | 154 +++++++++++
>  9 files changed, 616 insertions(+), 101 deletions(-)
>  create mode 100644 Documentation/admin-guide/pm/standby-states.rst
>
> --
> 2.47.0
>
>