Hashmap *object_map;
bool async_hotplug : 1;
+ bool hotplug : 1;
bool running : 1;
bool ready : 1;
bool cap_dumb : 1;
return false;
if (a->vdisplay != b->vdisplay)
return false;
+ if (a->vrefresh != b->vrefresh)
+ return false;
return true;
}
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);
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;
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;
}
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;
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])
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;
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])
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 = { };
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]);
}
}
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);
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) {
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);
}
for (i = 0; i < l; i += event->length) {
event = (void*)&buf[i];
- if (i + event->length > l) {
+ if (i + (ssize_t)sizeof(*event) > l || i + (ssize_t)event->length > l) {
log_debug("grdrm: %s/%s: truncated event", card->base.session->name, card->base.name);
break;
}
sd_event_source_set_enabled(card->fd_src, SD_EVENT_OFF);
+ card->hotplug = true;
card->fd = fd;
fd = -1;
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)
/* 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);
+ }
}
}