2 * uterm - Linux User-Space Terminal
4 * Copyright (c) 2011-2013 David Herrmann <dh.herrmann@googlemail.com>
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:
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
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.
27 * DRM shared functions
38 #include <xf86drmMode.h>
40 #include "shl_timer.h"
41 #include "uterm_drm_shared_internal.h"
42 #include "uterm_video.h"
43 #include "uterm_video_internal.h"
45 #define LOG_SUBSYSTEM "drm_shared"
47 int uterm_drm_mode_init(struct uterm_mode *mode)
49 struct uterm_drm_mode *m;
51 m = malloc(sizeof(*m));
54 memset(m, 0, sizeof(*m));
60 void uterm_drm_mode_destroy(struct uterm_mode *mode)
65 const char *uterm_drm_mode_get_name(const struct uterm_mode *mode)
67 struct uterm_drm_mode *m = mode->data;
72 unsigned int uterm_drm_mode_get_width(const struct uterm_mode *mode)
74 struct uterm_drm_mode *m = mode->data;
76 return m->info.hdisplay;
79 unsigned int uterm_drm_mode_get_height(const struct uterm_mode *mode)
81 struct uterm_drm_mode *m = mode->data;
83 return m->info.vdisplay;
86 void uterm_drm_mode_set(struct uterm_mode *mode, drmModeModeInfo *info)
88 struct uterm_drm_mode *m = mode->data;
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,
101 int uterm_drm_set_dpms(int fd, uint32_t conn_id, int state)
104 drmModeConnector *conn;
105 drmModePropertyRes *prop;
109 set = DRM_MODE_DPMS_ON;
111 case UTERM_DPMS_STANDBY:
112 set = DRM_MODE_DPMS_STANDBY;
114 case UTERM_DPMS_SUSPEND:
115 set = DRM_MODE_DPMS_SUSPEND;
118 set = DRM_MODE_DPMS_OFF;
124 conn = drmModeGetConnector(fd, conn_id);
126 log_err("cannot get display connector");
131 for (i = 0; i < conn->count_props; ++i) {
132 prop = drmModeGetProperty(fd, conn->props[i]);
134 log_error("cannot get DRM property (%d): %m", errno);
138 if (!strcmp(prop->name, "DPMS")) {
139 ret = drmModeConnectorSetProperty(fd, conn_id,
142 log_info("cannot set DPMS");
145 drmModeFreeProperty(prop);
148 drmModeFreeProperty(prop);
151 if (i == conn->count_props) {
152 log_warn("display does not support DPMS");
153 ret = UTERM_DPMS_UNKNOWN;
156 drmModeFreeConnector(conn);
160 int uterm_drm_get_dpms(int fd, drmModeConnector *conn)
163 drmModePropertyRes *prop;
165 for (i = 0; i < conn->count_props; ++i) {
166 prop = drmModeGetProperty(fd, conn->props[i]);
168 log_error("cannot get DRM property (%d): %m", errno);
172 if (!strcmp(prop->name, "DPMS")) {
173 switch (conn->prop_values[i]) {
174 case DRM_MODE_DPMS_ON:
177 case DRM_MODE_DPMS_STANDBY:
178 ret = UTERM_DPMS_STANDBY;
180 case DRM_MODE_DPMS_SUSPEND:
181 ret = UTERM_DPMS_SUSPEND;
183 case DRM_MODE_DPMS_OFF:
185 ret = UTERM_DPMS_OFF;
188 drmModeFreeProperty(prop);
191 drmModeFreeProperty(prop);
194 if (i == conn->count_props)
195 log_warn("display does not support DPMS");
196 return UTERM_DPMS_UNKNOWN;
199 int uterm_drm_display_init(struct uterm_display *disp, void *data)
201 struct uterm_drm_display *d;
203 d = malloc(sizeof(*d));
206 memset(d, 0, sizeof(*d));
213 void uterm_drm_display_destroy(struct uterm_display *disp)
218 int uterm_drm_display_activate(struct uterm_display *disp, int fd)
220 struct uterm_video *video = disp->video;
221 struct uterm_drm_display *ddrm = disp->data;
223 drmModeConnector *conn;
227 res = drmModeGetResources(fd);
229 log_err("cannot get resources for display %p", disp);
232 conn = drmModeGetConnector(fd, ddrm->conn_id);
234 log_err("cannot get connector for display %p", disp);
235 drmModeFreeResources(res);
240 for (i = 0; i < conn->count_encoders; ++i) {
241 enc = drmModeGetEncoder(fd, conn->encoders[i]);
244 crtc = uterm_drm_video_find_crtc(video, res, enc);
245 drmModeFreeEncoder(enc);
250 drmModeFreeConnector(conn);
251 drmModeFreeResources(res);
254 log_warn("cannot find crtc for new display");
258 ddrm->crtc_id = crtc;
259 if (ddrm->saved_crtc)
260 drmModeFreeCrtc(ddrm->saved_crtc);
261 ddrm->saved_crtc = drmModeGetCrtc(fd, ddrm->crtc_id);
266 void uterm_drm_display_deactivate(struct uterm_display *disp, int fd)
268 struct uterm_drm_display *ddrm = disp->data;
270 uterm_drm_display_wait_pflip(disp);
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,
279 &ddrm->saved_crtc->mode);
281 drmModeFreeCrtc(ddrm->saved_crtc);
282 ddrm->saved_crtc = NULL;
286 disp->flags &= ~(DISPLAY_VSYNC | DISPLAY_ONLINE | DISPLAY_PFLIP);
289 int uterm_drm_display_set_dpms(struct uterm_display *disp, int state)
292 struct uterm_drm_display *ddrm = disp->data;
293 struct uterm_drm_video *vdrm = disp->video->data;
295 log_info("setting DPMS of display %p to %s", disp,
296 uterm_dpms_to_name(state));
298 ret = uterm_drm_set_dpms(vdrm->fd, ddrm->conn_id, state);
306 int uterm_drm_display_wait_pflip(struct uterm_display *disp)
308 struct uterm_video *video = disp->video;
310 unsigned int timeout = 1000; /* 1s */
312 if ((disp->flags & DISPLAY_PFLIP) || !(disp->flags & DISPLAY_VSYNC))
316 ret = uterm_drm_video_wait_pflip(video, &timeout);
319 else if ((disp->flags & DISPLAY_PFLIP))
321 } while (timeout > 0);
325 if (ret == 0 || !timeout) {
326 log_warning("timeout waiting for page-flip on display %p",
334 int uterm_drm_display_swap(struct uterm_display *disp, uint32_t fb,
337 struct uterm_drm_display *ddrm = disp->data;
338 struct uterm_video *video = disp->video;
339 struct uterm_drm_video *vdrm = video->data;
341 drmModeModeInfo *mode;
343 if (disp->dpms != UTERM_DPMS_ON)
347 ret = uterm_drm_display_wait_pflip(disp);
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);
355 log_error("cannot set DRM-CRTC (%d): %m", errno);
359 if ((disp->flags & DISPLAY_VSYNC))
362 ret = drmModePageFlip(vdrm->fd, ddrm->crtc_id, fb,
363 DRM_MODE_PAGE_FLIP_EVENT, disp);
365 log_error("cannot page-flip on DRM-CRTC (%d): %m",
370 uterm_display_ref(disp);
371 disp->flags |= DISPLAY_VSYNC;
377 static void uterm_drm_display_pflip(struct uterm_display *disp)
379 struct uterm_drm_video *vdrm = disp->video->data;
381 disp->flags &= ~(DISPLAY_PFLIP | DISPLAY_VSYNC);
383 vdrm->page_flip(disp);
385 DISPLAY_CB(disp, UTERM_PAGE_FLIP);
388 static void display_event(int fd, unsigned int frame, unsigned int sec,
389 unsigned int usec, void *data)
391 struct uterm_display *disp = data;
393 if (disp->video && (disp->flags & DISPLAY_VSYNC))
394 disp->flags |= DISPLAY_PFLIP;
396 uterm_display_unref(disp);
399 static int uterm_drm_video_read_events(struct uterm_video *video)
401 struct uterm_drm_video *vdrm = video->data;
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;
413 ret = drmHandleEvent(vdrm->fd, &ev);
415 if (ret < 0 && errno != EAGAIN)
421 static void do_pflips(struct ev_eloop *eloop, void *unused, void *data)
423 struct uterm_video *video = data;
424 struct uterm_display *disp;
425 struct shl_dlist *iter;
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);
434 static void io_event(struct ev_fd *fd, int mask, void *data)
436 struct uterm_video *video = data;
437 struct uterm_drm_video *vdrm = video->data;
438 struct uterm_display *disp;
439 struct shl_dlist *iter;
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);
450 if (!(mask & EV_READABLE))
453 ret = uterm_drm_video_read_events(video);
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);
464 static void vt_timeout(struct ev_timer *timer, uint64_t exp, void *data)
466 struct uterm_video *video = data;
467 struct uterm_drm_video *vdrm = video->data;
468 struct uterm_display *disp;
469 struct shl_dlist *iter;
472 r = uterm_drm_video_wake_up(video);
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);
482 void uterm_drm_video_arm_vt_timer(struct uterm_video *video)
484 struct uterm_drm_video *vdrm = video->data;
485 struct itimerspec spec;
487 spec.it_value.tv_sec = 0;
488 spec.it_value.tv_nsec = 20L * 1000L * 1000L; /* 20ms */
489 spec.it_interval = spec.it_value;
491 ev_timer_update(vdrm->vt_timer, &spec);
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)
498 struct uterm_drm_video *vdrm;
501 log_info("new drm device via %s", node);
503 vdrm = malloc(sizeof(*vdrm));
506 memset(vdrm, 0, sizeof(*vdrm));
509 vdrm->page_flip = pflip;
510 vdrm->display_ops = display_ops;
512 vdrm->fd = open(node, O_RDWR | O_CLOEXEC | O_NONBLOCK);
514 log_err("cannot open drm device %s (%d): %m", node, errno);
518 /* TODO: fix the race-condition with DRM-Master-on-open */
519 drmDropMaster(vdrm->fd);
521 ret = ev_eloop_new_fd(video->eloop, &vdrm->efd, vdrm->fd, EV_READABLE,
526 ret = shl_timer_new(&vdrm->timer);
530 ret = ev_eloop_new_timer(video->eloop, &vdrm->vt_timer, NULL,
535 video->flags |= VIDEO_HOTPLUG;
539 shl_timer_free(vdrm->timer);
541 ev_eloop_rm_fd(vdrm->efd);
549 void uterm_drm_video_destroy(struct uterm_video *video)
551 struct uterm_drm_video *vdrm = video->data;
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);
561 int uterm_drm_video_find_crtc(struct uterm_video *video, drmModeRes *res,
565 struct uterm_display *iter;
566 struct uterm_drm_display *ddrm;
567 struct shl_dlist *it;
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,
577 if (ddrm->crtc_id == crtc)
580 if (it == &video->displays)
588 static void bind_display(struct uterm_video *video, drmModeRes *res,
589 drmModeConnector *conn)
591 struct uterm_drm_video *vdrm = video->data;
592 struct uterm_display *disp;
593 struct uterm_drm_display *ddrm;
594 struct uterm_mode *mode;
597 ret = display_new(&disp, vdrm->display_ops);
602 for (i = 0; i < conn->count_modes; ++i) {
603 ret = mode_new(&mode, &uterm_drm_mode_ops);
607 uterm_drm_mode_set(mode, &conn->modes[i]);
609 ret = uterm_mode_bind(mode, disp);
611 uterm_mode_unref(mode);
615 /* TODO: more sophisticated default-mode selection */
616 if (!disp->default_mode)
617 disp->default_mode = mode;
619 uterm_mode_unref(mode);
622 if (shl_dlist_empty(&disp->modes)) {
623 log_warn("no valid mode for display found");
628 ddrm->conn_id = conn->connector_id;
629 disp->flags |= DISPLAY_AVAILABLE;
630 disp->dpms = uterm_drm_get_dpms(vdrm->fd, conn);
632 log_info("display %p DPMS is %s", disp,
633 uterm_dpms_to_name(disp->dpms));
635 ret = uterm_display_bind(disp, video);
639 uterm_display_unref(disp);
643 uterm_display_unref(disp);
647 int uterm_drm_video_hotplug(struct uterm_video *video,
648 bool read_dpms, bool modeset)
650 struct uterm_drm_video *vdrm = video->data;
652 drmModeConnector *conn;
653 struct uterm_display *disp;
654 struct uterm_drm_display *ddrm;
656 struct shl_dlist *iter, *tmp;
658 if (!video_is_awake(video) || !video_need_hotplug(video))
661 log_debug("testing DRM hotplug status");
663 res = drmModeGetResources(vdrm->fd);
665 log_err("cannot retrieve drm resources");
669 shl_dlist_for_each(iter, &video->displays) {
670 disp = shl_dlist_entry(iter, struct uterm_display, list);
671 disp->flags &= ~DISPLAY_AVAILABLE;
674 for (i = 0; i < res->count_connectors; ++i) {
675 conn = drmModeGetConnector(vdrm->fd, res->connectors[i]);
678 if (conn->connection != DRM_MODE_CONNECTED) {
679 drmModeFreeConnector(conn);
683 shl_dlist_for_each(iter, &video->displays) {
684 disp = shl_dlist_entry(iter, struct uterm_display,
688 if (ddrm->conn_id != res->connectors[i])
691 disp->flags |= DISPLAY_AVAILABLE;
692 if (!display_is_online(disp))
696 dpms = uterm_drm_get_dpms(vdrm->fd, conn);
697 if (dpms != disp->dpms) {
698 log_debug("DPMS state for display %p changed",
700 uterm_drm_display_set_dpms(disp, disp->dpms);
705 log_debug("re-activate display %p", disp);
706 uterm_display_swap(disp, true);
712 if (iter == &video->displays)
713 bind_display(video, res, conn);
715 drmModeFreeConnector(conn);
718 drmModeFreeResources(res);
720 shl_dlist_for_each_safe(iter, tmp, &video->displays) {
721 disp = shl_dlist_entry(iter, struct uterm_display, list);
722 if (!(disp->flags & DISPLAY_AVAILABLE))
723 uterm_display_unbind(disp);
726 video->flags &= ~VIDEO_HOTPLUG;
730 int uterm_drm_video_wake_up(struct uterm_video *video)
733 struct uterm_drm_video *vdrm = video->data;
735 ret = drmSetMaster(vdrm->fd);
737 log_err("cannot set DRM-master");
741 video->flags |= VIDEO_AWAKE | VIDEO_HOTPLUG;
742 ret = uterm_drm_video_hotplug(video, true, true);
744 drmDropMaster(vdrm->fd);
751 void uterm_drm_video_sleep(struct uterm_video *video)
753 struct uterm_drm_video *vdrm = video->data;
755 drmDropMaster(vdrm->fd);
756 ev_timer_drain(vdrm->vt_timer, NULL);
757 ev_timer_update(vdrm->vt_timer, NULL);
760 int uterm_drm_video_poll(struct uterm_video *video)
762 video->flags |= VIDEO_HOTPLUG;
763 return uterm_drm_video_hotplug(video, false, false);
766 /* Waits for events on DRM fd for \mtimeout milliseconds and returns 0 if the
767 * timeout expired, -ERR on errors and 1 if a page-flip event has been read.
768 * \mtimeout is adjusted to the remaining time. */
769 int uterm_drm_video_wait_pflip(struct uterm_video *video,
770 unsigned int *mtimeout)
772 struct uterm_drm_video *vdrm = video->data;
777 shl_timer_start(vdrm->timer);
779 memset(&pfd, 0, sizeof(pfd));
783 log_debug("waiting for pageflip on %p", video);
784 ret = poll(&pfd, 1, *mtimeout);
786 elapsed = shl_timer_stop(vdrm->timer);
787 *mtimeout = *mtimeout - (elapsed / 1000 + 1);
790 log_error("poll() failed on DRM fd (%d): %m", errno);
793 log_warning("timeout waiting for page-flip on %p", video);
795 } else if ((pfd.revents & POLLIN)) {
796 ret = uterm_drm_video_read_events(video);
800 ret = ev_eloop_register_idle_cb(video->eloop, do_pflips,
801 video, EV_ONESHOT | EV_SINGLE);
807 log_debug("poll() HUP/ERR on DRM fd (%d)", pfd.revents);