From 134c525c7b54871830fae22d6e6fabfbe2f79fbd Mon Sep 17 00:00:00 2001 From: Kieran Graham Date: Wed, 14 Oct 2026 23:16:08 +0100 Subject: [PATCH 4/6] drm/apple, thunderbolt: retain LG UUID pairing and balanced HBR2 Reuse the parent generic split-display ownership, drain and CRTC-follow paths instead of duplicating the old tiled routes. Pass the selected router identity with the immutable binding generation. The explicit LG override bypasses missing firmware tile hints; generic docks keep the parent detection. Cap DP capabilities before either tunnel reserves bandwidth. Keep upstream domain-reference pairing untouched. Ported from release 56 onto omacom/linux-aurora 6f6367775. Assisted-by: Codex:gpt-6.1-sol --- drivers/gpu/drm/apple/dcp-fabric.c | 8 +++--- drivers/thunderbolt/apple.c | 10 +++++-- drivers/thunderbolt/tunnel.c | 41 +++++++++++++++++++++++++++++ drivers/thunderbolt/tunnel.h | 2 ++ include/linux/soc/apple/dp-tunnel.h | 2 +- 5 files changed, 57 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp-fabric.c b/drivers/gpu/drm/apple/dcp-fabric.c index a5eb7e8b4..d82f319fb 100644 --- a/drivers/gpu/drm/apple/dcp-fabric.c +++ b/drivers/gpu/drm/apple/dcp-fabric.c @@ -1835,6 +1835,7 @@ struct dcp_tb_attach_context { struct mux_control *ctl; unsigned int dpin; bool active; + bool tiled; int (*set_active)(void *binding, bool active); void *binding; }; @@ -2168,7 +2169,7 @@ static int dcp_tb_split_locked(struct apple_dcp_typec_port *port, * topology for dpin0 shortly after its link comes up: wait for that, * but not for ever, as a sink may never send one. */ - if (!smp_load_acquire(&dcp->dptxport[0].tile_hint)) { + if (!req->tiled && !smp_load_acquire(&dcp->dptxport[0].tile_hint)) { if (time_before(jiffies, port->tile_hint_deadline)) return -ENODEV; return -EOPNOTSUPP; @@ -2236,7 +2237,7 @@ static int dcp_tb_dispatch_request(void *data, const struct dcp_fabric_port *fou slot = dpin ? &port->secondary_owner : &port->owner; - if (dpin == 1 && (tiled_split || port->split_dcp)) { + if (dpin == 1 && (ctx->tiled || tiled_split || port->split_dcp)) { ret = dcp_tb_split_locked(port, ctx); if (ret != -EOPNOTSUPP) return ret; @@ -2352,7 +2353,7 @@ static int dcp_tb_dispatch(void *data, const struct dcp_fabric_port *found) } int apple_dcp_tb_dp_tunnel(struct device_node *connector_np, unsigned int dpin, - u64 generation, bool active, + u64 generation, bool active, bool tiled, int (*set_active)(void *binding, bool active), void *binding) { struct apple_dcp_typec_port *pos; @@ -2361,6 +2362,7 @@ int apple_dcp_tb_dp_tunnel(struct device_node *connector_np, unsigned int dpin, .generation = generation, .dpin = dpin, .active = active, + .tiled = tiled, .set_active = set_active, .binding = binding, }; diff --git a/drivers/thunderbolt/apple.c b/drivers/thunderbolt/apple.c index 98379a59a..db398dae3 100644 --- a/drivers/thunderbolt/apple.c +++ b/drivers/thunderbolt/apple.c @@ -86,6 +86,7 @@ #include "apple-dpin-platform.h" #include "nhi.h" #include "tb.h" +#include "tunnel.h" #define APPLE_CIO_M3_CTRL 0x0c #define APPLE_CIO_M3_CTRL_START BIT(1) @@ -228,6 +229,7 @@ struct apple_cio { struct apple_dpin_state state; struct apple_dpin_tokens tokens; struct apple_dpin_binding *binding; /* worker-owned, handed to DCP */ + bool tiled; /* c->lock: UUID policy for the requested generation */ } dpin[2]; }; @@ -235,6 +237,7 @@ struct apple_cio { struct apple_dpin_binding { struct apple_dpin_ctx *ctx; u64 generation; + bool tiled; typeof(apple_dcp_tb_dp_tunnel) *tunnel; /* owns the provider module reference */ }; @@ -456,7 +459,7 @@ static int apple_dpin_provider_invoke(void *data, bool active) struct apple_dpin_binding *binding = data; struct apple_dpin_ctx *c = binding->ctx; - return binding->tunnel(c->acio->connector_np, c->idx, binding->generation, active, + return binding->tunnel(c->acio->connector_np, c->idx, binding->generation, active, binding->tiled, active ? apple_dpin_dcp_set_active : NULL, active ? binding : NULL); } @@ -641,7 +644,7 @@ static void apple_dpin_work_fn(struct work_struct *work) struct apple_dpin_ctx *c = container_of(work, struct apple_dpin_ctx, work); struct apple_dpin_binding *binding; enum apple_dpin_event event; - bool deferred, stale; + bool deferred, stale, tiled; unsigned int actions; u64 generation; int ret; @@ -651,6 +654,7 @@ static void apple_dpin_work_fn(struct work_struct *work) actions = apple_dpin_step(&c->state, c->acio->dp, APPLE_DPIN_WORK, !!c->regs, 0); generation = c->tokens.requested; + tiled = c->tiled; } if (actions & APPLE_DPIN_CANCEL_RETRY) @@ -673,6 +677,7 @@ static void apple_dpin_work_fn(struct work_struct *work) if (binding) { binding->ctx = c; binding->generation = generation; + binding->tiled = tiled; ret = apple_dpin_up(c, binding, &deferred); } else { ret = -ENOMEM; @@ -1704,6 +1709,7 @@ static int apple_nhi_dp_tunnel_post_activate(struct tb_nhi *nhi, scoped_guard(mutex, &c->lock) { if (!apple_dpin_request(c, generation, true)) return -ESTALE; + c->tiled = tb_dp_is_apple_tiled(nhi, out); } } diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index 4935f4543..a68bd8d4f 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -19,6 +19,13 @@ #include "tunnel.h" #include "tb.h" +/* Opt-in by sink identity so tiled pairing follows the monitor between ports. */ +static char apple_tiled_uuid[UUID_STRING_LEN + 1]; +module_param_string(apple_tiled_uuid, apple_tiled_uuid, + sizeof(apple_tiled_uuid), 0444); +MODULE_PARM_DESC(apple_tiled_uuid, + "Thunderbolt router UUID of an Apple-host two-stream tiled display"); + /* PCIe adapters use always HopID of 8 for both directions */ #define TB_PCI_HOPID 8 @@ -117,6 +124,23 @@ static bool tb_host_dp_policy(const struct tb_nhi *nhi, unsigned long policy) return nhi && (nhi->host_dp_policy & policy); } +bool tb_dp_is_apple_tiled(struct tb_nhi *nhi, const struct tb_port *out) +{ + uuid_t uuid; + + if (!nhi || !nhi->dev || + !of_device_is_compatible(nhi->dev->of_node, "apple,t8103-usb4-nhi") || + !out || !tb_port_is_dpout(out) || + !out->sw->uuid || !apple_tiled_uuid[0]) + return false; + if (uuid_parse(apple_tiled_uuid, &uuid)) { + pr_warn_once("thunderbolt: invalid apple_tiled_uuid\n"); + return false; + } + return uuid_equal(out->sw->uuid, &uuid); +} +EXPORT_SYMBOL_GPL(tb_dp_is_apple_tiled); + static const char * const tb_tunnel_names[] = { "PCI", "DP", "DMA", "USB3" }; static const char * const tb_event_names[] = { @@ -906,6 +930,23 @@ static int tb_dp_xchg_caps(struct tb_tunnel *tunnel) if (ret) return ret; + /* + * A two-stream tiled display needs bandwidth for both tiles. An + * HBR3 reservation for its first stream can leave only HBR for + * the second, which is insufficient for a 2560x2160 tile. Limit + * both advertised ends before reserving or training either stream; + * limiting DCP alone is too late for the capability-based reserve. + * Keep the hardware's local capabilities intact and leave all + * other ports unchanged. Automatic tile discovery is still needed. + */ + if (tb_dp_is_apple_tiled(tunnel->tb->nhi, out)) { + in_dp_cap = tb_dp_cap_set_rate(in_dp_cap, + min(tb_dp_cap_get_rate(in_dp_cap), 5400U)); + out_dp_cap = tb_dp_cap_set_rate(out_dp_cap, + min(tb_dp_cap_get_rate(out_dp_cap), 5400U)); + tb_tunnel_info(tunnel, "Apple tiled display: balanced HBR2 capability limit\n"); + } + /* Write IN local caps to OUT remote caps */ ret = tb_port_write(out, &in_dp_cap, TB_CFG_PORT, out->cap_adap + DP_REMOTE_CAP, 1); diff --git a/drivers/thunderbolt/tunnel.h b/drivers/thunderbolt/tunnel.h index 30c5420ac..41b4f281d 100644 --- a/drivers/thunderbolt/tunnel.h +++ b/drivers/thunderbolt/tunnel.h @@ -13,6 +13,8 @@ #include "tb.h" +bool tb_dp_is_apple_tiled(struct tb_nhi *nhi, const struct tb_port *out); + enum tb_tunnel_type { TB_TUNNEL_PCI, TB_TUNNEL_DP, diff --git a/include/linux/soc/apple/dp-tunnel.h b/include/linux/soc/apple/dp-tunnel.h index 396daa36f..1be403f32 100644 --- a/include/linux/soc/apple/dp-tunnel.h +++ b/include/linux/soc/apple/dp-tunnel.h @@ -35,7 +35,7 @@ struct phy; * and drains every binding before module unload/counter reset. */ int apple_dcp_tb_dp_tunnel(struct device_node *connector_np, unsigned int dpin, - u64 generation, bool active, + u64 generation, bool active, bool tiled, int (*set_active)(void *binding, bool active), void *binding); /* -- 2.56.0