compositor-drm: combine mode list from heads

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@collabora.co.uk>
Reviewed-by: Daniel Stone <daniels@collabora.com>
Acked-by: Derek Foreman <derekf@osg.samsung.com>
dev
Pekka Paalanen 7 years ago
parent 456dc73777
commit f005f25d62
  1. 114
      libweston/compositor-drm.c

@ -4428,6 +4428,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.
@ -4444,22 +4539,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;

Loading…
Cancel
Save