Skip to content

drm/apple: start and light the Neo's internal panel from the resume, as macOS does - #1631

Merged
DjDeveloperr merged 2 commits into
integration/aurorafrom
fix/t8140-dcp-wake-macos
Nov 12, 2026
Merged

DjDeveloperr merged 2 commits into
integration/aurorafrom
fix/t8140-dcp-wake-macos

Conversation

@DjDeveloperr

@DjDeveloperr DjDeveloperr commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

On a wake from a DRAM-kept sleep, the Neo (J700) starts the internal display only when KWin turns the output back on after the thaw. macOS powers the DCP about 80 ms after the wake, from its kernel. These two commits start the internal DCP's firmware from the resume and, on a full wake with the lid open, light the panel from the resume too. The backlight still comes with KWin's first frame.

Everything below was measured on the Neo, including real lid wakes.

Commits (on integration 2b05915f, #1445 merged)

  • 21b2d172 drm/apple: start the internal display's firmware from the resume
    • On a real wake that isn't dark, with the panel off by DPMS, the resume queues the warm restart (domains, DARTs, firmware) on a high-priority work instead of waiting for DPMS on.
    • DPMS on then only needs set_power_state(1).
    • If userspace never turns the output on, a timeout stops the firmware again after wake_prewarm_ms (default 10000).
    • wake_prewarm_ms=0 restores today's behaviour.
  • b5989a04 drm/apple: light the internal panel from the resume, as macOS does
    • The SMC's noirq resume reads the lid on every wake. With the lid open, the resume also sends macOS's power-on calls (A414, A478, A476 clamshell, A412, A473), in the recorded order, and waits for D572.
    • The panel stays dark until KWin's first frame. The brightness present that today reuses the frame from before the sleep is held for that first new frame.
    • With the lid closed, nothing starts. wake_light=0 gives the first commit alone.

Measured on the Neo (J700), 2026-11-11

The patched kernel was booted from the SSD. Both arms ran on one boot, interleaved: A is wake_prewarm_ms=0 (today), C is the defaults. Times are seconds after the PMU RTC alarm, medians of 12 wakes, on the ARM generic counter.

Stage A: today C: this PR C − A
Linux back 0.014 0.015 0
Suspend exit 0.529 0.528 0
Internal DCP firmware up 0.584 0.279 −0.305
Panel on from the resume — 0.336
KWin's DPMS on / mode set 0.640 0.591 −0.049
Scanout running 0.844 0.537 −0.307
Backlight on 0.861 0.604 −0.257
Brightness present on the frame from before the sleep 11 of 11 wakes 0 of 12
  • Today's path puts the frame from before the sleep back on the panel on every wake, before KWin's first frame. With this PR it never shows.
  • The backlight level 2 s after the wake equals the level before it in both arms. The one exception is the first pair, which ran 40 s after boot, before the session's output was up.
  • Safety: 10 DRAM-kept sleeps on the defaults, 10/10 returned, with no commit latch, deep path or refusal. No no D572, display power-on, firmware start or crash line in any arm.
  • Remaining gap to macOS: the firmware is now up 0.28 s after the alarm, against about 0.08 s on macOS, because it waits for the resume phase. The backlight follows KWin's first frame, which waits for userspace's thaw (suspend exit 0.53 s).

Lid wakes (2026-11-11 23:37, patched kernel, defaults)

The owner closed and opened the Neo's lid, and the panel came back each time and looked fine. The Neo's log has six lid cycles from 23:37:34 to 23:37:58, each with logind's "Lid closed." and "Lid opened.", 1-3 s apart. All six were DRAM-kept sleeps, woken by the lid (SMC wake record MBSW 00 04 ...: lid, wake source the MTP DockChannel or the SMC outbox), and all returned.

  • Three of them started with the panel off by DPMS, and took this PR's path:
    • wake: panel on in 56 ms (firmware 1 ms), dark until the first frame from the resume;
    • DPMS on: panel already on, 213-215 ms after the resume lit it;
    • wake: first new frame, backlight back with it.
  • The other three started with the panel still lit, because the lid closed again about a second after the previous wake, before KWin turned the output off. Those took the existing lit-at-sleep path, which this PR doesn't change: the DCP's power off at the sleep, then the firmware restart and mode set from the DRM resume. The panel came back on those too.

Details: migration/neo-continuation-20261108/dcp-wake-macos/README.md (macOS's order from the recording, and the design) and RESULT.md (this A/B).

🤖 Generated with Claude Code

When the panel was off by DPMS before a sleep, as it is when KWin
blanks the outputs for it, the DCP firmware stays stopped through the
resume. It starts only when KWin turns the output on again after the
thaw: on the Neo that is 0.57 s into the wake, and the warm restart
then runs inside the modeset.

macOS doesn't wait for that. In both wakes of the recording with the
display in use, the display domains go on 78-82 ms into the wake,
DCP's HELLO comes 0.5 ms later and its first display call about 10 ms
after that, which by its timing comes from the kernel's power path, not
WindowServer. Frames follow from about 0.3 s.

Start the firmware from the DCP's resume on a work item, so it comes up
while the other devices resume, and leave the panel dark. That is the
state a refused idle leaves: the firmware running and the display off.
DPMS on, DPMS off and the next sleep already handle it, and they now
wait for the start first. DPMS on then only sends set_power_state(1)
and waits for the scanout DART, as on a refused idle.

If DPMS on doesn't come within wake_prewarm_ms (10 s), the firmware
stops again as DPMS off stops it, so a closed lid or a session that
keeps the output off ends up as before. A dark wake doesn't start it.
wake_prewarm_ms=0 keeps the old behaviour.

Signed-off-by: Diljit Singh <hey@djdev.me>
With the firmware started from the resume (the previous patch), the
panel itself still waits for KWin's DPMS on, 0.57 s into a wake on the
Neo. macOS doesn't wait for WindowServer there either: about 10 ms after
DCP's HELLO, 90 ms into the wake, its power path sends the display-on
calls, A414(0), A478 twice, A476(1) (the clamshell state), A478, A412
and set_power_state(1), and frames follow from about 0.3 s. Its
backlight comes on after the first frame.

On a full wake with the lid open, do the same from the resume work:
after the warm restart, send those calls and wait for D572, as DPMS on
would. The panel is then powered with its backlight at the DPMS-off
level, 0. When KWin turns the output on, DPMS on finds the panel
already on and leaves only the mode set and the first frame to the
commit. That frame carries the backlight level from before the sleep,
as DPMS on already brings it back. If DPMS on doesn't come within
wake_prewarm_ms, set_power_state(0) and the firmware stop turn it off
again, and a sleep before then does the same.

The backlight's own present reuses the retained scanout, and after a
sleep that's the frame from before it, which needn't be what KWin shows
next: the session may lock as it resumes. Hold that present after a
wake until KWin's first frame, which then carries the level, so nothing
from before the sleep is shown. The log says when the old frame would
have been reused, also with wake_light=0.

The lid comes from the SMC: its noirq resume now reads MSLD on every
wake, not only on a wake for the lid alone, and the sleep driver keeps
it for the display. With the lid read closed, the firmware and panel
stay off, as macOS keeps the internal display off with the lid closed.
A dark wake lights nothing, as before. A lid that couldn't be read
gives the previous patch's behaviour.

wake_light=0 gives the previous patch's behaviour, and
wake_prewarm_ms=0 the behaviour before both.

Signed-off-by: Diljit Singh <hey@djdev.me>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant