X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Flibsystemd-terminal%2Fgrdev-drm.c;h=dba6db2691c836cf009a4cf6fb6fcb38ec0d1dec;hb=0b76b4d8c28e8d82422ddeea2cc3c079fe4bb3dc;hp=2e55ad326b15ff3484b52179949673a2497267e0;hpb=0fbd4d113e0d2123e896e8005d1b7fe407c28c05;p=elogind.git diff --git a/src/libsystemd-terminal/grdev-drm.c b/src/libsystemd-terminal/grdev-drm.c index 2e55ad326..dba6db269 100644 --- a/src/libsystemd-terminal/grdev-drm.c +++ b/src/libsystemd-terminal/grdev-drm.c @@ -1095,19 +1095,19 @@ static void grdrm_crtc_expose(grdrm_crtc *crtc) { grdev_pipe_ready(&crtc->pipe->base, true); } -static void grdrm_crtc_commit_deep(grdrm_crtc *crtc, grdev_fb **slot) { +static void grdrm_crtc_commit_deep(grdrm_crtc *crtc, grdev_fb *basefb) { 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; + grdrm_fb *fb; int r; assert(crtc); - assert(slot); - assert(*slot); + assert(basefb); assert(pipe); + fb = fb_from_base(basefb); + 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; @@ -1132,7 +1132,7 @@ static void grdrm_crtc_commit_deep(grdrm_crtc *crtc, grdev_fb **slot) { crtc->applied = true; } - *slot = NULL; + pipe->base.back = NULL; pipe->base.front = &fb->base; fb->flipid = 0; ++pipe->counter; @@ -1144,39 +1144,37 @@ static void grdrm_crtc_commit_deep(grdrm_crtc *crtc, grdev_fb **slot) { * 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]) - 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) { +static int grdrm_crtc_commit_flip(grdrm_crtc *crtc, grdev_fb *basefb) { 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); + grdrm_fb *fb; uint32_t cnt; - size_t i; int r; assert(crtc); - assert(slot); - assert(*slot); + assert(basefb); assert(pipe); - if (!crtc->applied && !grdrm_modes_compatible(&crtc->kern.mode, &crtc->set.mode)) + if (!crtc->applied) { + if (!grdrm_modes_compatible(&crtc->kern.mode, &crtc->set.mode)) + return 0; + + /* TODO: Theoretically, we should be able to page-flip to our + * framebuffer here. We didn't perform any deep modeset, but the + * DRM driver is really supposed to reject our page-flip in case + * the FB is not compatible. We then properly fall back to a + * deep modeset. + * As it turns out, drivers don't to this. Therefore, we need to + * perform a full modeset on enter now. We might avoid this in + * the future with fixed drivers.. */ + return 0; + } + + fb = fb_from_base(basefb); cnt = ++pipe->counter ? : ++pipe->counter; page_flip.fb_id = fb->id; @@ -1209,29 +1207,13 @@ static int grdrm_crtc_commit_flip(grdrm_crtc *crtc, grdev_fb **slot) { pipe->base.flip = false; pipe->counter = cnt; fb->flipid = cnt; - *slot = NULL; + pipe->base.back = 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]) - 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; } @@ -1239,7 +1221,7 @@ 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; + grdev_fb *fb; int r; assert(crtc); @@ -1280,19 +1262,19 @@ static void grdrm_crtc_commit(grdrm_crtc *crtc) { assert(crtc->set.n_connectors > 0); if (pipe->base.flip) - slot = &pipe->base.back; + fb = pipe->base.back; else if (!crtc->applied) - slot = &pipe->base.front; + fb = pipe->base.front; else return; - if (!*slot) + if (!fb) return; - r = grdrm_crtc_commit_flip(crtc, slot); + r = grdrm_crtc_commit_flip(crtc, fb); if (r == 0) { /* in case we couldn't page-flip, perform deep modeset */ - grdrm_crtc_commit_deep(crtc, slot); + grdrm_crtc_commit_deep(crtc, fb); } } @@ -1335,7 +1317,6 @@ static void grdrm_crtc_restore(grdrm_crtc *crtc) { static void grdrm_crtc_flip_complete(grdrm_crtc *crtc, uint32_t counter, struct drm_event_vblank *event) { bool flipped = false; grdrm_pipe *pipe; - grdrm_fb *back = NULL; size_t i; assert(crtc); @@ -1362,20 +1343,13 @@ static void grdrm_crtc_flip_complete(grdrm_crtc *crtc, uint32_t counter, struct fb = fb_from_base(pipe->base.fbs[i]); if (counter != 0 && counter == pipe->counter && fb->flipid == counter) { pipe->base.front = &fb->base; + fb->flipid = 0; flipped = true; - } - - if (counter - fb->flipid < UINT16_MAX) { + } else if (counter - fb->flipid < UINT16_MAX) { fb->flipid = 0; - back = fb; - } else if (fb->flipid == 0) { - back = fb; } } - if (!pipe->base.back) - pipe->base.back = &back->base; - if (flipped) { crtc->pipe->base.flipping = false; grdev_pipe_frame(&pipe->base); @@ -1474,14 +1448,22 @@ static int grdrm_fb_new(grdrm_fb **out, grdrm_card *card, const struct drm_mode_ grdrm_fb *grdrm_fb_free(grdrm_fb *fb) { unsigned int i; + int r; if (!fb) return NULL; assert(fb->card); - if (fb->id > 0 && fb->card->fd >= 0) - ioctl(fb->card->fd, DRM_IOCTL_MODE_RMFB, fb->id); + if (fb->base.free_fn) + fb->base.free_fn(fb->base.data.ptr); + + if (fb->id > 0 && fb->card->fd >= 0) { + r = ioctl(fb->card->fd, DRM_IOCTL_MODE_RMFB, fb->id); + if (r < 0) + log_debug("grdrm: %s: cannot delete framebuffer %" PRIu32 ": %m", + fb->card->base.name, fb->id); + } for (i = 0; i < ELEMENTSOF(fb->handles); ++i) { struct drm_mode_destroy_dumb destroy_dumb = { }; @@ -1491,7 +1473,10 @@ grdrm_fb *grdrm_fb_free(grdrm_fb *fb) { if (fb->handles[i] > 0 && fb->card->fd >= 0) { destroy_dumb.handle = fb->handles[i]; - ioctl(fb->card->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_dumb); + r = ioctl(fb->card->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_dumb); + if (r < 0) + log_debug("grdrm: %s: cannot destroy dumb-buffer %" PRIu32 ": %m", + fb->card->base.name, fb->handles[i]); } } @@ -1554,8 +1539,46 @@ static void grdrm_pipe_free(grdev_pipe *basepipe) { free(pipe); } +static grdev_fb *grdrm_pipe_target(grdev_pipe *basepipe) { + grdrm_fb *fb; + size_t i; + + if (!basepipe->back) { + for (i = 0; i < basepipe->max_fbs; ++i) { + if (!basepipe->fbs[i]) + continue; + + fb = fb_from_base(basepipe->fbs[i]); + if (&fb->base == basepipe->front) + continue; + if (basepipe->flipping && fb->flipid) + continue; + + basepipe->back = &fb->base; + break; + } + } + + return basepipe->back; +} + +static void grdrm_pipe_enable(grdev_pipe *basepipe) { + grdrm_pipe *pipe = grdrm_pipe_from_base(basepipe); + + pipe->crtc->applied = false; +} + +static void grdrm_pipe_disable(grdev_pipe *basepipe) { + grdrm_pipe *pipe = grdrm_pipe_from_base(basepipe); + + pipe->crtc->applied = false; +} + static const grdev_pipe_vtable grdrm_pipe_vtable = { .free = grdrm_pipe_free, + .target = grdrm_pipe_target, + .enable = grdrm_pipe_enable, + .disable = grdrm_pipe_disable, }; /* @@ -2188,7 +2211,8 @@ static int grdrm_card_io_fn(sd_event_source *s, int fd, uint32_t revents, void * uint32_t id, counter; grdrm_object *object; char buf[4096]; - ssize_t l, i; + size_t len; + ssize_t l; if (revents & (EPOLLHUP | EPOLLERR)) { /* Immediately close device on HUP; no need to flush pending @@ -2207,15 +2231,12 @@ static int grdrm_card_io_fn(sd_event_source *s, int fd, uint32_t revents, void * log_debug("grdrm: %s/%s: read error: %m", card->base.session->name, card->base.name); grdrm_card_close(card); return 0; - } else if ((size_t)l < sizeof(*event)) { - log_debug("grdrm: %s/%s: short read of %zd bytes", card->base.session->name, card->base.name, l); - return 0; } - for (i = 0; i < l; i += event->length) { - event = (void*)&buf[i]; + for (len = l; len > 0; len -= event->length) { + event = (void*)buf; - if (i + event->length > l) { + if (len < sizeof(*event) || len < event->length) { log_debug("grdrm: %s/%s: truncated event", card->base.session->name, card->base.name); break; } @@ -2549,8 +2570,13 @@ static int unmanaged_card_new(grdev_card **out, grdev_session *session, struct u basecard->session->name, basecard->name, cu->devnode); } else { /* We might get DRM-Master implicitly on open(); drop it immediately - * so we acquire it only once we're actually enabled. */ - ioctl(fd, DRM_IOCTL_DROP_MASTER, 0); + * so we acquire it only once we're actually enabled. We don't + * really care whether this call fails or not, but lets log any + * weird errors, anyway. */ + r = ioctl(fd, DRM_IOCTL_DROP_MASTER, 0); + if (r < 0 && errno != EACCES && errno != EINVAL) + log_debug("grdrm: %s/%s: cannot drop DRM-Master: %m", + basecard->session->name, basecard->name); r = grdrm_card_open(&cu->card, fd); if (r < 0)