Skip to content

thunderbolt: apple: let T6020 DP IN tunnels wait for a display pipeline - #426

Open
nekolyanich wants to merge 1 commit into
aurora-silicon:integration/aurorafrom
nekolyanich:pr/m2-capacity-retry
Open

nekolyanich wants to merge 1 commit into
aurora-silicon:integration/aurorafrom
nekolyanich:pr/m2-capacity-retry

Conversation

@nekolyanich

Copy link
Copy Markdown

On T6020/T6021 (M2 Pro/Max) apple_dpin_m2 leaves capacity_retry off, so a DP IN tunnel that finds
no free display pipeline gets -EBUSY once and that is final (dpin0: DP tunnel setup failed: -16).
The CM keeps the tunnel through the DPRX timeout, but nothing asks for a pipeline again, and the
display stays dark.

On a MacBook Pro 16" M2 Pro (j416s) with a Dell on Thunderbolt and a TV on a USB-C to HDMI adapter,
the TV takes dcpext0 first after a system resume and the tunnel is refused; the fabric frees a
pipeline a few seconds later, too late. The Dell came back after 0 of 2 resumes (aurora 7.3
ac6b017, same thunderbolt and appledrm code).

Change

  • capacity_retry = true on apple_dpin_m2: a refused tunnel waits and retries as on M1.
    TB_HOST_DP_KEEP_DPRX_TIMEOUT already keeps M2 tunnels, so no dp_tunnel_awaits_display hook.
  • The PM gate (pause/resume of retries, end of the gate at NHI removal) now covers capacity_retry
    policies too. M2 pauses at prepare also at the default resume_recovery=0, and resumes when
    complete starts, so a tunnel refused inside complete retries on time. M1 at the default is unchanged.
  • apple_dpin_end_pm_gate() pauses before draining, so work in flight cannot rearm the retry.
  • -EAGAIN waits too, as on M1.
  • Comments that named M1-family hosts; KUnit cases for the M2 wait, sleep and unplug.

Base M2 (T8112) and M3 are unchanged. omacom 068fd09 restores the same flag for M3 (T6030);
M2 never had it.

Testing — j416s, DCP 13.5, under the m1n1 hypervisor, pm_test=platform, with a bench-only DT
change that keeps the hypervisor's USB-C port out of the DCP routes (t602x-j414-j416.dtsi):

  • Dell + TV: Dell back after 6 of 6 resumes (0 of 2 without), routed 6.2–6.5 s after the refusal and
    3.4–3.8 s ahead of the DPRX timeout; no DPRX timeout, no kernel WARNINGs. DP audio played on both
    sinks before the suspend, 16 of 16.
  • Dell alone: back after 4 of 4 resumes, no wait, no DPRX timeout, no WARNINGs.
  • KUnit apple-dpin-lifecycle: 35 of 35.

Not tested: natively; a suspend while a tunnel waits (KUnit only); j414s, j414c, j416c; other
firmware; a wait past the DPRX timeout; NHI removal while waiting.

apple_dpin_m2, the T6020/T6021 (M2 Pro/Max) ACIO policy, leaves
capacity_retry off "pending M2 hardware qualification". A DP IN tunnel
that finds no free display pipeline gets -EBUSY once and that is final
("dpin0: DP tunnel setup failed: -16"). The connection manager keeps
the tunnel through the DPRX timeout (TB_HOST_DP_KEEP_DPRX_TIMEOUT), but
nothing asks for a pipeline again, so the display stays dark.

On a MacBook Pro 16" M2 Pro (j416s) with a monitor on a Thunderbolt
cable and a TV on a USB-C to HDMI adapter (DP Alt Mode), the fabric
moves the TV to dcpext1 at boot and gives the tunnel dcpext0. After a
system resume the TV comes back first on dcpext0 and the tunnel's
request is refused. The fabric moves the TV to the other pipeline
a few seconds later, too late for a request that is not retried.

Set capacity_retry on apple_dpin_m2. A refused tunnel then waits as on
M1: the DP IN work arms its retry and routes the tunnel once a pipeline
comes free. M2 needs no dp_tunnel_awaits_display hook, which is set up
for the APPLE_DPIN_CHANGED flow only: KEEP_DPRX_TIMEOUT already keeps
its tunnels. As on M1, -EAGAIN waits too: an attempt that prepare cut
short returns it and has to ask again after complete. A DCP service
that is never published then waits until unplug, with a 120 s readiness
wait per retry, where it used to fail once.

Pause these retries across system sleep and at NHI removal. The PM gate
(apple_dpin_pause_retries(), apple_dpin_resume_retries(),
apple_dpin_end_pm_gate()) now applies wherever the policy has
capacity_retry or defer_new_bringup, not defer_new_bringup only.
apple_nhi_prepare() pauses the retries of a gated policy without
defer_new_bringup (M2) before its resume_recovery=0 return, so at the
default a waiting M2 tunnel stops asking at prepare; with
resume_recovery set it pauses where M1 does. Either way its pause ends
when complete starts, before tb_domain_complete(): every device has
resumed by then, and the tunnel that the domain's complete brings back
makes its first attempt there, since M2 does not defer it. Its retries
then run from the refusal on, as before, and do not wait for the end of
complete, which can take seconds. M1 and M3 at the default are
unchanged: their pause stays behind resume_recovery (6ceb98a).
Without the gate a waiting M2 tunnel would keep calling into appledrm on
the non-freezable dp_wq while the DCPs power off, and after the NHI is
removed. M2 does not get defer_new_bringup: that would defer every first
M2 handoff across sleep, which has not been tested, and DPRX retention
for a deferred handoff goes through the hook M2 does not install.

apple_dpin_end_pm_gate() now pauses both adapters before it drains
them, as apple_dpin_pause_retries() does: work in flight at NHI removal
could otherwise get -EBUSY and rearm the retry it had just cancelled.
On M1 this is the only change: a bring-up in flight at NHI removal is
deferred and then dropped instead of attaching. Base M2 (T8112) uses
apple_dpin_m1, and M3 is unchanged.

Update the comments above apple_dpin_work_fn() and
apple_dpin_pause_retries(), which named M1-family hosts. Tests: the M2
row of apple_dpin_all_policy_fields; the t602x rearm trace, whose -EBUSY
now waits and recovers; the capacity trace run for M2 as well; terminal
errors end an M2 wait; apple_dpin_m2_capacity_suspend (pause, resume, a
refusal after the pause has ended, resume with no pause, -EAGAIN under
pause, unplug while waiting, end of the gate).

In omacom/linux-aurora, commit 068fd09 ("thunderbolt: apple: let
T6030 DP IN tunnels wait for a display pipeline") restores the same
flag for M3 after the aurora-silicon#139 merge dropped it; M2 never had it.

Testing: j416s, DCP firmware 13.5, under the m1n1 hypervisor,
pm_test=platform, with a bench-only DT change that keeps the
hypervisor's USB-C port out of the DCP routes. Monitor and TV: the
monitor came back after 6 of 6 resumes with this change (0 of 2 without,
on aurora 7.3 ac6b017, same thunderbolt and appledrm code), routed
6.2-6.5 s after the refusal and 3.4-3.8 s ahead of the DPRX timeout; no
DPRX timeout, no kernel WARNINGs. Monitor alone: back after 4 of 4
resumes, no wait. KUnit apple-dpin-lifecycle 35 of 35.
Not tested: natively, a suspend while a tunnel waits (KUnit only),
j414s, j414c, j416c, other firmware, a wait past the DPRX timeout, NHI
removal while waiting.

Assisted-by: Claude:claude-opus-5-5 checkpatch
Signed-off-by: nekolyanich <me@nekolyanich.com>
@RyanTheTide

RyanTheTide commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

The current port is review/pr426-integration-t3094-v3, head bb91b3f. It preserves original authorship and sign-offs, #395's M3 retry/suspend behavior, the M2 capacity trace and terminal-error coverage across all three policies. Earlier public refs and the contributor's branch remain intact.

On integration/aurora at 421e314 with shared #438 and #456, DCP is PASS (63/63). KUnit has 906 passes and 1 policy skips. Universal WERROR is PASS; Fedora-base WERROR is PASS.

Software gates pass; hardware and shared #438/#456 review pending. The operator reports #456 passed all 63 host groups with stock make 3.81 and GNU Make; this worker did not rerun native macOS on the assembled base. Hardware still needed: J416s capacity retry, suspend, unplug and NHI removal; M1 Pro/Neo regression.

@nekolyanich

Copy link
Copy Markdown
Author

Tested on J416s (M2 Pro), review/pr426-integration-t3094-v3 (bb91b3f) plus the bench DT change, under the
m1n1 hypervisor, Dell on TB + TV on the adapter.

  • KUnit apple-dpin-lifecycle: 36/36.
  • Capacity retry: the Dell came back after 5/5 resumes (3 boots): refused (waiting for one), then routed when
    the TV moved, 6.3-6.6 s after the refusal, 3.4-3.7 s before the DPRX deadline. 0 DPRX timeouts. Same timing as
    our build of the retry.
  • Suspend with no sink: 3/3 back.
  • Unplug: 11/11 Dell replugs came back, 0 -95, 0 DPRX timeouts (1 session).
  • NHI removal with the Dell: the unbind was clean (tunnel down, crossbar released, the TV moved back, no retry
    left waiting). On the first bind the ACIO firmware crashed 0.5 s later (assert_id=1385) and later binds timed
    out. That's the base: our own build without your port crashes the same way (1 session each). With no sink
    there's no NHI to remove (ACIO only starts for a USB4 cable).
  • Two DPTX set_hpd -110 per resume while the tunnel waits, every resume, same on our build: firmware, not the PR.
  • One base observation: in 1 of 4 boots with sinks the USB4 link dropped 18 ms after the Dell's tunnel came up
    at boot, and every later tunnel got -95 (no retry on that path). It's the boot-time variant of the quick-drop
    bug; 086e6b9 doesn't cover it. Not this PR.
  • Not run: natively; the Dell alone (retry should cost nothing).

Runs and this summary were prepared with Claude (Opus 5.5); counts checked against the logs.

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