Skip to content

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

Open
RyanTheTide wants to merge 1 commit into
integration/aurorafrom
rescue/pr426-t6020-dp-in-retry
Open

RyanTheTide wants to merge 1 commit into
integration/aurorafrom
rescue/pr426-t6020-dp-in-retry

Conversation

@RyanTheTide

Copy link
Copy Markdown
Contributor

Let T6020/T6021 (M2 Pro/Max) DP IN tunnels wait for a free display pipeline, so a Thunderbolt display comes back after resume. Rebased onto integration/aurora at 78ea426.

This is nekolyanich's change from #426, as ported in review/pr426-integration-t3094-v3 (bb91b3f). Authorship and sign-off are unchanged; the port applies to current integration without conflicts.

Problem

apple_dpin_m2 leaves capacity_retry off, so a DP IN tunnel that finds no free pipeline gets -EBUSY once and that is final (dpin0: DP tunnel setup failed: -16). The connection manager keeps the tunnel through the DPRX timeout, but nothing asks again and the display stays dark. On a MacBook Pro 16" M2 Pro (j416s) with a monitor on Thunderbolt and a TV on a USB-C to HDMI adapter, the TV takes dcpext0 first after resume and the tunnel is refused; the fabric frees a pipeline a few seconds later, too late.

Change

  • capacity_retry = true on apple_dpin_m2: a refused tunnel waits and retries as on M1. -EAGAIN waits too. TB_HOST_DP_KEEP_DPRX_TIMEOUT already keeps M2 tunnels, so there is no dp_tunnel_awaits_display hook.
  • The PM gate (pause/resume of retries, end of the gate at NHI removal) 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 in time. M1 and M3 at the default are unchanged.
  • apple_dpin_end_pm_gate() pauses both adapters before draining, so work in flight cannot rearm the retry it just cancelled.
  • KUnit: the M2 row of apple_dpin_all_policy_fields, the t602x rearm trace, the capacity trace for M2, terminal errors ending an M2 wait, and apple_dpin_m2_capacity_suspend.

Base M2 (T8112) uses apple_dpin_m1 and is unchanged.

Testing

On this branch:

  • KUnit (kunit.py, arm64 qemu): apple-dpin-lifecycle 37/37 including apple_dpin_m2_capacity_suspend, thunderbolt suite passing; 108 of 109 overall, the one skip is an unrelated G17 mailbox case that needs CONFIG_UCLAMP_TASK.
  • checkpatch.pl --strict: clean.

Hardware, by nekolyanich on J416s (M2 Pro) with this port (bb91b3f) under the m1n1 hypervisor, monitor on TB plus TV on an adapter:

  • Capacity retry: the monitor came back after 5/5 resumes over 3 boots, routed 6.3–6.6 s after the refusal and 3.4–3.7 s before the DPRX deadline; 0 DPRX timeouts. Without the change it came back after 0 of 2.
  • Suspend with no sink: 3/3. Unplug: 11/11 replugs came back, 0 -95, 0 DPRX timeouts.
  • NHI removal with the monitor: clean unbind (tunnel down, crossbar released, no retry left waiting). The ACIO firmware crash on the following bind happens on the base without this change too.

Still to run: a native (non-hypervisor) J414s/J416s row, and a no-regression boot and resume on base M2 (T8112) and M1.

Thanks to nekolyanich for finding, fixing and testing this.

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 #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>
Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
@RyanTheTide RyanTheTide added soc:T6020 Hardware discussions for T6020 (M2 Pro) soc:T6021 Hardware discussions for T6021 (M2 Max) needs hardware test labels Nov 12, 2026
@RyanTheTide

Copy link
Copy Markdown
Contributor Author

M2 (J413, T8112) no-regression row. This board uses apple_dpin_m1, so the only path the change touches here is the reordered apple_dpin_end_pm_gate() at ACIO removal. There is no Thunderbolt display on this bench, so this covers boot, resume and removal only.

Kernel: 7.3.0 at c6c15ac with 2c361fb cherry-picked (drivers/thunderbolt identical to the PR head), one-shot boot, SELinux enforcing.

  • Boot: thunderbolt + thunderbolt_apple loaded, both ACIOs (381ac0000.cio, 501ac0000.cio) bound to thunderbolt-apple-acio. Kernel -p 3 empty. Kernel warnings are the same 3 lines as the unpatched kernel (2x btrfs swapfile, Bluetooth LE Coded PHY).
  • s2idle (RTC wake): 3/3 rc 0.
  • ACIO unbind/bind with no sink, both controllers: rebound cleanly; tps6598x reapplied the cable state on each.
  • s2idle after the rebind: 1/1 rc 0.
  • Counters over the whole boot: 0 WARNING, 0 UBSAN, 0 BUG, 0 DP tunnel setup failed, 0 DPRX timeouts, 0 thunderbolt/ACIO errors, 0 aborted suspends, 0 DART faults, 0 AVC.

No regression on M2. The T6020/T6021 retry path itself still needs a native J414s/J416s row.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs hardware test soc:T6020 Hardware discussions for T6020 (M2 Pro) soc:T6021 Hardware discussions for T6021 (M2 Max)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant