Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 49 additions & 1 deletion drivers/gpu/drm/apple/dcp-fabric.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ module_param(tiled_split, bool, 0644);
MODULE_PARM_DESC(tiled_split,
"Drive both halves of a tiled Thunderbolt display (LG UltraFine 5K) on one pipeline, through its two DPTX ports (default: on)");

static bool split_dock_main;
module_param(split_dock_main, bool, 0644);
MODULE_PARM_DESC(split_dock_main,
"EXPERIMENT: put a Thunderbolt display on the free HDMI pipeline (dcpext0), so split_modeset can drive it over two pipes as macOS does");

static bool typec_follow_crtc = true;
module_param(typec_follow_crtc, bool, 0444);
MODULE_PARM_DESC(typec_follow_crtc,
Expand Down Expand Up @@ -180,7 +185,9 @@ static bool dcp_typec_route_busy(struct apple_dcp_typec_route *route)
struct apple_dcp *dcp = route->dcp;

return dcp_typec_route_fixed_output_busy(route) ||
(dcp->external_native && iomfb_v14_7_external_failed(dcp));
(dcp->external_native && iomfb_v14_7_external_failed(dcp)) ||
/* Experimental (split_modeset): lent to another display's split. */
(dcp->external_native && iomfb_v14_7_split_helper(dcp));
}

static bool dcp_typec_route_available(struct apple_dcp_typec_route *route)
Expand Down Expand Up @@ -1954,6 +1961,47 @@ static int dcp_tb_candidate(void *data)
return ret;
}

/*
* Experimental (split_dock_main): a display driven over two pipes
* needs the HDMI pipeline (the hybrid, dcpext0 on T6030) as the one
* that owns the link, as macOS does. Take it for the tunnel while it is
* free and no HDMI display is present.
*/
if (READ_ONCE(split_dock_main) && !best->dcp->fixed_phy) {
struct apple_dcp_typec_route *r;

list_for_each_entry(r, &port->routes, port_link) {
struct apple_dcp *h = r->dcp;
bool hdmi_empty;

if (r == best || !h->fixed_phy || !dcp_typec_tunnel_ctl(r, dpin))
continue;
/*
* HDMI presence stays SETTLING for DCP_HDMI_HOLD_MS after
* any edge, including the first sample at boot, which is
* when a dock's tunnel comes up. Read the HPD line then.
*/
switch (dcp_hdmi_presence(h)) {
case DCP_FABRIC_ABSENT:
hdmi_empty = true;
break;
case DCP_FABRIC_SETTLING:
hdmi_empty = h->hdmi_hpd && !gpiod_get_value_cansleep(h->hdmi_hpd);
break;
default:
hdmi_empty = false;
break;
}
if (!hdmi_empty || h->active_typec_route || dcp_typec_safe_held(h) ||
dcp_typec_route_busy(r))
continue;
dev_info(r->dcp->dev, "split_dock_main: tunnel dpin%u takes this pipeline instead of %s\n",
dpin, dev_name(best->dcp->dev));
best = r;
break;
}
}

ctl = dcp_typec_tunnel_ctl(best, dpin);
if (!ctl)
return -EOPNOTSUPP;
Expand Down
39 changes: 39 additions & 0 deletions drivers/gpu/drm/apple/dptxep.c
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,21 @@ struct dptxport_apcall_set_tiled {
#define DPTX_TILE_HINT_HEIGHT 0x3c
#define DPTX_TILE_HINT_MIN_SIZE 0x40

/*
* Experimental (split_alloc): macOS 14 on J514S answers this call
* with a pipe allocation at +0x64..+0x77 (seen in a hypervisor trace:
* 1, 1, 6144, 1438000000, 1274019840). The DCP then offers 5K@120/180 as
* split timings (ID + 10000). Off by default; can be flipped at runtime and
* takes effect on the next hotplug.
*/
static bool split_alloc;
module_param(split_alloc, bool, 0644);
MODULE_PARM_DESC(split_alloc,
"EXPERIMENT: offer a dcpext peer pipe in SetTiledDisplayHints replies like macOS 14");

#define DPTX_SPLIT_ALLOC_OFF 0x64
#define DPTX_SPLIT_ALLOC_END 0x78

/*
* A Thunderbolt DP tunnel uses the same CORE|ATC|DIE|CONNECTED target as a
* direct alt-mode PHY: CORE is the DFP port (0 = dpphy, 1/2 = dpin0/dpin1)
Expand Down Expand Up @@ -661,6 +676,30 @@ static int dptxport_call_set_tiled_display_hint(struct apple_epic_service *servi
}

reply->retcode = cpu_to_le32(1);

/* Only for a single-stream sink that reports its size (not a 2-tile LG). */
if (READ_ONCE(split_alloc) && data_size >= DPTX_SPLIT_ALLOC_END &&
reply_size >= DPTX_SPLIT_ALLOC_END &&
get_unaligned_le32(hint + DPTX_TILE_HINT_TILES_H) <= 1 &&
get_unaligned_le32(hint + DPTX_TILE_HINT_WIDTH) &&
get_unaligned_le32(hint + DPTX_TILE_HINT_HEIGHT)) {
u8 *r = reply_;

put_unaligned_le32(1, r + DPTX_SPLIT_ALLOC_OFF + 0x00);
put_unaligned_le32(1, r + DPTX_SPLIT_ALLOC_OFF + 0x04);
put_unaligned_le32(0x1800, r + DPTX_SPLIT_ALLOC_OFF + 0x08);
put_unaligned_le32(0x55b62380, r + DPTX_SPLIT_ALLOC_OFF + 0x0c);
put_unaligned_le32(0x4bf00000, r + DPTX_SPLIT_ALLOC_OFF + 0x10);
dev_info(service->ep->dcp->dev,
"DPTXPort: EXPERIMENT split_alloc: offered a peer pipe for %ux%u (data %zu, reply %zu)\n",
get_unaligned_le32(hint + DPTX_TILE_HINT_WIDTH),
get_unaligned_le32(hint + DPTX_TILE_HINT_HEIGHT),
data_size, reply_size);
} else if (READ_ONCE(split_alloc)) {
dev_info(service->ep->dcp->dev,
"DPTXPort: EXPERIMENT split_alloc: hint not used (data %zu, reply %zu)\n",
data_size, reply_size);
}
return 0;
}

Expand Down
Loading