blob: 3956960c8e11f4ebc07dd3d56975c9b1358337af [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>
34#include <drm_fourcc.h>
35
36#include "drm-internal.h"
37
38/**
39 * Allocate a new, empty, plane state.
40 */
41struct drm_plane_state *
42drm_plane_state_alloc(struct drm_output_state *state_output,
43 struct drm_plane *plane)
44{
45 struct drm_plane_state *state = zalloc(sizeof(*state));
46
47 assert(state);
48 state->output_state = state_output;
49 state->plane = plane;
50 state->in_fence_fd = -1;
51 pixman_region32_init(&state->damage);
52
53 /* Here we only add the plane state to the desired link, and not
54 * set the member. Having an output pointer set means that the
55 * plane will be displayed on the output; this won't be the case
56 * when we go to disable a plane. In this case, it must be part of
57 * the commit (and thus the output state), but the member must be
58 * NULL, as it will not be on any output when the state takes
59 * effect.
60 */
61 if (state_output)
62 wl_list_insert(&state_output->plane_list, &state->link);
63 else
64 wl_list_init(&state->link);
65
66 return state;
67}
68
69/**
70 * Free an existing plane state. As a special case, the state will not
71 * normally be freed if it is the current state; see drm_plane_set_state.
72 */
73void
74drm_plane_state_free(struct drm_plane_state *state, bool force)
75{
76 if (!state)
77 return;
78
79 wl_list_remove(&state->link);
80 wl_list_init(&state->link);
81 state->output_state = NULL;
82 state->in_fence_fd = -1;
83 pixman_region32_fini(&state->damage);
84
85 if (force || state != state->plane->state_cur) {
86 drm_fb_unref(state->fb);
87 free(state);
88 }
89}
90
91/**
92 * Duplicate an existing plane state into a new plane state, storing it within
93 * the given output state. If the output state already contains a plane state
94 * for the drm_plane referenced by 'src', that plane state is freed first.
95 */
96struct drm_plane_state *
97drm_plane_state_duplicate(struct drm_output_state *state_output,
98 struct drm_plane_state *src)
99{
100 struct drm_plane_state *dst = malloc(sizeof(*dst));
101 struct drm_plane_state *old, *tmp;
102
103 assert(src);
104 assert(dst);
105 *dst = *src;
106 wl_list_init(&dst->link);
107
108 wl_list_for_each_safe(old, tmp, &state_output->plane_list, link) {
109 /* Duplicating a plane state into the same output state, so
110 * it can replace itself with an identical copy of itself,
111 * makes no sense. */
112 assert(old != src);
113 if (old->plane == dst->plane)
114 drm_plane_state_free(old, false);
115 }
116
117 wl_list_insert(&state_output->plane_list, &dst->link);
118 if (src->fb)
119 dst->fb = drm_fb_ref(src->fb);
120 dst->output_state = state_output;
121 pixman_region32_init(&dst->damage);
122 dst->complete = false;
123
124 return dst;
125}
126
127/**
128 * Remove a plane state from an output state; if the plane was previously
129 * enabled, then replace it with a disabling state. This ensures that the
130 * output state was untouched from it was before the plane state was
131 * modified by the caller of this function.
132 *
133 * This is required as drm_output_state_get_plane may either allocate a
134 * new plane state, in which case this function will just perform a matching
135 * drm_plane_state_free, or it may instead repurpose an existing disabling
136 * state (if the plane was previously active), in which case this function
137 * will reset it.
138 */
139void
140drm_plane_state_put_back(struct drm_plane_state *state)
141{
142 struct drm_output_state *state_output;
143 struct drm_plane *plane;
144
145 if (!state)
146 return;
147
148 state_output = state->output_state;
149 plane = state->plane;
150 drm_plane_state_free(state, false);
151
152 /* Plane was previously disabled; no need to keep this temporary
153 * state around. */
154 if (!plane->state_cur->fb)
155 return;
156
157 (void) drm_plane_state_alloc(state_output, plane);
158}
159
160/**
161 * Given a weston_view, fill the drm_plane_state's co-ordinates to display on
162 * a given plane.
163 */
164bool
165drm_plane_state_coords_for_view(struct drm_plane_state *state,
166 struct weston_view *ev)
167{
168 struct drm_output *output = state->output;
169 struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
170 pixman_region32_t dest_rect, src_rect;
171 pixman_box32_t *box, tbox;
172 float sxf1, syf1, sxf2, syf2;
173
174 if (!drm_view_transform_supported(ev, &output->base))
175 return false;
176
177 /* Update the base weston_plane co-ordinates. */
178 box = pixman_region32_extents(&ev->transform.boundingbox);
179 state->plane->base.x = box->x1;
180 state->plane->base.y = box->y1;
181
182 /* First calculate the destination co-ordinates by taking the
183 * area of the view which is visible on this output, performing any
184 * transforms to account for output rotation and scale as necessary. */
185 pixman_region32_init(&dest_rect);
186 pixman_region32_intersect(&dest_rect, &ev->transform.boundingbox,
187 &output->base.region);
188 pixman_region32_translate(&dest_rect, -output->base.x, -output->base.y);
189 box = pixman_region32_extents(&dest_rect);
190 tbox = weston_transformed_rect(output->base.width,
191 output->base.height,
192 output->base.transform,
193 output->base.current_scale,
194 *box);
195 state->dest_x = tbox.x1;
196 state->dest_y = tbox.y1;
197 state->dest_w = tbox.x2 - tbox.x1;
198 state->dest_h = tbox.y2 - tbox.y1;
199 pixman_region32_fini(&dest_rect);
200
201 /* Now calculate the source rectangle, by finding the extents of the
202 * view, and working backwards to source co-ordinates. */
203 pixman_region32_init(&src_rect);
204 pixman_region32_intersect(&src_rect, &ev->transform.boundingbox,
205 &output->base.region);
206 box = pixman_region32_extents(&src_rect);
207 weston_view_from_global_float(ev, box->x1, box->y1, &sxf1, &syf1);
208 weston_surface_to_buffer_float(ev->surface, sxf1, syf1, &sxf1, &syf1);
209 weston_view_from_global_float(ev, box->x2, box->y2, &sxf2, &syf2);
210 weston_surface_to_buffer_float(ev->surface, sxf2, syf2, &sxf2, &syf2);
211 pixman_region32_fini(&src_rect);
212
213 /* Buffer transforms may mean that x2 is to the left of x1, and/or that
214 * y2 is above y1. */
215 if (sxf2 < sxf1) {
216 double tmp = sxf1;
217 sxf1 = sxf2;
218 sxf2 = tmp;
219 }
220 if (syf2 < syf1) {
221 double tmp = syf1;
222 syf1 = syf2;
223 syf2 = tmp;
224 }
225
226 /* Shift from S23.8 wl_fixed to U16.16 KMS fixed-point encoding. */
227 state->src_x = wl_fixed_from_double(sxf1) << 8;
228 state->src_y = wl_fixed_from_double(syf1) << 8;
229 state->src_w = wl_fixed_from_double(sxf2 - sxf1) << 8;
230 state->src_h = wl_fixed_from_double(syf2 - syf1) << 8;
231
232 /* Clamp our source co-ordinates to surface bounds; it's possible
233 * for intermediate translations to give us slightly incorrect
234 * co-ordinates if we have, for example, multiple zooming
235 * transformations. View bounding boxes are also explicitly rounded
236 * greedily. */
237 if (state->src_x < 0)
238 state->src_x = 0;
239 if (state->src_y < 0)
240 state->src_y = 0;
241 if (state->src_w > (uint32_t) ((buffer->width << 16) - state->src_x))
242 state->src_w = (buffer->width << 16) - state->src_x;
243 if (state->src_h > (uint32_t) ((buffer->height << 16) - state->src_y))
244 state->src_h = (buffer->height << 16) - state->src_y;
245
246 return true;
247}
248
249/**
250 * Return a plane state from a drm_output_state.
251 */
252struct drm_plane_state *
253drm_output_state_get_existing_plane(struct drm_output_state *state_output,
254 struct drm_plane *plane)
255{
256 struct drm_plane_state *ps;
257
258 wl_list_for_each(ps, &state_output->plane_list, link) {
259 if (ps->plane == plane)
260 return ps;
261 }
262
263 return NULL;
264}
265
266/**
267 * Return a plane state from a drm_output_state, either existing or
268 * freshly allocated.
269 */
270struct drm_plane_state *
271drm_output_state_get_plane(struct drm_output_state *state_output,
272 struct drm_plane *plane)
273{
274 struct drm_plane_state *ps;
275
276 ps = drm_output_state_get_existing_plane(state_output, plane);
277 if (ps)
278 return ps;
279
280 return drm_plane_state_alloc(state_output, plane);
281}
282
283/**
284 * Allocate a new, empty drm_output_state. This should not generally be used
285 * in the repaint cycle; see drm_output_state_duplicate.
286 */
287struct drm_output_state *
288drm_output_state_alloc(struct drm_output *output,
289 struct drm_pending_state *pending_state)
290{
291 struct drm_output_state *state = zalloc(sizeof(*state));
292
293 assert(state);
294 state->output = output;
295 state->dpms = WESTON_DPMS_OFF;
Ankit Nautiyala344fe32019-05-14 18:36:08 +0530296 state->protection = WESTON_HDCP_DISABLE;
Daniel Stone6b466f22019-06-18 11:30:54 +0100297 state->pending_state = pending_state;
298 if (pending_state)
299 wl_list_insert(&pending_state->output_list, &state->link);
300 else
301 wl_list_init(&state->link);
302
303 wl_list_init(&state->plane_list);
304
305 return state;
306}
307
308/**
309 * Duplicate an existing drm_output_state into a new one. This is generally
310 * used during the repaint cycle, to capture the existing state of an output
311 * and modify it to create a new state to be used.
312 *
313 * The mode determines whether the output will be reset to an a blank state,
314 * or an exact mirror of the current state.
315 */
316struct drm_output_state *
317drm_output_state_duplicate(struct drm_output_state *src,
318 struct drm_pending_state *pending_state,
319 enum drm_output_state_duplicate_mode plane_mode)
320{
321 struct drm_output_state *dst = malloc(sizeof(*dst));
322 struct drm_plane_state *ps;
323
324 assert(dst);
325
326 /* Copy the whole structure, then individually modify the
327 * pending_state, as well as the list link into our pending
328 * state. */
329 *dst = *src;
330
331 dst->pending_state = pending_state;
332 if (pending_state)
333 wl_list_insert(&pending_state->output_list, &dst->link);
334 else
335 wl_list_init(&dst->link);
336
337 wl_list_init(&dst->plane_list);
338
339 wl_list_for_each(ps, &src->plane_list, link) {
340 /* Don't carry planes which are now disabled; these should be
341 * free for other outputs to reuse. */
342 if (!ps->output)
343 continue;
344
345 if (plane_mode == DRM_OUTPUT_STATE_CLEAR_PLANES)
346 (void) drm_plane_state_alloc(dst, ps->plane);
347 else
348 (void) drm_plane_state_duplicate(dst, ps);
349 }
350
351 return dst;
352}
353
354/**
355 * Free an unused drm_output_state.
356 */
357void
358drm_output_state_free(struct drm_output_state *state)
359{
360 struct drm_plane_state *ps, *next;
361
362 if (!state)
363 return;
364
365 wl_list_for_each_safe(ps, next, &state->plane_list, link)
366 drm_plane_state_free(ps, false);
367
368 wl_list_remove(&state->link);
369
370 free(state);
371}
372
373/**
374 * Allocate a new drm_pending_state
375 *
376 * Allocate a new, empty, 'pending state' structure to be used across a
377 * repaint cycle or similar.
378 *
379 * @param backend DRM backend
380 * @returns Newly-allocated pending state structure
381 */
382struct drm_pending_state *
383drm_pending_state_alloc(struct drm_backend *backend)
384{
385 struct drm_pending_state *ret;
386
387 ret = calloc(1, sizeof(*ret));
388 if (!ret)
389 return NULL;
390
391 ret->backend = backend;
392 wl_list_init(&ret->output_list);
393
394 return ret;
395}
396
397/**
398 * Free a drm_pending_state structure
399 *
400 * Frees a pending_state structure, as well as any output_states connected
401 * to this pending state.
402 *
403 * @param pending_state Pending state structure to free
404 */
405void
406drm_pending_state_free(struct drm_pending_state *pending_state)
407{
408 struct drm_output_state *output_state, *tmp;
409
410 if (!pending_state)
411 return;
412
413 wl_list_for_each_safe(output_state, tmp, &pending_state->output_list,
414 link) {
415 drm_output_state_free(output_state);
416 }
417
418 free(pending_state);
419}
420
421/**
422 * Find an output state in a pending state
423 *
424 * Given a pending_state structure, find the output_state for a particular
425 * output.
426 *
427 * @param pending_state Pending state structure to search
428 * @param output Output to find state for
429 * @returns Output state if present, or NULL if not
430 */
431struct drm_output_state *
432drm_pending_state_get_output(struct drm_pending_state *pending_state,
433 struct drm_output *output)
434{
435 struct drm_output_state *output_state;
436
437 wl_list_for_each(output_state, &pending_state->output_list, link) {
438 if (output_state->output == output)
439 return output_state;
440 }
441
442 return NULL;
443}