[systemd-commits] 12 commits - src/libsystemd-terminal

David Herrmann dvdhrm at kemper.freedesktop.org
Sat Sep 20 02:47:23 PDT 2014


 src/libsystemd-terminal/grdev-drm.c      |  317 ++++++++++++++++++++-----------
 src/libsystemd-terminal/grdev-internal.h |    4 
 src/libsystemd-terminal/grdev.c          |  102 +++++++--
 src/libsystemd-terminal/modeset.c        |   15 -
 src/libsystemd-terminal/sysview.c        |   20 +
 src/libsystemd-terminal/sysview.h        |    7 
 6 files changed, 326 insertions(+), 139 deletions(-)

New commits:
commit 0fbd4d113e0d2123e896e8005d1b7fe407c28c05
Author: David Herrmann <dh.herrmann at gmail.com>
Date:   Sat Sep 20 11:43:32 2014 +0200

    terminal: fix mode sync for connectors
    
    The GETXY ioctls of DRM are usually called twice by libdrm: Once to
    retrieve the number of objects, a second time with suitably sized buffers
    to actually retrieve all objects. In grdrm, we avoid these excessive calls
    and instead just call ioctls with cached buffers and resize them if they
    were too small.
    
    However, connectors need to read the mode list via EDID, which is horribly
    slow. As the kernel still cannot do that asynchronously (seriously, we
    need to fix this!), it has a hack to only do it if count_modes==0. This is
    fine with libdrm, as it calls every ioctl twice, anyway. However, we fail
    horribly with this as we usually never pass 0.
    
    Fix this by calling into GETCONNECTOR ioctls twice in case we received an
    hotplug event. Only in those cases, we need to re-read modes, so this
    should be totally fine.

diff --git a/src/libsystemd-terminal/grdev-drm.c b/src/libsystemd-terminal/grdev-drm.c
index 5cebb06..2e55ad3 100644
--- a/src/libsystemd-terminal/grdev-drm.c
+++ b/src/libsystemd-terminal/grdev-drm.c
@@ -264,6 +264,7 @@ struct grdrm_card {
         Hashmap *object_map;
 
         bool async_hotplug : 1;
+        bool hotplug : 1;
         bool running : 1;
         bool ready : 1;
         bool cap_dumb : 1;
@@ -603,12 +604,19 @@ static int grdrm_connector_resync(grdrm_connector *connector) {
                 res.count_encoders = connector->kern.max_encoders;
                 res.count_props = connector->kern.max_props;
 
-                /* Retrieve modes only if we have none. This avoids expensive
-                 * EDID reads in the kernel, that can slow down resyncs
-                 * considerably! */
-                if (connector->kern.n_modes == 0) {
-                        res.modes_ptr = PTR_TO_UINT64(connector->kern.modes);
-                        res.count_modes = connector->kern.max_modes;
+                /* The kernel reads modes from the EDID information only if we
+                 * pass count_modes==0. This is a legacy hack for libdrm (which
+                 * called every ioctl twice). Now we have to adopt.. *sigh*.
+                 * If we never received an hotplug event, there's no reason to
+                 * sync modes. EDID reads are heavy, so skip that if not
+                 * required. */
+                if (card->hotplug) {
+                        if (tries > 0) {
+                                res.modes_ptr = PTR_TO_UINT64(connector->kern.modes);
+                                res.count_modes = connector->kern.max_modes;
+                        } else {
+                                resized = true;
+                        }
                 }
 
                 r = ioctl(card->fd, DRM_IOCTL_MODE_GETCONNECTOR, &res);
@@ -689,7 +697,6 @@ static int grdrm_connector_resync(grdrm_connector *connector) {
                         continue;
 
                 connector->kern.n_encoders = res.count_encoders;
-                connector->kern.n_modes = res.count_modes;
                 connector->kern.n_props = res.count_props;
                 connector->kern.type = res.connector_type;
                 connector->kern.type_id = res.connector_type_id;
@@ -698,6 +705,8 @@ static int grdrm_connector_resync(grdrm_connector *connector) {
                 connector->kern.mm_width = res.mm_width;
                 connector->kern.mm_height = res.mm_height;
                 connector->kern.subpixel = res.subpixel;
+                if (res.modes_ptr == PTR_TO_UINT64(connector->kern.modes))
+                        connector->kern.n_modes = res.count_modes;
 
                 break;
         }
@@ -2167,6 +2176,7 @@ static void grdrm_card_hotplug(grdrm_card *card) {
 
         grdrm_card_configure(card);
         card->ready = true;
+        card->hotplug = false;
 
         grdev_session_unpin(card->base.session);
 }
@@ -2374,6 +2384,7 @@ static int grdrm_card_open(grdrm_card *card, int dev_fd) {
 
         sd_event_source_set_enabled(card->fd_src, SD_EVENT_OFF);
 
+        card->hotplug = true;
         card->fd = fd;
         fd = -1;
 
@@ -3029,13 +3040,17 @@ void grdev_drm_card_hotplug(grdev_card *basecard, struct udev_device *ud) {
                 /* If we get add/remove events on DRM nodes without devnum, we
                  * got hotplugged DRM objects so refresh the device. */
                 devnum = udev_device_get_devnum(ud);
-                if (devnum == 0)
+                if (devnum == 0) {
+                        card->hotplug = true;
                         grdrm_card_hotplug(card);
+                }
         } else if (streq_ptr(action, "change")) {
                 /* A change event with HOTPLUG=1 is sent whenever a connector
                  * changed state. Refresh the device to update our state. */
                 p = udev_device_get_property_value(ud, "HOTPLUG");
-                if (streq_ptr(p, "1"))
+                if (streq_ptr(p, "1")) {
+                        card->hotplug = true;
                         grdrm_card_hotplug(card);
+                }
         }
 }

commit f919ad9d3dc3e25f6eed268fe7eb5e922bcdb3b6
Author: David Herrmann <dh.herrmann at gmail.com>
Date:   Sat Sep 20 11:41:34 2014 +0200

    terminal: restructure some logging calls in grdrm
    
    Multiple issues here:
     1) Don't print excessive card dumps on each resync. Disable it and make
        developers add it themselves.
     2) Ignore EINVAL on page-flips. Some cards don't support page-flips, so
        we'd print it on each frame. Maybe, at some point, the kernel will add
        support to retrieve capabilities for that. Until then, simply ignore
        it.
     3) Replace the now dropped card-dump with a short message about resyncing
        the card.

diff --git a/src/libsystemd-terminal/grdev-drm.c b/src/libsystemd-terminal/grdev-drm.c
index 3936a02..5cebb06 100644
--- a/src/libsystemd-terminal/grdev-drm.c
+++ b/src/libsystemd-terminal/grdev-drm.c
@@ -1177,8 +1177,12 @@ static int grdrm_crtc_commit_flip(grdrm_crtc *crtc, grdev_fb **slot) {
         r = ioctl(card->fd, DRM_IOCTL_MODE_PAGE_FLIP, &page_flip);
         if (r < 0) {
                 r = -errno;
-                log_debug("grdrm: %s: cannot schedule page-flip on crtc %" PRIu32 ": %m",
-                          card->base.name, crtc->object.id);
+                /* Avoid excessive logging on EINVAL; it is currently not
+                 * possible to see whether cards support page-flipping, so
+                 * avoid logging on each frame. */
+                if (r != -EINVAL)
+                        log_debug("grdrm: %s: cannot schedule page-flip on crtc %" PRIu32 ": %m",
+                                  card->base.name, crtc->object.id);
 
                 if (grdrm_card_async(card, r))
                         return r;
@@ -2145,6 +2149,8 @@ static void grdrm_card_hotplug(grdrm_card *card) {
         if (!card->running)
                 return;
 
+        log_debug("grdrm: %s/%s: reconfigure card", card->base.session->name, card->base.name);
+
         card->ready = false;
         r = grdrm_card_resync(card);
         if (r < 0) {
@@ -2155,7 +2161,10 @@ static void grdrm_card_hotplug(grdrm_card *card) {
 
         grdev_session_pin(card->base.session);
 
-        grdrm_card_print(card);
+        /* debug statement to print card information */
+        if (0)
+                grdrm_card_print(card);
+
         grdrm_card_configure(card);
         card->ready = true;
 

commit 7b12a45b2dc6993e3f31642df2cc9b528294da40
Author: David Herrmann <dh.herrmann at gmail.com>
Date:   Sat Sep 20 11:12:44 2014 +0200

    terminal: grdev: schedule virtual frame events if hw doesn't support it
    
    Whenever we cannot use hardware frame events, we now schedule a virtual
    frame event to make sure applications don't have to do this. Usually,
    applications render only on data changes, but we can further reduce
    render-time by also limiting rendering to vsyncs.

diff --git a/src/libsystemd-terminal/grdev-drm.c b/src/libsystemd-terminal/grdev-drm.c
index 00aac29..3936a02 100644
--- a/src/libsystemd-terminal/grdev-drm.c
+++ b/src/libsystemd-terminal/grdev-drm.c
@@ -346,6 +346,8 @@ static bool grdrm_modes_compatible(const struct drm_mode_modeinfo *a, const stru
                 return false;
         if (a->vdisplay != b->vdisplay)
                 return false;
+        if (a->vrefresh != b->vrefresh)
+                return false;
 
         return true;
 }
@@ -1038,7 +1040,8 @@ static void grdrm_crtc_expose(grdrm_crtc *crtc) {
         pipe = crtc->pipe;
         if (pipe) {
                 if (pipe->base.width != crtc->set.mode.hdisplay ||
-                    pipe->base.height != crtc->set.mode.vdisplay) {
+                    pipe->base.height != crtc->set.mode.vdisplay ||
+                    pipe->base.vrefresh != crtc->set.mode.vrefresh) {
                         grdev_pipe_free(&pipe->base);
                         crtc->pipe = NULL;
                         pipe = NULL;
@@ -1127,6 +1130,12 @@ static void grdrm_crtc_commit_deep(grdrm_crtc *crtc, grdev_fb **slot) {
         pipe->base.flipping = false;
         pipe->base.flip = false;
 
+        /* We cannot schedule dummy page-flips on pipes, hence, the
+         * application would have to schedule their own frame-timers.
+         * To avoid duplicating that everywhere, we schedule our own
+         * timer and raise a fake FRAME event when it fires. */
+        grdev_pipe_schedule(&pipe->base, 1);
+
         if (!pipe->base.back) {
                 for (i = 0; i < pipe->base.max_fbs; ++i) {
                         if (!pipe->base.fbs[i])
@@ -1189,6 +1198,11 @@ static int grdrm_crtc_commit_flip(grdrm_crtc *crtc, grdev_fb **slot) {
         fb->flipid = cnt;
         *slot = NULL;
 
+        /* Raise fake FRAME event if it takes longer than 2
+         * frames to receive the pageflip event. We assume the
+         * queue ran over or some other error happened. */
+        grdev_pipe_schedule(&pipe->base, 2);
+
         if (!pipe->base.back) {
                 for (i = 0; i < pipe->base.max_fbs; ++i) {
                         if (!pipe->base.fbs[i])
@@ -1501,6 +1515,7 @@ static int grdrm_pipe_new(grdrm_pipe **out, grdrm_crtc *crtc, struct drm_mode_mo
         pipe->crtc = crtc;
         pipe->base.width = mode->hdisplay;
         pipe->base.height = mode->vdisplay;
+        pipe->base.vrefresh = mode->vrefresh ? : 25;
 
         grdrm_pipe_name(name, crtc);
         r = grdev_pipe_add(&pipe->base, name, n_fbs);
diff --git a/src/libsystemd-terminal/grdev-internal.h b/src/libsystemd-terminal/grdev-internal.h
index 96830a7..f5915b1 100644
--- a/src/libsystemd-terminal/grdev-internal.h
+++ b/src/libsystemd-terminal/grdev-internal.h
@@ -142,9 +142,11 @@ struct grdev_pipe {
 
         grdev_tile *tile;
         grdev_display_cache *cache;
+        sd_event_source *vsync_src;
 
         uint32_t width;
         uint32_t height;
+        uint32_t vrefresh;
 
         size_t max_fbs;
         grdev_fb *front;
@@ -171,6 +173,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(grdev_pipe*, grdev_pipe_free);
 
 void grdev_pipe_ready(grdev_pipe *pipe, bool running);
 void grdev_pipe_frame(grdev_pipe *pipe);
+void grdev_pipe_schedule(grdev_pipe *pipe, uint64_t frames);
 
 /*
  * Cards
diff --git a/src/libsystemd-terminal/grdev.c b/src/libsystemd-terminal/grdev.c
index 397da1b..43d0c7c 100644
--- a/src/libsystemd-terminal/grdev.c
+++ b/src/libsystemd-terminal/grdev.c
@@ -574,6 +574,13 @@ grdev_pipe *grdev_find_pipe(grdev_card *card, const char *name) {
         return hashmap_get(card->pipe_map, name);
 }
 
+static int pipe_vsync_fn(sd_event_source *src, uint64_t usec, void *userdata) {
+        grdev_pipe *pipe = userdata;
+
+        grdev_pipe_frame(pipe);
+        return 0;
+}
+
 int grdev_pipe_add(grdev_pipe *pipe, const char *name, size_t n_fbs) {
         int r;
 
@@ -585,6 +592,7 @@ int grdev_pipe_add(grdev_pipe *pipe, const char *name, size_t n_fbs) {
         assert_return(!pipe->cache, -EINVAL);
         assert_return(pipe->width > 0, -EINVAL);
         assert_return(pipe->height > 0, -EINVAL);
+        assert_return(pipe->vrefresh > 0, -EINVAL);
         assert_return(!pipe->enabled, -EINVAL);
         assert_return(!pipe->running, -EINVAL);
         assert_return(name, -EINVAL);
@@ -605,6 +613,20 @@ int grdev_pipe_add(grdev_pipe *pipe, const char *name, size_t n_fbs) {
         if (r < 0)
                 return r;
 
+        r = sd_event_add_time(pipe->card->session->context->event,
+                              &pipe->vsync_src,
+                              CLOCK_MONOTONIC,
+                              0,
+                              10 * USEC_PER_MSEC,
+                              pipe_vsync_fn,
+                              pipe);
+        if (r < 0)
+                return r;
+
+        r = sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_OFF);
+        if (r < 0)
+                return r;
+
         r = hashmap_put(pipe->card->pipe_map, pipe->name, pipe);
         if (r < 0)
                 return r;
@@ -633,6 +655,7 @@ grdev_pipe *grdev_pipe_free(grdev_pipe *pipe) {
         tmp = *pipe;
         pipe->vtable->free(pipe);
 
+        sd_event_source_unref(tmp.vsync_src);
         grdev_tile_free(tmp.tile);
         card_modified(tmp.card);
         free(tmp.fbs);
@@ -676,17 +699,15 @@ void grdev_pipe_ready(grdev_pipe *pipe, bool running) {
         pipe->running = running;
 
         /* runtime events for unused pipes are not interesting */
-        if (pipe->cache) {
+        if (pipe->cache && pipe->enabled) {
                 grdev_display *display = pipe->tile->display;
 
                 assert(display);
 
-                if (running) {
-                        if (pipe->enabled)
-                                session_frame(display->session, display);
-                } else {
+                if (running)
+                        session_frame(display->session, display);
+                else
                         pipe->cache->incomplete = true;
-                }
         }
 }
 
@@ -696,14 +717,44 @@ void grdev_pipe_frame(grdev_pipe *pipe) {
         assert(pipe);
 
         /* if pipe is unused, ignore any frame events */
-        if (!pipe->cache)
+        if (!pipe->cache || !pipe->enabled)
                 return;
 
         display = pipe->tile->display;
         assert(display);
 
-        if (pipe->enabled)
-                session_frame(display->session, display);
+        grdev_pipe_schedule(pipe, 0);
+        session_frame(display->session, display);
+}
+
+void grdev_pipe_schedule(grdev_pipe *pipe, uint64_t frames) {
+        int r;
+        uint64_t ts;
+
+        if (!frames) {
+                sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_OFF);
+                return;
+        }
+
+        r = sd_event_now(pipe->card->session->context->event, CLOCK_MONOTONIC, &ts);
+        if (r < 0)
+                goto error;
+
+        ts += frames * USEC_PER_MSEC * 1000ULL / pipe->vrefresh;
+
+        r = sd_event_source_set_time(pipe->vsync_src, ts);
+        if (r < 0)
+                goto error;
+
+        r = sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_ONESHOT);
+        if (r < 0)
+                goto error;
+
+        return;
+
+error:
+        log_debug("grdev: %s/%s/%s: cannot schedule vsync timer: %s",
+                  pipe->card->session->name, pipe->card->name, pipe->name, strerror(-r));
 }
 
 /*

commit 3ec19e5d91d3d705682fee62a509801737c56c1e
Author: David Herrmann <dh.herrmann at gmail.com>
Date:   Sat Sep 20 11:11:07 2014 +0200

    terminal: grdev: raise frame event after DISPLAY_ADD/CHANGE
    
    Whenever a display is added or changed, we suppressed any frame events.
    Make sure to raise them manually so we can avoid rendering when handling
    anything but FRAME events.

diff --git a/src/libsystemd-terminal/grdev.c b/src/libsystemd-terminal/grdev.c
index 3e3833f..397da1b 100644
--- a/src/libsystemd-terminal/grdev.c
+++ b/src/libsystemd-terminal/grdev.c
@@ -921,14 +921,17 @@ static void session_change_display(grdev_session *session, grdev_display *displa
 
         changed = display_cache(display);
 
-        if (display->n_leafs == 0)
+        if (display->n_leafs == 0) {
                 session_remove_display(session, display);
-        else if (!display->public)
+        } else if (!display->public) {
                 session_add_display(session, display);
-        else if (changed)
+                session_frame(session, display);
+        } else if (changed) {
                 session_raise_display_change(session, display);
-        else if (display->framed)
                 session_frame(session, display);
+        } else if (display->framed) {
+                session_frame(session, display);
+        }
 }
 
 static void session_frame(grdev_session *session, grdev_display *display) {
diff --git a/src/libsystemd-terminal/modeset.c b/src/libsystemd-terminal/modeset.c
index 57bf299..33c79a2 100644
--- a/src/libsystemd-terminal/modeset.c
+++ b/src/libsystemd-terminal/modeset.c
@@ -270,12 +270,10 @@ static void modeset_grdev_fn(grdev_session *session, void *userdata, grdev_event
         switch (ev->type) {
         case GRDEV_EVENT_DISPLAY_ADD:
                 grdev_display_enable(ev->display_add.display);
-                modeset_render(m, ev->display_add.display);
                 break;
         case GRDEV_EVENT_DISPLAY_REMOVE:
                 break;
         case GRDEV_EVENT_DISPLAY_CHANGE:
-                modeset_render(m, ev->display_change.display);
                 break;
         case GRDEV_EVENT_DISPLAY_FRAME:
                 modeset_render(m, ev->display_frame.display);

commit 95dbf6b19e8f25e28224b954ef99d96225b4e6e7
Author: David Herrmann <dh.herrmann at gmail.com>
Date:   Sat Sep 20 11:10:04 2014 +0200

    terminal: split grdrm_crtc_commit() apart
    
    This helper is quite huge, split it apart to make it easier to follow.

diff --git a/src/libsystemd-terminal/grdev-drm.c b/src/libsystemd-terminal/grdev-drm.c
index f0f0448..00aac29 100644
--- a/src/libsystemd-terminal/grdev-drm.c
+++ b/src/libsystemd-terminal/grdev-drm.c
@@ -1083,15 +1083,136 @@ static void grdrm_crtc_expose(grdrm_crtc *crtc) {
         grdev_pipe_ready(&crtc->pipe->base, true);
 }
 
-static void grdrm_crtc_commit(grdrm_crtc *crtc) {
+static void grdrm_crtc_commit_deep(grdrm_crtc *crtc, grdev_fb **slot) {
+        struct drm_mode_crtc set_crtc = { .crtc_id = crtc->object.id };
+        grdrm_card *card = crtc->object.card;
+        grdrm_pipe *pipe = crtc->pipe;
+        grdrm_fb *fb = fb_from_base(*slot);
+        size_t i;
+        int r;
+
+        assert(crtc);
+        assert(slot);
+        assert(*slot);
+        assert(pipe);
+
+        set_crtc.set_connectors_ptr = PTR_TO_UINT64(crtc->set.connectors);
+        set_crtc.count_connectors = crtc->set.n_connectors;
+        set_crtc.fb_id = fb->id;
+        set_crtc.x = 0;
+        set_crtc.y = 0;
+        set_crtc.mode_valid = 1;
+        set_crtc.mode = crtc->set.mode;
+
+        r = ioctl(card->fd, DRM_IOCTL_MODE_SETCRTC, &set_crtc);
+        if (r < 0) {
+                r = -errno;
+                log_debug("grdrm: %s: cannot set crtc %" PRIu32 ": %m",
+                          card->base.name, crtc->object.id);
+
+                grdrm_card_async(card, r);
+                return;
+        }
+
+        if (!crtc->applied) {
+                log_debug("grdrm: %s: crtc %" PRIu32 " applied via deep modeset",
+                          card->base.name, crtc->object.id);
+                crtc->applied = true;
+        }
+
+        *slot = NULL;
+        pipe->base.front = &fb->base;
+        fb->flipid = 0;
+        ++pipe->counter;
+        pipe->base.flipping = false;
+        pipe->base.flip = false;
+
+        if (!pipe->base.back) {
+                for (i = 0; i < pipe->base.max_fbs; ++i) {
+                        if (!pipe->base.fbs[i])
+                                continue;
+
+                        fb = fb_from_base(pipe->base.fbs[i]);
+                        if (&fb->base == pipe->base.front)
+                                continue;
+
+                        fb->flipid = 0;
+                        pipe->base.back = &fb->base;
+                        break;
+                }
+        }
+}
+
+static int grdrm_crtc_commit_flip(grdrm_crtc *crtc, grdev_fb **slot) {
         struct drm_mode_crtc_page_flip page_flip = { .crtc_id = crtc->object.id };
+        grdrm_card *card = crtc->object.card;
+        grdrm_pipe *pipe = crtc->pipe;
+        grdrm_fb *fb = fb_from_base(*slot);
+        uint32_t cnt;
+        size_t i;
+        int r;
+
+        assert(crtc);
+        assert(slot);
+        assert(*slot);
+        assert(pipe);
+
+        if (!crtc->applied && !grdrm_modes_compatible(&crtc->kern.mode, &crtc->set.mode))
+                return 0;
+
+        cnt = ++pipe->counter ? : ++pipe->counter;
+        page_flip.fb_id = fb->id;
+        page_flip.flags = DRM_MODE_PAGE_FLIP_EVENT;
+        page_flip.user_data = grdrm_encode_vblank_data(crtc->object.id, cnt);
+
+        r = ioctl(card->fd, DRM_IOCTL_MODE_PAGE_FLIP, &page_flip);
+        if (r < 0) {
+                r = -errno;
+                log_debug("grdrm: %s: cannot schedule page-flip on crtc %" PRIu32 ": %m",
+                          card->base.name, crtc->object.id);
+
+                if (grdrm_card_async(card, r))
+                        return r;
+
+                return 0;
+        }
+
+        if (!crtc->applied) {
+                log_debug("grdrm: %s: crtc %" PRIu32 " applied via page flip",
+                          card->base.name, crtc->object.id);
+                crtc->applied = true;
+        }
+
+        pipe->base.flipping = true;
+        pipe->base.flip = false;
+        pipe->counter = cnt;
+        fb->flipid = cnt;
+        *slot = NULL;
+
+        if (!pipe->base.back) {
+                for (i = 0; i < pipe->base.max_fbs; ++i) {
+                        if (!pipe->base.fbs[i])
+                                continue;
+
+                        fb = fb_from_base(pipe->base.fbs[i]);
+                        if (&fb->base == pipe->base.front)
+                                continue;
+                        if (fb->flipid)
+                                continue;
+
+                        pipe->base.back = &fb->base;
+                        break;
+                }
+        }
+
+        return 1;
+}
+
+static void grdrm_crtc_commit(grdrm_crtc *crtc) {
         struct drm_mode_crtc set_crtc = { .crtc_id = crtc->object.id };
         grdrm_card *card = crtc->object.card;
         grdrm_pipe *pipe;
         grdev_fb **slot;
-        grdrm_fb *fb;
-        uint32_t cnt;
-        size_t i;
         int r;
 
         assert(crtc);
@@ -1141,102 +1262,11 @@ static void grdrm_crtc_commit(grdrm_crtc *crtc) {
         if (!*slot)
                 return;
 
-        fb = fb_from_base(*slot);
-
-        if (crtc->applied || grdrm_modes_compatible(&crtc->kern.mode, &crtc->set.mode)) {
-                cnt = ++pipe->counter ? : ++pipe->counter;
-                page_flip.fb_id = fb->id;
-                page_flip.flags = DRM_MODE_PAGE_FLIP_EVENT;
-                page_flip.user_data = grdrm_encode_vblank_data(crtc->object.id, cnt);
-
-                r = ioctl(card->fd, DRM_IOCTL_MODE_PAGE_FLIP, &page_flip);
-                if (r < 0) {
-                        r = -errno;
-                        log_debug("grdrm: %s: cannot schedule page-flip on crtc %" PRIu32 ": %m",
-                                  card->base.name, crtc->object.id);
-
-                        if (grdrm_card_async(card, r))
-                                return;
-
-                        /* fall through to deep modeset */
-                } else {
-                        if (!crtc->applied) {
-                                log_debug("grdrm: %s: crtc %" PRIu32 " applied via page flip",
-                                          card->base.name, crtc->object.id);
-                                crtc->applied = true;
-                        }
-
-                        pipe->base.flipping = true;
-                        pipe->counter = cnt;
-                        fb->flipid = cnt;
-                        *slot = NULL;
-
-                        if (!pipe->base.back) {
-                                for (i = 0; i < pipe->base.max_fbs; ++i) {
-                                        if (!pipe->base.fbs[i])
-                                                continue;
-
-                                        fb = fb_from_base(pipe->base.fbs[i]);
-                                        if (&fb->base == pipe->base.front)
-                                                continue;
-                                        if (fb->flipid)
-                                                continue;
-
-                                        pipe->base.back = &fb->base;
-                                        break;
-                                }
-                        }
-                }
-        }
-
-        if (!crtc->applied) {
-                set_crtc.set_connectors_ptr = PTR_TO_UINT64(crtc->set.connectors);
-                set_crtc.count_connectors = crtc->set.n_connectors;
-                set_crtc.fb_id = fb->id;
-                set_crtc.x = 0;
-                set_crtc.y = 0;
-                set_crtc.mode_valid = 1;
-                set_crtc.mode = crtc->set.mode;
-
-                r = ioctl(card->fd, DRM_IOCTL_MODE_SETCRTC, &set_crtc);
-                if (r < 0) {
-                        r = -errno;
-                        log_debug("grdrm: %s: cannot set crtc %" PRIu32 ": %m",
-                                  card->base.name, crtc->object.id);
-
-                        grdrm_card_async(card, r);
-                        return;
-                }
-
-                if (!crtc->applied) {
-                        log_debug("grdrm: %s: crtc %" PRIu32 " applied via deep modeset",
-                                  card->base.name, crtc->object.id);
-                        crtc->applied = true;
-                }
-
-                *slot = NULL;
-                pipe->base.front = &fb->base;
-                fb->flipid = 0;
-                ++pipe->counter;
-                pipe->base.flipping = false;
-
-                if (!pipe->base.back) {
-                        for (i = 0; i < pipe->base.max_fbs; ++i) {
-                                if (!pipe->base.fbs[i])
-                                        continue;
-
-                                fb = fb_from_base(pipe->base.fbs[i]);
-                                if (&fb->base == pipe->base.front)
-                                        continue;
-
-                                fb->flipid = 0;
-                                pipe->base.back = &fb->base;
-                                break;
-                        }
-                }
+        r = grdrm_crtc_commit_flip(crtc, slot);
+        if (r == 0) {
+                /* in case we couldn't page-flip, perform deep modeset */
+                grdrm_crtc_commit_deep(crtc, slot);
         }
-
-        pipe->base.flip = false;
 }
 
 static void grdrm_crtc_restore(grdrm_crtc *crtc) {

commit 6221d249d1f03d235a23a284c597c86676b32d2f
Author: David Herrmann <dh.herrmann at gmail.com>
Date:   Sat Sep 20 09:50:10 2014 +0200

    terminal: grdev: refresh device state on hotplug events
    
    Whenever we get udev hotplug events, re-read the device state so we
    properly detect any changed in the display setups.

diff --git a/src/libsystemd-terminal/grdev-drm.c b/src/libsystemd-terminal/grdev-drm.c
index 3481584..f0f0448 100644
--- a/src/libsystemd-terminal/grdev-drm.c
+++ b/src/libsystemd-terminal/grdev-drm.c
@@ -2096,8 +2096,11 @@ static void grdrm_card_hotplug(grdrm_card *card) {
         int r;
 
         assert(card);
-        assert(!card->ready);
 
+        if (!card->running)
+                return;
+
+        card->ready = false;
         r = grdrm_card_resync(card);
         if (r < 0) {
                 log_debug("grdrm: %s/%s: cannot re-sync card: %s",
@@ -2955,3 +2958,30 @@ int grdev_drm_card_new(grdev_card **out, grdev_session *session, struct udev_dev
 
         return session->managed ? managed_card_new(out, session, ud) : unmanaged_card_new(out, session, ud);
 }
+
+void grdev_drm_card_hotplug(grdev_card *basecard, struct udev_device *ud) {
+        const char *p, *action;
+        grdrm_card *card;
+        dev_t devnum;
+
+        assert(basecard);
+        assert(grdev_is_drm_card(basecard));
+        assert(ud);
+
+        card = grdrm_card_from_base(basecard);
+
+        action = udev_device_get_action(ud);
+        if (!action || streq(action, "add") || streq(action, "remove")) {
+                /* If we get add/remove events on DRM nodes without devnum, we
+                 * got hotplugged DRM objects so refresh the device. */
+                devnum = udev_device_get_devnum(ud);
+                if (devnum == 0)
+                        grdrm_card_hotplug(card);
+        } else if (streq_ptr(action, "change")) {
+                /* A change event with HOTPLUG=1 is sent whenever a connector
+                 * changed state. Refresh the device to update our state. */
+                p = udev_device_get_property_value(ud, "HOTPLUG");
+                if (streq_ptr(p, "1"))
+                        grdrm_card_hotplug(card);
+        }
+}
diff --git a/src/libsystemd-terminal/grdev-internal.h b/src/libsystemd-terminal/grdev-internal.h
index 0064f0b..96830a7 100644
--- a/src/libsystemd-terminal/grdev-internal.h
+++ b/src/libsystemd-terminal/grdev-internal.h
@@ -47,6 +47,7 @@ typedef struct grdev_card               grdev_card;
 bool grdev_is_drm_card(grdev_card *card);
 grdev_card *grdev_find_drm_card(grdev_session *session, dev_t devnum);
 int grdev_drm_card_new(grdev_card **out, grdev_session *session, struct udev_device *ud);
+void grdev_drm_card_hotplug(grdev_card *card, struct udev_device *ud);
 
 /*
  * Displays
diff --git a/src/libsystemd-terminal/grdev.c b/src/libsystemd-terminal/grdev.c
index bb89ee7..3e3833f 100644
--- a/src/libsystemd-terminal/grdev.c
+++ b/src/libsystemd-terminal/grdev.c
@@ -1150,7 +1150,7 @@ void grdev_session_hotplug_drm(grdev_session *session, struct udev_device *ud) {
         if (!card)
                 return;
 
-        /* TODO: hotplug card */
+        grdev_drm_card_hotplug(card, ud);
 }
 
 static void session_configure(grdev_session *session) {

commit a3eabec96b872bbf581c9bfa81ecc9c2819b8de8
Author: David Herrmann <dh.herrmann at gmail.com>
Date:   Sat Sep 20 09:48:22 2014 +0200

    terminal: grdev: treat udev-devices without devnum as hotplug
    
    If we get udev-device events via sysview, but they lack devnum
    annotations, we know it cannot be a DRM card. Look through it's parents
    and treat it as hotplug event in case we find such a card.
    
    This will treat any new/removed connectors as sub-devices of the real
    card, instead of as devices on its own.

diff --git a/src/libsystemd-terminal/grdev.c b/src/libsystemd-terminal/grdev.c
index 1e02a67..bb89ee7 100644
--- a/src/libsystemd-terminal/grdev.c
+++ b/src/libsystemd-terminal/grdev.c
@@ -1095,7 +1095,7 @@ void grdev_session_add_drm(grdev_session *session, struct udev_device *ud) {
 
         devnum = udev_device_get_devnum(ud);
         if (devnum == 0)
-                return;
+                return grdev_session_hotplug_drm(session, ud);
 
         card = grdev_find_drm_card(session, devnum);
         if (card)
@@ -1120,7 +1120,7 @@ void grdev_session_remove_drm(grdev_session *session, struct udev_device *ud) {
 
         devnum = udev_device_get_devnum(ud);
         if (devnum == 0)
-                return;
+                return grdev_session_hotplug_drm(session, ud);
 
         card = grdev_find_drm_card(session, devnum);
         if (!card)
@@ -1130,17 +1130,23 @@ void grdev_session_remove_drm(grdev_session *session, struct udev_device *ud) {
 }
 
 void grdev_session_hotplug_drm(grdev_session *session, struct udev_device *ud) {
-        grdev_card *card;
+        grdev_card *card = NULL;
+        struct udev_device *p;
         dev_t devnum;
 
         assert(session);
         assert(ud);
 
-        devnum = udev_device_get_devnum(ud);
-        if (devnum == 0)
-                return;
+        for (p = ud; p; p = udev_device_get_parent_with_subsystem_devtype(p, "drm", NULL)) {
+                devnum = udev_device_get_devnum(ud);
+                if (devnum == 0)
+                        continue;
+
+                card = grdev_find_drm_card(session, devnum);
+                if (card)
+                        break;
+        }
 
-        card = grdev_find_drm_card(session, devnum);
         if (!card)
                 return;
 

commit 46c9a12780ea24ef311682897077423f1825c519
Author: David Herrmann <dh.herrmann at gmail.com>
Date:   Sat Sep 20 09:46:13 2014 +0200

    terminal: modeset: forward DEVICE_CHANGE events into grdev
    
    Properly forward DEVICE_CHANGE events into grdev so we can react to
    changing display setups.

diff --git a/src/libsystemd-terminal/modeset.c b/src/libsystemd-terminal/modeset.c
index c1119c9..57bf299 100644
--- a/src/libsystemd-terminal/modeset.c
+++ b/src/libsystemd-terminal/modeset.c
@@ -364,6 +364,13 @@ static int modeset_sysview_fn(sysview_context *c, void *userdata, sysview_event
                 }
 
                 break;
+        case SYSVIEW_EVENT_DEVICE_CHANGE:
+                d = ev->device_change.device;
+                type = sysview_device_get_type(d);
+                if (type == SYSVIEW_DEVICE_DRM)
+                        grdev_session_hotplug_drm(m->grdev_session, ev->device_change.ud);
+
+                break;
         }
 
         return 0;

commit 39cf40e846754fe37f5c8a948f37227ce1ef8472
Author: David Herrmann <dh.herrmann at gmail.com>
Date:   Sat Sep 20 09:45:26 2014 +0200

    terminal: reduce speed of morphing colors in modeset test
    
    The high frequency of the color-morphing is kinda irritating. Reduce it
    to a much lower frequency so you can actually look at it longer than few
    seconds.

diff --git a/src/libsystemd-terminal/modeset.c b/src/libsystemd-terminal/modeset.c
index 02ed1a8..c1119c9 100644
--- a/src/libsystemd-terminal/modeset.c
+++ b/src/libsystemd-terminal/modeset.c
@@ -252,9 +252,9 @@ static void modeset_draw(Modeset *m, const grdev_display_target *t) {
 static void modeset_render(Modeset *m, grdev_display *d) {
         const grdev_display_target *t;
 
-        m->r = next_color(&m->r_up, m->r, 20);
-        m->g = next_color(&m->g_up, m->g, 10);
-        m->b = next_color(&m->b_up, m->b, 5);
+        m->r = next_color(&m->r_up, m->r, 4);
+        m->g = next_color(&m->g_up, m->g, 3);
+        m->b = next_color(&m->b_up, m->b, 2);
 
         GRDEV_DISPLAY_FOREACH_TARGET(d, t, 0) {
                 modeset_draw(m, t);

commit c1102405c1c151b693cf92f1b704a4eb8aacee73
Author: David Herrmann <dh.herrmann at gmail.com>
Date:   Sat Sep 20 09:44:14 2014 +0200

    terminal: make drm-connectors first-level devices
    
    So far, we only forward DRM cards via sysview APIs. However, with MST,
    connectors can be hotplugged, too. Forward the connectors as first-level
    devices via sysview so API users can react to changing DRM connectors.

diff --git a/src/libsystemd-terminal/sysview.c b/src/libsystemd-terminal/sysview.c
index 23d564d..161ea11 100644
--- a/src/libsystemd-terminal/sysview.c
+++ b/src/libsystemd-terminal/sysview.c
@@ -895,7 +895,7 @@ static int context_ud_hotplug(sysview_context *c, struct udev_device *d) {
 
                 if (streq(subsystem, "input") && startswith(sysname, "event") && safe_atou(sysname + 5, &t) >= 0)
                         type = SYSVIEW_DEVICE_EVDEV;
-                else if (streq(subsystem, "drm") && startswith(sysname, "card") && safe_atou(sysname + 4, &t) >= 0)
+                else if (streq(subsystem, "drm") && startswith(sysname, "card"))
                         type = SYSVIEW_DEVICE_DRM;
                 else
                         type = (unsigned)-1;

commit 965f7a3f9bf7afb85be62198fabc70ffa033d8b1
Author: David Herrmann <dh.herrmann at gmail.com>
Date:   Sat Sep 20 09:43:16 2014 +0200

    terminal: forward DEVICE_CHANGE events via sysview
    
    Whe need to react to "change" events on devices, but we want to avoid
    duplicating udev-monitors everywhere. Therefore, make sysview forward
    change events to the sysview controllers, which can then properly react
    to it.

diff --git a/src/libsystemd-terminal/sysview.c b/src/libsystemd-terminal/sysview.c
index 6c1a954..23d564d 100644
--- a/src/libsystemd-terminal/sysview.c
+++ b/src/libsystemd-terminal/sysview.c
@@ -517,6 +517,18 @@ static int context_raise_session_detach(sysview_context *c, sysview_session *ses
         return context_raise(c, &event, 0);
 }
 
+static int context_raise_device_change(sysview_context *c, sysview_device *device, struct udev_device *ud) {
+        sysview_event event = {
+                .type = SYSVIEW_EVENT_DEVICE_CHANGE,
+                .device_change = {
+                        .device = device,
+                        .ud = ud,
+                }
+        };
+
+        return context_raise(c, &event, 0);
+}
+
 static int context_add_device(sysview_context *c, sysview_device *device) {
         sysview_session *session;
         int r, error = 0;
@@ -872,7 +884,7 @@ static int context_ud_hotplug(sysview_context *c, struct udev_device *d) {
                 if (!device)
                         return 0;
 
-                /* TODO: send REFRESH event */
+                return context_raise_device_change(c, device, d);
         } else if (!action || streq_ptr(action, "add")) {
                 struct udev_device *p;
                 unsigned int type, t;
diff --git a/src/libsystemd-terminal/sysview.h b/src/libsystemd-terminal/sysview.h
index de6ff37..b9452fa 100644
--- a/src/libsystemd-terminal/sysview.h
+++ b/src/libsystemd-terminal/sysview.h
@@ -64,6 +64,8 @@ enum {
         SYSVIEW_EVENT_SESSION_ATTACH,
         SYSVIEW_EVENT_SESSION_DETACH,
         SYSVIEW_EVENT_SESSION_CONTROL,
+
+        SYSVIEW_EVENT_DEVICE_CHANGE,
 };
 
 struct sysview_event {
@@ -94,6 +96,11 @@ struct sysview_event {
                         sysview_session *session;
                         int error;
                 } session_control;
+
+                struct {
+                        sysview_device *device;
+                        struct udev_device *ud;
+                } device_change;
         };
 };
 

commit 3e7f6cf9565e007545112f245e69b2bf45866258
Author: David Herrmann <dh.herrmann at gmail.com>
Date:   Sat Sep 20 09:29:11 2014 +0200

    terminal: parse ID_SEAT not only for parents but the device itself
    
    When deciding what seat a device is on, we have to traverse all parents
    to find one with an ID_SEAT tag, otherwise, input devices plugged on a
    seated USB-hub are not automatically attached to the right seat. But any
    tags on the main device still overwrite the tags of the childs, so fix our
    logic to check the device itself first, before traversing the parents.

diff --git a/src/libsystemd-terminal/sysview.c b/src/libsystemd-terminal/sysview.c
index fde87d1..6c1a954 100644
--- a/src/libsystemd-terminal/sysview.c
+++ b/src/libsystemd-terminal/sysview.c
@@ -893,11 +893,11 @@ static int context_ud_hotplug(sysview_context *c, struct udev_device *d) {
 
                 p = d;
                 seatname = NULL;
-                while ((p = udev_device_get_parent(p))) {
+                do {
                         seatname = udev_device_get_property_value(p, "ID_SEAT");
                         if (seatname)
                                 break;
-                }
+                } while ((p = udev_device_get_parent(p)));
 
                 seat = sysview_find_seat(c, seatname ? : "seat0");
                 if (!seat)



More information about the systemd-commits mailing list