X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Flibsystemd-terminal%2Fgrdev-drm.c;h=6b130116d7c4011204d6cc527484818a27bc8222;hb=6a15ce2b3eb852023d77787f96c6a4a72eb4d60d;hp=00aac292d2fc445b12ab3709debdc8f59c2e80b9;hpb=95dbf6b19e8f25e28224b954ef99d96225b4e6e7;p=elogind.git diff --git a/src/libsystemd-terminal/grdev-drm.c b/src/libsystemd-terminal/grdev-drm.c index 00aac292d..6b130116d 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; @@ -346,6 +347,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; } @@ -601,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); @@ -687,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; @@ -696,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; } @@ -1038,7 +1049,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 +1139,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]) @@ -1168,8 +1186,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; @@ -1189,6 +1211,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]) @@ -1335,10 +1362,9 @@ 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) { @@ -1346,7 +1372,7 @@ static void grdrm_crtc_flip_complete(grdrm_crtc *crtc, uint32_t counter, struct } } - if (!pipe->base.back) + if (!pipe->base.back && back) pipe->base.back = &back->base; if (flipped) { @@ -1447,14 +1473,19 @@ 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->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 = { }; @@ -1464,7 +1495,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]); } } @@ -1501,6 +1535,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); @@ -2130,6 +2165,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) { @@ -2140,9 +2177,13 @@ 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; + card->hotplug = false; grdev_session_unpin(card->base.session); } @@ -2154,7 +2195,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 @@ -2173,15 +2215,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; } @@ -2350,6 +2389,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; @@ -2514,8 +2554,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) @@ -3005,13 +3050,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); + } } }