diff --git a/backend/drm/backend.c b/backend/drm/backend.c index 27e5585dc..f91492ac4 100644 --- a/backend/drm/backend.c +++ b/backend/drm/backend.c @@ -75,15 +75,29 @@ static int backend_get_drm_fd(struct wlr_backend *backend) { return drm->fd; } -static uint32_t drm_backend_get_buffer_caps(struct wlr_backend *backend) { +static uint32_t backend_get_buffer_caps(struct wlr_backend *backend) { return WLR_BUFFER_CAP_DMABUF; } +static bool backend_test(struct wlr_backend *backend, + const struct wlr_backend_output_state *states, size_t states_len) { + struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend); + return commit_drm_device(drm, states, states_len, true); +} + +static bool backend_commit(struct wlr_backend *backend, + const struct wlr_backend_output_state *states, size_t states_len) { + struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend); + return commit_drm_device(drm, states, states_len, false); +} + static const struct wlr_backend_impl backend_impl = { .start = backend_start, .destroy = backend_destroy, .get_drm_fd = backend_get_drm_fd, - .get_buffer_caps = drm_backend_get_buffer_caps, + .get_buffer_caps = backend_get_buffer_caps, + .test = backend_test, + .commit = backend_commit, }; bool wlr_backend_is_drm(struct wlr_backend *b) { diff --git a/backend/drm/drm.c b/backend/drm/drm.c index a79d6a390..fcdd060ec 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -1870,6 +1870,73 @@ void restore_drm_device(struct wlr_drm_backend *drm) { } } +bool commit_drm_device(struct wlr_drm_backend *drm, + const struct wlr_backend_output_state *output_states, size_t output_states_len, + bool test_only) { + if (!drm->session->active) { + return false; + } + + struct wlr_drm_connector_state *conn_states = calloc(output_states_len, sizeof(conn_states[0])); + if (conn_states == NULL) { + return false; + } + + bool ok = false; + bool modeset = false; + size_t conn_states_len = 0; + for (size_t i = 0; i < output_states_len; i++) { + const struct wlr_backend_output_state *output_state = &output_states[i]; + struct wlr_output *output = output_state->output; + if (!output->enabled && !output_pending_enabled(output, &output_state->base)) { + // KMS rejects commits which disable already-disabled connectors + // and have the DRM_MODE_PAGE_FLIP_EVENT flag + continue; + } + + struct wlr_drm_connector *conn = get_drm_connector_from_output(output); + + if (output_pending_enabled(output, &output_state->base) && !drm_connector_alloc_crtc(conn)) { + wlr_drm_conn_log(conn, WLR_DEBUG, + "No CRTC available for this connector"); + goto out; + } + + struct wlr_drm_connector_state *conn_state = &conn_states[conn_states_len]; + drm_connector_state_init(conn_state, conn, &output_state->base); + conn_states_len++; + + if (!drm_connector_prepare(conn_state, test_only)) { + goto out; + } + + if (output_state->base.tearing_page_flip) { + wlr_log(WLR_DEBUG, "Tearing not supported for DRM device-wide commits"); + goto out; + } + + modeset |= output_state->base.allow_reconfiguration; + } + + uint32_t flags = 0; + if (!test_only) { + flags |= DRM_MODE_PAGE_FLIP_EVENT; + } + struct wlr_drm_device_state dev_state = { + .modeset = modeset, + .connectors = conn_states, + .connectors_len = conn_states_len, + }; + ok = drm_commit(drm, &dev_state, flags, test_only); + +out: + for (size_t i = 0; i < conn_states_len; i++) { + drm_connector_state_finish(&conn_states[i]); + } + free(conn_states); + return ok; +} + static int mhz_to_nsec(int mhz) { return 1000000000000LL / mhz; } diff --git a/include/backend/drm/drm.h b/include/backend/drm/drm.h index 46dfa7625..8a56a5fc0 100644 --- a/include/backend/drm/drm.h +++ b/include/backend/drm/drm.h @@ -206,6 +206,8 @@ void finish_drm_resources(struct wlr_drm_backend *drm); void scan_drm_connectors(struct wlr_drm_backend *state, struct wlr_device_hotplug_event *event); void scan_drm_leases(struct wlr_drm_backend *drm); +bool commit_drm_device(struct wlr_drm_backend *drm, + const struct wlr_backend_output_state *states, size_t states_len, bool test_only); void restore_drm_device(struct wlr_drm_backend *drm); int handle_drm_event(int fd, uint32_t mask, void *data); void destroy_drm_connector(struct wlr_drm_connector *conn);