// SPDX-License-Identifier: GPL-2.0 OR MIT1/**************************************************************************2*3* Copyright (c) 2018 VMware, Inc., Palo Alto, CA., USA4* All Rights Reserved.5*6* Permission is hereby granted, free of charge, to any person obtaining a7* copy of this software and associated documentation files (the8* "Software"), to deal in the Software without restriction, including9* without limitation the rights to use, copy, modify, merge, publish,10* distribute, sub license, and/or sell copies of the Software, and to11* permit persons to whom the Software is furnished to do so, subject to12* the following conditions:13*14* The above copyright notice and this permission notice (including the15* next paragraph) shall be included in all copies or substantial portions16* of the Software.17*18* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR19* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,20* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL21* THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,22* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR23* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE24* USE OR OTHER DEALINGS IN THE SOFTWARE.25*26* Authors:27* Deepak Rawat <[email protected]>28* Rob Clark <[email protected]>29*30**************************************************************************/3132#include <linux/export.h>3334#include <drm/drm_atomic.h>35#include <drm/drm_damage_helper.h>36#include <drm/drm_device.h>37#include <drm/drm_framebuffer.h>3839static void convert_clip_rect_to_rect(const struct drm_clip_rect *src,40struct drm_mode_rect *dest,41uint32_t num_clips, uint32_t src_inc)42{43while (num_clips > 0) {44dest->x1 = src->x1;45dest->y1 = src->y1;46dest->x2 = src->x2;47dest->y2 = src->y2;48src += src_inc;49dest++;50num_clips--;51}52}5354/**55* drm_atomic_helper_check_plane_damage - Verify plane damage on atomic_check.56* @state: The driver state object.57* @plane_state: Plane state for which to verify damage.58*59* This helper function makes sure that damage from plane state is discarded60* for full modeset. If there are more reasons a driver would want to do a full61* plane update rather than processing individual damage regions, then those62* cases should be taken care of here.63*64* Note that &drm_plane_state.fb_damage_clips == NULL in plane state means that65* full plane update should happen. It also ensure helper iterator will return66* &drm_plane_state.src as damage.67*/68void drm_atomic_helper_check_plane_damage(struct drm_atomic_state *state,69struct drm_plane_state *plane_state)70{71struct drm_crtc_state *crtc_state;7273if (plane_state->crtc) {74crtc_state = drm_atomic_get_new_crtc_state(state,75plane_state->crtc);7677if (WARN_ON(!crtc_state))78return;7980if (drm_atomic_crtc_needs_modeset(crtc_state)) {81drm_property_blob_put(plane_state->fb_damage_clips);82plane_state->fb_damage_clips = NULL;83}84}85}86EXPORT_SYMBOL(drm_atomic_helper_check_plane_damage);8788/**89* drm_atomic_helper_dirtyfb - Helper for dirtyfb.90* @fb: DRM framebuffer.91* @file_priv: Drm file for the ioctl call.92* @flags: Dirty fb annotate flags.93* @color: Color for annotate fill.94* @clips: Dirty region.95* @num_clips: Count of clip in clips.96*97* A helper to implement &drm_framebuffer_funcs.dirty using damage interface98* during plane update. If num_clips is 0 then this helper will do a full plane99* update. This is the same behaviour expected by DIRTFB IOCTL.100*101* Note that this helper is blocking implementation. This is what current102* drivers and userspace expect in their DIRTYFB IOCTL implementation, as a way103* to rate-limit userspace and make sure its rendering doesn't get ahead of104* uploading new data too much.105*106* Return: Zero on success, negative errno on failure.107*/108int drm_atomic_helper_dirtyfb(struct drm_framebuffer *fb,109struct drm_file *file_priv, unsigned int flags,110unsigned int color, struct drm_clip_rect *clips,111unsigned int num_clips)112{113struct drm_modeset_acquire_ctx ctx;114struct drm_property_blob *damage = NULL;115struct drm_mode_rect *rects = NULL;116struct drm_atomic_state *state;117struct drm_plane *plane;118int ret = 0;119120/*121* When called from ioctl, we are interruptible, but not when called122* internally (ie. defio worker)123*/124drm_modeset_acquire_init(&ctx,125file_priv ? DRM_MODESET_ACQUIRE_INTERRUPTIBLE : 0);126127state = drm_atomic_state_alloc(fb->dev);128if (!state) {129ret = -ENOMEM;130goto out_drop_locks;131}132state->acquire_ctx = &ctx;133134if (clips) {135uint32_t inc = 1;136137if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY) {138inc = 2;139num_clips /= 2;140}141142rects = kcalloc(num_clips, sizeof(*rects), GFP_KERNEL);143if (!rects) {144ret = -ENOMEM;145goto out;146}147148convert_clip_rect_to_rect(clips, rects, num_clips, inc);149damage = drm_property_create_blob(fb->dev,150num_clips * sizeof(*rects),151rects);152if (IS_ERR(damage)) {153ret = PTR_ERR(damage);154damage = NULL;155goto out;156}157}158159retry:160drm_for_each_plane(plane, fb->dev) {161struct drm_plane_state *plane_state;162163ret = drm_modeset_lock(&plane->mutex, state->acquire_ctx);164if (ret)165goto out;166167if (plane->state->fb != fb) {168drm_modeset_unlock(&plane->mutex);169continue;170}171172plane_state = drm_atomic_get_plane_state(state, plane);173if (IS_ERR(plane_state)) {174ret = PTR_ERR(plane_state);175goto out;176}177178drm_property_replace_blob(&plane_state->fb_damage_clips,179damage);180}181182ret = drm_atomic_commit(state);183184out:185if (ret == -EDEADLK) {186drm_atomic_state_clear(state);187ret = drm_modeset_backoff(&ctx);188if (!ret)189goto retry;190}191192drm_property_blob_put(damage);193kfree(rects);194drm_atomic_state_put(state);195196out_drop_locks:197drm_modeset_drop_locks(&ctx);198drm_modeset_acquire_fini(&ctx);199200return ret;201202}203EXPORT_SYMBOL(drm_atomic_helper_dirtyfb);204205/**206* drm_atomic_helper_damage_iter_init - Initialize the damage iterator.207* @iter: The iterator to initialize.208* @old_state: Old plane state for validation.209* @state: Plane state from which to iterate the damage clips.210*211* Initialize an iterator, which clips plane damage212* &drm_plane_state.fb_damage_clips to plane &drm_plane_state.src. This iterator213* returns full plane src in case damage is not present because either214* user-space didn't sent or driver discarded it (it want to do full plane215* update). Currently this iterator returns full plane src in case plane src216* changed but that can be changed in future to return damage.217*218* For the case when plane is not visible or plane update should not happen the219* first call to iter_next will return false. Note that this helper use clipped220* &drm_plane_state.src, so driver calling this helper should have called221* drm_atomic_helper_check_plane_state() earlier.222*/223void224drm_atomic_helper_damage_iter_init(struct drm_atomic_helper_damage_iter *iter,225const struct drm_plane_state *old_state,226const struct drm_plane_state *state)227{228struct drm_rect src;229memset(iter, 0, sizeof(*iter));230231if (!state || !state->crtc || !state->fb || !state->visible)232return;233234iter->clips = (struct drm_rect *)drm_plane_get_damage_clips(state);235iter->num_clips = drm_plane_get_damage_clips_count(state);236237/* Round down for x1/y1 and round up for x2/y2 to catch all pixels */238src = drm_plane_state_src(state);239240iter->plane_src.x1 = src.x1 >> 16;241iter->plane_src.y1 = src.y1 >> 16;242iter->plane_src.x2 = (src.x2 >> 16) + !!(src.x2 & 0xFFFF);243iter->plane_src.y2 = (src.y2 >> 16) + !!(src.y2 & 0xFFFF);244245if (!iter->clips || state->ignore_damage_clips ||246!drm_rect_equals(&state->src, &old_state->src)) {247iter->clips = NULL;248iter->num_clips = 0;249iter->full_update = true;250}251}252EXPORT_SYMBOL(drm_atomic_helper_damage_iter_init);253254/**255* drm_atomic_helper_damage_iter_next - Advance the damage iterator.256* @iter: The iterator to advance.257* @rect: Return a rectangle in fb coordinate clipped to plane src.258*259* Since plane src is in 16.16 fixed point and damage clips are whole number,260* this iterator round off clips that intersect with plane src. Round down for261* x1/y1 and round up for x2/y2 for the intersected coordinate. Similar rounding262* off for full plane src, in case it's returned as damage. This iterator will263* skip damage clips outside of plane src.264*265* Return: True if the output is valid, false if reached the end.266*267* If the first call to iterator next returns false then it means no need to268* update the plane.269*/270bool271drm_atomic_helper_damage_iter_next(struct drm_atomic_helper_damage_iter *iter,272struct drm_rect *rect)273{274bool ret = false;275276if (iter->full_update) {277*rect = iter->plane_src;278iter->full_update = false;279return true;280}281282while (iter->curr_clip < iter->num_clips) {283*rect = iter->clips[iter->curr_clip];284iter->curr_clip++;285286if (drm_rect_intersect(rect, &iter->plane_src)) {287ret = true;288break;289}290}291292return ret;293}294EXPORT_SYMBOL(drm_atomic_helper_damage_iter_next);295296/**297* drm_atomic_helper_damage_merged - Merged plane damage298* @old_state: Old plane state for validation.299* @state: Plane state from which to iterate the damage clips.300* @rect: Returns the merged damage rectangle301*302* This function merges any valid plane damage clips into one rectangle and303* returns it in @rect.304*305* For details see: drm_atomic_helper_damage_iter_init() and306* drm_atomic_helper_damage_iter_next().307*308* Returns:309* True if there is valid plane damage otherwise false.310*/311bool drm_atomic_helper_damage_merged(const struct drm_plane_state *old_state,312const struct drm_plane_state *state,313struct drm_rect *rect)314{315struct drm_atomic_helper_damage_iter iter;316struct drm_rect clip;317bool valid = false;318319rect->x1 = INT_MAX;320rect->y1 = INT_MAX;321rect->x2 = 0;322rect->y2 = 0;323324drm_atomic_helper_damage_iter_init(&iter, old_state, state);325drm_atomic_for_each_plane_damage(&iter, &clip) {326rect->x1 = min(rect->x1, clip.x1);327rect->y1 = min(rect->y1, clip.y1);328rect->x2 = max(rect->x2, clip.x2);329rect->y2 = max(rect->y2, clip.y2);330valid = true;331}332333return valid;334}335EXPORT_SYMBOL(drm_atomic_helper_damage_merged);336337338