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 | 36 | |
b9fc5e01 RC |
37 | static 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 | ||
d9778b40 DR |
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 | */ | |
66 | void 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 | } | |
84 | EXPORT_SYMBOL(drm_atomic_helper_check_plane_damage); | |
d2780b1f | 85 | |
b9fc5e01 RC |
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 | */ | |
106 | int 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 | /* | |
0ae865ef | 119 | * When called from ioctl, we are interruptible, but not when called |
b9fc5e01 RC |
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; | |
cbdd2663 | 128 | goto out_drop_locks; |
b9fc5e01 RC |
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 | ||
157 | retry: | |
158 | drm_for_each_plane(plane, fb->dev) { | |
159 | struct drm_plane_state *plane_state; | |
160 | ||
354c2d31 SP |
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); | |
b9fc5e01 | 167 | continue; |
354c2d31 | 168 | } |
b9fc5e01 RC |
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 | ||
182 | out: | |
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 | ||
cbdd2663 | 194 | out_drop_locks: |
b9fc5e01 RC |
195 | drm_modeset_drop_locks(&ctx); |
196 | drm_modeset_acquire_fini(&ctx); | |
197 | ||
198 | return ret; | |
199 | ||
200 | } | |
201 | EXPORT_SYMBOL(drm_atomic_helper_dirtyfb); | |
202 | ||
d2780b1f DR |
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. | |
0660d8cd | 207 | * @state: Plane state from which to iterate the damage clips. |
d2780b1f DR |
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 | */ | |
221 | void | |
222 | drm_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 | ||
6f11f374 | 231 | iter->clips = (struct drm_rect *)drm_plane_get_damage_clips(state); |
d2780b1f DR |
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)) { | |
fd86575f | 241 | iter->clips = NULL; |
d2780b1f DR |
242 | iter->num_clips = 0; |
243 | iter->full_update = true; | |
244 | } | |
245 | } | |
246 | EXPORT_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 | */ | |
264 | bool | |
265 | drm_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 | } | |
288 | EXPORT_SYMBOL(drm_atomic_helper_damage_iter_next); | |
255f6fe7 NT |
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 | */ | |
305 | bool 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 | } | |
329 | EXPORT_SYMBOL(drm_atomic_helper_damage_merged); |