drm/atomic-helper: Add {begin,end}_fb_access to plane helpers
authorThomas Zimmermann <tzimmermann@suse.de>
Tue, 25 Oct 2022 10:17:36 +0000 (12:17 +0200)
committerThomas Zimmermann <tzimmermann@suse.de>
Tue, 8 Nov 2022 16:10:27 +0000 (17:10 +0100)
Add {begin,end}_fb_access helpers to run at the beginning and end of
an atomic commit. The begin_fb_access helper acquires resources that
are necessary to perform the atomic commit. It it similar to prepare_fb,
except that the resources are to be released at the end of the commit.
Resources acquired by prepare_fb are held until after the next pageflip.

The end_fb_access helper performs the corresponding resource cleanup.
Atomic helpers call it with the new plane state. This is different from
cleanup_fb, which releases resources of the old plane state.

v2:
* fix typos in commit message (Javier)

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20221025101737.8874-2-tzimmermann@suse.de
drivers/gpu/drm/drm_atomic_helper.c
drivers/gpu/drm/drm_gem_atomic_helper.c
drivers/gpu/drm/drm_simple_kms_helper.c
include/drm/drm_modeset_helper_vtables.h
include/drm/drm_simple_kms_helper.h

index 1a586b3..d579fd8 100644 (file)
@@ -2536,7 +2536,7 @@ int drm_atomic_helper_prepare_planes(struct drm_device *dev,
                if (funcs->prepare_fb) {
                        ret = funcs->prepare_fb(plane, new_plane_state);
                        if (ret)
-                               goto fail;
+                               goto fail_prepare_fb;
                } else {
                        WARN_ON_ONCE(funcs->cleanup_fb);
 
@@ -2545,13 +2545,34 @@ int drm_atomic_helper_prepare_planes(struct drm_device *dev,
 
                        ret = drm_gem_plane_helper_prepare_fb(plane, new_plane_state);
                        if (ret)
-                               goto fail;
+                               goto fail_prepare_fb;
+               }
+       }
+
+       for_each_new_plane_in_state(state, plane, new_plane_state, i) {
+               const struct drm_plane_helper_funcs *funcs = plane->helper_private;
+
+               if (funcs->begin_fb_access) {
+                       ret = funcs->begin_fb_access(plane, new_plane_state);
+                       if (ret)
+                               goto fail_begin_fb_access;
                }
        }
 
        return 0;
 
-fail:
+fail_begin_fb_access:
+       for_each_new_plane_in_state(state, plane, new_plane_state, j) {
+               const struct drm_plane_helper_funcs *funcs = plane->helper_private;
+
+               if (j >= i)
+                       continue;
+
+               if (funcs->end_fb_access)
+                       funcs->end_fb_access(plane, new_plane_state);
+       }
+       i = j; /* set i to upper limit to cleanup all planes */
+fail_prepare_fb:
        for_each_new_plane_in_state(state, plane, new_plane_state, j) {
                const struct drm_plane_helper_funcs *funcs;
 
@@ -2828,6 +2849,13 @@ void drm_atomic_helper_cleanup_planes(struct drm_device *dev,
        int i;
 
        for_each_oldnew_plane_in_state(old_state, plane, old_plane_state, new_plane_state, i) {
+               const struct drm_plane_helper_funcs *funcs = plane->helper_private;
+
+               if (funcs->end_fb_access)
+                       funcs->end_fb_access(plane, new_plane_state);
+       }
+
+       for_each_oldnew_plane_in_state(old_state, plane, old_plane_state, new_plane_state, i) {
                const struct drm_plane_helper_funcs *funcs;
                struct drm_plane_state *plane_state;
 
index b6a0110..1de0a08 100644 (file)
@@ -414,16 +414,14 @@ void drm_gem_cleanup_shadow_fb(struct drm_plane *plane, struct drm_plane_state *
 EXPORT_SYMBOL(drm_gem_cleanup_shadow_fb);
 
 /**
- * drm_gem_simple_kms_prepare_shadow_fb - prepares shadow framebuffers
+ * drm_gem_simple_kms_begin_shadow_fb_access - prepares shadow framebuffers for CPU access
  * @pipe: the simple display pipe
  * @plane_state: the plane state of type struct drm_shadow_plane_state
  *
- * This function implements struct drm_simple_display_funcs.prepare_fb. It
- * maps all buffer objects of the plane's framebuffer into kernel address
- * space and stores them in struct drm_shadow_plane_state.map. The
- * framebuffer will be synchronized as part of the atomic commit.
+ * This function implements struct drm_simple_display_funcs.begin_fb_access.
  *
- * See drm_gem_simple_kms_cleanup_shadow_fb() for cleanup.
+ * See drm_gem_begin_shadow_fb_access() for details and
+ * drm_gem_simple_kms_cleanup_shadow_fb() for cleanup.
  *
  * Returns:
  * 0 on success, or a negative errno code otherwise.
@@ -436,14 +434,15 @@ int drm_gem_simple_kms_prepare_shadow_fb(struct drm_simple_display_pipe *pipe,
 EXPORT_SYMBOL(drm_gem_simple_kms_prepare_shadow_fb);
 
 /**
- * drm_gem_simple_kms_cleanup_shadow_fb - releases shadow framebuffers
+ * drm_gem_simple_kms_end_shadow_fb_access - releases shadow framebuffers from CPU access
  * @pipe: the simple display pipe
  * @plane_state: the plane state of type struct drm_shadow_plane_state
  *
- * This function implements struct drm_simple_display_funcs.cleanup_fb.
- * This function unmaps all buffer objects of the plane's framebuffer.
+ * This function implements struct drm_simple_display_funcs.end_fb_access.
+ * It undoes all effects of drm_gem_simple_kms_begin_shadow_fb_access() in
+ * reverse order.
  *
- * See drm_gem_simple_kms_prepare_shadow_fb().
+ * See drm_gem_simple_kms_begin_shadow_fb_access().
  */
 void drm_gem_simple_kms_cleanup_shadow_fb(struct drm_simple_display_pipe *pipe,
                                          struct drm_plane_state *plane_state)
index 31233c6..3ef420e 100644 (file)
@@ -285,6 +285,30 @@ static void drm_simple_kms_plane_cleanup_fb(struct drm_plane *plane,
        pipe->funcs->cleanup_fb(pipe, state);
 }
 
+static int drm_simple_kms_plane_begin_fb_access(struct drm_plane *plane,
+                                               struct drm_plane_state *new_plane_state)
+{
+       struct drm_simple_display_pipe *pipe;
+
+       pipe = container_of(plane, struct drm_simple_display_pipe, plane);
+       if (!pipe->funcs || !pipe->funcs->begin_fb_access)
+               return 0;
+
+       return pipe->funcs->begin_fb_access(pipe, new_plane_state);
+}
+
+static void drm_simple_kms_plane_end_fb_access(struct drm_plane *plane,
+                                              struct drm_plane_state *new_plane_state)
+{
+       struct drm_simple_display_pipe *pipe;
+
+       pipe = container_of(plane, struct drm_simple_display_pipe, plane);
+       if (!pipe->funcs || !pipe->funcs->end_fb_access)
+               return;
+
+       pipe->funcs->end_fb_access(pipe, new_plane_state);
+}
+
 static bool drm_simple_kms_format_mod_supported(struct drm_plane *plane,
                                                uint32_t format,
                                                uint64_t modifier)
@@ -295,6 +319,8 @@ static bool drm_simple_kms_format_mod_supported(struct drm_plane *plane,
 static const struct drm_plane_helper_funcs drm_simple_kms_plane_helper_funcs = {
        .prepare_fb = drm_simple_kms_plane_prepare_fb,
        .cleanup_fb = drm_simple_kms_plane_cleanup_fb,
+       .begin_fb_access = drm_simple_kms_plane_begin_fb_access,
+       .end_fb_access = drm_simple_kms_plane_end_fb_access,
        .atomic_check = drm_simple_kms_plane_atomic_check,
        .atomic_update = drm_simple_kms_plane_atomic_update,
 };
index fafa70a..d9f2254 100644 (file)
@@ -1184,11 +1184,20 @@ struct drm_plane_helper_funcs {
         * can call drm_gem_plane_helper_prepare_fb() from their @prepare_fb
         * hook.
         *
+        * The resources acquired in @prepare_fb persist after the end of
+        * the atomic commit. Resources that can be release at the commit's end
+        * should be acquired in @begin_fb_access and released in @end_fb_access.
+        * For example, a GEM buffer's pin operation belongs into @prepare_fb to
+        * keep the buffer pinned after the commit. But a vmap operation for
+        * shadow-plane helpers belongs into @begin_fb_access, so that atomic
+        * helpers remove the mapping at the end of the commit.
+        *
         * The helpers will call @cleanup_fb with matching arguments for every
         * successful call to this hook.
         *
         * This callback is used by the atomic modeset helpers and by the
-        * transitional plane helpers, but it is optional.
+        * transitional plane helpers, but it is optional. See @begin_fb_access
+        * for preparing per-commit resources.
         *
         * RETURNS:
         *
@@ -1212,6 +1221,36 @@ struct drm_plane_helper_funcs {
                           struct drm_plane_state *old_state);
 
        /**
+        * @begin_fb_access:
+        *
+        * This hook prepares the plane for access during an atomic commit.
+        * In contrast to @prepare_fb, resources acquired in @begin_fb_access,
+        * are released at the end of the atomic commit in @end_fb_access.
+        *
+        * For example, with shadow-plane helpers, the GEM buffer's vmap
+        * operation belongs into @begin_fb_access, so that the buffer's
+        * memory will be unmapped at the end of the commit in @end_fb_access.
+        * But a GEM buffer's pin operation belongs into @prepare_fb
+        * to keep the buffer pinned after the commit.
+        *
+        * The callback is used by the atomic modeset helpers, but it is optional.
+        * See @end_fb_cleanup for undoing the effects of @begin_fb_access and
+        * @prepare_fb for acquiring resources until the next pageflip.
+        *
+        * Returns:
+        * 0 on success, or a negative errno code otherwise.
+        */
+       int (*begin_fb_access)(struct drm_plane *plane, struct drm_plane_state *new_plane_state);
+
+       /**
+        * @end_fb_access:
+        *
+        * This hook cleans up resources allocated by @begin_fb_access. It it called
+        * at the end of a commit for the new plane state.
+        */
+       void (*end_fb_access)(struct drm_plane *plane, struct drm_plane_state *new_plane_state);
+
+       /**
         * @atomic_check:
         *
         * Drivers should check plane specific constraints in this hook.
index 0b3647e..2298fe3 100644 (file)
@@ -136,6 +136,26 @@ struct drm_simple_display_pipe_funcs {
                           struct drm_plane_state *plane_state);
 
        /**
+        * @begin_fb_access:
+        *
+        * Optional, called by &drm_plane_helper_funcs.begin_fb_access. Please read
+        * the documentation for the &drm_plane_helper_funcs.begin_fb_access hook for
+        * more details.
+        */
+       int (*begin_fb_access)(struct drm_simple_display_pipe *pipe,
+                              struct drm_plane_state *new_plane_state);
+
+       /**
+        * @end_fb_access:
+        *
+        * Optional, called by &drm_plane_helper_funcs.end_fb_access. Please read
+        * the documentation for the &drm_plane_helper_funcs.end_fb_access hook for
+        * more details.
+        */
+       void (*end_fb_access)(struct drm_simple_display_pipe *pipe,
+                             struct drm_plane_state *plane_state);
+
+       /**
         * @enable_vblank:
         *
         * Optional, called by &drm_crtc_funcs.enable_vblank. Please read