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 int uterm_drm_video_init(struct uterm_video *video, const char *node,
465 uterm_drm_page_flip_t pflip, void *data)
467 struct uterm_drm_video *vdrm;
470 log_info("new drm device via %s", node);
472 vdrm = malloc(sizeof(*vdrm));
475 memset(vdrm, 0, sizeof(*vdrm));
478 vdrm->page_flip = pflip;
480 vdrm->fd = open(node, O_RDWR | O_CLOEXEC | O_NONBLOCK);
482 log_err("cannot open drm device %s (%d): %m", node, errno);
486 /* TODO: fix the race-condition with DRM-Master-on-open */
487 drmDropMaster(vdrm->fd);
489 ret = ev_eloop_new_fd(video->eloop, &vdrm->efd, vdrm->fd, EV_READABLE,
494 ret = shl_timer_new(&vdrm->timer);
498 video->flags |= VIDEO_HOTPLUG;
502 ev_eloop_rm_fd(vdrm->efd);
510 void uterm_drm_video_destroy(struct uterm_video *video)
512 struct uterm_drm_video *vdrm = video->data;
514 ev_eloop_unregister_idle_cb(video->eloop, do_pflips, video, EV_SINGLE);
515 shl_timer_free(vdrm->timer);
516 ev_eloop_rm_fd(vdrm->efd);
521 int uterm_drm_video_find_crtc(struct uterm_video *video, drmModeRes *res,
525 struct uterm_display *iter;
526 struct uterm_drm_display *ddrm;
527 struct shl_dlist *it;
529 for (i = 0; i < res->count_crtcs; ++i) {
530 if (enc->possible_crtcs & (1 << i)) {
531 crtc = res->crtcs[i];
532 shl_dlist_for_each(it, &video->displays) {
533 iter = shl_dlist_entry(it,
534 struct uterm_display,
537 if (ddrm->crtc_id == crtc)
540 if (it == &video->displays)
548 static void bind_display(struct uterm_video *video, drmModeRes *res,
549 drmModeConnector *conn,
550 const struct display_ops *ops)
552 struct uterm_drm_video *vdrm = video->data;
553 struct uterm_display *disp;
554 struct uterm_drm_display *ddrm;
555 struct uterm_mode *mode;
558 ret = display_new(&disp, ops);
563 for (i = 0; i < conn->count_modes; ++i) {
564 ret = mode_new(&mode, &uterm_drm_mode_ops);
568 uterm_drm_mode_set(mode, &conn->modes[i]);
570 ret = uterm_mode_bind(mode, disp);
572 uterm_mode_unref(mode);
576 /* TODO: more sophisticated default-mode selection */
577 if (!disp->default_mode)
578 disp->default_mode = mode;
580 uterm_mode_unref(mode);
583 if (shl_dlist_empty(&disp->modes)) {
584 log_warn("no valid mode for display found");
589 ddrm->conn_id = conn->connector_id;
590 disp->flags |= DISPLAY_AVAILABLE;
591 disp->dpms = uterm_drm_get_dpms(vdrm->fd, conn);
593 log_info("display %p DPMS is %s", disp,
594 uterm_dpms_to_name(disp->dpms));
596 ret = uterm_display_bind(disp, video);
600 uterm_display_unref(disp);
604 uterm_display_unref(disp);
608 int uterm_drm_video_hotplug(struct uterm_video *video,
609 const struct display_ops *ops,
612 struct uterm_drm_video *vdrm = video->data;
614 drmModeConnector *conn;
615 struct uterm_display *disp;
616 struct uterm_drm_display *ddrm;
618 struct shl_dlist *iter, *tmp;
620 if (!video_is_awake(video) || !video_need_hotplug(video))
623 res = drmModeGetResources(vdrm->fd);
625 log_err("cannot retrieve drm resources");
629 shl_dlist_for_each(iter, &video->displays) {
630 disp = shl_dlist_entry(iter, struct uterm_display, list);
631 disp->flags &= ~DISPLAY_AVAILABLE;
634 for (i = 0; i < res->count_connectors; ++i) {
635 conn = drmModeGetConnector(vdrm->fd, res->connectors[i]);
638 if (conn->connection != DRM_MODE_CONNECTED) {
639 drmModeFreeConnector(conn);
643 shl_dlist_for_each(iter, &video->displays) {
644 disp = shl_dlist_entry(iter, struct uterm_display,
648 if (ddrm->conn_id != res->connectors[i])
651 disp->flags |= DISPLAY_AVAILABLE;
652 if (!read_dpms || !display_is_online(disp))
655 dpms = uterm_drm_get_dpms(vdrm->fd, conn);
656 if (dpms != disp->dpms) {
657 log_debug("DPMS state for display %p changed",
659 uterm_drm_display_set_dpms(disp, disp->dpms);
664 if (iter == &video->displays)
665 bind_display(video, res, conn, ops);
667 drmModeFreeConnector(conn);
670 drmModeFreeResources(res);
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);
678 video->flags &= ~VIDEO_HOTPLUG;
682 int uterm_drm_video_wake_up(struct uterm_video *video,
683 const struct display_ops *ops)
686 struct uterm_drm_video *vdrm = video->data;
688 ret = drmSetMaster(vdrm->fd);
690 log_err("cannot set DRM-master");
694 video->flags |= VIDEO_AWAKE;
695 ret = uterm_drm_video_hotplug(video, ops, true);
697 drmDropMaster(vdrm->fd);
704 void uterm_drm_video_sleep(struct uterm_video *video)
706 struct uterm_drm_video *vdrm = video->data;
708 drmDropMaster(vdrm->fd);
711 int uterm_drm_video_poll(struct uterm_video *video,
712 const struct display_ops *ops)
714 video->flags |= VIDEO_HOTPLUG;
715 return uterm_drm_video_hotplug(video, ops, false);
718 /* Waits for events on DRM fd for \mtimeout milliseconds and returns 0 if the
719 * timeout expired, -ERR on errors and 1 if a page-flip event has been read.
720 * \mtimeout is adjusted to the remaining time. */
721 int uterm_drm_video_wait_pflip(struct uterm_video *video,
722 unsigned int *mtimeout)
724 struct uterm_drm_video *vdrm = video->data;
729 shl_timer_start(vdrm->timer);
731 memset(&pfd, 0, sizeof(pfd));
735 log_debug("waiting for pageflip on %p", video);
736 ret = poll(&pfd, 1, *mtimeout);
738 elapsed = shl_timer_stop(vdrm->timer);
739 *mtimeout = *mtimeout - (elapsed / 1000 + 1);
742 log_error("poll() failed on DRM fd (%d): %m", errno);
745 log_warning("timeout waiting for page-flip on %p", video);
747 } else if ((pfd.revents & POLLIN)) {
748 ret = uterm_drm_video_read_events(video);
752 ret = ev_eloop_register_idle_cb(video->eloop, do_pflips,
753 video, EV_ONESHOT | EV_SINGLE);
759 log_debug("poll() HUP/ERR on DRM fd (%d)", pfd.revents);