uterm: drm: retry DRM wakeup after short timeout
[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 static void vt_timeout(struct ev_timer *timer, uint64_t exp, void *data)
465 {
466         struct uterm_video *video = data;
467         struct uterm_drm_video *vdrm = video->data;
468         struct uterm_display *disp;
469         struct shl_dlist *iter;
470         int r;
471
472         r = uterm_drm_video_wake_up(video);
473         if (!r) {
474                 ev_timer_update(vdrm->vt_timer, NULL);
475                 shl_dlist_for_each(iter, &video->displays) {
476                         disp = shl_dlist_entry(iter, struct uterm_display, list);
477                         VIDEO_CB(video, disp, UTERM_REFRESH);
478                 }
479         }
480 }
481
482 void uterm_drm_video_arm_vt_timer(struct uterm_video *video)
483 {
484         struct uterm_drm_video *vdrm = video->data;
485         struct itimerspec spec;
486
487         spec.it_value.tv_sec = 0;
488         spec.it_value.tv_nsec = 20L * 1000L * 1000L; /* 20ms */
489         spec.it_interval = spec.it_value;
490
491         ev_timer_update(vdrm->vt_timer, &spec);
492 }
493
494 int uterm_drm_video_init(struct uterm_video *video, const char *node,
495                          const struct display_ops *display_ops,
496                          uterm_drm_page_flip_t pflip, void *data)
497 {
498         struct uterm_drm_video *vdrm;
499         int ret;
500
501         log_info("new drm device via %s", node);
502
503         vdrm = malloc(sizeof(*vdrm));
504         if (!vdrm)
505                 return -ENOMEM;
506         memset(vdrm, 0, sizeof(*vdrm));
507         video->data = vdrm;
508         vdrm->data = data;
509         vdrm->page_flip = pflip;
510         vdrm->display_ops = display_ops;
511
512         vdrm->fd = open(node, O_RDWR | O_CLOEXEC | O_NONBLOCK);
513         if (vdrm->fd < 0) {
514                 log_err("cannot open drm device %s (%d): %m", node, errno);
515                 ret = -EFAULT;
516                 goto err_free;
517         }
518         /* TODO: fix the race-condition with DRM-Master-on-open */
519         drmDropMaster(vdrm->fd);
520
521         ret = ev_eloop_new_fd(video->eloop, &vdrm->efd, vdrm->fd, EV_READABLE,
522                               io_event, video);
523         if (ret)
524                 goto err_close;
525
526         ret = shl_timer_new(&vdrm->timer);
527         if (ret)
528                 goto err_fd;
529
530         ret = ev_eloop_new_timer(video->eloop, &vdrm->vt_timer, NULL,
531                                  vt_timeout, video);
532         if (ret)
533                 goto err_timer;
534
535         video->flags |= VIDEO_HOTPLUG;
536         return 0;
537
538 err_timer:
539         shl_timer_free(vdrm->timer);
540 err_fd:
541         ev_eloop_rm_fd(vdrm->efd);
542 err_close:
543         close(vdrm->fd);
544 err_free:
545         free(vdrm);
546         return ret;
547 }
548
549 void uterm_drm_video_destroy(struct uterm_video *video)
550 {
551         struct uterm_drm_video *vdrm = video->data;
552
553         ev_eloop_rm_timer(vdrm->vt_timer);
554         ev_eloop_unregister_idle_cb(video->eloop, do_pflips, video, EV_SINGLE);
555         shl_timer_free(vdrm->timer);
556         ev_eloop_rm_fd(vdrm->efd);
557         close(vdrm->fd);
558         free(video->data);
559 }
560
561 int uterm_drm_video_find_crtc(struct uterm_video *video, drmModeRes *res,
562                               drmModeEncoder *enc)
563 {
564         int i, crtc;
565         struct uterm_display *iter;
566         struct uterm_drm_display *ddrm;
567         struct shl_dlist *it;
568
569         for (i = 0; i < res->count_crtcs; ++i) {
570                 if (enc->possible_crtcs & (1 << i)) {
571                         crtc = res->crtcs[i];
572                         shl_dlist_for_each(it, &video->displays) {
573                                 iter = shl_dlist_entry(it,
574                                                        struct uterm_display,
575                                                        list);
576                                 ddrm = iter->data;
577                                 if (ddrm->crtc_id == crtc)
578                                         break;
579                         }
580                         if (it == &video->displays)
581                                 return crtc;
582                 }
583         }
584
585         return -1;
586 }
587
588 static void bind_display(struct uterm_video *video, drmModeRes *res,
589                          drmModeConnector *conn)
590 {
591         struct uterm_drm_video *vdrm = video->data;
592         struct uterm_display *disp;
593         struct uterm_drm_display *ddrm;
594         struct uterm_mode *mode;
595         int ret, i;
596
597         ret = display_new(&disp, vdrm->display_ops);
598         if (ret)
599                 return;
600         ddrm = disp->data;
601
602         for (i = 0; i < conn->count_modes; ++i) {
603                 ret = mode_new(&mode, &uterm_drm_mode_ops);
604                 if (ret)
605                         continue;
606
607                 uterm_drm_mode_set(mode, &conn->modes[i]);
608
609                 ret = uterm_mode_bind(mode, disp);
610                 if (ret) {
611                         uterm_mode_unref(mode);
612                         continue;
613                 }
614
615                 /* TODO: more sophisticated default-mode selection */
616                 if (!disp->default_mode)
617                         disp->default_mode = mode;
618
619                 uterm_mode_unref(mode);
620         }
621
622         if (shl_dlist_empty(&disp->modes)) {
623                 log_warn("no valid mode for display found");
624                 ret = -EFAULT;
625                 goto err_unref;
626         }
627
628         ddrm->conn_id = conn->connector_id;
629         disp->flags |= DISPLAY_AVAILABLE;
630         disp->dpms = uterm_drm_get_dpms(vdrm->fd, conn);
631
632         log_info("display %p DPMS is %s", disp,
633                  uterm_dpms_to_name(disp->dpms));
634
635         ret = uterm_display_bind(disp, video);
636         if (ret)
637                 goto err_unref;
638
639         uterm_display_unref(disp);
640         return;
641
642 err_unref:
643         uterm_display_unref(disp);
644         return;
645 }
646
647 int uterm_drm_video_hotplug(struct uterm_video *video,
648                             bool read_dpms)
649 {
650         struct uterm_drm_video *vdrm = video->data;
651         drmModeRes *res;
652         drmModeConnector *conn;
653         struct uterm_display *disp;
654         struct uterm_drm_display *ddrm;
655         int i, dpms;
656         struct shl_dlist *iter, *tmp;
657
658         if (!video_is_awake(video) || !video_need_hotplug(video))
659                 return 0;
660
661         res = drmModeGetResources(vdrm->fd);
662         if (!res) {
663                 log_err("cannot retrieve drm resources");
664                 return -EACCES;
665         }
666
667         shl_dlist_for_each(iter, &video->displays) {
668                 disp = shl_dlist_entry(iter, struct uterm_display, list);
669                 disp->flags &= ~DISPLAY_AVAILABLE;
670         }
671
672         for (i = 0; i < res->count_connectors; ++i) {
673                 conn = drmModeGetConnector(vdrm->fd, res->connectors[i]);
674                 if (!conn)
675                         continue;
676                 if (conn->connection != DRM_MODE_CONNECTED) {
677                         drmModeFreeConnector(conn);
678                         continue;
679                 }
680
681                 shl_dlist_for_each(iter, &video->displays) {
682                         disp = shl_dlist_entry(iter, struct uterm_display,
683                                                list);
684                         ddrm = disp->data;
685
686                         if (ddrm->conn_id != res->connectors[i])
687                                 continue;
688
689                         disp->flags |= DISPLAY_AVAILABLE;
690                         if (!read_dpms || !display_is_online(disp))
691                                 break;
692
693                         dpms = uterm_drm_get_dpms(vdrm->fd, conn);
694                         if (dpms != disp->dpms) {
695                                 log_debug("DPMS state for display %p changed",
696                                           disp);
697                                 uterm_drm_display_set_dpms(disp, disp->dpms);
698                         }
699                         break;
700                 }
701
702                 if (iter == &video->displays)
703                         bind_display(video, res, conn);
704
705                 drmModeFreeConnector(conn);
706         }
707
708         drmModeFreeResources(res);
709
710         shl_dlist_for_each_safe(iter, tmp, &video->displays) {
711                 disp = shl_dlist_entry(iter, struct uterm_display, list);
712                 if (!(disp->flags & DISPLAY_AVAILABLE))
713                         uterm_display_unbind(disp);
714         }
715
716         video->flags &= ~VIDEO_HOTPLUG;
717         return 0;
718 }
719
720 int uterm_drm_video_wake_up(struct uterm_video *video)
721 {
722         int ret;
723         struct uterm_drm_video *vdrm = video->data;
724
725         ret = drmSetMaster(vdrm->fd);
726         if (ret) {
727                 log_err("cannot set DRM-master");
728                 return -EACCES;
729         }
730
731         video->flags |= VIDEO_AWAKE;
732         ret = uterm_drm_video_hotplug(video, true);
733         if (ret) {
734                 drmDropMaster(vdrm->fd);
735                 return ret;
736         }
737
738         return 0;
739 }
740
741 void uterm_drm_video_sleep(struct uterm_video *video)
742 {
743         struct uterm_drm_video *vdrm = video->data;
744
745         drmDropMaster(vdrm->fd);
746         ev_timer_drain(vdrm->vt_timer, NULL);
747         ev_timer_update(vdrm->vt_timer, NULL);
748 }
749
750 int uterm_drm_video_poll(struct uterm_video *video)
751 {
752         video->flags |= VIDEO_HOTPLUG;
753         return uterm_drm_video_hotplug(video, false);
754 }
755
756 /* Waits for events on DRM fd for \mtimeout milliseconds and returns 0 if the
757  * timeout expired, -ERR on errors and 1 if a page-flip event has been read.
758  * \mtimeout is adjusted to the remaining time. */
759 int uterm_drm_video_wait_pflip(struct uterm_video *video,
760                                unsigned int *mtimeout)
761 {
762         struct uterm_drm_video *vdrm = video->data;
763         struct pollfd pfd;
764         int ret;
765         uint64_t elapsed;
766
767         shl_timer_start(vdrm->timer);
768
769         memset(&pfd, 0, sizeof(pfd));
770         pfd.fd = vdrm->fd;
771         pfd.events = POLLIN;
772
773         log_debug("waiting for pageflip on %p", video);
774         ret = poll(&pfd, 1, *mtimeout);
775
776         elapsed = shl_timer_stop(vdrm->timer);
777         *mtimeout = *mtimeout - (elapsed / 1000 + 1);
778
779         if (ret < 0) {
780                 log_error("poll() failed on DRM fd (%d): %m", errno);
781                 return -EFAULT;
782         } else if (!ret) {
783                 log_warning("timeout waiting for page-flip on %p", video);
784                 return 0;
785         } else if ((pfd.revents & POLLIN)) {
786                 ret = uterm_drm_video_read_events(video);
787                 if (ret)
788                         return ret;
789
790                 ret = ev_eloop_register_idle_cb(video->eloop, do_pflips,
791                                                 video, EV_ONESHOT | EV_SINGLE);
792                 if (ret)
793                         return ret;
794
795                 return 1;
796         } else {
797                 log_debug("poll() HUP/ERR on DRM fd (%d)", pfd.revents);
798                 return -EFAULT;
799         }
800 }