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 <dani...@collabora.com>
Co-authored-by: Pekka Paalanen <pekka.paala...@collabora.co.uk>
Co-authored-by: Louis-Francis Ratté-Boulianne 
<louis-francis.ratte-boulia...@collabora.com>
Co-authored-by: Derek Foreman <derek.fore...@collabora.co.uk>
---
 libweston/compositor-drm.c | 208 +++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 203 insertions(+), 5 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index b33d519..2f9415c 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -349,6 +349,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;
@@ -1376,6 +1377,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);
@@ -1388,6 +1390,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
@@ -1399,7 +1404,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)
@@ -1686,7 +1692,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);
@@ -1844,6 +1850,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)
@@ -1977,9 +2145,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);
@@ -1997,12 +2168,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;
 
@@ -2012,6 +2185,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)
@@ -2633,11 +2825,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);
 
@@ -3956,7 +4152,8 @@ drm_output_destroy(struct weston_output *base)
        struct drm_output *output = to_drm_output(base);
        struct drm_backend *b = to_drm_backend(base->compositor);
 
-       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;
@@ -3989,7 +4186,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;
        }
-- 
2.9.3

_______________________________________________
wayland-devel mailing list
wayland-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/wayland-devel

Reply via email to