summaryrefslogtreecommitdiffstats
path: root/video/out
diff options
context:
space:
mode:
authorDudemanguy <random342@airmail.cc>2023-01-04 21:34:26 -0600
committerDudemanguy <random342@airmail.cc>2023-01-21 17:08:29 +0000
commit92a6f2d687e90452c9080a2e0b471ced7557518f (patch)
tree0676341e88224c4e670e7ed0f3904b04e204b8bd /video/out
parent8c617765fe88a5d5597fb58d4b593161049de710 (diff)
downloadmpv-92a6f2d687e90452c9080a2e0b471ced7557518f.tar.bz2
mpv-92a6f2d687e90452c9080a2e0b471ced7557518f.tar.xz
drm: rewrite based around vo_drm_state
A longstanding pain point of the drm VOs is the relative lack of state sharing. While drm_common does provide some sharing, it's far less than other platforms like x11 or wayland. What we do here is essentially copy them by creating a new vo_drm_state struct and using it in vo_drm and context_drm_egl. Much of the functionality that was essentially duplicated in both VOs/contexts is now reduced simple functions in drm_common. The usage of the term 'kms' was also mostly eliminated since this is libdrm nowadays from a userspace perspective.
Diffstat (limited to 'video/out')
-rw-r--r--video/out/drm_atomic.c10
-rw-r--r--video/out/drm_atomic.h9
-rw-r--r--video/out/drm_common.c1081
-rw-r--r--video/out/drm_common.h119
-rw-r--r--video/out/hwdec/hwdec_drmprime.c2
-rw-r--r--video/out/hwdec/hwdec_drmprime_overlay.c5
-rw-r--r--video/out/opengl/context_drm_egl.c429
-rw-r--r--video/out/vo.h1
-rw-r--r--video/out/vo_drm.c461
9 files changed, 950 insertions, 1167 deletions
diff --git a/video/out/drm_atomic.c b/video/out/drm_atomic.c
index dfd27c6f52..5754504e98 100644
--- a/video/out/drm_atomic.c
+++ b/video/out/drm_atomic.c
@@ -107,14 +107,14 @@ int drm_object_set_property(drmModeAtomicReq *request, struct drm_object *object
return -EINVAL;
}
-struct drm_object * drm_object_create(struct mp_log *log, int fd,
- uint32_t object_id, uint32_t type)
+struct drm_object *drm_object_create(struct mp_log *log, int fd,
+ uint32_t object_id, uint32_t type)
{
struct drm_object *obj = NULL;
obj = talloc_zero(NULL, struct drm_object);
+ obj->fd = fd;
obj->id = object_id;
obj->type = type;
- obj->fd = fd;
if (drm_object_create_properties(log, fd, obj)) {
talloc_free(obj);
@@ -195,7 +195,6 @@ struct drm_atomic_context *drm_atomic_create_context(struct mp_log *log, int fd,
if (connector->connector_id == connector_id)
ctx->connector = drm_object_create(log, ctx->fd, connector->connector_id,
DRM_MODE_OBJECT_CONNECTOR);
-
drmModeFreeConnector(connector);
if (ctx->connector)
break;
@@ -211,8 +210,7 @@ struct drm_atomic_context *drm_atomic_create_context(struct mp_log *log, int fd,
drmplane = NULL;
if (possible_crtcs & (1 << crtc_index)) {
- plane = drm_object_create(log, ctx->fd, plane_id,
- DRM_MODE_OBJECT_PLANE);
+ plane = drm_object_create(log, ctx->fd, plane_id, DRM_MODE_OBJECT_PLANE);
if (!plane) {
mp_err(log, "Failed to create Plane object from plane ID %d\n",
diff --git a/video/out/drm_atomic.h b/video/out/drm_atomic.h
index 32e56c7f97..499aa33319 100644
--- a/video/out/drm_atomic.h
+++ b/video/out/drm_atomic.h
@@ -24,15 +24,11 @@
#include <xf86drmMode.h>
#include "common/msg.h"
+#include "drm_common.h"
#define DRM_OPTS_PRIMARY_PLANE -1
#define DRM_OPTS_OVERLAY_PLANE -2
-struct drm_mode {
- drmModeModeInfo mode;
- uint32_t blob_id;
-};
-
struct drm_atomic_plane_state {
uint64_t fb_id;
uint64_t crtc_id;
@@ -83,13 +79,12 @@ struct drm_atomic_context {
struct drm_atomic_state old_state;
};
-
int drm_object_create_properties(struct mp_log *log, int fd, struct drm_object *object);
void drm_object_free_properties(struct drm_object *object);
int drm_object_get_property(struct drm_object *object, char *name, uint64_t *value);
int drm_object_set_property(drmModeAtomicReq *request, struct drm_object *object, char *name, uint64_t value);
drmModePropertyBlobPtr drm_object_get_property_blob(struct drm_object *object, char *name);
-struct drm_object * drm_object_create(struct mp_log *log, int fd, uint32_t object_id, uint32_t type);
+struct drm_object *drm_object_create(struct mp_log *log, int fd, uint32_t object_id, uint32_t type);
void drm_object_free(struct drm_object *object);
void drm_object_print_info(struct mp_log *log, struct drm_object *object);
struct drm_atomic_context *drm_atomic_create_context(struct mp_log *log, int fd, int crtc_id, int connector_id,
diff --git a/video/out/drm_common.c b/video/out/drm_common.c
index 7845edb734..09494d6317 100644
--- a/video/out/drm_common.c
+++ b/video/out/drm_common.c
@@ -35,10 +35,12 @@
#include <sys/vt.h>
#endif
+#include "drm_atomic.h"
#include "drm_common.h"
#include "common/common.h"
#include "common/msg.h"
+#include "options/m_config.h"
#include "osdep/io.h"
#include "osdep/timer.h"
#include "misc/ctype.h"
@@ -55,38 +57,36 @@
static int vt_switcher_pipe[2];
-static int drm_connector_opt_help(
- struct mp_log *log, const struct m_option *opt, struct bstr name);
+static int drm_connector_opt_help(struct mp_log *log, const struct m_option *opt,
+ struct bstr name);
-static int drm_mode_opt_help(
- struct mp_log *log, const struct m_option *opt, struct bstr name);
+static int drm_mode_opt_help(struct mp_log *log, const struct m_option *opt,
+ struct bstr name);
-static int drm_validate_mode_opt(
- struct mp_log *log, const struct m_option *opt, struct bstr name,
- const char **value);
+static int drm_validate_mode_opt(struct mp_log *log, const struct m_option *opt,
+ struct bstr name, const char **value);
-static void kms_show_available_modes(
- struct mp_log *log, const drmModeConnector *connector);
+static void drm_show_available_modes(struct mp_log *log, const drmModeConnector *connector);
-static void kms_show_available_connectors(struct mp_log *log, int card_no,
+static void drm_show_available_connectors(struct mp_log *log, int card_no,
const char *card_path);
static double mode_get_Hz(const drmModeModeInfo *mode);
#define OPT_BASE_STRUCT struct drm_opts
const struct m_sub_options drm_conf = {
.opts = (const struct m_option[]) {
- {"drm-device", OPT_STRING(drm_device_path), .flags = M_OPT_FILE},
- {"drm-connector", OPT_STRING(drm_connector_spec),
+ {"drm-device", OPT_STRING(device_path), .flags = M_OPT_FILE},
+ {"drm-connector", OPT_STRING(connector_spec),
.help = drm_connector_opt_help},
- {"drm-mode", OPT_STRING_VALIDATE(drm_mode_spec, drm_validate_mode_opt),
+ {"drm-mode", OPT_STRING_VALIDATE(mode_spec, drm_validate_mode_opt),
.help = drm_mode_opt_help},
{"drm-atomic", OPT_CHOICE(drm_atomic, {"no", 0}, {"auto", 1}),
.deprecation_message = "this option is deprecated: DRM Atomic is required"},
- {"drm-draw-plane", OPT_CHOICE(drm_draw_plane,
+ {"drm-draw-plane", OPT_CHOICE(draw_plane,
{"primary", DRM_OPTS_PRIMARY_PLANE},
{"overlay", DRM_OPTS_OVERLAY_PLANE}),
M_RANGE(0, INT_MAX)},
- {"drm-drmprime-video-plane", OPT_CHOICE(drm_drmprime_video_plane,
+ {"drm-drmprime-video-plane", OPT_CHOICE(drmprime_video_plane,
{"primary", DRM_OPTS_PRIMARY_PLANE},
{"overlay", DRM_OPTS_OVERLAY_PLANE}),
M_RANGE(0, INT_MAX)},
@@ -95,21 +95,21 @@ const struct m_sub_options drm_conf = {
{"xrgb2101010", DRM_OPTS_FORMAT_XRGB2101010},
{"xbgr8888", DRM_OPTS_FORMAT_XBGR8888},
{"xbgr2101010", DRM_OPTS_FORMAT_XBGR2101010})},
- {"drm-draw-surface-size", OPT_SIZE_BOX(drm_draw_surface_size)},
+ {"drm-draw-surface-size", OPT_SIZE_BOX(draw_surface_size)},
+ {"drm-vrr-enabled", OPT_CHOICE(vrr_enabled,
+ {"no", 0}, {"yes", 1}, {"auto", -1})},
{"drm-osd-plane-id", OPT_REPLACED("drm-draw-plane")},
{"drm-video-plane-id", OPT_REPLACED("drm-drmprime-video-plane")},
{"drm-osd-size", OPT_REPLACED("drm-draw-surface-size")},
- {"drm-vrr-enabled", OPT_CHOICE(drm_vrr_enabled,
- {"no", 0}, {"yes", 1}, {"auto", -1})},
{0},
},
.defaults = &(const struct drm_opts) {
- .drm_mode_spec = "preferred",
+ .mode_spec = "preferred",
.drm_atomic = 1,
- .drm_draw_plane = DRM_OPTS_PRIMARY_PLANE,
- .drm_drmprime_video_plane = DRM_OPTS_OVERLAY_PLANE,
- .drm_vrr_enabled = 0,
+ .draw_plane = DRM_OPTS_PRIMARY_PLANE,
+ .drmprime_video_plane = DRM_OPTS_OVERLAY_PLANE,
+ .vrr_enabled = 0,
},
.size = sizeof(struct drm_opts),
};
@@ -151,8 +151,299 @@ struct drm_mode_spec {
double refresh;
};
-// KMS ------------------------------------------------------------------------
+/* VT Switcher */
+static void vt_switcher_sighandler(int sig)
+{
+ unsigned char event = sig == RELEASE_SIGNAL ? EVT_RELEASE : EVT_ACQUIRE;
+ (void)write(vt_switcher_pipe[1], &event, sizeof(event));
+}
+
+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);
+}
+
+static void release_vt(void *data)
+{
+ struct vo_drm_state *drm = data;
+ MP_VERBOSE(drm, "Releasing VT\n");
+ vo_drm_release_crtc(drm);
+}
+
+static void acquire_vt(void *data)
+{
+ struct vo_drm_state *drm = data;
+ MP_VERBOSE(drm, "Acquiring VT\n");
+ vo_drm_acquire_crtc(drm);
+}
+
+static void vt_switcher_acquire(struct vt_switcher *s,
+ void (*handler)(void*), void *user_data)
+{
+ s->handlers[HANDLER_ACQUIRE] = handler;
+ s->handler_data[HANDLER_ACQUIRE] = user_data;
+}
+
+static void vt_switcher_release(struct vt_switcher *s,
+ void (*handler)(void*), void *user_data)
+{
+ s->handlers[HANDLER_RELEASE] = handler;
+ s->handler_data[HANDLER_RELEASE] = user_data;
+}
+
+static bool vt_switcher_init(struct vt_switcher *s, struct mp_log *log)
+{
+ s->tty_fd = -1;
+ s->log = log;
+ vt_switcher_pipe[0] = -1;
+ vt_switcher_pipe[1] = -1;
+
+ if (mp_make_cloexec_pipe(vt_switcher_pipe)) {
+ mp_err(log, "Creating pipe failed: %s\n", mp_strerror(errno));
+ return false;
+ }
+
+ s->tty_fd = open("/dev/tty", O_RDWR | O_CLOEXEC);
+ if (s->tty_fd < 0) {
+ mp_err(log, "Can't open TTY for VT control: %s\n", mp_strerror(errno));
+ return false;
+ }
+
+ if (has_signal_installed(RELEASE_SIGNAL)) {
+ mp_err(log, "Can't handle VT release - signal already used\n");
+ return false;
+ }
+ if (has_signal_installed(ACQUIRE_SIGNAL)) {
+ mp_err(log, "Can't handle VT acquire - signal already used\n");
+ return false;
+ }
+
+ if (install_signal(RELEASE_SIGNAL, vt_switcher_sighandler)) {
+ mp_err(log, "Failed to install release signal: %s\n", mp_strerror(errno));
+ return false;
+ }
+ if (install_signal(ACQUIRE_SIGNAL, vt_switcher_sighandler)) {
+ mp_err(log, "Failed to install acquire signal: %s\n", mp_strerror(errno));
+ return false;
+ }
+
+ struct vt_mode vt_mode = { 0 };
+ if (ioctl(s->tty_fd, VT_GETMODE, &vt_mode) < 0) {
+ mp_err(log, "VT_GETMODE failed: %s\n", mp_strerror(errno));
+ return false;
+ }
+
+ vt_mode.mode = VT_PROCESS;
+ vt_mode.relsig = RELEASE_SIGNAL;
+ vt_mode.acqsig = ACQUIRE_SIGNAL;
+ // frsig is a signal for forced release. Not implemented on Linux,
+ // Solaris, BSDs but must be set to a valid signal on some of those.
+ vt_mode.frsig = SIGIO; // unused
+ if (ioctl(s->tty_fd, VT_SETMODE, &vt_mode) < 0) {
+ mp_err(log, "VT_SETMODE failed: %s\n", mp_strerror(errno));
+ return false;
+ }
+
+ // Block the VT switching signals from interrupting the VO thread (they will
+ // still be picked up by other threads, which will fill vt_switcher_pipe for us)
+ sigset_t set;
+ sigemptyset(&set);
+ sigaddset(&set, RELEASE_SIGNAL);
+ sigaddset(&set, ACQUIRE_SIGNAL);
+ pthread_sigmask(SIG_BLOCK, &set, NULL);
+
+ return true;
+}
+
+static void vt_switcher_interrupt_poll(struct vt_switcher *s)
+{
+ unsigned char event = EVT_INTERRUPT;
+ (void)write(vt_switcher_pipe[1], &event, sizeof(event));
+}
+
+static void vt_switcher_destroy(struct vt_switcher *s)
+{
+ struct vt_mode vt_mode = {0};
+ vt_mode.mode = VT_AUTO;
+ if (ioctl(s->tty_fd, VT_SETMODE, &vt_mode) < 0) {
+ MP_ERR(s, "VT_SETMODE failed: %s\n", mp_strerror(errno));
+ return;
+ }
+
+ 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]);
+}
+
+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:
+ s->handlers[HANDLER_RELEASE](s->handler_data[HANDLER_RELEASE]);
+ if (ioctl(s->tty_fd, VT_RELDISP, 1) < 0) {
+ MP_ERR(s, "Failed to release virtual terminal\n");
+ }
+ break;
+ case EVT_ACQUIRE:
+ s->handlers[HANDLER_ACQUIRE](s->handler_data[HANDLER_ACQUIRE]);
+ if (ioctl(s->tty_fd, VT_RELDISP, VT_ACKACQ) < 0) {
+ MP_ERR(s, "Failed to acquire virtual terminal\n");
+ }
+ break;
+ case EVT_INTERRUPT:
+ break;
+ }
+}
+
+bool vo_drm_acquire_crtc(struct vo_drm_state *drm)
+{
+ if (drm->active)
+ return true;
+ drm->active = true;
+
+ if (drmSetMaster(drm->fd)) {
+ MP_WARN(drm, "Failed to acquire DRM master: %s\n",
+ mp_strerror(errno));
+ }
+
+ struct drm_atomic_context *atomic_ctx = drm->atomic_context;
+
+ if (!drm_atomic_save_old_state(atomic_ctx))
+ MP_WARN(drm, "Failed to save old DRM atomic state\n");
+
+ drmModeAtomicReqPtr request = drmModeAtomicAlloc();
+ if (!request) {
+ MP_ERR(drm, "Failed to allocate drm atomic request\n");
+ goto err;
+ }
+
+ if (drm_object_set_property(request, atomic_ctx->connector, "CRTC_ID", drm->crtc_id) < 0) {
+ MP_ERR(drm, "Could not set CRTC_ID on connector\n");
+ goto err;
+ }
+
+ if (!drm_mode_ensure_blob(drm->fd, &drm->mode)) {
+ MP_ERR(drm, "Failed to create DRM mode blob\n");
+ goto err;
+ }
+ if (drm_object_set_property(request, atomic_ctx->crtc, "MODE_ID", drm->mode.blob_id) < 0) {
+ MP_ERR(drm, "Could not set MODE_ID on crtc\n");
+ goto err;
+ }
+ if (drm_object_set_property(request, atomic_ctx->crtc, "ACTIVE", 1) < 0) {
+ MP_ERR(drm, "Could not set ACTIVE on crtc\n");
+ goto err;
+ }
+
+ /*
+ * VRR related properties were added in kernel 5.0. We will not fail if we
+ * cannot query or set the value, but we will log as appropriate.
+ */
+ uint64_t vrr_capable = 0;
+ drm_object_get_property(atomic_ctx->connector, "VRR_CAPABLE", &vrr_capable);
+ MP_VERBOSE(drm, "crtc is%s VRR capable\n", vrr_capable ? "" : " not");
+
+ uint64_t vrr_requested = drm->opts->vrr_enabled;
+ if (vrr_requested == 1 || (vrr_capable && vrr_requested == -1)) {
+ if (drm_object_set_property(request, atomic_ctx->crtc, "VRR_ENABLED", 1) < 0) {
+ MP_WARN(drm, "Could not enable VRR on crtc\n");
+ } else {
+ MP_VERBOSE(drm, "Enabled VRR on crtc\n");
+ }
+ }
+
+ drm_object_set_property(request, atomic_ctx->draw_plane, "FB_ID", drm->fb->id);
+ drm_object_set_property(request, atomic_ctx->draw_plane, "CRTC_ID", drm->crtc_id);
+ drm_object_set_property(request, atomic_ctx->draw_plane, "SRC_X", 0);
+ drm_object_set_property(request, atomic_ctx->draw_plane, "SRC_Y", 0);
+ drm_object_set_property(request, atomic_ctx->draw_plane, "SRC_W", drm->width << 16);
+ drm_object_set_property(request, atomic_ctx->draw_plane, "SRC_H", drm->height << 16);
+ drm_object_set_property(request, atomic_ctx->draw_plane, "CRTC_X", 0);
+ drm_object_set_property(request, atomic_ctx->draw_plane, "CRTC_Y", 0);
+ drm_object_set_property(request, atomic_ctx->draw_plane, "CRTC_W", drm->mode.mode.hdisplay);
+ drm_object_set_property(request, atomic_ctx->draw_plane, "CRTC_H", drm->mode.mode.vdisplay);
+
+ if (drmModeAtomicCommit(drm->fd, request, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL)) {
+ MP_ERR(drm, "Failed to commit ModeSetting atomic request: %s\n", strerror(errno));
+ goto err;
+ }
+
+ drmModeAtomicFree(request);
+ return true;
+
+err:
+ drmModeAtomicFree(request);
+ return false;
+}
+
+
+void vo_drm_release_crtc(struct vo_drm_state *drm)
+{
+ if (!drm->active)
+ return;
+ drm->active = false;
+
+ if (!drm->atomic_context->old_state.saved)
+ return;
+
+ bool success = true;
+ struct drm_atomic_context *atomic_ctx = drm->atomic_context;
+ drmModeAtomicReqPtr request = drmModeAtomicAlloc();
+ if (!request) {
+ MP_ERR(drm, "Failed to allocate drm atomic request\n");
+ success = false;
+ }
+
+ if (request && !drm_atomic_restore_old_state(request, atomic_ctx)) {
+ MP_WARN(drm, "Got error while restoring old state\n");
+ success = false;
+ }
+
+ if (request) {
+ if (drmModeAtomicCommit(drm->fd, request, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL)) {
+ MP_WARN(drm, "Failed to commit ModeSetting atomic request: %s\n",
+ mp_strerror(errno));
+ success = false;
+ }
+ }
+
+ if (request)
+ drmModeAtomicFree(request);
+
+ if (!success)
+ MP_ERR(drm, "Failed to restore previous mode\n");
+
+ if (drmDropMaster(drm->fd)) {
+ MP_WARN(drm, "Failed to drop DRM master: %s\n",
+ mp_strerror(errno));
+ }
+}
+
+/* libdrm */
static void get_connector_name(const drmModeConnector *connector,
char ret[MAX_CONNECTOR_NAME_LEN])
{
@@ -171,13 +462,13 @@ static void get_connector_name(const drmModeConnector *connector,
// Gets the first connector whose name matches the input parameter.
// The returned connector may be disconnected.
// Result must be freed with drmModeFreeConnector.
-static drmModeConnector *get_connector_by_name(const struct kms *kms,
- const drmModeRes *res,
- const char *connector_name)
+static drmModeConnector *get_connector_by_name(const drmModeRes *res,
+ const char *connector_name,
+ int fd)
{
for (int i = 0; i < res->count_connectors; i++) {
drmModeConnector *connector
- = drmModeGetConnector(kms->fd, res->connectors[i]);
+ = drmModeGetConnector(fd, res->connectors[i]);
if (!connector)
continue;
char other_connector_name[MAX_CONNECTOR_NAME_LEN];
@@ -191,16 +482,14 @@ static drmModeConnector *get_connector_by_name(const struct kms *kms,
// Gets the first connected connector.
// Result must be freed with drmModeFreeConnector.
-static drmModeConnector *get_first_connected_connector(const struct kms *kms,
- const drmModeRes *res)
+static drmModeConnector *get_first_connected_connector(const drmModeRes *res,
+ int fd)
{
for (int i = 0; i < res->count_connectors; i++) {
- drmModeConnector *connector
- = drmModeGetConnector(kms->fd, res->connectors[i]);
+ drmModeConnector *connector = drmModeGetConnector(fd, res->connectors[i]);
if (!connector)
continue;
- if (connector->connection == DRM_MODE_CONNECTED
- && connector->count_modes > 0) {
+ if (connector->connection == DRM_MODE_CONNECTED && connector->count_modes > 0) {
return connector;
}
drmModeFreeConnector(connector);
@@ -208,61 +497,58 @@ static drmModeConnector *get_first_connected_connector(const struct kms *kms,
return NULL;
}
-static bool setup_connector(struct kms *kms, const drmModeRes *res,
+static bool setup_connector(struct vo_drm_state *drm, const drmModeRes *res,
const char *connector_name)
{
drmModeConnector *connector;
- if (connector_name
- && strcmp(connector_name, "")
- && strcmp(connector_name, "auto")) {
- connector = get_connector_by_name(kms, res, connector_name);
+ if (connector_name && strcmp(connector_name, "") && strcmp(connector_name, "auto")) {
+ connector = get_connector_by_name(res, connector_name, drm->fd);
if (!connector) {
- MP_ERR(kms, "No connector with name %s found\n", connector_name);
- kms_show_available_connectors(kms->log, kms->card_no,
- kms->primary_node_path);
+ MP_ERR(drm, "No connector with name %s found\n", connector_name);
+ drm_show_available_connectors(drm->log, drm->card_no, drm->card_path);
return false;
}
} else {
- connector = get_first_connected_connector(kms, res);
+ connector = get_first_connected_connector(res, drm->fd);
if (!connector) {
- MP_ERR(kms, "No connected connectors found\n");
+ MP_ERR(drm, "No connected connectors found\n");
return false;
}
}
if (connector->connection != DRM_MODE_CONNECTED) {
drmModeFreeConnector(connector);
- MP_ERR(kms, "Chosen connector is disconnected\n");
+ MP_ERR(drm, "Chosen connector is disconnected\n");
return false;
}
if (connector->count_modes == 0) {
drmModeFreeConnector(connector);
- MP_ERR(kms, "Chosen connector has no valid modes\n");
+ MP_ERR(drm, "Chosen connector has no valid modes\n");
return false;
}
- kms->connector = connector;
+ drm->connector = connector;
return true;
}
-static bool setup_crtc(struct kms *kms, const drmModeRes *res)
+static bool setup_crtc(struct vo_drm_state *drm, const drmModeRes *res)
{
// First try to find currently connected encoder and its current CRTC
for (unsigned int i = 0; i < res->count_encoders; i++) {
- drmModeEncoder *encoder = drmModeGetEncoder(kms->fd, res->encoders[i]);
+ drmModeEncoder *encoder = drmModeGetEncoder(drm->fd, res->encoders[i]);
if (!encoder) {
- MP_WARN(kms, "Cannot retrieve encoder %u:%u: %s\n",
+ MP_WARN(drm, "Cannot retrieve encoder %u:%u: %s\n",
i, res->encoders[i], mp_strerror(errno));
continue;
}
- if (encoder->encoder_id == kms->connector->encoder_id && encoder->crtc_id != 0) {
- MP_VERBOSE(kms, "Connector %u currently connected to encoder %u\n",
- kms->connector->connector_id, kms->connector->encoder_id);
- kms->encoder = encoder;
- kms->crtc_id = encoder->crtc_id;
+ if (encoder->encoder_id == drm->connector->encoder_id && encoder->crtc_id != 0) {
+ MP_VERBOSE(drm, "Connector %u currently connected to encoder %u\n",
+ drm->connector->connector_id, drm->connector->encoder_id);
+ drm->encoder = encoder;
+ drm->crtc_id = encoder->crtc_id;
goto success;
}
@@ -270,12 +556,12 @@ static bool setup_crtc(struct kms *kms, const drmModeRes *res)
}
// Otherwise pick first legal encoder and CRTC combo for the connector
- for (unsigned int i = 0; i < kms->connector->count_encoders; ++i) {
+ for (unsigned int i = 0; i < drm->connector->count_encoders; ++i) {
drmModeEncoder *encoder
- = drmModeGetEncoder(kms->fd, kms->connector->encoders[i]);
+ = drmModeGetEncoder(drm->fd, drm->connector->encoders[i]);
if (!encoder) {
- MP_WARN(kms, "Cannot retrieve encoder %u:%u: %s\n",
- i, kms->connector->encoders[i], mp_strerror(errno));
+ MP_WARN(drm, "Cannot retrieve encoder %u:%u: %s\n",
+ i, drm->connector->encoders[i], mp_strerror(errno));
continue;
}
@@ -285,21 +571,21 @@ static bool setup_crtc(struct kms *kms, const drmModeRes *res)
if (!(encoder->possible_crtcs & (1 << j)))
continue;
- kms->encoder = encoder;
- kms->crtc_id = res->crtcs[j];
+ drm->encoder = encoder;
+ drm->crtc_id = res->crtcs[j];
goto success;
}
drmModeFreeEncoder(encoder);
}
- MP_ERR(kms, "Connector %u has no suitable CRTC\n",
- kms->connector->connector_id);
+ MP_ERR(drm, "Connector %u has no suitable CRTC\n",
+ drm->connector->connector_id);
return false;
success:
- MP_VERBOSE(kms, "Selected Encoder %u with CRTC %u\n",
- kms->encoder->encoder_id, kms->crtc_id);
+ MP_VERBOSE(drm, "Selected Encoder %u with CRTC %u\n",
+ drm->encoder->encoder_id, drm->crtc_id);
return true;
}
@@ -388,15 +674,15 @@ static bool parse_mode_spec(const char *spec, struct drm_mode_spec *parse_result
return true;
}
-static bool setup_mode_by_idx(struct kms *kms, unsigned int mode_idx)
+static bool setup_mode_by_idx(struct vo_drm_state *drm, unsigned int mode_idx)
{
- if (mode_idx >= kms->connector->count_modes) {
- MP_ERR(kms, "Bad mode index (max = %d).\n",
- kms->connector->count_modes - 1);
+ if (mode_idx >= drm->connector->count_modes) {
+ MP_ERR(drm, "Bad mode index (max = %d).\n",
+ drm->connector->count_modes - 1);
return false;
}
- kms->mode.mode = kms->connector->modes[mode_idx];
+ drm->mode.mode = drm->connector->modes[mode_idx];
return true;
}
@@ -418,46 +704,45 @@ static bool mode_match(const drmModeModeInfo *mode,
}
}
-static bool setup_mode_by_numbers(struct kms *kms,
+static bool setup_mode_by_numbers(struct vo_drm_state *drm,
unsigned int width,
unsigned int height,
- double refresh,
- const char *mode_spec)
+ double refresh)
{
- for (unsigned int i = 0; i < kms->connector->count_modes; ++i) {
- drmModeModeInfo *current_mode = &kms->connector->modes[i];
+ for (unsigned int i = 0; i < drm->connector->count_modes; ++i) {
+ drmModeModeInfo *current_mode = &drm->connector->modes[i];
if (mode_match(current_mode, width, height, refresh)) {
- kms->mode.mode = *current_mode;
+ drm->mode.mode = *current_mode;
return true;
}
}
- MP_ERR(kms, "Could not find mode matching %s\n", mode_spec);
+ MP_ERR(drm, "Could not find mode matching %s\n", drm->opts->mode_spec);
return false;
}
-static bool setup_mode_preferred(struct kms *kms)
+static bool setup_mode_preferred(struct vo_drm_state *drm)
{
- for (unsigned int i = 0; i < kms->connector->count_modes; ++i) {
- drmModeModeInfo *current_mode = &kms->connector->modes[i];
+ for (unsigned int i = 0; i < drm->connector->count_modes; ++i) {
+ drmModeModeInfo *current_mode = &drm->connector->modes[i];
if (current_mode->type & DRM_MODE_TYPE_PREFERRED) {
- kms->mode.mode = *current_mode;
+ drm->mode.mode = *current_mode;
return true;
}
}
// Fall back to first mode
- MP_WARN(kms, "Could not find any preferred mode. Picking the first mode.\n");
- kms->mode.mode = kms->connector->modes[0];
+ MP_WARN(drm, "Could not find any preferred mode. Picking the first mode.\n");
+ drm->mode.mode = drm->connector->modes[0];
return true;
}
-static bool setup_mode_highest(struct kms *kms)
+static bool setup_mode_highest(struct vo_drm_state *drm)
{
unsigned int area = 0;
- drmModeModeInfo *highest_resolution_mode = &kms->connector->modes[0];
- for (unsigned int i = 0; i < kms->connector->count_modes; ++i) {
- drmModeModeInfo *current_mode = &kms->connector->modes[i];
+ drmModeModeInfo *highest_resolution_mode = &drm->connector->modes[0];
+ for (unsigned int i = 0; i < drm->connector->count_modes; ++i) {
+ drmModeModeInfo *current_mode = &drm->connector->modes[i];
const unsigned int current_area =
current_mode->hdisplay * current_mode->vdisplay;
@@ -467,55 +752,54 @@ static bool setup_mode_highest(struct kms *kms)
}
}
- kms->mode.mode = *highest_resolution_mode;
+ drm->mode.mode = *highest_resolution_mode;
return true;
}
-static bool setup_mode(struct kms *kms, const char *mode_spec)
+static bool setup_mode(struct vo_drm_state *drm)
{
- if (kms->connector->count_modes <= 0) {
- MP_ERR(kms, "No available modes\n");
+ if (drm->connector->count_modes <= 0) {
+ MP_ERR(drm, "No available modes\n");
return false;
}
struct drm_mode_spec parsed;
- if (!parse_mode_spec(mode_spec, &parsed)) {
- MP_ERR(kms, "Parse error\n");
+ if (!parse_mode_spec(drm->opts->mode_spec, &parsed)) {
+ MP_ERR(drm, "Parse error\n");
goto err;
}
switch (parsed.type) {
case DRM_MODE_SPEC_BY_IDX:
- if (!setup_mode_by_idx(kms, parsed.idx))
+ if (!setup_mode_by_idx(drm, parsed.idx))
goto err;
break;
case DRM_MODE_SPEC_BY_NUMBERS:
- if (!setup_mode_by_numbers(kms, parsed.width, parsed.height, parsed.refresh,
- mode_spec))
+ if (!setup_mode_by_numbers(drm, parsed.width, parsed.height, parsed.refresh))
goto err;
break;
case DRM_MODE_SPEC_PREFERRED:
- if (!setup_mode_preferred(kms))
+ if (!setup_mode_preferred(drm))
goto err;
break;
case DRM_MODE_SPEC_HIGHEST:
- if (!setup_mode_highest(kms))
+ if (!setup_mode_highest(drm))
goto err;
break;
default:
- MP_ERR(kms, "setup_mode: Internal error\n");
+ MP_ERR(drm, "setup_mode: Internal error\n");
goto err;
}
- drmModeModeInfo *mode = &kms->mode.mode;
- MP_VERBOSE(kms, "Selected mode: %s (%dx%d@%.2fHz)\n",
+ drmModeModeInfo *mode = &drm->mode.mode;
+ MP_VERBOSE(drm, "Selected mode: %s (%dx%d@%.2fHz)\n",
mode->name, mode->hdisplay, mode->vdisplay, mode_get_Hz(mode));
return true;
err:
- MP_INFO(kms, "Available modes:\n");
- kms_show_available_modes(kms->log, kms->connector);
+ MP_INFO(drm, "Available modes:\n");
+ drm_show_available_modes(drm->log, drm->connector);
return false;
}
@@ -537,32 +821,35 @@ static bool card_supports_kms(const char *path)
#endif
}
-static char *get_primary_device_path(struct mp_log *log, int *card_no)
+static void get_primary_device_path(struct vo_drm_state *drm)
{
+ if (drm->opts->device_path) {
+ drm->card_path = talloc_strdup(drm, drm->opts->device_path);
+ return;
+ }
+
drmDevice *devices[DRM_MAX_MINOR] = { 0 };
int card_count = drmGetDevices2(0, devices, MP_ARRAY_SIZE(devices));
- char *device_path = NULL;
- bool card_no_given = (*card_no >= 0);
+ bool card_no_given = drm->card_no >= 0;
if (card_count < 0) {
- mp_err(log, "Listing DRM devices with drmGetDevices failed! (%s)\n",
+ MP_ERR(drm, "Listing DRM devices with drmGetDevices failed! (%s)\n",
mp_strerror(errno));
goto err;
}
- if (card_no_given && *card_no > (card_count - 1)) {
- mp_err(log, "Card number %d given too high! %d devices located.\n",
- *card_no, card_count);
+ if (card_no_given && drm->card_no > (card_count - 1)) {
+ MP_ERR(drm, "Card number %d given too high! %d devices located.\n",
+ drm->card_no, card_count);
goto err;
}
- for (int i = card_no_given ? *card_no : 0; i < card_count; i++) {
+ for (int i = card_no_given ? drm->card_no : 0; i < card_count; i++) {
drmDevice *dev = devices[i];
if (!(dev->available_nodes & (1 << DRM_NODE_PRIMARY))) {
if (card_no_given) {
- mp_err(log,
- "DRM card number %d given, yet it does not have "
+ MP_ERR(drm, "DRM card number %d given, but it does not have "
"a primary node!\n", i);
break;
}
@@ -570,12 +857,12 @@ static char *get_primary_device_path(struct mp_log *log, int *card_no)
continue;
}
- const char *primary_node_path = dev->nodes[DRM_NODE_PRIMARY];
+ const char *card_path = dev->nodes[DRM_NODE_PRIMARY];
- if (!card_supports_kms(primary_node_path)) {
+ if (!card_supports_kms(card_path)) {
if (card_no_given) {
- mp_err(log,
- "DRM card number %d given, yet it does not support "
+ MP_ERR(drm,
+ "DRM card number %d given, but it does not support "
"KMS!\n", i);
break;
}
@@ -583,163 +870,247 @@ static char *get_primary_device_path(struct mp_log *log, int *card_no)
continue;
}
- mp_verbose(log, "Picked DRM card %