summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Jackson <ajax@redhat.com>2008-03-04 11:38:34 -0500
committerAdam Jackson <ajax@redhat.com>2008-03-04 11:38:34 -0500
commit27e7dacbf7ef17712be31ff90f98ee3a5c5cf909 (patch)
tree38c4fb6225422091865691e53a0e98cf331ab60c
parent95df04b744c6a3498a9a9e2ea9bb03ee780e60f8 (diff)
Make xf86InitialConfiguration slightly smarter.
Old heuristic was to find the first monitor that expressed a preference, then attempt to get all other monitors to agree. This doesn't work particularly well when the two sets of modes don't precisely intersect, you get overlapping-but-not-identical output geometry and things go wrong. New heuristic is: - Exact user preference, if given - Exact output preference, if the same for all outputs - Best (largest) mode of modes common to all outputs: - with the same aspect ratio as all outputs (may be NULL) - with 4:3 aspect ratio - Then the old heuristic to try to get something lit Note that it is simply not doable to have a reliable initial output guess if you insist on trying to clone all outputs together. It's far too easy to end up with displays that simply don't have modes in common. We need to switch to right-of placement someday, once we're not limited to CRTC size limits and we have working multi-GPU in RANDR.
-rw-r--r--hw/xfree86/modes/xf86Crtc.c376
1 files changed, 299 insertions, 77 deletions
diff --git a/hw/xfree86/modes/xf86Crtc.c b/hw/xfree86/modes/xf86Crtc.c
index 14b60496a..4d5d7b848 100644
--- a/hw/xfree86/modes/xf86Crtc.c
+++ b/hw/xfree86/modes/xf86Crtc.c
@@ -1,5 +1,6 @@
/*
* Copyright © 2006 Keith Packard
+ * Copyright © 2008 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
@@ -808,7 +809,7 @@ xf86ClosestMode (xf86OutputPtr output,
return target_mode;
}
-static Bool
+static DisplayModePtr
xf86OutputHasPreferredMode (xf86OutputPtr output, int width, int height)
{
DisplayModePtr mode;
@@ -820,9 +821,21 @@ xf86OutputHasPreferredMode (xf86OutputPtr output, int width, int height)
continue;
if (mode->type & M_T_PREFERRED)
- return TRUE;
+ return mode;
}
- return FALSE;
+ return NULL;
+}
+
+static DisplayModePtr
+xf86OutputHasUserPreferredMode (xf86OutputPtr output)
+{
+ DisplayModePtr mode, first = output->probed_modes;
+
+ for (mode = first; mode && mode->next != first; mode = mode->next)
+ if (mode->type & M_T_USERPREF)
+ return mode;
+
+ return NULL;
}
static int
@@ -1577,6 +1590,265 @@ xf86SetScrnInfoModes (ScrnInfoPtr scrn)
scrn->currentMode = scrn->modes;
}
+static void
+xf86EnableOutputs(ScrnInfoPtr scrn, xf86CrtcConfigPtr config, Bool *enabled)
+{
+ Bool any_enabled = FALSE;
+ int o;
+
+ for (o = 0; o < config->num_output; o++)
+ any_enabled |= enabled[o] = xf86OutputEnabled(config->output[o], TRUE);
+
+ if (!any_enabled) {
+ xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+ "No outputs definitely connected, trying again...\n");
+
+ for (o = 0; o < config->num_output; o++)
+ enabled[o] = xf86OutputEnabled(config->output[o], FALSE);
+ }
+}
+
+static Bool
+nextEnabledOutput(xf86CrtcConfigPtr config, Bool *enabled, int *index)
+{
+ int o = *index;
+
+ for (o++; o < config->num_output; o++) {
+ if (enabled[o]) {
+ *index = o;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static Bool
+xf86TargetExact(ScrnInfoPtr scrn, xf86CrtcConfigPtr config,
+ DisplayModePtr *modes, Bool *enabled,
+ int width, int height)
+{
+ int o;
+ int pref_width = 0, pref_height = 0;
+ DisplayModePtr *preferred;
+ Bool ret = FALSE;
+
+ preferred = xnfcalloc(config->num_output, sizeof(DisplayModePtr));
+
+ /* Find all the preferred modes; fail if any outputs lack them */
+ for (o = -1; nextEnabledOutput(config, enabled, &o); ) {
+ preferred[o] =
+ xf86OutputHasPreferredMode(config->output[o], width, height);
+
+ if (!preferred[o])
+ goto out;
+ }
+
+ /* check that they're all the same size */
+ for (o = -1; nextEnabledOutput(config, enabled, &o); ) {
+ Rotation r = config->output[o]->initial_rotation;
+ if (!pref_width) {
+ pref_width = xf86ModeWidth(preferred[o], r);
+ pref_height = xf86ModeHeight(preferred[o], r);
+ } else {
+ if (pref_width != xf86ModeWidth(preferred[o], r))
+ goto out;
+ if (pref_height != xf86ModeHeight(preferred[o], r))
+ goto out;
+ }
+ }
+
+ /* oh good, they match. stash the selected modes and return. */
+ memcpy(modes, preferred, config->num_output * sizeof(DisplayModePtr));
+ ret = TRUE;
+
+out:
+ xfree(preferred);
+ return ret;
+}
+
+static Bool
+aspectMatch(float a, float b)
+{
+ return fabs(1 - (a / b)) < 0.05;
+}
+
+static DisplayModePtr
+nextAspectMode(DisplayModePtr start, float aspect)
+{
+ DisplayModePtr m = start;
+
+ for (m = m->next; m; m = m->next)
+ if (aspectMatch(aspect, (float)m->HDisplay / (float)m->VDisplay))
+ return m;
+
+ return NULL;
+}
+
+static DisplayModePtr
+bestModeForAspect(xf86CrtcConfigPtr config, Bool *enabled, float aspect)
+{
+ int o, p;
+ DisplayModePtr mode, test = NULL, match = NULL;
+
+ for (o = -1; nextEnabledOutput(config, enabled, &o); ) {
+ mode = config->output[o]->probed_modes;
+ while ((mode = nextAspectMode(mode, aspect))) {
+ for (p = o; nextEnabledOutput(config, enabled, &p); ) {
+ test = xf86OutputFindClosestMode(config->output[p], mode);
+ if (!test)
+ break;
+ if (test->HDisplay != mode->HDisplay ||
+ test->VDisplay != mode->VDisplay) {
+ test = NULL;
+ break;
+ }
+ }
+
+ /* if we didn't match it on all outputs, try the next one */
+ if (!test)
+ continue;
+
+ /* if it's bigger than the last one, save it */
+ if (!match || (test->HDisplay > match->HDisplay))
+ match = test;
+ }
+ }
+
+ /* return the biggest one found */
+ return match;
+}
+
+static DisplayModePtr
+biggestMode(DisplayModePtr a, DisplayModePtr b)
+{
+ int A, B;
+
+ if (!a)
+ return b;
+ if (!b)
+ return a;
+
+ A = a->HDisplay * a->VDisplay;
+ B = b->HDisplay * b->VDisplay;
+
+ if (A > B)
+ return a;
+
+ return b;
+}
+
+static Bool
+xf86TargetAspect(ScrnInfoPtr scrn, xf86CrtcConfigPtr config,
+ DisplayModePtr *modes, Bool *enabled,
+ int width, int height)
+{
+ int o;
+ float aspect = 0.0, *aspects;
+ xf86OutputPtr output;
+ Bool ret = FALSE;
+ DisplayModePtr guess = NULL, aspect_guess = NULL, base_guess = NULL;
+
+ aspects = xnfcalloc(config->num_output, sizeof(float));
+
+ /* collect the aspect ratios */
+ for (o = -1; nextEnabledOutput(config, enabled, &o); ) {
+ output = config->output[o];
+ if (output->mm_height)
+ aspects[o] = (float)output->mm_width / (float)output->mm_height;
+ else
+ aspects[o] = 4.0 / 3.0;
+ }
+
+ /* check that they're all the same */
+ for (o = -1; nextEnabledOutput(config, enabled, &o); ) {
+ output = config->output[o];
+ if (!aspect) {
+ aspect = aspects[o];
+ } else if (!aspectMatch(aspect, aspects[o])) {
+ goto no_aspect_match;
+ }
+ }
+
+ /* if they're all 4:3, just skip ahead and save effort */
+ if (!aspectMatch(aspect, 4.0/3.0))
+ aspect_guess = bestModeForAspect(config, enabled, aspect);
+
+no_aspect_match:
+ base_guess = bestModeForAspect(config, enabled, 4.0/3.0);
+
+ guess = biggestMode(base_guess, aspect_guess);
+
+ if (!guess)
+ goto out;
+
+ /* found a mode that works everywhere, now apply it */
+ for (o = -1; nextEnabledOutput(config, enabled, &o); ) {
+ modes[o] = xf86OutputFindClosestMode(config->output[o], guess);
+ }
+ ret = TRUE;
+
+out:
+ xfree(aspects);
+ return ret;
+}
+
+static Bool
+xf86TargetFallback(ScrnInfoPtr scrn, xf86CrtcConfigPtr config,
+ DisplayModePtr *modes, Bool *enabled,
+ int width, int height)
+{
+ DisplayModePtr target_mode = NULL;
+ Rotation target_rotation = RR_Rotate_0;
+ DisplayModePtr default_mode;
+ int default_preferred, target_preferred = 0, o;
+
+ /* User preferred > preferred > other modes */
+ for (o = -1; nextEnabledOutput(config, enabled, &o); ) {
+ default_mode = xf86DefaultMode (config->output[o], width, height);
+ if (!default_mode)
+ continue;
+
+ default_preferred = (((default_mode->type & M_T_PREFERRED) != 0) +
+ ((default_mode->type & M_T_USERPREF) != 0));
+
+ if (default_preferred > target_preferred || !target_mode) {
+ target_mode = default_mode;
+ target_preferred = default_preferred;
+ target_rotation = config->output[o]->initial_rotation;
+ config->compat_output = o;
+ }
+ }
+
+ if (target_mode)
+ modes[config->compat_output] = target_mode;
+
+ /* Fill in other output modes */
+ for (o = -1; nextEnabledOutput(config, enabled, &o); ) {
+ if (!modes[o])
+ modes[o] = xf86ClosestMode(config->output[o], target_mode,
+ target_rotation, width, height);
+ }
+
+ return (target_mode != NULL);
+}
+
+static Bool
+xf86TargetUserpref(ScrnInfoPtr scrn, xf86CrtcConfigPtr config,
+ DisplayModePtr *modes, Bool *enabled,
+ int width, int height)
+{
+ int o;
+
+ for (o = -1; nextEnabledOutput(config, enabled, &o); )
+ if (xf86OutputHasUserPreferredMode(config->output[o]))
+ return
+ xf86TargetFallback(scrn, config, modes, enabled, width, height);
+
+ return FALSE;
+}
+
+
/**
* Construct default screen configuration
*
@@ -1596,14 +1868,11 @@ xf86InitialConfiguration (ScrnInfoPtr scrn, Bool canGrow)
{
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
int o, c;
- DisplayModePtr target_mode = NULL;
- int target_preferred = 0;
- Rotation target_rotation = RR_Rotate_0;
xf86CrtcPtr *crtcs;
DisplayModePtr *modes;
- Bool *enabled, any_enabled = FALSE;
- int width;
- int height;
+ Bool *enabled;
+ int width, height;
+ int i = scrn->scrnIndex;
/* Set up the device options */
config->options = xnfalloc (sizeof (xf86DeviceOptions));
@@ -1629,75 +1898,28 @@ xf86InitialConfiguration (ScrnInfoPtr scrn, Bool canGrow)
modes = xnfcalloc (config->num_output, sizeof (DisplayModePtr));
enabled = xnfcalloc (config->num_output, sizeof (Bool));
- for (o = 0; o < config->num_output; o++)
- {
- xf86OutputPtr output = config->output[o];
-
- modes[o] = NULL;
- any_enabled |= (enabled[o] = xf86OutputEnabled (output, TRUE));
- }
-
- if (!any_enabled)
- {
- xf86DrvMsg (scrn->scrnIndex, X_WARNING,
- "No outputs definitely connected, trying again...\n");
-
- for (o = 0; o < config->num_output; o++)
- {
- xf86OutputPtr output = config->output[o];
-
- modes[o] = NULL;
- enabled[o] = xf86OutputEnabled (output, FALSE);
- }
- }
-
- /*
- * User preferred > preferred > other modes
- */
- for (o = 0; o < config->num_output; o++)
- {
- xf86OutputPtr output = config->output[o];
- DisplayModePtr default_mode;
- int default_preferred;
+ xf86EnableOutputs(scrn, config, enabled);
+
+ if (xf86TargetUserpref(scrn, config, modes, enabled, width, height))
+ xf86DrvMsg(i, X_INFO, "Using user preference for initial modes\n");
+ else if (xf86TargetExact(scrn, config, modes, enabled, width, height))
+ xf86DrvMsg(i, X_INFO, "Using exact sizes for initial modes\n");
+ else if (xf86TargetAspect(scrn, config, modes, enabled, width, height))
+ xf86DrvMsg(i, X_INFO, "Using fuzzy aspect match for initial modes\n");
+ else if (xf86TargetFallback(scrn, config, modes, enabled, width, height))
+ xf86DrvMsg(i, X_INFO, "Using sloppy heuristic for initial modes\n");
+ else
+ xf86DrvMsg(i, X_WARNING, "Unable to find initial modes\n");
- if (!enabled[o])
- continue;
- default_mode = xf86DefaultMode (output, width, height);
- if (!default_mode)
- continue;
- default_preferred = (((default_mode->type & M_T_PREFERRED) != 0) +
- ((default_mode->type & M_T_USERPREF) != 0));
- if (default_preferred > target_preferred || !target_mode)
- {
- target_mode = default_mode;
- target_preferred = default_preferred;
- target_rotation = output->initial_rotation;
- config->compat_output = o;
- }
- }
- if (target_mode)
- modes[config->compat_output] = target_mode;
- /*
- * Fill in other output modes
- */
- for (o = 0; o < config->num_output; o++)
- {
- xf86OutputPtr output = config->output[o];
-
- if (enabled[o])
- {
- if (!modes[o])
- modes[o] = xf86ClosestMode (output, target_mode,
- target_rotation, width, height);
- if (!modes[o])
- xf86DrvMsg (scrn->scrnIndex, X_ERROR,
- "Output %s enabled but has no modes\n",
- output->name);
- else
- xf86DrvMsg (scrn->scrnIndex, X_INFO,
- "Output %s using initial mode %s\n",
- output->name, modes[o]->name);
- }
+ for (o = -1; nextEnabledOutput(config, enabled, &o); ) {
+ if (!modes[o])
+ xf86DrvMsg (scrn->scrnIndex, X_ERROR,
+ "Output %s enabled but has no modes\n",
+ config->output[o]->name);
+ else
+ xf86DrvMsg (scrn->scrnIndex, X_INFO,
+ "Output %s using initial mode %s\n",
+ config->output[o]->name, modes[o]->name);
}
/*