root/drivers/gpu/drm/drm_damage_helper.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. convert_clip_rect_to_rect
  2. drm_plane_enable_fb_damage_clips
  3. drm_atomic_helper_check_plane_damage
  4. drm_atomic_helper_dirtyfb
  5. drm_atomic_helper_damage_iter_init
  6. drm_atomic_helper_damage_iter_next
  7. drm_atomic_helper_damage_merged

   1 // SPDX-License-Identifier: GPL-2.0 OR MIT
   2 /**************************************************************************
   3  *
   4  * Copyright (c) 2018 VMware, Inc., Palo Alto, CA., USA
   5  * All Rights Reserved.
   6  *
   7  * Permission is hereby granted, free of charge, to any person obtaining a
   8  * copy of this software and associated documentation files (the
   9  * "Software"), to deal in the Software without restriction, including
  10  * without limitation the rights to use, copy, modify, merge, publish,
  11  * distribute, sub license, and/or sell copies of the Software, and to
  12  * permit persons to whom the Software is furnished to do so, subject to
  13  * the following conditions:
  14  *
  15  * The above copyright notice and this permission notice (including the
  16  * next paragraph) shall be included in all copies or substantial portions
  17  * of the Software.
  18  *
  19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
  22  * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
  23  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  24  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
  25  * USE OR OTHER DEALINGS IN THE SOFTWARE.
  26  *
  27  * Authors:
  28  * Deepak Rawat <drawat@vmware.com>
  29  * Rob Clark <robdclark@gmail.com>
  30  *
  31  **************************************************************************/
  32 
  33 #include <drm/drm_atomic.h>
  34 #include <drm/drm_damage_helper.h>
  35 #include <drm/drm_device.h>
  36 
  37 /**
  38  * DOC: overview
  39  *
  40  * FB_DAMAGE_CLIPS is an optional plane property which provides a means to
  41  * specify a list of damage rectangles on a plane in framebuffer coordinates of
  42  * the framebuffer attached to the plane. In current context damage is the area
  43  * of plane framebuffer that has changed since last plane update (also called
  44  * page-flip), irrespective of whether currently attached framebuffer is same as
  45  * framebuffer attached during last plane update or not.
  46  *
  47  * FB_DAMAGE_CLIPS is a hint to kernel which could be helpful for some drivers
  48  * to optimize internally especially for virtual devices where each framebuffer
  49  * change needs to be transmitted over network, usb, etc.
  50  *
  51  * Since FB_DAMAGE_CLIPS is a hint so it is an optional property. User-space can
  52  * ignore damage clips property and in that case driver will do a full plane
  53  * update. In case damage clips are provided then it is guaranteed that the area
  54  * inside damage clips will be updated to plane. For efficiency driver can do
  55  * full update or can update more than specified in damage clips. Since driver
  56  * is free to read more, user-space must always render the entire visible
  57  * framebuffer. Otherwise there can be corruptions. Also, if a user-space
  58  * provides damage clips which doesn't encompass the actual damage to
  59  * framebuffer (since last plane update) can result in incorrect rendering.
  60  *
  61  * FB_DAMAGE_CLIPS is a blob property with the layout of blob data is simply an
  62  * array of &drm_mode_rect. Unlike plane &drm_plane_state.src coordinates,
  63  * damage clips are not in 16.16 fixed point. Similar to plane src in
  64  * framebuffer, damage clips cannot be negative. In damage clip, x1/y1 are
  65  * inclusive and x2/y2 are exclusive. While kernel does not error for overlapped
  66  * damage clips, it is strongly discouraged.
  67  *
  68  * Drivers that are interested in damage interface for plane should enable
  69  * FB_DAMAGE_CLIPS property by calling drm_plane_enable_fb_damage_clips().
  70  * Drivers implementing damage can use drm_atomic_helper_damage_iter_init() and
  71  * drm_atomic_helper_damage_iter_next() helper iterator function to get damage
  72  * rectangles clipped to &drm_plane_state.src.
  73  */
  74 
  75 static void convert_clip_rect_to_rect(const struct drm_clip_rect *src,
  76                                       struct drm_mode_rect *dest,
  77                                       uint32_t num_clips, uint32_t src_inc)
  78 {
  79         while (num_clips > 0) {
  80                 dest->x1 = src->x1;
  81                 dest->y1 = src->y1;
  82                 dest->x2 = src->x2;
  83                 dest->y2 = src->y2;
  84                 src += src_inc;
  85                 dest++;
  86                 num_clips--;
  87         }
  88 }
  89 
  90 /**
  91  * drm_plane_enable_fb_damage_clips - Enables plane fb damage clips property.
  92  * @plane: Plane on which to enable damage clips property.
  93  *
  94  * This function lets driver to enable the damage clips property on a plane.
  95  */
  96 void drm_plane_enable_fb_damage_clips(struct drm_plane *plane)
  97 {
  98         struct drm_device *dev = plane->dev;
  99         struct drm_mode_config *config = &dev->mode_config;
 100 
 101         drm_object_attach_property(&plane->base, config->prop_fb_damage_clips,
 102                                    0);
 103 }
 104 EXPORT_SYMBOL(drm_plane_enable_fb_damage_clips);
 105 
 106 /**
 107  * drm_atomic_helper_check_plane_damage - Verify plane damage on atomic_check.
 108  * @state: The driver state object.
 109  * @plane_state: Plane state for which to verify damage.
 110  *
 111  * This helper function makes sure that damage from plane state is discarded
 112  * for full modeset. If there are more reasons a driver would want to do a full
 113  * plane update rather than processing individual damage regions, then those
 114  * cases should be taken care of here.
 115  *
 116  * Note that &drm_plane_state.fb_damage_clips == NULL in plane state means that
 117  * full plane update should happen. It also ensure helper iterator will return
 118  * &drm_plane_state.src as damage.
 119  */
 120 void drm_atomic_helper_check_plane_damage(struct drm_atomic_state *state,
 121                                           struct drm_plane_state *plane_state)
 122 {
 123         struct drm_crtc_state *crtc_state;
 124 
 125         if (plane_state->crtc) {
 126                 crtc_state = drm_atomic_get_new_crtc_state(state,
 127                                                            plane_state->crtc);
 128 
 129                 if (WARN_ON(!crtc_state))
 130                         return;
 131 
 132                 if (drm_atomic_crtc_needs_modeset(crtc_state)) {
 133                         drm_property_blob_put(plane_state->fb_damage_clips);
 134                         plane_state->fb_damage_clips = NULL;
 135                 }
 136         }
 137 }
 138 EXPORT_SYMBOL(drm_atomic_helper_check_plane_damage);
 139 
 140 /**
 141  * drm_atomic_helper_dirtyfb - Helper for dirtyfb.
 142  * @fb: DRM framebuffer.
 143  * @file_priv: Drm file for the ioctl call.
 144  * @flags: Dirty fb annotate flags.
 145  * @color: Color for annotate fill.
 146  * @clips: Dirty region.
 147  * @num_clips: Count of clip in clips.
 148  *
 149  * A helper to implement &drm_framebuffer_funcs.dirty using damage interface
 150  * during plane update. If num_clips is 0 then this helper will do a full plane
 151  * update. This is the same behaviour expected by DIRTFB IOCTL.
 152  *
 153  * Note that this helper is blocking implementation. This is what current
 154  * drivers and userspace expect in their DIRTYFB IOCTL implementation, as a way
 155  * to rate-limit userspace and make sure its rendering doesn't get ahead of
 156  * uploading new data too much.
 157  *
 158  * Return: Zero on success, negative errno on failure.
 159  */
 160 int drm_atomic_helper_dirtyfb(struct drm_framebuffer *fb,
 161                               struct drm_file *file_priv, unsigned int flags,
 162                               unsigned int color, struct drm_clip_rect *clips,
 163                               unsigned int num_clips)
 164 {
 165         struct drm_modeset_acquire_ctx ctx;
 166         struct drm_property_blob *damage = NULL;
 167         struct drm_mode_rect *rects = NULL;
 168         struct drm_atomic_state *state;
 169         struct drm_plane *plane;
 170         int ret = 0;
 171 
 172         /*
 173          * When called from ioctl, we are interruptable, but not when called
 174          * internally (ie. defio worker)
 175          */
 176         drm_modeset_acquire_init(&ctx,
 177                 file_priv ? DRM_MODESET_ACQUIRE_INTERRUPTIBLE : 0);
 178 
 179         state = drm_atomic_state_alloc(fb->dev);
 180         if (!state) {
 181                 ret = -ENOMEM;
 182                 goto out_drop_locks;
 183         }
 184         state->acquire_ctx = &ctx;
 185 
 186         if (clips) {
 187                 uint32_t inc = 1;
 188 
 189                 if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY) {
 190                         inc = 2;
 191                         num_clips /= 2;
 192                 }
 193 
 194                 rects = kcalloc(num_clips, sizeof(*rects), GFP_KERNEL);
 195                 if (!rects) {
 196                         ret = -ENOMEM;
 197                         goto out;
 198                 }
 199 
 200                 convert_clip_rect_to_rect(clips, rects, num_clips, inc);
 201                 damage = drm_property_create_blob(fb->dev,
 202                                                   num_clips * sizeof(*rects),
 203                                                   rects);
 204                 if (IS_ERR(damage)) {
 205                         ret = PTR_ERR(damage);
 206                         damage = NULL;
 207                         goto out;
 208                 }
 209         }
 210 
 211 retry:
 212         drm_for_each_plane(plane, fb->dev) {
 213                 struct drm_plane_state *plane_state;
 214 
 215                 ret = drm_modeset_lock(&plane->mutex, state->acquire_ctx);
 216                 if (ret)
 217                         goto out;
 218 
 219                 if (plane->state->fb != fb) {
 220                         drm_modeset_unlock(&plane->mutex);
 221                         continue;
 222                 }
 223 
 224                 plane_state = drm_atomic_get_plane_state(state, plane);
 225                 if (IS_ERR(plane_state)) {
 226                         ret = PTR_ERR(plane_state);
 227                         goto out;
 228                 }
 229 
 230                 drm_property_replace_blob(&plane_state->fb_damage_clips,
 231                                           damage);
 232         }
 233 
 234         ret = drm_atomic_commit(state);
 235 
 236 out:
 237         if (ret == -EDEADLK) {
 238                 drm_atomic_state_clear(state);
 239                 ret = drm_modeset_backoff(&ctx);
 240                 if (!ret)
 241                         goto retry;
 242         }
 243 
 244         drm_property_blob_put(damage);
 245         kfree(rects);
 246         drm_atomic_state_put(state);
 247 
 248 out_drop_locks:
 249         drm_modeset_drop_locks(&ctx);
 250         drm_modeset_acquire_fini(&ctx);
 251 
 252         return ret;
 253 
 254 }
 255 EXPORT_SYMBOL(drm_atomic_helper_dirtyfb);
 256 
 257 /**
 258  * drm_atomic_helper_damage_iter_init - Initialize the damage iterator.
 259  * @iter: The iterator to initialize.
 260  * @old_state: Old plane state for validation.
 261  * @state: Plane state from which to iterate the damage clips.
 262  *
 263  * Initialize an iterator, which clips plane damage
 264  * &drm_plane_state.fb_damage_clips to plane &drm_plane_state.src. This iterator
 265  * returns full plane src in case damage is not present because either
 266  * user-space didn't sent or driver discarded it (it want to do full plane
 267  * update). Currently this iterator returns full plane src in case plane src
 268  * changed but that can be changed in future to return damage.
 269  *
 270  * For the case when plane is not visible or plane update should not happen the
 271  * first call to iter_next will return false. Note that this helper use clipped
 272  * &drm_plane_state.src, so driver calling this helper should have called
 273  * drm_atomic_helper_check_plane_state() earlier.
 274  */
 275 void
 276 drm_atomic_helper_damage_iter_init(struct drm_atomic_helper_damage_iter *iter,
 277                                    const struct drm_plane_state *old_state,
 278                                    const struct drm_plane_state *state)
 279 {
 280         memset(iter, 0, sizeof(*iter));
 281 
 282         if (!state || !state->crtc || !state->fb || !state->visible)
 283                 return;
 284 
 285         iter->clips = drm_helper_get_plane_damage_clips(state);
 286         iter->num_clips = drm_plane_get_damage_clips_count(state);
 287 
 288         /* Round down for x1/y1 and round up for x2/y2 to catch all pixels */
 289         iter->plane_src.x1 = state->src.x1 >> 16;
 290         iter->plane_src.y1 = state->src.y1 >> 16;
 291         iter->plane_src.x2 = (state->src.x2 >> 16) + !!(state->src.x2 & 0xFFFF);
 292         iter->plane_src.y2 = (state->src.y2 >> 16) + !!(state->src.y2 & 0xFFFF);
 293 
 294         if (!iter->clips || !drm_rect_equals(&state->src, &old_state->src)) {
 295                 iter->clips = NULL;
 296                 iter->num_clips = 0;
 297                 iter->full_update = true;
 298         }
 299 }
 300 EXPORT_SYMBOL(drm_atomic_helper_damage_iter_init);
 301 
 302 /**
 303  * drm_atomic_helper_damage_iter_next - Advance the damage iterator.
 304  * @iter: The iterator to advance.
 305  * @rect: Return a rectangle in fb coordinate clipped to plane src.
 306  *
 307  * Since plane src is in 16.16 fixed point and damage clips are whole number,
 308  * this iterator round off clips that intersect with plane src. Round down for
 309  * x1/y1 and round up for x2/y2 for the intersected coordinate. Similar rounding
 310  * off for full plane src, in case it's returned as damage. This iterator will
 311  * skip damage clips outside of plane src.
 312  *
 313  * Return: True if the output is valid, false if reached the end.
 314  *
 315  * If the first call to iterator next returns false then it means no need to
 316  * update the plane.
 317  */
 318 bool
 319 drm_atomic_helper_damage_iter_next(struct drm_atomic_helper_damage_iter *iter,
 320                                    struct drm_rect *rect)
 321 {
 322         bool ret = false;
 323 
 324         if (iter->full_update) {
 325                 *rect = iter->plane_src;
 326                 iter->full_update = false;
 327                 return true;
 328         }
 329 
 330         while (iter->curr_clip < iter->num_clips) {
 331                 *rect = iter->clips[iter->curr_clip];
 332                 iter->curr_clip++;
 333 
 334                 if (drm_rect_intersect(rect, &iter->plane_src)) {
 335                         ret = true;
 336                         break;
 337                 }
 338         }
 339 
 340         return ret;
 341 }
 342 EXPORT_SYMBOL(drm_atomic_helper_damage_iter_next);
 343 
 344 /**
 345  * drm_atomic_helper_damage_merged - Merged plane damage
 346  * @old_state: Old plane state for validation.
 347  * @state: Plane state from which to iterate the damage clips.
 348  * @rect: Returns the merged damage rectangle
 349  *
 350  * This function merges any valid plane damage clips into one rectangle and
 351  * returns it in @rect.
 352  *
 353  * For details see: drm_atomic_helper_damage_iter_init() and
 354  * drm_atomic_helper_damage_iter_next().
 355  *
 356  * Returns:
 357  * True if there is valid plane damage otherwise false.
 358  */
 359 bool drm_atomic_helper_damage_merged(const struct drm_plane_state *old_state,
 360                                      struct drm_plane_state *state,
 361                                      struct drm_rect *rect)
 362 {
 363         struct drm_atomic_helper_damage_iter iter;
 364         struct drm_rect clip;
 365         bool valid = false;
 366 
 367         rect->x1 = INT_MAX;
 368         rect->y1 = INT_MAX;
 369         rect->x2 = 0;
 370         rect->y2 = 0;
 371 
 372         drm_atomic_helper_damage_iter_init(&iter, old_state, state);
 373         drm_atomic_for_each_plane_damage(&iter, &clip) {
 374                 rect->x1 = min(rect->x1, clip.x1);
 375                 rect->y1 = min(rect->y1, clip.y1);
 376                 rect->x2 = max(rect->x2, clip.x2);
 377                 rect->y2 = max(rect->y2, clip.y2);
 378                 valid = true;
 379         }
 380 
 381         return valid;
 382 }
 383 EXPORT_SYMBOL(drm_atomic_helper_damage_merged);

/* [<][>][^][v][top][bottom][index][help] */