[PATCH weston v6 61/73] compositor-drm: combine mode list from heads

Pekka Paalanen ppaalanen at gmail.com
Fri Feb 16 14:57:46 UTC 2018


From: Pekka Paalanen <pekka.paalanen at collabora.co.uk>

A single list of modes needs to be combined from the mode lists in each
attached head. We could just concatenate the lists, but that might
introduce duplicates. Try to avoid duplicates instead by using partially
fuzzy matching.

When a duplicate is found, try to figure out which is more suitable to
use in place of both. If one has the preferred flag and the other
doesn't, take the preferred one. Otherwise use the one already in the
list.

Signed-off-by: Pekka Paalanen <pekka.paalanen at collabora.co.uk>
---
 libweston/compositor-drm.c | 114 +++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 104 insertions(+), 10 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index d7d58cc3..63ce8e58 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -4446,6 +4446,101 @@ parse_gbm_format(const char *s, uint32_t default_value, uint32_t *gbm_format)
 	return ret;
 }
 
+static uint32_t
+u32distance(uint32_t a, uint32_t b)
+{
+	if (a < b)
+		return b - a;
+	else
+		return a - b;
+}
+
+/** Choose equivalent mode
+ *
+ * If the two modes are not equivalent, return NULL.
+ * Otherwise return the mode that is more likely to work in place of both.
+ *
+ * None of the fuzzy matching criteria in this function have any justification.
+ *
+ * typedef struct _drmModeModeInfo {
+ *         uint32_t clock;
+ *         uint16_t hdisplay, hsync_start, hsync_end, htotal, hskew;
+ *         uint16_t vdisplay, vsync_start, vsync_end, vtotal, vscan;
+ *
+ *         uint32_t vrefresh;
+ *
+ *         uint32_t flags;
+ *         uint32_t type;
+ *         char name[DRM_DISPLAY_MODE_LEN];
+ * } drmModeModeInfo, *drmModeModeInfoPtr;
+ */
+static const drmModeModeInfo *
+drm_mode_pick_equivalent(const drmModeModeInfo *a, const drmModeModeInfo *b)
+{
+	uint32_t refresh_a, refresh_b;
+
+	if (a->hdisplay != b->hdisplay || a->vdisplay != b->vdisplay)
+		return NULL;
+
+	if (a->flags != b->flags)
+		return NULL;
+
+	/* kHz */
+	if (u32distance(a->clock, b->clock) > 500)
+		return NULL;
+
+	refresh_a = drm_refresh_rate_mHz(a);
+	refresh_b = drm_refresh_rate_mHz(b);
+	if (u32distance(refresh_a, refresh_b) > 50)
+		return NULL;
+
+	if ((a->type ^ b->type) & DRM_MODE_TYPE_PREFERRED) {
+		if (a->type & DRM_MODE_TYPE_PREFERRED)
+			return a;
+		else
+			return b;
+	}
+
+	return a;
+}
+
+/* If the given mode info is not already in the list, add it.
+ * If it is in the list, either keep the existing or replace it,
+ * depending on which one is "better".
+ */
+static int
+drm_output_try_add_mode(struct drm_output *output, const drmModeModeInfo *info)
+{
+	struct weston_mode *base;
+	struct drm_mode *mode;
+	struct drm_backend *backend;
+	const drmModeModeInfo *chosen = NULL;
+
+	assert(info);
+
+	wl_list_for_each(base, &output->base.mode_list, link) {
+		mode = to_drm_mode(base);
+		chosen = drm_mode_pick_equivalent(&mode->mode_info, info);
+		if (chosen)
+			break;
+	}
+
+	if (chosen == info) {
+		backend = to_drm_backend(output->base.compositor);
+		drm_output_destroy_mode(backend, mode);
+		chosen = NULL;
+	}
+
+	if (!chosen) {
+		mode = drm_output_add_mode(output, info);
+		if (!mode)
+			return -1;
+	}
+	/* else { the equivalent mode is already in the list } */
+
+	return 0;
+}
+
 /** Rewrite the output's mode list
  *
  * @param output The output.
@@ -4462,22 +4557,21 @@ drm_output_update_modelist_from_heads(struct drm_output *output)
 	struct drm_backend *backend = to_drm_backend(output->base.compositor);
 	struct weston_head *head_base;
 	struct drm_head *head;
-	struct drm_mode *mode;
 	int i;
+	int ret;
 
 	assert(!output->base.enabled);
 
 	drm_mode_list_destroy(backend, &output->base.mode_list);
 
-	/* XXX: needs a strategy for combining mode lists from multiple heads */
-	head_base = weston_output_get_first_head(&output->base);
-	assert(head_base);
-	head = to_drm_head(head_base);
-
-	for (i = 0; i < head->connector->count_modes; i++) {
-		mode = drm_output_add_mode(output, &head->connector->modes[i]);
-		if (!mode)
-			return -1;
+	wl_list_for_each(head_base, &output->base.head_list, output_link) {
+		head = to_drm_head(head_base);
+		for (i = 0; i < head->connector->count_modes; i++) {
+			ret = drm_output_try_add_mode(output,
+						&head->connector->modes[i]);
+			if (ret < 0)
+				return -1;
+		}
 	}
 
 	return 0;
-- 
2.13.6



More information about the wayland-devel mailing list