blob: b0388ba2e56d993b20a6a5103685a184d1049c24 [file] [log] [blame]
Daniel Stone6b466f22019-06-18 11:30:54 +01001/*
2 * Copyright © 2008-2011 Kristian Høgsberg
3 * Copyright © 2011 Intel Corporation
4 * Copyright © 2017, 2018 Collabora, Ltd.
5 * Copyright © 2017, 2018 General Electric Company
6 * Copyright (c) 2018 DisplayLink (UK) Ltd.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining
9 * a copy of this software and associated documentation files (the
10 * "Software"), to deal in the Software without restriction, including
11 * without limitation the rights to use, copy, modify, merge, publish,
12 * distribute, sublicense, and/or sell copies of the Software, and to
13 * permit persons to whom the Software is furnished to do so, subject to
14 * the following conditions:
15 *
16 * The above copyright notice and this permission notice (including the
17 * next paragraph) shall be included in all copies or substantial
18 * portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
24 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
25 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
26 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 * SOFTWARE.
28 */
29
30#include "config.h"
31
32#include <xf86drm.h>
33#include <xf86drmMode.h>
Daniel Stone6b466f22019-06-18 11:30:54 +010034
35#include "drm-internal.h"
Pekka Paalanen4b301fe2021-02-04 17:39:45 +020036#include "shared/weston-drm-fourcc.h"
leng.fang32af9fc2024-06-13 11:22:15 +080037#include "aml-weston/aml-backend.h"
Daniel Stone6b466f22019-06-18 11:30:54 +010038
39/**
40 * Allocate a new, empty, plane state.
41 */
42struct drm_plane_state *
43drm_plane_state_alloc(struct drm_output_state *state_output,
44 struct drm_plane *plane)
45{
46 struct drm_plane_state *state = zalloc(sizeof(*state));
47
48 assert(state);
49 state->output_state = state_output;
50 state->plane = plane;
51 state->in_fence_fd = -1;
Marius Vladcdd6fa22019-08-29 20:42:00 +030052 state->zpos = DRM_PLANE_ZPOS_INVALID_PLANE;
Daniel Stone6b466f22019-06-18 11:30:54 +010053
54 /* Here we only add the plane state to the desired link, and not
55 * set the member. Having an output pointer set means that the
56 * plane will be displayed on the output; this won't be the case
57 * when we go to disable a plane. In this case, it must be part of
58 * the commit (and thus the output state), but the member must be
59 * NULL, as it will not be on any output when the state takes
60 * effect.
61 */
62 if (state_output)
63 wl_list_insert(&state_output->plane_list, &state->link);
64 else
65 wl_list_init(&state->link);
66
67 return state;
68}
69
70/**
71 * Free an existing plane state. As a special case, the state will not
72 * normally be freed if it is the current state; see drm_plane_set_state.
73 */
74void
75drm_plane_state_free(struct drm_plane_state *state, bool force)
76{
77 if (!state)
78 return;
79
80 wl_list_remove(&state->link);
81 wl_list_init(&state->link);
82 state->output_state = NULL;
83 state->in_fence_fd = -1;
Marius Vladcdd6fa22019-08-29 20:42:00 +030084 state->zpos = DRM_PLANE_ZPOS_INVALID_PLANE;
Scott Anderson15c603c2020-06-02 17:39:43 +120085
86 /* Once the damage blob has been submitted, it is refcounted internally
87 * by the kernel, which means we can safely discard it.
88 */
89 if (state->damage_blob_id != 0) {
90 drmModeDestroyPropertyBlob(state->plane->backend->drm.fd,
91 state->damage_blob_id);
92 state->damage_blob_id = 0;
93 }
Daniel Stone6b466f22019-06-18 11:30:54 +010094
95 if (force || state != state->plane->state_cur) {
96 drm_fb_unref(state->fb);
Daniel Stone2ecc38b2021-11-18 15:33:17 +000097 weston_buffer_reference(&state->fb_ref.buffer, NULL);
98 weston_buffer_release_reference(&state->fb_ref.release, NULL);
Daniel Stone6b466f22019-06-18 11:30:54 +010099 free(state);
100 }
101}
102
103/**
104 * Duplicate an existing plane state into a new plane state, storing it within
105 * the given output state. If the output state already contains a plane state
106 * for the drm_plane referenced by 'src', that plane state is freed first.
107 */
108struct drm_plane_state *
109drm_plane_state_duplicate(struct drm_output_state *state_output,
110 struct drm_plane_state *src)
111{
Scott Anderson15c603c2020-06-02 17:39:43 +1200112 struct drm_plane_state *dst = zalloc(sizeof(*dst));
Daniel Stone6b466f22019-06-18 11:30:54 +0100113 struct drm_plane_state *old, *tmp;
114
115 assert(src);
116 assert(dst);
117 *dst = *src;
Scott Anderson15c603c2020-06-02 17:39:43 +1200118 /* We don't want to copy this, because damage is transient, and only
119 * lasts for the duration of a single repaint.
120 */
121 dst->damage_blob_id = 0;
Daniel Stone6b466f22019-06-18 11:30:54 +0100122 wl_list_init(&dst->link);
123
124 wl_list_for_each_safe(old, tmp, &state_output->plane_list, link) {
125 /* Duplicating a plane state into the same output state, so
126 * it can replace itself with an identical copy of itself,
127 * makes no sense. */
128 assert(old != src);
129 if (old->plane == dst->plane)
130 drm_plane_state_free(old, false);
131 }
132
133 wl_list_insert(&state_output->plane_list, &dst->link);
Daniel Stone2ecc38b2021-11-18 15:33:17 +0000134
135 /* Take a reference on the src framebuffer; if it wraps a client
136 * buffer, then we must also transfer the reference on the client
137 * buffer. */
138 if (src->fb) {
Daniel Stone6b466f22019-06-18 11:30:54 +0100139 dst->fb = drm_fb_ref(src->fb);
Daniel Stone2ecc38b2021-11-18 15:33:17 +0000140 memset(&dst->fb_ref, 0, sizeof(dst->fb_ref));
141 weston_buffer_reference(&dst->fb_ref.buffer,
142 src->fb_ref.buffer.buffer);
143 weston_buffer_release_reference(&dst->fb_ref.release,
144 src->fb_ref.release.buffer_release);
145 } else {
146 assert(!src->fb_ref.buffer.buffer);
147 assert(!src->fb_ref.release.buffer_release);
148 }
Daniel Stone6b466f22019-06-18 11:30:54 +0100149 dst->output_state = state_output;
Daniel Stone6b466f22019-06-18 11:30:54 +0100150 dst->complete = false;
151
152 return dst;
153}
154
155/**
156 * Remove a plane state from an output state; if the plane was previously
157 * enabled, then replace it with a disabling state. This ensures that the
158 * output state was untouched from it was before the plane state was
159 * modified by the caller of this function.
160 *
161 * This is required as drm_output_state_get_plane may either allocate a
162 * new plane state, in which case this function will just perform a matching
163 * drm_plane_state_free, or it may instead repurpose an existing disabling
164 * state (if the plane was previously active), in which case this function
165 * will reset it.
166 */
167void
168drm_plane_state_put_back(struct drm_plane_state *state)
169{
170 struct drm_output_state *state_output;
171 struct drm_plane *plane;
172
173 if (!state)
174 return;
175
176 state_output = state->output_state;
177 plane = state->plane;
178 drm_plane_state_free(state, false);
179
180 /* Plane was previously disabled; no need to keep this temporary
181 * state around. */
182 if (!plane->state_cur->fb)
183 return;
184
185 (void) drm_plane_state_alloc(state_output, plane);
186}
187
188/**
189 * Given a weston_view, fill the drm_plane_state's co-ordinates to display on
190 * a given plane.
191 */
192bool
193drm_plane_state_coords_for_view(struct drm_plane_state *state,
Marius Vlad2538aac2019-10-14 11:05:30 +0300194 struct weston_view *ev, uint64_t zpos)
Daniel Stone6b466f22019-06-18 11:30:54 +0100195{
196 struct drm_output *output = state->output;
197 struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
198 pixman_region32_t dest_rect, src_rect;
199 pixman_box32_t *box, tbox;
200 float sxf1, syf1, sxf2, syf2;
201
limin.tianc616dad2024-07-15 11:35:38 +0000202 //if (!drm_view_transform_supported(ev, &output->base))
203 // return false;
Daniel Stone6b466f22019-06-18 11:30:54 +0100204
205 /* Update the base weston_plane co-ordinates. */
206 box = pixman_region32_extents(&ev->transform.boundingbox);
207 state->plane->base.x = box->x1;
208 state->plane->base.y = box->y1;
209
210 /* First calculate the destination co-ordinates by taking the
211 * area of the view which is visible on this output, performing any
212 * transforms to account for output rotation and scale as necessary. */
213 pixman_region32_init(&dest_rect);
214 pixman_region32_intersect(&dest_rect, &ev->transform.boundingbox,
215 &output->base.region);
216 pixman_region32_translate(&dest_rect, -output->base.x, -output->base.y);
217 box = pixman_region32_extents(&dest_rect);
218 tbox = weston_transformed_rect(output->base.width,
219 output->base.height,
220 output->base.transform,
221 output->base.current_scale,
222 *box);
223 state->dest_x = tbox.x1;
224 state->dest_y = tbox.y1;
225 state->dest_w = tbox.x2 - tbox.x1;
226 state->dest_h = tbox.y2 - tbox.y1;
leng.fang32af9fc2024-06-13 11:22:15 +0800227
228 drm_update_aml_state_coords(output, state);
229
Daniel Stone6b466f22019-06-18 11:30:54 +0100230 pixman_region32_fini(&dest_rect);
231
232 /* Now calculate the source rectangle, by finding the extents of the
233 * view, and working backwards to source co-ordinates. */
234 pixman_region32_init(&src_rect);
leng.fang32af9fc2024-06-13 11:22:15 +0800235 if (state->plane->is_video_plane)
236 src_rect = ev->transform.boundingbox;
237 else
238 pixman_region32_intersect(&src_rect, &ev->transform.boundingbox,
Daniel Stone6b466f22019-06-18 11:30:54 +0100239 &output->base.region);
240 box = pixman_region32_extents(&src_rect);
241 weston_view_from_global_float(ev, box->x1, box->y1, &sxf1, &syf1);
242 weston_surface_to_buffer_float(ev->surface, sxf1, syf1, &sxf1, &syf1);
243 weston_view_from_global_float(ev, box->x2, box->y2, &sxf2, &syf2);
244 weston_surface_to_buffer_float(ev->surface, sxf2, syf2, &sxf2, &syf2);
245 pixman_region32_fini(&src_rect);
246
247 /* Buffer transforms may mean that x2 is to the left of x1, and/or that
248 * y2 is above y1. */
249 if (sxf2 < sxf1) {
250 double tmp = sxf1;
251 sxf1 = sxf2;
252 sxf2 = tmp;
253 }
254 if (syf2 < syf1) {
255 double tmp = syf1;
256 syf1 = syf2;
257 syf2 = tmp;
258 }
259
260 /* Shift from S23.8 wl_fixed to U16.16 KMS fixed-point encoding. */
261 state->src_x = wl_fixed_from_double(sxf1) << 8;
262 state->src_y = wl_fixed_from_double(syf1) << 8;
263 state->src_w = wl_fixed_from_double(sxf2 - sxf1) << 8;
264 state->src_h = wl_fixed_from_double(syf2 - syf1) << 8;
265
266 /* Clamp our source co-ordinates to surface bounds; it's possible
267 * for intermediate translations to give us slightly incorrect
268 * co-ordinates if we have, for example, multiple zooming
269 * transformations. View bounding boxes are also explicitly rounded
270 * greedily. */
271 if (state->src_x < 0)
272 state->src_x = 0;
273 if (state->src_y < 0)
274 state->src_y = 0;
275 if (state->src_w > (uint32_t) ((buffer->width << 16) - state->src_x))
276 state->src_w = (buffer->width << 16) - state->src_x;
277 if (state->src_h > (uint32_t) ((buffer->height << 16) - state->src_y))
278 state->src_h = (buffer->height << 16) - state->src_y;
279
Marius Vlad2538aac2019-10-14 11:05:30 +0300280 /* apply zpos if available */
281 state->zpos = zpos;
282
Daniel Stone6b466f22019-06-18 11:30:54 +0100283 return true;
284}
285
286/**
Alexandros Frantzis99751342020-05-18 15:22:49 +0300287 * Reset the current state of a DRM plane
288 *
289 * The current state will be freed and replaced by a pristine state.
290 *
291 * @param plane The plane to reset the current state of
292 */
293void
294drm_plane_reset_state(struct drm_plane *plane)
295{
296 drm_plane_state_free(plane->state_cur, true);
297 plane->state_cur = drm_plane_state_alloc(NULL, plane);
298 plane->state_cur->complete = true;
299}
300
301/**
Daniel Stone6b466f22019-06-18 11:30:54 +0100302 * Return a plane state from a drm_output_state.
303 */
304struct drm_plane_state *
305drm_output_state_get_existing_plane(struct drm_output_state *state_output,
306 struct drm_plane *plane)
307{
308 struct drm_plane_state *ps;
309
310 wl_list_for_each(ps, &state_output->plane_list, link) {
311 if (ps->plane == plane)
312 return ps;
313 }
314
315 return NULL;
316}
317
318/**
319 * Return a plane state from a drm_output_state, either existing or
320 * freshly allocated.
321 */
322struct drm_plane_state *
323drm_output_state_get_plane(struct drm_output_state *state_output,
324 struct drm_plane *plane)
325{
326 struct drm_plane_state *ps;
327
328 ps = drm_output_state_get_existing_plane(state_output, plane);
329 if (ps)
330 return ps;
331
332 return drm_plane_state_alloc(state_output, plane);
333}
334
335/**
336 * Allocate a new, empty drm_output_state. This should not generally be used
337 * in the repaint cycle; see drm_output_state_duplicate.
338 */
339struct drm_output_state *
340drm_output_state_alloc(struct drm_output *output,
341 struct drm_pending_state *pending_state)
342{
343 struct drm_output_state *state = zalloc(sizeof(*state));
344
345 assert(state);
346 state->output = output;
347 state->dpms = WESTON_DPMS_OFF;
Ankit Nautiyala344fe32019-05-14 18:36:08 +0530348 state->protection = WESTON_HDCP_DISABLE;
Daniel Stone6b466f22019-06-18 11:30:54 +0100349 state->pending_state = pending_state;
350 if (pending_state)
351 wl_list_insert(&pending_state->output_list, &state->link);
352 else
353 wl_list_init(&state->link);
354
355 wl_list_init(&state->plane_list);
356
357 return state;
358}
359
360/**
361 * Duplicate an existing drm_output_state into a new one. This is generally
362 * used during the repaint cycle, to capture the existing state of an output
363 * and modify it to create a new state to be used.
364 *
365 * The mode determines whether the output will be reset to an a blank state,
366 * or an exact mirror of the current state.
367 */
368struct drm_output_state *
369drm_output_state_duplicate(struct drm_output_state *src,
370 struct drm_pending_state *pending_state,
371 enum drm_output_state_duplicate_mode plane_mode)
372{
373 struct drm_output_state *dst = malloc(sizeof(*dst));
374 struct drm_plane_state *ps;
375
376 assert(dst);
377
378 /* Copy the whole structure, then individually modify the
379 * pending_state, as well as the list link into our pending
380 * state. */
381 *dst = *src;
382
383 dst->pending_state = pending_state;
384 if (pending_state)
385 wl_list_insert(&pending_state->output_list, &dst->link);
386 else
387 wl_list_init(&dst->link);
388
389 wl_list_init(&dst->plane_list);
390
391 wl_list_for_each(ps, &src->plane_list, link) {
392 /* Don't carry planes which are now disabled; these should be
393 * free for other outputs to reuse. */
394 if (!ps->output)
395 continue;
396
397 if (plane_mode == DRM_OUTPUT_STATE_CLEAR_PLANES)
398 (void) drm_plane_state_alloc(dst, ps->plane);
399 else
400 (void) drm_plane_state_duplicate(dst, ps);
401 }
402
403 return dst;
404}
405
406/**
407 * Free an unused drm_output_state.
408 */
409void
410drm_output_state_free(struct drm_output_state *state)
411{
412 struct drm_plane_state *ps, *next;
413
414 if (!state)
415 return;
416
417 wl_list_for_each_safe(ps, next, &state->plane_list, link)
418 drm_plane_state_free(ps, false);
419
420 wl_list_remove(&state->link);
421
422 free(state);
423}
424
425/**
426 * Allocate a new drm_pending_state
427 *
428 * Allocate a new, empty, 'pending state' structure to be used across a
429 * repaint cycle or similar.
430 *
431 * @param backend DRM backend
432 * @returns Newly-allocated pending state structure
433 */
434struct drm_pending_state *
435drm_pending_state_alloc(struct drm_backend *backend)
436{
437 struct drm_pending_state *ret;
438
439 ret = calloc(1, sizeof(*ret));
440 if (!ret)
441 return NULL;
442
443 ret->backend = backend;
444 wl_list_init(&ret->output_list);
445
446 return ret;
447}
448
449/**
450 * Free a drm_pending_state structure
451 *
452 * Frees a pending_state structure, as well as any output_states connected
453 * to this pending state.
454 *
455 * @param pending_state Pending state structure to free
456 */
457void
458drm_pending_state_free(struct drm_pending_state *pending_state)
459{
460 struct drm_output_state *output_state, *tmp;
461
462 if (!pending_state)
463 return;
464
465 wl_list_for_each_safe(output_state, tmp, &pending_state->output_list,
466 link) {
467 drm_output_state_free(output_state);
468 }
469
470 free(pending_state);
471}
472
473/**
474 * Find an output state in a pending state
475 *
476 * Given a pending_state structure, find the output_state for a particular
477 * output.
478 *
479 * @param pending_state Pending state structure to search
480 * @param output Output to find state for
481 * @returns Output state if present, or NULL if not
482 */
483struct drm_output_state *
484drm_pending_state_get_output(struct drm_pending_state *pending_state,
485 struct drm_output *output)
486{
487 struct drm_output_state *output_state;
488
489 wl_list_for_each(output_state, &pending_state->output_list, link) {
490 if (output_state->output == output)
491 return output_state;
492 }
493
494 return NULL;
495}