Repository navigation
Conversation
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>
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>
This was referenced Nov 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The J713's (MacBook Air 13" M4, T8132) display pipeline stays powered whenever the panel is on: through system sleep, while it is blanked, and while a static frame is shown. This series powers it down with the DCP's own handshake. It replaces the display half of #449, plus #501 and #503, as one drm/apple PR; the commits are unchanged.
The handshake (commits 1–3)
On IOMFB 26.x the handshake is
setPowerState(A473) on the out-of-band channel. The T8132 words carry that channel as stream 1 (bit 10).setPowerState(0)makes the firmware logpower on -> off, disable its M3 helpers and switch to run mode 0.setPowerState(1)brings it back (run mode 0 → 4, M3 firmware reloaded), and the next present scans out.The three commits are:
apple-drm is a device-link consumer of the DCP, so it has blanked before the DCP suspends and restores the CRTC after it resumes. The DCP keeps its domains on through the wakeup path, as the older DCPs do.
Power the display down while it is blanked (commit 4)
DPMS off (screen lock, idle blank,
hyprctl dispatch dpms) used to only blank the display:iomfb_m4_blank()swaps to no surface, but the pipeline stays powered until the next frame.setPowerState(0), the same call system sleep makes.setPowerState(1)before its swap, so the compositor's first frame brings the panel and backlight back. That's the path resume already uses.setPowerState(0), and the display stays down after resume until the next present.iomfb_m4gainsdisplay_off,off_cycles, andoff_us/on_us(last and longest).Cache a static frame (commit 5)
With a static image, the 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, and leaves the cache on the next present, brightness change or blank.Testing (J713)
On integration/aurora 41c4055 with the rest of the J713 PR stack (this series, the suspend-to-idle and USB series, #429, #432, #819/#820, #450, #531, #825):
error=0.Earlier, on our J713 tree:
The device tests ran on our J713 tree, which has the J713 series this branch alone lacks (the display doesn't come up on integration/aurora alone yet). The commits are identical to the ones tested. Today, on integration/aurora 41c4055 with the rest of the J713 stack, it went through several real suspend-to-idle cycles of up to 352 s, each resuming to a working desktop.
System sleep:
systemctl suspend(up to about 16 min asleep) and from the lid. Display and GPU come back (glmark2 630–642, unchanged), and so do Wi-Fi and audio.pm_test=devicesandpm_test=platformpass.DPMS off, power: idle on battery, 2 s samples over 120 s per phase, medians. Each column is one run on one boot, so compare within a column.
DPMS stress: 200 blank/wake cycles through Omarchy's DPMS commands, checking debugfs after every step. All 200 passed, with no errors.
setPowerState(0)setPowerState(1)The power-down runs inside the commit that disables the CRTC, so that commit takes about 130 ms longer. Hyprland's DPMS dispatch still returns in 10–12 ms.
DPMS, other cases:
Idle cache: J713, battery, backlight 0, 180 s per phase, same boot.
idle_cache_ms=0draws 3266 mW,idle_cache_ms=1000draws 2931 mW: −335 mW.Host tests:
apple-dcpmatches integration/aurora, with the same failures on the bare base. New cases cover stream-1 decoding and routing, and a present refused while powered off.m4-dcp-errorspasses, including its case where a failed display domain refuses maps.m4-dpms-retirement, the host test of the production blank and present paths, gets stubs for the new calls in commits 4 and 5. It now also asserts that a blank powers the display down once, the next frame powers it back up, and every shown frame restarts the idle-cache countdown. It passes at both commits.Checks (on integration/aurora 41c4055):
checkpatch --strictis clean;CHECK_DTBSW=1 on all six T8132 DTBs gives the same output as the base;run-host-tests: 132/133. The one failing group,apple-dcp, fails the same nine cases on the bare base and nothing more.Known limits
IOAVVideoInterface open failedafter each power-up, with no effect.This series was developed and tested by Claude (Anthropic), working with me on the J713.