]>
Commit | Line | Data |
---|---|---|
d3b21767 LS |
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> | |
b9fc5e01 | 29 | * Rob Clark <robdclark@gmail.com> |
d3b21767 LS |
30 | * |
31 | **************************************************************************/ | |
32 | ||
d9778b40 | 33 | #include <drm/drm_atomic.h> |
d3b21767 | 34 | #include <drm/drm_damage_helper.h> |
d713e330 | 35 | #include <drm/drm_device.h> |
d3b21767 LS |
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(). | |
d2780b1f DR |
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. | |
d3b21767 LS |
73 | */ |
74 | ||
b9fc5e01 RC |
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 | ||
d3b21767 LS |
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); | |
d9778b40 DR |
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); | |
d2780b1f | 139 | |
b9fc5e01 RC |
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; | |
cbdd2663 | 182 | goto out_drop_locks; |
b9fc5e01 RC |
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 | ||
354c2d31 SP |
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); | |
b9fc5e01 | 221 | continue; |
354c2d31 | 222 | } |
b9fc5e01 RC |
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 | ||
cbdd2663 | 248 | out_drop_locks: |
b9fc5e01 RC |
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 | ||
d2780b1f DR |
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. | |
0660d8cd | 261 | * @state: Plane state from which to iterate the damage clips. |
d2780b1f DR |
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)) { | |
fd86575f | 295 | iter->clips = NULL; |
d2780b1f DR |
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); | |
255f6fe7 NT |
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); |