[PATCH] drmmode: update logic for dynamic connectors, paths, and tiles

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]<

 



This largely copies the code from modesetting with minor adjustments.

Signed-off-by: Ilia Mirkin <imirkin@xxxxxxxxxxxx>
---

I've tested this back and forth with a GTX 960 + 2x U2415 monitors,
chained to each other. I turned them on/off. I messed with their DP
1.1/1.2 settings. I pulled plugs a few times.

This seems to pass the test. No crashes, the connectors seem to come
and go just fine. When plugging back in, X server doesn't redo a modeset
even though it claims the screen is on. Same as modesetting though.

Note that the TILE stuff is wholly untested. Just copied it from
modesetting's drmmode_display.c.

 src/drmmode_display.c | 390 +++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 335 insertions(+), 55 deletions(-)

diff --git a/src/drmmode_display.c b/src/drmmode_display.c
index 04a0b57..6b8e3f3 100644
--- a/src/drmmode_display.c
+++ b/src/drmmode_display.c
@@ -46,7 +46,6 @@ static Bool drmmode_xf86crtc_resize(ScrnInfoPtr scrn, int width, int height);
 typedef struct {
     int fd;
     uint32_t fb_id;
-    drmModeResPtr mode_res;
     int cpp;
     drmEventContext event_context;
 #ifdef HAVE_LIBUDEV
@@ -83,6 +82,7 @@ typedef struct {
     drmModeConnectorPtr mode_output;
     drmModeEncoderPtr mode_encoder;
     drmModePropertyBlobPtr edid_blob;
+    drmModePropertyBlobPtr tile_blob;
     int num_props;
     drmmode_prop_ptr props;
 } drmmode_output_private_rec, *drmmode_output_private_ptr;
@@ -477,6 +477,8 @@ drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
 			continue;
 
 		drmmode_output = output->driver_private;
+		if (drmmode_output->output_id == -1)
+			continue;
 		output_ids[output_count] =
 			drmmode_output->mode_output->connector_id;
 		output_count++;
@@ -770,7 +772,7 @@ static const xf86CrtcFuncsRec drmmode_crtc_funcs = {
 
 
 static unsigned int
-drmmode_crtc_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int num)
+drmmode_crtc_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, drmModeResPtr mode_res, int num)
 {
 	NVPtr pNv = NVPTR(pScrn);
 	NVEntPtr pNVEnt = NVEntPriv(pScrn);
@@ -784,7 +786,7 @@ drmmode_crtc_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int num)
 
 	drmmode_crtc = xnfcalloc(sizeof(drmmode_crtc_private_rec), 1);
 	drmmode_crtc->mode_crtc = drmModeGetCrtc(drmmode->fd,
-						 drmmode->mode_res->crtcs[num]);
+						 mode_res->crtcs[num]);
 	drmmode_crtc->drmmode = drmmode;
 	drmmode_crtc->hw_crtc_index = num;
 
@@ -809,13 +811,19 @@ drmmode_output_detect(xf86OutputPtr output)
 	drmmode_output_private_ptr drmmode_output = output->driver_private;
 	drmmode_ptr drmmode = drmmode_output->drmmode;
 	xf86OutputStatus status;
+
+	if (drmmode_output->output_id == -1)
+		return XF86OutputStatusDisconnected;
+
 	drmModeFreeConnector(drmmode_output->mode_output);
 
 	drmmode_output->mode_output =
 		drmModeGetConnector(drmmode->fd, drmmode_output->output_id);
 
-	if (!drmmode_output->mode_output)
+	if (!drmmode_output->mode_output) {
+		drmmode_output->output_id = -1;
 		return XF86OutputStatusDisconnected;
+	}
 
 	switch (drmmode_output->mode_output->connection) {
 	case DRM_MODE_CONNECTED:
@@ -842,6 +850,69 @@ drmmode_output_mode_valid(xf86OutputPtr output, DisplayModePtr mode)
 	return MODE_OK;
 }
 
+static int
+koutput_get_prop_idx(int fd, drmModeConnectorPtr koutput,
+		     int type, const char *name)
+{
+	int idx = -1;
+
+	for (int i = 0; i < koutput->count_props; i++) {
+		drmModePropertyPtr prop = drmModeGetProperty(fd, koutput->props[i]);
+
+		if (!prop)
+			continue;
+
+		if (drm_property_type_is(prop, type) && !strcmp(prop->name, name))
+			idx = i;
+
+		drmModeFreeProperty(prop);
+
+		if (idx > -1)
+			break;
+	}
+
+	return idx;
+}
+
+static drmModePropertyBlobPtr
+koutput_get_prop_blob(int fd, drmModeConnectorPtr koutput, const char *name)
+{
+	drmModePropertyBlobPtr blob = NULL;
+	int idx = koutput_get_prop_idx(fd, koutput, DRM_MODE_PROP_BLOB, name);
+
+	if (idx > -1)
+		blob = drmModeGetPropertyBlob(fd, koutput->prop_values[idx]);
+
+	return blob;
+}
+
+static void
+drmmode_output_attach_tile(xf86OutputPtr output)
+{
+	drmmode_output_private_ptr drmmode_output = output->driver_private;
+	drmModeConnectorPtr koutput = drmmode_output->mode_output;
+	drmmode_ptr drmmode = drmmode_output->drmmode;
+	struct xf86CrtcTileInfo tile_info, *set = NULL;
+
+	if (!koutput) {
+		xf86OutputSetTile(output, NULL);
+		return;
+	}
+
+	drmModeFreePropertyBlob(drmmode_output->tile_blob);
+
+	/* look for a TILE property */
+	drmmode_output->tile_blob =
+		koutput_get_prop_blob(drmmode->fd, koutput, "TILE");
+
+	if (drmmode_output->tile_blob) {
+		if (xf86OutputParseKMSTile(drmmode_output->tile_blob->data, drmmode_output->tile_blob->length, &tile_info) == TRUE)
+			set = &tile_info;
+	}
+	xf86OutputSetTile(output, set);
+}
+
+
 static DisplayModePtr
 drmmode_output_get_modes(xf86OutputPtr output)
 {
@@ -850,27 +921,14 @@ drmmode_output_get_modes(xf86OutputPtr output)
 	drmmode_ptr drmmode = drmmode_output->drmmode;
 	int i;
 	DisplayModePtr Modes = NULL, Mode;
-	drmModePropertyPtr props;
 	xf86MonPtr ddc_mon = NULL;
 
 	if (!koutput)
 		return NULL;
 
 	/* look for an EDID property */
-	for (i = 0; i < koutput->count_props; i++) {
-		props = drmModeGetProperty(drmmode->fd, koutput->props[i]);
-		if (!props || !(props->flags & DRM_MODE_PROP_BLOB))
-			continue;
-
-		if (!strcmp(props->name, "EDID")) {
-			if (drmmode_output->edid_blob)
-				drmModeFreePropertyBlob(drmmode_output->edid_blob);
-			drmmode_output->edid_blob =
-				drmModeGetPropertyBlob(drmmode->fd,
-						       koutput->prop_values[i]);
-		}
-		drmModeFreeProperty(props);
-	}
+	drmmode_output->edid_blob =
+		koutput_get_prop_blob(drmmode->fd, koutput, "EDID");
 
 	if (drmmode_output->edid_blob) {
 		ddc_mon = xf86InterpretEDID(output->scrn->scrnIndex,
@@ -880,6 +938,8 @@ drmmode_output_get_modes(xf86OutputPtr output)
 	}
 	xf86OutputSetEDID(output, ddc_mon);
 
+	drmmode_output_attach_tile(output);
+
 	/* modes should already be available */
 	for (i = 0; i < koutput->count_modes; i++) {
 		Mode = xnfalloc(sizeof(DisplayModeRec));
@@ -900,6 +960,8 @@ drmmode_output_destroy(xf86OutputPtr output)
 
 	if (drmmode_output->edid_blob)
 		drmModeFreePropertyBlob(drmmode_output->edid_blob);
+	if (drmmode_output->tile_blob)
+		drmModeFreePropertyBlob(drmmode_output->tile_blob);
 	for (i = 0; i < drmmode_output->num_props; i++) {
 		drmModeFreeProperty(drmmode_output->props[i].mode_prop);
 		free(drmmode_output->props[i].atoms);
@@ -918,6 +980,9 @@ drmmode_output_dpms(xf86OutputPtr output, int mode)
 	drmmode_ptr drmmode = drmmode_output->drmmode;
 	int mode_id = -1, i;
 
+	if (!koutput)
+		return;
+
 	for (i = 0; i < koutput->count_props; i++) {
 		props = drmModeGetProperty(drmmode->fd, koutput->props[i]);
 		if (props && (props->flags & DRM_MODE_PROP_ENUM)) {
@@ -1227,42 +1292,130 @@ drmmode_zaphod_match(ScrnInfoPtr pScrn, const char *s, char *output_name)
     return FALSE;
 }
 
+static xf86OutputPtr find_output(ScrnInfoPtr pScrn, int id)
+{
+	xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
+	int i;
+	for (i = 0; i < xf86_config->num_output; i++) {
+		xf86OutputPtr output = xf86_config->output[i];
+		drmmode_output_private_ptr drmmode_output;
+
+		drmmode_output = output->driver_private;
+		if (drmmode_output->output_id == id)
+			return output;
+	}
+	return NULL;
+}
+
+static int parse_path_blob(drmModePropertyBlobPtr path_blob, int *conn_base_id, char **path)
+{
+	char *conn;
+	char conn_id[5];
+	int id, len;
+	char *blob_data;
+
+	if (!path_blob)
+		return -1;
+
+	blob_data = path_blob->data;
+	/* we only handle MST paths for now */
+	if (strncmp(blob_data, "mst:", 4))
+		return -1;
+
+	conn = strchr(blob_data + 4, '-');
+	if (!conn)
+		return -1;
+	len = conn - (blob_data + 4);
+	if (len + 1 > 5)
+		return -1;
+	memcpy(conn_id, blob_data + 4, len);
+	conn_id[len + 1] = '\0';
+	id = strtoul(conn_id, NULL, 10);
+
+	*conn_base_id = id;
+
+	*path = conn + 1;
+	return 0;
+}
+
+static void
+drmmode_create_name(ScrnInfoPtr pScrn, drmModeConnectorPtr koutput, char *name,
+                    drmModePropertyBlobPtr path_blob)
+{
+	int ret;
+	char *extra_path;
+	int conn_id;
+	xf86OutputPtr output;
+
+	ret = parse_path_blob(path_blob, &conn_id, &extra_path);
+	if (ret == -1)
+		goto fallback;
+
+	output = find_output(pScrn, conn_id);
+	if (!output)
+		goto fallback;
+
+	snprintf(name, 32, "%s-%s", output->name, extra_path);
+	return;
+
+fallback:
+	if (koutput->connector_type >= ARRAY_SIZE(output_names))
+		snprintf(name, 32, "Unknown%d-%d", koutput->connector_type, koutput->connector_type_id);
+	else if (pScrn->is_gpu)
+		snprintf(name, 32, "%s-%d-%d", output_names[koutput->connector_type], pScrn->scrnIndex - GPU_SCREEN_OFFSET + 1, koutput->connector_type_id);
+	else
+		snprintf(name, 32, "%s-%d", output_names[koutput->connector_type], koutput->connector_type_id);
+}
+
 static unsigned int
-drmmode_output_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int num, int crtcshift)
+drmmode_output_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, drmModeResPtr mode_res, int num, Bool dynamic, int crtcshift)
 {
 	NVPtr pNv = NVPTR(pScrn);
 	xf86OutputPtr output;
+	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
 	drmModeConnectorPtr koutput;
 	drmModeEncoderPtr kencoder;
 	drmmode_output_private_ptr drmmode_output;
 	const char *s;
 	char name[32];
+	drmModePropertyBlobPtr path_blob = NULL;
+	int i;
 
 	koutput = drmModeGetConnector(drmmode->fd,
-				      drmmode->mode_res->connectors[num]);
+				      mode_res->connectors[num]);
 	if (!koutput)
 		return 0;
 
+	path_blob = koutput_get_prop_blob(drmmode->fd, koutput, "PATH");
+
+	drmmode_create_name(pScrn, koutput, name, path_blob);
+
+	if (path_blob)
+		drmModeFreePropertyBlob(path_blob);
+
+	if (path_blob && dynamic) {
+		/* see if we have an output with this name already
+		   and hook stuff up */
+		for (i = 0; i < xf86_config->num_output; i++) {
+			output = xf86_config->output[i];
+
+			if (strncmp(output->name, name, 32))
+				continue;
+
+			drmmode_output = output->driver_private;
+			drmmode_output->output_id = mode_res->connectors[num];
+			drmmode_output->mode_output = koutput;
+			return 1;
+		}
+	}
+
+
 	kencoder = drmModeGetEncoder(drmmode->fd, koutput->encoders[0]);
 	if (!kencoder) {
 		drmModeFreeConnector(koutput);
 		return 0;
 	}
 
-	if (koutput->connector_type >= NUM_OUTPUT_NAMES)
-		snprintf(name, 32, "Unknown%d-%d", koutput->connector_type,
-			 koutput->connector_type_id);
-#ifdef NOUVEAU_PIXMAP_SHARING
-	else if (pScrn->is_gpu)
-		snprintf(name, 32, "%s-%d-%d",
-			 output_names[koutput->connector_type], pScrn->scrnIndex - GPU_SCREEN_OFFSET + 1,
-			 koutput->connector_type_id);
-#endif
-	else
-		snprintf(name, 32, "%s-%d",
-			 output_names[koutput->connector_type],
-			 koutput->connector_type_id);
-
 	if (xf86IsEntityShared(pScrn->entityList[0])) {
 		s = xf86GetOptValString(pNv->Options, OPTION_ZAPHOD_HEADS);
 		if (s) {
@@ -1300,7 +1453,7 @@ drmmode_output_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int num, int crtcshi
 		return 0;
 	}
 
-	drmmode_output->output_id = drmmode->mode_res->connectors[num];
+	drmmode_output->output_id = mode_res->connectors[num];
 	drmmode_output->mode_output = koutput;
 	drmmode_output->mode_encoder = kencoder;
 	drmmode_output->drmmode = drmmode;
@@ -1316,6 +1469,9 @@ drmmode_output_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int num, int crtcshi
 	output->interlaceAllowed = true;
 	output->doubleScanAllowed = true;
 
+	if (dynamic)
+		output->randr_output = RROutputCreate(xf86ScrnToScreen(pScrn), output->name, strlen(output->name), output);
+
 	return 1;
 }
 
@@ -1429,6 +1585,7 @@ static const xf86CrtcConfigFuncsRec drmmode_xf86crtc_config_funcs = {
 Bool drmmode_pre_init(ScrnInfoPtr pScrn, int fd, int cpp)
 {
 	drmmode_ptr drmmode;
+	drmModeResPtr mode_res;
 	NVEntPtr pNVEnt = NVEntPriv(pScrn);
 	int i;
 	unsigned int crtcs_needed = 0;
@@ -1441,32 +1598,31 @@ Bool drmmode_pre_init(ScrnInfoPtr pScrn, int fd, int cpp)
 	xf86CrtcConfigInit(pScrn, &drmmode_xf86crtc_config_funcs);
 
 	drmmode->cpp = cpp;
-	drmmode->mode_res = drmModeGetResources(drmmode->fd);
-	if (!drmmode->mode_res)
+	mode_res = drmModeGetResources(drmmode->fd);
+	if (!mode_res)
 		return FALSE;
 
-	xf86CrtcSetSizeRange(pScrn, 320, 200, drmmode->mode_res->max_width,
-			     drmmode->mode_res->max_height);
+	xf86CrtcSetSizeRange(pScrn, 320, 200, mode_res->max_width,
+			     mode_res->max_height);
 
-	if (!drmmode->mode_res->count_connectors ||
-	    !drmmode->mode_res->count_crtcs) {
-		drmModeFreeResources(drmmode->mode_res);
+	if (!mode_res->count_connectors ||
+	    !mode_res->count_crtcs) {
 		free(drmmode);
 		goto done;
 	}
 
 	xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Initializing outputs ...\n");
 	crtcshift = ffs(pNVEnt->assigned_crtcs ^ 0xffffffff) - 1;
-	for (i = 0; i < drmmode->mode_res->count_connectors; i++)
-		crtcs_needed += drmmode_output_init(pScrn, drmmode, i, crtcshift);
+	for (i = 0; i < mode_res->count_connectors; i++)
+		crtcs_needed += drmmode_output_init(pScrn, drmmode, mode_res, i, FALSE, crtcshift);
 
 	xf86DrvMsg(pScrn->scrnIndex, X_INFO,
 		   "%d crtcs needed for screen.\n", crtcs_needed);
 
-	for (i = 0; i < drmmode->mode_res->count_crtcs; i++) {
+	for (i = 0; i < mode_res->count_crtcs; i++) {
 		if (!xf86IsEntityShared(pScrn->entityList[0]) ||
 		    (crtcs_needed && !(pNVEnt->assigned_crtcs & (1 << i))))
-			crtcs_needed -= drmmode_crtc_init(pScrn, drmmode, i);
+			crtcs_needed -= drmmode_crtc_init(pScrn, drmmode, mode_res, i);
 	}
 
 	/* All ZaphodHeads outputs provided with matching crtcs? */
@@ -1476,6 +1632,8 @@ Bool drmmode_pre_init(ScrnInfoPtr pScrn, int fd, int cpp)
 			   crtcs_needed);
 
 done:
+	drmModeFreeResources(mode_res);
+
 #ifdef NOUVEAU_PIXMAP_SHARING
 	xf86ProviderSetup(pScrn, NULL, "nouveau");
 #endif
@@ -1533,19 +1691,141 @@ drmmode_cursor_init(ScreenPtr pScreen)
 }
 
 #ifdef HAVE_LIBUDEV
+
+#define DRM_MODE_LINK_STATUS_GOOD       0
+#define DRM_MODE_LINK_STATUS_BAD        1
+
 static void
 drmmode_handle_uevents(ScrnInfoPtr scrn)
 {
-	drmmode_ptr drmmode = drmmode_from_scrn(scrn);
-	struct udev_device *dev;
+    struct udev_device *dev;
+    drmmode_ptr drmmode = drmmode_from_scrn(scrn);
+    drmModeResPtr mode_res;
+    xf86CrtcConfigPtr  config = XF86_CRTC_CONFIG_PTR(scrn);
+    int i, j;
+    Bool found = FALSE;
+    Bool changed = FALSE;
+
+    while ((dev = udev_monitor_receive_device(drmmode->uevent_monitor))) {
+        udev_device_unref(dev);
+        found = TRUE;
+    }
+    if (!found)
+        return;
+
+    /* Try to re-set the mode on all the connectors with a BAD link-state:
+     * This may happen if a link degrades and a new modeset is necessary, using
+     * different link-training parameters. If the kernel found that the current
+     * mode is not achievable anymore, it should have pruned the mode before
+     * sending the hotplug event. Try to re-set the currently-set mode to keep
+     * the display alive, this will fail if the mode has been pruned.
+     * In any case, we will send randr events for the Desktop Environment to
+     * deal with it, if it wants to.
+     */
+    for (i = 0; i < config->num_output; i++) {
+        xf86OutputPtr output = config->output[i];
+        xf86CrtcPtr crtc = output->crtc;
+        drmmode_output_private_ptr drmmode_output = output->driver_private;
+        uint32_t con_id, idx;
+        drmModeConnectorPtr koutput;
+
+        if (crtc == NULL || drmmode_output->mode_output == NULL)
+            continue;
+
+        con_id = drmmode_output->mode_output->connector_id;
+        /* Get an updated view of the properties for the current connector and
+         * look for the link-status property
+         */
+        koutput = drmModeGetConnectorCurrent(drmmode->fd, con_id);
+        if (!koutput)
+            continue;
+
+        idx = koutput_get_prop_idx(drmmode->fd, koutput,
+                DRM_MODE_PROP_ENUM, "link-status");
+
+        if ((idx > -1) &&
+                (koutput->prop_values[idx] == DRM_MODE_LINK_STATUS_BAD)) {
+            /* the connector got a link failure, re-set the current mode */
+            drmmode_set_mode_major(crtc, &crtc->mode, crtc->rotation,
+                                   crtc->x, crtc->y);
+
+            xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+                       "hotplug event: connector %u's link-state is BAD, "
+                       "tried resetting the current mode. You may be left"
+                       "with a black screen if this fails...\n", con_id);
+        }
+
+        drmModeFreeConnector(koutput);
+    }
+
+    mode_res = drmModeGetResources(drmmode->fd);
+    if (!mode_res)
+        goto out;
+
+    if (mode_res->count_crtcs != config->num_crtc) {
+        ErrorF("number of CRTCs changed - failed to handle, %d vs %d\n", mode_res->count_crtcs, config->num_crtc);
+        goto out_free_res;
+    }
+
+    /* figure out if we have gotten rid of any connectors
+       traverse old output list looking for outputs */
+    for (i = 0; i < config->num_output; i++) {
+        xf86OutputPtr output = config->output[i];
+        drmmode_output_private_ptr drmmode_output;
+
+        drmmode_output = output->driver_private;
+        found = FALSE;
+        for (j = 0; j < mode_res->count_connectors; j++) {
+            if (mode_res->connectors[j] == drmmode_output->output_id) {
+                found = TRUE;
+                break;
+            }
+        }
+        if (found)
+            continue;
+
+        drmModeFreeConnector(drmmode_output->mode_output);
+        drmmode_output->mode_output = NULL;
+        drmmode_output->output_id = -1;
+
+        changed = TRUE;
+    }
+
+    /* find new output ids we don't have outputs for */
+    for (i = 0; i < mode_res->count_connectors; i++) {
+        found = FALSE;
+
+        for (j = 0; j < config->num_output; j++) {
+            xf86OutputPtr output = config->output[j];
+            drmmode_output_private_ptr drmmode_output;
+
+            drmmode_output = output->driver_private;
+            if (mode_res->connectors[i] == drmmode_output->output_id) {
+                found = TRUE;
+                break;
+            }
+        }
+        if (found)
+            continue;
+
+        changed = TRUE;
+        drmmode_output_init(scrn, drmmode, mode_res, i, TRUE, 0);
+    }
+
+    if (changed) {
+        RRSetChanged(xf86ScrnToScreen(scrn));
+        RRTellChanged(xf86ScrnToScreen(scrn));
+    }
+
+out_free_res:
+    drmModeFreeResources(mode_res);
+out:
+    RRGetInfo(xf86ScrnToScreen(scrn), TRUE);
+}
 
-	dev = udev_monitor_receive_device(drmmode->uevent_monitor);
-	if (!dev)
-		return;
+#undef DRM_MODE_LINK_STATUS_BAD
+#undef DRM_MODE_LINK_STATUS_GOOD
 
-	RRGetInfo(xf86ScrnToScreen(scrn), TRUE);
-	udev_device_unref(dev);
-}
 #endif
 
 #if HAVE_NOTIFY_FD
-- 
2.16.4

_______________________________________________
Nouveau mailing list
Nouveau@xxxxxxxxxxxxxxxxxxxxx
https://lists.freedesktop.org/mailman/listinfo/nouveau




[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [eCos]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux