Skip to content

drm/apple: set an external mode again once the display is on - #295

Merged
chriskearney merged 1 commit into
integration/aurorafrom
fix/m3-pro-dsc-mode-after-power-on
Oct 30, 2026
Merged

chriskearney merged 1 commit into
integration/aurorafrom
fix/m3-pro-dsc-mode-after-power-on

Conversation

@erykwieliczko

@erykwieliczko erykwieliczko commented Oct 30, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

On the M3 Pro (J514S), a 4K TV attached at its preferred 3840x2160 @ 100 Hz mode stays dark: the system stalls for about 5 s, then the external display processor is stopped until a reboot (DCP call 0x41343037 failed: -110; recovery requires a reboot). The same happens on the HDMI port. These are attaches in a mode that needs DSC; 4K @ 120 Hz (also DSC) came up in all 5 attaches we logged, so the race does not hit every DSC attach. 60 Hz never hit it.

Cause

dcp_v14_external_modeset() sets the mode (A411) with the display powered down, then powers it up (A472). The firmware log of a failing attach:

mode_set_gated(): 3840x2160@100 Hz DSC=YES VRR=NO link: 1
ERROR: Modeset done, but pipe not enabled: fSoftPowerState=0, fDisplayPowerState=0, fHardPowerState=1
Resetting DSC and it's PS on modeset failure
IOMFB: power off -> on vmaod 0 aot 0
configure_dsc: Configuring DSC..
display mode "3840x2160": 100 … (timing 167, color 219): 0          <- A472 returned
IOMobileFramebuffer::hotPlug_notify(uint64_t)
swap_submit_dcp: swallowed swap ID 1 as timinsg are not enabled for External Display
mode_set_gated(): 3840x2160@100 Hz DSC=YES VRR=NO link: 1             <- the firmware's own mode set
set_run_mode_safe: deferring: 2 -> 4
switch to normal mode succeeded
DCP call 0x41343037 failed: -110; recovery requires a reboot           <- 5 s later

With DSC, the mode set on the unpowered pipe fails and resets DSC, and the power-on only configures DSC. The timings start later, in a mode set the firmware makes after a hotplug notification it sends itself. A swap that reaches the firmware before then is dropped without a completion, and A407 waits for that completion until it times out.

Without DSC, the power-on itself starts the timings (deferring: 2 -> 4 and switch to normal mode succeeded both come before A472 returns), so the first swap is shown.

The race is lost depending on timing. Re-enabling an already running external display processor won 16 of 16 times, but a fresh attach (plugging in, or booting with the display attached) lost 3 of 3.

Fix

Set the mode once more after the power-on, with the pipe enabled. The firmware then starts the timings before that call returns, so before the first swap. Its own mode set after the hotplug notification then finds them running (set_timings() called after timings were started, the same as a non-DSC attach already logs).

Testing

J514S (M3 Pro), Omarchy linux-aurora 7.2.11.aurora11 (integration/aurora 3bdf11d) plus this patch (rebased onto ec8bf85 for this PR without conflicts; #259 only touches the panel backlight). The display is a 4K TV with 43 modes, including 3840x2160 @ 100/120/119.88 Hz with DSC.

  • Fresh attach at boot, 3840x2160 @ 100 Hz DSC, Type-C:
    • without the patch, 3 of 3 failed with the A407 timeout;
    • with it, 5 of 5 came up, with swaps 1–3 complete.
  • Every mode the TV offers, each set from off (output disabled, then enabled directly in that mode), no timeout and no swap error:
    • Type-C: 43 of 43;
    • HDMI port: 43 of 43.
  • Both ports at once in the preferred 3840x2160 @ 100 Hz DSC mode (compositor default, no pin): both up, no timeout.
  • New KUnit suite dcp_v14_external_modeset with two cases: an attach (display off) sends A411, A472 on, A411; a mode change sends A472 off, A411, A472 on, A411.
  • run-kunit.py: 144 suites, 1735 cases: 1732 pass, 0 fail, 3 skip (the expected ones in kunit-expected.json), no splats; the new suite passes 2/2.
  • check-commits.py / checkpatch.pl --strict: 0 errors; the only warning is the generic "does MAINTAINERS need updating?" for the new test file, and drivers/gpu/drm/apple/ is already covered.

The check was for crashes and timeouts only. The picture on the TV was not inspected for every mode.

Notes

  • Non-DSC modes also get the second A411. It costs one more mode set call per attach; the firmware already sets the same mode again after its hotplug notification. All 40 non-DSC modes in the sweeps came up without a timeout or swap error.
  • The workaround users can apply today without this patch is pinning the output to 3840x2160@60 in the compositor.

🤖 Generated with Claude Code

An external mode is set with the display powered down, and the display
is powered up after it. For a mode that needs DSC (3840x2160 at 100 and
120 Hz on the J514S HDMI and Type-C ports) the firmware refuses that mode
set ("Modeset done, but pipe not enabled") and resets DSC, and the
power-on only configures DSC: the timings start in a mode set the
firmware makes on its own, after a hotplug notification it sends itself.
A swap that reaches it before then is dropped ("swallowed swap ID 1 as
timings are not enabled") and never completed, so A407 times out after
5 s and the display stays dark until a reboot. A fresh attach lost that
race 3 times out of 3. Without DSC, the power-on starts the timings
itself.

Set the mode once more after the power-on, with the pipe enabled. The
firmware then starts the timings before that call returns, before the
first swap; its own mode set finds them running. KUnit cases check
the calls of an attach and of a mode change.

Signed-off-by: Eryk Wieliczko <eryk@wieliczko.ninja>
Assisted-by: Claude:claude-opus-5-5

@chriskearney chriskearney left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed exact head 7ef3bc1. No source-level blocker found: the second A411 follows successful power-on under the existing RPC lock, and its failure cannot publish the mode as live. Existing admission, already-showing fast path and error handling remain intact.

Independent validation: 27/27 source-extracted host controls passed; the unmodified base fails the added-order check, and 6/6 focused mutants were detected. These cover command ordering, IDs, status/transport failures, locking and scope. The new production-function KUnit suite also passed 2/2 in PR CI. The exact per-file merge onto 31748f0 is clean and preserves #225/#287. The later integration tip 919b433 adds only host-test/CI-vouch files, leaving these driver inputs unchanged.

Our physical display coverage adds J516S, built-in HDMI, 3840×2160@120 Hz with DSC (timing 53, color 88):

  • Cold attach after boot completed the A411 → A472-on → A411 sequence successfully; output off/on also returned successful mode status.
  • Visual inspection confirmed a clean picture. The declared DCP/DART fault checks recorded zero matches.
  • The tested #298 package combines the source-disjoint #295/#297 changes on 31748f0; the primary A2 display test had #297's HDMI-audio opt-in off. Supplemental opt-in B attach/replug observations also returned mode status 0; only those display observations are included here.

One early sn201202x/SPMI “Got interrupt but selection not complete?” warning remains recorded and unattributed. That source is byte-identical between the tested package and 31748f0; it is not hidden by the display-fault result or attributed to this patch.

The author's J514S results on older 3bdf11d plus the patch—3/3 failing fresh attaches before, 5/5 successful after, and 43-mode sweeps per port—remain separate, attributed author evidence, with the stated limit that not every picture was visually inspected.

Our new qualification is bounded to the J516S HDMI observations above. It does not establish USB-C, boot-with-sink-attached, suspend, audio-playback or other-board/default coverage. The source supports approval of this fix; normal current-base merge-queue CI is still required. The earlier PR CI used the older ec8bf85 base, and the combined-package run does not replace that gate.

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.

2 participants