1From 0a3e3bf559dea73dd489c0d3cacab6ba649db412 Mon Sep 17 00:00:00 2001
2From: Jeffy Chen <jeffy.chen@rock-chips.com>
3Date: Wed, 24 Jun 2020 11:59:42 +0800
4Subject: [PATCH 14/79] backend-drm: Support virtual screen size
5
6Support setting virtual screen size, for example:
7export WESTON_DRM_VIRTUAL_SIZE=1024x768
8
9Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
10---
11 libweston/backend-drm/drm-internal.h |  23 ++++++
12 libweston/backend-drm/drm.c          |  28 ++++++-
13 libweston/backend-drm/kms.c          | 115 ++++++++++++++++++++++-----
14 libweston/backend-drm/modes.c        |  22 ++++-
15 4 files changed, 162 insertions(+), 26 deletions(-)
16
17diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h
18index 3f42a25..996f587 100644
19--- a/libweston/backend-drm/drm-internal.h
20+++ b/libweston/backend-drm/drm-internal.h
21@@ -166,6 +166,7 @@ enum wdrm_plane_property {
22 	WDRM_PLANE_IN_FENCE_FD,
23 	WDRM_PLANE_FB_DAMAGE_CLIPS,
24 	WDRM_PLANE_ZPOS,
25+	WDRM_PLANE_FEATURE,
26 	WDRM_PLANE__COUNT
27 };
28
29@@ -179,6 +180,15 @@ enum wdrm_plane_type {
30 	WDRM_PLANE_TYPE__COUNT
31 };
32
33+/**
34+ * Possible values for the WDRM_PLANE_FEATURE property.
35+ */
36+enum wdrm_plane_feature {
37+	WDRM_PLANE_FEATURE_SCALE = 0,
38+	WDRM_PLANE_FEATURE_ALPHA,
39+	WDRM_PLANE_FEATURE__COUNT
40+};
41+
42 /**
43  * List of properties attached to a DRM connector
44  */
45@@ -325,6 +335,7 @@ struct drm_backend {
46 	bool pending_update;
47 	int64_t last_update_ms;
48 	int64_t last_resize_ms;
49+	int64_t resize_freeze_ms;
50
51 	bool single_head;
52 	bool head_fallback;
53@@ -332,6 +343,9 @@ struct drm_backend {
54 	drm_head_match_t *head_matches;
55 	struct drm_head *primary_head;
56 	struct wl_listener output_create_listener;
57+
58+	int virtual_width;
59+	int virtual_height;
60 };
61
62 struct drm_mode {
63@@ -501,6 +515,8 @@ struct drm_plane {
64 	struct wl_list link;
65
66 	struct weston_drm_format_array formats;
67+
68+	bool can_scale;
69 };
70
71 struct drm_connector {
72@@ -597,6 +613,9 @@ struct drm_output {
73 	submit_frame_cb virtual_submit_frame;
74
75 	bool state_invalid;
76+
77+	/* The dummy framebuffer for SET_CRTC. */
78+	struct drm_fb *fb_dummy;
79 };
80
81 static inline struct drm_head *
82@@ -694,6 +713,10 @@ uint64_t
83 drm_property_get_value(struct drm_property_info *info,
84 		       const drmModeObjectProperties *props,
85 		       uint64_t def);
86+bool
87+drm_property_has_feature(struct drm_property_info *infos,
88+			 const drmModeObjectProperties *props,
89+			 enum wdrm_plane_feature feature);
90 uint64_t *
91 drm_property_get_range_values(struct drm_property_info *info,
92 			      const drmModeObjectProperties *props);
93diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c
94index 4e00933..1c839c5 100644
95--- a/libweston/backend-drm/drm.c
96+++ b/libweston/backend-drm/drm.c
97@@ -324,6 +324,11 @@ drm_output_update_complete(struct drm_output *output, uint32_t flags,
98 		output->state_last = NULL;
99 	}
100
101+	if (output->fb_dummy) {
102+		drm_fb_unref(output->fb_dummy);
103+		output->fb_dummy = NULL;
104+	}
105+
106 	if (output->destroy_pending) {
107 		output->destroy_pending = false;
108 		output->disable_pending = false;
109@@ -397,6 +402,7 @@ drm_output_render(struct drm_output_state *state, pixman_region32_t *damage)
110 	struct drm_property_info *damage_info =
111 		&scanout_plane->props[WDRM_PLANE_FB_DAMAGE_CLIPS];
112 	struct drm_backend *b = to_drm_backend(c);
113+	struct drm_mode *mode;
114 	struct drm_fb *fb;
115 	pixman_region32_t scanout_damage;
116 	pixman_box32_t *rects;
117@@ -440,10 +446,11 @@ drm_output_render(struct drm_output_state *state, pixman_region32_t *damage)
118 	scanout_state->src_w = fb->width << 16;
119 	scanout_state->src_h = fb->height << 16;
120
121+	mode = to_drm_mode(output->base.current_mode);
122 	scanout_state->dest_x = 0;
123 	scanout_state->dest_y = 0;
124-	scanout_state->dest_w = output->base.current_mode->width;
125-	scanout_state->dest_h = output->base.current_mode->height;
126+	scanout_state->dest_w = mode->mode_info.hdisplay;
127+	scanout_state->dest_h = mode->mode_info.vdisplay;
128
129 	pixman_region32_subtract(&c->primary_plane.damage,
130 				 &c->primary_plane.damage, damage);
131@@ -516,7 +523,7 @@ drm_output_repaint(struct weston_output *output_base,
132
133 	weston_compositor_read_presentation_clock(b->compositor, &now);
134 	now_ms = timespec_to_msec(&now);
135-	if (now_ms < b->last_resize_ms + DRM_RESIZE_FREEZE_MS) {
136+	if (now_ms < b->last_resize_ms + b->resize_freeze_ms) {
137 		/* Resize fullscreen/maxmium views(not always success) */
138 		if (now_ms < b->last_resize_ms + DRM_RESIZE_FREEZE_MS)
139 			wl_signal_emit(&b->compositor->output_resized_signal,
140@@ -852,6 +859,11 @@ drm_plane_create(struct drm_backend *b, const drmModePlane *kplane)
141 				       props,
142 				       WDRM_PLANE_TYPE__COUNT);
143
144+	plane->can_scale =
145+		drm_property_has_feature(plane->props,
146+					 props,
147+					 WDRM_PLANE_FEATURE_SCALE);
148+
149 	zpos_range_values =
150 		drm_property_get_range_values(&plane->props[WDRM_PLANE_ZPOS],
151 					      props);
152@@ -3298,6 +3310,16 @@ drm_backend_create(struct weston_compositor *compositor,
153
154 	b->head_matches = drm_head_matches[head_mode];
155
156+	buf = getenv("WESTON_DRM_VIRTUAL_SIZE");
157+	if (buf)
158+		sscanf(buf, "%dx%d", &b->virtual_width, &b->virtual_height);
159+
160+	buf = getenv("WESTON_DRM_RESIZE_FREEZE_MS");
161+	if (buf)
162+		b->resize_freeze_ms = atoi(buf);
163+	else
164+		b->resize_freeze_ms = DRM_RESIZE_FREEZE_MS;
165+
166 	b->state_invalid = true;
167 	b->drm.fd = -1;
168
169diff --git a/libweston/backend-drm/kms.c b/libweston/backend-drm/kms.c
170index 4426af4..1fcbdeb 100644
171--- a/libweston/backend-drm/kms.c
172+++ b/libweston/backend-drm/kms.c
173@@ -56,6 +56,15 @@ struct drm_property_enum_info plane_type_enums[] = {
174 	},
175 };
176
177+struct drm_property_enum_info plane_feature_enums[] = {
178+	[WDRM_PLANE_FEATURE_SCALE] = {
179+		.name = "scale",
180+	},
181+	[WDRM_PLANE_FEATURE_ALPHA] = {
182+		.name = "alpha",
183+	},
184+};
185+
186 const struct drm_property_info plane_props[] = {
187 	[WDRM_PLANE_TYPE] = {
188 		.name = "type",
189@@ -76,6 +85,11 @@ const struct drm_property_info plane_props[] = {
190 	[WDRM_PLANE_IN_FENCE_FD] = { .name = "IN_FENCE_FD" },
191 	[WDRM_PLANE_FB_DAMAGE_CLIPS] = { .name = "FB_DAMAGE_CLIPS" },
192 	[WDRM_PLANE_ZPOS] = { .name = "zpos" },
193+	[WDRM_PLANE_FEATURE] = {
194+		.name = "FEATURE",
195+		.enum_values = plane_feature_enums,
196+		.num_enum_values = WDRM_PLANE_FEATURE__COUNT,
197+	},
198 };
199
200 struct drm_property_enum_info dpms_state_enums[] = {
201@@ -213,6 +227,31 @@ drm_property_get_value(struct drm_property_info *info,
202 	return def;
203 }
204
205+bool
206+drm_property_has_feature(struct drm_property_info *infos,
207+			 const drmModeObjectProperties *props,
208+			 enum wdrm_plane_feature feature)
209+{
210+	struct drm_property_info *info = &infos[WDRM_PLANE_FEATURE];
211+	unsigned int i;
212+
213+	if (info->prop_id == 0 ||
214+	    feature >= info->num_enum_values ||
215+	    !info->enum_values[feature].valid)
216+		return false;
217+
218+	for (i = 0; i < props->count_props; i++) {
219+		if (props->props[i] != info->prop_id)
220+			continue;
221+
222+		if (props->prop_values[i] &
223+		    (1LL << info->enum_values[feature].value))
224+			return true;
225+	}
226+
227+	return false;
228+}
229+
230 /**
231  * Get the current range values of a KMS property
232  *
233@@ -333,9 +372,11 @@ drm_property_info_populate(struct drm_backend *b,
234 		}
235
236 		if (info[j].num_enum_values == 0 &&
237-		    (prop->flags & DRM_MODE_PROP_ENUM)) {
238+		    (prop->flags & DRM_MODE_PROP_ENUM ||
239+		     prop->flags & DRM_MODE_PROP_BITMASK)) {
240 			weston_log("DRM: expected property %s to not be an"
241-			           " enum, but it is; ignoring\n", prop->name);
242+			           " enum or bitmask, but it is; ignoring\n",
243+				   prop->name);
244 			drmModeFreeProperty(prop);
245 			continue;
246 		}
247@@ -356,9 +397,11 @@ drm_property_info_populate(struct drm_backend *b,
248 			continue;
249 		}
250
251-		if (!(prop->flags & DRM_MODE_PROP_ENUM)) {
252-			weston_log("DRM: expected property %s to be an enum,"
253-				   " but it is not; ignoring\n", prop->name);
254+		if (!(prop->flags & DRM_MODE_PROP_ENUM ||
255+		      prop->flags & DRM_MODE_PROP_BITMASK)) {
256+			weston_log("DRM: expected property %s to be an enum or "
257+				   "bitmask, but it is not; ignoring\n",
258+				   prop->name);
259 			drmModeFreeProperty(prop);
260 			info[j].prop_id = 0;
261 			continue;
262@@ -660,6 +703,7 @@ drm_output_apply_state_legacy(struct drm_output_state *state)
263 	int n_conn = 0;
264 	struct timespec now;
265 	int ret = 0;
266+	bool scaling;
267
268 	wl_list_for_each(head, &output->base.head_list, base.output_link) {
269 		assert(n_conn < MAX_CLONED_CONNECTORS);
270@@ -707,30 +751,36 @@ drm_output_apply_state_legacy(struct drm_output_state *state)
271 	if (!scanout_state || !scanout_state->fb)
272 		return 0;
273
274-	/* The legacy SetCrtc API doesn't allow us to do scaling, and the
275-	 * legacy PageFlip API doesn't allow us to do clipping either. */
276-	assert(scanout_state->src_x == 0);
277-	assert(scanout_state->src_y == 0);
278-	assert(scanout_state->src_w ==
279-		(unsigned) (output->base.current_mode->width << 16));
280-	assert(scanout_state->src_h ==
281-		(unsigned) (output->base.current_mode->height << 16));
282-	assert(scanout_state->dest_x == 0);
283-	assert(scanout_state->dest_y == 0);
284-	assert(scanout_state->dest_w == scanout_state->src_w >> 16);
285-	assert(scanout_state->dest_h == scanout_state->src_h >> 16);
286 	/* The legacy SetCrtc API doesn't support fences */
287 	assert(scanout_state->in_fence_fd == -1);
288
289 	mode = to_drm_mode(output->base.current_mode);
290+
291+	scaling = scanout_state->src_w >> 16 != scanout_state->dest_w ||
292+		scanout_state->src_h >> 16 != scanout_state->dest_h;
293+
294 	if (output->state_invalid ||
295-	    !scanout_plane->state_cur->fb ||
296-	    scanout_plane->state_cur->fb->strides[0] !=
297-	    scanout_state->fb->strides[0]) {
298+	    !scanout_plane->state_cur->fb) {
299+		int fb_id = scanout_state->fb->fb_id;
300+
301+		/* Use a dummy fb for initial mode setting */
302+		if (!output->fb_dummy) {
303+			output->fb_dummy =
304+				drm_fb_create_dumb(backend,
305+						   mode->mode_info.hdisplay,
306+						   mode->mode_info.vdisplay,
307+						   output->gbm_format);
308+			if (!output->fb_dummy) {
309+				weston_log("failed to create fb_dummy\n");
310+				goto err;
311+			}
312+		}
313+
314+		if (n_conn == 1 || scaling)
315+			fb_id = output->fb_dummy->fb_id;
316
317 		ret = drmModeSetCrtc(backend->drm.fd, crtc->crtc_id,
318-				     scanout_state->fb->fb_id,
319-				     0, 0,
320+				     fb_id, 0, 0,
321 				     connectors, n_conn,
322 				     &mode->mode_info);
323 		if (ret) {
324@@ -741,6 +791,27 @@ drm_output_apply_state_legacy(struct drm_output_state *state)
325 		output->state_invalid = false;
326 	}
327
328+	if (scaling && !output->scanout_plane->can_scale) {
329+		weston_log("Couldn't do scaling on output %s\n",
330+			   output->base.name);
331+		weston_output_finish_frame(&output->base, NULL,
332+					   WP_PRESENTATION_FEEDBACK_INVALID);
333+		return 0;
334+	}
335+
336+	ret = drmModeSetPlane(backend->drm.fd,
337+			      scanout_state->plane->plane_id,
338+			      crtc->crtc_id,
339+			      scanout_state->fb->fb_id, 0,
340+			      scanout_state->dest_x, scanout_state->dest_y,
341+			      scanout_state->dest_w, scanout_state->dest_h,
342+			      scanout_state->src_x, scanout_state->src_y,
343+			      scanout_state->src_w, scanout_state->src_h);
344+	if (ret) {
345+		weston_log("set plane failed: %s\n", strerror(errno));
346+		goto err;
347+	}
348+
349 	pinfo = scanout_state->fb->format;
350 	drm_debug(backend, "\t[CRTC:%u, PLANE:%u] FORMAT: %s\n",
351 			   crtc->crtc_id, scanout_state->plane->plane_id,
352diff --git a/libweston/backend-drm/modes.c b/libweston/backend-drm/modes.c
353index a071375..e7bbfa5 100644
354--- a/libweston/backend-drm/modes.c
355+++ b/libweston/backend-drm/modes.c
356@@ -378,6 +378,7 @@ drm_refresh_rate_mHz(const drmModeModeInfo *info)
357 static struct drm_mode *
358 drm_output_add_mode(struct drm_output *output, const drmModeModeInfo *info)
359 {
360+	struct drm_backend *b = to_drm_backend(output->base.compositor);
361 	struct drm_mode *mode;
362
363 	mode = malloc(sizeof *mode);
364@@ -388,6 +389,11 @@ drm_output_add_mode(struct drm_output *output, const drmModeModeInfo *info)
365 	mode->base.width = info->hdisplay;
366 	mode->base.height = info->vdisplay;
367
368+	if (b->virtual_width && b->virtual_height) {
369+		mode->base.width = b->virtual_width;
370+		mode->base.height = b->virtual_height;
371+	}
372+
373 	mode->base.refresh = drm_refresh_rate_mHz(info);
374 	mode->mode_info = *info;
375 	mode->blob_id = 0;
376@@ -434,20 +440,34 @@ drm_output_print_modes(struct drm_output *output)
377 	struct weston_mode *m;
378 	struct drm_mode *dm;
379 	const char *aspect_ratio;
380+	bool virtual_size = false;
381
382 	wl_list_for_each(m, &output->base.mode_list, link) {
383 		dm = to_drm_mode(m);
384
385 		aspect_ratio = aspect_ratio_to_string(m->aspect_ratio);
386 		weston_log_continue(STAMP_SPACE "%dx%d@%.1f%s%s%s, %.1f MHz\n",
387-				    m->width, m->height, m->refresh / 1000.0,
388+				    dm->mode_info.hdisplay,
389+				    dm->mode_info.vdisplay,
390+				    m->refresh / 1000.0,
391 				    aspect_ratio,
392 				    m->flags & WL_OUTPUT_MODE_PREFERRED ?
393 				    ", preferred" : "",
394 				    m->flags & WL_OUTPUT_MODE_CURRENT ?
395 				    ", current" : "",
396 				    dm->mode_info.clock / 1000.0);
397+
398+		if(m->flags & WL_OUTPUT_MODE_CURRENT &&
399+		   (dm->mode_info.hdisplay != m->width ||
400+		    dm->mode_info.vdisplay != m->height))
401+			virtual_size = true;
402 	}
403+
404+	if (virtual_size)
405+		weston_log("Output %s: using virtual size %dx%d\n",
406+			   output->base.name,
407+			   output->base.current_mode->width,
408+			   output->base.current_mode->height);
409 }
410
411
412--
4132.20.1
414
415