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