Introduce a distinct flag fb_damage_partial_update in plane state to
signal the option for a partial plane update. Replaces the semantics
of having no damaged areas to trigger a full update. Decoupling the
existence of damage clipping from partial plane updates will allow to
sometimes avoid plane updates at all.

By default the new flag is cleared, which triggers a full update. We
also keep doing a full update on modesetting operations or if the
framebuffer has been moved within the plane. Installing damage clipping
areas on a plane state sets the new flag and triggers a partial update
of the given areas.

Userspace that does not support damage clipping continues to work as
before.

Signed-off-by: Thomas Zimmermann <tzimmerm...@suse.de>
---
 drivers/gpu/drm/drm_atomic_state_helper.c |  1 +
 drivers/gpu/drm/drm_damage_helper.c       | 68 ++++++++++++++++-------
 include/drm/drm_plane.h                   |  9 +++
 3 files changed, 59 insertions(+), 19 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c 
b/drivers/gpu/drm/drm_atomic_state_helper.c
index bf31b9d92094..85b13c221bd8 100644
--- a/drivers/gpu/drm/drm_atomic_state_helper.c
+++ b/drivers/gpu/drm/drm_atomic_state_helper.c
@@ -338,6 +338,7 @@ void __drm_atomic_helper_plane_duplicate_state(struct 
drm_plane *plane,
        state->fence = NULL;
        state->commit = NULL;
        state->fb_damage_clips = NULL;
+       state->fb_damage_partial_update = false;
 }
 EXPORT_SYMBOL(__drm_atomic_helper_plane_duplicate_state);
 
diff --git a/drivers/gpu/drm/drm_damage_helper.c 
b/drivers/gpu/drm/drm_damage_helper.c
index 4b1f26ef119f..16f0d5a97ee3 100644
--- a/drivers/gpu/drm/drm_damage_helper.c
+++ b/drivers/gpu/drm/drm_damage_helper.c
@@ -55,30 +55,48 @@ static void convert_clip_rect_to_rect(const struct 
drm_clip_rect *src,
  * @state: The driver state object.
  * @new_plane_state: Plane state for which to verify damage.
  *
- * This helper function makes sure that damage from plane state is discarded
- * for full modeset. If there are more reasons a driver would want to do a full
- * plane update rather than processing individual damage regions, then those
- * cases should be taken care of here.
+ * This helper function makes sure that damage from plane state is set up
+ * for full plane updates if necessary. This happens for full modesets on the
+ * plane's CRTC, and for pageflips without damage. If there are more reasons
+ * a driver would want to do a full plane update rather than processing
+ * individual damage regions, then those cases should be taken care of here.
  *
- * Note that &drm_plane_state.fb_damage_clips == NULL in plane state means that
- * full plane update should happen. It also ensure helper iterator will return
- * &drm_plane_state.src as damage.
+ * Note that &drm_plane_state.fb_damage_partial_update == false in plane state
+ * means that a full plane update should happen. It also ensures the helper
+ * iterator will return &drm_plane_state.src as damage.
  */
 void drm_atomic_helper_check_plane_damage(struct drm_atomic_state *state,
                                          struct drm_plane_state 
*new_plane_state)
 {
        struct drm_crtc_state *new_crtc_state;
+       struct drm_crtc *new_crtc = new_plane_state->crtc;
+       bool partial_update = false;
 
-       if (new_plane_state->crtc) {
-               new_crtc_state = drm_atomic_get_new_crtc_state(state, 
new_plane_state->crtc);
+       if (new_crtc) {
+               new_crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc);
 
                if (WARN_ON(!new_crtc_state))
-                       return;
+                       goto out;
 
-               if (drm_atomic_crtc_needs_modeset(new_crtc_state)) {
-                       drm_property_blob_put(new_plane_state->fb_damage_clips);
-                       new_plane_state->fb_damage_clips = NULL;
-               }
+               /*
+                * The plane's CRTC does a modeset; always do full update.
+                */
+               if (drm_atomic_crtc_needs_modeset(new_crtc_state))
+                       goto out;
+       }
+
+       /*
+        * Damage clips are a good indicator for partial updates.
+        */
+       if (new_plane_state->fb_damage_clips)
+               partial_update = true;
+
+out:
+       new_plane_state->fb_damage_partial_update = partial_update;
+
+       if (!new_plane_state->fb_damage_partial_update) {
+               drm_property_blob_put(new_plane_state->fb_damage_clips);
+               new_plane_state->fb_damage_clips = NULL;
        }
 }
 EXPORT_SYMBOL(drm_atomic_helper_check_plane_damage);
@@ -224,13 +242,24 @@ drm_atomic_helper_damage_iter_init(struct 
drm_atomic_helper_damage_iter *iter,
                                   const struct drm_plane_state *state)
 {
        struct drm_rect src;
+       bool partial_update;
+
        memset(iter, 0, sizeof(*iter));
 
        if (!state || !state->crtc || !state->fb || !state->visible)
                return;
 
-       iter->clips = (struct drm_rect *)drm_plane_get_damage_clips(state);
-       iter->num_clips = drm_plane_get_damage_clips_count(state);
+       partial_update = state->fb_damage_partial_update;
+
+       /*
+        * Only allow a partial update if the framebuffer did not move
+        * within the plane. Otherwise do a full update. We have to test
+        * this here, instead of drm_atomic_helper_check_plane_damage(),
+        * as the plane's atomic_check helper could meanwhile have changed
+        * the source coordinates.
+        */
+       if (partial_update)
+               partial_update = drm_rect_equals(&state->src, &old_state->src);
 
        /* Round down for x1/y1 and round up for x2/y2 to catch all pixels */
        src = drm_plane_state_src(state);
@@ -240,9 +269,10 @@ drm_atomic_helper_damage_iter_init(struct 
drm_atomic_helper_damage_iter *iter,
        iter->plane_src.x2 = (src.x2 >> 16) + !!(src.x2 & 0xFFFF);
        iter->plane_src.y2 = (src.y2 >> 16) + !!(src.y2 & 0xFFFF);
 
-       if (!iter->clips || !drm_rect_equals(&state->src, &old_state->src)) {
-               iter->clips = NULL;
-               iter->num_clips = 0;
+       if (partial_update) {
+               iter->clips = (struct drm_rect 
*)drm_plane_get_damage_clips(state);
+               iter->num_clips = drm_plane_get_damage_clips_count(state);
+       } else {
                iter->full_update = true;
        }
 }
diff --git a/include/drm/drm_plane.h b/include/drm/drm_plane.h
index 89ea54652e87..3ba91349d799 100644
--- a/include/drm/drm_plane.h
+++ b/include/drm/drm_plane.h
@@ -190,6 +190,15 @@ struct drm_plane_state {
         */
        struct drm_property_blob *fb_damage_clips;
 
+       /**
+        * @fb_damage_partial_update:
+        *
+        * Marks the plane for a partial update. The value of this field
+        * depends on the supplied atomic state and the operation that
+        * initiated the update.
+        */
+       bool fb_damage_partial_update;
+
        /**
         * @src:
         *
-- 
2.37.3

Reply via email to