Skip to content

drm/apple: T6041 (M4 Pro/Max) displays on desktop 26.6 firmware (carries #445) - #446

Merged
RyanTheTide merged 13 commits into
integration/aurorafrom
ryan/t6041-display-445
Oct 25, 2026
Merged

RyanTheTide merged 13 commits into
integration/aurorafrom
ryan/t6041-display-445

Conversation

@RyanTheTide

Copy link
Copy Markdown
Contributor

This carries František Balázsy's T6041 display series from #445 with three small fixes on top, so it can land tonight. His eight commits keep his authorship, his sign-off and the co-developer credit (Niklas Sheth, Cody Ho, Josh Rodd, Jonathan Goyvaerts) unchanged. Thanks for all the work on this one!

What differs from #445:

  • Commit 1, the T6050 fixed DP PHY sequence tables, is now Dj's own commit from phy: apple: Add T6050 fixed DP and four-clock crossbar support #248 (440f79f), with his authorship and Signed-off-by. Only atc-t6050-dp-data.h is taken, byte for byte the same file. The rest of phy: apple: Add T6050 fixed DP and four-clock crossbar support #248 builds on the APIF series.
  • drm/apple: look up the crossbar link and the T6040 DP alt helpers at runtime. The direct calls made appledrm.ko depend on the crossbar and T6040 PHY modules on every SoC, and DRM_APPLE=y with the crossbar as a module failed to link. Both now go through symbol_get(), the way dcp_dpxbar_link() already did, and the Kconfig dependency on the PHY is gone.
  • phy: apple: t6040: never leave an error pointer in the tunables. A failed USB3 tunable parse left an ERR_PTR that a later DP power on would hand to apple_tunable_apply().
  • selftests: apple-dcp: follow the desktop 26.6 firmware path. The series broke 9 host selftest groups at compile time. The fixtures now know the new fields and helpers, and keep testing the existing paths.

Gates

On integration/aurora 653c489 with #394, #435, #436, this branch, #427 and #428 merged (test tree 4582b72703d5):

  • Universal recipe (Fedora asahi base, asahi.config, olddefconfig, LLVM=1, CONFIG_DRM_WERROR=y): Image, modules and DTBs build with no warnings, and all 130 asahi.config settings survive.
  • KUnit: asahi 84/84, drm/apple display set 212/212. Before the fixes the drm/apple set didn't link.
  • apple-dcp host selftests 62/63, the same as the base. dwc3-teardown fails on the base too.
  • dt_binding_check and CHECK_DTBS for the touched bindings and the J616s, J713, J700, M1 and M2 DTBs: clean.

Hardware

On the MacBook Pro 16" M4 Pro (J616s) with 26.6.2 firmware:

Follow-ups, not in this PR

  • The J616s device tree pieces the handoff needs (apple,dma-range on both display DARTs, cells on dcp for the piodma regions, a disp0_piodma alias), and a kernel step that enables the display nodes when the handoff marker is present.
  • "systemep: failed to parse report: -22" shows up for the powerlog reports on desktop 26.x. They don't seem to use the dictionary format parse() expects. It's harmless, since only the brightness hint is lost, but the format needs a look.
  • "unusable firmware gamma table, GAMMA_LUT ignored" once the desktop starts.
  • After a Type-C DP session ends, USB3 on that port isn't brought back until dwc3 power cycles the PHY (from the drm/apple: T6041 (M4 Pro/Max) displays on desktop 26.6 firmware: J616s internal panel, HDMI and USB-C DisplayPort #445 review).

Supersedes #445.

DjDeveloperr and others added 12 commits October 25, 2026 23:50
The register sequence tables of the T6050 fixed DisplayPort PHY, taken
alone from 440f79f ("phy: apple: Add T6050 fixed DP and four-clock
crossbar support"). The T6041 DisplayPort mode of the T6040 PHY uses the
same sequences. The driver and crossbar parts of that commit build on
the APIF series and are not included here.

Signed-off-by: Dj <hey@djdev.me>
(cherry picked from commit 440f79f)
Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
…ed DP PHY

The T6040 Type-C PHY also carries DisplayPort: the PHY of a USB-C port as
the alt mode switch of its connector (mode-switch), and the PHY of an HDMI
converter as a fixed DisplayPort link. The fixed instance has no USB
controller behind it, so no PIPE interface ("pipehandler" region), no reset
line and no connector port. Describe the DP phy type, the mode switch and
the ten-region variant, and require #reset-cells only where the PIPE
interface exists.

Signed-off-by: František Balázsy <frantisekbalazsy@gmail.com>
Add PHY_TYPE_DP to the T6040 Type-C PHY: PHY mode 3 of the paired
AppleT6040TypeCPhy driver, DisplayPort on all four lanes, for a Type-C port
whose controller selected DP alt mode with pin assignment C or E, and for
the fixed DP PHY of an HDMI converter, a node without the PIPE interface
and the DWC3 reset line, which now probes too.

Bring-up follows the paired driver's mode 3: wake the CFG block, apply the
generic ADT tunables and the DPTX tunables, set the lane crossbar to DP,
hold the common block and both TX lanes on by override (the PHY
coprocessor does not run in this mode), release the reset, then set the
lane mode and the AUX block. On a Type-C port the USB3 side is shut down
first. The per-rate link sequences (RBR to HBR3) and the drive-setting
presets are the T6050 fixed-DP tables, whose PHY the T6050 ADT marks as
T6040-compatible ("atc-phy,t6050", "atc-phy,t6040"); the T6050 clock shim
steps are replaced by the T6040 DP clock switch of the CFG window, run
after the lane program as the paired driver does. Pin assignment D (two DP
lanes next to USB3) is not supported.

The Type-C mode switch latches whether the port is in DP alt mode;
apple_t6040_phy_dp_alt() (include/linux/soc/apple/dp-tunnel.h) exposes it
for a DCP that lists several Type-C DP PHYs and routes to the one with a
sink plugged.

Signed-off-by: František Balázsy <frantisekbalazsy@gmail.com>
The IOMFB template keys its H17 (A18 Pro, M5) wire changes on
DCP_FW_VER >= 26.0. The desktop firmware of the M4 Pro/Max DCP (T6041)
also reports 26.x, but continues the 14.7 IOMFB.

Mark a desktop instance's version with DCP_FW_DESKTOP and key the H17
branches on DCP_FW_H17(). No functional change: the 12.3, 13.3, 14.7
and H17P template objects are unchanged.

Signed-off-by: František Balázsy <frantisekbalazsy@gmail.com>
The 26.6 firmware of the M4 Pro/Max DCP (T6041) continues the 14.7
IOMFB rather than the H17 protocol: most method numbers match H17P,
but the records and the start, power and mode-set flows are those of
14.7 with extensions. Add it as a template instance,
iomfb_v26_6_desktop.c, with firmware compatibility value
DCP_FIRMWARE_V_26_6_DESKTOP. Nothing selects it yet.

The instance carries this firmware's wire layout: a 0x588-byte swap
record with presents_surface, has_completion and the panel level in
nits as a binary64, a 0x1bd8-byte swap_submit with six surf2 entries,
swap_start with an opaque client cookie, set_power_state with four
transition flags, and the padded abort_swaps, read_edt_data, hotplug,
swap intent and frame_sync records. DCP_FW_LAYOUT_14_7() covers the
desktop 26.x versions, and static assertions pin the record sizes.

Power-on is A414, first_client_open, A474, is_main_display and
set_power_state(1), then set_display_device; power-off clears the
swap and sets power state 0. A mode set selects the timing through
get_mode_info (A364) before set_digital_out_mode. A pipe without a
live mode is bootstrapped through the timing's best virtual color
element, which the parser now records. GAMMA_LUT is composed with the
firmware's gamma table (A420/A421). VRR timings are not offered.

The per-DCP desktop state is allocated only for this firmware, so
other instances keep their struct apple_dcp size and the 12.3, 13.3,
14.7 and H17P template objects are unchanged.

Co-developed-by: Niklas Sheth <hi@niklassheth.com>
Signed-off-by: Niklas Sheth <hi@niklassheth.com>
Co-developed-by: Josh Rodd <josh@rodd.us>
Signed-off-by: Josh Rodd <josh@rodd.us>
Co-developed-by: Jonathan Goyvaerts <jonathan.goyvaerts@gmail.com>
Signed-off-by: Jonathan Goyvaerts <jonathan.goyvaerts@gmail.com>
Signed-off-by: František Balázsy <frantisekbalazsy@gmail.com>
The desktop 26.6 firmware of the T6041 DCP speaks a compact EPIC on
its system and auxiliary endpoints: an 8-byte transport header
(sequence, flags, interface, total length), the timestamp, an 8-byte
sub-header and the payload. Messages longer than 1028 bytes arrive in
fragments that repeat the header. Services announce themselves with
type 0x11 and are opened with type 0x12; commands, replies and calls
carry a status/length descriptor.

On DCP_FIRMWARE_V_26_6_DESKTOP endpoints, switch to it on the first
compact message: reassemble fragments, register and open announced
services, answer calls, deliver reports and match replies to pending
commands. Send commands and replies the same way, fragmented when
needed, waiting for ring space with WAIT_FOR_SPACE, and answer the
firmware's WAIT_FOR_SPACE after draining the receive ring. Call
replies are sent from a separate workqueue so that a full transmit
ring cannot stall reception. Ring pointers read from shared memory
are validated before use; a send that fails part-way fails the
commands in flight without shutting the endpoint down.

Other firmware never takes these paths.

Co-developed-by: Niklas Sheth <hi@niklassheth.com>
Signed-off-by: Niklas Sheth <hi@niklassheth.com>
Signed-off-by: František Balázsy <frantisekbalazsy@gmail.com>
Match apple,t6041-dcp and apple,t6041-dcpext and drive the internal
DCP of the M4 Pro/Max (J616s) through the desktop 26.6 IOMFB instance.

Probe accepts a T6041 processor only with apple,firmware-version and
apple,firmware-compat both 26.6.2, and the internal one only if its
register windows, iommus, mboxes and piodma match the ADT. The clock
is optional there, and without apple,max-brightness the backlight
maximum defaults to the panel's 600 nits.

The firmware does not tolerate being left running with its endpoints
unserviced, so bind sets the coprocessor running and dcp_start()
wakes RTKit once every handler is in place. It starts the system
endpoint, the IOMFB transport and the auxiliary endpoints the firmware
advertises, routes their messages, and binds the IOMFB shared memory
last. An auxiliary endpoint that fails to start is skipped.

Before the boot framebuffer is evicted, the panel goes through a
takeover trial: DCP ready, modes known, panel connected, power-on and
one mode set, boot-like mode first. If it fails, the DCP is left
running, the DRM device is kept, shutdown and resume leave the DCP
alone and suspend is refused, and the boot framebuffer keeps the
screen.

The T6041 external processors stay out of the DRM device unless
t6041_dcpext=1, and then as unusable pipes.

Co-developed-by: Cody Ho <codyho@stanford.edu>
Signed-off-by: Cody Ho <codyho@stanford.edu>
Co-developed-by: Jonathan Goyvaerts <jonathan.goyvaerts@gmail.com>
Signed-off-by: Jonathan Goyvaerts <jonathan.goyvaerts@gmail.com>
Signed-off-by: František Balázsy <frantisekbalazsy@gmail.com>
The T6041 external display processors publish their DPTX port services
as dcpdptx-port-epic:0 and :1 in compact EPIC on EPIC2C (0x2c). Bind
them by unit, open port 0 last, and follow the firmware's own client
where the compact ABI differs:

- set_hpd returns an IOReturn status in its first word;
- GET_SUPPORTS_HPD and GET_VIRTUAL_DEVICE_MODE are answered with 0;
- GET_MAX_LINK_RATE offers HBR3;
- the crossbar link is taken down before a link configuration change
  and brought up after it;
- group 1 calls 3 and 4 keep the first 32 bytes of their record and
  clear the result word.

The DPAVSERV EDID request asks for 2 KiB there, as a 32 KiB inline
request does not fit the compact ring, and dcpdp-service-epic is not
opened.

Co-developed-by: Niklas Sheth <hi@niklassheth.com>
Signed-off-by: Niklas Sheth <hi@niklassheth.com>
Signed-off-by: František Balázsy <frantisekbalazsy@gmail.com>
The two external display processors of the M4 Pro/Max (J616s) join the
panel's DRM device as further pipelines; t6041_dcpext=0 leaves them
alone. dcpext0 drives the HDMI port through its fixed route (atcphy3
and its crossbar), dcpext1 the USB-C ports through a route chosen at
connect from the PHY that entered DP alt mode.

- probe powers the fixed route's DP PHY and lists the dynamic routes
  (dp-phyN, dp-xbarN, apple,dptx-phys, apple,dptx-mux-index);
- start brings up the advertised auxiliary endpoints, the DPTX port
  services, DPAVSERV, EXPERT and EPIC25, and connects DPTX port 0;
- a connect asserts HPD after the display request and does not fail
  when no display is attached;
- an HDMI hotplug on a connected port pulses HPD, so the firmware
  exports the modes of a display plugged in after boot;
- a USB-C hotplug selects and releases the route through the
  connector's out-of-band hotplug under hpd_mutex; a failed connect
  and unbind release it;
- a CRTC kept active across a replug is powered on and set up again on
  its next flush.

A dcpext that is absent or fails to probe or bind is kept as an
unusable pipe and never holds up the panel.

apple_t6040_phy_dp_alt() gets a stub, and DRM_APPLE cannot be built in
with the T6040 USB2 PHY as a module.

Signed-off-by: František Balázsy <frantisekbalazsy@gmail.com>
The desktop 26.x DPTX path called apple_dpxbar_link_up/down() and
apple_t6040_phy_dp_alt() directly. That made appledrm.ko depend on the
crossbar and T6040 PHY modules on every SoC, and DRM_APPLE=y with the
crossbar built as a module failed to link. The PHY dependency was also
expressed as "depends on PHY_APPLE_T6040_USB2 || !PHY_APPLE_T6040_USB2",
which silently demotes DRM_APPLE=y to m wherever the PHY is a module.

Use the existing symbol_get() based dcp_dpxbar_link() for the crossbar,
look the PHY helper up the same way, and drop the Kconfig dependency.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
When a USB3 tunable fails to parse, t6040_usb3_probe() falls back to
USB2 only but leaves the ERR_PTR in tphy->tunables[]. The node is then
treated as DP only, and t6040_dp_probe() skips an absent property
without touching that slot. A later DP power on hands the error pointer
to apple_tunable_apply(), which only checks for NULL.

Clear the slot on the USB3 failure path and on the DP skip path, so an
unparsed tunable is always NULL and simply not applied.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
The T6041 display series added DCP_FIRMWARE_V_26_6_DESKTOP, the
fw_compat and desktop checks it implies, the DCP_FW_H17() lineage test,
compact EPIC in the AFK sender, receive worker and quiesce, a VRR filter
and a gamma size check in the extracted functions. The fixtures lacked
the matching mock fields, defines and helpers, and the lookup_mode()
extraction matched a new call site instead of the definition, so nine
units no longer compiled. Add them so every scenario stays off the
desktop path (fw_compat not desktop, desktop NULL, compact false, no
colour-management change), with stubs that assert if it is reached, so
the existing paths are checked exactly as before.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
Resolve the conflict with #395 in apple_drm_uninit(): withdraw the T6030
display publication first, then keep the T6041 rule that an aborted
takeover leaves its DCPs running.

Assisted-by: LLM
Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
@fbalazsy

Copy link
Copy Markdown

Thanks for carrying this. The Type-C routes of the T6041 DCP still use the private dynamic route from the original series. fbalazsy:j616s/display-t6041-fabric is #446 plus three commits: the first moves them onto the display fabric (typec-routes, as T602x/T6030/J700) and drops apple_t6040_phy_dp_alt(); the other two are the callback size check and the USB3-after-DP fix from your review notes. Tested on a J616s with a USB-C display (attach at boot, port controller rebind, device suspend); it builds with W=1 for DRM_APPLE=m and =y. I can open it as a PR once #446 is in, or you can pull it.

@fbalazsy

Copy link
Copy Markdown

Opened as #451 (stacked on this one, the top three commits).

@RyanTheTide

Copy link
Copy Markdown
Contributor Author

Follow-up from testing the merged desktop path on our J616s (issues are off on this repo, so posting here).

On the J616s (M4 Pro, DCP firmware 26.6.2), the internal panel offers 120, 60, 59.94, 50, 48 and 47.95 Hz at 3456x2160 with the T6041 desktop path (#446, with #452 for the handoff). Setting 120 or 60 Hz works, and modetest's vsync page flip test measures 120.00 and 60.00 Hz.

Setting 50, 59.94 or 48 Hz doesn't. Each of those modesets logs

apple-dcp 402e00000.dcp: received unknown callback D202 (24, 0) context=0
apple-dcp 402e00000.dcp: set_digital_out_mode timed out

and the page flips that follow complete at about 199 Hz, so they aren't paced by the panel. On a build with the display polish series on top, the same sequence ended in a WARN in dcp_push() (iomfb.c:188, called from iomfb_modeset_v26_6_desktop()) and then "DCP has crashed".

Reproduce, with the session stopped so modetest owns the display. The stdin pipe keeps modetest's flip loop running:

sleep 14 | timeout -s INT 13 modetest -M apple -s <eDP-1 connector>@<crtc>:3456x2160-50 -v

D202 isn't in the desktop 26.6 callback table. Handling it, or not offering these timings until it is, would be the place to start. Until then, GNOME or any compositor that picks one of these rates will hit the timeout.

@fbalazsy

Copy link
Copy Markdown

D202 and D454 are handled in #454. D202 (unmap_piodma) arrives as a 24-byte record with the DVA at offset 12, not the layout the older firmware sends, and D454 (release_mem_desc) wants a 32-bit result. With only D454 answered the DCP crashes; with both, 50, 59.94, 48, 47.95, 60 and 120 Hz all apply on the J616s internal panel, including 48 changes in a row.

RyanTheTide added a commit that referenced this pull request Oct 25, 2026
Include the qualified M4 Pro/Max display and GPU identity updates from #435, #427, #428 and #446 on top of the existing consolidation. Regate this final tree before publication; earlier-head gate results do not qualify it.

Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>

Assisted-by: LLM
RyanTheTide pushed a commit that referenced this pull request Oct 25, 2026
…e callbacks

Setting 50, 59.94 or 48 Hz on the J616s internal panel logged
"received unknown callback D202 (24, 0)" and timed out in
set_digital_out_mode (reported on #446). A mode change makes the desktop
26.6 firmware retire the piodma buffers it mapped for the change with
unmap_piodma (D202), then their descriptors with release_mem_desc (D454).
The desktop callback table had neither, so the first stayed unanswered and
every later modeset timed out.

D202 is not the record the older firmware sends: the buffer id and the id
again, then the DVA unaligned at offset 12, then one word. Reading it as
struct dcp_unmap_buf_resp makes the DVA check fail and leaves the piodma
translation standing. Decode it and unmap that. D454 carries a 32-bit
descriptor id and takes a 32-bit result back (the older firmware returned
a byte); run the existing release handler and return its result as 32 bits.

D454 alone, with the piodma buffers still mapped, made the firmware crash
("RTKit: co-processor has crashed"); with D202 unmapping first, both
together run clean.

Signed-off-by: František Balázsy <frantisekbalazsy@gmail.com>
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