1From 475bf406ef30aa6c04efc35caf2ff748a4a20df9 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 13/79] 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                    |  26 +-
31 desktop-shell/shell.c                |  24 +-
32 include/libweston/libweston.h        |   2 +
33 libweston/backend-drm/drm-internal.h |  23 ++
34 libweston/backend-drm/drm.c          | 369 ++++++++++++++++++++++++---
35 libweston/backend-drm/kms.c          |  42 ++-
36 libweston/compositor.c               |  42 ++-
37 7 files changed, 471 insertions(+), 57 deletions(-)
38
39diff --git a/compositor/main.c b/compositor/main.c
40index e946b4a..47bf540 100644
41--- a/compositor/main.c
42+++ b/compositor/main.c
43@@ -2067,7 +2067,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@@ -2097,11 +2097,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@@ -2289,6 +2288,25 @@ 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+	struct weston_config_section *section;
76+
77+	if (head->section)
78+		return true;
79+
80+	section = drm_config_find_controlling_output_section(wet->config, name);
81+	if (!section)
82+		return false;
83+
84+	head->section = section;
85+	return true;
86+}
87+
88 static void
89 drm_heads_changed(struct wl_listener *listener, void *arg)
90 {
91@@ -2304,6 +2322,8 @@ drm_heads_changed(struct wl_listener *listener, void *arg)
92 	 * output.
93 	 */
94 	while ((head = weston_compositor_iterate_heads(compositor, head))) {
95+		drm_head_update_output_section(head);
96+
97 		connected = weston_head_is_connected(head);
98 		enabled = weston_head_is_enabled(head);
99 		changed = weston_head_is_device_changed(head);
100diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c
101index 15c148f..515c57f 100644
102--- a/desktop-shell/shell.c
103+++ b/desktop-shell/shell.c
104@@ -3988,6 +3988,9 @@ shell_fade_done_for_output(struct weston_view_animation *animation, void *data)
105 	struct shell_output *shell_output = data;
106 	struct desktop_shell *shell = shell_output->shell;
107
108+	if (!shell_output->fade.view)
109+		return;
110+
111 	shell_output->fade.animation = NULL;
112 	switch (shell_output->fade.type) {
113 	case FADE_IN:
114@@ -4021,6 +4024,7 @@ shell_fade_create_surface_for_output(struct desktop_shell *shell, struct shell_o
115
116 	weston_surface_set_size(surface, shell_output->output->width, shell_output->output->height);
117 	weston_view_set_position(view, shell_output->output->x, shell_output->output->y);
118+	weston_view_set_output(view, shell_output->output);
119 	weston_surface_set_color(surface, 0.0, 0.0, 0.0, 1.0);
120 	weston_layer_entry_insert(&compositor->fade_layer.view_list,
121 				  &view->layer_link);
122@@ -4841,8 +4845,11 @@ shell_output_destroy(struct shell_output *shell_output)
123 	}
124
125 	if (shell_output->fade.view) {
126+		struct weston_view *view = shell_output->fade.view;
127+		shell_output->fade.view = NULL;
128+
129 		/* destroys the view as well */
130-		weston_surface_destroy(shell_output->fade.view->surface);
131+		weston_surface_destroy(view->surface);
132 	}
133
134 	if (shell_output->fade.startup_timer)
135@@ -4946,12 +4953,25 @@ handle_output_move_layer(struct desktop_shell *shell,
136 static void
137 handle_output_move(struct wl_listener *listener, void *data)
138 {
139+	struct weston_output *output = data;
140+	struct weston_compositor *compositor = output->compositor;
141 	struct desktop_shell *shell;
142
143 	shell = container_of(listener, struct desktop_shell,
144 			     output_move_listener);
145
146-	shell_for_each_layer(shell, handle_output_move_layer, data);
147+	if (shell->lock_surface)
148+		shell->lock_surface->committed(shell->lock_surface, 0, 0);
149+
150+	/* Only move normal layers for non-default output */
151+	if (output != get_default_output(compositor)) {
152+		shell_for_each_layer(shell, handle_output_move_layer, data);
153+		return;
154+	}
155+
156+	handle_output_move_layer(shell, &shell->lock_layer, data);
157+	handle_output_move_layer(shell, &shell->background_layer, data);
158+	handle_output_move_layer(shell, &shell->panel_layer, data);
159 }
160
161 static void
162diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h
163index c6ba508..bdecc19 100644
164--- a/include/libweston/libweston.h
165+++ b/include/libweston/libweston.h
166@@ -261,6 +261,8 @@ struct weston_head {
167
168 	/** Current content protection status */
169 	enum weston_hdcp_protection current_protection;
170+
171+	struct weston_config_section *section; /**< config section **/
172 };
173
174 /** Content producer for heads
175diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h
176index 4860088..3f42a25 100644
177--- a/libweston/backend-drm/drm-internal.h
178+++ b/libweston/backend-drm/drm-internal.h
179@@ -110,6 +110,10 @@
180
181 #define MAX_CLONED_CONNECTORS 4
182
183+/* Min duration between drm outputs update requests, to avoid glith */
184+#define DRM_MIN_UPDATE_MS	1000
185+
186+#define DRM_RESIZE_FREEZE_MS    600
187
188 /**
189  * Represents the values of an enum-type KMS property
190@@ -249,6 +253,10 @@ enum actions_needed_dmabuf_feedback {
191 	ACTION_NEEDED_REMOVE_SCANOUT_TRANCHE = (1 << 1),
192 };
193
194+struct drm_head;
195+struct drm_backend;
196+typedef bool (*drm_head_match_t) (struct drm_backend *, struct drm_head *);
197+
198 struct drm_backend {
199 	struct weston_backend base;
200 	struct weston_compositor *compositor;
201@@ -264,6 +272,7 @@ struct drm_backend {
202 		int fd;
203 		char *filename;
204 		dev_t devnum;
205+		char *syspath;
206 	} drm;
207 	struct gbm_device *gbm;
208 	struct wl_listener session_listener;
209@@ -311,6 +320,18 @@ struct drm_backend {
210 	bool fb_modifiers;
211
212 	struct weston_log_scope *debug;
213+
214+	struct wl_event_source *hotplug_timer;
215+	bool pending_update;
216+	int64_t last_update_ms;
217+	int64_t last_resize_ms;
218+
219+	bool single_head;
220+	bool head_fallback;
221+	bool head_fallback_all;
222+	drm_head_match_t *head_matches;
223+	struct drm_head *primary_head;
224+	struct wl_listener output_create_listener;
225 };
226
227 struct drm_mode {
228@@ -574,6 +595,8 @@ struct drm_output {
229 	bool virtual;
230
231 	submit_frame_cb virtual_submit_frame;
232+
233+	bool state_invalid;
234 };
235
236 static inline struct drm_head *
237diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c
238index d6a8588..4e00933 100644
239--- a/libweston/backend-drm/drm.c
240+++ b/libweston/backend-drm/drm.c
241@@ -47,6 +47,7 @@
242
243 #include <libudev.h>
244
245+#include <libweston/config-parser.h>
246 #include <libweston/libweston.h>
247 #include <libweston/backend-drm.h>
248 #include <libweston/weston-log.h>
249@@ -68,6 +69,44 @@
250
251 static const char default_seat[] = "seat0";
252
253+static inline bool
254+drm_head_is_external(struct drm_head *head)
255+{
256+	drmModeConnector *conn = head->connector.conn;
257+	switch (conn->connector_type) {
258+		case DRM_MODE_CONNECTOR_LVDS:
259+		case DRM_MODE_CONNECTOR_eDP:
260+#ifdef DRM_MODE_CONNECTOR_DSI
261+		case DRM_MODE_CONNECTOR_DSI:
262+#endif
263+			return false;
264+		default:
265+			return true;
266+	}
267+};
268+
269+static void
270+drm_backend_update_outputs(struct drm_backend *b)
271+{
272+	struct weston_output *primary;
273+
274+	if (!b->primary_head)
275+		return;
276+
277+	primary = b->primary_head->base.output;
278+	if (!primary)
279+		return;
280+
281+	/* Move primary output to (0,0) */
282+	wl_list_remove(&primary->link);
283+	wl_list_insert(&b->compositor->output_list, &primary->link);
284+
285+	/* Reflow outputs */
286+	weston_compositor_reflow_outputs(b->compositor);
287+
288+	weston_compositor_damage_all(b->compositor);
289+}
290+
291 static void
292 drm_backend_create_faked_zpos(struct drm_backend *b)
293 {
294@@ -460,10 +499,13 @@ drm_output_repaint(struct weston_output *output_base,
295 		   pixman_region32_t *damage,
296 		   void *repaint_data)
297 {
298+	struct drm_backend *b = to_drm_backend(output_base->compositor);
299 	struct drm_pending_state *pending_state = repaint_data;
300 	struct drm_output *output = to_drm_output(output_base);
301 	struct drm_output_state *state = NULL;
302 	struct drm_plane_state *scanout_state;
303+	struct timespec now;
304+	int64_t now_ms;
305
306 	assert(!output->virtual);
307
308@@ -472,6 +514,20 @@ drm_output_repaint(struct weston_output *output_base,
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@@ -497,7 +553,7 @@ drm_output_repaint(struct weston_output *output_base,
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@@ -718,6 +774,7 @@ drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mo
339 	 *      content.
340 	 */
341 	b->state_invalid = true;
342+	output->state_invalid = true;
343
344 	if (b->use_pixman) {
345 		drm_output_fini_pixman(output);
346@@ -1271,6 +1328,7 @@ drm_output_attach_head(struct weston_output *output_base,
347 		       struct weston_head *head_base)
348 {
349 	struct drm_backend *b = to_drm_backend(output_base->compositor);
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@@ -1288,6 +1346,7 @@ drm_output_attach_head(struct weston_output *output_base,
355 	 * will not clear the flag before this output is updated?
356 	 */
357 	b->state_invalid = true;
358+	output->state_invalid = true;
359
360 	weston_output_schedule_repaint(output_base);
361
362@@ -1299,6 +1358,7 @@ drm_output_detach_head(struct weston_output *output_base,
363 		       struct weston_head *head_base)
364 {
365 	struct drm_backend *b = to_drm_backend(output_base->compositor);
366+	struct drm_output *output = to_drm_output(output_base);
367
368 	if (!output_base->enabled)
369 		return;
370@@ -1307,6 +1367,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 	b->state_invalid = true;
374+	output->state_invalid = true;
375
376 	weston_output_schedule_repaint(output_base);
377 }
378@@ -1795,6 +1856,7 @@ drm_output_detach_crtc(struct drm_output *output)
379
380 	/* Force resetting unused CRTCs */
381 	b->state_invalid = true;
382+	output->state_invalid = true;
383 }
384
385 static int
386@@ -1839,6 +1901,8 @@ drm_output_enable(struct weston_output *base)
387 	output->base.switch_mode = drm_output_switch_mode;
388 	output->base.set_gamma = drm_output_set_gamma;
389
390+	output->state_invalid = true;
391+
392 	weston_log("Output %s (crtc %d) video modes:\n",
393 		   output->base.name, output->crtc->crtc_id);
394 	drm_output_print_modes(output);
395@@ -2173,8 +2237,7 @@ drm_head_create(struct drm_backend *backend, drmModeConnector *conn,
396
397 	head->backlight = backlight_init(drm_device, conn->connector_type);
398
399-	if (conn->connector_type == DRM_MODE_CONNECTOR_LVDS ||
400-	    conn->connector_type == DRM_MODE_CONNECTOR_eDP)
401+	if (!drm_head_is_external(head))
402 		weston_head_set_internal(&head->base);
403
404 	if (drm_head_read_current_setup(head, backend) < 0) {
405@@ -2335,71 +2398,77 @@ drm_backend_add_connector(struct drm_backend *b, drmModeConnector *conn,
406 	return ret;
407 }
408
409-/** Find all connectors of the fd and create drm_head or drm_writeback objects
410- * (depending on the type of connector they are) for each of them
411- *
412- * These objects are added to the DRM-backend lists of heads and writebacks.
413- *
414- * @param b The DRM-backend structure
415- * @param drm_device udev device pointer
416- * @param resources The DRM resources, it is taken with drmModeGetResources
417- * @return 0 on success, -1 on failure
418- */
419-static int
420-drm_backend_discover_connectors(struct drm_backend *b, struct udev_device *drm_device,
421-				drmModeRes *resources)
422+static bool
423+resources_has_connector(drmModeRes *resources, uint32_t connector_id)
424 {
425-	drmModeConnector *conn;
426-	int i, ret;
427+	for (int i = 0; i < resources->count_connectors; i++) {
428+		if (resources->connectors[i] == connector_id)
429+			return true;
430+	}
431
432-	b->min_width  = resources->min_width;
433-	b->max_width  = resources->max_width;
434-	b->min_height = resources->min_height;
435-	b->max_height = resources->max_height;
436+	return false;
437+}
438
439-	for (i = 0; i < resources->count_connectors; i++) {
440-		uint32_t connector_id = resources->connectors[i];
441+/* based on compositor/main.c#drm_head_prepare_enable() */
442+static bool
443+drm_head_is_available(struct weston_head *head)
444+{
445+	struct weston_config_section *section;
446+	char *mode = NULL;
447
448-		conn = drmModeGetConnector(b->drm.fd, connector_id);
449-		if (!conn)
450-			continue;
451+	section = head->section;
452+	if (!section)
453+		return true;
454
455-		ret = drm_backend_add_connector(b, conn, drm_device);
456-		if (ret < 0)
457-			drmModeFreeConnector(conn);
458+	/* skip outputs that are explicitly off, or non-desktop and not
459+	 * explicitly enabled.
460+	 */
461+	weston_config_section_get_string(section, "mode", &mode, NULL);
462+	if (mode && strcmp(mode, "off") == 0) {
463+		free(mode);
464+		return false;
465 	}
466
467-	return 0;
468+	if (!mode && weston_head_is_non_desktop(head))
469+		return false;
470+
471+	free(mode);
472+	return true;
473 }
474
475 static bool
476-resources_has_connector(drmModeRes *resources, uint32_t connector_id)
477+drm_head_match_fallback(struct drm_backend *b, struct drm_head *head)
478 {
479-	for (int i = 0; i < resources->count_connectors; i++) {
480-		if (resources->connectors[i] == connector_id)
481-			return true;
482-	}
483+	if (b->head_fallback_all)
484+		return true;
485
486-	return false;
487+	return b->head_fallback && !b->primary_head;
488 }
489
490-static void
491+static int
492 drm_backend_update_connectors(struct drm_backend *b, struct udev_device *drm_device)
493 {
494 	drmModeRes *resources;
495 	drmModeConnector *conn;
496 	struct weston_head *base, *base_next;
497-	struct drm_head *head;
498+	struct drm_head *head, *old_primary_head;
499 	struct drm_writeback *writeback, *writeback_next;
500+	drm_head_match_t *match = b->head_matches;
501+	struct timespec now;
502 	uint32_t connector_id;
503 	int i, ret;
504
505 	resources = drmModeGetResources(b->drm.fd);
506 	if (!resources) {
507 		weston_log("drmModeGetResources failed\n");
508-		return;
509+		return -1;
510 	}
511
512+	b->min_width  = resources->min_width;
513+	b->max_width  = resources->max_width;
514+	b->min_height = resources->min_height;
515+	b->max_height = resources->max_height;
516+
517 	/* collect new connectors that have appeared, e.g. MST */
518 	for (i = 0; i < resources->count_connectors; i++) {
519 		connector_id = resources->connectors[i];
520@@ -2456,6 +2525,65 @@ drm_backend_update_connectors(struct drm_backend *b, struct udev_device *drm_dev
521 	}
522
523 	drmModeFreeResources(resources);
524+
525+	old_primary_head = b->primary_head;
526+	b->primary_head = NULL;
527+
528+	wl_list_for_each_safe(base, base_next,
529+			      &b->compositor->head_list, compositor_link)
530+		weston_head_set_connection_status(base, false);
531+
532+	/* Re-connect matched heads and find primary head */
533+	while (*match) {
534+		wl_list_for_each_safe(base, base_next,
535+				      &b->compositor->head_list,
536+				      compositor_link) {
537+			drmModeConnector *conn;
538+
539+			if (!drm_head_is_available(base))
540+				continue;
541+
542+			head = to_drm_head(base);
543+			conn = head->connector.conn;
544+
545+			if (conn->connection != DRM_MODE_CONNECTED ||
546+			    !(*match)(b, head))
547+				continue;
548+
549+			weston_head_set_connection_status(base, true);
550+
551+			if (!b->primary_head) {
552+				b->primary_head = head;
553+
554+				/* Done the single-head match */
555+				if (b->single_head)
556+					goto match_done;
557+			}
558+		}
559+
560+		/* Done the fallback match */
561+		if (*match == drm_head_match_fallback)
562+			goto match_done;
563+
564+		match++;
565+
566+		/* Try the fallback match */
567+		if (!match && !b->primary_head)
568+			*match = drm_head_match_fallback;
569+	}
570+match_done:
571+
572+	drm_backend_update_outputs(b);
573+
574+	weston_compositor_read_presentation_clock(b->compositor, &now);
575+	b->last_update_ms = timespec_to_msec(&now);
576+
577+	/* Assume primary output's size changed */
578+	if (b->primary_head && old_primary_head &&
579+	    b->primary_head != old_primary_head)
580+		b->last_resize_ms = b->last_update_ms;
581+
582+	return 0;
583 }
584
585 static enum wdrm_connector_property
586@@ -2546,6 +2674,48 @@ udev_event_is_conn_prop_change(struct drm_backend *b,
587 	return 1;
588 }
589
590+static void
591+udev_hotplug_event(struct drm_backend *b, struct udev_device *device)
592+{
593+	struct timespec now;
594+	int64_t now_ms, next_ms;
595+
596+	weston_compositor_read_presentation_clock(b->compositor, &now);
597+	now_ms = timespec_to_msec(&now);
598+
599+	/* Already have a pending request */
600+	if (b->pending_update)
601+		return;
602+
603+	next_ms = b->last_update_ms + DRM_MIN_UPDATE_MS;
604+	if (next_ms <= now_ms) {
605+		/* Long enough to trigger a new request */
606+		drm_backend_update_connectors(b, device);
607+	} else {
608+		/* Too close to the last request, schedule a new one */
609+		b->pending_update = true;
610+		wl_event_source_timer_update(b->hotplug_timer,
611+					     next_ms - now_ms);
612+	}
613+}
614+
615+static int
616+hotplug_timer_handler(void *data)
617+{
618+	struct drm_backend *b = data;
619+	struct udev_device *device;
620+	struct udev *udev;
621+
622+	udev = udev_monitor_get_udev(b->udev_monitor);
623+	device = udev_device_new_from_syspath(udev, b->drm.syspath);
624+
625+	drm_backend_update_connectors(b, device);
626+	b->pending_update = false;
627+
628+	udev_device_unref(device);
629+	return 0;
630+}
631+
632 static int
633 udev_drm_event(int fd, uint32_t mask, void *data)
634 {
635@@ -2559,7 +2729,7 @@ udev_drm_event(int fd, uint32_t mask, void *data)
636 		if (udev_event_is_conn_prop_change(b, event, &conn_id, &prop_id))
637 			drm_backend_update_conn_props(b, conn_id, prop_id);
638 		else
639-			drm_backend_update_connectors(b, event);
640+			udev_hotplug_event(b, event);
641 	}
642
643 	udev_device_unref(event);
644@@ -2577,6 +2747,7 @@ drm_destroy(struct weston_compositor *ec)
645
646 	udev_input_destroy(&b->input);
647
648+	wl_event_source_remove(b->hotplug_timer);
649 	wl_event_source_remove(b->udev_drm_source);
650 	wl_event_source_remove(b->drm_source);
651
652@@ -2627,6 +2798,10 @@ session_notify(struct wl_listener *listener, void *data)
653 		weston_compositor_wake(compositor);
654 		weston_compositor_damage_all(compositor);
655 		b->state_invalid = true;
656+
657+		wl_list_for_each(output, &compositor->output_list, base.link)
658+			output->state_invalid = true;
659+
660 		udev_input_enable(&b->input);
661 	} else {
662 		weston_log("deactivating session\n");
663@@ -2987,6 +3162,14 @@ recorder_binding(struct weston_keyboard *keyboard, const struct timespec *time,
664 }
665 #endif
666
667+static void
668+output_create_notify(struct wl_listener *listener, void *data)
669+{
670+	struct drm_backend *b = container_of(listener, struct drm_backend,
671+					     output_create_listener);
672+
673+	drm_backend_update_outputs(b);
674+}
675
676 static const struct weston_drm_output_api api = {
677 	drm_output_set_mode,
678@@ -2994,6 +3177,63 @@ static const struct weston_drm_output_api api = {
679 	drm_output_set_seat,
680 };
681
682+enum drm_head_mode {
683+	DRM_HEAD_MODE_DEFAULT,
684+	DRM_HEAD_MODE_PRIMARY,
685+	DRM_HEAD_MODE_INTERNAL,
686+	DRM_HEAD_MODE_EXTERNAL,
687+	DRM_HEAD_MODE_EXTERNAL_DUAL,
688+};
689+
690+static bool
691+drm_head_match_primary(struct drm_backend *b, struct drm_head *head)
692+{
693+	const char *buf = getenv("WESTON_DRM_PRIMARY");
694+	return buf && !strcmp(buf, head->base.name);
695+}
696+
697+static bool
698+drm_head_match_external(struct drm_backend *b, struct drm_head *head)
699+{
700+	return drm_head_is_external(head);
701+}
702+
703+static bool
704+drm_head_match_internal(struct drm_backend *b, struct drm_head *head)
705+{
706+	return !drm_head_is_external(head);
707+}
708+
709+#define DRM_HEAD_MAX_MATCHES 5
710+static drm_head_match_t drm_head_matches[][DRM_HEAD_MAX_MATCHES] = {
711+	[DRM_HEAD_MODE_DEFAULT] = {
712+		drm_head_match_primary,
713+		drm_head_match_internal,
714+		drm_head_match_external,
715+		NULL,
716+	},
717+	[DRM_HEAD_MODE_PRIMARY] = {
718+		drm_head_match_primary,
719+		NULL,
720+	},
721+	[DRM_HEAD_MODE_INTERNAL] = {
722+		drm_head_match_primary,
723+		drm_head_match_internal,
724+		NULL,
725+	},
726+	[DRM_HEAD_MODE_EXTERNAL] = {
727+		drm_head_match_primary,
728+		drm_head_match_external,
729+		NULL,
730+	},
731+	[DRM_HEAD_MODE_EXTERNAL_DUAL] = {
732+		drm_head_match_primary,
733+		drm_head_match_external,
734+		drm_head_match_internal,
735+		NULL,
736+	},
737+};
738+
739 static struct drm_backend *
740 drm_backend_create(struct weston_compositor *compositor,
741 		   struct weston_drm_backend_config *config)
742@@ -3004,7 +3244,9 @@ drm_backend_create(struct weston_compositor *compositor,
743 	const char *seat_id = default_seat;
744 	const char *session_seat;
745 	struct weston_drm_format_array *scanout_formats;
746+	enum drm_head_mode head_mode = DRM_HEAD_MODE_DEFAULT;
747 	drmModeRes *res;
748+	char *buf;
749 	int ret;
750
751 	session_seat = getenv("XDG_SEAT");
752@@ -3020,6 +3262,42 @@ drm_backend_create(struct weston_compositor *compositor,
753 	if (b == NULL)
754 		return NULL;
755
756+	buf = getenv("WESTON_DRM_SINGLE_HEAD");
757+	if (buf && buf[0] == '1')
758+		b->single_head = true;
759+
760+	buf = getenv("WESTON_DRM_HEAD_FALLBACK");
761+	if (buf && buf[0] == '1')
762+		b->head_fallback = true;
763+
764+	buf = getenv("WESTON_DRM_HEAD_FALLBACK_ALL");
765+	if (buf && buf[0] == '1')
766+		b->head_fallback_all = true;
767+
768+	buf = getenv("WESTON_DRM_PREFER_EXTERNAL");
769+	if (buf && buf[0] == '1') {
770+		head_mode = DRM_HEAD_MODE_EXTERNAL;
771+		b->head_fallback = true;
772+	}
773+
774+	buf = getenv("WESTON_DRM_PREFER_EXTERNAL_DUAL");
775+	if (buf && buf[0] == '1')
776+		head_mode = DRM_HEAD_MODE_EXTERNAL_DUAL;
777+
778+	buf = getenv("WESTON_DRM_HEAD_MODE");
779+	if (buf) {
780+		if (!strcmp(buf, "primary"))
781+			head_mode = DRM_HEAD_MODE_PRIMARY;
782+		else if (!strcmp(buf, "internal"))
783+			head_mode = DRM_HEAD_MODE_INTERNAL;
784+		else if (!strcmp(buf, "external"))
785+			head_mode = DRM_HEAD_MODE_EXTERNAL;
786+		else if (!strcmp(buf, "external-dual"))
787+			head_mode = DRM_HEAD_MODE_EXTERNAL_DUAL;
788+	}
789+
790+	b->head_matches = drm_head_matches[head_mode];
791+
792 	b->state_invalid = true;
793 	b->drm.fd = -1;
794
795@@ -3115,7 +3393,7 @@ drm_backend_create(struct weston_compositor *compositor,
796 	}
797
798 	wl_list_init(&b->writeback_connector_list);
799-	if (drm_backend_discover_connectors(b, drm_device, res) < 0) {
800+	if (drm_backend_update_connectors(b, drm_device) < 0) {
801 		weston_log("Failed to create heads for %s\n", b->drm.filename);
802 		goto err_udev_input;
803 	}
804@@ -3154,6 +3432,10 @@ drm_backend_create(struct weston_compositor *compositor,
805
806 	udev_device_unref(drm_device);
807
808+	b->output_create_listener.notify = output_create_notify;
809+	wl_signal_add(&b->compositor->output_created_signal,
810+		      &b->output_create_listener);
811+
812 	weston_compositor_add_debug_binding(compositor, KEY_O,
813 					    planes_binding, b);
814 	weston_compositor_add_debug_binding(compositor, KEY_C,
815@@ -3215,6 +3497,9 @@ drm_backend_create(struct weston_compositor *compositor,
816 		goto err_udev_monitor;
817 	}
818
819+	b->hotplug_timer =
820+		wl_event_loop_add_timer(loop, hotplug_timer_handler, b);
821+
822 	return b;
823
824 err_udev_monitor:
825diff --git a/libweston/backend-drm/kms.c b/libweston/backend-drm/kms.c
826index 35113d5..4426af4 100644
827--- a/libweston/backend-drm/kms.c
828+++ b/libweston/backend-drm/kms.c
829@@ -704,6 +704,8 @@ drm_output_apply_state_legacy(struct drm_output_state *state)
830
831 	scanout_state =
832 		drm_output_state_get_existing_plane(state, scanout_plane);
833+	if (!scanout_state || !scanout_state->fb)
834+		return 0;
835
836 	/* The legacy SetCrtc API doesn't allow us to do scaling, and the
837 	 * legacy PageFlip API doesn't allow us to do clipping either. */
838@@ -721,7 +723,7 @@ drm_output_apply_state_legacy(struct drm_output_state *state)
839 	assert(scanout_state->in_fence_fd == -1);
840
841 	mode = to_drm_mode(output->base.current_mode);
842-	if (backend->state_invalid ||
843+	if (output->state_invalid ||
844 	    !scanout_plane->state_cur->fb ||
845 	    scanout_plane->state_cur->fb->strides[0] !=
846 	    scanout_state->fb->strides[0]) {
847@@ -735,6 +737,8 @@ drm_output_apply_state_legacy(struct drm_output_state *state)
848 			weston_log("set mode failed: %s\n", strerror(errno));
849 			goto err;
850 		}
851+
852+		output->state_invalid = false;
853 	}
854
855 	pinfo = scanout_state->fb->format;
856@@ -945,6 +949,11 @@ drm_output_apply_state_atomic(struct drm_output_state *state,
857 		*flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
858 	}
859
860+	if (output->state_invalid) {
861+		drm_debug(b, "\t\t\t[atomic] output state invalid, modeset OK\n");
862+		*flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
863+	}
864+
865 	if (state->dpms == WESTON_DPMS_ON) {
866 		ret = drm_mode_ensure_blob(b, current_mode);
867 		if (ret != 0)
868@@ -1050,6 +1059,7 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
869 	struct drm_output_state *output_state, *tmp;
870 	struct drm_plane *plane;
871 	drmModeAtomicReq *req = drmModeAtomicAlloc();
872+	struct timespec now;
873 	uint32_t flags;
874 	int ret = 0;
875
876@@ -1180,14 +1190,34 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
877 		goto out;
878 	}
879
880+	weston_compositor_read_presentation_clock(b->compositor, &now);
881+
882 	wl_list_for_each_safe(output_state, tmp, &pending_state->output_list,
883-			      link)
884+			      link) {
885+		struct drm_output *output = output_state->output;
886+		struct drm_plane *scanout_plane = output->scanout_plane;
887+		struct drm_plane_state *scanout_state =
888+			drm_output_state_get_existing_plane(output_state,
889+							    scanout_plane);
890+
891+		/* Don't have a new state to apply */
892+		if (output_state->dpms == WESTON_DPMS_ON &&
893+		    (!scanout_state || !scanout_state->fb))
894+			continue;
895+
896 		drm_output_assign_state(output_state, mode);
897+		output->state_invalid = false;
898+
899+		/* Not gonna receive flip event when dpms off */
900+		if (output_state->dpms != WESTON_DPMS_ON)
901+			drm_output_update_complete(output,
902+						   WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION,
903+						   now.tv_sec,
904+						   now.tv_nsec / 1000);
905+	}
906
907 	b->state_invalid = false;
908
909-	assert(wl_list_empty(&pending_state->output_list));
910-
911 out:
912 	drmModeAtomicFree(req);
913 	drm_pending_state_free(pending_state);
914@@ -1286,8 +1316,6 @@ drm_pending_state_apply(struct drm_pending_state *pending_state)
915
916 	b->state_invalid = false;
917
918-	assert(wl_list_empty(&pending_state->output_list));
919-
920 	drm_pending_state_free(pending_state);
921
922 	return 0;
923@@ -1339,8 +1367,6 @@ drm_pending_state_apply_sync(struct drm_pending_state *pending_state)
924
925 	b->state_invalid = false;
926
927-	assert(wl_list_empty(&pending_state->output_list));
928-
929 	drm_pending_state_free(pending_state);
930
931 	return 0;
932diff --git a/libweston/compositor.c b/libweston/compositor.c
933index e184349..22a8593 100644
934--- a/libweston/compositor.c
935+++ b/libweston/compositor.c
936@@ -157,6 +157,25 @@ weston_paint_node_destroy(struct weston_paint_node *pnode)
937 	free(pnode);
938 }
939
940+static struct weston_layer *
941+get_view_layer(struct weston_view *view);
942+
943+static bool
944+weston_compositor_is_static_layer(struct weston_layer *layer)
945+{
946+	if (!layer)
947+		return false;
948+
949+	switch (layer->position) {
950+	case WESTON_LAYER_POSITION_BACKGROUND:
951+	case WESTON_LAYER_POSITION_UI:
952+	case WESTON_LAYER_POSITION_FADE:
953+		return true;
954+	default:
955+		return false;
956+	}
957+}
958+
959 /** Send wl_output events for mode and scale changes
960  *
961  * \param head Send on all resources bound to this head.
962@@ -1367,6 +1386,22 @@ weston_view_assign_output(struct weston_view *ev)
963 	uint32_t max, area, mask;
964 	pixman_box32_t *e;
965
966+	/* The static views should bind to the specific output */
967+	if (weston_compositor_is_static_layer(get_view_layer(ev))) {
968+		struct weston_view *view = ev;
969+
970+		while (view && !(output = view->output))
971+			view = view->geometry.parent;
972+
973+		if (output && !output->destroying)
974+			ev->output_mask |= 1u << output->id;
975+		else
976+			weston_view_set_output(ev, NULL);
977+
978+		weston_surface_assign_output(ev->surface);
979+		return;
980+	}
981+
982 	new_output = NULL;
983 	max = 0;
984 	mask = 0;
985@@ -2942,12 +2977,14 @@ weston_output_repaint(struct weston_output *output, void *repaint_data)
986 	if (output->dirty)
987 		weston_output_update_matrix(output);
988
989+	output->repaint_needed = false;
990 	r = output->repaint(output, &output_damage, repaint_data);
991
992 	pixman_region32_fini(&output_damage);
993
994-	output->repaint_needed = false;
995-	if (r == 0)
996+	if (output->repaint_needed)
997+		output->repaint_status = REPAINT_SCHEDULED;
998+	else if (r == 0)
999 		output->repaint_status = REPAINT_AWAITING_COMPLETION;
1000
1001 	weston_compositor_repick(ec);
1002@@ -6407,6 +6444,7 @@ weston_compositor_remove_output(struct weston_output *output)
1003 	 * Use view_list in case the output did not go through repaint
1004 	 * after a view came on it, lacking a paint node. Just to be sure.
1005 	 */
1006+	weston_compositor_build_view_list(compositor, NULL);
1007 	wl_list_for_each(view, &compositor->view_list, link) {
1008 		if (view->output_mask & (1u << output->id))
1009 			weston_view_assign_output(view);
1010--
10112.20.1
1012
1013