summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Stone <daniels@collabora.com>2016-11-16 11:55:20 +0000
committerDaniel Stone <daniels@collabora.com>2017-02-07 19:16:32 +0000
commitc5367a381118be0b5fcae3182c6453d6fad00b0c (patch)
tree5a7d91ec39d1799410a962fa31afc112517c0199
parentb383aee714ab180fbc48972129c474224b966711 (diff)
compositor-drm: Atomic modesetting support
Add support for using the atomic-modesetting API to apply output state. Unlike previous series, this commit does not unflip sprites_are_broken, until further work has been done with assign_planes to make it reliable. Differential Revision: https://phabricator.freedesktop.org/D1507 Signed-off-by: Daniel Stone <daniels@collabora.com> Co-authored-by: Pekka Paalanen <pekka.paalanen@collabora.co.uk> Co-authored-by: Louis-Francis Ratté-Boulianne <louis-francis.ratte-boulianne@collabora.com> Co-authored-by: Derek Foreman <derek.foreman@collabora.co.uk>
-rw-r--r--libweston/compositor-drm.c208
1 files changed, 203 insertions, 5 deletions
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index 63a2ce43..13cf6e3e 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -348,6 +348,7 @@ struct drm_output {
int vblank_pending;
int page_flip_pending;
+ int atomic_complete_pending;
int destroy_pending;
int disable_pending;
int dpms_off_pending;
@@ -1375,6 +1376,7 @@ drm_output_assign_state(struct drm_output_state *state,
enum drm_output_state_update_mode mode)
{
struct drm_output *output = state->output;
+ struct drm_backend *b = to_drm_backend(output->base.compositor);
struct drm_plane_state *plane_state;
assert(!output->state_last);
@@ -1387,6 +1389,9 @@ drm_output_assign_state(struct drm_output_state *state,
output->state_cur = state;
output->state_pending = NULL;
+ if (b->atomic_modeset && mode == DRM_OUTPUT_STATE_UPDATE_ASYNCHRONOUS)
+ output->atomic_complete_pending = 1;
+
/* Replace state_cur on each affected plane with the new state, being
* careful to dispose of orphaned (but only orphaned) previous state.
* If the previous state is not orphaned (still has an output_state
@@ -1398,7 +1403,8 @@ drm_output_assign_state(struct drm_output_state *state,
drm_plane_state_free(plane->state_cur, true);
plane->state_cur = plane_state;
- if (mode != DRM_OUTPUT_STATE_UPDATE_ASYNCHRONOUS)
+ if (mode != DRM_OUTPUT_STATE_UPDATE_ASYNCHRONOUS ||
+ b->atomic_modeset)
continue;
if (plane->type == WDRM_PLANE_TYPE_OVERLAY)
@@ -1685,7 +1691,7 @@ drm_waitvblank_pipe(struct drm_output *output)
}
static int
-drm_output_apply_state(struct drm_output_state *state)
+drm_output_apply_state_legacy(struct drm_output_state *state)
{
struct drm_output *output = state->output;
struct drm_backend *backend = to_drm_backend(output->base.compositor);
@@ -1843,6 +1849,168 @@ err:
return -1;
}
+#ifdef HAVE_DRM_ATOMIC
+static int
+crtc_add_prop(drmModeAtomicReq *req, struct drm_output *output,
+ enum wdrm_crtc_property prop, uint64_t val)
+{
+ struct property_item *item = &output->props_crtc.item[prop];
+ int ret;
+
+ if (!item)
+ return -1;
+
+ ret = drmModeAtomicAddProperty(req, output->crtc_id,
+ item->drm_prop->prop_id, val);
+ return (ret <= 0) ? -1 : 0;
+}
+
+static int
+connector_add_prop(drmModeAtomicReq *req, struct drm_output *output,
+ enum wdrm_connector_property prop, uint64_t val)
+{
+ struct property_item *item = &output->props_conn.item[prop];
+ int ret;
+
+ if (!item)
+ return -1;
+
+ ret = drmModeAtomicAddProperty(req, output->connector_id,
+ item->drm_prop->prop_id, val);
+ return (ret <= 0) ? -1 : 0;
+}
+
+static int
+plane_add_prop(drmModeAtomicReq *req, struct drm_plane *plane,
+ enum wdrm_plane_property prop, uint64_t val)
+{
+ struct property_item *item = &plane->props.item[prop];
+ int ret;
+
+ if (!item)
+ return -1;
+
+ ret = drmModeAtomicAddProperty(req, plane->plane_id,
+ item->drm_prop->prop_id, val);
+ return (ret <= 0) ? -1 : 0;
+}
+
+static int
+drm_mode_ensure_blob(struct drm_backend *backend, struct drm_mode *mode)
+{
+ int ret;
+
+ if (mode->blob_id)
+ return 0;
+
+ ret = drmModeCreatePropertyBlob(backend->drm.fd,
+ &mode->mode_info,
+ sizeof(mode->mode_info),
+ &mode->blob_id);
+ if (ret != 0)
+ weston_log("failed to create mode property blob: %m\n");
+
+ return ret;
+}
+
+static int
+drm_output_apply_state_atomic(struct drm_output_state *state)
+{
+ struct drm_output *output = state->output;
+ struct drm_backend *backend = to_drm_backend(output->base.compositor);
+ struct drm_plane_state *plane_state;
+ drmModeAtomicReq *req = drmModeAtomicAlloc();
+ struct drm_mode *current_mode = to_drm_mode(output->base.current_mode);
+ uint32_t flags = DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_NONBLOCK;
+ int ret = 0;
+
+ if (!req)
+ return -1;
+
+ if (state->dpms != output->state_cur->dpms)
+ flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
+
+ if (state->dpms == WESTON_DPMS_ON) {
+ ret = drm_mode_ensure_blob(backend, current_mode);
+ if (ret != 0)
+ goto err;
+
+ ret |= crtc_add_prop(req, output, WDRM_CRTC_MODE_ID,
+ current_mode->blob_id);
+ ret |= crtc_add_prop(req, output, WDRM_CRTC_ACTIVE, 1);
+ ret |= connector_add_prop(req, output, WDRM_CONNECTOR_CRTC_ID,
+ output->crtc_id);
+ } else {
+ ret |= crtc_add_prop(req, output, WDRM_CRTC_MODE_ID, 0);
+ ret |= crtc_add_prop(req, output, WDRM_CRTC_ACTIVE, 0);
+ ret |= connector_add_prop(req, output, WDRM_CONNECTOR_CRTC_ID,
+ 0);
+ }
+
+ if (ret != 0) {
+ weston_log("couldn't set atomic CRTC/connector state\n");
+ goto err;
+ }
+
+ wl_list_for_each(plane_state, &state->plane_list, link) {
+ struct drm_plane *plane = plane_state->plane;
+
+ ret |= plane_add_prop(req, plane, WDRM_PLANE_FB_ID,
+ plane_state->fb ? plane_state->fb->fb_id : 0);
+ ret |= plane_add_prop(req, plane, WDRM_PLANE_CRTC_ID,
+ plane_state->fb ? output->crtc_id : 0);
+ ret |= plane_add_prop(req, plane, WDRM_PLANE_SRC_X,
+ plane_state->src_x);
+ ret |= plane_add_prop(req, plane, WDRM_PLANE_SRC_Y,
+ plane_state->src_y);
+ ret |= plane_add_prop(req, plane, WDRM_PLANE_SRC_W,
+ plane_state->src_w);
+ ret |= plane_add_prop(req, plane, WDRM_PLANE_SRC_H,
+ plane_state->src_h);
+ ret |= plane_add_prop(req, plane, WDRM_PLANE_CRTC_X,
+ plane_state->dest_x);
+ ret |= plane_add_prop(req, plane, WDRM_PLANE_CRTC_Y,
+ plane_state->dest_y);
+ ret |= plane_add_prop(req, plane, WDRM_PLANE_CRTC_W,
+ plane_state->dest_w);
+ ret |= plane_add_prop(req, plane, WDRM_PLANE_CRTC_H,
+ plane_state->dest_h);
+
+ if (ret != 0) {
+ weston_log("couldn't set plane state\n");
+ goto err;
+ }
+ }
+
+ if (drmModeAtomicCommit(backend->drm.fd, req, flags, output) != 0) {
+ weston_log("couldn't commit new state: %m\n");
+ goto err;
+ }
+
+ drm_output_assign_state(state, DRM_OUTPUT_STATE_UPDATE_ASYNCHRONOUS);
+
+ return 0;
+
+err:
+ drm_output_state_free(state);
+ output->state_pending = NULL;
+ return -1;
+}
+#endif
+
+static int
+drm_output_apply_state(struct drm_output_state *state)
+{
+#ifdef HAVE_DRM_ATOMIC
+ struct drm_backend *b = to_drm_backend(state->output->base.compositor);
+
+ if (b->atomic_modeset)
+ return drm_output_apply_state_atomic(state);
+ else
+#endif
+ return drm_output_apply_state_legacy(state);
+}
+
static int
drm_output_repaint(struct weston_output *output_base,
pixman_region32_t *damage,
@@ -1976,9 +2144,12 @@ vblank_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec,
struct drm_plane_state *ps = (struct drm_plane_state *) data;
struct drm_output_state *os = ps->output_state;
struct drm_output *output = os->output;
+ struct drm_backend *b = to_drm_backend(output->base.compositor);
uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION |
WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK;
+ assert(!b->atomic_modeset);
+
drm_output_update_msc(output, frame);
output->vblank_pending--;
assert(output->vblank_pending >= 0);
@@ -1996,12 +2167,14 @@ page_flip_handler(int fd, unsigned int frame,
unsigned int sec, unsigned int usec, void *data)
{
struct drm_output *output = data;
+ struct drm_backend *b = to_drm_backend(output->base.compositor);
uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_VSYNC |
WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION |
WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK;
drm_output_update_msc(output, frame);
+ assert(!b->atomic_modeset);
assert(output->page_flip_pending);
output->page_flip_pending = 0;
@@ -2011,6 +2184,25 @@ page_flip_handler(int fd, unsigned int frame,
drm_output_update_complete(output, flags, sec, usec);
}
+static void
+atomic_flip_handler(int fd, unsigned int frame,
+ unsigned int sec, unsigned int usec, void *data)
+{
+ struct drm_output *output = data;
+ struct drm_backend *b = to_drm_backend(output->base.compositor);
+ uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_VSYNC |
+ WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION |
+ WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK;
+
+ drm_output_update_msc(output, frame);
+
+ assert(b->atomic_modeset);
+ assert(output->atomic_complete_pending);
+ output->atomic_complete_pending = 0;
+
+ drm_output_update_complete(output, flags, sec, usec);
+}
+
static uint32_t
drm_output_check_plane_format(struct drm_plane *p,
struct weston_view *ev, struct gbm_bo *bo)
@@ -2643,11 +2835,15 @@ drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mo
static int
on_drm_input(int fd, uint32_t mask, void *data)
{
+ struct drm_backend *b = data;
drmEventContext evctx;
memset(&evctx, 0, sizeof evctx);
evctx.version = DRM_EVENT_CONTEXT_VERSION;
- evctx.page_flip_handler = page_flip_handler;
+ if (b->atomic_modeset)
+ evctx.page_flip_handler = atomic_flip_handler;
+ else
+ evctx.page_flip_handler = page_flip_handler;
evctx.vblank_handler = vblank_handler;
drmHandleEvent(fd, &evctx);
@@ -3949,7 +4145,8 @@ drm_output_destroy(struct weston_output *base)
struct drm_backend *b = to_drm_backend(base->compositor);
struct drm_mode *drm_mode, *next;
- if (output->page_flip_pending || output->vblank_pending) {
+ if (output->page_flip_pending || output->vblank_pending ||
+ output->atomic_complete_pending) {
output->destroy_pending = 1;
weston_log("destroy output while page flip pending\n");
return;
@@ -3986,7 +4183,8 @@ drm_output_disable(struct weston_output *base)
struct drm_output *output = to_drm_output(base);
int ret;
- if (output->page_flip_pending || output->vblank_pending) {
+ if (output->page_flip_pending || output->vblank_pending ||
+ output->atomic_complete_pending) {
output->disable_pending = 1;
return -1;
}