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 <tzimmermann@xxxxxxx> --- 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