linux/drivers/gpu/drm/drm_damage_helper.c
<<
>>
Prefs
   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
  37static void convert_clip_rect_to_rect(const struct drm_clip_rect *src,
  38                                      struct drm_mode_rect *dest,
  39                                      uint32_t num_clips, uint32_t src_inc)
  40{
  41        while (num_clips > 0) {
  42                dest->x1 = src->x1;
  43                dest->y1 = src->y1;
  44                dest->x2 = src->x2;
  45                dest->y2 = src->y2;
  46                src += src_inc;
  47                dest++;
  48                num_clips--;
  49        }
  50}
  51
  52/**
  53 * drm_atomic_helper_check_plane_damage - Verify plane damage on atomic_check.
  54 * @state: The driver state object.
  55 * @plane_state: Plane state for which to verify damage.
  56 *
  57 * This helper function makes sure that damage from plane state is discarded
  58 * for full modeset. If there are more reasons a driver would want to do a full
  59 * plane update rather than processing individual damage regions, then those
  60 * cases should be taken care of here.
  61 *
  62 * Note that &drm_plane_state.fb_damage_clips == NULL in plane state means that
  63 * full plane update should happen. It also ensure helper iterator will return
  64 * &drm_plane_state.src as damage.
  65 */
  66void drm_atomic_helper_check_plane_damage(struct drm_atomic_state *state,
  67                                          struct drm_plane_state *plane_state)
  68{
  69        struct drm_crtc_state *crtc_state;
  70
  71        if (plane_state->crtc) {
  72                crtc_state = drm_atomic_get_new_crtc_state(state,
  73                                                           plane_state->crtc);
  74
  75                if (WARN_ON(!crtc_state))
  76                        return;
  77
  78                if (drm_atomic_crtc_needs_modeset(crtc_state)) {
  79                        drm_property_blob_put(plane_state->fb_damage_clips);
  80                        plane_state->fb_damage_clips = NULL;
  81                }
  82        }
  83}
  84EXPORT_SYMBOL(drm_atomic_helper_check_plane_damage);
  85
  86/**
  87 * drm_atomic_helper_dirtyfb - Helper for dirtyfb.
  88 * @fb: DRM framebuffer.
  89 * @file_priv: Drm file for the ioctl call.
  90 * @flags: Dirty fb annotate flags.
  91 * @color: Color for annotate fill.
  92 * @clips: Dirty region.
  93 * @num_clips: Count of clip in clips.
  94 *
  95 * A helper to implement &drm_framebuffer_funcs.dirty using damage interface
  96 * during plane update. If num_clips is 0 then this helper will do a full plane
  97 * update. This is the same behaviour expected by DIRTFB IOCTL.
  98 *
  99 * Note that this helper is blocking implementation. This is what current
 100 * drivers and userspace expect in their DIRTYFB IOCTL implementation, as a way
 101 * to rate-limit userspace and make sure its rendering doesn't get ahead of
 102 * uploading new data too much.
 103 *
 104 * Return: Zero on success, negative errno on failure.
 105 */
 106int drm_atomic_helper_dirtyfb(struct drm_framebuffer *fb,
 107                              struct drm_file *file_priv, unsigned int flags,
 108                              unsigned int color, struct drm_clip_rect *clips,
 109                              unsigned int num_clips)
 110{
 111        struct drm_modeset_acquire_ctx ctx;
 112        struct drm_property_blob *damage = NULL;
 113        struct drm_mode_rect *rects = NULL;
 114        struct drm_atomic_state *state;
 115        struct drm_plane *plane;
 116        int ret = 0;
 117
 118        /*
 119         * When called from ioctl, we are interruptible, but not when called
 120         * internally (ie. defio worker)
 121         */
 122        drm_modeset_acquire_init(&ctx,
 123                file_priv ? DRM_MODESET_ACQUIRE_INTERRUPTIBLE : 0);
 124
 125        state = drm_atomic_state_alloc(fb->dev);
 126        if (!state) {
 127                ret = -ENOMEM;
 128                goto out_drop_locks;
 129        }
 130        state->acquire_ctx = &ctx;
 131
 132        if (clips) {
 133                uint32_t inc = 1;
 134
 135                if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY) {
 136                        inc = 2;
 137                        num_clips /= 2;
 138                }
 139
 140                rects = kcalloc(num_clips, sizeof(*rects), GFP_KERNEL);
 141                if (!rects) {
 142                        ret = -ENOMEM;
 143                        goto out;
 144                }
 145
 146                convert_clip_rect_to_rect(clips, rects, num_clips, inc);
 147                damage = drm_property_create_blob(fb->dev,
 148                                                  num_clips * sizeof(*rects),
 149                                                  rects);
 150                if (IS_ERR(damage)) {
 151                        ret = PTR_ERR(damage);
 152                        damage = NULL;
 153                        goto out;
 154                }
 155        }
 156
 157retry:
 158        drm_for_each_plane(plane, fb->dev) {
 159                struct drm_plane_state *plane_state;
 160
 161                ret = drm_modeset_lock(&plane->mutex, state->acquire_ctx);
 162                if (ret)
 163                        goto out;
 164
 165                if (plane->state->fb != fb) {
 166                        drm_modeset_unlock(&plane->mutex);
 167                        continue;
 168                }
 169
 170                plane_state = drm_atomic_get_plane_state(state, plane);
 171                if (IS_ERR(plane_state)) {
 172                        ret = PTR_ERR(plane_state);
 173                        goto out;
 174                }
 175
 176                drm_property_replace_blob(&plane_state->fb_damage_clips,
 177                                          damage);
 178        }
 179
 180        ret = drm_atomic_commit(state);
 181
 182out:
 183        if (ret == -EDEADLK) {
 184                drm_atomic_state_clear(state);
 185                ret = drm_modeset_backoff(&ctx);
 186                if (!ret)
 187                        goto retry;
 188        }
 189
 190        drm_property_blob_put(damage);
 191        kfree(rects);
 192        drm_atomic_state_put(state);
 193
 194out_drop_locks:
 195        drm_modeset_drop_locks(&ctx);
 196        drm_modeset_acquire_fini(&ctx);
 197
 198        return ret;
 199
 200}
 201EXPORT_SYMBOL(drm_atomic_helper_dirtyfb);
 202
 203/**
 204 * drm_atomic_helper_damage_iter_init - Initialize the damage iterator.
 205 * @iter: The iterator to initialize.
 206 * @old_state: Old plane state for validation.
 207 * @state: Plane state from which to iterate the damage clips.
 208 *
 209 * Initialize an iterator, which clips plane damage
 210 * &drm_plane_state.fb_damage_clips to plane &drm_plane_state.src. This iterator
 211 * returns full plane src in case damage is not present because either
 212 * user-space didn't sent or driver discarded it (it want to do full plane
 213 * update). Currently this iterator returns full plane src in case plane src
 214 * changed but that can be changed in future to return damage.
 215 *
 216 * For the case when plane is not visible or plane update should not happen the
 217 * first call to iter_next will return false. Note that this helper use clipped
 218 * &drm_plane_state.src, so driver calling this helper should have called
 219 * drm_atomic_helper_check_plane_state() earlier.
 220 */
 221void
 222drm_atomic_helper_damage_iter_init(struct drm_atomic_helper_damage_iter *iter,
 223                                   const struct drm_plane_state *old_state,
 224                                   const struct drm_plane_state *state)
 225{
 226        memset(iter, 0, sizeof(*iter));
 227
 228        if (!state || !state->crtc || !state->fb || !state->visible)
 229                return;
 230
 231        iter->clips = (struct drm_rect *)drm_plane_get_damage_clips(state);
 232        iter->num_clips = drm_plane_get_damage_clips_count(state);
 233
 234        /* Round down for x1/y1 and round up for x2/y2 to catch all pixels */
 235        iter->plane_src.x1 = state->src.x1 >> 16;
 236        iter->plane_src.y1 = state->src.y1 >> 16;
 237        iter->plane_src.x2 = (state->src.x2 >> 16) + !!(state->src.x2 & 0xFFFF);
 238        iter->plane_src.y2 = (state->src.y2 >> 16) + !!(state->src.y2 & 0xFFFF);
 239
 240        if (!iter->clips || !drm_rect_equals(&state->src, &old_state->src)) {
 241                iter->clips = NULL;
 242                iter->num_clips = 0;
 243                iter->full_update = true;
 244        }
 245}
 246EXPORT_SYMBOL(drm_atomic_helper_damage_iter_init);
 247
 248/**
 249 * drm_atomic_helper_damage_iter_next - Advance the damage iterator.
 250 * @iter: The iterator to advance.
 251 * @rect: Return a rectangle in fb coordinate clipped to plane src.
 252 *
 253 * Since plane src is in 16.16 fixed point and damage clips are whole number,
 254 * this iterator round off clips that intersect with plane src. Round down for
 255 * x1/y1 and round up for x2/y2 for the intersected coordinate. Similar rounding
 256 * off for full plane src, in case it's returned as damage. This iterator will
 257 * skip damage clips outside of plane src.
 258 *
 259 * Return: True if the output is valid, false if reached the end.
 260 *
 261 * If the first call to iterator next returns false then it means no need to
 262 * update the plane.
 263 */
 264bool
 265drm_atomic_helper_damage_iter_next(struct drm_atomic_helper_damage_iter *iter,
 266                                   struct drm_rect *rect)
 267{
 268        bool ret = false;
 269
 270        if (iter->full_update) {
 271                *rect = iter->plane_src;
 272                iter->full_update = false;
 273                return true;
 274        }
 275
 276        while (iter->curr_clip < iter->num_clips) {
 277                *rect = iter->clips[iter->curr_clip];
 278                iter->curr_clip++;
 279
 280                if (drm_rect_intersect(rect, &iter->plane_src)) {
 281                        ret = true;
 282                        break;
 283                }
 284        }
 285
 286        return ret;
 287}
 288EXPORT_SYMBOL(drm_atomic_helper_damage_iter_next);
 289
 290/**
 291 * drm_atomic_helper_damage_merged - Merged plane damage
 292 * @old_state: Old plane state for validation.
 293 * @state: Plane state from which to iterate the damage clips.
 294 * @rect: Returns the merged damage rectangle
 295 *
 296 * This function merges any valid plane damage clips into one rectangle and
 297 * returns it in @rect.
 298 *
 299 * For details see: drm_atomic_helper_damage_iter_init() and
 300 * drm_atomic_helper_damage_iter_next().
 301 *
 302 * Returns:
 303 * True if there is valid plane damage otherwise false.
 304 */
 305bool drm_atomic_helper_damage_merged(const struct drm_plane_state *old_state,
 306                                     struct drm_plane_state *state,
 307                                     struct drm_rect *rect)
 308{
 309        struct drm_atomic_helper_damage_iter iter;
 310        struct drm_rect clip;
 311        bool valid = false;
 312
 313        rect->x1 = INT_MAX;
 314        rect->y1 = INT_MAX;
 315        rect->x2 = 0;
 316        rect->y2 = 0;
 317
 318        drm_atomic_helper_damage_iter_init(&iter, old_state, state);
 319        drm_atomic_for_each_plane_damage(&iter, &clip) {
 320                rect->x1 = min(rect->x1, clip.x1);
 321                rect->y1 = min(rect->y1, clip.y1);
 322                rect->x2 = max(rect->x2, clip.x2);
 323                rect->y2 = max(rect->y2, clip.y2);
 324                valid = true;
 325        }
 326
 327        return valid;
 328}
 329EXPORT_SYMBOL(drm_atomic_helper_damage_merged);
 330