From f3c8c613ba93f622a8a15299b2a05838b470b923 Mon Sep 17 00:00:00 2001 From: Marcin Kurczewski Date: Fri, 17 Apr 2015 19:59:31 +0200 Subject: vo_drm: fix VT switching Fixes #1827 --- video/out/vo_drm.c | 284 ++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 229 insertions(+), 55 deletions(-) diff --git a/video/out/vo_drm.c b/video/out/vo_drm.c index f05e6eb940..2d820ba566 100644 --- a/video/out/vo_drm.c +++ b/video/out/vo_drm.c @@ -22,12 +22,19 @@ #include #include #include +#include +#include +#include #include +#include +#include #include #include #include #include +#include "osdep/io.h" +#include "osdep/timer.h" #include "common/msg.h" #include "sub/osd.h" #include "video/fmt-conversion.h" @@ -35,8 +42,15 @@ #include "video/sws_utils.h" #include "vo.h" +#define USE_MASTER 0 +#define EVT_RELEASE 1 +#define EVT_ACQUIRE 2 +#define EVT_INTERRUPT 255 #define BUF_COUNT 2 +static int setup_vo_crtc(struct vo *vo); +static void release_vo_crtc(struct vo *vo); + struct modeset_buf { uint32_t width; uint32_t height; @@ -55,11 +69,18 @@ struct modeset_dev { int front_buf; }; +struct vt_switcher { + int tty_fd; + struct vo *vo; +}; + struct priv { int fd; + struct vt_switcher vt_switcher; struct modeset_dev *dev; drmModeCrtc *old_crtc; + bool active; char *device_path; int connector_id; @@ -76,6 +97,8 @@ struct priv { static int modeset_open(struct vo *vo, int *out, const char *node) { + *out = -1; + int fd = open(node, O_RDWR | O_CLOEXEC); if (fd < 0) { MP_ERR(vo, "Cannot open \"%s\": %s.\n", node, mp_strerror(errno)); @@ -325,6 +348,162 @@ end: +static int vt_switcher_pipe[2]; + +static void vt_switcher_sighandler(int sig) +{ + unsigned char event = sig == SIGUSR1 ? EVT_RELEASE : EVT_ACQUIRE; + write(vt_switcher_pipe[1], &event, sizeof(event)); +} + +static void vt_switcher_interrupt(struct vt_switcher *s) +{ + unsigned char event = EVT_INTERRUPT; + write(vt_switcher_pipe[1], &event, sizeof(event)); +} + +static int vt_switcher_init(struct vt_switcher *s, struct vo *vo) +{ + s->tty_fd = -1; + vt_switcher_pipe[0] = -1; + vt_switcher_pipe[1] = -1; + + s->vo = vo; + if (mp_make_cloexec_pipe(vt_switcher_pipe)) { + MP_ERR(vo, "Creating pipe failed: %s", mp_strerror(errno)); + return -1; + } + + s->tty_fd = open("/dev/tty", O_RDWR | O_CLOEXEC); + if (s->tty_fd < 0) { + MP_ERR(vo, "Can't open TTY for VT control: %s", mp_strerror(errno)); + return -1; + } + + struct sigaction act; + act.sa_handler = vt_switcher_sighandler; + act.sa_flags = SA_RESTART; + sigemptyset(&act.sa_mask); + sigaction(SIGUSR1, &act, 0); + sigaction(SIGUSR2, &act, 0); + + struct vt_mode vt_mode; + if (ioctl(s->tty_fd, VT_GETMODE, &vt_mode) < 0) { + MP_ERR(vo, "VT_GETMODE failed: %s", mp_strerror(errno)); + return -1; + } + + vt_mode.mode = VT_PROCESS; + vt_mode.relsig = SIGUSR1; + vt_mode.acqsig = SIGUSR2; + if (ioctl(s->tty_fd, VT_SETMODE, &vt_mode) < 0) { + MP_ERR(vo, "VT_SETMODE failed: %s", mp_strerror(errno)); + return -1; + } + + return 0; +} + +static void vt_switcher_destroy(struct vt_switcher *s) +{ + close(s->tty_fd); + close(vt_switcher_pipe[0]); + close(vt_switcher_pipe[1]); +} + +static void vt_switcher_poll(struct vt_switcher *s, int timeout_ms) +{ + struct pollfd fds[1] = { + { .events = POLLIN, .fd = vt_switcher_pipe[0] }, + }; + poll(fds, 1, timeout_ms); + if (!fds[0].revents) return; + + unsigned char event; + if (read(fds[0].fd, &event, sizeof(event)) != sizeof(event)) return; + + switch (event) { + case EVT_RELEASE: + release_vo_crtc(s->vo); + if (USE_MASTER) { + //this function enables support for switching to x, weston etc. + //however, for whatever reason, it can be called only by root users. + //until things change, this is commented. + struct priv *p = s->vo->priv; + if (drmDropMaster(p->fd)) { + MP_WARN(s->vo, "Failed to drop DRM master: %s\n", mp_strerror(errno)); + } + } + + if (ioctl(s->tty_fd, VT_RELDISP, 1) < 0) { + MP_ERR(s->vo, "Failed to release virtual terminal\n"); + } + break; + + case EVT_ACQUIRE: + if (USE_MASTER) { + struct priv *p = s->vo->priv; + if (drmSetMaster(p->fd)) { + MP_WARN(s->vo, "Failed to acquire DRM master: %s\n", mp_strerror(errno)); + } + } + + setup_vo_crtc(s->vo); + if (ioctl(s->tty_fd, VT_RELDISP, VT_ACKACQ) < 0) { + MP_ERR(s->vo, "Failed to acquire virtual terminal\n"); + } + break; + + case EVT_INTERRUPT: + break; + } +} + + + +static int setup_vo_crtc(struct vo *vo) +{ + struct priv *p = vo->priv; + p->old_crtc = drmModeGetCrtc(p->fd, p->dev->crtc); + int ret = drmModeSetCrtc(p->fd, p->dev->crtc, + p->dev->bufs[p->dev->front_buf + BUF_COUNT - 1].fb, + 0, 0, &p->dev->conn, 1, &p->dev->mode); + p->active = true; + return ret; +} + +static void release_vo_crtc(struct vo *vo) +{ + struct priv *p = vo->priv; + p->active = false; + if (p->old_crtc) { + drmModeSetCrtc(p->fd, + p->old_crtc->crtc_id, + p->old_crtc->buffer_id, + p->old_crtc->x, + p->old_crtc->y, + &p->dev->conn, + 1, + &p->dev->mode); + drmModeFreeCrtc(p->old_crtc); + } +} + +static int wait_events(struct vo *vo, int64_t until_time_us) +{ + struct priv *p = vo->priv; + int64_t wait_us = until_time_us - mp_time_us(); + int timeout_ms = MPCLAMP((wait_us + 500) / 1000, 0, 10000); + vt_switcher_poll(&p->vt_switcher, timeout_ms); + return 0; +} + +static void wakeup(struct vo *vo) +{ + struct priv *p = vo->priv; + vt_switcher_interrupt(&p->vt_switcher); +} + static int reconfig(struct vo *vo, struct mp_image_params *params, int flags) { struct priv *p = vo->priv; @@ -375,23 +554,23 @@ static void draw_image(struct vo *vo, mp_image_t *mpi) { struct priv *p = vo->priv; - struct mp_rect src_rc = p->src; - src_rc.x0 = MP_ALIGN_DOWN(src_rc.x0, mpi->fmt.align_x); - src_rc.y0 = MP_ALIGN_DOWN(src_rc.y0, mpi->fmt.align_y); - mp_image_crop_rc(mpi, src_rc); - - mp_sws_scale(p->sws, p->cur_frame, mpi); - - osd_draw_on_image(vo->osd, p->osd, mpi ? mpi->pts : 0, 0, p->cur_frame); - - struct modeset_buf *front_buf = &p->dev->bufs[p->dev->front_buf]; - int32_t shift = (p->device_w * p->y + p->x) * 4; - memcpy_pic(front_buf->map + shift, - p->cur_frame->planes[0], - (p->dst.x1 - p->dst.x0) * 4, - p->dst.y1 - p->dst.y0, - p->device_w * 4, - p->cur_frame->stride[0]); + if (p->active) { + struct mp_rect src_rc = p->src; + src_rc.x0 = MP_ALIGN_DOWN(src_rc.x0, mpi->fmt.align_x); + src_rc.y0 = MP_ALIGN_DOWN(src_rc.y0, mpi->fmt.align_y); + mp_image_crop_rc(mpi, src_rc); + mp_sws_scale(p->sws, p->cur_frame, mpi); + osd_draw_on_image(vo->osd, p->osd, mpi ? mpi->pts : 0, 0, p->cur_frame); + + struct modeset_buf *front_buf = &p->dev->bufs[p->dev->front_buf]; + int32_t shift = (p->device_w * p->y + p->x) * 4; + memcpy_pic(front_buf->map + shift, + p->cur_frame->planes[0], + (p->dst.x1 - p->dst.x0) * 4, + p->dst.y1 - p->dst.y0, + p->device_w * 4, + p->cur_frame->stride[0]); + } if (mpi != p->last_input) { talloc_free(p->last_input); @@ -402,6 +581,7 @@ static void draw_image(struct vo *vo, mp_image_t *mpi) static void flip_page(struct vo *vo) { struct priv *p = vo->priv; + if (!p->active) return; int ret = drmModeSetCrtc(p->fd, p->dev->crtc, p->dev->bufs[p->dev->front_buf].fb, @@ -414,62 +594,54 @@ static void flip_page(struct vo *vo) } } +static void uninit(struct vo *vo) +{ + struct priv *p = vo->priv; + + if (p->dev) { + release_vo_crtc(vo); + + modeset_destroy_fb(p->fd, &p->dev->bufs[1]); + modeset_destroy_fb(p->fd, &p->dev->bufs[0]); + } + + vt_switcher_destroy(&p->vt_switcher); + talloc_free(p->last_input); + talloc_free(p->cur_frame); + talloc_free(p->dev); + close(p->fd); +} + static int preinit(struct vo *vo) { struct priv *p = vo->priv; p->sws = mp_sws_alloc(vo); + p->fd = -1; - int ret; + if (vt_switcher_init(&p->vt_switcher, vo)) + goto err; - ret = modeset_open(vo, &p->fd, p->device_path); - if (ret) - return -1; + if (modeset_open(vo, &p->fd, p->device_path)) + goto err; - ret = modeset_prepare_dev(vo, p->fd, p->connector_id, &p->dev); - if (ret) - return -1; + if (modeset_prepare_dev(vo, p->fd, p->connector_id, &p->dev)) + goto err; assert(p->dev); p->device_w = p->dev->bufs[0].width; p->device_h = p->dev->bufs[0].height; - p->old_crtc = drmModeGetCrtc(p->fd, p->dev->crtc); - ret = drmModeSetCrtc(p->fd, p->dev->crtc, - p->dev->bufs[p->dev->front_buf + BUF_COUNT - 1].fb, - 0, 0, &p->dev->conn, 1, &p->dev->mode); - if (ret) { + if (setup_vo_crtc(vo)) { MP_ERR(vo, "Cannot set CRTC for connector %u: %s\n", p->connector_id, mp_strerror(errno)); - return -1; + goto err; } return 0; -} -static void uninit(struct vo *vo) -{ - struct priv *p = vo->priv; - - if (p->dev) { - if (p->old_crtc) { - drmModeSetCrtc(p->fd, - p->old_crtc->crtc_id, - p->old_crtc->buffer_id, - p->old_crtc->x, - p->old_crtc->y, - &p->dev->conn, - 1, - &p->dev->mode); - drmModeFreeCrtc(p->old_crtc); - } - - modeset_destroy_fb(p->fd, &p->dev->bufs[1]); - modeset_destroy_fb(p->fd, &p->dev->bufs[0]); - } - - talloc_free(p->last_input); - talloc_free(p->cur_frame); - talloc_free(p->dev); +err: + uninit(vo); + return -1; } static int query_format(struct vo *vo, int format) @@ -500,6 +672,8 @@ const struct vo_driver video_out_drm = { .draw_image = draw_image, .flip_page = flip_page, .uninit = uninit, + .wait_events = wait_events, + .wakeup = wakeup, .priv_size = sizeof(struct priv), .options = (const struct m_option[]) { OPT_STRING("devpath", device_path, 0), -- cgit v1.2.3