[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