summaryrefslogtreecommitdiff
path: root/hw/xfree86/modes/xf86Crtc.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/xfree86/modes/xf86Crtc.c')
-rw-r--r--hw/xfree86/modes/xf86Crtc.c498
1 files changed, 399 insertions, 99 deletions
diff --git a/hw/xfree86/modes/xf86Crtc.c b/hw/xfree86/modes/xf86Crtc.c
index 266e08195..8c2b24786 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
@@ -693,7 +694,12 @@ xf86CrtcCloseScreen (int index, ScreenPtr screen)
/*
* Called at ScreenInit time to set up
*/
-_X_EXPORT Bool
+_X_EXPORT
+#ifdef RANDR_13_INTERFACE
+int
+#else
+Bool
+#endif
xf86CrtcScreenInit (ScreenPtr screen)
{
ScrnInfoPtr scrn = xf86Screens[screen->myNum];
@@ -726,7 +732,11 @@ xf86CrtcScreenInit (ScreenPtr screen)
config->CloseScreen = screen->CloseScreen;
screen->CloseScreen = xf86CrtcCloseScreen;
+#ifdef RANDR_13_INTERFACE
+ return RANDR_INTERFACE_VERSION;
+#else
return TRUE;
+#endif
}
static DisplayModePtr
@@ -808,7 +818,7 @@ xf86ClosestMode (xf86OutputPtr output,
return target_mode;
}
-static Bool
+static DisplayModePtr
xf86OutputHasPreferredMode (xf86OutputPtr output, int width, int height)
{
DisplayModePtr mode;
@@ -820,9 +830,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
@@ -1261,6 +1283,23 @@ xf86SortModes (DisplayModePtr input)
return output;
}
+static char *
+preferredMode(ScrnInfoPtr pScrn, xf86OutputPtr output)
+{
+ char *preferred_mode = NULL;
+
+ /* Check for a configured preference for a particular mode */
+ preferred_mode = xf86GetOptValString (output->options,
+ OPTION_PREFERRED_MODE);
+ if (preferred_mode)
+ return preferred_mode;
+
+ if (pScrn->display->modes && *pScrn->display->modes)
+ preferred_mode = *pScrn->display->modes;
+
+ return preferred_mode;
+}
+
_X_EXPORT void
xf86ProbeOutputModes (ScrnInfoPtr scrn, int maxX, int maxY)
{
@@ -1445,8 +1484,7 @@ xf86ProbeOutputModes (ScrnInfoPtr scrn, int maxX, int maxY)
output->probed_modes = xf86SortModes (output->probed_modes);
/* Check for a configured preference for a particular mode */
- preferred_mode = xf86GetOptValString (output->options,
- OPTION_PREFERRED_MODE);
+ preferred_mode = preferredMode(scrn, output);
if (preferred_mode)
{
@@ -1561,6 +1599,271 @@ 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(xf86OutputPtr o, DisplayModePtr last, float aspect)
+{
+ DisplayModePtr m = NULL;
+
+ if (!o)
+ return NULL;
+
+ if (!last)
+ m = o->probed_modes;
+ else
+ m = last->next;
+
+ for (; 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 = -1, p;
+ DisplayModePtr mode = NULL, test = NULL, match = NULL;
+
+ nextEnabledOutput(config, enabled, &o);
+ while ((mode = nextAspectMode(config->output[o], 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
*
@@ -1580,14 +1883,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));
@@ -1613,75 +1913,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);
}
/*
@@ -1781,6 +2034,68 @@ xf86InitialConfiguration (ScrnInfoPtr scrn, Bool canGrow)
}
/*
+ * Check the CRTC we're going to map each output to vs. it's current
+ * CRTC. If they don't match, we have to disable the output and the CRTC
+ * since the driver will have to re-route things.
+ */
+static void
+xf86PrepareOutputs (ScrnInfoPtr scrn)
+{
+ xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
+ int o;
+
+ for (o = 0; o < config->num_output; o++) {
+ xf86OutputPtr output = config->output[o];
+#if RANDR_GET_CRTC_INTERFACE
+ /* Disable outputs that are unused or will be re-routed */
+ if (!output->funcs->get_crtc ||
+ output->crtc != (*output->funcs->get_crtc)(output) ||
+ output->crtc == NULL)
+#endif
+ (*output->funcs->dpms)(output, DPMSModeOff);
+ }
+}
+
+static void
+xf86PrepareCrtcs (ScrnInfoPtr scrn)
+{
+ xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
+ int c;
+
+ for (c = 0; c < config->num_crtc; c++) {
+#if RANDR_GET_CRTC_INTERFACE
+ xf86CrtcPtr crtc = config->crtc[c];
+ xf86OutputPtr output = NULL;
+ uint32_t desired_outputs = 0, current_outputs = 0;
+ int o;
+
+ for (o = 0; o < config->num_output; o++) {
+ output = config->output[o];
+ if (output->crtc == crtc)
+ desired_outputs |= (1<<o);
+ /* If we can't tell where it's mapped, force it off */
+ if (!output->funcs->get_crtc) {
+ desired_outputs = 0;
+ break;
+ }
+ if ((*output->funcs->get_crtc)(output) == crtc)
+ current_outputs |= (1<<o);
+ }
+
+ /*
+ * If mappings are different or the CRTC is unused,
+ * we need to disable it
+ */
+ if (desired_outputs != current_outputs ||
+ !desired_outputs)
+ (*crtc->funcs->dpms)(crtc, DPMSModeOff);
+#else
+ (*crtc->funcs->dpms)(crtc, DPMSModeOff);
+#endif
+ }
+}
+
+/*
* Using the desired mode information in each crtc, set
* modes (used in EnterVT functions, or at server startup)
*/
@@ -1789,26 +2104,11 @@ _X_EXPORT Bool
xf86SetDesiredModes (ScrnInfoPtr scrn)
{
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
- int c, o;
-
- /*
- * Turn off everything so mode setting is done
- * with hardware in a consistent state
- */
- for (o = 0; o < config->num_output; o++)
- {
- xf86OutputPtr output = config->output[o];
- (*output->funcs->dpms)(output, DPMSModeOff);
- }
+ int c;
- for (c = 0; c < config->num_crtc; c++)
- {
- xf86CrtcPtr crtc = config->crtc[c];
+ xf86PrepareOutputs(scrn);
+ xf86PrepareCrtcs(scrn);
- crtc->funcs->dpms(crtc, DPMSModeOff);
- memset(&crtc->mode, 0, sizeof(crtc->mode));
- }
-
for (c = 0; c < config->num_crtc; c++)
{
xf86CrtcPtr crtc = config->crtc[c];
@@ -1987,7 +2287,7 @@ xf86SetSingleMode (ScrnInfoPtr pScrn, DisplayModePtr desired, Rotation rotation)
}
}
xf86DisableUnusedFunctions(pScrn);
-#if RANDR_12_INTERFACE
+#ifdef RANDR_12_INTERFACE
xf86RandR12TellChanged (pScrn->pScreen);
#endif
return ok;