+ int r;
+
+ assert(crtc);
+ assert(basefb);
+ assert(pipe);
+
+ 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;
+ 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;
+ /* 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_errno(errno, "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;
+ 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);
+
+ 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 *fb;