Skip to content

j615: external DisplayPort over USB-C (dcpext), with fbdma framebuffer mirror - #356

Open
ericjuta wants to merge 7 commits into
omacom:integration/aurorafrom
ericjuta:j615/dcpext
Open

ericjuta wants to merge 7 commits into
omacom:integration/aurorafrom
ericjuta:j615/dcpext

Conversation

@ericjuta

@ericjuta ericjuta commented Nov 4, 2026

Copy link
Copy Markdown

External DisplayPort over USB-C on the J615 (MacBook Air 15", M3), on both ports. It's the J613 dcpext path from #103/#114 plus two fixes the J615 needs. Reports and logs are in #103.

Commits

  1. soc: apple: t8122 display gate: dcpext_disp_dart switch. apple_t8122_display.dcpext_disp_dart=0 enables the dcpext without appending dispext0_dart to the display subsystem's iommus. On the J615 that shared domain faults the internal DCP's DART (28d304000, NO PMD/PTE at 0x1000a000000..) about 2 s into boot, and the internal DCP crashes (the dark panel of kboot: reserve the T8122 dcpext firmware only with chosen.asahi,t8122-dcpext=1 m1n1#14). The default is unchanged, so J613 behaves as before.
  2. drm: apple: dcp: mirror framebuffers into the T8122 dcpext fbdma domain. Without that DART in the shared domain, the dcpext faults at the framebuffer DVAs. An optional fbdma child (dispext0_dart stream 0, with the display subsystem's DVA window) gets its own domain, and each swapped framebuffer is mapped into it read-only at its display DVA. Imported (GPU) buffers use obj->sgt. Without the node nothing changes.
  3. arm64: dts: apple: t8122-j615: external display over USB-C (dcpext). The J613 routes block, with the same Type-C wiring (usb-pd@c Left-back/ATC0, usb-pd@a Left-front/ATC1), plus fbdma. Everything stays disabled until the m1n1 marker is set (chosen.asahi,t8122-dcpext=1).

Tested (J615, 14.7, Samsung C49G95T 5120x1440@120, direct USB-C to DP)

Both ports, unplug/replug ×2, s2idle with the display attached, lid closed, pipeline takeover between ports. 0 DART faults, internal panel and GPU fine. Not tested: docks/HDMI/MST, DP audio, long sleeps.

For review

  • The switch is a kernel argument. It could instead be a J615 board default, or the gate could skip the DART on T8122 whenever an fbdma node exists. Your call; I don't know why the shared domain faults on J615 but not on J613.
  • Depends on drm/apple: enable J615 panel brightness control #354 only for panel brightness, not for the display.

Disclosure: the analysis, the patches and this text were made with an AI coding agent (Claude). The tests ran on real hardware, and I checked the results.

apple_t8122_display.dcpext_disp_dart=0 enables the T8122 dcpext without
appending its display DART to the display subsystem's iommus. On the J615
the shared domain faults the internal DCP's DART (NO PMD/PTE FOR IOVA at
0x1000a000000..) and crashes the internal DCP about 2 s into boot.
With the dcpext display DART out of the display subsystem's domain, the
external pipe scans framebuffers through a DART stream nothing maps. An
optional fbdma child of the dcpext (display DART stream 0, the display
subsystem's DVA window) gets its own domain, and each swapped framebuffer
is mapped read-only there at its display DVA. Imported (GPU) buffers use
the importer's sg table.
The J613 dcpext routes, carried to the J615 (same T8122 Type-C wiring:
usb-pd@c Left-back/ATC0, usb-pd@a Left-front/ATC1), plus an fbdma child on
dispext0_dart stream 0 for the framebuffer mirror.
@ericjuta

ericjuta commented Nov 8, 2026

Copy link
Copy Markdown
Author

Additional J615 field observation (2026-11-08): the user reports stable unplug/replug and lid-open/closed use with the combined local kernel 7.2.11-dcpexp-usb-recovery-20261108, retaining apple_t8122_display.dcpext_disp_dart=0. This build also carries the idle-port handover, brightness, thermal and USB-recovery changes, so the result cannot isolate this PR or #357 as the cause.

The installed Image matches the USB-recovery build Image by SHA-256. The current boot journal search found no DART translation-fault or co-processor-crash entries. The tested combined kernel is now the first/default Limine entry, with stock and older kernels retained. No additional cable cycles or suspend tests were requested during this audit; the lid/replug result is user-reported, not a controlled suspend/resume qualification. Please treat this as supporting combined-stack hardware evidence, not an exact-PR-branch boot test.

@scottjones scottjones left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks, this is great work, and the testing is really thorough: both ports, unplug/replug, s2idle, lid closed, takeover between ports, 0 DART faults. It looks sound for the J615, and J613 is untouched (no fbdma node there, switch default unchanged).

A few code points:

  • Must fix: dcp_fb_mirror() in drivers/gpu/drm/apple/dcp.c maps each framebuffer read-only into the fbdma domain, but nothing unmaps it when the framebuffer or GEM object is freed. Freed pages stay readable by the external DCP until that DVA is reused. A lifetime bug, not a crash. Could you unmap on fb release / cleanup_fb, or keep a per-fb record that's torn down with the fb?
  • Minor: for native buffers it builds a fresh sg_table via drm_gem_dma_get_sg_table() on every flush, even when the buffer is already mapped. At 120 Hz that adds up, so do the already-mapped check before the allocation.
  • Fine as is: only logging a failed iommu_map_sgtable().
  • Wording: a few comments still say "Experiment" (dcp-internal.h, dcp.c, t6030-display-gate.c). Please reword before merge.

On your question about the switch: we'd like a J615 board default, driven by the DT, so a J615 works with no kernel argument:

  • The J615 dcpext node gets apple,no-shared-display-dart.
  • The display gate keeps dispext0_dart out of the shared domain only if that node also has its fbdma child. Otherwise it warns and shares.
  • apple_t8122_display.dcpext_disp_dart becomes tri-state: unset means the board decides, =0/=1 overrides.

The DT property ties the two halves together: m1n1 can check the same marker, so it only turns dcpext on by default for the J615 once a kernel with this support is installed. We have that m1n1 change drafted, and the 13-inch Air's m1n1 default is up as omacom/m1n1#44.

We have this as a commit on top of your branch. It builds clean with W=1 and the property lands in the dtb, but it hasn't run on hardware, since we don't have a J615. You can fold the same change in yourself, or we can share the commit for you to cherry-pick. Which do you prefer?

Once the unmap fix and board default are in, could you retest on your J615 with no dcpext_disp_dart=0 and confirm the internal panel and both ports still work with 0 DART faults?

@maravilla2610

Copy link
Copy Markdown

Please merge this! I need an external monitor support for my MacBook Air 13-inch M3 2024

…lay-dart

Describe the fbdma child an external coprocessor scans mirrored
framebuffers through, and the marker that keeps its display DART out of
the display subsystem's IOMMU domain.
… freed

dcp_fb_mirror() mapped each framebuffer read-only into the fbdma domain
and never unmapped it, so freed pages stayed readable by the external
DCP until their DVA was reused.

Track each mirrored GEM object in an xarray and remove its mapping from
the GEM free hook, before the pages and the DVA are released, and drop
all mirrors when the fbdma device goes away. The xarray also answers
"already mapped" before any sg_table is built, so a flush of a mapped
native buffer no longer allocates. Only a failed mapping is logged.
…DART

apple,no-shared-display-dart on a dcpext node that has an fbdma child
keeps its display DART out of the display subsystem's domain, so a J615
needs no kernel argument. Without the fbdma child the gate warns and
shares. apple_t8122_display.dcpext_disp_dart becomes tri-state: unset
lets the board decide, =0 and =1 override.
@ericjuta

Copy link
Copy Markdown
Author

Pushed four review-response commits through f592f13. These add the binding and J615 board marker, fbdma-guarded DT default with tri-state dcpext_disp_dart override, framebuffer mirror lifetime cleanup, and the allocation/comment fixes.

Lifetime detail: unmapping is tied to GEM-object free rather than framebuffer release, since multiple framebuffers can share one buffer. Per-DCP mirror tracking also permits teardown to unmap outstanding mirrors before destroying the fbdma domain. The already-mapped check precedes sg_table allocation.

J615 hardware retest: booted 7.2.11-dcpexp-combined-20261111 without any dcpext_disp_dart command-line override. The live DT contains apple,no-shared-display-dart and fbdma; the gate logs that the display DART is kept out of the display subsystem and framebuffers are mirrored. Internal panel works at 2880x1864@60; Samsung G9 works at 5120x1440@120 on both left USB-C ports. Monitor replug and lid close/open recovered; no matching DART translation faults, coprocessor crashes or mirror failures were found in the checked boot logs. Links bounced for approximately five seconds around replugs before settling. This is functional coverage, not a long-duration GEM-lifetime stress test.

Important qualification: this was a combined kernel, carrying #357 idle-port handover, #354 brightness, local #410 thermal work and local USB-recovery changes in addition to this PR and the four review commits. It is not an isolated #356 test. The local m1n1 boot DT overlay was updated with the board marker; this does not test the drafted upstream m1n1 change.

Separate unresolved observation: after swapping monitor/hub ports, the hub mouse (USB2) returned but its USB3 hub and connected Kiyo Pro Ultra did not. Power-cycling the hub recovered USB3 and the camera at 5 Gb/s. Through-hub MJPEG 3840x2160@30 capture then passed for 20 seconds (599 decoded frames, discarded). USB3 reconnect reliability remains unresolved; cause and relationship to these changes are unproven.

Build verification: changed objects built W=1 and J615 DTB built; combined Image/modules/DTBs built successfully. Focused combined-tree ARM64 KUnit: 267 passed, 1 skipped. Existing boot default retained.

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.

3 participants