chiark / gitweb /
terminal: forward evdev RESYNC events to linked devices
[elogind.git] / src / libsystemd-terminal / grdev.c
index ab1c407ecb7a3f40aa5d0c901886499e269cf362..43d0c7c9bfa4fa4076e9ecdc9a8dedb7d2fd88cd 100644 (file)
@@ -20,6 +20,7 @@
 ***/
 
 #include <inttypes.h>
+#include <libudev.h>
 #include <stdbool.h>
 #include <stdlib.h>
 #include <systemd/sd-bus.h>
@@ -30,6 +31,7 @@
 #include "hashmap.h"
 #include "login-shared.h"
 #include "macro.h"
+#include "udev-util.h"
 #include "util.h"
 
 static void pipe_enable(grdev_pipe *pipe);
@@ -572,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;
 
@@ -583,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);
@@ -603,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;
@@ -631,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);
@@ -674,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;
-                }
         }
 }
 
@@ -694,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));
 }
 
 /*
@@ -919,14 +972,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) {
@@ -1083,6 +1139,74 @@ void grdev_session_restore(grdev_session *session) {
                         card->vtable->restore(card);
 }
 
+void grdev_session_add_drm(grdev_session *session, struct udev_device *ud) {
+        grdev_card *card;
+        dev_t devnum;
+        int r;
+
+        assert(session);
+        assert(ud);
+
+        devnum = udev_device_get_devnum(ud);
+        if (devnum == 0)
+                return grdev_session_hotplug_drm(session, ud);
+
+        card = grdev_find_drm_card(session, devnum);
+        if (card)
+                return;
+
+        r = grdev_drm_card_new(&card, session, ud);
+        if (r < 0) {
+                log_debug("grdev: %s: cannot add DRM device for %s: %s",
+                          session->name, udev_device_get_syspath(ud), strerror(-r));
+                return;
+        }
+
+        session_add_card(session, card);
+}
+
+void grdev_session_remove_drm(grdev_session *session, struct udev_device *ud) {
+        grdev_card *card;
+        dev_t devnum;
+
+        assert(session);
+        assert(ud);
+
+        devnum = udev_device_get_devnum(ud);
+        if (devnum == 0)
+                return grdev_session_hotplug_drm(session, ud);
+
+        card = grdev_find_drm_card(session, devnum);
+        if (!card)
+                return;
+
+        session_remove_card(session, card);
+}
+
+void grdev_session_hotplug_drm(grdev_session *session, struct udev_device *ud) {
+        grdev_card *card = NULL;
+        struct udev_device *p;
+        dev_t devnum;
+
+        assert(session);
+        assert(ud);
+
+        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;
+        }
+
+        if (!card)
+                return;
+
+        grdev_drm_card_hotplug(card, ud);
+}
+
 static void session_configure(grdev_session *session) {
         grdev_display *display;
         grdev_tile *tile;