Hi,
the external Mini-DisplayPort output of an Apple MacBook5,1 (NVIDIA C79 /
GeForce 9400M, 10de:0863) never appears in /sys/class/drm: only LVDS-1 is
listed, so the output cannot be used at all. The VBIOS describes that
connector with two outputs sharing the same connector index:
disp: conn 01:0146: type 46 loc 1 hpd 02 dp 2 ...
disp: outp 01:0002:0242: type 02 (TMDS) loc 0 or 2 link 1 con 1 ...
disp: outp 02:0006:0242: type 06 (DP) loc 0 or 2 link 1 con 1 ...
nvkm_uconn_uevent() picks the first output whose connector index matches,
i.e.
the TMDS one without an AUX channel, so a NVIF_CONN_EVENT_V0_IRQ request
ends
up in the GPIO path and is refused with -EINVAL. nouveau_connector_create()
treats that as fatal and destroys connector and output.
With that fixed, ddc_detect() shows the second half of the problem: it also
walks the encoders in list order, so a sink that is really speaking
DisplayPort is probed through the TMDS/DDC path first and reported as
disconnected because its EDID cannot be read from that bus.
Tested with a Samsung U28E590 on the Mini-DisplayPort output (Linux 7.2.5):
the connector is created, its IRQ event is registered on the DP output,
nouveau_dp_detect() returns NOUVEAU_DP_SST, and 1920x1080@60 and
2560x1440@60
work. Native 3840x2160 is limited to 30 Hz by the DP 1.1 link of this GPU;
on this particular panel 4K@30 trains but leaves the panel asleep, which I
believe is a separate sink-side issue and not covered here.
Comments welcome.
Daniele Parravicini (2):
drm/nouveau/nvkm: pick an AUX-capable output for DP IRQ events
drm/nouveau: probe the DP output first on DisplayPort connectors
drivers/gpu/drm/nouveau/nouveau_connector.c | 23 ++++++++++++++++++
drivers/gpu/drm/nouveau/nvkm/engine/disp/uconn.c | 22 +++++++++++++++---
2 files changed, 42 insertions(+), 3 deletions(-)
>From fa3c4fb42c735323ef48214c174dc112e637303c Mon Sep 17 00:00:00 2001
From: Daniele Parravicini <[email protected]>
Date: Thu, 18 Sep 2026 15:55:00 +0200
Subject: [PATCH 1/2] drm/nouveau/nvkm: pick an AUX-capable output for
DP IRQ events
nvkm_uconn_uevent() picks the first output whose connector index matches,
and
decides how to service the requested events from that output alone.
A connector can be driven by more than one output: on dual-mode connectors
(eg. Apple Mini-DP) both a TMDS and a DP output carry the same connector
index. The TMDS output has no AUX channel, so a request for
NVIF_CONN_EVENT_V0_IRQ ends up in the GPIO path, where it is refused with
-EINVAL:
if (!outp->info.location && !nvkm_connector_is_dp_dms(conn->info.type))
return -EINVAL;
nouveau_connector_create() treats that error as fatal and destroys the
connector, so the external output disappears entirely (only LVDS-1 is left
in
/sys/class/drm).
Prefer an output that can actually service the request: when IRQ events are
asked for, use the first matching output with an AUX channel, falling back
to
the previous behaviour when there is none.
Diagnosed on a MacBook5,1 (GeForce 9400M, C79) with a Samsung U28E590 on the
Mini-DisplayPort output: the connector is created and its IRQ event is
registered on the DP output instead of being dropped.
Signed-off-by: Daniele Parravicini <[email protected]>
---
drivers/gpu/drm/nouveau/nvkm/engine/disp/uconn.c | 22
+++++++++++++++++++---
1 file changed, 19 insertions(+), 3 deletions(-)
---
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/uconn.c
b/drivers/gpu/drm/nouveau/nvkm/engine/disp/uconn.c
index 23d1e5c..319352e 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/uconn.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/uconn.c
@@ -98,6 +98,7 @@ nvkm_uconn_uevent(struct nvkm_object *object, void
*argv, u32 argc, struct nvkm_
struct nvkm_disp *disp = conn->disp;
struct nvkm_device *device = disp->engine.subdev.device;
struct nvkm_outp *outp;
+ struct nvkm_outp *iter;
union nvif_conn_event_args *args = argv;
u64 bits = 0;
@@ -110,12 +111,27 @@ nvkm_uconn_uevent(struct nvkm_object *object,
void *argv, u32 argc, struct nvkm_
if (argc != sizeof(args->v0) || args->v0.version != 0)
return -ENOSYS;
- list_for_each_entry(outp, &conn->disp->outps, head) {
- if (outp->info.connector == conn->index)
+ /* A connector can be driven by more than one output: on dual-mode
+ * connectors (eg. Apple Mini-DP) both a TMDS and a DP output carry the
+ * same connector index. DP IRQ events can only be serviced by an output
+ * that has an AUX channel, and picking the wrong one makes event setup
+ * fail, which in turn makes DRM drop the connector completely.
+ */
+ outp = NULL;
+ list_for_each_entry(iter, &conn->disp->outps, head) {
+ if (iter->info.connector != conn->index)
+ continue;
+
+ if (!(args->v0.types & NVIF_CONN_EVENT_V0_IRQ) || iter->dp.aux) {
+ outp = iter;
break;
+ }
+
+ if (!outp)
+ outp = iter;
}
- if (&outp->head == &conn->disp->outps)
+ if (!outp)
return -EINVAL;
if (disp->rm.client.gsp) {
>From 4d3bc3d57988c5fb6f2c976e66f5ec7a8b921eb7 Mon Sep 17 00:00:00 2001
From: Daniele Parravicini <[email protected]>
Date: Thu, 18 Sep 2026 15:55:00 +0200
Subject: [PATCH 2/2] drm/nouveau: probe the DP output first on
DisplayPort connectors
nouveau_connector_ddc_detect() walks the encoders attached to the connector
in
list order. On a dual-mode DisplayPort connector both a TMDS and a DP
output
are attached to the same connector and HPD presence is shared by both, so
the
TMDS output is picked first: a sink in DP mode is then taken for a DVI/HDMI
sink and its EDID is read from a DDC bus that doesn't exist, losing the
display.
Ask the DP output first for DP-capable connectors. Only it can tell whether
the sink is really speaking DisplayPort; if it is not, the previous TMDS/DDC
path is still tried.
Diagnosed on a MacBook5,1 (GeForce 9400M, C79): HPD reads present and the
monitor answers on AUX (nvkm-...-aux-0008), but the connector was reported
disconnected because the EDID was read from the TMDS DDC bus.
Signed-off-by: Daniele Parravicini <[email protected]>
---
drivers/gpu/drm/nouveau/nouveau_connector.c | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
---
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c
b/drivers/gpu/drm/nouveau/nouveau_connector.c
index b0b0ad9..3628e6d 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -417,6 +417,29 @@ nouveau_connector_ddc_detect(struct drm_connector
*connector)
int ret;
bool switcheroo_ddc = false;
+ /* Dual-mode DisplayPort connectors (eg. Apple Mini-DP) carry both a TMDS
+ * and a DP output on the same connector, and HPD presence is shared by
+ * both of them. Probing them in list order would pick the TMDS output
+ * first and read EDID from a DDC bus that doesn't exist, even when the
+ * sink is connected in DP mode. Ask the DP output first: only it can
+ * tell whether the sink is really speaking DisplayPort.
+ */
+ if (conn->type == DCB_CONNECTOR_DMS59_DP0 ||
+ conn->type == DCB_CONNECTOR_DMS59_DP1 ||
+ conn->type == DCB_CONNECTOR_DP ||
+ conn->type == DCB_CONNECTOR_mDP ||
+ conn->type == DCB_CONNECTOR_USB_C ||
+ conn->type == DCB_CONNECTOR_eDP) {
+ nv_encoder = find_encoder(connector, DCB_OUTPUT_DP);
+ if (nv_encoder && nvif_object_constructed(&nv_encoder->outp.object)) {
+ ret = nouveau_dp_detect(conn, nv_encoder);
+ if (ret == NOUVEAU_DP_MST)
+ return NULL;
+ if (ret == NOUVEAU_DP_SST)
+ return nv_encoder;
+ }
+ }
+
drm_connector_for_each_possible_encoder(connector, encoder) {
nv_encoder = nouveau_encoder(encoder);