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