• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
35 #include <libweston/libweston.h>
36 #include <libweston/backend-drm.h>
37 #include <libweston/pixel-formats.h>
38 
39 #include "drm-internal.h"
40 
41 #include "linux-dmabuf.h"
42 #include "presentation-time-server-protocol.h"
43 
44 enum drm_output_propose_state_mode {
45 	DRM_OUTPUT_PROPOSE_STATE_MIXED, /**< mix renderer & planes */
46 	DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY, /**< only assign to renderer & cursor */
47 	DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY, /**< no renderer use, only planes */
48 };
49 
50 static const char *const drm_output_propose_state_mode_as_string[] = {
51 	[DRM_OUTPUT_PROPOSE_STATE_MIXED] = "mixed state",
52 	[DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY] = "render-only state",
53 	[DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY]	= "plane-only state"
54 };
55 
56 static const char *
drm_propose_state_mode_to_string(enum drm_output_propose_state_mode mode)57 drm_propose_state_mode_to_string(enum drm_output_propose_state_mode mode)
58 {
59 	if (mode < 0 || mode >= ARRAY_LENGTH(drm_output_propose_state_mode_as_string))
60 		return " unknown compositing mode";
61 
62 	return drm_output_propose_state_mode_as_string[mode];
63 }
64 
65 static void
drm_output_add_zpos_plane(struct drm_plane * plane,struct wl_list * planes)66 drm_output_add_zpos_plane(struct drm_plane *plane, struct wl_list *planes)
67 {
68 	struct drm_backend *b = plane->backend;
69 	struct drm_plane_zpos *h_plane;
70 	struct drm_plane_zpos *plane_zpos;
71 
72 	plane_zpos = zalloc(sizeof(*plane_zpos));
73 	if (!plane_zpos)
74 		return;
75 
76 	plane_zpos->plane = plane;
77 
78 	drm_debug(b, "\t\t\t\t[plane] plane %d added to candidate list\n",
79 		      plane->plane_id);
80 
81 	if (wl_list_empty(planes)) {
82 		wl_list_insert(planes, &plane_zpos->link);
83 		return;
84 	}
85 
86 	h_plane = wl_container_of(planes->next, h_plane, link);
87 	if (h_plane->plane->zpos_max >= plane->zpos_max) {
88 		wl_list_insert(planes->prev, &plane_zpos->link);
89 	} else {
90 		struct drm_plane_zpos *p_zpos = NULL;
91 
92 		if (wl_list_length(planes) == 1) {
93 			wl_list_insert(planes->prev, &plane_zpos->link);
94 			return;
95 		}
96 
97 		wl_list_for_each(p_zpos, planes, link) {
98 			if (p_zpos->plane->zpos_max >
99 			    plane_zpos->plane->zpos_max)
100 				break;
101 		}
102 
103 		wl_list_insert(p_zpos->link.prev, &plane_zpos->link);
104 	}
105 }
106 
107 static void
drm_output_destroy_zpos_plane(struct drm_plane_zpos * plane_zpos)108 drm_output_destroy_zpos_plane(struct drm_plane_zpos *plane_zpos)
109 {
110 	wl_list_remove(&plane_zpos->link);
111 	free(plane_zpos);
112 }
113 
114 static bool
drm_output_check_plane_has_view_assigned(struct drm_plane * plane,struct drm_output_state * output_state)115 drm_output_check_plane_has_view_assigned(struct drm_plane *plane,
116                                         struct drm_output_state *output_state)
117 {
118 	struct drm_plane_state *ps;
119 	wl_list_for_each(ps, &output_state->plane_list, link) {
120 		if (ps->plane == plane && ps->fb)
121 			return true;
122 	}
123 	return false;
124 }
125 
126 static bool
drm_output_plane_has_valid_format(struct drm_plane * plane,struct drm_output_state * state,struct drm_fb * fb)127 drm_output_plane_has_valid_format(struct drm_plane *plane,
128 				  struct drm_output_state *state,
129 				  struct drm_fb *fb)
130 {
131 	struct drm_backend *b = plane->backend;
132 	unsigned int i;
133 
134 	if (!fb)
135 		return false;
136 
137 	/* Check whether the format is supported */
138 	for (i = 0; i < plane->count_formats; i++) {
139 		unsigned int j;
140 
141 		if (plane->formats[i].format != fb->format->format)
142 			continue;
143 
144 		if (fb->modifier == DRM_FORMAT_MOD_INVALID)
145 			return true;
146 
147 		for (j = 0; j < plane->formats[i].count_modifiers; j++) {
148 			if (plane->formats[i].modifiers[j] == fb->modifier)
149 				return true;
150 		}
151 	}
152 
153 	drm_debug(b, "\t\t\t\t[%s] not placing view on %s: "
154 		  "no free %s planes matching format %s (0x%lx) "
155 		  "modifier 0x%llx\n",
156 		  drm_output_get_plane_type_name(plane),
157 		  drm_output_get_plane_type_name(plane),
158 		  drm_output_get_plane_type_name(plane),
159 		  fb->format->drm_format_name,
160 		  (unsigned long) fb->format,
161 		  (unsigned long long) fb->modifier);
162 
163 	return false;
164 }
165 
166 static bool
drm_output_plane_cursor_has_valid_format(struct weston_view * ev)167 drm_output_plane_cursor_has_valid_format(struct weston_view *ev)
168 {
169 	struct wl_shm_buffer *shmbuf =
170 		wl_shm_buffer_get(ev->surface->buffer_ref.buffer->resource);
171 
172 	if (shmbuf && wl_shm_buffer_get_format(shmbuf) == WL_SHM_FORMAT_ARGB8888)
173 		return true;
174 
175 	return false;
176 }
177 
178 static struct drm_plane_state *
drm_output_prepare_overlay_view(struct drm_plane * plane,struct drm_output_state * output_state,struct weston_view * ev,enum drm_output_propose_state_mode mode,struct drm_fb * fb,uint64_t zpos)179 drm_output_prepare_overlay_view(struct drm_plane *plane,
180 				struct drm_output_state *output_state,
181 				struct weston_view *ev,
182 				enum drm_output_propose_state_mode mode,
183 				struct drm_fb *fb, uint64_t zpos)
184 {
185 	struct drm_output *output = output_state->output;
186 	struct weston_compositor *ec = output->base.compositor;
187 	struct drm_backend *b = to_drm_backend(ec);
188 	struct drm_plane_state *state = NULL;
189 	int ret;
190 
191 	assert(!b->sprites_are_broken);
192 	assert(b->atomic_modeset);
193 
194 	if (!fb) {
195 		drm_debug(b, "\t\t\t\t[overlay] not placing view %p on overlay: "
196 			     " couldn't get fb\n", ev);
197 		return NULL;
198 	}
199 
200 	state = drm_output_state_get_plane(output_state, plane);
201 	/* we can't have a 'pending' framebuffer as never set one before reaching here */
202 	assert(!state->fb);
203 
204 	state->ev = ev;
205 	state->output = output;
206 
207 	if (!drm_plane_state_coords_for_view(state, ev, zpos)) {
208 		drm_debug(b, "\t\t\t\t[overlay] not placing view %p on overlay: "
209 			     "unsuitable transform\n", ev);
210 		drm_plane_state_put_back(state);
211 		state = NULL;
212 		goto out;
213 	}
214 
215 	/* If the surface buffer has an in-fence fd, but the plane
216 	 * doesn't support fences, we can't place the buffer on this
217 	 * plane. */
218 	if (ev->surface->acquire_fence_fd >= 0 &&
219 	     plane->props[WDRM_PLANE_IN_FENCE_FD].prop_id == 0) {
220 		drm_debug(b, "\t\t\t\t[overlay] not placing view %p on overlay: "
221 			     "no in-fence support\n", ev);
222 		drm_plane_state_put_back(state);
223 		state = NULL;
224 		goto out;
225 	}
226 
227 	/* We hold one reference for the lifetime of this function; from
228 	 * calling drm_fb_get_from_view() in drm_output_prepare_plane_view(),
229 	 * so, we take another reference here to live within the state. */
230 	state->fb = drm_fb_ref(fb);
231 
232 	state->in_fence_fd = ev->surface->acquire_fence_fd;
233 
234 	/* In planes-only mode, we don't have an incremental state to
235 	 * test against, so we just hope it'll work. */
236 	if (mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY) {
237 		drm_debug(b, "\t\t\t[overlay] provisionally placing "
238 			     "view %p on overlay %lu in planes-only mode\n",
239 			  ev, (unsigned long) plane->plane_id);
240 		goto out;
241 	}
242 
243 	ret = drm_pending_state_test(output_state->pending_state);
244 	if (ret == 0) {
245 		drm_debug(b, "\t\t\t[overlay] provisionally placing "
246 			     "view %p on overlay %d in mixed mode\n",
247 			  ev, plane->plane_id);
248 		goto out;
249 	}
250 
251 	drm_debug(b, "\t\t\t[overlay] not placing view %p on overlay %lu "
252 		     "in mixed mode: kernel test failed\n",
253 		  ev, (unsigned long) plane->plane_id);
254 
255 	drm_plane_state_put_back(state);
256 	state = NULL;
257 
258 out:
259 	return state;
260 }
261 
262 #ifdef BUILD_DRM_GBM
263 /**
264  * Update the image for the current cursor surface
265  *
266  * @param plane_state DRM cursor plane state
267  * @param ev Source view for cursor
268  */
269 static void
cursor_bo_update(struct drm_plane_state * plane_state,struct weston_view * ev)270 cursor_bo_update(struct drm_plane_state *plane_state, struct weston_view *ev)
271 {
272 	struct drm_backend *b = plane_state->plane->backend;
273 	struct gbm_bo *bo = plane_state->fb->bo;
274 	struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
275 	uint32_t buf[b->cursor_width * b->cursor_height];
276 	int32_t stride;
277 	uint8_t *s;
278 	int i;
279 
280 	assert(buffer && buffer->shm_buffer);
281 	assert(buffer->shm_buffer == wl_shm_buffer_get(buffer->resource));
282 	assert(buffer->width <= b->cursor_width);
283 	assert(buffer->height <= b->cursor_height);
284 
285 	memset(buf, 0, sizeof buf);
286 	stride = wl_shm_buffer_get_stride(buffer->shm_buffer);
287 	s = wl_shm_buffer_get_data(buffer->shm_buffer);
288 
289 	wl_shm_buffer_begin_access(buffer->shm_buffer);
290 	for (i = 0; i < buffer->height; i++)
291 		memcpy(buf + i * b->cursor_width,
292 		       s + i * stride,
293 		       buffer->width * 4);
294 	wl_shm_buffer_end_access(buffer->shm_buffer);
295 
296 	if (gbm_bo_write(bo, buf, sizeof buf) < 0)
297 		weston_log("failed update cursor: %s\n", strerror(errno));
298 }
299 
300 static struct drm_plane_state *
drm_output_prepare_cursor_view(struct drm_output_state * output_state,struct weston_view * ev,uint64_t zpos)301 drm_output_prepare_cursor_view(struct drm_output_state *output_state,
302 			       struct weston_view *ev, uint64_t zpos)
303 {
304 	struct drm_output *output = output_state->output;
305 	struct drm_backend *b = to_drm_backend(output->base.compositor);
306 	struct drm_plane *plane = output->cursor_plane;
307 	struct drm_plane_state *plane_state;
308 	bool needs_update = false;
309 	const char *p_name = drm_output_get_plane_type_name(plane);
310 
311 	assert(!b->cursors_are_broken);
312 
313 	if (!plane)
314 		return NULL;
315 
316 	if (!plane->state_cur->complete)
317 		return NULL;
318 
319 	if (plane->state_cur->output && plane->state_cur->output != output)
320 		return NULL;
321 
322 	/* We use GBM to import SHM buffers. */
323 	if (b->gbm == NULL)
324 		return NULL;
325 
326 	plane_state =
327 		drm_output_state_get_plane(output_state, output->cursor_plane);
328 
329 	if (plane_state && plane_state->fb)
330 		return NULL;
331 
332 	/* We can't scale with the legacy API, and we don't try to account for
333 	 * simple cropping/translation in cursor_bo_update. */
334 	plane_state->output = output;
335 	if (!drm_plane_state_coords_for_view(plane_state, ev, zpos)) {
336 		drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
337 			     "unsuitable transform\n", p_name, ev, p_name);
338 		goto err;
339 	}
340 
341 	if (plane_state->src_x != 0 || plane_state->src_y != 0 ||
342 	    plane_state->src_w > (unsigned) b->cursor_width << 16 ||
343 	    plane_state->src_h > (unsigned) b->cursor_height << 16 ||
344 	    plane_state->src_w != plane_state->dest_w << 16 ||
345 	    plane_state->src_h != plane_state->dest_h << 16) {
346 		drm_debug(b, "\t\t\t\t[%s] not assigning view %p to %s plane "
347 			     "(positioning requires cropping or scaling)\n",
348 			     p_name, ev, p_name);
349 		goto err;
350 	}
351 
352 	/* Since we're setting plane state up front, we need to work out
353 	 * whether or not we need to upload a new cursor. We can't use the
354 	 * plane damage, since the planes haven't actually been calculated
355 	 * yet: instead try to figure it out directly. KMS cursor planes are
356 	 * pretty unique here, in that they lie partway between a Weston plane
357 	 * (direct scanout) and a renderer. */
358 	if (ev != output->cursor_view ||
359 	    pixman_region32_not_empty(&ev->surface->damage)) {
360 		output->current_cursor++;
361 		output->current_cursor =
362 			output->current_cursor %
363 				ARRAY_LENGTH(output->gbm_cursor_fb);
364 		needs_update = true;
365 	}
366 
367 	output->cursor_view = ev;
368 	plane_state->ev = ev;
369 
370 	plane_state->fb =
371 		drm_fb_ref(output->gbm_cursor_fb[output->current_cursor]);
372 
373 	if (needs_update) {
374 		drm_debug(b, "\t\t\t\t[%s] copying new content to cursor BO\n", p_name);
375 		cursor_bo_update(plane_state, ev);
376 	}
377 
378 	/* The cursor API is somewhat special: in cursor_bo_update(), we upload
379 	 * a buffer which is always cursor_width x cursor_height, even if the
380 	 * surface we want to promote is actually smaller than this. Manually
381 	 * mangle the plane state to deal with this. */
382 	plane_state->src_w = b->cursor_width << 16;
383 	plane_state->src_h = b->cursor_height << 16;
384 	plane_state->dest_w = b->cursor_width;
385 	plane_state->dest_h = b->cursor_height;
386 
387 	drm_debug(b, "\t\t\t\t[%s] provisionally assigned view %p to cursor\n",
388 		  p_name, ev);
389 
390 	return plane_state;
391 
392 err:
393 	drm_plane_state_put_back(plane_state);
394 	return NULL;
395 }
396 #else
397 static struct drm_plane_state *
drm_output_prepare_cursor_view(struct drm_output_state * output_state,struct weston_view * ev,uint64_t zpos)398 drm_output_prepare_cursor_view(struct drm_output_state *output_state,
399 			       struct weston_view *ev, uint64_t zpos)
400 {
401 	return NULL;
402 }
403 #endif
404 
405 static struct drm_plane_state *
drm_output_prepare_scanout_view(struct drm_output_state * output_state,struct weston_view * ev,enum drm_output_propose_state_mode mode,struct drm_fb * fb,uint64_t zpos)406 drm_output_prepare_scanout_view(struct drm_output_state *output_state,
407 				struct weston_view *ev,
408 				enum drm_output_propose_state_mode mode,
409 				struct drm_fb *fb, uint64_t zpos)
410 {
411 	struct drm_output *output = output_state->output;
412 	struct drm_backend *b = to_drm_backend(output->base.compositor);
413 	struct drm_plane *scanout_plane = output->scanout_plane;
414 	struct drm_plane_state *state;
415 	const char *p_name = drm_output_get_plane_type_name(scanout_plane);
416 
417 	assert(!b->sprites_are_broken);
418 	assert(b->atomic_modeset);
419 	assert(mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY);
420 
421 	/* Check the view spans exactly the output size, calculated in the
422 	 * logical co-ordinate space. */
423 	if (!weston_view_matches_output_entirely(ev, &output->base)) {
424 		drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
425 			     " view does not match output entirely\n",
426 			     p_name, ev, p_name);
427 		return NULL;
428 	}
429 
430 	/* If the surface buffer has an in-fence fd, but the plane doesn't
431 	 * support fences, we can't place the buffer on this plane. */
432 	if (ev->surface->acquire_fence_fd >= 0 &&
433 	    scanout_plane->props[WDRM_PLANE_IN_FENCE_FD].prop_id == 0) {
434 		drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
435 			     "no in-fence support\n", p_name, ev, p_name);
436 		return NULL;
437 	}
438 
439 	if (!fb) {
440 		drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
441 			     " couldn't get fb\n", p_name, ev, p_name);
442 		return NULL;
443 	}
444 
445 	state = drm_output_state_get_plane(output_state, scanout_plane);
446 
447 	/* The only way we can already have a buffer in the scanout plane is
448 	 * if we are in mixed mode, or if a client buffer has already been
449 	 * placed into scanout. The former case will never call into here,
450 	 * and in the latter case, the view must have been marked as occluded,
451 	 * meaning we should never have ended up here. */
452 	assert(!state->fb);
453 
454 	/* take another reference here to live within the state */
455 	state->fb = drm_fb_ref(fb);
456 	state->ev = ev;
457 	state->output = output;
458 	if (!drm_plane_state_coords_for_view(state, ev, zpos)) {
459 		drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
460 			     "unsuitable transform\n", p_name, ev, p_name);
461 		goto err;
462 	}
463 
464 	if (state->dest_x != 0 || state->dest_y != 0 ||
465 	    state->dest_w != (unsigned) output->base.current_mode->width ||
466 	    state->dest_h != (unsigned) output->base.current_mode->height) {
467 		drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
468 			     " invalid plane state\n", p_name, ev, p_name);
469 		goto err;
470 	}
471 
472 	state->in_fence_fd = ev->surface->acquire_fence_fd;
473 
474 	/* In plane-only mode, we don't need to test the state now, as we
475 	 * will only test it once at the end. */
476 	return state;
477 
478 err:
479 	drm_plane_state_put_back(state);
480 	return NULL;
481 }
482 
483 static bool
drm_output_plane_view_has_valid_format(struct drm_plane * plane,struct drm_output_state * state,struct weston_view * ev,struct drm_fb * fb)484 drm_output_plane_view_has_valid_format(struct drm_plane *plane,
485 				       struct drm_output_state *state,
486 				       struct weston_view *ev,
487 				       struct drm_fb *fb)
488 {
489 	/* depending on the type of the plane we have different requirements */
490 	switch (plane->type) {
491 	case WDRM_PLANE_TYPE_CURSOR:
492 		return drm_output_plane_cursor_has_valid_format(ev);
493 	case WDRM_PLANE_TYPE_OVERLAY:
494 		return drm_output_plane_has_valid_format(plane, state, fb);
495 	case WDRM_PLANE_TYPE_PRIMARY:
496 		return drm_output_plane_has_valid_format(plane, state, fb);
497 	default:
498 		assert(0);
499 		return false;
500 	}
501 
502 	return false;
503 }
504 
505 static struct drm_plane_state *
drm_output_try_view_on_plane(struct drm_plane * plane,struct drm_output_state * state,struct weston_view * ev,enum drm_output_propose_state_mode mode,struct drm_fb * fb,uint64_t zpos)506 drm_output_try_view_on_plane(struct drm_plane *plane,
507 			     struct drm_output_state *state,
508 			     struct weston_view *ev,
509 			     enum drm_output_propose_state_mode mode,
510 			     struct drm_fb *fb, uint64_t zpos)
511 {
512 	struct drm_backend *b = state->pending_state->backend;
513 	struct weston_output *wet_output = &state->output->base;
514 	bool view_matches_entire_output, scanout_has_view_assigned;
515 	struct drm_plane *scanout_plane = state->output->scanout_plane;
516 	struct drm_plane_state *ps = NULL;
517 	const char *p_name = drm_output_get_plane_type_name(plane);
518 	enum {
519 		NO_PLANES,	/* generic err-handle */
520 		NO_PLANES_ACCEPTED,
521 		PLACED_ON_PLANE,
522 	} availability = NO_PLANES;
523 
524 	/* sanity checks in case we over/underflow zpos or pass incorrect
525 	 * values */
526 	assert(zpos <= plane->zpos_max ||
527 	       zpos != DRM_PLANE_ZPOS_INVALID_PLANE);
528 
529 	switch (plane->type) {
530 	case WDRM_PLANE_TYPE_CURSOR:
531 		if (b->cursors_are_broken) {
532 			availability = NO_PLANES_ACCEPTED;
533 			goto out;
534 		}
535 
536 		ps = drm_output_prepare_cursor_view(state, ev, zpos);
537 		if (ps)
538 			availability = PLACED_ON_PLANE;
539 		break;
540 	case WDRM_PLANE_TYPE_OVERLAY:
541 		/* do not attempt to place it in the overlay if we don't have
542 		 * anything in the scanout/primary and the view doesn't cover
543 		 * the entire output  */
544 		view_matches_entire_output =
545 			weston_view_matches_output_entirely(ev, wet_output);
546 		scanout_has_view_assigned =
547 			drm_output_check_plane_has_view_assigned(scanout_plane,
548 								 state);
549 
550 		if (view_matches_entire_output && !scanout_has_view_assigned) {
551 			availability = NO_PLANES_ACCEPTED;
552 			goto out;
553 		}
554 
555 		ps = drm_output_prepare_overlay_view(plane, state, ev, mode,
556 						     fb, zpos);
557 		if (ps)
558 			availability = PLACED_ON_PLANE;
559 		break;
560 	case WDRM_PLANE_TYPE_PRIMARY:
561 		if (mode != DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY) {
562 			availability = NO_PLANES_ACCEPTED;
563 			goto out;
564 		}
565 
566 		ps = drm_output_prepare_scanout_view(state, ev, mode,
567 						     fb, zpos);
568 		if (ps)
569 			availability = PLACED_ON_PLANE;
570 		break;
571 	default:
572 		assert(0);
573 		break;
574 	}
575 
576 out:
577 	switch (availability) {
578 	case NO_PLANES:
579 		/* set initial to this catch-all case, such that
580 		 * prepare_cursor/overlay/scanout() should have/contain the
581 		 * reason for failling */
582 		break;
583 	case NO_PLANES_ACCEPTED:
584 		drm_debug(b, "\t\t\t\t[plane] plane %d refusing to "
585 			     "place view %p in %s\n",
586 			     plane->plane_id, ev, p_name);
587 		break;
588 	case PLACED_ON_PLANE:
589 		break;
590 	}
591 
592 
593 	return ps;
594 }
595 
596 static void
drm_output_check_zpos_plane_states(struct drm_output_state * state)597 drm_output_check_zpos_plane_states(struct drm_output_state *state)
598 {
599 	struct drm_plane_state *ps;
600 
601 	wl_list_for_each(ps, &state->plane_list, link) {
602 		struct wl_list *next_node = ps->link.next;
603 		bool found_dup = false;
604 
605 		/* skip any plane that is not enabled */
606 		if (!ps->fb)
607 			continue;
608 
609 		assert(ps->zpos != DRM_PLANE_ZPOS_INVALID_PLANE);
610 
611 		/* find another plane with the same zpos value */
612 		if (next_node == &state->plane_list)
613 			break;
614 
615 		while (next_node && next_node != &state->plane_list) {
616 			struct drm_plane_state *ps_next;
617 
618 			ps_next = container_of(next_node,
619 					       struct drm_plane_state,
620 					       link);
621 
622 			if (ps->zpos == ps_next->zpos) {
623 				found_dup = true;
624 				break;
625 			}
626 			next_node = next_node->next;
627 		}
628 
629 		/* this should never happen so exit hard in case
630 		 * we screwed up that bad */
631 		assert(!found_dup);
632 	}
633 }
634 
635 static struct drm_plane_state *
drm_output_prepare_plane_view(struct drm_output_state * state,struct weston_view * ev,enum drm_output_propose_state_mode mode,struct drm_plane_state * scanout_state,uint64_t current_lowest_zpos)636 drm_output_prepare_plane_view(struct drm_output_state *state,
637 			      struct weston_view *ev,
638 			      enum drm_output_propose_state_mode mode,
639 			      struct drm_plane_state *scanout_state,
640 			      uint64_t current_lowest_zpos)
641 {
642 	struct drm_output *output = state->output;
643 	struct drm_backend *b = to_drm_backend(output->base.compositor);
644 
645 	struct drm_plane_state *ps = NULL;
646 	struct drm_plane *plane;
647 	struct drm_plane_zpos *p_zpos, *p_zpos_next;
648 	struct wl_list zpos_candidate_list;
649 
650 	struct drm_fb *fb;
651 
652 	wl_list_init(&zpos_candidate_list);
653 
654 	/* check view for valid buffer, doesn't make sense to even try */
655 	if (!weston_view_has_valid_buffer(ev))
656 		return ps;
657 
658 	fb = drm_fb_get_from_view(state, ev);
659 
660 	/* assemble a list with possible candidates */
661 	wl_list_for_each(plane, &b->plane_list, link) {
662 		if (!drm_plane_is_available(plane, output))
663 			continue;
664 
665 		if (drm_output_check_plane_has_view_assigned(plane, state)) {
666 			drm_debug(b, "\t\t\t\t[plane] not adding plane %d to"
667 				     " candidate list: view already assigned "
668 				     "to a plane\n", plane->plane_id);
669 			continue;
670 		}
671 
672 		if (plane->zpos_min >= current_lowest_zpos) {
673 			drm_debug(b, "\t\t\t\t[plane] not adding plane %d to "
674 				     "candidate list: minium zpos (%"PRIu64") "
675 				     "plane's above current lowest zpos "
676 				     "(%"PRIu64")\n", plane->plane_id,
677 				     plane->zpos_min, current_lowest_zpos);
678 			continue;
679 		}
680 
681 		if (mode == DRM_OUTPUT_PROPOSE_STATE_MIXED) {
682 			assert(scanout_state != NULL);
683 			if (scanout_state->zpos >= plane->zpos_max) {
684 				drm_debug(b, "\t\t\t\t[plane] not adding plane %d to "
685 					     "candidate list: primary's zpos "
686 					     "value (%"PRIu64") higher than "
687 					     "plane's maximum value (%"PRIu64")\n",
688 					     plane->plane_id, scanout_state->zpos,
689 					     plane->zpos_max);
690 				continue;
691 			}
692 		}
693 
694 		if (mode == DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY &&
695 		    (plane->type == WDRM_PLANE_TYPE_OVERLAY ||
696 		     plane->type == WDRM_PLANE_TYPE_PRIMARY)) {
697 			drm_debug(b, "\t\t\t\t[plane] not adding plane %d to "
698 				     "candidate list: renderer-only mode\n",
699 				     plane->plane_id);
700 			continue;
701 		}
702 
703 		if (plane->type != WDRM_PLANE_TYPE_CURSOR &&
704 		    b->sprites_are_broken) {
705 			drm_debug(b, "\t\t\t\t[plane] not adding plane %d, type %s to "
706 				     "candidate list: sprites are broken!\n",
707 				     plane->plane_id,
708 				     drm_output_get_plane_type_name(plane));
709 			continue;
710 		}
711 
712 		if (!drm_output_plane_view_has_valid_format(plane, state, ev, fb)) {
713 			drm_debug(b, "\t\t\t\t[plane] not adding plane %d to "
714 				     "candidate list: invalid pixel format\n",
715 				     plane->plane_id);
716 			continue;
717 		}
718 
719 		drm_output_add_zpos_plane(plane, &zpos_candidate_list);
720 	}
721 
722 	/* go over the potential candidate list and try to find a possible
723 	 * plane suitable for \c ev; start with the highest zpos value of a
724 	 * plane to maximize our chances, but do note we pass the zpos value
725 	 * based on current tracked value by \c current_lowest_zpos_in_use */
726 	while (!wl_list_empty(&zpos_candidate_list)) {
727 		struct drm_plane_zpos *head_p_zpos =
728 			wl_container_of(zpos_candidate_list.next,
729 					head_p_zpos, link);
730 		struct drm_plane *plane = head_p_zpos->plane;
731 		const char *p_name = drm_output_get_plane_type_name(plane);
732 		uint64_t zpos;
733 
734 		if (current_lowest_zpos == DRM_PLANE_ZPOS_INVALID_PLANE)
735 			zpos = plane->zpos_max;
736 		else
737 			zpos = MIN(current_lowest_zpos - 1, plane->zpos_max);
738 
739 		drm_debug(b, "\t\t\t\t[plane] plane %d picked "
740 			     "from candidate list, type: %s\n",
741 			     plane->plane_id, p_name);
742 
743 		ps = drm_output_try_view_on_plane(plane, state, ev,
744 						  mode, fb, zpos);
745 		drm_output_destroy_zpos_plane(head_p_zpos);
746 		if (ps) {
747 			drm_debug(b, "\t\t\t\t[view] view %p has been placed to "
748 				     "%s plane with computed zpos %"PRIu64"\n",
749 				     ev, p_name, zpos);
750 			break;
751 		}
752 	}
753 
754 	wl_list_for_each_safe(p_zpos, p_zpos_next, &zpos_candidate_list, link)
755 		drm_output_destroy_zpos_plane(p_zpos);
756 
757 	drm_fb_unref(fb);
758 	return ps;
759 }
760 
761 static struct drm_output_state *
drm_output_propose_state(struct weston_output * output_base,struct drm_pending_state * pending_state,enum drm_output_propose_state_mode mode)762 drm_output_propose_state(struct weston_output *output_base,
763 			 struct drm_pending_state *pending_state,
764 			 enum drm_output_propose_state_mode mode)
765 {
766 	struct drm_output *output = to_drm_output(output_base);
767 	struct drm_backend *b = to_drm_backend(output->base.compositor);
768 	struct drm_output_state *state;
769 	struct drm_plane_state *scanout_state = NULL;
770 	struct weston_view *ev;
771 
772 	pixman_region32_t surface_overlap, renderer_region, planes_region;
773 	pixman_region32_t occluded_region;
774 
775 	bool renderer_ok = (mode != DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY);
776 	int ret;
777 	uint64_t current_lowest_zpos = DRM_PLANE_ZPOS_INVALID_PLANE;
778 
779 	assert(!output->state_last);
780 	state = drm_output_state_duplicate(output->state_cur,
781 					   pending_state,
782 					   DRM_OUTPUT_STATE_CLEAR_PLANES);
783 
784 	/* We implement mixed mode by progressively creating and testing
785 	 * incremental states, of scanout + overlay + cursor. Since we
786 	 * walk our views top to bottom, the scanout plane is last, however
787 	 * we always need it in our scene for the test modeset to be
788 	 * meaningful. To do this, we steal a reference to the last
789 	 * renderer framebuffer we have, if we think it's basically
790 	 * compatible. If we don't have that, then we conservatively fall
791 	 * back to only using the renderer for this repaint. */
792 	if (mode == DRM_OUTPUT_PROPOSE_STATE_MIXED) {
793 		struct drm_plane *plane = output->scanout_plane;
794 		struct drm_fb *scanout_fb = plane->state_cur->fb;
795 
796 		if (!scanout_fb ||
797 		    (scanout_fb->type != BUFFER_GBM_SURFACE &&
798 		     scanout_fb->type != BUFFER_PIXMAN_DUMB)) {
799 			drm_debug(b, "\t\t[state] cannot propose mixed mode: "
800 			             "for output %s (%lu): no previous renderer "
801 			             "fb\n",
802 				  output->base.name,
803 				  (unsigned long) output->base.id);
804 			drm_output_state_free(state);
805 			return NULL;
806 		}
807 
808 		if (scanout_fb->width != output_base->current_mode->width ||
809 		    scanout_fb->height != output_base->current_mode->height) {
810 			drm_debug(b, "\t\t[state] cannot propose mixed mode "
811 			             "for output %s (%lu): previous fb has "
812 				     "different size\n",
813 				  output->base.name,
814 				  (unsigned long) output->base.id);
815 			drm_output_state_free(state);
816 			return NULL;
817 		}
818 
819 		scanout_state = drm_plane_state_duplicate(state,
820 							  plane->state_cur);
821 		/* assign the primary primary the lowest zpos value */
822 		scanout_state->zpos = plane->zpos_min;
823 		drm_debug(b, "\t\t[state] using renderer FB ID %lu for mixed "
824 			     "mode for output %s (%lu)\n",
825 			  (unsigned long) scanout_fb->fb_id, output->base.name,
826 			  (unsigned long) output->base.id);
827 		drm_debug(b, "\t\t[state] scanout will use for zpos %"PRIu64"\n",
828 				scanout_state->zpos);
829 	}
830 
831 	/* - renderer_region contains the total region which which will be
832 	 *   covered by the renderer
833 	 * - planes_region contains the total region which has been covered by
834 	 *   hardware planes
835 	 * - occluded_region contains the total region which which will be
836 	 *   covered by the renderer and hardware planes, where the view's
837 	 *   visible-and-opaque region is added in both cases (the view's
838 	 *   opaque region  accumulates there for each view); it is being used
839 	 *   to skip the view, if it is completely occluded; includes the
840 	 *   situation where occluded_region covers entire output's region.
841 	 */
842 	pixman_region32_init(&renderer_region);
843 	pixman_region32_init(&planes_region);
844 	pixman_region32_init(&occluded_region);
845 
846 	wl_list_for_each(ev, &output_base->compositor->view_list, link) {
847 		struct drm_plane_state *ps = NULL;
848 		bool force_renderer = false;
849 		pixman_region32_t clipped_view;
850 		bool totally_occluded = false;
851 
852 		drm_debug(b, "\t\t\t[view] evaluating view %p for "
853 		             "output %s (%lu)\n",
854 		          ev, output->base.name,
855 			  (unsigned long) output->base.id);
856 
857 		/* If this view doesn't touch our output at all, there's no
858 		 * reason to do anything with it. */
859 		if (!(ev->output_mask & (1u << output->base.id))) {
860 			drm_debug(b, "\t\t\t\t[view] ignoring view %p "
861 			             "(not on our output)\n", ev);
862 			continue;
863 		}
864 
865 		/* Ignore views we know to be totally occluded. */
866 		pixman_region32_init(&clipped_view);
867 		pixman_region32_intersect(&clipped_view,
868 					  &ev->transform.boundingbox,
869 					  &output->base.region);
870 
871 		pixman_region32_init(&surface_overlap);
872 		pixman_region32_subtract(&surface_overlap, &clipped_view,
873 					 &occluded_region);
874 		/* if the view is completely occluded then ignore that
875 		 * view; includes the case where occluded_region covers
876 		 * the entire output */
877 		totally_occluded = !pixman_region32_not_empty(&surface_overlap);
878 		if (totally_occluded) {
879 			drm_debug(b, "\t\t\t\t[view] ignoring view %p "
880 			             "(occluded on our output)\n", ev);
881 			pixman_region32_fini(&surface_overlap);
882 			pixman_region32_fini(&clipped_view);
883 			continue;
884 		}
885 
886 		/* We only assign planes to views which are exclusively present
887 		 * on our output. */
888 		if (ev->output_mask != (1u << output->base.id)) {
889 			drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
890 			             "(on multiple outputs)\n", ev);
891 			force_renderer = true;
892 		}
893 
894 		if (!weston_view_has_valid_buffer(ev)) {
895 			drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
896 			             "(no buffer available)\n", ev);
897 			force_renderer = true;
898 		}
899 
900 		/* Since we process views from top to bottom, we know that if
901 		 * the view intersects the calculated renderer region, it must
902 		 * be part of, or occluded by, it, and cannot go on a plane. */
903 		pixman_region32_intersect(&surface_overlap, &renderer_region,
904 					  &clipped_view);
905 		if (pixman_region32_not_empty(&surface_overlap)) {
906 			drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
907 			             "(occluded by renderer views)\n", ev);
908 			force_renderer = true;
909 		}
910 		pixman_region32_fini(&surface_overlap);
911 
912 		/* In case of enforced mode of content-protection do not
913 		 * assign planes for a protected surface on an unsecured output.
914 		 */
915 		if (ev->surface->protection_mode == WESTON_SURFACE_PROTECTION_MODE_ENFORCED &&
916 		    ev->surface->desired_protection > output_base->current_protection) {
917 			drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
918 				     "(enforced protection mode on unsecured output)\n", ev);
919 			force_renderer = true;
920 		}
921 
922 		if (!force_renderer) {
923 			drm_debug(b, "\t\t\t[plane] started with zpos %"PRIu64"\n",
924 				      current_lowest_zpos);
925 			ps = drm_output_prepare_plane_view(state, ev, mode,
926 							   scanout_state,
927 							   current_lowest_zpos);
928 		}
929 
930 		if (ps) {
931 			current_lowest_zpos = ps->zpos;
932 			drm_debug(b, "\t\t\t[plane] next zpos to use %"PRIu64"\n",
933 				      current_lowest_zpos);
934 
935 			/* If we have been assigned to an overlay or scanout
936 			 * plane, add this area to the occluded region, so
937 			 * other views are known to be behind it. The cursor
938 			 * plane, however, is special, in that it blends with
939 			 * the content underneath it: the area should neither
940 			 * be added to the renderer region nor the occluded
941 			 * region. */
942 			if (ps->plane->type != WDRM_PLANE_TYPE_CURSOR) {
943 				pixman_region32_union(&planes_region,
944 						      &planes_region,
945 						      &clipped_view);
946 
947 				if (!weston_view_is_opaque(ev, &clipped_view))
948 					pixman_region32_intersect(&clipped_view,
949 								  &clipped_view,
950 								  &ev->transform.opaque);
951 				/* the visible-and-opaque region of this view
952 				 * will occlude views underneath it */
953 				pixman_region32_union(&occluded_region,
954 						      &occluded_region,
955 						      &clipped_view);
956 
957 				pixman_region32_fini(&clipped_view);
958 
959 			}
960 			continue;
961 		}
962 
963 		/* We have been assigned to the primary (renderer) plane:
964 		 * check if this is OK, and add ourselves to the renderer
965 		 * region if so. */
966 		if (!renderer_ok) {
967 			drm_debug(b, "\t\t[view] failing state generation: "
968 				      "placing view %p to renderer not allowed\n",
969 				  ev);
970 			pixman_region32_fini(&clipped_view);
971 			goto err_region;
972 		}
973 
974 		pixman_region32_union(&renderer_region,
975 				      &renderer_region,
976 				      &clipped_view);
977 
978 		if (!weston_view_is_opaque(ev, &clipped_view))
979 			pixman_region32_intersect(&clipped_view,
980 						  &clipped_view,
981 						  &ev->transform.opaque);
982 
983 		pixman_region32_union(&occluded_region,
984 				      &occluded_region,
985 				      &clipped_view);
986 
987 		pixman_region32_fini(&clipped_view);
988 
989 		drm_debug(b, "\t\t\t\t[view] view %p will be placed "
990 			     "on the renderer\n", ev);
991 	}
992 
993 	pixman_region32_fini(&renderer_region);
994 	pixman_region32_fini(&planes_region);
995 	pixman_region32_fini(&occluded_region);
996 
997 	/* In renderer-only mode, we can't test the state as we don't have a
998 	 * renderer buffer yet. */
999 	if (mode == DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY)
1000 		return state;
1001 
1002 	/* check if we have invalid zpos values, like duplicate(s) */
1003 	drm_output_check_zpos_plane_states(state);
1004 
1005 	/* Check to see if this state will actually work. */
1006 	ret = drm_pending_state_test(state->pending_state);
1007 	if (ret != 0) {
1008 		drm_debug(b, "\t\t[view] failing state generation: "
1009 			     "atomic test not OK\n");
1010 		goto err;
1011 	}
1012 
1013 	/* Counterpart to duplicating scanout state at the top of this
1014 	 * function: if we have taken a renderer framebuffer and placed it in
1015 	 * the pending state in order to incrementally test overlay planes,
1016 	 * remove it now. */
1017 	if (mode == DRM_OUTPUT_PROPOSE_STATE_MIXED) {
1018 		assert(scanout_state->fb->type == BUFFER_GBM_SURFACE ||
1019 		       scanout_state->fb->type == BUFFER_PIXMAN_DUMB);
1020 		drm_plane_state_put_back(scanout_state);
1021 	}
1022 	return state;
1023 
1024 err_region:
1025 	pixman_region32_fini(&renderer_region);
1026 	pixman_region32_fini(&occluded_region);
1027 err:
1028 	drm_output_state_free(state);
1029 	return NULL;
1030 }
1031 
1032 void
drm_assign_planes(struct weston_output * output_base,void * repaint_data)1033 drm_assign_planes(struct weston_output *output_base, void *repaint_data)
1034 {
1035 	struct drm_backend *b = to_drm_backend(output_base->compositor);
1036 	struct drm_pending_state *pending_state = repaint_data;
1037 	struct drm_output *output = to_drm_output(output_base);
1038 	struct drm_output_state *state = NULL;
1039 	struct drm_plane_state *plane_state;
1040 	struct weston_view *ev;
1041 	struct weston_plane *primary = &output_base->compositor->primary_plane;
1042 	enum drm_output_propose_state_mode mode = DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY;
1043 
1044 	drm_debug(b, "\t[repaint] preparing state for output %s (%lu)\n",
1045 		  output_base->name, (unsigned long) output_base->id);
1046 
1047 	if (!b->sprites_are_broken && !output->virtual) {
1048 		drm_debug(b, "\t[repaint] trying planes-only build state\n");
1049 		state = drm_output_propose_state(output_base, pending_state, mode);
1050 		if (!state) {
1051 			drm_debug(b, "\t[repaint] could not build planes-only "
1052 				     "state, trying mixed\n");
1053 			mode = DRM_OUTPUT_PROPOSE_STATE_MIXED;
1054 			state = drm_output_propose_state(output_base,
1055 							 pending_state,
1056 							 mode);
1057 		}
1058 		if (!state) {
1059 			drm_debug(b, "\t[repaint] could not build mixed-mode "
1060 				     "state, trying renderer-only\n");
1061 		}
1062 	} else {
1063 		drm_debug(b, "\t[state] no overlay plane support\n");
1064 	}
1065 
1066 	if (!state) {
1067 		mode = DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY;
1068 		state = drm_output_propose_state(output_base, pending_state,
1069 						 mode);
1070 	}
1071 
1072 	assert(state);
1073 	drm_debug(b, "\t[repaint] Using %s composition\n",
1074 		  drm_propose_state_mode_to_string(mode));
1075 
1076 	wl_list_for_each(ev, &output_base->compositor->view_list, link) {
1077 		struct drm_plane *target_plane = NULL;
1078 
1079 		/* If this view doesn't touch our output at all, there's no
1080 		 * reason to do anything with it. */
1081 		if (!(ev->output_mask & (1u << output->base.id)))
1082 			continue;
1083 
1084 		/* Test whether this buffer can ever go into a plane:
1085 		 * non-shm, or small enough to be a cursor.
1086 		 *
1087 		 * Also, keep a reference when using the pixman renderer.
1088 		 * That makes it possible to do a seamless switch to the GL
1089 		 * renderer and since the pixman renderer keeps a reference
1090 		 * to the buffer anyway, there is no side effects.
1091 		 */
1092 		if (b->use_pixman ||
1093 		    (weston_view_has_valid_buffer(ev) &&
1094 		    (!wl_shm_buffer_get(ev->surface->buffer_ref.buffer->resource) ||
1095 		     (ev->surface->width <= b->cursor_width &&
1096 		      ev->surface->height <= b->cursor_height))))
1097 			ev->surface->keep_buffer = true;
1098 		else
1099 			ev->surface->keep_buffer = false;
1100 
1101 		/* This is a bit unpleasant, but lacking a temporary place to
1102 		 * hang a plane off the view, we have to do a nested walk.
1103 		 * Our first-order iteration has to be planes rather than
1104 		 * views, because otherwise we won't reset views which were
1105 		 * previously on planes to being on the primary plane. */
1106 		wl_list_for_each(plane_state, &state->plane_list, link) {
1107 			if (plane_state->ev == ev) {
1108 				plane_state->ev = NULL;
1109 				target_plane = plane_state->plane;
1110 				break;
1111 			}
1112 		}
1113 
1114 		if (target_plane) {
1115 			drm_debug(b, "\t[repaint] view %p on %s plane %lu\n",
1116 				  ev, plane_type_enums[target_plane->type].name,
1117 				  (unsigned long) target_plane->plane_id);
1118 			weston_view_move_to_plane(ev, &target_plane->base);
1119 		} else {
1120 			drm_debug(b, "\t[repaint] view %p using renderer "
1121 				     "composition\n", ev);
1122 			weston_view_move_to_plane(ev, primary);
1123 		}
1124 
1125 		if (!target_plane ||
1126 		    target_plane->type == WDRM_PLANE_TYPE_CURSOR) {
1127 			/* cursor plane & renderer involve a copy */
1128 			ev->psf_flags = 0;
1129 		} else {
1130 			/* All other planes are a direct scanout of a
1131 			 * single client buffer.
1132 			 */
1133 			ev->psf_flags = WP_PRESENTATION_FEEDBACK_KIND_ZERO_COPY;
1134 		}
1135 	}
1136 
1137 	/* We rely on output->cursor_view being both an accurate reflection of
1138 	 * the cursor plane's state, but also being maintained across repaints
1139 	 * to avoid unnecessary damage uploads, per the comment in
1140 	 * drm_output_prepare_cursor_view. In the event that we go from having
1141 	 * a cursor view to not having a cursor view, we need to clear it. */
1142 	if (output->cursor_view) {
1143 		plane_state =
1144 			drm_output_state_get_existing_plane(state,
1145 							    output->cursor_plane);
1146 		if (!plane_state || !plane_state->fb)
1147 			output->cursor_view = NULL;
1148 	}
1149 }
1150