From 48718da490db1f34b2437f745b883af6788e04c1 Mon Sep 17 00:00:00 2001 From: Techmore Date: Thu, 22 Oct 2026 07:23:40 -0400 Subject: [PATCH 1/3] Kernel wip4 candidate: free a DP tunnel route pinned to a silent dcpext, plus the 2026-10-22 evidence First wip3 dock plug of a boot routed to dcpext1 (28cc00000), whose DPTX RemotePort never answered validate (six timeouts); the release timed out too, so the port stayed pinned to it and replugging the same port failed until another port was used. Local patch 0002: a DCP whose DPTX call times out is passed over for 300 s, and a timed-out release is completed instead of kept owned. series.json revision 5 (7.2.11-elementary-aurora72-wip4), review receipt, test updates, evidence note, README and handoff entries. Build-only: the patch applies on the pinned files and the routing change ran in userspace; kernel build, KUnit and any boot are not done. Does not move a tunnel already attached to the silent pipeline within the same plug. wip3 stays installed; nothing staged or armed. Co-Authored-By: Claude Sonnet 5.5 --- mseries/OS9-HANDOFF.md | 15 + ...026-10-22-j314s-wip3-dcpext1-dptx-wedge.md | 119 ++++++++ mseries/aurora/test_qualification.py | 19 +- mseries/aurora/unified72/README.md | 13 + ...e-dp-tunnel-route-from-silent-dcpext.patch | 257 ++++++++++++++++++ .../unified72/review-wip4-2026-10-22.json | 57 ++++ mseries/aurora/unified72/series.json | 20 +- 7 files changed, 488 insertions(+), 12 deletions(-) create mode 100644 mseries/aurora/hardware-tests/2026-10-22-j314s-wip3-dcpext1-dptx-wedge.md create mode 100644 mseries/aurora/unified72/patches/0002-drm-apple-free-dp-tunnel-route-from-silent-dcpext.patch create mode 100644 mseries/aurora/unified72/review-wip4-2026-10-22.json diff --git a/mseries/OS9-HANDOFF.md b/mseries/OS9-HANDOFF.md index 6ccfe429f..e8dfd86cd 100644 --- a/mseries/OS9-HANDOFF.md +++ b/mseries/OS9-HANDOFF.md @@ -2477,6 +2477,21 @@ receipt predates this change; it does not validate speaker protection. Next: fix the repart/signature failure and fail-closed recipe, rebuild, inspect both payloads, then install and boot that exact image in an isolated VM. Physical Apple boot and speaker acceptance remain open. +## Dock monitor dark on wip3: dcpext1 DPTX stall, wip4 draft patch — 2026-10-22 (Claude Code) + +Owner plugged the WD22TB4 on the running wip3 trial kernel; no picture. Dock USB/Thunderbolt fine; `28cc00000` (dcpext1) never +answered `validate DPTX target` (six attempts), its release then timed out too, and replugging the same port failed again +(`DP tunnel setup failed: -110`). Moving the dock to another USB-C port routed it to `289c00000` and gave 1080p/60 at once. +Full timeline, what is and is not shown: `mseries/aurora/hardware-tests/2026-10-22-j314s-wip3-dcpext1-dptx-wedge.md`. + +- **Cause (partly shown).** The pinned source keeps a Thunderbolt route owned when its release is refused; a timeout is + treated as a refusal, so the port stays on the dead pipeline. Why `28cc00000` was picked first and why it went silent: not shown. +- **Draft.** wip4 candidate (`unified72/series.json` revision 5, `patches/0002-...`). Build-only: patch applies on the pinned + files, routing logic exercised in userspace; kernel build, KUnit and every boot are open. wip3 untouched, nothing staged. +- **Workaround on wip3.** Use a different USB-C port; reboot clears a pinned port. +- **Side note.** Audio blips on dock plug/unplug coincide with `mseries-power` re-applying the CPU profile on each AC flip; one logged + ALSA underrun at the first plug. Not investigated further. +- **Next.** CI native build of the PR, then owner decides on unarmed staging; design the same-plug re-request on the Thunderbolt side. ## Orchard: Machine Lab and Orchard for Incus merged into one app — 2026-10-21 (Claude Code) diff --git a/mseries/aurora/hardware-tests/2026-10-22-j314s-wip3-dcpext1-dptx-wedge.md b/mseries/aurora/hardware-tests/2026-10-22-j314s-wip3-dcpext1-dptx-wedge.md new file mode 100644 index 000000000..8a788266b --- /dev/null +++ b/mseries/aurora/hardware-tests/2026-10-22-j314s-wip3-dcpext1-dptx-wedge.md @@ -0,0 +1,119 @@ +# J314S wip3: first dock plug lands on a silent dcpext1 — 2026-10-22 + +Diagnosis by Claude Code on the owner's J314S (MacBook Pro 14-inch, M1 Pro), running the trial kernel +`7.2.11-elementary-aurora72-wip3` (up since 2026-10-21 16:42 EDT; `apple_sep.xart_guard=0`), with the Dell WD22TB4. +Owner report: the dock's monitor did not come on. The diagnosis changed nothing on the host. Times are EDT on 2026-10-22. + +## Result + +The dock's Thunderbolt link, USB hubs, Ethernet and HID all came up. The DisplayPort side did not, and could not be +recovered by replugging into the same port. It worked at once on a different port. The fix drafted for it +(`unified72/patches/0002-...`) is **build-only and unproven**: nothing here was compiled into a kernel or booted. + +## Timeline + +| Time | Event | +| --- | --- | +| 07:04:23 | `mseries-power` runs (AC arrived); `38bc` (panel) power-cycles; `289c00000` logs `dcp_dptx_disconnect(port=0)` and `connected:0`. | +| 07:04:27.95 | Dock `thunderbolt 0-1` authorised. `28cc00000` (dcpext1): `allocated Type-C DPTX PHY 0`, `display routed to Thunderbolt DP tunnel dpin0`. | +| 07:04:29–07:04:39 | Six attempts (the first plus five retries), two seconds apart (1 s timeout, 1 s delay): `AppleDCPDPTXRemotePort (chan:1) command type 0xc0 ... timed out after 1000 ms`, `service call group 0 cmd 12 failed ... -110`, `dcp_dptx_connect: failed to validate DPTX target 0:0: -110`. Then `Type-C DPTX reconnect failed after 5 retries: -110`. | +| 07:04:40–41 | `dcp_hotplug() connected:0`; teardown: `group 8 cmd 8 failed ... -110`, `dpin0: display teardown failed: -110`. | +| 07:10:40–50 | Owner unplugs and replugs the **same** port. Dock re-enumerates on Thunderbolt. | +| 07:10:51–52 | `28cc00000` again; a `group 8 cmd 8` call times out; `dpin0: DP tunnel setup failed: -110`. 07:11:03 `0:5 <-> 1:14 (DP): not active, tearing down`. | +| 07:12:21–27 | Owner moves the dock to **another** port. `289c00000` (dcpext0): `allocated Type-C DPTX PHY 1`, `DCPDP13Service` appears in 26 ms, `connected:1`, 1920x1080@60, `card2-DP-2 connected`. | + +## What the evidence supports + +1. **dcpext1's DPTX RemotePort service stopped answering.** `validate` (cmd 12) timed out on all six attempts; the later + release (cmd 8, group 8) timed out too, in the same state and on the next plug. Nothing on `289c00000` timed out. +2. **The failed port stays pinned.** In `dcp_tb_release_locked()` (drivers/gpu/drm/apple/dcp-fabric.c at the pinned + source), a failing `dcp_dptx_disconnect_oob()` returns before the route is freed; the comment there says the + route "stays owned for the next request to release". A timeout is not a refusal, and the next request + (07:10:51) hit the same timeout in that same release. That matches `DP tunnel setup failed: -110` appearing + one second after `group 8 cmd 8 failed`. The port that was moved to was a different `apple_dcp_typec_port`, so + it did not inherit the stuck ownership. +3. **The kernel does not look at the other display controller.** After the failed validates nothing tried + `289c00000` for that plug. +4. **The same failure is not new in wip3's source.** `dcp.c`, `dptxep.c`, `dcp-fabric.c` and `dcp-fabric-core.c` are + unchanged between the wip2 pin (`644133b7`) and the wip3 pin (`bd526b3c`) (the first three fetched at both + refs and compared by size and by the GitHub compare file list; `dcp.c` by the compare list only); only `apple_drv.c`, + `iomfb_plane.h`, `iomfb_template.c`, `plane.c` and `plane.h` differ in `drivers/gpu/drm/apple/` (GitHub compare). + So this is not a wip3 regression in the code that decides routing or talks to the DPTX service. (It remains + possible that something outside those files changed the firmware's behaviour; not shown.) +5. **Earlier boots never hit it.** In boots −4 to −7 (wip2 and `.20-enh1`) every dock plug logged either an answer + within ~80 ms from `28cc00000` (a cold-boot plug, after which `289c00000` took over the picture) or went straight + to `289c00000`. No `failed to validate DPTX target` line exists in the kernel journal of boots −1 to −7. This boot + (wip3) had no earlier dock plug; the three wip3 boots before it ran for under an hour each and none logged a dock. + +## Related + +`dcpext1` is `28cc00000` (the tunnel connector, no Type-C mux in the device tree); `dcpext0` is `289c00000` +(the built-in HDMI). An earlier, different failure of the same DCP is recorded in +[`fairydust/usb4/evidence/2026-10-16-dbgboot-dcp-silent-never-configures-link.md`](../../fairydust/usb4/evidence/2026-10-16-dbgboot-dcp-silent-never-configures-link.md) +(older kernel: firmware silent after ACTIVATE, never answering `set_hpd`). Not shown to be the same cause. + +## What is not shown + +- **Why `28cc00000` was chosen at 07:04:27.** The candidate scorer prefers the lowest CRTC index among available + pipelines (`dcp_fabric_tunnel_candidate()`), which would be `289c00000`. Both `dcp_fabric_available()` (owned, + fixed output busy, HDMI presence not settled) and the planner (`keep_order`, another port wanting that pipeline) can + exclude it. `289c00000` logged a disconnect at 07:04:23, four seconds earlier. No verbose logging was on, so the + reason is not in the journal. +- **Why the service went silent.** Candidates: long idle since boot (14 h) of an external DCP; the 07:04:23 power + event; a firmware state left by the previous trial boots. None is tested. A reboot is expected to clear it, since + a fresh `28cc00000` answered on every earlier boot — not verified on this boot. +- **Whether this repeats.** One occurrence. + +## Workaround for the running kernel + +Move the dock to another USB-C port. If the port stays dark on the same port after replug, that port is pinned until +reboot (finding 2). Reboot also clears it. + +## The draft patch + +`unified72/patches/0002-drm-apple-free-dp-tunnel-route-from-silent-dcpext.patch`, series revision 5, candidate +`7.2.11-elementary-aurora72-wip4`: + +- A DPTX call that times out marks that DCP's pipeline unresponsive for 300 s (cleared on the next connect that + gets an answer). New `dptx_holdoff` fields on `struct apple_dcp`; `unresponsive` on `struct dcp_fabric_pipeline`. +- `dcp_fabric_available()` is false for an unresponsive pipeline and `dcp_fabric_plan()` does not plan onto one, + unless a tunnel already holds it. With no other pipeline free the request returns `-EBUSY`; the Thunderbolt side + already treats that as "no display pipeline free; waiting for one" and retries every 2 s + (`apple_dpin_m1`, `capacity_retry`), so a retry lands on `289c00000` when it is free. +- `dcp_tb_release_locked()` completes a release whose firmware call timed out: it drops the DCP's connected view + (`dcp_dptx_abandon()`) and goes on to free the crossbar route, instead of keeping the route owned. + +### What it fixes and what it does not + +- Fixes: a port pinned to a dead pipeline until reboot (finding 2), and the next plug choosing the dead pipeline + again while it is held off. +- Does **not** move the dock to `289c00000` within the plug that already attached to the silent pipeline (the + 07:04 case): the tunnel is attached before the validate runs, and the Thunderbolt driver only re-requests + on a new tunnel event. The expected result there is a dark monitor, then a working one after one replug, + instead of a port dark until reboot. Closing that gap needs the DCP to ask the Thunderbolt side for a new request + (not designed here). +- Does not explain or prevent the silence itself. + +### Checked here + +- The patch applies (`git apply --check`) on the exact pinned files with 0001 applied. The six file blobs were read + from GitHub at `bd526b3c`; no archive was downloaded and no full tree was built. +- The pure routing change (`dcp_fabric_available()`, `dcp_fabric_tunnel_candidate()`, `dcp_fabric_plan()`) was + compiled from the real `dcp-fabric-core.c` against stub headers and run in userspace. The scenario passes, and + four checks fail when the availability change is removed. The same scenario is added as the KUnit case + `fabric_unresponsive_pipeline_test`; KUnit itself was not run. +- `dcp.c` and `dcp-fabric.c` edits were not compiled. The `aurora-enhancements` workflow builds `unified72` on this PR. + +### Acceptance before anyone arms it + +Native build and KUnit pass in CI; stage unarmed beside wip3 (owner decision); then on the J314S: dock plug on each port +from a fresh boot, a plug after hours idle, and a deliberate replug on the port that failed. Look for +`DPTX service not answering`, `no display pipeline free; waiting for one` and `a display pipeline came free`. Keep +`.20` as the default and the wip3 rollback in `unified72/BOOT-TRIAL.md`. + +## Side observation: audio blips on plug and unplug + +Each dock plug or unplug flips AC/battery; `mseries-power` then reapplies the CPU profile (07:12:20–23: power-saver +2208 MHz caps, then balanced 3036 MHz, three runs in 3 s) and the panel DCP power-cycles. The only logged glitch is one +`snd_pcm_avail after recover: Broken pipe` on `hw:AppleJ314,1p` at 07:04:23. The dock exposes no audio device to +PipeWire. Cause not established. diff --git a/mseries/aurora/test_qualification.py b/mseries/aurora/test_qualification.py index 8eb3878e0..6c22ece9b 100644 --- a/mseries/aurora/test_qualification.py +++ b/mseries/aurora/test_qualification.py @@ -224,20 +224,25 @@ def test_unified72_snapshot_has_separate_identity_and_no_moving_branch_url(self) self.assertRegex(data["source"]["sha256"], r"^[a-f0-9]{64}$") self.assertTrue(data["kernel_release"]["unified72"].startswith("7.2.11-")) lane = candidate.HERE / "unified72" - # wip3 carries exactly the reviewed #157 display patch on the #158 source. + # wip4 carries the reviewed #157 display patch, then one local DPTX-stall patch. self.assertEqual([patch["file"] for patch in data["patches"]], - ["0001-drm-apple-srgb-external-display-colour.patch"]) + ["0001-drm-apple-srgb-external-display-colour.patch", + "0002-drm-apple-free-dp-tunnel-route-from-silent-dcpext.patch"]) + for entry in data["patches"]: + self.assertEqual(entry["profile"], "candidate") + self.assertEqual(candidate.sha256(lane / "patches" / entry["file"]), entry["sha256"]) patch = data["patches"][0] - self.assertEqual(patch["profile"], "candidate") - self.assertEqual(candidate.sha256(lane / "patches" / patch["file"]), patch["sha256"]) + self.assertIsNone(data["patches"][1]["upstream"]) self.assertEqual(data["superseded_patches"][0]["head"], "4c56f9cd3033e5f65ef9c61bc7b396d50bce23f7") retired = data["superseded_patches"][0] self.assertEqual(candidate.sha256(lane / "patches" / retired["file"]), retired["sha256"]) self.assertEqual(data["kernel_release"]["unified72"], - "7.2.11-elementary-aurora72-wip3") - self.assertEqual(data["debian_package"], "linux-image-elementary-aurora72-wip3") + "7.2.11-elementary-aurora72-wip4") + self.assertEqual(data["debian_package"], "linux-image-elementary-aurora72-wip4") + # The installed wip3 and wip2 packages must stay distinct from this one. + self.assertNotEqual(data["debian_package"], "linux-image-elementary-aurora72-wip3") self.assertNotEqual(data["debian_package"], "linux-image-elementary-aurora72-wip2") receipt = json.loads((lane / data["review_receipt"]).read_text()) self.assertEqual(receipt["source"], data["source"]["commit"]) @@ -245,6 +250,8 @@ def test_unified72_snapshot_has_separate_identity_and_no_moving_branch_url(self) self.assertEqual(receipt["archive_verification"]["archive_root"], data["source"]["archive_root"]) self.assertEqual(receipt["patches"][0]["head"], patch["head"]) + self.assertEqual([entry["file"] for entry in receipt["patches"]], + [entry["file"] for entry in data["patches"]]) self.assertIn("not_rerun", receipt) self.assertEqual(candidate.settings(data, "unified72")["DRM_AMDGPU"], "n") diff --git a/mseries/aurora/unified72/README.md b/mseries/aurora/unified72/README.md index 090283112..4e545daf4 100644 --- a/mseries/aurora/unified72/README.md +++ b/mseries/aurora/unified72/README.md @@ -1,5 +1,18 @@ # Unified Aurora 7.2.10 build candidate +## wip4 candidate — October 22, 2026 (build-only, not booted) + +**Revision 5 pins `7.2.11-elementary-aurora72-wip4`:** the same #158 source and #157 patch as wip3, plus one local +patch, `patches/0002-drm-apple-free-dp-tunnel-route-from-silent-dcpext.patch`. It answers the first wip3 dock plug of +a boot (2026-10-22), where `dcpext1` (`28cc00000`) never answered the DisplayPort validate call and its port stayed +pinned to it until reboot. A DCP whose DPTX call times out is passed over for 300 s, and a timed-out release is +completed instead of kept owned. Evidence, the reasoning, and what it does **not** fix (a tunnel already attached to +the silent pipeline is not moved within that plug): +[2026-10-22-j314s-wip3-dcpext1-dptx-wedge.md](../hardware-tests/2026-10-22-j314s-wip3-dcpext1-dptx-wedge.md). +Receipt: [review-wip4-2026-10-22.json](review-wip4-2026-10-22.json). Checked: patch applies on the exact pinned +files; the routing change ran in userspace against the real `dcp-fabric-core.c`. **Not done:** kernel build, KUnit, +any boot. wip3 stays installed and is the running trial kernel; nothing is staged or armed for wip4. + ## wip3 candidate — October 21, 2026 (build-only, not booted) **Revision 4 pins `7.2.11-elementary-aurora72-wip3`:** unmerged upstream diff --git a/mseries/aurora/unified72/patches/0002-drm-apple-free-dp-tunnel-route-from-silent-dcpext.patch b/mseries/aurora/unified72/patches/0002-drm-apple-free-dp-tunnel-route-from-silent-dcpext.patch new file mode 100644 index 000000000..f874d4963 --- /dev/null +++ b/mseries/aurora/unified72/patches/0002-drm-apple-free-dp-tunnel-route-from-silent-dcpext.patch @@ -0,0 +1,257 @@ +drm/apple: stop a silent external DCP from pinning a Thunderbolt DP route + +Local patch (not upstream), written 2026-10-22 against the #158 source plus +0001. Build-only; never booted. Evidence and reasoning: +mseries/aurora/hardware-tests/2026-10-22-j314s-wip3-dcpext1-dptx-wedge.md + +On the J314S, the first dock plug of a boot can be routed to dcpext1 +(28cc00000). Its AppleDCPDPTXRemotePort service then never answers: the link +validation times out, and so does every later call, including the release. +dcp_tb_release_locked() treats a refused release as "keep the route owned", +which is right for a refusal but wrong for a timeout: the port stays pinned to +the dead pipeline, and each new request on it first tries to release it again +and fails with -110 until reboot. Other ports are unaffected. + + - A DPTX call that times out marks that DCP's pipeline unresponsive for 300 s + (cleared when a connect gets an answer). + - An unresponsive pipeline is not offered to new tunnels or direct routes and + is not planned onto, unless a tunnel already holds it. With no other + pipeline free the request returns -EBUSY, so the Thunderbolt side waits and + asks again (apple_dpin capacity_retry) and lands on dcpext0 meanwhile. + - A release that times out is completed without the firmware: our view of the + display is dropped and the crossbar route freed, instead of kept owned. + +Not covered: moving a tunnel that already attached to the silent pipeline, +within the same plug, to another one. That needs the Thunderbolt side to +re-request the tunnel; see the evidence note. + +diff --git a/drivers/gpu/drm/apple/dcp-fabric-core.c b/drivers/gpu/drm/apple/dcp-fabric-core.c +--- a/drivers/gpu/drm/apple/dcp-fabric-core.c ++++ b/drivers/gpu/drm/apple/dcp-fabric-core.c +@@ -118,7 +118,7 @@ EXPORT_SYMBOL_GPL(dcp_fabric_presence_expire); + bool dcp_fabric_available(const struct dcp_fabric_pipeline *pipeline, + const struct dcp_fabric_policy *policy) + { +- return !pipeline->owned && !pipeline->fixed_busy && ++ return !pipeline->owned && !pipeline->fixed_busy && !pipeline->unresponsive && + (policy->dual_stream || pipeline->presence == DCP_FABRIC_ABSENT); + } + EXPORT_SYMBOL_GPL(dcp_fabric_available); +@@ -262,7 +262,9 @@ void dcp_fabric_plan(struct dcp_fabric_pipeline *pipelines, + } + /* The adapter supplies pipelines in CRTC index order. */ + for (pipeline = pipelines; pipeline; pipeline = pipeline->next) +- if (pipeline->tunnel_held || !pipeline->fixed_busy) ++ /* A held tunnel keeps its pairing; nothing new is planned onto a dead one. */ ++ if (pipeline->tunnel_held || ++ (!pipeline->fixed_busy && !pipeline->unresponsive)) + dcp_fabric_plan_pipeline(pipeline, ports, + arriving, dpin); + again = false; +diff --git a/drivers/gpu/drm/apple/dcp-fabric-core.h b/drivers/gpu/drm/apple/dcp-fabric-core.h +--- a/drivers/gpu/drm/apple/dcp-fabric-core.h ++++ b/drivers/gpu/drm/apple/dcp-fabric-core.h +@@ -29,6 +29,8 @@ struct dcp_fabric_pipeline { + enum dcp_fabric_presence_state presence; + bool terminal; + bool services_ready; ++ /* firmware DPTX service timed out lately: do not route a display here */ ++ bool unresponsive; + bool external; + bool t6030_dpin; + }; +diff --git a/drivers/gpu/drm/apple/dcp-fabric-test.c b/drivers/gpu/drm/apple/dcp-fabric-test.c +--- a/drivers/gpu/drm/apple/dcp-fabric-test.c ++++ b/drivers/gpu/drm/apple/dcp-fabric-test.c +@@ -483,6 +483,50 @@ static void fabric_scenario_test(struct kunit *test) + } + } + ++static void fabric_unresponsive_pipeline_test(struct kunit *test) ++{ ++ struct fabric_fixture f; ++ struct dcp_fabric_route *route; ++ int error; ++ ++ fabric_init(&f, false); ++ /* Without a fault the tunnel takes the lower-scoring pipeline 1. */ ++ route = dcp_fabric_tunnel_candidate(&f.port[0], &f.policy, NULL, false, ++ 0, true, &error); ++ KUNIT_ASSERT_PTR_EQ(test, route, &f.route[0][1]); ++ ++ /* A pipeline whose firmware service timed out is passed over. */ ++ f.pipeline[1].unresponsive = true; ++ KUNIT_EXPECT_FALSE(test, dcp_fabric_available(&f.pipeline[1], &f.policy)); ++ route = dcp_fabric_tunnel_candidate(&f.port[0], &f.policy, NULL, false, ++ 0, true, &error); ++ KUNIT_EXPECT_PTR_EQ(test, route, &f.route[0][0]); ++ KUNIT_EXPECT_EQ(test, error, 0); ++ ++ /* With nothing else free the request waits for a pipeline. */ ++ f.pipeline[0].unresponsive = true; ++ route = dcp_fabric_tunnel_candidate(&f.port[0], &f.policy, NULL, false, ++ 0, true, &error); ++ KUNIT_EXPECT_PTR_EQ(test, route, NULL); ++ KUNIT_EXPECT_EQ(test, error, -EBUSY); ++ ++ /* Planning leaves the dead pipeline out, so the next stream goes dark. */ ++ f.pipeline[0].unresponsive = false; ++ f.port[0].wanted = true; ++ f.port[0].hpd = true; ++ f.port[1].wanted = true; ++ f.port[1].hpd = true; ++ dcp_fabric_plan(f.pipeline, f.port, NULL, 0); ++ KUNIT_EXPECT_PTR_EQ(test, f.plan[0].target[0], &f.route[0][0]); ++ KUNIT_EXPECT_PTR_EQ(test, f.plan[1].target[0], NULL); ++ ++ /* Once the hold-off lapses the pipeline is used again. */ ++ f.pipeline[1].unresponsive = false; ++ dcp_fabric_plan(f.pipeline, f.port, NULL, 0); ++ KUNIT_EXPECT_PTR_EQ(test, f.plan[0].target[0], &f.route[0][0]); ++ KUNIT_EXPECT_PTR_EQ(test, f.plan[1].target[0], &f.route[1][1]); ++} ++ + static void fabric_dark_tunnel_test(struct kunit *test) + { + struct fabric_fixture f; +@@ -2290,6 +2334,7 @@ static struct kunit_case fabric_tests[] = { + KUNIT_CASE(fabric_capacity_notification_test), + KUNIT_CASE_PARAM(fabric_scenario_test, fabric_scenario_gen_params), + KUNIT_CASE(fabric_dark_tunnel_test), ++ KUNIT_CASE(fabric_unresponsive_pipeline_test), + KUNIT_CASE(fabric_effect_failure_core_test), + KUNIT_CASE(fabric_deactivate_failure_test), + KUNIT_CASE(fabric_unbound_and_mask_test), +diff --git a/drivers/gpu/drm/apple/dcp-fabric.c b/drivers/gpu/drm/apple/dcp-fabric.c +--- a/drivers/gpu/drm/apple/dcp-fabric.c ++++ b/drivers/gpu/drm/apple/dcp-fabric.c +@@ -890,6 +890,7 @@ static void dcp_fabric_snapshot_port(struct apple_dcp_typec_port *port, + pipeline->terminal = tunnel && dcpext_scanout_terminal(dcp); + pipeline->services_ready = !tunnel || + dcp_tb_services_ready(dcp); ++ pipeline->unresponsive = dcp_dptx_held_off(dcp); + pipeline->external = dcp->external; + route->core.pipeline = pipeline; + route->core.tunnel = route->tunnel; +@@ -2284,6 +2285,18 @@ static int dcp_tb_release_locked(struct apple_dcp_typec_port *port, + /* A refused display release keeps the route and its binding owned. */ + if (port->hpd || dcp->typec_cable_connected) { + ret = dcp_dptx_disconnect_oob(to_platform_device(dcp->dev), 0); ++ if (ret == -ETIMEDOUT) { ++ /* ++ * A service that does not answer cannot refuse: waiting for it ++ * would keep this port on a dead pipeline, and every later ++ * request would time out releasing it again. Free our side. ++ */ ++ dev_warn(dcp->dev, ++ "DPTX service not answering; releasing its DP tunnel route without it\n"); ++ dcp_dptx_note_timeout(dcp, ret); ++ dcp_dptx_abandon(dcp, 0); ++ ret = 0; ++ } + if (ret) + return ret; + } +diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h +--- a/drivers/gpu/drm/apple/dcp-internal.h ++++ b/drivers/gpu/drm/apple/dcp-internal.h +@@ -72,6 +72,9 @@ void dcp_retry_placeholder_edid(struct apple_dcp *dcp, + const struct drm_edid *drm_edid); + bool dcp_typec_relink(struct apple_dcp *dcp); + bool dcp_typec_sink_relink(struct apple_dcp *dcp); ++bool dcp_dptx_held_off(struct apple_dcp *dcp); ++void dcp_dptx_note_timeout(struct apple_dcp *dcp, int err); ++void dcp_dptx_abandon(struct apple_dcp *dcp, u32 port); + + struct dcpav_service_epic; + +@@ -515,6 +518,9 @@ struct apple_dcp { + bool typec_cable_connected; + /* DPTX feeds a Thunderbolt DP IN adapter, not the Type-C PHY lanes */ + bool dptx_tunnel; ++ /* DPTX RemotePort timed out: jiffies until which no display is routed here */ ++ bool dptx_holdoff; ++ unsigned long dptx_holdoff_until; + /* DFP port in the DPTX target: 0 = dpphy, 1 = dpin0, 2 = dpin1 */ + u8 dptx_dfp_port; + /* wakes/sleeps the Thunderbolt DP IN adapter from DCP Activate/Deactivate */ +diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c +--- a/drivers/gpu/drm/apple/dcp.c ++++ b/drivers/gpu/drm/apple/dcp.c +@@ -980,6 +980,8 @@ int dcp_get_connector_type(struct platform_device *pdev) + #define DPTX_TUNNEL_CONNECT_TIMEOUT msecs_to_jiffies(8000) + #define DPTX_RECONNECT_DELAY msecs_to_jiffies(1000) + #define DPTX_RECONNECT_RETRIES 5 ++/* How long a pipeline whose DPTX service timed out is passed over. */ ++#define DCP_DPTX_HOLDOFF msecs_to_jiffies(300 * MSEC_PER_SEC) + + /* + * The connection a DPTX connect or queued reconnect belongs to: the Type-C +@@ -1135,6 +1137,7 @@ static int dcp_dptx_connect_raw(struct apple_dcp *dcp, u32 port, bool *retryable + dev_err(dcp->dev, + "dcp_dptx_connect: failed to validate DPTX target %u:%u: %d\n", + dcp->dptx_die, dcp->dptx_phy, ret); ++ dcp_dptx_note_timeout(dcp, ret); + goto out_unlock; + } + } +@@ -1151,8 +1154,11 @@ static int dcp_dptx_connect_raw(struct apple_dcp *dcp, u32 port, bool *retryable + dev_err(dcp->dev, + "dcp_dptx_connect: failed to connect DPTX target %u:%u: %d\n", + dcp->dptx_die, dcp->dptx_phy, ret); ++ dcp_dptx_note_timeout(dcp, ret); + goto out_unlock; + } ++ /* The service answered: whatever held this pipeline off is over. */ ++ WRITE_ONCE(dcp->dptx_holdoff, false); + + if (!dcp_connect_session_valid_locked(dcp, session, cable, recovery)) { + ret = -ESTALE; +@@ -2188,6 +2194,47 @@ static int dcp_dptx_release_locked(struct apple_dcp *dcp, u32 port) + return 0; + } + ++/* ++ * A DPTX RemotePort call that times out means the firmware service itself ++ * is not answering, not that it refused: every later call waits out its ++ * timeout too. Remember it for a while, so the display fabric routes new ++ * displays to another pipeline instead of back to this one. ++ */ ++void dcp_dptx_note_timeout(struct apple_dcp *dcp, int err) ++{ ++ if (err != -ETIMEDOUT) ++ return; ++ /* Each failed retry extends the hold-off; say so once. */ ++ if (!dcp_dptx_held_off(dcp)) ++ dev_warn(dcp->dev, ++ "DPTX service not answering; routing displays elsewhere for %u s\n", ++ jiffies_to_msecs(DCP_DPTX_HOLDOFF) / MSEC_PER_SEC); ++ WRITE_ONCE(dcp->dptx_holdoff_until, jiffies + DCP_DPTX_HOLDOFF); ++ WRITE_ONCE(dcp->dptx_holdoff, true); ++} ++ ++bool dcp_dptx_held_off(struct apple_dcp *dcp) ++{ ++ return READ_ONCE(dcp->dptx_holdoff) && ++ time_before(jiffies, READ_ONCE(dcp->dptx_holdoff_until)); ++} ++ ++/* ++ * Drop our own view of a display the firmware can no longer be asked to ++ * release, so that the route it held can be freed. If the firmware comes ++ * back, the next connection starts from scratch. ++ */ ++void dcp_dptx_abandon(struct apple_dcp *dcp, u32 port) ++{ ++ guard(mutex)(&dcp->hpd_mutex); ++ if (dcp->external) { ++ smp_store_release(&dcp->external_link_ready, false); ++ dcpext_scanout_invalidate(dcp); ++ } ++ dcp->dptxport[port].connected = false; ++ WRITE_ONCE(dcp->dptxport[port].tile_hint, false); ++} ++ + int dcp_dptx_disconnect(struct apple_dcp *dcp, u32 port) + { + int ret; diff --git a/mseries/aurora/unified72/review-wip4-2026-10-22.json b/mseries/aurora/unified72/review-wip4-2026-10-22.json new file mode 100644 index 000000000..310cbab48 --- /dev/null +++ b/mseries/aurora/unified72/review-wip4-2026-10-22.json @@ -0,0 +1,57 @@ +{ + "schema": 1, + "reviewed_on": "2026-10-22", + "source": "bd526b3c11ccf8e91d6240261beb3219d8f17f03", + "previous_source": "bd526b3c11ccf8e91d6240261beb3219d8f17f03", + "upstream_pr": "https://github.com/omacom/linux-aurora/pull/158", + "upstream_status": "unchanged from wip3: #158 and #157 still open at review time; this lane adds one local patch only", + "patches": [ + { + "file": "0001-drm-apple-srgb-external-display-colour.patch", + "upstream": "https://github.com/omacom/linux-aurora/pull/157", + "head": "9d3aeb24cb864d5f99a7172e9004884bf3c1a9a0", + "base_of_pr": "44d5afc79da79bb3b17a3ee7494cb8d07a1f1aaa", + "files": [ + "drivers/gpu/drm/apple/dcp-internal.h", + "drivers/gpu/drm/apple/dcp.c", + "drivers/gpu/drm/apple/iomfb_template.c", + "drivers/gpu/drm/apple/parser.c", + "drivers/gpu/drm/apple/plane.c" + ], + "applicability": "patch(1) dry run against the five files at #158 head: clean, offsets +5 (iomfb_template.c) and +89 (plane.c); also clean against integration/aurora faed71028. Full-tree git apply --check runs in candidate.py prepare and in CI." + }, + { + "file": "0002-drm-apple-free-dp-tunnel-route-from-silent-dcpext.patch", + "upstream": null, + "files": [ + "drivers/gpu/drm/apple/dcp-fabric-core.c", + "drivers/gpu/drm/apple/dcp-fabric-core.h", + "drivers/gpu/drm/apple/dcp-fabric-test.c", + "drivers/gpu/drm/apple/dcp-fabric.c", + "drivers/gpu/drm/apple/dcp-internal.h", + "drivers/gpu/drm/apple/dcp.c" + ], + "applicability": "git apply --check passes on a tree holding the pinned #158 versions of the six files with 0001 applied (the six blobs were read from GitHub at bd526b3c, not from a full archive). Full-tree apply runs in candidate.py prepare and CI.", + "evidence": "mseries/aurora/hardware-tests/2026-10-22-j314s-wip3-dcpext1-dptx-wedge.md" + } + ], + "not_rerun": [ + "kernel build (no compile of dcp.c or dcp-fabric.c was possible here; CI decides)", + "KUnit run of fabric_unresponsive_pipeline_test (the same scenario ran as a userspace harness against the real dcp-fabric-core.c with stub headers: passes, and fails when the availability change is removed)", + "64-fix semantic parity", + "host tests listed in series.json against the source", + "any hardware test" + ], + "known_interactions": [ + "#158 changes apple_plane_init() to take both supports_xrgb2101010 and an interchange argument; #157 edits plane.c nearby. The dry run is clean but the build is the real check.", + "#158 does not advertise Interchange framebuffer formats on H17P firmware; not relevant to the J314S.", + "0002 touches dcp.c and dcp-internal.h, which 0001 also edits; the hunks do not overlap but apply order is 0001 then 0002.", + "0002 changes dcp_tb_release_locked(): a timed-out release is now treated as complete. This is the riskiest line; it relies on the firmware being unreachable, not merely slow (the failure seen was six consecutive 1 s timeouts, then every later call timing out)." + ], + "archive_verification": { + "sha256": "881d5a061efa212ed28489311cc97ab6d9bf52891a6c7a00b18f0bbaff131957", + "bytes": 270217582, + "method": "Entire codeload gzip downloaded and hashed locally (trust on first use: the pin is this digest); tar root read from the archive listing; Makefile reads VERSION 7, PATCHLEVEL 2, SUBLEVEL 11.", + "archive_root": "linux-aurora-bd526b3c11ccf8e91d6240261beb3219d8f17f03" + } +} diff --git a/mseries/aurora/unified72/series.json b/mseries/aurora/unified72/series.json index c1f03d4d6..f592310b1 100644 --- a/mseries/aurora/unified72/series.json +++ b/mseries/aurora/unified72/series.json @@ -1,7 +1,7 @@ { "schema": 1, "base_source": "c46d7dcdb8712ea9aeb0ce47b439ffae76371e55", - "revision": 4, + "revision": 5, "source": { "repository": "aurora-silicon/linux", "commit": "bd526b3c11ccf8e91d6240261beb3219d8f17f03", @@ -14,10 +14,10 @@ "sha256": "881d5a061efa212ed28489311cc97ab6d9bf52891a6c7a00b18f0bbaff131957" }, "kernel_release": { - "unified72": "7.2.11-elementary-aurora72-wip3" + "unified72": "7.2.11-elementary-aurora72-wip4" }, - "debian_package": "linux-image-elementary-aurora72-wip3", - "debian_version": "7.2.11+elementary.aurora72.wip3-1", + "debian_package": "linux-image-elementary-aurora72-wip4", + "debian_version": "7.2.11+elementary.aurora72.wip4-1", "patches": [ { "file": "0001-drm-apple-srgb-external-display-colour.patch", @@ -26,6 +26,14 @@ "head": "9d3aeb24cb864d5f99a7172e9004884bf3c1a9a0", "profile": "candidate", "note": "drm/apple: sRGB surfaces and an sRGB link colorimetry for external displays (unmerged upstream PR #157, 5 files, +73/-6). Applies to the #158 head with small offsets; must be re-checked if #158 moves." + }, + { + "file": "0002-drm-apple-free-dp-tunnel-route-from-silent-dcpext.patch", + "sha256": "fdd6f9cd13ca90b4500130d289182eb69728e25afe5a5ed3f0c92571aa54fd1a", + "upstream": null, + "head": null, + "profile": "candidate", + "note": "drm/apple: local, not upstream. A DCP whose DPTX RemotePort times out is passed over for 300 s, and a timed-out release no longer pins the port's DP tunnel route to it. Applies after 0001 to the #158 source (checked against the exact files; full-tree apply is checked by candidate.py prepare and CI). Never built or booted." } ], "config": { @@ -39,7 +47,7 @@ "tools/asahi/pmp-bootargs-tests.rs", "tools/asahi/pmp-power-tests.rs" ], - "qualification": "Unmerged upstream PR #158 (Asahi 7.2.11 rebase, head bd526b3c) plus unmerged PR #157 as one local patch, pinned as a new build-only candidate. Replaces the wip2 pin (merged #139 source, 7.2.10), which stays on disk and booted as the owner's trial. Source and patch applicability are checked here; native build success, the 64-fix semantic parity (not re-run for 7.2.11), and J314S boot/dock/power/recovery acceptance are NOT established. Preserve shared-DTB fallback compatibility and review upstream M1-family Touch ID regression before hardware use.", + "qualification": "Same unmerged #158 source (head bd526b3c) and #157 patch as wip3, plus one local patch for the dcpext1 DPTX stall seen on the first wip3 dock plug (2026-10-22). Build-only candidate: patch application is checked on the exact source files and the pure routing logic was exercised in userspace; kernel build, KUnit, J314S boot, dock and recovery are NOT established. Replaces the wip3 pin in this file; wip3 stays installed and is the running trial kernel.", "superseded_patches": [ { "file": "0001-stock-bindgen-layout-policy.patch", @@ -50,5 +58,5 @@ "note": "Historical local port for wip1; corresponding stock-bindgen default and BINDGEN_LAYOUT_TESTS opt-in are now carried in merged upstream source. Retained for provenance; not applied to wip2." } ], - "review_receipt": "review-wip3-2026-10-21.json" + "review_receipt": "review-wip4-2026-10-22.json" } From f31aca7bec8e6a12415c8060b455d5800761c7c7 Mon Sep 17 00:00:00 2001 From: sdolbec Date: Tue, 27 Oct 2026 07:41:04 -0400 Subject: [PATCH 2/3] Aurora wip4 revision 6: carry upstream #186 (route DP-alt ports only after HPD) with local 0002 --- mseries/OS9-HANDOFF.md | 4 + mseries/aurora/test_qualification.py | 14 ++- mseries/aurora/unified72/README.md | 21 +++++ ...t-with-hpd-take-over-a-held-pipeline.patch | 88 ++++++++++++++++++ ...-port-only-once-its-sink-asserts-hpd.patch | 59 ++++++++++++ ...ospc-as-a-taken-display-on-every-soc.patch | 57 ++++++++++++ .../unified72/review-wip4-2026-10-27.json | 90 +++++++++++++++++++ mseries/aurora/unified72/series.json | 30 ++++++- 8 files changed, 358 insertions(+), 5 deletions(-) create mode 100644 mseries/aurora/unified72/patches/0003-drm-apple-dcp-let-a-typec-port-with-hpd-take-over-a-held-pipeline.patch create mode 100644 mseries/aurora/unified72/patches/0004-drm-apple-dcp-route-a-dp-alt-port-only-once-its-sink-asserts-hpd.patch create mode 100644 mseries/aurora/unified72/patches/0005-usb-typec-tipd-treat-enospc-as-a-taken-display-on-every-soc.patch create mode 100644 mseries/aurora/unified72/review-wip4-2026-10-27.json diff --git a/mseries/OS9-HANDOFF.md b/mseries/OS9-HANDOFF.md index e8dfd86cd..f7ef4a88a 100644 --- a/mseries/OS9-HANDOFF.md +++ b/mseries/OS9-HANDOFF.md @@ -1,3 +1,7 @@ +## wip4 revision 6: DP routing candidate — 2026-10-27 + +Owner report: dock HDMI and Ethernet not working on wip3; HDMI worked after moving the dock to another port. Journals show `apple-dcp 28cc00000` timing out the DPTX validate call (-110 x6, "Type-C DPTX reconnect failed after 5 retries") on every failing boot, each time on the first plug within ~4 s of resuming from s2idle; the same dock then worked on `289c00000`. wip2 used `28cc00000` successfully; wip3 never did. Ethernet (RTL8153, `r8152`) enumerated but had no carrier this boot (it had carrier on the previous wip3 boot with the identical missing-`rtl8153b-2.fw` message), so that looks physical/cable, not kernel. Candidate: wip4 revision 6 = wip3 source + #157 + local 0002 + upstream #186 (0003-0005; #163 excluded as conflicting duplicate). Full-tree `candidate.py prepare`/`check` and 83 aurora tests pass; kernel build, KUnit and boot are NOT done. Nothing installed, staged, armed or rebooted. `thunderbolt_apple.s2idle_teardown/resume_recovery/router_sleep` remain at the default N (upstream did not test them with a real dock). + ## Upstream watch: retry transient GitHub errors — 2026-10-27 After #598/#610 the dashboard renders and the 03:40 manual run passed, but the 05:06 scheduled run exited 1 because one source (`aurora-silicon/linux`) returned HTTP 500 (shown under "Fetch failures" in issue #51). `people-watch.get` had no retry, so any transient upstream 5xx failed the job. It now retries 5xx, 429, URL errors and timeouts up to 3 times (1 s, 3 s, 8 s); 404 still returns None and other 4xx (401/403/422) are not retried; a persistent failure still raises and still fails the run. 5 new tests in `test_github_watch.py`; 93 local-ai tests pass. Not yet observed on a scheduled run. diff --git a/mseries/aurora/test_qualification.py b/mseries/aurora/test_qualification.py index 6c22ece9b..7e9b7a8e3 100644 --- a/mseries/aurora/test_qualification.py +++ b/mseries/aurora/test_qualification.py @@ -224,10 +224,20 @@ def test_unified72_snapshot_has_separate_identity_and_no_moving_branch_url(self) self.assertRegex(data["source"]["sha256"], r"^[a-f0-9]{64}$") self.assertTrue(data["kernel_release"]["unified72"].startswith("7.2.11-")) lane = candidate.HERE / "unified72" - # wip4 carries the reviewed #157 display patch, then one local DPTX-stall patch. + # wip4 carries the reviewed #157 display patch, one local DPTX-stall patch, and the + # three commits of upstream PR #186 (route DP-alt ports only once their sink asserts HPD). self.assertEqual([patch["file"] for patch in data["patches"]], ["0001-drm-apple-srgb-external-display-colour.patch", - "0002-drm-apple-free-dp-tunnel-route-from-silent-dcpext.patch"]) + "0002-drm-apple-free-dp-tunnel-route-from-silent-dcpext.patch", + "0003-drm-apple-dcp-let-a-typec-port-with-hpd-take-over-a-held-pipeline.patch", + "0004-drm-apple-dcp-route-a-dp-alt-port-only-once-its-sink-asserts-hpd.patch", + "0005-usb-typec-tipd-treat-enospc-as-a-taken-display-on-every-soc.patch"]) + # #163 fixes the same tipd bug as 0005 and conflicts with it; it must not be carried as well. + self.assertFalse(any(patch["upstream"] and patch["upstream"].endswith("/pull/163") + for patch in data["patches"])) + self.assertEqual({patch["upstream"] for patch in data["patches"][2:]}, + {"https://github.com/omacom/linux-aurora/pull/186"}) + self.assertEqual(data["patches"][4]["head"], "2a37f6f206456462970b3bbb3bf9bf8bd8944acd") for entry in data["patches"]: self.assertEqual(entry["profile"], "candidate") self.assertEqual(candidate.sha256(lane / "patches" / entry["file"]), entry["sha256"]) diff --git a/mseries/aurora/unified72/README.md b/mseries/aurora/unified72/README.md index 4e545daf4..9e9577d41 100644 --- a/mseries/aurora/unified72/README.md +++ b/mseries/aurora/unified72/README.md @@ -1,5 +1,26 @@ # Unified Aurora 7.2.10 build candidate +## wip4 revision 6 — October 27, 2026 (build-only, not booted) + +Revision 6 adds the three commits of unmerged upstream [PR #186](https://github.com/omacom/linux-aurora/pull/186) +(`patches/0003`-`0005`, tested upstream head `2a37f6f2`): take over a pipeline held by a port whose sink never asserted +HPD, **route a DP-alt port only once its sink asserts HPD**, and treat `-ENOSPC` as a taken display on every SoC. Commit 2 +reports that routing before HPD left the firmware's DPTX service on that DCP answering `-110` until reboot, the same +symptom as `dcpext1` (`28cc00000`) here. It complements local patch 0002 (which recovers after a DCP goes silent). +[#163](https://github.com/omacom/linux-aurora/pull/163) is **not** carried: it fixes the same tipd bug as 0005 by a +different change and conflicts with it. + +Why now: across the retained journals (Oct 20-27) the dock produced no DPTX failures on `.20` and wip2, or on a wip3 +boot where it was plugged in at boot. All three failing boots (2026-10-22, 10-26, 10-27) were wip3 with the dock +**first plugged within about 4 s of resuming from s2idle**; each landed on `28cc00000`, timed out six times, then worked on +`289c00000` after the port was changed. That pattern is consistent with, not proof of, a post-resume DCP state problem. + +Checked: `candidate.py prepare --profile unified72` applied all five patches to the full pinned source (exit 0), +`candidate.py check` passed, 83 aurora tests pass. **Not done:** kernel build (CI), KUnit, any boot; the upstream test +report did not hot-plug a dock. 0002 and 0003/0004 both edit `dcp-fabric.c`; they apply in order but their semantic +interaction is unreviewed. Receipt: [review-wip4-2026-10-27.json](review-wip4-2026-10-27.json). wip3 stays installed; +nothing is staged or armed. + ## wip4 candidate — October 22, 2026 (build-only, not booted) **Revision 5 pins `7.2.11-elementary-aurora72-wip4`:** the same #158 source and #157 patch as wip3, plus one local diff --git a/mseries/aurora/unified72/patches/0003-drm-apple-dcp-let-a-typec-port-with-hpd-take-over-a-held-pipeline.patch b/mseries/aurora/unified72/patches/0003-drm-apple-dcp-let-a-typec-port-with-hpd-take-over-a-held-pipeline.patch new file mode 100644 index 000000000..d5cc6da4b --- /dev/null +++ b/mseries/aurora/unified72/patches/0003-drm-apple-dcp-let-a-typec-port-with-hpd-take-over-a-held-pipeline.patch @@ -0,0 +1,88 @@ +From 4da4932b130b21d7367d56859f558c4e8a8f101f Mon Sep 17 00:00:00 2001 +From: Andreas Fernandez +Date: Fri, 23 Oct 2026 11:10:03 +0200 +Subject: [PATCH 1/3] drm/apple: dcp: let a Type-C port with HPD take over a + pipeline held without it + +The PD controllers on the M1 Pro/Max laptops report a DP alt mode state +at probe for ports that have no display behind them, for example an +empty port or a dock without a display. Whichever port is routed first +gets a Type-C pipeline. If that is the wrong one, the port with the +display finds every pipeline taken (-ENOSPC) and stays dark until the +other port is unplugged, which can also put the LUKS prompt on the +internal panel of a closed laptop. + +When a port whose sink asserts HPD finds no free pipeline, release one +held by a direct DP-alt port whose sink never asserted HPD and take it. +Tunnels, ports with a pending safe state and pipelines whose fixed +output is in use are left alone. The released port drops its applied +state so its next update is routed again once a pipeline is free. + +Link: https://github.com/omacom/omarchy-pkgs/issues/1037 +Signed-off-by: Andreas Fernandez +--- + drivers/gpu/drm/apple/dcp-fabric.c | 43 ++++++++++++++++++++++++++++++ + 1 file changed, 43 insertions(+) + +diff --git a/drivers/gpu/drm/apple/dcp-fabric.c b/drivers/gpu/drm/apple/dcp-fabric.c +index 29321f8032a490..afdeaa974fe165 100644 +--- a/drivers/gpu/drm/apple/dcp-fabric.c ++++ b/drivers/gpu/drm/apple/dcp-fabric.c +@@ -1378,6 +1378,45 @@ static int dcp_typec_port_busy(struct apple_dcp_typec_port *port, + return -ENOSPC; + } + ++/* ++ * PD controllers report a DP state at probe for ports with no display behind ++ * them, an empty port or one with a dock that has none, and whichever port is ++ * routed first wins the pipeline. A port whose sink asserts HPD and finds no ++ * free pipeline takes one over from a direct DP-alt port whose sink never ++ * did. Tunnels, ports in a SAFE transition and pipelines whose fixed output ++ * is in use are left alone. The released port drops its applied state, so ++ * its next update is routed again once a pipeline is free. ++ */ ++static struct apple_dcp_typec_route * ++dcp_typec_take_over_without_hpd(struct apple_dcp_typec_port *port) ++{ ++ struct apple_dcp_typec_route *candidate; ++ ++ list_for_each_entry(candidate, &port->routes, port_link) { ++ struct apple_dcp_typec_route *held = ++ candidate->dcp->active_typec_route; ++ int ret; ++ ++ if (!held || held->tunnel || held->port == port || ++ held->port->hpd || held->port->dp_hpd || ++ held->port->safe_pending || ++ dcp_typec_route_busy(candidate)) ++ continue; ++ ++ ret = dcp_typec_route_deactivate(held); ++ if (ret) ++ return ERR_PTR(ret); ++ held->port->owner = NULL; ++ held->port->applied_valid = false; ++ dev_info(candidate->dcp->dev, ++ "released Type-C DPTX PHY %u held without HPD\n", ++ held->dptx_phy); ++ return candidate; ++ } ++ ++ return NULL; ++} ++ + static int dcp_typec_route_set(struct typec_mux_dev *mux, + struct typec_mux_state *state) + { +@@ -1519,6 +1558,10 @@ static int dcp_typec_route_set(struct typec_mux_dev *mux, + + if (!port->owner) { + best = dcp_typec_free_route(port); ++ if (!best && hpd) ++ best = dcp_typec_take_over_without_hpd(port); ++ if (IS_ERR(best)) ++ return PTR_ERR(best); + /* Every pipeline is driving another port's display. */ + if (!best) + return hpd ? dcp_typec_port_busy(port, route->dcp) : -ENOSPC; + diff --git a/mseries/aurora/unified72/patches/0004-drm-apple-dcp-route-a-dp-alt-port-only-once-its-sink-asserts-hpd.patch b/mseries/aurora/unified72/patches/0004-drm-apple-dcp-route-a-dp-alt-port-only-once-its-sink-asserts-hpd.patch new file mode 100644 index 000000000..cebc53c9b --- /dev/null +++ b/mseries/aurora/unified72/patches/0004-drm-apple-dcp-route-a-dp-alt-port-only-once-its-sink-asserts-hpd.patch @@ -0,0 +1,59 @@ +From bd1ad7b9b2795b7d3b3c7e616a54b78f9c92c6e0 Mon Sep 17 00:00:00 2001 +From: Andreas Fernandez +Date: Fri, 23 Oct 2026 11:10:03 +0200 +Subject: [PATCH 2/3] drm/apple: dcp: route a DP-alt port only once its sink + asserts HPD + +Outside compositor pairing order, a direct DP-alt port takes the lowest +free pipeline as soon as it enters DP mode, whether or not a display is +behind it. A USB-C dock without a display does exactly that, and on an +M1 Pro with two pipelines it leaves one display dark when it is plugged +in before them. The previous patch can take the pipeline back, but a +hand-over 6 ms after the dock's route was set left the firmware's DPTX +service on that DCP answering -110 until reboot. + +Leave a port that owns no pipeline and has no HPD unrouted, and route it +when the port controller reports HPD as a new status. A port that +already owns a pipeline keeps it across an HPD drop, as before, and the +compositor-ordered path is unchanged. + +Signed-off-by: Andreas Fernandez +--- + drivers/gpu/drm/apple/dcp-fabric.c | 17 +++++++++++++++-- + 1 file changed, 15 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/apple/dcp-fabric.c b/drivers/gpu/drm/apple/dcp-fabric.c +index afdeaa974fe165..41f8fdacebf3bb 100644 +--- a/drivers/gpu/drm/apple/dcp-fabric.c ++++ b/drivers/gpu/drm/apple/dcp-fabric.c +@@ -1557,14 +1557,27 @@ static int dcp_typec_route_set(struct typec_mux_dev *mux, + } + + if (!port->owner) { ++ /* ++ * Nothing behind the port asserts HPD yet, a dock without a ++ * display for one: leave the pipelines to ports with a display. ++ * The port controller reports HPD as a new status when a sink ++ * appears, and the port is routed then. A port that already ++ * owns a pipeline keeps it across an HPD drop, as before. ++ */ ++ if (!hpd) { ++ port->hpd = false; ++ port->applied_valid = true; ++ return 0; ++ } ++ + best = dcp_typec_free_route(port); +- if (!best && hpd) ++ if (!best) + best = dcp_typec_take_over_without_hpd(port); + if (IS_ERR(best)) + return PTR_ERR(best); + /* Every pipeline is driving another port's display. */ + if (!best) +- return hpd ? dcp_typec_port_busy(port, route->dcp) : -ENOSPC; ++ return dcp_typec_port_busy(port, route->dcp); + + ret = dcp_typec_route_activate(best, best->xbar); + if (ret) + diff --git a/mseries/aurora/unified72/patches/0005-usb-typec-tipd-treat-enospc-as-a-taken-display-on-every-soc.patch b/mseries/aurora/unified72/patches/0005-usb-typec-tipd-treat-enospc-as-a-taken-display-on-every-soc.patch new file mode 100644 index 000000000..aefede8e3 --- /dev/null +++ b/mseries/aurora/unified72/patches/0005-usb-typec-tipd-treat-enospc-as-a-taken-display-on-every-soc.patch @@ -0,0 +1,57 @@ +From 2a37f6f206456462970b3bbb3bf9bf8bd8944acd Mon Sep 17 00:00:00 2001 +From: Andreas Fernandez +Date: Fri, 23 Oct 2026 11:10:03 +0200 +Subject: [PATCH 3/3] usb: typec: tipd: treat -ENOSPC as a taken display on + every SoC + +The DCP fabric now reports a port without a pipeline as -ENOSPC on every +SoC, but cd321x_display_taken() only accepts it on H17 and expects -EBUSY +elsewhere. On an M1 Pro, a dock in DP alt mode with both pipelines +driving other displays gets "failed to configure Type-C connection: -28" +three times, the cable revalidation fails and the USB role is never set, +so everything behind the dock is gone. + +Accept -ENOSPC as a taken display everywhere and keep -EBUSY as one +outside H17 for older fabrics. + +Signed-off-by: Andreas Fernandez +--- + drivers/usb/typec/tipd/cd321x-pm-test.c | 2 +- + drivers/usb/typec/tipd/cd321x-pm.h | 8 +++++--- + 2 files changed, 6 insertions(+), 4 deletions(-) + +diff --git a/drivers/usb/typec/tipd/cd321x-pm-test.c b/drivers/usb/typec/tipd/cd321x-pm-test.c +index aef346d7bb4d8b..51d6d86f39e58a 100644 +--- a/drivers/usb/typec/tipd/cd321x-pm-test.c ++++ b/drivers/usb/typec/tipd/cd321x-pm-test.c +@@ -713,7 +713,7 @@ static void cd321x_display_taken_test(struct kunit *test) + KUNIT_EXPECT_FALSE(test, cd321x_display_taken(0, true)); + /* Other SoCs keep -EBUSY as the taken display. */ + KUNIT_EXPECT_TRUE(test, cd321x_display_taken(-EBUSY, false)); +- KUNIT_EXPECT_FALSE(test, cd321x_display_taken(-ENOSPC, false)); ++ KUNIT_EXPECT_TRUE(test, cd321x_display_taken(-ENOSPC, false)); + KUNIT_EXPECT_FALSE(test, cd321x_display_taken(-EAGAIN, false)); + KUNIT_EXPECT_FALSE(test, cd321x_display_taken(0, false)); + } +diff --git a/drivers/usb/typec/tipd/cd321x-pm.h b/drivers/usb/typec/tipd/cd321x-pm.h +index 8260e559b0c87f..b3f1bc8fad8567 100644 +--- a/drivers/usb/typec/tipd/cd321x-pm.h ++++ b/drivers/usb/typec/tipd/cd321x-pm.h +@@ -355,12 +355,14 @@ static inline void cd321x_pm_remove(struct cd321x_pm_state *pm) + /* + * A display mux reports -ENOSPC when every display pipeline is driving + * another port. That lasts until the other display goes away, so the port's +- * USB path is set up without the display and nothing is retried. Any other +- * error, such as -EBUSY from a PHY in the middle of a transition, is retried. ++ * USB path is set up without the display and nothing is retried. The DCP ++ * fabric reports it as -ENOSPC on every SoC now; older trees used -EBUSY ++ * outside H17, which is still accepted there. Any other error, such as ++ * -EBUSY from an H17 PHY in the middle of a transition, is retried. + */ + static inline bool cd321x_display_taken(int ret, bool enospc) + { +- return ret == (enospc ? -ENOSPC : -EBUSY); ++ return ret == -ENOSPC || (!enospc && ret == -EBUSY); + } + + enum cd321x_dp_send { diff --git a/mseries/aurora/unified72/review-wip4-2026-10-27.json b/mseries/aurora/unified72/review-wip4-2026-10-27.json new file mode 100644 index 000000000..8c132eca7 --- /dev/null +++ b/mseries/aurora/unified72/review-wip4-2026-10-27.json @@ -0,0 +1,90 @@ +{ + "schema": 1, + "reviewed_on": "2026-10-27", + "source": "bd526b3c11ccf8e91d6240261beb3219d8f17f03", + "previous_source": "bd526b3c11ccf8e91d6240261beb3219d8f17f03", + "upstream_pr": "https://github.com/omacom/linux-aurora/pull/158", + "upstream_status": "unchanged from wip3: #158 and #157 still open at review time; this lane adds one local patch only", + "patches": [ + { + "file": "0001-drm-apple-srgb-external-display-colour.patch", + "upstream": "https://github.com/omacom/linux-aurora/pull/157", + "head": "9d3aeb24cb864d5f99a7172e9004884bf3c1a9a0", + "base_of_pr": "44d5afc79da79bb3b17a3ee7494cb8d07a1f1aaa", + "files": [ + "drivers/gpu/drm/apple/dcp-internal.h", + "drivers/gpu/drm/apple/dcp.c", + "drivers/gpu/drm/apple/iomfb_template.c", + "drivers/gpu/drm/apple/parser.c", + "drivers/gpu/drm/apple/plane.c" + ], + "applicability": "patch(1) dry run against the five files at #158 head: clean, offsets +5 (iomfb_template.c) and +89 (plane.c); also clean against integration/aurora faed71028. Full-tree git apply --check runs in candidate.py prepare and in CI." + }, + { + "file": "0002-drm-apple-free-dp-tunnel-route-from-silent-dcpext.patch", + "upstream": null, + "files": [ + "drivers/gpu/drm/apple/dcp-fabric-core.c", + "drivers/gpu/drm/apple/dcp-fabric-core.h", + "drivers/gpu/drm/apple/dcp-fabric-test.c", + "drivers/gpu/drm/apple/dcp-fabric.c", + "drivers/gpu/drm/apple/dcp-internal.h", + "drivers/gpu/drm/apple/dcp.c" + ], + "applicability": "git apply --check passes on a tree holding the pinned #158 versions of the six files with 0001 applied (the six blobs were read from GitHub at bd526b3c, not from a full archive). Full-tree apply runs in candidate.py prepare and CI.", + "evidence": "mseries/aurora/hardware-tests/2026-10-22-j314s-wip3-dcpext1-dptx-wedge.md" + }, + { + "file": "0003-drm-apple-dcp-let-a-typec-port-with-hpd-take-over-a-held-pipeline.patch", + "upstream": "https://github.com/omacom/linux-aurora/pull/186", + "head": "4da4932b130b21d7367d56859f558c4e8a8f101f", + "base_of_pr": "42672ed79dead511ecf65e96d988a98a0f94b5bb", + "files": [ + "drivers/gpu/drm/apple/dcp-fabric.c" + ], + "applicability": "Applied in order after 0001 and 0002 to the pinned #158 (bd526b3c) versions of the eleven touched files, read from GitHub (not a full archive): git apply --check and apply succeeded for all five patches with no conflict. Not compiled, not KUnit-run, not booted. Full-tree apply runs in candidate.py prepare and CI." + }, + { + "file": "0004-drm-apple-dcp-route-a-dp-alt-port-only-once-its-sink-asserts-hpd.patch", + "upstream": "https://github.com/omacom/linux-aurora/pull/186", + "head": "bd1ad7b9b2795b7d3b3c7e616a54b78f9c92c6e0", + "base_of_pr": "42672ed79dead511ecf65e96d988a98a0f94b5bb", + "files": [ + "drivers/gpu/drm/apple/dcp-fabric.c" + ], + "applicability": "Applied in order after 0001 and 0002 to the pinned #158 (bd526b3c) versions of the eleven touched files, read from GitHub (not a full archive): git apply --check and apply succeeded for all five patches with no conflict. Not compiled, not KUnit-run, not booted. Full-tree apply runs in candidate.py prepare and CI." + }, + { + "file": "0005-usb-typec-tipd-treat-enospc-as-a-taken-display-on-every-soc.patch", + "upstream": "https://github.com/omacom/linux-aurora/pull/186", + "head": "2a37f6f206456462970b3bbb3bf9bf8bd8944acd", + "base_of_pr": "42672ed79dead511ecf65e96d988a98a0f94b5bb", + "files": [ + "drivers/usb/typec/tipd/cd321x-pm-test.c", + "drivers/usb/typec/tipd/cd321x-pm.h" + ], + "applicability": "Applied in order after 0001 and 0002 to the pinned #158 (bd526b3c) versions of the eleven touched files, read from GitHub (not a full archive): git apply --check and apply succeeded for all five patches with no conflict. Not compiled, not KUnit-run, not booted. Full-tree apply runs in candidate.py prepare and CI." + } + ], + "not_rerun": [ + "kernel build (no compile of dcp.c or dcp-fabric.c was possible here; CI decides)", + "KUnit run of fabric_unresponsive_pipeline_test (the same scenario ran as a userspace harness against the real dcp-fabric-core.c with stub headers: passes, and fails when the availability change is removed)", + "64-fix semantic parity", + "host tests listed in series.json against the source", + "any hardware test" + ], + "known_interactions": [ + "#158 changes apple_plane_init() to take both supports_xrgb2101010 and an interchange argument; #157 edits plane.c nearby. The dry run is clean but the build is the real check.", + "#158 does not advertise Interchange framebuffer formats on H17P firmware; not relevant to the J314S.", + "0002 touches dcp.c and dcp-internal.h, which 0001 also edits; the hunks do not overlap but apply order is 0001 then 0002.", + "0002 changes dcp_tb_release_locked(): a timed-out release is now treated as complete. This is the riskiest line; it relies on the firmware being unreachable, not merely slow (the failure seen was six consecutive 1 s timeouts, then every later call timing out).", + "0002 (skip a DCP whose DPTX times out) and 0003/0004 (route a DP-alt port only after HPD / take over a pipeline held without it) both modify dcp-fabric.c. They apply cleanly in order; semantic interaction and KUnit coverage are unreviewed.", + "Upstream PR #163 is not carried: it fixes the same tipd bug as 0005 and conflicts with it." + ], + "archive_verification": { + "sha256": "881d5a061efa212ed28489311cc97ab6d9bf52891a6c7a00b18f0bbaff131957", + "bytes": 270217582, + "method": "Entire codeload gzip downloaded and hashed locally (trust on first use: the pin is this digest); tar root read from the archive listing; Makefile reads VERSION 7, PATCHLEVEL 2, SUBLEVEL 11.", + "archive_root": "linux-aurora-bd526b3c11ccf8e91d6240261beb3219d8f17f03" + } +} diff --git a/mseries/aurora/unified72/series.json b/mseries/aurora/unified72/series.json index f592310b1..01ef5c098 100644 --- a/mseries/aurora/unified72/series.json +++ b/mseries/aurora/unified72/series.json @@ -1,7 +1,7 @@ { "schema": 1, "base_source": "c46d7dcdb8712ea9aeb0ce47b439ffae76371e55", - "revision": 5, + "revision": 6, "source": { "repository": "aurora-silicon/linux", "commit": "bd526b3c11ccf8e91d6240261beb3219d8f17f03", @@ -34,6 +34,30 @@ "head": null, "profile": "candidate", "note": "drm/apple: local, not upstream. A DCP whose DPTX RemotePort times out is passed over for 300 s, and a timed-out release no longer pins the port's DP tunnel route to it. Applies after 0001 to the #158 source (checked against the exact files; full-tree apply is checked by candidate.py prepare and CI). Never built or booted." + }, + { + "file": "0003-drm-apple-dcp-let-a-typec-port-with-hpd-take-over-a-held-pipeline.patch", + "sha256": "2273d0ee008bafc702358bfc553ecfdd8cd9ea8d665c4ce5fb2b3b36af4f9111", + "upstream": "https://github.com/omacom/linux-aurora/pull/186", + "head": "4da4932b130b21d7367d56859f558c4e8a8f101f", + "profile": "candidate", + "note": "drm/apple: dcp: let a Type-C port with HPD take over a pipeline held without it (omacom/linux-aurora PR #186 commit 1/3, author-reported on an M1 Pro J314s). Unmerged upstream; not reviewed upstream." + }, + { + "file": "0004-drm-apple-dcp-route-a-dp-alt-port-only-once-its-sink-asserts-hpd.patch", + "sha256": "111a8d0d4edca05367301e8e1afc3a487bf1638b015073ea81ae64cd1d4817ec", + "upstream": "https://github.com/omacom/linux-aurora/pull/186", + "head": "bd1ad7b9b2795b7d3b3c7e616a54b78f9c92c6e0", + "profile": "candidate", + "note": "drm/apple: dcp: route a DP-alt port only once its sink asserts HPD (PR #186 commit 2/3). Its message reports that routing before HPD left the DPTX service on that DCP answering -110 until reboot, the symptom seen on dcpext1 (28cc00000) here." + }, + { + "file": "0005-usb-typec-tipd-treat-enospc-as-a-taken-display-on-every-soc.patch", + "sha256": "f6c63701d3197204abd1f3c3bd9fb9c44a0a7156afdd63cba2feb04c5522c037", + "upstream": "https://github.com/omacom/linux-aurora/pull/186", + "head": "2a37f6f206456462970b3bbb3bf9bf8bd8944acd", + "profile": "candidate", + "note": "usb: typec: tipd: treat -ENOSPC as a taken display on every SoC (PR #186 commit 3/3). Overlaps unmerged PR #163, which fixes the same bug by changing the cd321x_display_taken() signature; the two conflict, so only this one is carried." } ], "config": { @@ -47,7 +71,7 @@ "tools/asahi/pmp-bootargs-tests.rs", "tools/asahi/pmp-power-tests.rs" ], - "qualification": "Same unmerged #158 source (head bd526b3c) and #157 patch as wip3, plus one local patch for the dcpext1 DPTX stall seen on the first wip3 dock plug (2026-10-22). Build-only candidate: patch application is checked on the exact source files and the pure routing logic was exercised in userspace; kernel build, KUnit, J314S boot, dock and recovery are NOT established. Replaces the wip3 pin in this file; wip3 stays installed and is the running trial kernel.", + "qualification": "Same unmerged #158 source (head bd526b3c) and #157 patch as wip3, plus one local patch for the dcpext1 DPTX stall seen on the first wip3 dock plug (2026-10-22). Build-only candidate: patch application is checked on the exact source files and the pure routing logic was exercised in userspace; kernel build, KUnit, J314S boot, dock and recovery are NOT established. Replaces the wip3 pin in this file; wip3 stays installed and is the running trial kernel. Revision 6 adds upstream PR #186 (three commits, tested head 2a37f6f2) so ports route to a display controller only once their sink asserts HPD; it complements local patch 0002. Build-only; nothing booted.", "superseded_patches": [ { "file": "0001-stock-bindgen-layout-policy.patch", @@ -58,5 +82,5 @@ "note": "Historical local port for wip1; corresponding stock-bindgen default and BINDGEN_LAYOUT_TESTS opt-in are now carried in merged upstream source. Retained for provenance; not applied to wip2." } ], - "review_receipt": "review-wip4-2026-10-22.json" + "review_receipt": "review-wip4-2026-10-27.json" } From 1dfb6ecc43400b2051bdd7c81c3408defd45e305 Mon Sep 17 00:00:00 2001 From: sdolbec Date: Tue, 27 Oct 2026 19:54:00 -0400 Subject: [PATCH 3/3] wip4 rev 6: record the passing native kernel build (CI run 40498010695) --- mseries/aurora/unified72/README.md | 2 +- mseries/aurora/unified72/review-wip4-2026-10-27.json | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/mseries/aurora/unified72/README.md b/mseries/aurora/unified72/README.md index 9e9577d41..d068a7bbd 100644 --- a/mseries/aurora/unified72/README.md +++ b/mseries/aurora/unified72/README.md @@ -16,7 +16,7 @@ boot where it was plugged in at boot. All three failing boots (2026-10-22, 10-26 `289c00000` after the port was changed. That pattern is consistent with, not proof of, a post-resume DCP state problem. Checked: `candidate.py prepare --profile unified72` applied all five patches to the full pinned source (exit 0), -`candidate.py check` passed, 83 aurora tests pass. **Not done:** kernel build (CI), KUnit, any boot; the upstream test +`candidate.py check` passed, 83 aurora tests pass. **Native build:** passed in [CI run 40498010695](https://github.com/techmore/ElementaryOS_MSeries/actions/runs/40498010695) (job *ARM64 unified72 build and payload inspection*, 2026-10-27; the 293 MB / 297 MB artifacts were not downloaded or audited here). **Not done:** KUnit, any boot; the upstream test report did not hot-plug a dock. 0002 and 0003/0004 both edit `dcp-fabric.c`; they apply in order but their semantic interaction is unreviewed. Receipt: [review-wip4-2026-10-27.json](review-wip4-2026-10-27.json). wip3 stays installed; nothing is staged or armed. diff --git a/mseries/aurora/unified72/review-wip4-2026-10-27.json b/mseries/aurora/unified72/review-wip4-2026-10-27.json index 8c132eca7..95abdd77e 100644 --- a/mseries/aurora/unified72/review-wip4-2026-10-27.json +++ b/mseries/aurora/unified72/review-wip4-2026-10-27.json @@ -86,5 +86,13 @@ "bytes": 270217582, "method": "Entire codeload gzip downloaded and hashed locally (trust on first use: the pin is this digest); tar root read from the archive listing; Makefile reads VERSION 7, PATCHLEVEL 2, SUBLEVEL 11.", "archive_root": "linux-aurora-bd526b3c11ccf8e91d6240261beb3219d8f17f03" + }, + "native_build": { + "ci_run": "https://github.com/techmore/ElementaryOS_MSeries/actions/runs/40498010695", + "job": "ARM64 unified72 build and payload inspection", + "conclusion": "success", + "date": "2026-10-27", + "artifacts_audited": false, + "note": "Build and the workflow payload inspection passed. Artifact digest/SHA256SUMS/module audit not performed here; KUnit and boot not done." } }