diff options
Diffstat (limited to 'video/out/drm_common.c')
-rw-r--r-- | video/out/drm_common.c | 250 |
1 files changed, 233 insertions, 17 deletions
diff --git a/video/out/drm_common.c b/video/out/drm_common.c index 1ea0e45ae8..3462a5d4de 100644 --- a/video/out/drm_common.c +++ b/video/out/drm_common.c @@ -1,8 +1,6 @@ /* * This file is part of mpv. * - * by rr- <rr-@sakuya.pl> - * * mpv is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -15,6 +13,11 @@ * * You should have received a copy of the GNU General Public License along * with mpv. If not, see <http://www.gnu.org/licenses/>. + * + * You can alternatively redistribute this file and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. */ #include <errno.h> @@ -35,16 +38,216 @@ #define EVT_INTERRUPT 255 #define HANDLER_ACQUIRE 0 #define HANDLER_RELEASE 1 +#define RELEASE_SIGNAL SIGUSR1 +#define ACQUIRE_SIGNAL SIGUSR2 static int vt_switcher_pipe[2]; +// KMS ------------------------------------------------------------------------ + +static bool is_connector_valid(struct kms *kms, int connector_id, + drmModeConnector *connector, bool silent) +{ + if (!connector) { + if (!silent) { + MP_ERR(kms, "Cannot get connector %d: %s\n", connector_id, + mp_strerror(errno)); + } + return false; + } + + if (connector->connection != DRM_MODE_CONNECTED) { + if (!silent) { + MP_ERR(kms, "Connector %d is disconnected\n", connector_id); + } + return false; + } + + if (connector->count_modes == 0) { + if (!silent) { + MP_ERR(kms, "Connector %d has no valid modes\n", connector_id); + } + return false; + } + + return true; +} + +static bool setup_connector( + struct kms *kms, const drmModeRes *res, int connector_id) +{ + drmModeConnector *connector = NULL; + if (connector_id == -1) { + // get the first connected connector + for (int i = 0; i < res->count_connectors; i++) { + connector = drmModeGetConnector(kms->fd, res->connectors[i]); + if (is_connector_valid(kms, i, connector, true)) { + connector_id = i; + break; + } + if (connector) { + drmModeFreeConnector(connector); + connector = NULL; + } + } + if (connector_id == -1) { + MP_ERR(kms, "No connected connectors found\n"); + return false; + } + } + + if (connector_id < 0 || connector_id >= res->count_connectors) { + MP_ERR(kms, "Bad connector ID. Max valid connector ID = %u\n", + res->count_connectors); + return false; + } + + connector = drmModeGetConnector(kms->fd, res->connectors[connector_id]); + if (!is_connector_valid(kms, connector_id, connector, false)) + return false; + + kms->connector = connector; + return true; +} + +static bool setup_crtc(struct kms *kms, const drmModeRes *res) +{ + for (unsigned int i = 0; i < kms->connector->count_encoders; ++i) { + drmModeEncoder *encoder + = drmModeGetEncoder(kms->fd, kms->connector->encoders[i]); + if (!encoder) { + MP_WARN(kms, "Cannot retrieve encoder %u:%u: %s\n", + i, kms->connector->encoders[i], mp_strerror(errno)); + continue; + } + + // iterate all global CRTCs + for (unsigned int j = 0; j < res->count_crtcs; ++j) { + // check whether this CRTC works with the encoder + if (!(encoder->possible_crtcs & (1 << j))) + continue; + + kms->encoder = encoder; + kms->crtc_id = encoder->crtc_id; + return true; + } + + drmModeFreeEncoder(encoder); + } + + MP_ERR(kms, + "Connector %u has no suitable CRTC\n", + kms->connector->connector_id); + return false; +} + +static bool setup_mode(struct kms *kms, int mode_id) +{ + if (mode_id < 0 || mode_id >= kms->connector->count_modes) { + MP_ERR( + kms, + "Bad mode ID (max = %d).\n", + kms->connector->count_modes - 1); + + MP_INFO(kms, "Available modes:\n"); + for (unsigned int i = 0; i < kms->connector->count_modes; i++) { + MP_INFO(kms, + "Mode %d: %s (%dx%d)\n", + i, + kms->connector->modes[i].name, + kms->connector->modes[i].hdisplay, + kms->connector->modes[i].vdisplay); + } + return false; + } + + kms->mode = kms->connector->modes[mode_id]; + return true; +} + + +struct kms *kms_create(struct mp_log *log) +{ + struct kms *ret = talloc(NULL, struct kms); + *ret = (struct kms) { + .log = mp_log_new(ret, log, "kms"), + .fd = -1, + .connector = NULL, + .encoder = NULL, + .mode = { 0 }, + .crtc_id = -1, + }; + return ret; +} + +bool kms_setup(struct kms *kms, const char *device_path, int connector_id, int mode_id) +{ + kms->fd = open(device_path, O_RDWR | O_CLOEXEC); + if (kms->fd < 0) { + MP_ERR(kms, "Cannot open \"%s\": %s.\n", device_path, mp_strerror(errno)); + return false; + } + + drmModeRes *res = drmModeGetResources(kms->fd); + if (!res) { + MP_ERR(kms, "Cannot retrieve DRM resources: %s\n", mp_strerror(errno)); + return false; + } + + if (!setup_connector(kms, res, mode_id)) + return false; + if (!setup_crtc(kms, res)) + return false; + if (!setup_mode(kms, mode_id)) + return false; + + return true; +} + +void kms_destroy(struct kms *kms) +{ + if (!kms) + return; + if (kms->connector) { + drmModeFreeConnector(kms->connector); + kms->connector = NULL; + } + if (kms->encoder) { + drmModeFreeEncoder(kms->encoder); + kms->encoder = NULL; + } + close(kms->fd); + talloc_free(kms); +} + + + +// VT switcher ---------------------------------------------------------------- + static void vt_switcher_sighandler(int sig) { - unsigned char event = sig == SIGUSR1 ? EVT_RELEASE : EVT_ACQUIRE; + unsigned char event = sig == RELEASE_SIGNAL ? EVT_RELEASE : EVT_ACQUIRE; write(vt_switcher_pipe[1], &event, sizeof(event)); } -int vt_switcher_init(struct vt_switcher *s, struct mp_log *log) +static bool has_signal_installed(int signo) +{ + struct sigaction act = { 0 }; + sigaction(signo, 0, &act); + return act.sa_handler != 0; +} + +static int install_signal(int signo, void (*handler)(int)) +{ + struct sigaction act = { 0 }; + act.sa_handler = handler; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESTART; + return sigaction(signo, &act, NULL); +} + + +bool vt_switcher_init(struct vt_switcher *s, struct mp_log *log) { s->log = log; s->tty_fd = -1; @@ -53,37 +256,48 @@ int vt_switcher_init(struct vt_switcher *s, struct mp_log *log) if (mp_make_cloexec_pipe(vt_switcher_pipe)) { MP_ERR(s, "Creating pipe failed: %s\n", mp_strerror(errno)); - return -1; + return false; } s->tty_fd = open("/dev/tty", O_RDWR | O_CLOEXEC); if (s->tty_fd < 0) { MP_ERR(s, "Can't open TTY for VT control: %s\n", mp_strerror(errno)); - return -1; + return false; } - 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); + if (has_signal_installed(RELEASE_SIGNAL)) { + MP_ERR(s, "Can't handle VT release - signal already used\n"); + return false; + } + if (has_signal_installed(ACQUIRE_SIGNAL)) { + MP_ERR(s, "Can't handle VT acquire - signal already used\n"); + return false; + } + + if (install_signal(RELEASE_SIGNAL, vt_switcher_sighandler)) { + MP_ERR(s, "Failed to install release signal: %s\n", mp_strerror(errno)); + return false; + } + if (install_signal(ACQUIRE_SIGNAL, vt_switcher_sighandler)) { + MP_ERR(s, "Failed to install acquire signal: %s\n", mp_strerror(errno)); + return false; + } struct vt_mode vt_mode; if (ioctl(s->tty_fd, VT_GETMODE, &vt_mode) < 0) { MP_ERR(s, "VT_GETMODE failed: %s\n", mp_strerror(errno)); - return -1; + return false; } vt_mode.mode = VT_PROCESS; - vt_mode.relsig = SIGUSR1; - vt_mode.acqsig = SIGUSR2; + vt_mode.relsig = RELEASE_SIGNAL; + vt_mode.acqsig = ACQUIRE_SIGNAL; if (ioctl(s->tty_fd, VT_SETMODE, &vt_mode) < 0) { MP_ERR(s, "VT_SETMODE failed: %s\n", mp_strerror(errno)); - return -1; + return false; } - return 0; + return true; } void vt_switcher_acquire(struct vt_switcher *s, @@ -108,6 +322,8 @@ void vt_switcher_interrupt_poll(struct vt_switcher *s) void vt_switcher_destroy(struct vt_switcher *s) { + install_signal(RELEASE_SIGNAL, SIG_DFL); + install_signal(ACQUIRE_SIGNAL, SIG_DFL); close(s->tty_fd); close(vt_switcher_pipe[0]); close(vt_switcher_pipe[1]); |