Repository navigation
j613: second external display with the lid closed (internal pipe on the left-front port) - #127
Conversation
…anel On J613 the internal DCP gets a Type-C display route to the left-front USB-C port in the device tree: with the lid closed and the panel off, its pipe can drive a second external display. The route stays disabled there; the display gate enables it at boot together with dcpext. When the gate does not (no boot-loader marker, a refused check, apple_t8122_display.dcpext=0, the 26.6 profile that includes the J613 device tree, or a kernel that does not know the route), the processor has a typec-routes container with no enabled route, which made route registration fail with "Type-C route container is empty" and took the panel down with it. - A typec-routes container without an enabled route means no route, for every processor but the external ones (T6030 and T8122 dcpext, whose routes are enabled together with them and must register). - On the internal panel's processor (14.7 DCPLink session), a route that fails to register is disabled in the device tree, as for a T6030 external processor, and the panel goes on without it. - On J613, a probe of the panel's processor that fails for any other reason (a deferral is not a failure) disables the gate-enabled route too, so that the left-front port does not wait for it forever. Device trees whose routes are enabled are unchanged. Assisted-by: Claude:claude-opus-5-5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Marius Gehrmann <marius@gehrmann.solutions> [Chris Kearney: rebased onto siliconforest/next 6a421d4e76ff; resolved a conflict with the H17P probe path in dcp_platform_probe(); the T8140 dcpext (H17P), an external processor added since, is excluded from the empty-container exemption too, so it still fails without an enabled route; message reworded; Claude-Session trailer dropped] Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com> [TARS: ported to integration/aurora 7.2.11 (4fdd0fc): dcp.c reorganised, so the dcp.c hunks are applied by hand; uses integration's dcp_is_v14_7_panel() instead of adding dcp_is_v14_panel()] (cherry picked from commit 93258ff)
…ront port With the lid closed and the panel off, the internal DCP's pipe can drive a second external display next to dcpext. In the J613 ADT the internal DCP is display-crossbar ufp0, port 0: crossbar state 0 (dispext0,0), here on ATC1's crossbar, with DPTX target 1 as dcpext's route to the same port. Give the internal DCP that Type-C display route to the left-front port (typec1): ATC1's DP PHY and display crossbar as typec0, crossbar state 0, DPTX target 1, and a second endpoint on the front connector's port@3, one per pipeline as on the M1 Pro/Max laptops. The route stays disabled, like dcpext and its routes; the display gate enables it in the same changeset, behind the boot loader's marker. The display driver uses it only while the lid is closed and the panel and its CRTC are off. Assisted-by: Claude:claude-opus-5-5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Marius Gehrmann <marius@gehrmann.solutions> [Chris Kearney: rebased onto siliconforest/next 6a421d4e76ff; message and device tree comment reworded; Claude-Session trailer dropped] Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com> (cherry picked from commit 4c643f7)
…cpext The J613 device tree gives the internal DCP a disabled Type-C route to the left-front port, the second pipeline for a display there with the lid closed. It leads to a port that dcpext drives too, and the display driver only uses it while dcpext runs alongside, so it is usable exactly when dcpext is. Enable the internal DCP's routes in the changeset that enables dcpext, its routes, DARTs, mailbox and crossbars, with one more log line: enabled the Type-C routes of /soc/dcp@28ec00000 with it, for a second external display with the lid closed As for the rest of that changeset, the routes must be described and still disabled, and every display crossbar the internal DCP switches must be one of dcpext's, which the changeset enables; otherwise dcpext is refused and nothing is enabled. Without dcpext (no marker, other firmware, refused checks, apple_t8122_display.dcpext=0) the route stays disabled and the internal DCP has no route. Device trees without such a route are unchanged. Assisted-by: Claude:claude-opus-5-5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Marius Gehrmann <marius@gehrmann.solutions> [Chris Kearney: rebased onto siliconforest/next 6a421d4e76ff; Claude-Session trailer dropped] Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com> (cherry picked from commit 1991adf)
…l off On J613 the internal panel's pipe can also drive a display on the left-front USB-C port, but only once its panel is off: the pipe's first DPTX core drives the panel otherwise. Once told the lid is closed, the firmware takes the pipe for a second external display at every panel power-on and swallows each panel frame; a swallowed frame never completes, the swap times out and the DCP is gone until reboot. For the internal pipe that has a Type-C route (dcp->clamshell), tell the panel's IOMFB the lid state, power the panel off while the lid is closed, and treat a closed lid as a panel that is not there: - Follow the lid switch (SW_LID, the SMC's on Apple laptops) with an input handler, registered once the panel has started. Only a switch with a parent device counts, not one made from user space (uinput). Lid changes are applied from a freezable work item: opening the lid wakes the system, and the change must wait until the DRM device has restored its state. - Tell IOMFB the state with A475 (one u32: 1 open, 2 closed): the real state from the start, then every change. IOMFB hands it to its DPTX power controller at each panel power-on, which refuses anything else. - Closing the lid powers the panel off at once (A472(0)), its CRTC on or not, after a black frame while IOMFB still has the lid open. Presents are serialized, so that black frame and a commit's swap never mix up which framebuffer is on screen. From A475(2) on no frame goes to the panel; one that raced with the lid is dropped under the session lock, not sent. - While the lid is closed the panel's connector reads disconnected, from boot on if the lid is closed then, so compositors switch its CRTC off and never light it. A CRTC switched on anyway leaves the panel off and gets no frames (one warning). - When the lid opens IOMFB is told first (A475(1)), then a panel whose CRTC is on is powered on again (A472(1), colour matrix resent), and its connector reads connected again. - Answer what the firmware sends around those power changes: D2, D591 and D574 as on an external processor, D593 (four bytes once after power-on, meaning not known) with nothing. Other machines, and J613 without the Type-C route, are unchanged. The next patch lets the free pipe drive the display on its route. Assisted-by: Claude:claude-opus-5-5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Marius Gehrmann <marius@gehrmann.solutions> [Chris Kearney: rebased onto siliconforest/next 6a421d4e76ff; resolved a conflict with the H17P power-on path in dcp_poweron(); message and comments reworded; Claude-Session trailer dropped] Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com> [TARS: ported to integration/aurora 7.2.11 (4fdd0fc): presents already run under v14->lock there (dcp_v14_present_locked()), so present_lock is gone and the lid's black frame is sent under v14->lock together with A475; a frame not sent with the lid closed is handed back as *retired; poweron/poweroff keep integration's backlight DPMS; dcp.c hunks applied by hand; the clamshell unbind releases the cable (integration disables the internal processor's Type-C work without releasing it)] (cherry picked from commit cce117a)
…osed With the lid closed the panel is off, and the internal DCP's pipe, whose first DPTX core is free then, can drive a second external display on the left-front USB-C port. When the lid opens, the pipe goes back to the panel. Do this on J613, where the internal pipe has a Type-C route to that port (dcp->clamshell). Fabric. The internal pipe counts as busy unless the lid is closed and the panel and its CRTC are off (dcp->clamshell_free). While it is free it ranks as a plain pipeline, before dcpext: with the panel's connector disconnected, aquamarine (Hyprland) gives a display coming up on the left-front port the lowest free CRTC it can use, the internal pipe's, and the route follows that pairing (routed to dcpext instead, every mode failed the atomic check, and the panel found its only CRTC taken on lid open). When the pipe becomes free it takes a display left waiting, as any pipeline that is let go of (dcp_typec_pipeline_changed()). When the lid opens, that display lets go of the pipe first (powered off, HPD dropped, route taken down), then IOMFB is told; the display goes to dcpext if dcpext is free, or waits again with the usual log line. Lid open, order. The panel's connector comes back once the compositor switched the pipe's CRTC off, or two seconds later, and at once if the CRTC was off already. A left-front display that leaves the pipe for a free dcpext reads disconnected until then (at most 3 s) and is reported 200 ms after the panel, so that the compositor gives the panel its CRTC back and the display dcpext's. Session. Once the panel has started, the DPTX endpoint is started for the route, and the DPAV controller endpoint with it, as for the T6030 external processors. The DPTX RemotePort unit 0 (core 0, crossbar dispext0,0) is DPTX port 0; the panel's own DPTX port is never bound. The link is connected as on the T8122 dcpext (dcp_ext_dptx_607()), the same firmware image. Call 25, which the firmware sends to each RemotePort when the lid state changes, is acknowledged quietly. While the route is active (v14->typec) the session handles the display description as on an external processor; the mode is set with A472(1) before A411 (with A411 alone the mode is set, the pipe not enabled), its CRTC off powers it off (A472(0)), swaps are sized to its mode (a larger framebuffer is shown from its top-left, as on an external processor) without notch rows or the panel's colour matrix, and a refused swap does not end the session. When the route is taken down, the panel's one mode is the pipe's catalog again. Other machines are unchanged, and so is J613 without the route. Link: AsahiLinux#607 Assisted-by: Claude:claude-opus-5-5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Marius Gehrmann <marius@gehrmann.solutions> [Chris Kearney: rebased onto siliconforest/next 6a421d4e76ff; resolved a conflict with DPTX_APCALL_GET_VIRTUAL_DEVICE_MODE in dptxep.h; call 25 still gets -EOPNOTSUPP on the H17P external processor, as any call it does not handle; message and comments reworded; Claude-Session trailer dropped] Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com> [TARS: ported to integration/aurora 7.2.11 (4fdd0fc): the panel's own port is skipped in dptxport_prepare(), where 7.2 binds DPTX services; call 25 as before; dcp_ext_dptx_607() keeps integration's fw_14_7_endpoints check; detect keeps dcp_connector_connected(); swaps keep lockdep and backlight present state; the lid's black frame (under v14->lock) skips a Type-C display; poweron does not restore the panel backlight while the pipe drives a Type-C display; fabric and dcp.c hunks applied by hand] (cherry picked from commit cdd32c0)
…n needed
On J613 with the lid closed, dcpext can drive either USB-C port and the
internal pipe only the left-front one. A display on the left-front port
can hold dcpext while a display on the left-back port, which only dcpext
can drive, waits, although the internal pipe is free.
Move the left-front display to the internal pipe when that is needed,
and only then: as any move, an unplug and replug (its link goes down
first, which also tells the old CRTC's late power-off to leave the next
display alone), then the other display gets the dcpext it let go of. If
the internal pipe cannot take the moved display after all, that display
waits and the other one is driven. Three cases need it:
- A display arrives on the left-back port with the lid closed while
dcpext drives the left-front one (dcp_typec_clamshell_make_room()).
- The lid closes while a left-back display waits for dcpext, which
drives the left-front one (dcp_typec_clamshell_serve_waiting()).
- Boot with the lid closed, before a compositor has paired the
connectors: a left-front display that took dcpext first moves to the
internal pipe, where aquamarine will pair it, and a waiting display
then takes dcpext (dcp_typec_clamshell_settle()). Whether a
compositor runs is looked up from any pipeline's CRTC: the dual-stream
helper finds no DRM device on J613.
Otherwise a lid close moves nothing, so the left-front display stays
lit. (Moving it at every lid close frees dcpext early too, but blanks it
for about four seconds each time, for nothing when no second display
comes.)
A display that takes a pipeline it waited for says so ("the display on
<connector> takes over this pipeline").
Tested on J613 (MacBook Air 13" M3, display firmware 14.7) with a
1080p display on the left-front port and a 3440x1440 one on the
left-back port, Hyprland, and a user service that suspends a closed
laptop only after ten seconds without an external display: lid closed
and opened five times with both displays, both lit each time with the
lid closed and the panel back 33-173 ms after opening; a left-back
display waiting with the lid open gets dcpext on lid close; a display
arriving with the lid closed; boot with the lid closed (panel never
powered); suspend with the lid closed and wake by opening it.
Assisted-by: Claude:claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Marius Gehrmann <marius@gehrmann.solutions>
[Chris Kearney: rebased onto siliconforest/next 6a421d4e76ff; resolved
conflicts with the H17P SAFE-phase mux callbacks and the H17P -ENOSPC
check in dcp_typec_route_set() (dcp-fabric.c); message and comments
reworded; Claude-Session trailer dropped]
Signed-off-by: Chris Kearney <303316+iconidentify@users.noreply.github.com>
[TARS: ported to integration/aurora 7.2.11 (4fdd0fc): integration reports a
port without a pipeline as -ENOSPC on every DCP, so the H17P-only -ENOSPC
before dcp_typec_port_busy() is gone; the new helpers sit after
integration's dcp_typec_port_busy()]
(cherry picked from commit d39a981)
5bafd20 to
2688004
Compare
|
Rebased onto current Result: a J613 with the lid closed drives two external displays.
With the lid open, aurora16's "waits for a free external display pipeline" behaviour is unchanged: the second display waits, and it lights up when the first is unplugged. Rebase notes:
Ordering matters for the DT patch (2/6). I booted the packaged aurora16 kernel once with this series' The J613 test used a localmodconfig build of the series as an extra Limine entry. The boot.bin was rebuilt once with the series' DTB and restored to the packaged one afterwards. Disclosure: the rebase, the analysis and this text were made with an AI coding agent (Claude Code). The test ran on my J613, and I checked both displays with the lid closed. |
Follow-up to #114 / #103, as suggested in #103: the lid-closed dual-display set on top of the merged J613 dcpext series.
What
On the J613 (MacBook Air 13" M3) with the lid open, one external display runs on dcpext and a second one waits (the log line and takeover from #114). macOS does more with the lid closed: the panel is off, so the internal DCP's pipe drives a second external display on the left-front USB-C port (
IOMFBIntDcpUsedForExtWhenLidClose). This series does the same:typec-routescontainer with no enabled route no longer takes the internal panel down, for every DCP except the external ones. A route that fails to register on the panel's DCP is disabled, and the panel goes on without it; so is the route of a panel DCP whose probe fails for good, so the left-front port never waits for it. Needed because patch 2 adds a route that stays disabled when the gate does not enable dcpext.dispext0,0, from the J613 ADT), DPTX target 1, and a second endpoint on the front connector.the display on USB-C Left-front takes over this pipeline.Other machines and a J613 without the new route (no marker,
apple_t8122_display.dcpext=0, older DT) are unchanged. Like #114, the series needs the boot loader to setapple,t8122-dcpext-firmware-ready(the m1n1 half in #103).Tested
Kernel: this branch on integration/aurora
54f78d60c. W=1 build of every commit with no new warnings in drm/apple and soc/apple (the review fixes: compile-checked per commit, full W=1 build of the head); checkpatch clean apart from the trailers. DTB built from this branch, m1n1 with the marker. Test setup: a 3440x1440 monitor (through its USB-C hub) on the left-back port, a 1080p portable on the left-front port, and Hyprland.Failed to resumeany more. Before the fixes (same branch): 2 runs, see the notes below.Review
I ran a Claude Code review over the series (GehrmannSolutions#2). Fixed in the patches they belong to: the lid work could run during resume, before DRM restored its state, which made
drm_mode_config_helper_resumefail with -22 for every CRTC (that was the error we had taken as harmless); the lid-close black frame could race a commit's swap over which framebuffer is on screen; a Type-C display on the internal pipe had the panel's framebuffer-size rule instead of the external one; a failing panel-DCP probe left the route enabled; any SW_LID device (also uinput) set the lid state; a refused panel power-on was only logged at info level. Left for a follow-up:phys/mux-controlssit on the static dcp node even while the route is disabled (fw_devlink then makes the panel wait forphy-apple-atc, which is loaded on J613 anyway for USB), and some duplicated route helpers in dcp-fabric.c.Known, not changed here
DCPDPServiceinstead ofDCPDP13Service, without link training, with a shortTimingElements(48 kB instead of 281 kB) and no usable mode. The driver rejected it (failed to parse modes) and the firmware dropped the display. After that dcpext could not light it again, even after a replug (the link went down in the middle of the mode set), until a reboot. The same run on the series before the rebase passed right after. That resume also ran into the -22 restore failure fixed above, so the dcpext display was not restored by DRM and was set up again from scratch; that may well have been the trigger. Not seen since the fix (2 runs), and I collect the rejected description if it happens again.(Code, tests and this text by TARS, my Claude Code agent, with me plugging displays and reviewing; Signed-off-by is mine.)
🤖 Generated with Claude Code
https://claude.ai/code/session_017YMqWu7A3VwEYtHaA1TusK