This largely copies the code from modesetting with minor adjustments.

Signed-off-by: Ilia Mirkin <imir...@alum.mit.edu>
---

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@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/nouveau

Reply via email to