Skip to content

J713: cache a static frame while nothing is flipping (depends on #501) - #503

Closed
erikwb wants to merge 12 commits into
aurora-silicon:integration/aurorafrom
erikwb:j713/dcp-idle-cache
Closed

erikwb wants to merge 12 commits into
aurora-silicon:integration/aurorafrom
erikwb:j713/dcp-idle-cache

Conversation

@erikwb

@erikwb erikwb commented Oct 27, 2026

Copy link
Copy Markdown

Depends on #501. This branch is #501's head (c76eb68dd50f) plus one commit, drm/apple: m4: Cache a static frame while nothing is flipping. Only that last commit is for review here.

With a static image, the J713 panel keeps being scanned out of memory 60 times a
second. This registers a DCP idle-cache surface once no flip has happened for
appledrm.idle_cache_ms (default 1000 ms, 0 disables, max 60000), and leaves
the cache on the next present, brightness change or blank.

Power (J713, battery, backlight 0, 180 s per phase, same boot):

  • idle_cache_ms=0: 3266 mW
  • idle_cache_ms=1000: 2931 mW (-335 mW)

Tested on the J713:

  • Entry/exit: cached ~93% of a 10 min static run; the screen keeps showing
    the frame on screen, and the first swap after leaving the cache takes
    5-10 ms (ordinary swaps 12-14 ms).
  • Brightness changes, DPMS off/on and suspend-to-idle exit and re-enter the
    cache.
  • The text console (fbcon), which writes the scanout buffer without flips,
    updates live while cached.
  • A full-screen random-noise image caches without DART faults.
  • Fence refusals roll back cleanly and are retried; idle_cache_ms=0 stops
    entries; out-of-range values are rejected.
  • checkpatch --strict clean; builds with W=1 with no new warnings.

This change was developed and tested by Claude (Anthropic), working with me on the J713.

erikwb added 12 commits October 25, 2026 20:20
IOMFB 26.x takes setPowerState (A473) on the out-of-band channel, the
older contexts' +4, which the T8132 words carry as stream 1 (bit 10): the
AP packet goes at shared-memory offset 0x8000, the firmware answers on
the same stream, and the callbacks it makes meanwhile arrive at 0x68000
with bit 10 set. The J713 backend so far admitted stream 0 only.

Let a top-level AP call name stream 1, and while it is outstanding,
decode incoming words with bit 10 as stream-1 packets. Calls nested in
one of its callbacks stay on stream 1, at the next aligned offset after
the outstanding packet, as on stream 0. Stream-0 traffic is unchanged.
The first user is system sleep.

Signed-off-by: Erik Bourget <erik@ebourget.net>
Powering the J713 display down and up with setPowerState (A473), the
26.6.2 firmware calls back into the AP: acknowledgeSetPowerState
(D106), will_power_off_signal (D002), get_calendar_time_ms (D209),
powerUpDART (D572), swap_complete_intent (D592), set_kernel_power_assert
(D700), aot_supported_peek (D203), set_power_trace_enable (D008),
set_idle_caching_state_ap (D117) and enable_backlight_message_ap_gated
(D594). The exact-shape table refused them, which quarantines the
session.

Add their shapes and answer them for this machine: the notifications
and setters with an ACK, the status-returning calls with success, AOT
support with false, and the calendar time in milliseconds. The DART
and the power assertion are the AP's to hold: the J713 keeps the
display domains on across suspend-to-idle.

Signed-off-by: Erik Bourget <erik@ebourget.net>
The J713 DCP refused system sleep: the retained T8132 session had no
qualified handshake. setPowerState (A473) out of band is that handshake
on IOMFB 26.x: with the panel blanked, setPowerState(0) powers the
display pipeline down (the firmware logs "power on -> off" and disables
its M3 helpers) and setPowerState(1) brings it back, after which the
next present scans out again.

apple-drm is a consumer of the DCP, so its suspend handler has blanked
the panel when the DCP suspends, and its resume handler restores the
CRTC after the DCP resumed. Power the display down in between, with the
coprocessor running and its power domains kept on through the wakeup
path, as on the older DCPs; refuse sleep should the panel still scan
out. Presents while the display is down are refused.

The request is the 16-byte {u64 state; u8 flags[4]; u8 result_null;
u8 pad[3]}, the reply {u32 value; u32 status}; a nonzero status fails
the transition and the session.

Signed-off-by: Erik Bourget <erik@ebourget.net>
The J713's retained DMA owners refuse system sleep: the radio's DART
with managed streams, and the display DARTs, which borrow the running
DCP's live page-table root. Neither has a suspend boundary of its own,
and their state cannot be restored from a copy.

Suspend-to-idle does not need one. These DARTs have no power domain of
their own in the tree, and the DCP keeps the display domains on across
s2idle, so their state stays where it is. Let them through for
suspend-to-idle only, saving their registers solely for resume to check
that nothing moved. Every other sleep state is still refused.

A DART whose retained state changed cannot be trusted with its live DMA,
and a resume error does not stop the PM core from resuming consumers.
Quarantine the managed streams and the borrowed display consumers
through the same paths as a translation fault before failing resume, so
that their later maps are refused. A host test exercises the extracted
check, quarantine and map functions.

Signed-off-by: Erik Bourget <erik@ebourget.net>
The J713's radio port, whose streams the OS adopts from the boot
loader's handoff, refuses system sleep in its noirq phase. It has no
suspend boundary of its own, but suspend-to-idle does not need one:
the port has no power domain in the tree, so it stays up as any other
internal port does, whose resume does nothing. Let it through for
suspend-to-idle only; every other sleep state is still refused.

Signed-off-by: Erik Bourget <erik@ebourget.net>
A coprocessor can keep running in its power domain across system sleep
without system-sleep callbacks of its own, as the J713's GPU firmware
does. Describe such a domain, which the OS keeps on during system
sleep; runtime power management is unaffected.

Signed-off-by: Erik Bourget <erik@ebourget.net>
…tem sleep

The J713's G16 GPU has no system-sleep callbacks, so in the noirq
phase genpd powers its GFX domain off, and the TVM gate above it, under
the running GPU firmware; the power-on at resume then times out. Let a
domain marked apple,sleep-retained decline its power-off while the
system sleeps: genpd then keeps it and its parents on. Runtime power
management is unchanged.

Signed-off-by: Erik Bourget <erik@ebourget.net>
Some coprocessors message the AP periodically while idle: the J713's
GPU firmware asks for ring service about twice a second, and its AOP
sends a report every second. Describe a mailbox whose messages need not
wake the system, so the OS can leave them in the FIFO while it sleeps
and handle them at resume.

Signed-off-by: Erik Bourget <erik@ebourget.net>
…uest

The receive IRQ of the ASC mailboxes is IRQF_NO_SUSPEND, so every
coprocessor message wakes a CPU out of suspend-to-idle. On the J713
the GPU firmware's idle ring-service kicks and an AOP report every
second wake it about three times a second: in 938 s of s2idle, 1472
wakeups and only 512 s in deep idle.

For a mailbox marked apple,hold-rx-in-sleep, mask the receive IRQ from
noirq suspend to noirq resume, so its messages wait in the FIFO and the
consumer handles them on resume. The mailbox is a supplier of its
consumer, so this runs after the consumer suspended and before it
resumes. The firmware throttles once its outbox fills: after 148 s of
s2idle, 11 GPU and 3 AOP messages were waiting, with 3 wakeups and
146.9 s in deep idle.

Signed-off-by: Erik Bourget <erik@ebourget.net>
The G16 firmware keeps running across suspend-to-idle: keep its GFX
domain on through system sleep, and hold its idle ring-service kicks in
the mailbox instead of waking the system about twice a second.

Signed-off-by: Erik Bourget <erik@ebourget.net>
With DPMS off, the J713 display was only blanked: the swap to no
surface stops presenting, but the display pipeline stays powered. A
blanked panel is the state in which system sleep powers the display
down with setPowerState(0), and the same works here.

After a successful blank, power the display down, and power it back up
with setPowerState(1) before the next present, so the first frame after
DPMS on brings the panel back. System sleep with the display already
down leaves it down and skips its own setPowerState(0); after resume the
next present powers it up. A refused transition fails the session, as
in system sleep. debugfs reports whether the display is down, how often
it went down, and how long the last and longest power-down and power-up
took.

On the J713, idle on battery with the backlight off, DPMS off saved
346 mW before this change (3.04 -> 2.69 W) and saves 1.20 W with it
(2.93 -> 1.73 W), each measured within one run. Over 200 blank/wake
cycles, power-down took 128-132 ms and power-up 55-56 ms.

Signed-off-by: Erik Bourget <erik@ebourget.net>
With a static image on the J713 panel, the display keeps scanning the
frame out of memory 60 times a second. The DCP can instead cache it
(IdleCachingMethod 2), but only after the host registers a cache surface:
a swap with intent 8 whose sole surface is a separate surface object.
Setting the method alone does nothing.

Once no flip has followed for idle_cache_ms (default 1000, 0 disables),
set the method and submit that swap with a dedicated, retained surface
of the panel geometry, which the host never writes. The DCP keeps
presenting the frame on screen, steps through its idle states (D117) and
asks for a fence (D578) on entry and on each later refresh of the cache,
granted for the surface this host registered; the next present,
brightness change or blank leaves the cache (D579, then state 0). The
surface stays registered across exits, so later entries resubmit it.
After a rollback without a flip, entry is retried after ten quiet
periods. The surface is allocated at startup; without it, or if the DCP
refuses the method, the cache stays off. debugfs reports the idle state,
open fences and the number of entries.

On the J713, idle on battery with the backlight off, this saves 335 mW
(3.27 -> 2.93 W) within one run. Brightness changes, DPMS off/on and
suspend-to-idle leave and re-enter the cache.

Signed-off-by: Erik Bourget <erik@ebourget.net>
@erikwb

erikwb commented Nov 2, 2026

Copy link
Copy Markdown
Author

Superseded by #830, where this is commit 5.

@erikwb erikwb closed this Nov 2, 2026
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