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