uterm: drm: move display_ops into vdrm object
[platform/upstream/kmscon.git] / src / uterm_drm_shared.c
1 /*
2  * uterm - Linux User-Space Terminal
3  *
4  * Copyright (c) 2011-2013 David Herrmann <dh.herrmann@googlemail.com>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files
8  * (the "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included
15  * in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25
26 /*
27  * DRM shared functions
28  */
29
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <poll.h>
33 #include <stdbool.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <xf86drm.h>
38 #include <xf86drmMode.h>
39 #include "shl_log.h"
40 #include "shl_timer.h"
41 #include "uterm_drm_shared_internal.h"
42 #include "uterm_video.h"
43 #include "uterm_video_internal.h"
44
45 #define LOG_SUBSYSTEM "drm_shared"
46
47 int uterm_drm_mode_init(struct uterm_mode *mode)
48 {
49         struct uterm_drm_mode *m;
50
51         m = malloc(sizeof(*m));
52         if (!m)
53                 return -ENOMEM;
54         memset(m, 0, sizeof(*m));
55         mode->data = m;
56
57         return 0;
58 }
59
60 void uterm_drm_mode_destroy(struct uterm_mode *mode)
61 {
62         free(mode->data);
63 }
64
65 const char *uterm_drm_mode_get_name(const struct uterm_mode *mode)
66 {
67         struct uterm_drm_mode *m = mode->data;
68
69         return m->info.name;
70 }
71
72 unsigned int uterm_drm_mode_get_width(const struct uterm_mode *mode)
73 {
74         struct uterm_drm_mode *m = mode->data;
75
76         return m->info.hdisplay;
77 }
78
79 unsigned int uterm_drm_mode_get_height(const struct uterm_mode *mode)
80 {
81         struct uterm_drm_mode *m = mode->data;
82
83         return m->info.vdisplay;
84 }
85
86 void uterm_drm_mode_set(struct uterm_mode *mode, drmModeModeInfo *info)
87 {
88         struct uterm_drm_mode *m = mode->data;
89
90         m->info = *info;
91 }
92
93 const struct mode_ops uterm_drm_mode_ops = {
94         .init = uterm_drm_mode_init,
95         .destroy = uterm_drm_mode_destroy,
96         .get_name = uterm_drm_mode_get_name,
97         .get_width = uterm_drm_mode_get_width,
98         .get_height = uterm_drm_mode_get_height,
99 };
100
101 int uterm_drm_set_dpms(int fd, uint32_t conn_id, int state)
102 {
103         int i, ret, set;
104         drmModeConnector *conn;
105         drmModePropertyRes *prop;
106
107         switch (state) {
108         case UTERM_DPMS_ON:
109                 set = DRM_MODE_DPMS_ON;
110                 break;
111         case UTERM_DPMS_STANDBY:
112                 set = DRM_MODE_DPMS_STANDBY;
113                 break;
114         case UTERM_DPMS_SUSPEND:
115                 set = DRM_MODE_DPMS_SUSPEND;
116                 break;
117         case UTERM_DPMS_OFF:
118                 set = DRM_MODE_DPMS_OFF;
119                 break;
120         default:
121                 return -EINVAL;
122         }
123
124         conn = drmModeGetConnector(fd, conn_id);
125         if (!conn) {
126                 log_err("cannot get display connector");
127                 return -EFAULT;
128         }
129
130         ret = state;
131         for (i = 0; i < conn->count_props; ++i) {
132                 prop = drmModeGetProperty(fd, conn->props[i]);
133                 if (!prop) {
134                         log_error("cannot get DRM property (%d): %m", errno);
135                         continue;
136                 }
137
138                 if (!strcmp(prop->name, "DPMS")) {
139                         ret = drmModeConnectorSetProperty(fd, conn_id,
140                                                           prop->prop_id, set);
141                         if (ret) {
142                                 log_info("cannot set DPMS");
143                                 ret = -EFAULT;
144                         }
145                         drmModeFreeProperty(prop);
146                         break;
147                 }
148                 drmModeFreeProperty(prop);
149         }
150
151         if (i == conn->count_props) {
152                 log_warn("display does not support DPMS");
153                 ret = UTERM_DPMS_UNKNOWN;
154         }
155
156         drmModeFreeConnector(conn);
157         return ret;
158 }
159
160 int uterm_drm_get_dpms(int fd, drmModeConnector *conn)
161 {
162         int i, ret;
163         drmModePropertyRes *prop;
164
165         for (i = 0; i < conn->count_props; ++i) {
166                 prop = drmModeGetProperty(fd, conn->props[i]);
167                 if (!prop) {
168                         log_error("cannot get DRM property (%d): %m", errno);
169                         continue;
170                 }
171
172                 if (!strcmp(prop->name, "DPMS")) {
173                         switch (conn->prop_values[i]) {
174                         case DRM_MODE_DPMS_ON:
175                                 ret = UTERM_DPMS_ON;
176                                 break;
177                         case DRM_MODE_DPMS_STANDBY:
178                                 ret = UTERM_DPMS_STANDBY;
179                                 break;
180                         case DRM_MODE_DPMS_SUSPEND:
181                                 ret = UTERM_DPMS_SUSPEND;
182                                 break;
183                         case DRM_MODE_DPMS_OFF:
184                         default:
185                                 ret = UTERM_DPMS_OFF;
186                         }
187
188                         drmModeFreeProperty(prop);
189                         return ret;
190                 }
191                 drmModeFreeProperty(prop);
192         }
193
194         if (i == conn->count_props)
195                 log_warn("display does not support DPMS");
196         return UTERM_DPMS_UNKNOWN;
197 }
198
199 int uterm_drm_display_init(struct uterm_display *disp, void *data)
200 {
201         struct uterm_drm_display *d;
202
203         d = malloc(sizeof(*d));
204         if (!d)
205                 return -ENOMEM;
206         memset(d, 0, sizeof(*d));
207         disp->data = d;
208         d->data = data;
209
210         return 0;
211 }
212
213 void uterm_drm_display_destroy(struct uterm_display *disp)
214 {
215         free(disp->data);
216 }
217
218 int uterm_drm_display_activate(struct uterm_display *disp, int fd)
219 {
220         struct uterm_video *video = disp->video;
221         struct uterm_drm_display *ddrm = disp->data;
222         drmModeRes *res;
223         drmModeConnector *conn;
224         drmModeEncoder *enc;
225         int crtc, i;
226
227         res = drmModeGetResources(fd);
228         if (!res) {
229                 log_err("cannot get resources for display %p", disp);
230                 return -EFAULT;
231         }
232         conn = drmModeGetConnector(fd, ddrm->conn_id);
233         if (!conn) {
234                 log_err("cannot get connector for display %p", disp);
235                 drmModeFreeResources(res);
236                 return -EFAULT;
237         }
238
239         crtc = -1;
240         for (i = 0; i < conn->count_encoders; ++i) {
241                 enc = drmModeGetEncoder(fd, conn->encoders[i]);
242                 if (!enc)
243                         continue;
244                 crtc = uterm_drm_video_find_crtc(video, res, enc);
245                 drmModeFreeEncoder(enc);
246                 if (crtc >= 0)
247                         break;
248         }
249
250         drmModeFreeConnector(conn);
251         drmModeFreeResources(res);
252
253         if (crtc < 0) {
254                 log_warn("cannot find crtc for new display");
255                 return -ENODEV;
256         }
257
258         ddrm->crtc_id = crtc;
259         if (ddrm->saved_crtc)
260                 drmModeFreeCrtc(ddrm->saved_crtc);
261         ddrm->saved_crtc = drmModeGetCrtc(fd, ddrm->crtc_id);
262
263         return 0;
264 }
265
266 void uterm_drm_display_deactivate(struct uterm_display *disp, int fd)
267 {
268         struct uterm_drm_display *ddrm = disp->data;
269
270         uterm_drm_display_wait_pflip(disp);
271
272         if (ddrm->saved_crtc) {
273                 if (disp->video->flags & VIDEO_AWAKE) {
274                         drmModeSetCrtc(fd, ddrm->saved_crtc->crtc_id,
275                                        ddrm->saved_crtc->buffer_id,
276                                        ddrm->saved_crtc->x,
277                                        ddrm->saved_crtc->y,
278                                        &ddrm->conn_id, 1,
279                                        &ddrm->saved_crtc->mode);
280                 }
281                 drmModeFreeCrtc(ddrm->saved_crtc);
282                 ddrm->saved_crtc = NULL;
283         }
284
285         ddrm->crtc_id = 0;
286         disp->flags &= ~(DISPLAY_VSYNC | DISPLAY_ONLINE | DISPLAY_PFLIP);
287 }
288
289 int uterm_drm_display_set_dpms(struct uterm_display *disp, int state)
290 {
291         int ret;
292         struct uterm_drm_display *ddrm = disp->data;
293         struct uterm_drm_video *vdrm = disp->video->data;
294
295         log_info("setting DPMS of display %p to %s", disp,
296                  uterm_dpms_to_name(state));
297
298         ret = uterm_drm_set_dpms(vdrm->fd, ddrm->conn_id, state);
299         if (ret < 0)
300                 return ret;
301
302         disp->dpms = ret;
303         return 0;
304 }
305
306 int uterm_drm_display_wait_pflip(struct uterm_display *disp)
307 {
308         struct uterm_video *video = disp->video;
309         int ret;
310         unsigned int timeout = 1000; /* 1s */
311
312         if ((disp->flags & DISPLAY_PFLIP) || !(disp->flags & DISPLAY_VSYNC))
313                 return 0;
314
315         do {
316                 ret = uterm_drm_video_wait_pflip(video, &timeout);
317                 if (ret < 1)
318                         break;
319                 else if ((disp->flags & DISPLAY_PFLIP))
320                         break;
321         } while (timeout > 0);
322
323         if (ret < 0)
324                 return ret;
325         if (ret == 0 || !timeout) {
326                 log_warning("timeout waiting for page-flip on display %p",
327                             disp);
328                 return -ETIMEDOUT;
329         }
330
331         return 0;
332 }
333
334 int uterm_drm_display_swap(struct uterm_display *disp, uint32_t fb,
335                            bool immediate)
336 {
337         struct uterm_drm_display *ddrm = disp->data;
338         struct uterm_video *video = disp->video;
339         struct uterm_drm_video *vdrm = video->data;
340         int ret;
341         drmModeModeInfo *mode;
342
343         if (disp->dpms != UTERM_DPMS_ON)
344                 return -EINVAL;
345
346         if (immediate) {
347                 ret = uterm_drm_display_wait_pflip(disp);
348                 if (ret)
349                         return ret;
350
351                 mode = uterm_drm_mode_get_info(disp->current_mode);
352                 ret = drmModeSetCrtc(vdrm->fd, ddrm->crtc_id, fb, 0, 0,
353                                      &ddrm->conn_id, 1, mode);
354                 if (ret) {
355                         log_error("cannot set DRM-CRTC (%d): %m", errno);
356                         return -EFAULT;
357                 }
358         } else {
359                 if ((disp->flags & DISPLAY_VSYNC))
360                         return -EBUSY;
361
362                 ret = drmModePageFlip(vdrm->fd, ddrm->crtc_id, fb,
363                                       DRM_MODE_PAGE_FLIP_EVENT, disp);
364                 if (ret) {
365                         log_error("cannot page-flip on DRM-CRTC (%d): %m",
366                                   errno);
367                         return -EFAULT;
368                 }
369
370                 uterm_display_ref(disp);
371                 disp->flags |= DISPLAY_VSYNC;
372         }
373
374         return 0;
375 }
376
377 static void uterm_drm_display_pflip(struct uterm_display *disp)
378 {
379         struct uterm_drm_video *vdrm = disp->video->data;
380
381         disp->flags &= ~(DISPLAY_PFLIP | DISPLAY_VSYNC);
382         if (vdrm->page_flip)
383                 vdrm->page_flip(disp);
384
385         DISPLAY_CB(disp, UTERM_PAGE_FLIP);
386 }
387
388 static void display_event(int fd, unsigned int frame, unsigned int sec,
389                           unsigned int usec, void *data)
390 {
391         struct uterm_display *disp = data;
392
393         if (disp->video && (disp->flags & DISPLAY_VSYNC))
394                 disp->flags |= DISPLAY_PFLIP;
395
396         uterm_display_unref(disp);
397 }
398
399 static int uterm_drm_video_read_events(struct uterm_video *video)
400 {
401         struct uterm_drm_video *vdrm = video->data;
402         drmEventContext ev;
403         int ret;
404
405         /* TODO: DRM subsystem does not support non-blocking reads and it also
406          * doesn't return 0/-1 if the device is dead. This can lead to serious
407          * deadlocks in userspace if we read() after a device was unplugged. Fix
408          * this upstream and then make this code actually loop. */
409         memset(&ev, 0, sizeof(ev));
410         ev.version = DRM_EVENT_CONTEXT_VERSION;
411         ev.page_flip_handler = display_event;
412         errno = 0;
413         ret = drmHandleEvent(vdrm->fd, &ev);
414
415         if (ret < 0 && errno != EAGAIN)
416                 return -EFAULT;
417
418         return 0;
419 }
420
421 static void do_pflips(struct ev_eloop *eloop, void *unused, void *data)
422 {
423         struct uterm_video *video = data;
424         struct uterm_display *disp;
425         struct shl_dlist *iter;
426
427         shl_dlist_for_each(iter, &video->displays) {
428                 disp = shl_dlist_entry(iter, struct uterm_display, list);
429                 if ((disp->flags & DISPLAY_PFLIP))
430                         uterm_drm_display_pflip(disp);
431         }
432 }
433
434 static void io_event(struct ev_fd *fd, int mask, void *data)
435 {
436         struct uterm_video *video = data;
437         struct uterm_drm_video *vdrm = video->data;
438         struct uterm_display *disp;
439         struct shl_dlist *iter;
440         int ret;
441
442         /* TODO: forward HUP to caller */
443         if (mask & (EV_HUP | EV_ERR)) {
444                 log_err("error or hangup on DRM fd");
445                 ev_eloop_rm_fd(vdrm->efd);
446                 vdrm->efd = NULL;
447                 return;
448         }
449
450         if (!(mask & EV_READABLE))
451                 return;
452
453         ret = uterm_drm_video_read_events(video);
454         if (ret)
455                 return;
456
457         shl_dlist_for_each(iter, &video->displays) {
458                 disp = shl_dlist_entry(iter, struct uterm_display, list);
459                 if ((disp->flags & DISPLAY_PFLIP))
460                         uterm_drm_display_pflip(disp);
461         }
462 }
463
464 int uterm_drm_video_init(struct uterm_video *video, const char *node,
465                          const struct display_ops *display_ops,
466                          uterm_drm_page_flip_t pflip, void *data)
467 {
468         struct uterm_drm_video *vdrm;
469         int ret;
470
471         log_info("new drm device via %s", node);
472
473         vdrm = malloc(sizeof(*vdrm));
474         if (!vdrm)
475                 return -ENOMEM;
476         memset(vdrm, 0, sizeof(*vdrm));
477         video->data = vdrm;
478         vdrm->data = data;
479         vdrm->page_flip = pflip;
480         vdrm->display_ops = display_ops;
481
482         vdrm->fd = open(node, O_RDWR | O_CLOEXEC | O_NONBLOCK);
483         if (vdrm->fd < 0) {
484                 log_err("cannot open drm device %s (%d): %m", node, errno);
485                 ret = -EFAULT;
486                 goto err_free;
487         }
488         /* TODO: fix the race-condition with DRM-Master-on-open */
489         drmDropMaster(vdrm->fd);
490
491         ret = ev_eloop_new_fd(video->eloop, &vdrm->efd, vdrm->fd, EV_READABLE,
492                               io_event, video);
493         if (ret)
494                 goto err_close;
495
496         ret = shl_timer_new(&vdrm->timer);
497         if (ret)
498                 goto err_fd;
499
500         video->flags |= VIDEO_HOTPLUG;
501         return 0;
502
503 err_fd:
504         ev_eloop_rm_fd(vdrm->efd);
505 err_close:
506         close(vdrm->fd);
507 err_free:
508         free(vdrm);
509         return ret;
510 }
511
512 void uterm_drm_video_destroy(struct uterm_video *video)
513 {
514         struct uterm_drm_video *vdrm = video->data;
515
516         ev_eloop_unregister_idle_cb(video->eloop, do_pflips, video, EV_SINGLE);
517         shl_timer_free(vdrm->timer);
518         ev_eloop_rm_fd(vdrm->efd);
519         close(vdrm->fd);
520         free(video->data);
521 }
522
523 int uterm_drm_video_find_crtc(struct uterm_video *video, drmModeRes *res,
524                               drmModeEncoder *enc)
525 {
526         int i, crtc;
527         struct uterm_display *iter;
528         struct uterm_drm_display *ddrm;
529         struct shl_dlist *it;
530
531         for (i = 0; i < res->count_crtcs; ++i) {
532                 if (enc->possible_crtcs & (1 << i)) {
533                         crtc = res->crtcs[i];
534                         shl_dlist_for_each(it, &video->displays) {
535                                 iter = shl_dlist_entry(it,
536                                                        struct uterm_display,
537                                                        list);
538                                 ddrm = iter->data;
539                                 if (ddrm->crtc_id == crtc)
540                                         break;
541                         }
542                         if (it == &video->displays)
543                                 return crtc;
544                 }
545         }
546
547         return -1;
548 }
549
550 static void bind_display(struct uterm_video *video, drmModeRes *res,
551                          drmModeConnector *conn)
552 {
553         struct uterm_drm_video *vdrm = video->data;
554         struct uterm_display *disp;
555         struct uterm_drm_display *ddrm;
556         struct uterm_mode *mode;
557         int ret, i;
558
559         ret = display_new(&disp, vdrm->display_ops);
560         if (ret)
561                 return;
562         ddrm = disp->data;
563
564         for (i = 0; i < conn->count_modes; ++i) {
565                 ret = mode_new(&mode, &uterm_drm_mode_ops);
566                 if (ret)
567                         continue;
568
569                 uterm_drm_mode_set(mode, &conn->modes[i]);
570
571                 ret = uterm_mode_bind(mode, disp);
572                 if (ret) {
573                         uterm_mode_unref(mode);
574                         continue;
575                 }
576
577                 /* TODO: more sophisticated default-mode selection */
578                 if (!disp->default_mode)
579                         disp->default_mode = mode;
580
581                 uterm_mode_unref(mode);
582         }
583
584         if (shl_dlist_empty(&disp->modes)) {
585                 log_warn("no valid mode for display found");
586                 ret = -EFAULT;
587                 goto err_unref;
588         }
589
590         ddrm->conn_id = conn->connector_id;
591         disp->flags |= DISPLAY_AVAILABLE;
592         disp->dpms = uterm_drm_get_dpms(vdrm->fd, conn);
593
594         log_info("display %p DPMS is %s", disp,
595                  uterm_dpms_to_name(disp->dpms));
596
597         ret = uterm_display_bind(disp, video);
598         if (ret)
599                 goto err_unref;
600
601         uterm_display_unref(disp);
602         return;
603
604 err_unref:
605         uterm_display_unref(disp);
606         return;
607 }
608
609 int uterm_drm_video_hotplug(struct uterm_video *video,
610                             bool read_dpms)
611 {
612         struct uterm_drm_video *vdrm = video->data;
613         drmModeRes *res;
614         drmModeConnector *conn;
615         struct uterm_display *disp;
616         struct uterm_drm_display *ddrm;
617         int i, dpms;
618         struct shl_dlist *iter, *tmp;
619
620         if (!video_is_awake(video) || !video_need_hotplug(video))
621                 return 0;
622
623         res = drmModeGetResources(vdrm->fd);
624         if (!res) {
625                 log_err("cannot retrieve drm resources");
626                 return -EACCES;
627         }
628
629         shl_dlist_for_each(iter, &video->displays) {
630                 disp = shl_dlist_entry(iter, struct uterm_display, list);
631                 disp->flags &= ~DISPLAY_AVAILABLE;
632         }
633
634         for (i = 0; i < res->count_connectors; ++i) {
635                 conn = drmModeGetConnector(vdrm->fd, res->connectors[i]);
636                 if (!conn)
637                         continue;
638                 if (conn->connection != DRM_MODE_CONNECTED) {
639                         drmModeFreeConnector(conn);
640                         continue;
641                 }
642
643                 shl_dlist_for_each(iter, &video->displays) {
644                         disp = shl_dlist_entry(iter, struct uterm_display,
645                                                list);
646                         ddrm = disp->data;
647
648                         if (ddrm->conn_id != res->connectors[i])
649                                 continue;
650
651                         disp->flags |= DISPLAY_AVAILABLE;
652                         if (!read_dpms || !display_is_online(disp))
653                                 break;
654
655                         dpms = uterm_drm_get_dpms(vdrm->fd, conn);
656                         if (dpms != disp->dpms) {
657                                 log_debug("DPMS state for display %p changed",
658                                           disp);
659                                 uterm_drm_display_set_dpms(disp, disp->dpms);
660                         }
661                         break;
662                 }
663
664                 if (iter == &video->displays)
665                         bind_display(video, res, conn);
666
667                 drmModeFreeConnector(conn);
668         }
669
670         drmModeFreeResources(res);
671
672         shl_dlist_for_each_safe(iter, tmp, &video->displays) {
673                 disp = shl_dlist_entry(iter, struct uterm_display, list);
674                 if (!(disp->flags & DISPLAY_AVAILABLE))
675                         uterm_display_unbind(disp);
676         }
677
678         video->flags &= ~VIDEO_HOTPLUG;
679         return 0;
680 }
681
682 int uterm_drm_video_wake_up(struct uterm_video *video)
683 {
684         int ret;
685         struct uterm_drm_video *vdrm = video->data;
686
687         ret = drmSetMaster(vdrm->fd);
688         if (ret) {
689                 log_err("cannot set DRM-master");
690                 return -EACCES;
691         }
692
693         video->flags |= VIDEO_AWAKE;
694         ret = uterm_drm_video_hotplug(video, true);
695         if (ret) {
696                 drmDropMaster(vdrm->fd);
697                 return ret;
698         }
699
700         return 0;
701 }
702
703 void uterm_drm_video_sleep(struct uterm_video *video)
704 {
705         struct uterm_drm_video *vdrm = video->data;
706
707         drmDropMaster(vdrm->fd);
708 }
709
710 int uterm_drm_video_poll(struct uterm_video *video)
711 {
712         video->flags |= VIDEO_HOTPLUG;
713         return uterm_drm_video_hotplug(video, false);
714 }
715
716 /* Waits for events on DRM fd for \mtimeout milliseconds and returns 0 if the
717  * timeout expired, -ERR on errors and 1 if a page-flip event has been read.
718  * \mtimeout is adjusted to the remaining time. */
719 int uterm_drm_video_wait_pflip(struct uterm_video *video,
720                                unsigned int *mtimeout)
721 {
722         struct uterm_drm_video *vdrm = video->data;
723         struct pollfd pfd;
724         int ret;
725         uint64_t elapsed;
726
727         shl_timer_start(vdrm->timer);
728
729         memset(&pfd, 0, sizeof(pfd));
730         pfd.fd = vdrm->fd;
731         pfd.events = POLLIN;
732
733         log_debug("waiting for pageflip on %p", video);
734         ret = poll(&pfd, 1, *mtimeout);
735
736         elapsed = shl_timer_stop(vdrm->timer);
737         *mtimeout = *mtimeout - (elapsed / 1000 + 1);
738
739         if (ret < 0) {
740                 log_error("poll() failed on DRM fd (%d): %m", errno);
741                 return -EFAULT;
742         } else if (!ret) {
743                 log_warning("timeout waiting for page-flip on %p", video);
744                 return 0;
745         } else if ((pfd.revents & POLLIN)) {
746                 ret = uterm_drm_video_read_events(video);
747                 if (ret)
748                         return ret;
749
750                 ret = ev_eloop_register_idle_cb(video->eloop, do_pflips,
751                                                 video, EV_ONESHOT | EV_SINGLE);
752                 if (ret)
753                         return ret;
754
755                 return 1;
756         } else {
757                 log_debug("poll() HUP/ERR on DRM fd (%d)", pfd.revents);
758                 return -EFAULT;
759         }
760 }