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