X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Flibsystemd-terminal%2Fgrdev.c;h=43d0c7c9bfa4fa4076e9ecdc9a8dedb7d2fd88cd;hb=89febb631a4710992cd41e402a643451b19c11a7;hp=ab1c407ecb7a3f40aa5d0c901886499e269cf362;hpb=650c5444273993f969b9cd7df9add6ab2df0414e;p=elogind.git diff --git a/src/libsystemd-terminal/grdev.c b/src/libsystemd-terminal/grdev.c index ab1c407ec..43d0c7c9b 100644 --- a/src/libsystemd-terminal/grdev.c +++ b/src/libsystemd-terminal/grdev.c @@ -20,6 +20,7 @@ ***/ #include +#include #include #include #include @@ -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;