1d3b21767SLukasz Spintzyk // SPDX-License-Identifier: GPL-2.0 OR MIT
2d3b21767SLukasz Spintzyk /**************************************************************************
3d3b21767SLukasz Spintzyk *
4d3b21767SLukasz Spintzyk * Copyright (c) 2018 VMware, Inc., Palo Alto, CA., USA
5d3b21767SLukasz Spintzyk * All Rights Reserved.
6d3b21767SLukasz Spintzyk *
7d3b21767SLukasz Spintzyk * Permission is hereby granted, free of charge, to any person obtaining a
8d3b21767SLukasz Spintzyk * copy of this software and associated documentation files (the
9d3b21767SLukasz Spintzyk * "Software"), to deal in the Software without restriction, including
10d3b21767SLukasz Spintzyk * without limitation the rights to use, copy, modify, merge, publish,
11d3b21767SLukasz Spintzyk * distribute, sub license, and/or sell copies of the Software, and to
12d3b21767SLukasz Spintzyk * permit persons to whom the Software is furnished to do so, subject to
13d3b21767SLukasz Spintzyk * the following conditions:
14d3b21767SLukasz Spintzyk *
15d3b21767SLukasz Spintzyk * The above copyright notice and this permission notice (including the
16d3b21767SLukasz Spintzyk * next paragraph) shall be included in all copies or substantial portions
17d3b21767SLukasz Spintzyk * of the Software.
18d3b21767SLukasz Spintzyk *
19d3b21767SLukasz Spintzyk * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20d3b21767SLukasz Spintzyk * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21d3b21767SLukasz Spintzyk * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
22d3b21767SLukasz Spintzyk * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
23d3b21767SLukasz Spintzyk * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
24d3b21767SLukasz Spintzyk * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
25d3b21767SLukasz Spintzyk * USE OR OTHER DEALINGS IN THE SOFTWARE.
26d3b21767SLukasz Spintzyk *
27d3b21767SLukasz Spintzyk * Authors:
28d3b21767SLukasz Spintzyk * Deepak Rawat <drawat@vmware.com>
29b9fc5e01SRob Clark * Rob Clark <robdclark@gmail.com>
30d3b21767SLukasz Spintzyk *
31d3b21767SLukasz Spintzyk **************************************************************************/
32d3b21767SLukasz Spintzyk
33d9778b40SDeepak Rawat #include <drm/drm_atomic.h>
34d3b21767SLukasz Spintzyk #include <drm/drm_damage_helper.h>
35d713e330SSam Ravnborg #include <drm/drm_device.h>
36720cf96dSVille Syrjälä #include <drm/drm_framebuffer.h>
37d3b21767SLukasz Spintzyk
convert_clip_rect_to_rect(const struct drm_clip_rect * src,struct drm_mode_rect * dest,uint32_t num_clips,uint32_t src_inc)38b9fc5e01SRob Clark static void convert_clip_rect_to_rect(const struct drm_clip_rect *src,
39b9fc5e01SRob Clark struct drm_mode_rect *dest,
40b9fc5e01SRob Clark uint32_t num_clips, uint32_t src_inc)
41b9fc5e01SRob Clark {
42b9fc5e01SRob Clark while (num_clips > 0) {
43b9fc5e01SRob Clark dest->x1 = src->x1;
44b9fc5e01SRob Clark dest->y1 = src->y1;
45b9fc5e01SRob Clark dest->x2 = src->x2;
46b9fc5e01SRob Clark dest->y2 = src->y2;
47b9fc5e01SRob Clark src += src_inc;
48b9fc5e01SRob Clark dest++;
49b9fc5e01SRob Clark num_clips--;
50b9fc5e01SRob Clark }
51b9fc5e01SRob Clark }
52b9fc5e01SRob Clark
53d3b21767SLukasz Spintzyk /**
54d9778b40SDeepak Rawat * drm_atomic_helper_check_plane_damage - Verify plane damage on atomic_check.
55d9778b40SDeepak Rawat * @state: The driver state object.
56d9778b40SDeepak Rawat * @plane_state: Plane state for which to verify damage.
57d9778b40SDeepak Rawat *
58d9778b40SDeepak Rawat * This helper function makes sure that damage from plane state is discarded
59d9778b40SDeepak Rawat * for full modeset. If there are more reasons a driver would want to do a full
60d9778b40SDeepak Rawat * plane update rather than processing individual damage regions, then those
61d9778b40SDeepak Rawat * cases should be taken care of here.
62d9778b40SDeepak Rawat *
63d9778b40SDeepak Rawat * Note that &drm_plane_state.fb_damage_clips == NULL in plane state means that
64d9778b40SDeepak Rawat * full plane update should happen. It also ensure helper iterator will return
65d9778b40SDeepak Rawat * &drm_plane_state.src as damage.
66d9778b40SDeepak Rawat */
drm_atomic_helper_check_plane_damage(struct drm_atomic_state * state,struct drm_plane_state * plane_state)67d9778b40SDeepak Rawat void drm_atomic_helper_check_plane_damage(struct drm_atomic_state *state,
68d9778b40SDeepak Rawat struct drm_plane_state *plane_state)
69d9778b40SDeepak Rawat {
70d9778b40SDeepak Rawat struct drm_crtc_state *crtc_state;
71d9778b40SDeepak Rawat
72d9778b40SDeepak Rawat if (plane_state->crtc) {
73d9778b40SDeepak Rawat crtc_state = drm_atomic_get_new_crtc_state(state,
74d9778b40SDeepak Rawat plane_state->crtc);
75d9778b40SDeepak Rawat
76d9778b40SDeepak Rawat if (WARN_ON(!crtc_state))
77d9778b40SDeepak Rawat return;
78d9778b40SDeepak Rawat
79d9778b40SDeepak Rawat if (drm_atomic_crtc_needs_modeset(crtc_state)) {
80d9778b40SDeepak Rawat drm_property_blob_put(plane_state->fb_damage_clips);
81d9778b40SDeepak Rawat plane_state->fb_damage_clips = NULL;
82d9778b40SDeepak Rawat }
83d9778b40SDeepak Rawat }
84d9778b40SDeepak Rawat }
85d9778b40SDeepak Rawat EXPORT_SYMBOL(drm_atomic_helper_check_plane_damage);
86d2780b1fSDeepak Rawat
87d2780b1fSDeepak Rawat /**
88b9fc5e01SRob Clark * drm_atomic_helper_dirtyfb - Helper for dirtyfb.
89b9fc5e01SRob Clark * @fb: DRM framebuffer.
90b9fc5e01SRob Clark * @file_priv: Drm file for the ioctl call.
91b9fc5e01SRob Clark * @flags: Dirty fb annotate flags.
92b9fc5e01SRob Clark * @color: Color for annotate fill.
93b9fc5e01SRob Clark * @clips: Dirty region.
94b9fc5e01SRob Clark * @num_clips: Count of clip in clips.
95b9fc5e01SRob Clark *
96b9fc5e01SRob Clark * A helper to implement &drm_framebuffer_funcs.dirty using damage interface
97b9fc5e01SRob Clark * during plane update. If num_clips is 0 then this helper will do a full plane
98b9fc5e01SRob Clark * update. This is the same behaviour expected by DIRTFB IOCTL.
99b9fc5e01SRob Clark *
100b9fc5e01SRob Clark * Note that this helper is blocking implementation. This is what current
101b9fc5e01SRob Clark * drivers and userspace expect in their DIRTYFB IOCTL implementation, as a way
102b9fc5e01SRob Clark * to rate-limit userspace and make sure its rendering doesn't get ahead of
103b9fc5e01SRob Clark * uploading new data too much.
104b9fc5e01SRob Clark *
105b9fc5e01SRob Clark * Return: Zero on success, negative errno on failure.
106b9fc5e01SRob Clark */
drm_atomic_helper_dirtyfb(struct drm_framebuffer * fb,struct drm_file * file_priv,unsigned int flags,unsigned int color,struct drm_clip_rect * clips,unsigned int num_clips)107b9fc5e01SRob Clark int drm_atomic_helper_dirtyfb(struct drm_framebuffer *fb,
108b9fc5e01SRob Clark struct drm_file *file_priv, unsigned int flags,
109b9fc5e01SRob Clark unsigned int color, struct drm_clip_rect *clips,
110b9fc5e01SRob Clark unsigned int num_clips)
111b9fc5e01SRob Clark {
112b9fc5e01SRob Clark struct drm_modeset_acquire_ctx ctx;
113b9fc5e01SRob Clark struct drm_property_blob *damage = NULL;
114b9fc5e01SRob Clark struct drm_mode_rect *rects = NULL;
115b9fc5e01SRob Clark struct drm_atomic_state *state;
116b9fc5e01SRob Clark struct drm_plane *plane;
117b9fc5e01SRob Clark int ret = 0;
118b9fc5e01SRob Clark
119b9fc5e01SRob Clark /*
1200ae865efSCai Huoqing * When called from ioctl, we are interruptible, but not when called
121b9fc5e01SRob Clark * internally (ie. defio worker)
122b9fc5e01SRob Clark */
123b9fc5e01SRob Clark drm_modeset_acquire_init(&ctx,
124b9fc5e01SRob Clark file_priv ? DRM_MODESET_ACQUIRE_INTERRUPTIBLE : 0);
125b9fc5e01SRob Clark
126b9fc5e01SRob Clark state = drm_atomic_state_alloc(fb->dev);
127b9fc5e01SRob Clark if (!state) {
128b9fc5e01SRob Clark ret = -ENOMEM;
129cbdd2663SColin Ian King goto out_drop_locks;
130b9fc5e01SRob Clark }
131b9fc5e01SRob Clark state->acquire_ctx = &ctx;
132b9fc5e01SRob Clark
133b9fc5e01SRob Clark if (clips) {
134b9fc5e01SRob Clark uint32_t inc = 1;
135b9fc5e01SRob Clark
136b9fc5e01SRob Clark if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY) {
137b9fc5e01SRob Clark inc = 2;
138b9fc5e01SRob Clark num_clips /= 2;
139b9fc5e01SRob Clark }
140b9fc5e01SRob Clark
141b9fc5e01SRob Clark rects = kcalloc(num_clips, sizeof(*rects), GFP_KERNEL);
142b9fc5e01SRob Clark if (!rects) {
143b9fc5e01SRob Clark ret = -ENOMEM;
144b9fc5e01SRob Clark goto out;
145b9fc5e01SRob Clark }
146b9fc5e01SRob Clark
147b9fc5e01SRob Clark convert_clip_rect_to_rect(clips, rects, num_clips, inc);
148b9fc5e01SRob Clark damage = drm_property_create_blob(fb->dev,
149b9fc5e01SRob Clark num_clips * sizeof(*rects),
150b9fc5e01SRob Clark rects);
151b9fc5e01SRob Clark if (IS_ERR(damage)) {
152b9fc5e01SRob Clark ret = PTR_ERR(damage);
153b9fc5e01SRob Clark damage = NULL;
154b9fc5e01SRob Clark goto out;
155b9fc5e01SRob Clark }
156b9fc5e01SRob Clark }
157b9fc5e01SRob Clark
158b9fc5e01SRob Clark retry:
159b9fc5e01SRob Clark drm_for_each_plane(plane, fb->dev) {
160b9fc5e01SRob Clark struct drm_plane_state *plane_state;
161b9fc5e01SRob Clark
162354c2d31SSean Paul ret = drm_modeset_lock(&plane->mutex, state->acquire_ctx);
163354c2d31SSean Paul if (ret)
164354c2d31SSean Paul goto out;
165354c2d31SSean Paul
166354c2d31SSean Paul if (plane->state->fb != fb) {
167354c2d31SSean Paul drm_modeset_unlock(&plane->mutex);
168b9fc5e01SRob Clark continue;
169354c2d31SSean Paul }
170b9fc5e01SRob Clark
171b9fc5e01SRob Clark plane_state = drm_atomic_get_plane_state(state, plane);
172b9fc5e01SRob Clark if (IS_ERR(plane_state)) {
173b9fc5e01SRob Clark ret = PTR_ERR(plane_state);
174b9fc5e01SRob Clark goto out;
175b9fc5e01SRob Clark }
176b9fc5e01SRob Clark
177b9fc5e01SRob Clark drm_property_replace_blob(&plane_state->fb_damage_clips,
178b9fc5e01SRob Clark damage);
179b9fc5e01SRob Clark }
180b9fc5e01SRob Clark
181b9fc5e01SRob Clark ret = drm_atomic_commit(state);
182b9fc5e01SRob Clark
183b9fc5e01SRob Clark out:
184b9fc5e01SRob Clark if (ret == -EDEADLK) {
185b9fc5e01SRob Clark drm_atomic_state_clear(state);
186b9fc5e01SRob Clark ret = drm_modeset_backoff(&ctx);
187b9fc5e01SRob Clark if (!ret)
188b9fc5e01SRob Clark goto retry;
189b9fc5e01SRob Clark }
190b9fc5e01SRob Clark
191b9fc5e01SRob Clark drm_property_blob_put(damage);
192b9fc5e01SRob Clark kfree(rects);
193b9fc5e01SRob Clark drm_atomic_state_put(state);
194b9fc5e01SRob Clark
195cbdd2663SColin Ian King out_drop_locks:
196b9fc5e01SRob Clark drm_modeset_drop_locks(&ctx);
197b9fc5e01SRob Clark drm_modeset_acquire_fini(&ctx);
198b9fc5e01SRob Clark
199b9fc5e01SRob Clark return ret;
200b9fc5e01SRob Clark
201b9fc5e01SRob Clark }
202b9fc5e01SRob Clark EXPORT_SYMBOL(drm_atomic_helper_dirtyfb);
203b9fc5e01SRob Clark
204b9fc5e01SRob Clark /**
205d2780b1fSDeepak Rawat * drm_atomic_helper_damage_iter_init - Initialize the damage iterator.
206d2780b1fSDeepak Rawat * @iter: The iterator to initialize.
207d2780b1fSDeepak Rawat * @old_state: Old plane state for validation.
2080660d8cdSDeepak Rawat * @state: Plane state from which to iterate the damage clips.
209d2780b1fSDeepak Rawat *
210d2780b1fSDeepak Rawat * Initialize an iterator, which clips plane damage
211d2780b1fSDeepak Rawat * &drm_plane_state.fb_damage_clips to plane &drm_plane_state.src. This iterator
212d2780b1fSDeepak Rawat * returns full plane src in case damage is not present because either
213d2780b1fSDeepak Rawat * user-space didn't sent or driver discarded it (it want to do full plane
214d2780b1fSDeepak Rawat * update). Currently this iterator returns full plane src in case plane src
215d2780b1fSDeepak Rawat * changed but that can be changed in future to return damage.
216d2780b1fSDeepak Rawat *
217d2780b1fSDeepak Rawat * For the case when plane is not visible or plane update should not happen the
218d2780b1fSDeepak Rawat * first call to iter_next will return false. Note that this helper use clipped
219d2780b1fSDeepak Rawat * &drm_plane_state.src, so driver calling this helper should have called
220d2780b1fSDeepak Rawat * drm_atomic_helper_check_plane_state() earlier.
221d2780b1fSDeepak Rawat */
222d2780b1fSDeepak Rawat void
drm_atomic_helper_damage_iter_init(struct drm_atomic_helper_damage_iter * iter,const struct drm_plane_state * old_state,const struct drm_plane_state * state)223d2780b1fSDeepak Rawat drm_atomic_helper_damage_iter_init(struct drm_atomic_helper_damage_iter *iter,
224d2780b1fSDeepak Rawat const struct drm_plane_state *old_state,
225d2780b1fSDeepak Rawat const struct drm_plane_state *state)
226d2780b1fSDeepak Rawat {
227dd9b18e7SJouni Högander struct drm_rect src;
228d2780b1fSDeepak Rawat memset(iter, 0, sizeof(*iter));
229d2780b1fSDeepak Rawat
230d2780b1fSDeepak Rawat if (!state || !state->crtc || !state->fb || !state->visible)
231d2780b1fSDeepak Rawat return;
232d2780b1fSDeepak Rawat
2336f11f374SDaniel Vetter iter->clips = (struct drm_rect *)drm_plane_get_damage_clips(state);
234d2780b1fSDeepak Rawat iter->num_clips = drm_plane_get_damage_clips_count(state);
235d2780b1fSDeepak Rawat
236d2780b1fSDeepak Rawat /* Round down for x1/y1 and round up for x2/y2 to catch all pixels */
237dd9b18e7SJouni Högander src = drm_plane_state_src(state);
238dd9b18e7SJouni Högander
239dd9b18e7SJouni Högander iter->plane_src.x1 = src.x1 >> 16;
240dd9b18e7SJouni Högander iter->plane_src.y1 = src.y1 >> 16;
241dd9b18e7SJouni Högander iter->plane_src.x2 = (src.x2 >> 16) + !!(src.x2 & 0xFFFF);
242dd9b18e7SJouni Högander iter->plane_src.y2 = (src.y2 >> 16) + !!(src.y2 & 0xFFFF);
243d2780b1fSDeepak Rawat
244*45aafb50SJavier Martinez Canillas if (!iter->clips || state->ignore_damage_clips ||
245*45aafb50SJavier Martinez Canillas !drm_rect_equals(&state->src, &old_state->src)) {
246fd86575fSFabio Estevam iter->clips = NULL;
247d2780b1fSDeepak Rawat iter->num_clips = 0;
248d2780b1fSDeepak Rawat iter->full_update = true;
249d2780b1fSDeepak Rawat }
250d2780b1fSDeepak Rawat }
251d2780b1fSDeepak Rawat EXPORT_SYMBOL(drm_atomic_helper_damage_iter_init);
252d2780b1fSDeepak Rawat
253d2780b1fSDeepak Rawat /**
254d2780b1fSDeepak Rawat * drm_atomic_helper_damage_iter_next - Advance the damage iterator.
255d2780b1fSDeepak Rawat * @iter: The iterator to advance.
256d2780b1fSDeepak Rawat * @rect: Return a rectangle in fb coordinate clipped to plane src.
257d2780b1fSDeepak Rawat *
258d2780b1fSDeepak Rawat * Since plane src is in 16.16 fixed point and damage clips are whole number,
259d2780b1fSDeepak Rawat * this iterator round off clips that intersect with plane src. Round down for
260d2780b1fSDeepak Rawat * x1/y1 and round up for x2/y2 for the intersected coordinate. Similar rounding
261d2780b1fSDeepak Rawat * off for full plane src, in case it's returned as damage. This iterator will
262d2780b1fSDeepak Rawat * skip damage clips outside of plane src.
263d2780b1fSDeepak Rawat *
264d2780b1fSDeepak Rawat * Return: True if the output is valid, false if reached the end.
265d2780b1fSDeepak Rawat *
266d2780b1fSDeepak Rawat * If the first call to iterator next returns false then it means no need to
267d2780b1fSDeepak Rawat * update the plane.
268d2780b1fSDeepak Rawat */
269d2780b1fSDeepak Rawat bool
drm_atomic_helper_damage_iter_next(struct drm_atomic_helper_damage_iter * iter,struct drm_rect * rect)270d2780b1fSDeepak Rawat drm_atomic_helper_damage_iter_next(struct drm_atomic_helper_damage_iter *iter,
271d2780b1fSDeepak Rawat struct drm_rect *rect)
272d2780b1fSDeepak Rawat {
273d2780b1fSDeepak Rawat bool ret = false;
274d2780b1fSDeepak Rawat
275d2780b1fSDeepak Rawat if (iter->full_update) {
276d2780b1fSDeepak Rawat *rect = iter->plane_src;
277d2780b1fSDeepak Rawat iter->full_update = false;
278d2780b1fSDeepak Rawat return true;
279d2780b1fSDeepak Rawat }
280d2780b1fSDeepak Rawat
281d2780b1fSDeepak Rawat while (iter->curr_clip < iter->num_clips) {
282d2780b1fSDeepak Rawat *rect = iter->clips[iter->curr_clip];
283d2780b1fSDeepak Rawat iter->curr_clip++;
284d2780b1fSDeepak Rawat
285d2780b1fSDeepak Rawat if (drm_rect_intersect(rect, &iter->plane_src)) {
286d2780b1fSDeepak Rawat ret = true;
287d2780b1fSDeepak Rawat break;
288d2780b1fSDeepak Rawat }
289d2780b1fSDeepak Rawat }
290d2780b1fSDeepak Rawat
291d2780b1fSDeepak Rawat return ret;
292d2780b1fSDeepak Rawat }
293d2780b1fSDeepak Rawat EXPORT_SYMBOL(drm_atomic_helper_damage_iter_next);
294255f6fe7SNoralf Trønnes
295255f6fe7SNoralf Trønnes /**
296255f6fe7SNoralf Trønnes * drm_atomic_helper_damage_merged - Merged plane damage
297255f6fe7SNoralf Trønnes * @old_state: Old plane state for validation.
298255f6fe7SNoralf Trønnes * @state: Plane state from which to iterate the damage clips.
299255f6fe7SNoralf Trønnes * @rect: Returns the merged damage rectangle
300255f6fe7SNoralf Trønnes *
301255f6fe7SNoralf Trønnes * This function merges any valid plane damage clips into one rectangle and
302255f6fe7SNoralf Trønnes * returns it in @rect.
303255f6fe7SNoralf Trønnes *
304255f6fe7SNoralf Trønnes * For details see: drm_atomic_helper_damage_iter_init() and
305255f6fe7SNoralf Trønnes * drm_atomic_helper_damage_iter_next().
306255f6fe7SNoralf Trønnes *
307255f6fe7SNoralf Trønnes * Returns:
308255f6fe7SNoralf Trønnes * True if there is valid plane damage otherwise false.
309255f6fe7SNoralf Trønnes */
drm_atomic_helper_damage_merged(const struct drm_plane_state * old_state,struct drm_plane_state * state,struct drm_rect * rect)310255f6fe7SNoralf Trønnes bool drm_atomic_helper_damage_merged(const struct drm_plane_state *old_state,
311255f6fe7SNoralf Trønnes struct drm_plane_state *state,
312255f6fe7SNoralf Trønnes struct drm_rect *rect)
313255f6fe7SNoralf Trønnes {
314255f6fe7SNoralf Trønnes struct drm_atomic_helper_damage_iter iter;
315255f6fe7SNoralf Trønnes struct drm_rect clip;
316255f6fe7SNoralf Trønnes bool valid = false;
317255f6fe7SNoralf Trønnes
318255f6fe7SNoralf Trønnes rect->x1 = INT_MAX;
319255f6fe7SNoralf Trønnes rect->y1 = INT_MAX;
320255f6fe7SNoralf Trønnes rect->x2 = 0;
321255f6fe7SNoralf Trønnes rect->y2 = 0;
322255f6fe7SNoralf Trønnes
323255f6fe7SNoralf Trønnes drm_atomic_helper_damage_iter_init(&iter, old_state, state);
324255f6fe7SNoralf Trønnes drm_atomic_for_each_plane_damage(&iter, &clip) {
325255f6fe7SNoralf Trønnes rect->x1 = min(rect->x1, clip.x1);
326255f6fe7SNoralf Trønnes rect->y1 = min(rect->y1, clip.y1);
327255f6fe7SNoralf Trønnes rect->x2 = max(rect->x2, clip.x2);
328255f6fe7SNoralf Trønnes rect->y2 = max(rect->y2, clip.y2);
329255f6fe7SNoralf Trønnes valid = true;
330255f6fe7SNoralf Trønnes }
331255f6fe7SNoralf Trønnes
332255f6fe7SNoralf Trønnes return valid;
333255f6fe7SNoralf Trønnes }
334255f6fe7SNoralf Trønnes EXPORT_SYMBOL(drm_atomic_helper_damage_merged);
335