xref: /OK3568_Linux_fs/buildroot/package/weston/0015-backend-drm-Support-selecting-monitors.patch (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1From 14a416a3c5959e53d0de2e8cc7b8221a3544be5a Mon Sep 17 00:00:00 2001
2From: Jeffy Chen <jeffy.chen@rock-chips.com>
3Date: Tue, 23 Jun 2020 10:05:48 +0800
4Subject: [PATCH 15/93] backend-drm: Support selecting monitors
5
6Using these environments:
7
8WESTON_DRM_PRIMARY:
9  Specify primary connector.
10WESTON_DRM_SINGLE_HEAD:
11  Force using single connector.
12WESTON_DRM_HEAD_FALLBACK:
13  Fallback to any available connector if none matched.
14WESTON_DRM_HEAD_FALLBACK_ALL:
15  Fallback to all available connector if none matched.
16WESTON_DRM_PREFER_EXTERNAL_DUAL:
17  Prefer external connectors, and also enable internal ones.
18WESTON_DRM_PREFER_EXTERNAL:
19  Prefer external connectors, and disable internal ones if any matched.
20
21WESTON_DRM_HEAD_MODE:
22  default(match primary->internal->external)
23  primary(match primary only)
24  internal(match primary->internal)
25  external(match primary->external)
26  external-dual(match primary->external->internal)
27
28Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
29---
30 compositor/main.c                    |  22 +-
31 desktop-shell/shell.c                |  26 +-
32 include/libweston/libweston.h        |   2 +
33 libweston/backend-drm/drm-internal.h |  24 ++
34 libweston/backend-drm/drm.c          | 378 ++++++++++++++++++++++++---
35 libweston/backend-drm/kms.c          |  42 ++-
36 libweston/compositor.c               |  42 ++-
37 7 files changed, 477 insertions(+), 59 deletions(-)
38
39diff --git a/compositor/main.c b/compositor/main.c
40index 57a52b6..dcfe85b 100644
41--- a/compositor/main.c
42+++ b/compositor/main.c
43@@ -2326,7 +2326,7 @@ drm_head_prepare_enable(struct wet_compositor *wet,
44 	char *output_name = NULL;
45 	char *mode = NULL;
46
47-	section = drm_config_find_controlling_output_section(wet->config, name);
48+	section = head->section;
49 	if (section) {
50 		/* skip outputs that are explicitly off, or non-desktop and not
51 		 * explicitly enabled. The backend turns them off automatically.
52@@ -2356,11 +2356,10 @@ static bool
53 drm_head_should_force_enable(struct wet_compositor *wet,
54 			     struct weston_head *head)
55 {
56-	const char *name = weston_head_get_name(head);
57 	struct weston_config_section *section;
58 	bool force;
59
60-	section = drm_config_find_controlling_output_section(wet->config, name);
61+	section = head->section;
62 	if (!section)
63 		return false;
64
65@@ -2551,6 +2550,21 @@ drm_head_disable(struct weston_head *head)
66 		wet_output_destroy(output);
67 }
68
69+static bool
70+drm_head_update_output_section(struct weston_head *head)
71+{
72+	struct weston_compositor *compositor = head->compositor;
73+	struct wet_compositor *wet = to_wet_compositor(compositor);
74+	const char *name = weston_head_get_name(head);
75+
76+	if (head->section)
77+		return true;
78+
79+	head->section =
80+		drm_config_find_controlling_output_section(wet->config, name);
81+	return !!head->section;
82+}
83+
84 static void
85 drm_heads_changed(struct wl_listener *listener, void *arg)
86 {
87@@ -2566,6 +2580,8 @@ drm_heads_changed(struct wl_listener *listener, void *arg)
88 	 * output.
89 	 */
90 	while ((head = weston_compositor_iterate_heads(compositor, head))) {
91+		drm_head_update_output_section(head);
92+
93 		connected = weston_head_is_connected(head);
94 		enabled = weston_head_is_enabled(head);
95 		changed = weston_head_is_device_changed(head);
96diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c
97index 79e20ad..89ea491 100644
98--- a/desktop-shell/shell.c
99+++ b/desktop-shell/shell.c
100@@ -3550,6 +3550,9 @@ shell_fade_done_for_output(struct weston_view_animation *animation, void *data)
101 	struct shell_output *shell_output = data;
102 	struct desktop_shell *shell = shell_output->shell;
103
104+	if (!shell_output->fade.curtain)
105+		return;
106+
107 	shell_output->fade.animation = NULL;
108 	switch (shell_output->fade.type) {
109 	case FADE_IN:
110@@ -4334,8 +4337,12 @@ shell_output_destroy(struct shell_output *shell_output)
111 		shell_output->fade.animation = NULL;
112 	}
113
114-	if (shell_output->fade.curtain)
115-		weston_curtain_destroy(shell_output->fade.curtain);
116+	if (shell_output->fade.curtain) {
117+		struct weston_curtain *curtain = shell_output->fade.curtain;
118+
119+		shell_output->fade.curtain = NULL;
120+		weston_curtain_destroy(curtain);
121+	}
122
123 	if (shell_output->fade.startup_timer)
124 		wl_event_source_remove(shell_output->fade.startup_timer);
125@@ -4438,12 +4445,25 @@ handle_output_move_layer(struct desktop_shell *shell,
126 static void
127 handle_output_move(struct wl_listener *listener, void *data)
128 {
129+	struct weston_output *output = data;
130+	struct weston_compositor *compositor = output->compositor;
131 	struct desktop_shell *shell;
132
133 	shell = container_of(listener, struct desktop_shell,
134 			     output_move_listener);
135
136-	shell_for_each_layer(shell, handle_output_move_layer, data);
137+	if (shell->lock_surface)
138+		shell->lock_surface->committed(shell->lock_surface, 0, 0);
139+
140+	/* Only move normal layers for non-default output */
141+	if (output != get_default_output(compositor)) {
142+		shell_for_each_layer(shell, handle_output_move_layer, data);
143+		return;
144+	}
145+
146+	handle_output_move_layer(shell, &shell->lock_layer, data);
147+	handle_output_move_layer(shell, &shell->background_layer, data);
148+	handle_output_move_layer(shell, &shell->panel_layer, data);
149 }
150
151 static void
152diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h
153index 984d5f3..d35963f 100644
154--- a/include/libweston/libweston.h
155+++ b/include/libweston/libweston.h
156@@ -399,6 +399,8 @@ struct weston_head {
157
158 	/** Opaque pointer used by backends to identify heads as theirs */
159 	const void *backend_id;
160+
161+	struct weston_config_section *section; /**< config section **/
162 };
163
164 /** Output properties derived from its color characteristics and profile
165diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h
166index f4b8ed8..ec8bf0e 100644
167--- a/libweston/backend-drm/drm-internal.h
168+++ b/libweston/backend-drm/drm-internal.h
169@@ -110,6 +110,10 @@
170
171 #define MAX_CLONED_CONNECTORS 4
172
173+/* Min duration between drm outputs update requests, to avoid glith */
174+#define DRM_MIN_UPDATE_MS	1000
175+
176+#define DRM_RESIZE_FREEZE_MS    600
177
178 /**
179  * Represents the values of an enum-type KMS property
180@@ -268,6 +272,7 @@ struct drm_device {
181 		int fd;
182 		char *filename;
183 		dev_t devnum;
184+		char *syspath;
185 	} drm;
186
187 	/* drm_crtc::link */
188@@ -302,6 +307,10 @@ struct drm_device {
189 	int min_height, max_height;
190 };
191
192+struct drm_head;
193+struct drm_backend;
194+typedef bool (*drm_head_match_t) (struct drm_backend *, struct drm_head *);
195+
196 struct drm_backend {
197 	struct weston_backend base;
198 	struct weston_compositor *compositor;
199@@ -327,6 +336,19 @@ struct drm_backend {
200 	bool shutting_down;
201
202 	struct weston_log_scope *debug;
203+
204+	struct wl_event_source *hotplug_timer;
205+	bool pending_update;
206+	int64_t last_update_ms;
207+	int64_t last_resize_ms;
208+	int64_t resize_freeze_ms;
209+
210+	bool single_head;
211+	bool head_fallback;
212+	bool head_fallback_all;
213+	drm_head_match_t *head_matches;
214+	struct drm_head *primary_head;
215+	struct wl_listener output_create_listener;
216 };
217
218 struct drm_mode {
219@@ -589,6 +611,8 @@ struct drm_output {
220 	void (*virtual_destroy)(struct weston_output *base);
221
222 	submit_frame_cb virtual_submit_frame;
223+
224+	bool state_invalid;
225 };
226
227 void
228diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c
229index 711ed5b..f5ac1b6 100644
230--- a/libweston/backend-drm/drm.c
231+++ b/libweston/backend-drm/drm.c
232@@ -30,6 +30,7 @@
233 #include "config.h"
234
235 #include <errno.h>
236+#include <stdio.h>
237 #include <stdint.h>
238 #include <stdlib.h>
239 #include <ctype.h>
240@@ -47,6 +48,7 @@
241
242 #include <libudev.h>
243
244+#include <libweston/config-parser.h>
245 #include <libweston/libweston.h>
246 #include <libweston/backend-drm.h>
247 #include <libweston/weston-log.h>
248@@ -68,6 +70,43 @@
249
250 static const char default_seat[] = "seat0";
251
252+static inline bool
253+drm_head_is_external(struct drm_head *head)
254+{
255+	drmModeConnector *conn = head->connector.conn;
256+	switch (conn->connector_type) {
257+		case DRM_MODE_CONNECTOR_LVDS:
258+		case DRM_MODE_CONNECTOR_eDP:
259+#ifdef DRM_MODE_CONNECTOR_DSI
260+		case DRM_MODE_CONNECTOR_DSI:
261+#endif
262+			return false;
263+		default:
264+			return true;
265+	}
266+};
267+
268+static void
269+drm_backend_update_outputs(struct drm_backend *b)
270+{
271+	struct weston_output *primary;
272+
273+	if (!b->primary_head)
274+		return;
275+
276+	primary = b->primary_head->base.output;
277+	if (!primary)
278+		return;
279+
280+	wl_list_remove(&primary->link);
281+	wl_list_insert(&b->compositor->output_list, &primary->link);
282+
283+	/* Reflow outputs */
284+	weston_compositor_reflow_outputs(b->compositor);
285+
286+	weston_compositor_damage_all(b->compositor);
287+}
288+
289 static void
290 drm_backend_create_faked_zpos(struct drm_backend *b)
291 {
292@@ -464,18 +503,36 @@ drm_output_repaint(struct weston_output *output_base, pixman_region32_t *damage)
293 	struct drm_plane_state *scanout_state;
294 	struct drm_pending_state *pending_state;
295 	struct drm_device *device;
296+	struct drm_backend *b;
297+	struct timespec now;
298+	int64_t now_ms;
299
300 	assert(output);
301 	assert(!output->virtual);
302
303 	device = output->device;
304 	pending_state = device->repaint_data;
305+	b = device->backend;
306
307 	if (output->disable_pending || output->destroy_pending)
308 		goto err;
309
310 	assert(!output->state_last);
311
312+	weston_compositor_read_presentation_clock(b->compositor, &now);
313+	now_ms = timespec_to_msec(&now);
314+	if (now_ms < b->last_resize_ms + DRM_RESIZE_FREEZE_MS) {
315+		/* Resize fullscreen/maxmium views(not always success) */
316+		if (now_ms < b->last_resize_ms + DRM_RESIZE_FREEZE_MS)
317+			wl_signal_emit(&b->compositor->output_resized_signal,
318+				       output);
319+
320+		weston_output_damage(output_base);
321+		weston_output_finish_frame(output_base, NULL,
322+					   WP_PRESENTATION_FEEDBACK_INVALID);
323+		return 0;
324+	}
325+
326 	/* If planes have been disabled in the core, we might not have
327 	 * hit assign_planes at all, so might not have valid output state
328 	 * here. */
329@@ -504,7 +561,7 @@ drm_output_repaint(struct weston_output *output_base, pixman_region32_t *damage)
330
331 err:
332 	drm_output_state_free(state);
333-	return -1;
334+	return 0;
335 }
336
337 /* Determine the type of vblank synchronization to use for the output.
338@@ -743,6 +800,7 @@ drm_output_apply_mode(struct drm_output *output)
339 	 *      content.
340 	 */
341 	device->state_invalid = true;
342+	output->state_invalid = true;
343
344 	if (b->use_pixman) {
345 		drm_output_fini_pixman(output);
346@@ -1322,6 +1380,7 @@ drm_output_attach_head(struct weston_output *output_base,
347 {
348 	struct drm_backend *b = to_drm_backend(output_base->compositor);
349 	struct drm_device *device = b->drm;
350+	struct drm_output *output = to_drm_output(output_base);
351
352 	if (wl_list_length(&output_base->head_list) >= MAX_CLONED_CONNECTORS)
353 		return -1;
354@@ -1339,6 +1398,7 @@ drm_output_attach_head(struct weston_output *output_base,
355 	 * will not clear the flag before this output is updated?
356 	 */
357 	device->state_invalid = true;
358+	output->state_invalid = true;
359
360 	weston_output_schedule_repaint(output_base);
361
362@@ -1351,6 +1411,7 @@ drm_output_detach_head(struct weston_output *output_base,
363 {
364 	struct drm_backend *b = to_drm_backend(output_base->compositor);
365 	struct drm_device *device = b->drm;
366+	struct drm_output *output = to_drm_output(output_base);
367
368 	if (!output_base->enabled)
369 		return;
370@@ -1359,6 +1420,7 @@ drm_output_detach_head(struct weston_output *output_base,
371 	 * be driven. */
372 	/* XXX: Ideally we'd do this per-output, not globally. */
373 	device->state_invalid = true;
374+	output->state_invalid = true;
375
376 	weston_output_schedule_repaint(output_base);
377 }
378@@ -1917,6 +1979,8 @@ drm_output_enable(struct weston_output *base)
379 	output->base.switch_mode = drm_output_switch_mode;
380 	output->base.set_gamma = drm_output_set_gamma;
381
382+	output->state_invalid = true;
383+
384 	weston_log("Output %s (crtc %d) video modes:\n",
385 		   output->base.name, output->crtc->crtc_id);
386 	drm_output_print_modes(output);
387@@ -2278,8 +2342,7 @@ drm_head_create(struct drm_device *device, drmModeConnector *conn,
388
389 	head->backlight = backlight_init(drm_device, conn->connector_type);
390
391-	if (conn->connector_type == DRM_MODE_CONNECTOR_LVDS ||
392-	    conn->connector_type == DRM_MODE_CONNECTOR_eDP)
393+	if (!drm_head_is_external(head))
394 		weston_head_set_internal(&head->base);
395
396 	if (drm_head_read_current_setup(head, device) < 0) {
397@@ -2447,56 +2510,54 @@ drm_backend_add_connector(struct drm_device *device, drmModeConnector *conn,
398 	return ret;
399 }
400
401-/** Find all connectors of the fd and create drm_head or drm_writeback objects
402- * (depending on the type of connector they are) for each of them
403- *
404- * These objects are added to the DRM-backend lists of heads and writebacks.
405- *
406- * @param device The DRM device structure
407- * @param drm_device udev device pointer
408- * @param resources The DRM resources, it is taken with drmModeGetResources
409- * @return 0 on success, -1 on failure
410- */
411-static int
412-drm_backend_discover_connectors(struct drm_device *device,
413-				struct udev_device *drm_device,
414-				drmModeRes *resources)
415+static bool
416+resources_has_connector(drmModeRes *resources, uint32_t connector_id)
417 {
418-	drmModeConnector *conn;
419-	int i, ret;
420+	for (int i = 0; i < resources->count_connectors; i++) {
421+		if (resources->connectors[i] == connector_id)
422+			return true;
423+	}
424
425-	device->min_width  = resources->min_width;
426-	device->max_width  = resources->max_width;
427-	device->min_height = resources->min_height;
428-	device->max_height = resources->max_height;
429+	return false;
430+}
431
432-	for (i = 0; i < resources->count_connectors; i++) {
433-		uint32_t connector_id = resources->connectors[i];
434+/* based on compositor/main.c#drm_head_prepare_enable() */
435+static bool
436+drm_head_is_available(struct weston_head *head)
437+{
438+	struct weston_config_section *section;
439+	char *mode = NULL;
440
441-		conn = drmModeGetConnector(device->drm.fd, connector_id);
442-		if (!conn)
443-			continue;
444+	section = head->section;
445+	if (!section)
446+		return true;
447
448-		ret = drm_backend_add_connector(device, conn, drm_device);
449-		if (ret < 0)
450-			drmModeFreeConnector(conn);
451+	/* skip outputs that are explicitly off, or non-desktop and not
452+	 * explicitly enabled.
453+	 */
454+	weston_config_section_get_string(section, "mode", &mode, NULL);
455+	if (mode && strcmp(mode, "off") == 0) {
456+		free(mode);
457+		return false;
458 	}
459
460-	return 0;
461+	if (!mode && weston_head_is_non_desktop(head))
462+		return false;
463+
464+	free(mode);
465+	return true;
466 }
467
468 static bool
469-resources_has_connector(drmModeRes *resources, uint32_t connector_id)
470+drm_head_match_fallback(struct drm_backend *b, struct drm_head *head)
471 {
472-	for (int i = 0; i < resources->count_connectors; i++) {
473-		if (resources->connectors[i] == connector_id)
474-			return true;
475-	}
476+	if (b->head_fallback_all)
477+		return true;
478
479-	return false;
480+	return b->head_fallback && !b->primary_head;
481 }
482
483-static void
484+static int
485 drm_backend_update_connectors(struct drm_device *device,
486 			      struct udev_device *drm_device)
487 {
488@@ -2504,17 +2565,24 @@ drm_backend_update_connectors(struct drm_device *device,
489 	drmModeRes *resources;
490 	drmModeConnector *conn;
491 	struct weston_head *base, *base_next;
492-	struct drm_head *head;
493+	struct drm_head *head, *old_primary_head;
494 	struct drm_writeback *writeback, *writeback_next;
495+	drm_head_match_t *match = b->head_matches;
496+	struct timespec now;
497 	uint32_t connector_id;
498 	int i, ret;
499
500 	resources = drmModeGetResources(device->drm.fd);
501 	if (!resources) {
502 		weston_log("drmModeGetResources failed\n");
503-		return;
504+		return -1;
505 	}
506
507+	device->min_width  = resources->min_width;
508+	device->max_width  = resources->max_width;
509+	device->min_height = resources->min_height;
510+	device->max_height = resources->max_height;
511+
512 	/* collect new connectors that have appeared, e.g. MST */
513 	for (i = 0; i < resources->count_connectors; i++) {
514 		connector_id = resources->connectors[i];
515@@ -2576,6 +2644,65 @@ drm_backend_update_connectors(struct drm_device *device,
516 	}
517
518 	drmModeFreeResources(resources);
519+
520+	old_primary_head = b->primary_head;
521+	b->primary_head = NULL;
522+
523+	wl_list_for_each_safe(base, base_next,
524+			      &b->compositor->head_list, compositor_link)
525+		weston_head_set_connection_status(base, false);
526+
527+	/* Re-connect matched heads and find primary head */
528+	while (*match) {
529+		wl_list_for_each_safe(base, base_next,
530+				      &b->compositor->head_list,
531+				      compositor_link) {
532+			drmModeConnector *conn;
533+
534+			if (!drm_head_is_available(base))
535+				continue;
536+
537+			head = to_drm_head(base);
538+			conn = head->connector.conn;
539+
540+			if (conn->connection != DRM_MODE_CONNECTED ||
541+			    !(*match)(b, head))
542+				continue;
543+
544+			weston_head_set_connection_status(base, true);
545+
546+			if (!b->primary_head) {
547+				b->primary_head = head;
548+
549+				/* Done the single-head match */
550+				if (b->single_head)
551+					goto match_done;
552+			}
553+		}
554+
555+		/* Done the fallback match */
556+		if (*match == drm_head_match_fallback)
557+			goto match_done;
558+
559+		match++;
560+
561+		/* Try the fallback match */
562+		if (!match && !b->primary_head)
563+			*match = drm_head_match_fallback;
564+	}
565+match_done:
566+
567+	drm_backend_update_outputs(b);
568+
569+	weston_compositor_read_presentation_clock(b->compositor, &now);
570+	b->last_update_ms = timespec_to_msec(&now);
571+
572+	/* Assume primary output's size changed */
573+	if (b->primary_head && old_primary_head &&
574+	    b->primary_head != old_primary_head)
575+		b->last_resize_ms = b->last_update_ms;
576+
577+	return 0;
578 }
579
580 static enum wdrm_connector_property
581@@ -2666,6 +2793,50 @@ udev_event_is_conn_prop_change(struct drm_backend *b,
582 	return 1;
583 }
584
585+static void
586+udev_hotplug_event(struct drm_device *device, struct udev_device *udev_device)
587+{
588+	struct drm_backend *b = device->backend;
589+	struct timespec now;
590+	int64_t now_ms, next_ms;
591+
592+	weston_compositor_read_presentation_clock(b->compositor, &now);
593+	now_ms = timespec_to_msec(&now);
594+
595+	/* Already have a pending request */
596+	if (b->pending_update)
597+		return;
598+
599+	next_ms = b->last_update_ms + DRM_MIN_UPDATE_MS;
600+	if (next_ms <= now_ms) {
601+		/* Long enough to trigger a new request */
602+		drm_backend_update_connectors(device, udev_device);
603+	} else {
604+		/* Too close to the last request, schedule a new one */
605+		b->pending_update = true;
606+		wl_event_source_timer_update(b->hotplug_timer,
607+					     next_ms - now_ms);
608+	}
609+}
610+
611+static int
612+hotplug_timer_handler(void *data)
613+{
614+	struct drm_device *device = data;
615+	struct drm_backend *b = device->backend;
616+	struct udev_device *udev_device;
617+	struct udev *udev;
618+
619+	udev = udev_monitor_get_udev(b->udev_monitor);
620+	udev_device = udev_device_new_from_syspath(udev, device->drm.syspath);
621+
622+	drm_backend_update_connectors(device, udev_device);
623+	b->pending_update = false;
624+
625+	udev_device_unref(udev_device);
626+	return 0;
627+}
628+
629 static int
630 udev_drm_event(int fd, uint32_t mask, void *data)
631 {
632@@ -2679,7 +2850,7 @@ udev_drm_event(int fd, uint32_t mask, void *data)
633 		if (udev_event_is_conn_prop_change(b, event, &conn_id, &prop_id))
634 			drm_backend_update_conn_props(b, conn_id, prop_id);
635 		else
636-			drm_backend_update_connectors(b->drm, event);
637+			udev_hotplug_event(b->drm, event);
638 	}
639
640 	udev_device_unref(event);
641@@ -2698,6 +2869,7 @@ drm_destroy(struct weston_compositor *ec)
642
643 	udev_input_destroy(&b->input);
644
645+	wl_event_source_remove(b->hotplug_timer);
646 	wl_event_source_remove(b->udev_drm_source);
647 	wl_event_source_remove(b->drm_source);
648
649@@ -2750,6 +2922,10 @@ session_notify(struct wl_listener *listener, void *data)
650 		weston_compositor_wake(compositor);
651 		weston_compositor_damage_all(compositor);
652 		device->state_invalid = true;
653+
654+		wl_list_for_each(output, &compositor->output_list, link)
655+			to_drm_output(output)->state_invalid = true;
656+
657 		udev_input_enable(&b->input);
658 	} else {
659 		weston_log("deactivating session\n");
660@@ -3106,6 +3282,14 @@ recorder_binding(struct weston_keyboard *keyboard, const struct timespec *time,
661 }
662 #endif
663
664+static void
665+output_create_notify(struct wl_listener *listener, void *data)
666+{
667+	struct drm_backend *b = container_of(listener, struct drm_backend,
668+					     output_create_listener);
669+
670+	drm_backend_update_outputs(b);
671+}
672
673 static const struct weston_drm_output_api api = {
674 	drm_output_set_mode,
675@@ -3114,6 +3298,63 @@ static const struct weston_drm_output_api api = {
676 	drm_output_set_max_bpc,
677 };
678
679+enum drm_head_mode {
680+	DRM_HEAD_MODE_DEFAULT,
681+	DRM_HEAD_MODE_PRIMARY,
682+	DRM_HEAD_MODE_INTERNAL,
683+	DRM_HEAD_MODE_EXTERNAL,
684+	DRM_HEAD_MODE_EXTERNAL_DUAL,
685+};
686+
687+static bool
688+drm_head_match_primary(struct drm_backend *b, struct drm_head *head)
689+{
690+	const char *buf = getenv("WESTON_DRM_PRIMARY");
691+	return buf && !strcmp(buf, head->base.name);
692+}
693+
694+static bool
695+drm_head_match_external(struct drm_backend *b, struct drm_head *head)
696+{
697+	return drm_head_is_external(head);
698+}
699+
700+static bool
701+drm_head_match_internal(struct drm_backend *b, struct drm_head *head)
702+{
703+	return !drm_head_is_external(head);
704+}
705+
706+#define DRM_HEAD_MAX_MATCHES 5
707+static drm_head_match_t drm_head_matches[][DRM_HEAD_MAX_MATCHES] = {
708+	[DRM_HEAD_MODE_DEFAULT] = {
709+		drm_head_match_primary,
710+		drm_head_match_internal,
711+		drm_head_match_external,
712+		NULL,
713+	},
714+	[DRM_HEAD_MODE_PRIMARY] = {
715+		drm_head_match_primary,
716+		NULL,
717+	},
718+	[DRM_HEAD_MODE_INTERNAL] = {
719+		drm_head_match_primary,
720+		drm_head_match_internal,
721+		NULL,
722+	},
723+	[DRM_HEAD_MODE_EXTERNAL] = {
724+		drm_head_match_primary,
725+		drm_head_match_external,
726+		NULL,
727+	},
728+	[DRM_HEAD_MODE_EXTERNAL_DUAL] = {
729+		drm_head_match_primary,
730+		drm_head_match_external,
731+		drm_head_match_internal,
732+		NULL,
733+	},
734+};
735+
736 static struct drm_backend *
737 drm_backend_create(struct weston_compositor *compositor,
738 		   struct weston_drm_backend_config *config)
739@@ -3125,7 +3366,9 @@ drm_backend_create(struct weston_compositor *compositor,
740 	const char *seat_id = default_seat;
741 	const char *session_seat;
742 	struct weston_drm_format_array *scanout_formats;
743+	enum drm_head_mode head_mode = DRM_HEAD_MODE_DEFAULT;
744 	drmModeRes *res;
745+	char *buf;
746 	int ret;
747
748 	session_seat = getenv("XDG_SEAT");
749@@ -3141,6 +3384,48 @@ drm_backend_create(struct weston_compositor *compositor,
750 	if (b == NULL)
751 		return NULL;
752
753+	buf = getenv("WESTON_DRM_SINGLE_HEAD");
754+	if (buf && buf[0] == '1')
755+		b->single_head = true;
756+
757+	buf = getenv("WESTON_DRM_HEAD_FALLBACK");
758+	if (buf && buf[0] == '1')
759+		b->head_fallback = true;
760+
761+	buf = getenv("WESTON_DRM_HEAD_FALLBACK_ALL");
762+	if (buf && buf[0] == '1')
763+		b->head_fallback_all = true;
764+
765+	buf = getenv("WESTON_DRM_PREFER_EXTERNAL");
766+	if (buf && buf[0] == '1') {
767+		head_mode = DRM_HEAD_MODE_EXTERNAL;
768+		b->head_fallback = true;
769+	}
770+
771+	buf = getenv("WESTON_DRM_PREFER_EXTERNAL_DUAL");
772+	if (buf && buf[0] == '1')
773+		head_mode = DRM_HEAD_MODE_EXTERNAL_DUAL;
774+
775+	buf = getenv("WESTON_DRM_HEAD_MODE");
776+	if (buf) {
777+		if (!strcmp(buf, "primary"))
778+			head_mode = DRM_HEAD_MODE_PRIMARY;
779+		else if (!strcmp(buf, "internal"))
780+			head_mode = DRM_HEAD_MODE_INTERNAL;
781+		else if (!strcmp(buf, "external"))
782+			head_mode = DRM_HEAD_MODE_EXTERNAL;
783+		else if (!strcmp(buf, "external-dual"))
784+			head_mode = DRM_HEAD_MODE_EXTERNAL_DUAL;
785+	}
786+
787+	b->head_matches = drm_head_matches[head_mode];
788+
789+	buf = getenv("WESTON_DRM_RESIZE_FREEZE_MS");
790+	if (buf)
791+		b->resize_freeze_ms = atoi(buf);
792+	else
793+		b->resize_freeze_ms = DRM_RESIZE_FREEZE_MS;
794+
795 	device = zalloc(sizeof *device);
796 	if (device == NULL)
797 		return NULL;
798@@ -3241,7 +3526,7 @@ drm_backend_create(struct weston_compositor *compositor,
799 	}
800
801 	wl_list_init(&b->drm->writeback_connector_list);
802-	if (drm_backend_discover_connectors(b->drm, drm_device, res) < 0) {
803+	if (drm_backend_update_connectors(b->drm, drm_device) < 0) {
804 		weston_log("Failed to create heads for %s\n", b->drm->drm.filename);
805 		goto err_udev_input;
806 	}
807@@ -3280,6 +3565,10 @@ drm_backend_create(struct weston_compositor *compositor,
808
809 	udev_device_unref(drm_device);
810
811+	b->output_create_listener.notify = output_create_notify;
812+	wl_signal_add(&b->compositor->output_created_signal,
813+		      &b->output_create_listener);
814+
815 	weston_compositor_add_debug_binding(compositor, KEY_O,
816 					    planes_binding, b);
817 	weston_compositor_add_debug_binding(compositor, KEY_C,
818@@ -3339,6 +3628,9 @@ drm_backend_create(struct weston_compositor *compositor,
819 		goto err_udev_monitor;
820 	}
821
822+	b->hotplug_timer =
823+		wl_event_loop_add_timer(loop, hotplug_timer_handler, b->drm);
824+
825 	return b;
826
827 err_udev_monitor:
828diff --git a/libweston/backend-drm/kms.c b/libweston/backend-drm/kms.c
829index 735196b..c7dc2f8 100644
830--- a/libweston/backend-drm/kms.c
831+++ b/libweston/backend-drm/kms.c
832@@ -683,6 +683,8 @@ drm_output_apply_state_legacy(struct drm_output_state *state)
833
834 	scanout_state =
835 		drm_output_state_get_existing_plane(state, scanout_plane);
836+	if (!scanout_state || !scanout_state->fb)
837+		return 0;
838
839 	/* The legacy SetCrtc API doesn't allow us to do scaling, and the
840 	 * legacy PageFlip API doesn't allow us to do clipping either. */
841@@ -700,7 +702,7 @@ drm_output_apply_state_legacy(struct drm_output_state *state)
842 	assert(scanout_state->in_fence_fd == -1);
843
844 	mode = to_drm_mode(output->base.current_mode);
845-	if (device->state_invalid ||
846+	if (output->state_invalid ||
847 	    !scanout_plane->state_cur->fb ||
848 	    scanout_plane->state_cur->fb->strides[0] !=
849 	    scanout_state->fb->strides[0]) {
850@@ -714,6 +716,8 @@ drm_output_apply_state_legacy(struct drm_output_state *state)
851 			weston_log("set mode failed: %s\n", strerror(errno));
852 			goto err;
853 		}
854+
855+		output->state_invalid = false;
856 	}
857
858 	pinfo = scanout_state->fb->format;
859@@ -964,6 +968,11 @@ drm_output_apply_state_atomic(struct drm_output_state *state,
860 		*flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
861 	}
862
863+	if (output->state_invalid) {
864+		drm_debug(b, "\t\t\t[atomic] output state invalid, modeset OK\n");
865+		*flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
866+	}
867+
868 	if (state->dpms == WESTON_DPMS_ON) {
869 		ret = drm_mode_ensure_blob(device, current_mode);
870 		if (ret != 0)
871@@ -1080,6 +1089,7 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
872 	struct drm_output_state *output_state, *tmp;
873 	struct drm_plane *plane;
874 	drmModeAtomicReq *req = drmModeAtomicAlloc();
875+	struct timespec now;
876 	uint32_t flags;
877 	int ret = 0;
878
879@@ -1214,14 +1224,34 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
880 		goto out;
881 	}
882
883+	weston_compositor_read_presentation_clock(b->compositor, &now);
884+
885 	wl_list_for_each_safe(output_state, tmp, &pending_state->output_list,
886-			      link)
887+			      link) {
888+		struct drm_output *output = output_state->output;
889+		struct drm_plane *scanout_plane = output->scanout_plane;
890+		struct drm_plane_state *scanout_state =
891+			drm_output_state_get_existing_plane(output_state,
892+							    scanout_plane);
893+
894+		/* Don't have a new state to apply */
895+		if (output_state->dpms == WESTON_DPMS_ON &&
896+		    (!scanout_state || !scanout_state->fb))
897+			continue;
898+
899 		drm_output_assign_state(output_state, mode);
900+		output->state_invalid = false;
901+
902+		/* Not gonna receive flip event when dpms off */
903+		if (output_state->dpms != WESTON_DPMS_ON)
904+			drm_output_update_complete(output,
905+						   WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION,
906+						   now.tv_sec,
907+						   now.tv_nsec / 1000);
908+	}
909
910 	device->state_invalid = false;
911
912-	assert(wl_list_empty(&pending_state->output_list));
913-
914 out:
915 	drmModeAtomicFree(req);
916 	drm_pending_state_free(pending_state);
917@@ -1321,8 +1351,6 @@ drm_pending_state_apply(struct drm_pending_state *pending_state)
918
919 	device->state_invalid = false;
920
921-	assert(wl_list_empty(&pending_state->output_list));
922-
923 	drm_pending_state_free(pending_state);
924
925 	return 0;
926@@ -1374,8 +1402,6 @@ drm_pending_state_apply_sync(struct drm_pending_state *pending_state)
927
928 	device->state_invalid = false;
929
930-	assert(wl_list_empty(&pending_state->output_list));
931-
932 	drm_pending_state_free(pending_state);
933
934 	return 0;
935diff --git a/libweston/compositor.c b/libweston/compositor.c
936index 9f8282b..2125f1a 100644
937--- a/libweston/compositor.c
938+++ b/libweston/compositor.c
939@@ -160,6 +160,25 @@ weston_paint_node_destroy(struct weston_paint_node *pnode)
940 	free(pnode);
941 }
942
943+static struct weston_layer *
944+get_view_layer(struct weston_view *view);
945+
946+static bool
947+weston_compositor_is_static_layer(struct weston_layer *layer)
948+{
949+	if (!layer)
950+		return false;
951+
952+	switch (layer->position) {
953+	case WESTON_LAYER_POSITION_BACKGROUND:
954+	case WESTON_LAYER_POSITION_UI:
955+	case WESTON_LAYER_POSITION_FADE:
956+		return true;
957+	default:
958+		return false;
959+	}
960+}
961+
962 /** Send wl_output events for mode and scale changes
963  *
964  * \param head Send on all resources bound to this head.
965@@ -1386,6 +1405,22 @@ weston_view_assign_output(struct weston_view *ev)
966 	uint32_t max, area, mask;
967 	pixman_box32_t *e;
968
969+	/* The static views should bind to the specific output */
970+	if (weston_compositor_is_static_layer(get_view_layer(ev))) {
971+		struct weston_view *view = ev;
972+
973+		while (view && !(output = view->output))
974+			view = view->geometry.parent;
975+
976+		if (output && !output->destroying)
977+			ev->output_mask = 1u << output->id;
978+		else
979+			weston_view_set_output(ev, NULL);
980+
981+		weston_surface_assign_output(ev->surface);
982+		return;
983+	}
984+
985 	new_output = NULL;
986 	max = 0;
987 	mask = 0;
988@@ -3301,6 +3336,7 @@ weston_output_repaint(struct weston_output *output)
989 	if (output->dirty)
990 		weston_output_update_matrix(output);
991
992+	output->repaint_needed = false;
993 	r = output->repaint(output, &output_damage);
994
995 	/* Clear painted primary damage */
996@@ -3309,8 +3345,9 @@ weston_output_repaint(struct weston_output *output)
997
998 	pixman_region32_fini(&output_damage);
999
1000-	output->repaint_needed = false;
1001-	if (r == 0)
1002+	if (output->repaint_needed)
1003+		output->repaint_status = REPAINT_SCHEDULED;
1004+	else if (r == 0)
1005 		output->repaint_status = REPAINT_AWAITING_COMPLETION;
1006
1007 	weston_compositor_repick(ec);
1008@@ -6911,6 +6948,7 @@ weston_compositor_remove_output(struct weston_output *output)
1009 	 * Use view_list in case the output did not go through repaint
1010 	 * after a view came on it, lacking a paint node. Just to be sure.
1011 	 */
1012+	weston_compositor_build_view_list(compositor, NULL);
1013 	wl_list_for_each(view, &compositor->view_list, link) {
1014 		if (view->output_mask & (1u << output->id))
1015 			weston_view_assign_output(view);
1016--
10172.20.1
1018
1019