1/*
2 * Copyright © 2006 Keith Packard
3 * Copyright © 2008 Red Hat, Inc.
4 *
5 * Permission to use, copy, modify, distribute, and sell this software and its
6 * documentation for any purpose is hereby granted without fee, provided that
7 * the above copyright notice appear in all copies and that both that copyright
8 * notice and this permission notice appear in supporting documentation, and
9 * that the name of the copyright holders not be used in advertising or
10 * publicity pertaining to distribution of the software without specific,
11 * written prior permission.  The copyright holders make no representations
12 * about the suitability of this software for any purpose.  It is provided "as
13 * is" without express or implied warranty.
14 *
15 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
17 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
19 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
20 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
21 * OF THIS SOFTWARE.
22 */
23
24#ifdef HAVE_XORG_CONFIG_H
25#include <xorg-config.h>
26#else
27#ifdef HAVE_CONFIG_H
28#include <config.h>
29#endif
30#endif
31
32#include <stddef.h>
33#include <string.h>
34#include <stdio.h>
35
36#include "xf86.h"
37#include "xf86DDC.h"
38#include "xf86Crtc.h"
39#include "xf86Modes.h"
40#include "xf86Priv.h"
41#include "xf86RandR12.h"
42#include "X11/extensions/render.h"
43#include "X11/extensions/dpmsconst.h"
44#include "X11/Xatom.h"
45#include "picturestr.h"
46
47#include "xf86xv.h"
48
49#define NO_OUTPUT_DEFAULT_WIDTH 1024
50#define NO_OUTPUT_DEFAULT_HEIGHT 768
51/*
52 * Initialize xf86CrtcConfig structure
53 */
54
55int xf86CrtcConfigPrivateIndex = -1;
56
57void
58xf86CrtcConfigInit (ScrnInfoPtr scrn,
59		    const xf86CrtcConfigFuncsRec *funcs)
60{
61    xf86CrtcConfigPtr	config;
62
63    if (xf86CrtcConfigPrivateIndex == -1)
64	xf86CrtcConfigPrivateIndex = xf86AllocateScrnInfoPrivateIndex();
65    config = xnfcalloc (1, sizeof (xf86CrtcConfigRec));
66
67    config->funcs = funcs;
68
69    scrn->privates[xf86CrtcConfigPrivateIndex].ptr = config;
70}
71
72void
73xf86CrtcSetSizeRange (ScrnInfoPtr scrn,
74		      int minWidth, int minHeight,
75		      int maxWidth, int maxHeight)
76{
77    xf86CrtcConfigPtr	config = XF86_CRTC_CONFIG_PTR(scrn);
78
79    config->minWidth = minWidth;
80    config->minHeight = minHeight;
81    config->maxWidth = maxWidth;
82    config->maxHeight = maxHeight;
83}
84
85/*
86 * Crtc functions
87 */
88xf86CrtcPtr
89xf86CrtcCreate (ScrnInfoPtr		scrn,
90		const xf86CrtcFuncsRec	*funcs)
91{
92    xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
93    xf86CrtcPtr		crtc, *crtcs;
94
95    crtc = calloc(sizeof (xf86CrtcRec), 1);
96    if (!crtc)
97	return NULL;
98    crtc->version = XF86_CRTC_VERSION;
99    crtc->scrn = scrn;
100    crtc->funcs = funcs;
101#ifdef RANDR_12_INTERFACE
102    crtc->randr_crtc = NULL;
103#endif
104    crtc->rotation = RR_Rotate_0;
105    crtc->desiredRotation = RR_Rotate_0;
106    pixman_transform_init_identity (&crtc->crtc_to_framebuffer);
107    pixman_f_transform_init_identity (&crtc->f_crtc_to_framebuffer);
108    pixman_f_transform_init_identity (&crtc->f_framebuffer_to_crtc);
109    crtc->filter = NULL;
110    crtc->params = NULL;
111    crtc->nparams = 0;
112    crtc->filter_width = 0;
113    crtc->filter_height = 0;
114    crtc->transform_in_use = FALSE;
115    crtc->transformPresent = FALSE;
116    crtc->desiredTransformPresent = FALSE;
117    memset (&crtc->bounds, '\0', sizeof (crtc->bounds));
118
119    /* Preallocate gamma at a sensible size. */
120    crtc->gamma_size = 256;
121    crtc->gamma_red = malloc(3 * crtc->gamma_size * sizeof (CARD16));
122    if (!crtc->gamma_red) {
123	free(crtc);
124	return NULL;
125    }
126    crtc->gamma_green = crtc->gamma_red + crtc->gamma_size;
127    crtc->gamma_blue = crtc->gamma_green + crtc->gamma_size;
128
129    if (xf86_config->crtc)
130	crtcs = realloc(xf86_config->crtc,
131			  (xf86_config->num_crtc + 1) * sizeof (xf86CrtcPtr));
132    else
133	crtcs = malloc((xf86_config->num_crtc + 1) * sizeof (xf86CrtcPtr));
134    if (!crtcs)
135    {
136	free(crtc->gamma_red);
137	free(crtc);
138	return NULL;
139    }
140    xf86_config->crtc = crtcs;
141    xf86_config->crtc[xf86_config->num_crtc++] = crtc;
142    return crtc;
143}
144
145void
146xf86CrtcDestroy (xf86CrtcPtr crtc)
147{
148    xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
149    int			c;
150
151    (*crtc->funcs->destroy) (crtc);
152    for (c = 0; c < xf86_config->num_crtc; c++)
153	if (xf86_config->crtc[c] == crtc)
154	{
155	    memmove (&xf86_config->crtc[c],
156		     &xf86_config->crtc[c+1],
157		     ((xf86_config->num_crtc - (c + 1)) * sizeof(void*)));
158	    xf86_config->num_crtc--;
159	    break;
160	}
161    free(crtc->params);
162    free(crtc->gamma_red);
163    free(crtc);
164}
165
166
167/**
168 * Return whether any outputs are connected to the specified pipe
169 */
170
171Bool
172xf86CrtcInUse (xf86CrtcPtr crtc)
173{
174    ScrnInfoPtr		pScrn = crtc->scrn;
175    xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
176    int			o;
177
178    for (o = 0; o < xf86_config->num_output; o++)
179	if (xf86_config->output[o]->crtc == crtc)
180	    return TRUE;
181    return FALSE;
182}
183
184void
185xf86CrtcSetScreenSubpixelOrder (ScreenPtr pScreen)
186{
187    int			subpixel_order = SubPixelUnknown;
188    Bool		has_none = FALSE;
189    ScrnInfoPtr		scrn = xf86Screens[pScreen->myNum];
190    xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
191    int			c, o;
192
193    for (c = 0; c < xf86_config->num_crtc; c++)
194    {
195	xf86CrtcPtr crtc = xf86_config->crtc[c];
196
197	for (o = 0; o < xf86_config->num_output; o++)
198	{
199	    xf86OutputPtr   output = xf86_config->output[o];
200
201	    if (output->crtc == crtc)
202	    {
203		switch (output->subpixel_order) {
204		case SubPixelNone:
205		    has_none = TRUE;
206		    break;
207		case SubPixelUnknown:
208		    break;
209		default:
210		    subpixel_order = output->subpixel_order;
211		    break;
212		}
213	    }
214	    if (subpixel_order != SubPixelUnknown)
215		break;
216	}
217	if (subpixel_order != SubPixelUnknown)
218	{
219	    static const int circle[4] = {
220		SubPixelHorizontalRGB,
221		SubPixelVerticalRGB,
222		SubPixelHorizontalBGR,
223		SubPixelVerticalBGR,
224	    };
225	    int	rotate;
226	    int c;
227	    for (rotate = 0; rotate < 4; rotate++)
228		if (crtc->rotation & (1 << rotate))
229		    break;
230	    for (c = 0; c < 4; c++)
231		if (circle[c] == subpixel_order)
232		    break;
233	    c = (c + rotate) & 0x3;
234	    if ((crtc->rotation & RR_Reflect_X) && !(c & 1))
235		c ^= 2;
236	    if ((crtc->rotation & RR_Reflect_Y) && (c & 1))
237		c ^= 2;
238	    subpixel_order = circle[c];
239	    break;
240	}
241    }
242    if (subpixel_order == SubPixelUnknown && has_none)
243	subpixel_order = SubPixelNone;
244    PictureSetSubpixelOrder (pScreen, subpixel_order);
245}
246
247/**
248 * Sets the given video mode on the given crtc
249 */
250Bool
251xf86CrtcSetModeTransform (xf86CrtcPtr crtc, DisplayModePtr mode, Rotation rotation,
252			  RRTransformPtr transform, int x, int y)
253{
254    ScrnInfoPtr		scrn = crtc->scrn;
255    xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
256    int			i;
257    Bool		ret = FALSE;
258    Bool		didLock = FALSE;
259    DisplayModePtr	adjusted_mode;
260    DisplayModeRec	saved_mode;
261    int			saved_x, saved_y;
262    Rotation		saved_rotation;
263    RRTransformRec	saved_transform;
264    Bool		saved_transform_present;
265
266    crtc->enabled = xf86CrtcInUse (crtc);
267
268    /* We only hit this if someone explicitly sends a "disabled" modeset. */
269    if (!crtc->enabled)
270    {
271	/* Check everything for stuff that should be off. */
272	xf86DisableUnusedFunctions(scrn);
273	return TRUE;
274    }
275
276    adjusted_mode = xf86DuplicateMode(mode);
277
278
279    saved_mode = crtc->mode;
280    saved_x = crtc->x;
281    saved_y = crtc->y;
282    saved_rotation = crtc->rotation;
283    if (crtc->transformPresent) {
284	RRTransformInit (&saved_transform);
285	RRTransformCopy (&saved_transform, &crtc->transform);
286    }
287    saved_transform_present = crtc->transformPresent;
288
289    /* Update crtc values up front so the driver can rely on them for mode
290     * setting.
291     */
292    crtc->mode = *mode;
293    crtc->x = x;
294    crtc->y = y;
295    crtc->rotation = rotation;
296    if (transform) {
297	RRTransformCopy (&crtc->transform, transform);
298	crtc->transformPresent = TRUE;
299    } else
300	crtc->transformPresent = FALSE;
301
302    if (crtc->funcs->set_mode_major) {
303	ret = crtc->funcs->set_mode_major(crtc, mode, rotation, x, y);
304	goto done;
305    }
306
307    didLock = crtc->funcs->lock (crtc);
308    /* Pass our mode to the outputs and the CRTC to give them a chance to
309     * adjust it according to limitations or output properties, and also
310     * a chance to reject the mode entirely.
311     */
312    for (i = 0; i < xf86_config->num_output; i++) {
313	xf86OutputPtr output = xf86_config->output[i];
314
315	if (output->crtc != crtc)
316	    continue;
317
318	if (!output->funcs->mode_fixup(output, mode, adjusted_mode)) {
319	    goto done;
320	}
321    }
322
323    if (!crtc->funcs->mode_fixup(crtc, mode, adjusted_mode)) {
324	goto done;
325    }
326
327    if (!xf86CrtcRotate (crtc))
328	goto done;
329
330    /* Prepare the outputs and CRTCs before setting the mode. */
331    for (i = 0; i < xf86_config->num_output; i++) {
332	xf86OutputPtr output = xf86_config->output[i];
333
334	if (output->crtc != crtc)
335	    continue;
336
337	/* Disable the output as the first thing we do. */
338	output->funcs->prepare(output);
339    }
340
341    crtc->funcs->prepare(crtc);
342
343    /* Set up the DPLL and any output state that needs to adjust or depend
344     * on the DPLL.
345     */
346    crtc->funcs->mode_set(crtc, mode, adjusted_mode, crtc->x, crtc->y);
347    for (i = 0; i < xf86_config->num_output; i++)
348    {
349	xf86OutputPtr output = xf86_config->output[i];
350	if (output->crtc == crtc)
351	    output->funcs->mode_set(output, mode, adjusted_mode);
352    }
353
354    /* Only upload when needed, to avoid unneeded delays. */
355    if (!crtc->active && crtc->funcs->gamma_set)
356	crtc->funcs->gamma_set(crtc, crtc->gamma_red, crtc->gamma_green,
357                                            crtc->gamma_blue, crtc->gamma_size);
358
359    /* Now, enable the clocks, plane, pipe, and outputs that we set up. */
360    crtc->funcs->commit(crtc);
361    for (i = 0; i < xf86_config->num_output; i++)
362    {
363	xf86OutputPtr output = xf86_config->output[i];
364	if (output->crtc == crtc)
365	    output->funcs->commit(output);
366    }
367
368    ret = TRUE;
369
370done:
371    if (ret) {
372	crtc->active = TRUE;
373	if (scrn->pScreen)
374	    xf86CrtcSetScreenSubpixelOrder (scrn->pScreen);
375	if (scrn->ModeSet)
376	    scrn->ModeSet(scrn);
377    } else {
378	crtc->x = saved_x;
379	crtc->y = saved_y;
380	crtc->rotation = saved_rotation;
381	crtc->mode = saved_mode;
382	if (saved_transform_present)
383	    RRTransformCopy (&crtc->transform, &saved_transform);
384	crtc->transformPresent = saved_transform_present;
385    }
386
387    free(adjusted_mode->name);
388    free(adjusted_mode);
389
390    if (didLock)
391	crtc->funcs->unlock (crtc);
392
393    return ret;
394}
395
396/**
397 * Sets the given video mode on the given crtc, but without providing
398 * a transform
399 */
400Bool
401xf86CrtcSetMode (xf86CrtcPtr crtc, DisplayModePtr mode, Rotation rotation,
402		 int x, int y)
403{
404    return xf86CrtcSetModeTransform (crtc, mode, rotation, NULL, x, y);
405}
406
407/**
408 * Pans the screen, does not change the mode
409 */
410void
411xf86CrtcSetOrigin (xf86CrtcPtr crtc, int x, int y)
412{
413    ScrnInfoPtr scrn = crtc->scrn;
414
415    crtc->x = x;
416    crtc->y = y;
417    if (crtc->funcs->set_origin) {
418	if (!xf86CrtcRotate (crtc))
419	    return;
420	crtc->funcs->set_origin (crtc, x, y);
421	if (scrn->ModeSet)
422	    scrn->ModeSet(scrn);
423    }
424    else
425	xf86CrtcSetMode (crtc, &crtc->mode, crtc->rotation, x, y);
426}
427
428/*
429 * Output functions
430 */
431
432extern XF86ConfigPtr xf86configptr;
433
434typedef enum {
435    OPTION_PREFERRED_MODE,
436    OPTION_POSITION,
437    OPTION_BELOW,
438    OPTION_RIGHT_OF,
439    OPTION_ABOVE,
440    OPTION_LEFT_OF,
441    OPTION_ENABLE,
442    OPTION_DISABLE,
443    OPTION_MIN_CLOCK,
444    OPTION_MAX_CLOCK,
445    OPTION_IGNORE,
446    OPTION_ROTATE,
447    OPTION_PANNING,
448    OPTION_PRIMARY,
449    OPTION_DEFAULT_MODES,
450} OutputOpts;
451
452static OptionInfoRec xf86OutputOptions[] = {
453    {OPTION_PREFERRED_MODE, "PreferredMode",	OPTV_STRING,  {0}, FALSE },
454    {OPTION_POSITION,	    "Position",		OPTV_STRING,  {0}, FALSE },
455    {OPTION_BELOW,	    "Below",		OPTV_STRING,  {0}, FALSE },
456    {OPTION_RIGHT_OF,	    "RightOf",		OPTV_STRING,  {0}, FALSE },
457    {OPTION_ABOVE,	    "Above",		OPTV_STRING,  {0}, FALSE },
458    {OPTION_LEFT_OF,	    "LeftOf",		OPTV_STRING,  {0}, FALSE },
459    {OPTION_ENABLE,	    "Enable",		OPTV_BOOLEAN, {0}, FALSE },
460    {OPTION_DISABLE,	    "Disable",		OPTV_BOOLEAN, {0}, FALSE },
461    {OPTION_MIN_CLOCK,	    "MinClock",		OPTV_FREQ,    {0}, FALSE },
462    {OPTION_MAX_CLOCK,	    "MaxClock",		OPTV_FREQ,    {0}, FALSE },
463    {OPTION_IGNORE,	    "Ignore",		OPTV_BOOLEAN, {0}, FALSE },
464    {OPTION_ROTATE,	    "Rotate",		OPTV_STRING,  {0}, FALSE },
465    {OPTION_PANNING,	    "Panning",		OPTV_STRING,  {0}, FALSE },
466    {OPTION_PRIMARY,	    "Primary",		OPTV_BOOLEAN, {0}, FALSE },
467    {OPTION_DEFAULT_MODES,  "DefaultModes",	OPTV_BOOLEAN, {0}, FALSE },
468    {-1,		    NULL,		OPTV_NONE,    {0}, FALSE },
469};
470
471enum {
472    OPTION_MODEDEBUG,
473};
474
475static OptionInfoRec xf86DeviceOptions[] = {
476    {OPTION_MODEDEBUG,	    "ModeDebug",	OPTV_BOOLEAN,  {0}, FALSE },
477    {-1,		    NULL,		OPTV_NONE,    {0}, FALSE },
478};
479
480static void
481xf86OutputSetMonitor (xf86OutputPtr output)
482{
483    char    *option_name;
484    char    *monitor;
485
486    if (!output->name)
487	return;
488
489    free(output->options);
490
491    output->options = xnfalloc (sizeof (xf86OutputOptions));
492    memcpy (output->options, xf86OutputOptions, sizeof (xf86OutputOptions));
493
494    XNFasprintf(&option_name, "monitor-%s", output->name);
495    monitor = xf86findOptionValue (output->scrn->options, option_name);
496    if (!monitor)
497	monitor = output->name;
498    else
499	xf86MarkOptionUsedByName (output->scrn->options, option_name);
500    free(option_name);
501    output->conf_monitor = xf86findMonitor (monitor,
502					    xf86configptr->conf_monitor_lst);
503    /*
504     * Find the monitor section of the screen and use that
505     */
506    if (!output->conf_monitor && output->use_screen_monitor)
507	output->conf_monitor = xf86findMonitor (output->scrn->monitor->id,
508						xf86configptr->conf_monitor_lst);
509    if (output->conf_monitor)
510    {
511	xf86DrvMsg (output->scrn->scrnIndex, X_INFO,
512		    "Output %s using monitor section %s\n",
513		    output->name, output->conf_monitor->mon_identifier);
514	xf86ProcessOptions (output->scrn->scrnIndex,
515			    output->conf_monitor->mon_option_lst,
516			    output->options);
517    }
518    else
519	xf86DrvMsg (output->scrn->scrnIndex, X_INFO,
520		    "Output %s has no monitor section\n",
521		    output->name);
522}
523
524static Bool
525xf86OutputEnabled (xf86OutputPtr output, Bool strict)
526{
527    Bool    enable, disable;
528
529    /* check to see if this output was enabled in the config file */
530    if (xf86GetOptValBool (output->options, OPTION_ENABLE, &enable) && enable)
531    {
532	xf86DrvMsg (output->scrn->scrnIndex, X_INFO,
533		    "Output %s enabled by config file\n", output->name);
534	return TRUE;
535    }
536    /* or if this output was disabled in the config file */
537    if (xf86GetOptValBool (output->options, OPTION_DISABLE, &disable) && disable)
538    {
539	xf86DrvMsg (output->scrn->scrnIndex, X_INFO,
540		    "Output %s disabled by config file\n", output->name);
541	return FALSE;
542    }
543
544    /* If not, try to only light up the ones we know are connected */
545    if (strict) {
546	enable = output->status == XF86OutputStatusConnected;
547    }
548    /* But if that fails, try to light up even outputs we're unsure of */
549    else {
550	enable = output->status != XF86OutputStatusDisconnected;
551    }
552
553    xf86DrvMsg (output->scrn->scrnIndex, X_INFO,
554    	    "Output %s %sconnected\n", output->name, enable ? "" : "dis");
555    return enable;
556}
557
558static Bool
559xf86OutputIgnored (xf86OutputPtr    output)
560{
561    return xf86ReturnOptValBool (output->options, OPTION_IGNORE, FALSE);
562}
563
564static char *direction[4] = {
565    "normal",
566    "left",
567    "inverted",
568    "right"
569};
570
571static Rotation
572xf86OutputInitialRotation (xf86OutputPtr output)
573{
574    char    *rotate_name = xf86GetOptValString (output->options,
575						OPTION_ROTATE);
576    int	    i;
577
578    if (!rotate_name) {
579	if (output->initial_rotation)
580	    return output->initial_rotation;
581	return RR_Rotate_0;
582    }
583
584    for (i = 0; i < 4; i++)
585	if (xf86nameCompare (direction[i], rotate_name) == 0)
586	    return 1 << i;
587    return RR_Rotate_0;
588}
589
590xf86OutputPtr
591xf86OutputCreate (ScrnInfoPtr		    scrn,
592		  const xf86OutputFuncsRec  *funcs,
593		  const char		    *name)
594{
595    xf86OutputPtr	output, *outputs;
596    xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
597    int			len;
598    Bool		primary;
599
600    if (name)
601	len = strlen (name) + 1;
602    else
603	len = 0;
604
605    output = calloc(sizeof (xf86OutputRec) + len, 1);
606    if (!output)
607	return NULL;
608    output->scrn = scrn;
609    output->funcs = funcs;
610    if (name)
611    {
612	output->name = (char *) (output + 1);
613	strcpy (output->name, name);
614    }
615    output->subpixel_order = SubPixelUnknown;
616    /*
617     * Use the old per-screen monitor section for the first output
618     */
619    output->use_screen_monitor = (xf86_config->num_output == 0);
620#ifdef RANDR_12_INTERFACE
621    output->randr_output = NULL;
622#endif
623    if (name)
624    {
625	xf86OutputSetMonitor (output);
626	if (xf86OutputIgnored (output))
627	{
628	    free(output);
629	    return FALSE;
630	}
631    }
632
633
634    if (xf86_config->output)
635	outputs = realloc(xf86_config->output,
636			  (xf86_config->num_output + 1) * sizeof (xf86OutputPtr));
637    else
638	outputs = malloc((xf86_config->num_output + 1) * sizeof (xf86OutputPtr));
639    if (!outputs)
640    {
641	free(output);
642	return NULL;
643    }
644
645    xf86_config->output = outputs;
646
647    if (xf86GetOptValBool (output->options, OPTION_PRIMARY, &primary) && primary)
648    {
649	memmove(xf86_config->output + 1, xf86_config->output,
650		xf86_config->num_output * sizeof (xf86OutputPtr));
651	xf86_config->output[0] = output;
652    }
653    else
654    {
655	xf86_config->output[xf86_config->num_output] = output;
656    }
657
658    xf86_config->num_output++;
659
660    return output;
661}
662
663Bool
664xf86OutputRename (xf86OutputPtr output, const char *name)
665{
666    char    *newname = strdup(name);
667
668    if (!newname)
669	return FALSE;	/* so sorry... */
670
671    if (output->name && output->name != (char *) (output + 1))
672	free(output->name);
673    output->name = newname;
674    xf86OutputSetMonitor (output);
675    if (xf86OutputIgnored (output))
676	return FALSE;
677    return TRUE;
678}
679
680void
681xf86OutputUseScreenMonitor (xf86OutputPtr output, Bool use_screen_monitor)
682{
683    if (use_screen_monitor != output->use_screen_monitor)
684    {
685	output->use_screen_monitor = use_screen_monitor;
686	xf86OutputSetMonitor (output);
687    }
688}
689
690void
691xf86OutputDestroy (xf86OutputPtr output)
692{
693    ScrnInfoPtr		scrn = output->scrn;
694    xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
695    int			o;
696
697    (*output->funcs->destroy) (output);
698    while (output->probed_modes)
699	xf86DeleteMode (&output->probed_modes, output->probed_modes);
700    for (o = 0; o < xf86_config->num_output; o++)
701	if (xf86_config->output[o] == output)
702	{
703	    memmove (&xf86_config->output[o],
704		     &xf86_config->output[o+1],
705		     ((xf86_config->num_output - (o + 1)) * sizeof(void*)));
706	    xf86_config->num_output--;
707	    break;
708	}
709    if (output->name && output->name != (char *) (output + 1))
710	free(output->name);
711    free(output);
712}
713
714/*
715 * Called during CreateScreenResources to hook up RandR
716 */
717static Bool
718xf86CrtcCreateScreenResources (ScreenPtr screen)
719{
720    ScrnInfoPtr		scrn = xf86Screens[screen->myNum];
721    xf86CrtcConfigPtr	config = XF86_CRTC_CONFIG_PTR(scrn);
722
723    screen->CreateScreenResources = config->CreateScreenResources;
724
725    if (!(*screen->CreateScreenResources)(screen))
726	return FALSE;
727
728    if (!xf86RandR12CreateScreenResources (screen))
729	return FALSE;
730
731    return TRUE;
732}
733
734/*
735 * Clean up config on server reset
736 */
737static Bool
738xf86CrtcCloseScreen (int index, ScreenPtr screen)
739{
740    ScrnInfoPtr		scrn = xf86Screens[screen->myNum];
741    xf86CrtcConfigPtr	config = XF86_CRTC_CONFIG_PTR(scrn);
742    int			o, c;
743
744    screen->CloseScreen = config->CloseScreen;
745
746    xf86RotateCloseScreen (screen);
747
748    for (o = 0; o < config->num_output; o++)
749    {
750	xf86OutputPtr	output = config->output[o];
751
752	output->randr_output = NULL;
753    }
754    for (c = 0; c < config->num_crtc; c++)
755    {
756	xf86CrtcPtr	crtc = config->crtc[c];
757
758	crtc->randr_crtc = NULL;
759    }
760    xf86RandR12CloseScreen (screen);
761
762    return screen->CloseScreen (index, screen);
763}
764
765/*
766 * Called at ScreenInit time to set up
767 */
768#ifdef RANDR_13_INTERFACE
769int
770#else
771Bool
772#endif
773xf86CrtcScreenInit (ScreenPtr screen)
774{
775    ScrnInfoPtr		scrn = xf86Screens[screen->myNum];
776    xf86CrtcConfigPtr	config = XF86_CRTC_CONFIG_PTR(scrn);
777    int			c;
778
779    /* Rotation */
780    xf86DrvMsg(scrn->scrnIndex, X_INFO, "RandR 1.2 enabled, ignore the following RandR disabled message.\n");
781    xf86DisableRandR(); /* Disable old RandR extension support */
782    xf86RandR12Init (screen);
783
784    /* support all rotations if every crtc has the shadow alloc funcs */
785    for (c = 0; c < config->num_crtc; c++)
786    {
787	xf86CrtcPtr crtc = config->crtc[c];
788	if (!crtc->funcs->shadow_allocate || !crtc->funcs->shadow_create)
789	    break;
790    }
791    if (c == config->num_crtc)
792    {
793	xf86RandR12SetRotations (screen, RR_Rotate_0 | RR_Rotate_90 |
794				 RR_Rotate_180 | RR_Rotate_270 |
795				 RR_Reflect_X | RR_Reflect_Y);
796	xf86RandR12SetTransformSupport (screen, TRUE);
797    }
798    else
799    {
800	xf86RandR12SetRotations (screen, RR_Rotate_0);
801	xf86RandR12SetTransformSupport (screen, FALSE);
802    }
803
804    /* Wrap CreateScreenResources so we can initialize the RandR code */
805    config->CreateScreenResources = screen->CreateScreenResources;
806    screen->CreateScreenResources = xf86CrtcCreateScreenResources;
807
808    config->CloseScreen = screen->CloseScreen;
809    screen->CloseScreen = xf86CrtcCloseScreen;
810
811#ifdef XFreeXDGA
812    _xf86_di_dga_init_internal(screen);
813#endif
814#ifdef RANDR_13_INTERFACE
815    return RANDR_INTERFACE_VERSION;
816#else
817    return TRUE;
818#endif
819}
820
821static DisplayModePtr
822xf86DefaultMode (xf86OutputPtr output, int width, int height)
823{
824    DisplayModePtr  target_mode = NULL;
825    DisplayModePtr  mode;
826    int		    target_diff = 0;
827    int		    target_preferred = 0;
828    int		    mm_height;
829
830    mm_height = output->mm_height;
831    if (!mm_height)
832	mm_height = (768 * 25.4) / DEFAULT_DPI;
833    /*
834     * Pick a mode closest to DEFAULT_DPI
835     */
836    for (mode = output->probed_modes; mode; mode = mode->next)
837    {
838	int	    dpi;
839	int	    preferred = (((mode->type & M_T_PREFERRED) != 0) +
840				 ((mode->type & M_T_USERPREF) != 0));
841	int	    diff;
842
843	if (xf86ModeWidth (mode, output->initial_rotation) > width ||
844	    xf86ModeHeight (mode, output->initial_rotation) > height)
845	    continue;
846
847	/* yes, use VDisplay here, not xf86ModeHeight */
848	dpi = (mode->VDisplay * 254) / (mm_height * 10);
849	diff = dpi - DEFAULT_DPI;
850	diff = diff < 0 ? -diff : diff;
851	if (target_mode == NULL || (preferred > target_preferred) ||
852	    (preferred == target_preferred && diff < target_diff))
853	{
854	    target_mode = mode;
855	    target_diff = diff;
856	    target_preferred = preferred;
857	}
858    }
859    return target_mode;
860}
861
862static DisplayModePtr
863xf86ClosestMode (xf86OutputPtr output,
864		 DisplayModePtr match, Rotation match_rotation,
865		 int width, int height)
866{
867    DisplayModePtr  target_mode = NULL;
868    DisplayModePtr  mode;
869    int		    target_diff = 0;
870
871    /*
872     * Pick a mode closest to the specified mode
873     */
874    for (mode = output->probed_modes; mode; mode = mode->next)
875    {
876	int	    dx, dy;
877	int	    diff;
878
879	if (xf86ModeWidth (mode, output->initial_rotation) > width ||
880	    xf86ModeHeight (mode, output->initial_rotation) > height)
881	    continue;
882
883	/* exact matches are preferred */
884	if (output->initial_rotation == match_rotation &&
885	    xf86ModesEqual (mode, match))
886	    return mode;
887
888	dx = xf86ModeWidth (match, match_rotation) - xf86ModeWidth (mode, output->initial_rotation);
889	dy = xf86ModeHeight (match, match_rotation) - xf86ModeHeight (mode, output->initial_rotation);
890	diff = dx * dx + dy * dy;
891	if (target_mode == NULL || diff < target_diff)
892	{
893	    target_mode = mode;
894	    target_diff = diff;
895	}
896    }
897    return target_mode;
898}
899
900static DisplayModePtr
901xf86OutputHasPreferredMode (xf86OutputPtr output, int width, int height)
902{
903    DisplayModePtr  mode;
904
905    for (mode = output->probed_modes; mode; mode = mode->next)
906    {
907	if (xf86ModeWidth (mode, output->initial_rotation) > width ||
908	    xf86ModeHeight (mode, output->initial_rotation) > height)
909	    continue;
910
911	if (mode->type & M_T_PREFERRED)
912	    return mode;
913    }
914    return NULL;
915}
916
917static DisplayModePtr
918xf86OutputHasUserPreferredMode (xf86OutputPtr output)
919{
920    DisplayModePtr mode, first = output->probed_modes;
921
922    for (mode = first; mode && mode->next != first; mode = mode->next)
923	if (mode->type & M_T_USERPREF)
924	    return mode;
925
926    return NULL;
927}
928
929static int
930xf86PickCrtcs (ScrnInfoPtr	scrn,
931	       xf86CrtcPtr	*best_crtcs,
932	       DisplayModePtr	*modes,
933	       int		n,
934	       int		width,
935	       int		height)
936{
937    xf86CrtcConfigPtr	config = XF86_CRTC_CONFIG_PTR(scrn);
938    int		    c, o;
939    xf86OutputPtr   output;
940    xf86CrtcPtr	    crtc;
941    xf86CrtcPtr	    *crtcs;
942    xf86CrtcPtr	    best_crtc;
943    int		    best_score;
944    int		    score;
945    int		    my_score;
946
947    if (n == config->num_output)
948	return 0;
949    output = config->output[n];
950
951    /*
952     * Compute score with this output disabled
953     */
954    best_crtcs[n] = NULL;
955    best_crtc = NULL;
956    best_score = xf86PickCrtcs (scrn, best_crtcs, modes, n+1, width, height);
957    if (modes[n] == NULL)
958	return best_score;
959
960    crtcs = malloc(config->num_output * sizeof (xf86CrtcPtr));
961    if (!crtcs)
962	return best_score;
963
964    my_score = 1;
965    /* Score outputs that are known to be connected higher */
966    if (output->status == XF86OutputStatusConnected)
967	my_score++;
968    /* Score outputs with preferred modes higher */
969    if (xf86OutputHasPreferredMode (output, width, height))
970	my_score++;
971    /*
972     * Select a crtc for this output and
973     * then attempt to configure the remaining
974     * outputs
975     */
976    for (c = 0; c < config->num_crtc; c++)
977    {
978	if ((output->possible_crtcs & (1 << c)) == 0)
979	    continue;
980
981	crtc = config->crtc[c];
982	/*
983	 * Check to see if some other output is
984	 * using this crtc
985	 */
986	for (o = 0; o < n; o++)
987	    if (best_crtcs[o] == crtc)
988		break;
989	if (o < n)
990	{
991	    /*
992	     * If the two outputs desire the same mode,
993	     * see if they can be cloned
994	     */
995	    if (xf86ModesEqual (modes[o], modes[n]) &&
996		config->output[o]->initial_rotation == config->output[n]->initial_rotation &&
997		config->output[o]->initial_x == config->output[n]->initial_x &&
998		config->output[o]->initial_y == config->output[n]->initial_y)
999	    {
1000		if ((output->possible_clones & (1 << o)) == 0)
1001		    continue;		/* nope, try next CRTC */
1002	    }
1003	    else
1004		continue;		/* different modes, can't clone */
1005	}
1006	crtcs[n] = crtc;
1007	memcpy (crtcs, best_crtcs, n * sizeof (xf86CrtcPtr));
1008	score = my_score + xf86PickCrtcs (scrn, crtcs, modes, n+1, width, height);
1009	if (score > best_score)
1010	{
1011	    best_crtc = crtc;
1012	    best_score = score;
1013	    memcpy (best_crtcs, crtcs, config->num_output * sizeof (xf86CrtcPtr));
1014	}
1015    }
1016    free(crtcs);
1017    return best_score;
1018}
1019
1020
1021/*
1022 * Compute the virtual size necessary to place all of the available
1023 * crtcs in the specified configuration.
1024 *
1025 * canGrow indicates that the driver can make the screen larger than its initial
1026 * configuration.  If FALSE, this function will enlarge the screen to include
1027 * the largest available mode.
1028 */
1029
1030static void
1031xf86DefaultScreenLimits (ScrnInfoPtr scrn, int *widthp, int *heightp,
1032			 Bool canGrow)
1033{
1034    xf86CrtcConfigPtr	config = XF86_CRTC_CONFIG_PTR(scrn);
1035    int	    width = 0, height = 0;
1036    int	    o;
1037    int	    c;
1038    int	    s;
1039
1040    for (c = 0; c < config->num_crtc; c++)
1041    {
1042	int	    crtc_width = 0, crtc_height = 0;
1043	xf86CrtcPtr crtc = config->crtc[c];
1044
1045	if (crtc->enabled)
1046	{
1047	    crtc_width = crtc->desiredX + xf86ModeWidth (&crtc->desiredMode, crtc->desiredRotation);
1048	    crtc_height = crtc->desiredY + xf86ModeHeight (&crtc->desiredMode, crtc->desiredRotation);
1049	}
1050	if (!canGrow) {
1051	    for (o = 0; o < config->num_output; o++)
1052	    {
1053		xf86OutputPtr   output = config->output[o];
1054
1055		for (s = 0; s < config->num_crtc; s++)
1056		    if (output->possible_crtcs & (1 << s))
1057		    {
1058			DisplayModePtr  mode;
1059			for (mode = output->probed_modes; mode; mode = mode->next)
1060			{
1061			    if (mode->HDisplay > crtc_width)
1062				crtc_width = mode->HDisplay;
1063			    if (mode->VDisplay > crtc_width)
1064				crtc_width = mode->VDisplay;
1065			    if (mode->VDisplay > crtc_height)
1066				crtc_height = mode->VDisplay;
1067			    if (mode->HDisplay > crtc_height)
1068				crtc_height = mode->HDisplay;
1069			}
1070		    }
1071	    }
1072	}
1073	if (crtc_width > width)
1074	    width = crtc_width;
1075	if (crtc_height > height)
1076	    height = crtc_height;
1077    }
1078    if (config->maxWidth && width > config->maxWidth) width = config->maxWidth;
1079    if (config->maxHeight && height > config->maxHeight) height = config->maxHeight;
1080    if (config->minWidth && width < config->minWidth) width = config->minWidth;
1081    if (config->minHeight && height < config->minHeight) height = config->minHeight;
1082    *widthp = width;
1083    *heightp = height;
1084}
1085
1086#define POSITION_UNSET	-100000
1087
1088/*
1089 * check if the user configured any outputs at all
1090 * with either a position or a relative setting or a mode.
1091 */
1092static Bool
1093xf86UserConfiguredOutputs(ScrnInfoPtr scrn, DisplayModePtr *modes)
1094{
1095    xf86CrtcConfigPtr	config = XF86_CRTC_CONFIG_PTR(scrn);
1096    int o;
1097    Bool user_conf = FALSE;
1098
1099    for (o = 0; o < config->num_output; o++)
1100    {
1101	xf86OutputPtr output = config->output[o];
1102	char	    *position;
1103	char	    *relative_name;
1104	OutputOpts	    relation;
1105	int r;
1106	static const OutputOpts	relations[] = {
1107	    OPTION_BELOW, OPTION_RIGHT_OF, OPTION_ABOVE, OPTION_LEFT_OF
1108	};
1109
1110	position = xf86GetOptValString (output->options,
1111					OPTION_POSITION);
1112	if (position)
1113	    user_conf = TRUE;
1114
1115	relation = 0;
1116	relative_name = NULL;
1117	for (r = 0; r < 4; r++)
1118	{
1119	    relation = relations[r];
1120	    relative_name = xf86GetOptValString (output->options,
1121						     relation);
1122	    if (relative_name)
1123		break;
1124	}
1125	if (relative_name)
1126	    user_conf = TRUE;
1127
1128	modes[o] = xf86OutputHasUserPreferredMode(output);
1129	if (modes[o])
1130	    user_conf = TRUE;
1131    }
1132
1133    return user_conf;
1134}
1135
1136static Bool
1137xf86InitialOutputPositions (ScrnInfoPtr scrn, DisplayModePtr *modes)
1138{
1139    xf86CrtcConfigPtr	config = XF86_CRTC_CONFIG_PTR(scrn);
1140    int			o;
1141    int			min_x, min_y;
1142
1143    for (o = 0; o < config->num_output; o++)
1144    {
1145	xf86OutputPtr	output = config->output[o];
1146
1147	output->initial_x = output->initial_y = POSITION_UNSET;
1148    }
1149
1150    /*
1151     * Loop until all outputs are set
1152     */
1153    for (;;)
1154    {
1155	Bool	any_set = FALSE;
1156	Bool	keep_going = FALSE;
1157
1158	for (o = 0; o < config->num_output; o++)
1159	{
1160	    static const OutputOpts	relations[] = {
1161		OPTION_BELOW, OPTION_RIGHT_OF, OPTION_ABOVE, OPTION_LEFT_OF
1162	    };
1163	    xf86OutputPtr   output = config->output[o];
1164	    xf86OutputPtr   relative;
1165	    char	    *relative_name;
1166	    char	    *position;
1167	    OutputOpts	    relation;
1168	    int		    r;
1169
1170	    if (output->initial_x != POSITION_UNSET)
1171		continue;
1172	    position = xf86GetOptValString (output->options,
1173					    OPTION_POSITION);
1174	    /*
1175	     * Absolute position wins
1176	     */
1177	    if (position)
1178	    {
1179		int		    x, y;
1180		if (sscanf (position, "%d %d", &x, &y) == 2)
1181		{
1182		    output->initial_x = x;
1183		    output->initial_y = y;
1184		}
1185		else
1186		{
1187		    xf86DrvMsg (scrn->scrnIndex, X_ERROR,
1188				"Output %s position not of form \"x y\"\n",
1189				output->name);
1190		    output->initial_x = output->initial_y = 0;
1191		}
1192		any_set = TRUE;
1193		continue;
1194	    }
1195	    /*
1196	     * Next comes relative positions
1197	     */
1198	    relation = 0;
1199	    relative_name = NULL;
1200	    for (r = 0; r < 4; r++)
1201	    {
1202		relation = relations[r];
1203		relative_name = xf86GetOptValString (output->options,
1204						     relation);
1205		if (relative_name)
1206		    break;
1207	    }
1208	    if (relative_name)
1209	    {
1210		int or;
1211		relative = NULL;
1212		for (or = 0; or < config->num_output; or++)
1213		{
1214		    xf86OutputPtr	out_rel = config->output[or];
1215		    XF86ConfMonitorPtr	rel_mon = out_rel->conf_monitor;
1216
1217		    if (rel_mon)
1218		    {
1219			if (xf86nameCompare (rel_mon->mon_identifier,
1220					      relative_name) == 0)
1221			{
1222			    relative = config->output[or];
1223			    break;
1224			}
1225		    }
1226		    if (strcmp (out_rel->name, relative_name) == 0)
1227		    {
1228			relative = config->output[or];
1229			break;
1230		    }
1231		}
1232		if (!relative)
1233		{
1234		    xf86DrvMsg (scrn->scrnIndex, X_ERROR,
1235				"Cannot position output %s relative to unknown output %s\n",
1236				output->name, relative_name);
1237		    output->initial_x = 0;
1238		    output->initial_y = 0;
1239		    any_set = TRUE;
1240		    continue;
1241		}
1242		if (!modes[or])
1243		{
1244		    xf86DrvMsg (scrn->scrnIndex, X_ERROR,
1245				"Cannot position output %s relative to output %s without modes\n",
1246				output->name, relative_name);
1247		    output->initial_x = 0;
1248		    output->initial_y = 0;
1249		    any_set = TRUE;
1250		    continue;
1251		}
1252		if (relative->initial_x == POSITION_UNSET)
1253		{
1254		    keep_going = TRUE;
1255		    continue;
1256		}
1257		output->initial_x = relative->initial_x;
1258		output->initial_y = relative->initial_y;
1259		switch (relation) {
1260		case OPTION_BELOW:
1261		    output->initial_y += xf86ModeHeight (modes[or], relative->initial_rotation);
1262		    break;
1263		case OPTION_RIGHT_OF:
1264		    output->initial_x += xf86ModeWidth (modes[or], relative->initial_rotation);
1265		    break;
1266		case OPTION_ABOVE:
1267		    if (modes[o])
1268			output->initial_y -= xf86ModeHeight (modes[o], output->initial_rotation);
1269		    break;
1270		case OPTION_LEFT_OF:
1271		    if (modes[o])
1272			output->initial_x -= xf86ModeWidth (modes[o], output->initial_rotation);
1273		    break;
1274		default:
1275		    break;
1276		}
1277		any_set = TRUE;
1278		continue;
1279	    }
1280
1281	    /* Nothing set, just stick them at 0,0 */
1282	    output->initial_x = 0;
1283	    output->initial_y = 0;
1284	    any_set = TRUE;
1285	}
1286	if (!keep_going)
1287	    break;
1288	if (!any_set)
1289	{
1290	    for (o = 0; o < config->num_output; o++)
1291	    {
1292		xf86OutputPtr   output = config->output[o];
1293		if (output->initial_x == POSITION_UNSET)
1294		{
1295		    xf86DrvMsg (scrn->scrnIndex, X_ERROR,
1296				"Output position loop. Moving %s to 0,0\n",
1297				output->name);
1298		    output->initial_x = output->initial_y = 0;
1299		    break;
1300		}
1301	    }
1302	}
1303    }
1304
1305    /*
1306     * normalize positions
1307     */
1308    min_x = 1000000;
1309    min_y = 1000000;
1310    for (o = 0; o < config->num_output; o++)
1311    {
1312	xf86OutputPtr	output = config->output[o];
1313
1314	if (output->initial_x < min_x)
1315	    min_x = output->initial_x;
1316	if (output->initial_y < min_y)
1317	    min_y = output->initial_y;
1318    }
1319
1320    for (o = 0; o < config->num_output; o++)
1321    {
1322	xf86OutputPtr	output = config->output[o];
1323
1324	output->initial_x -= min_x;
1325	output->initial_y -= min_y;
1326    }
1327    return TRUE;
1328}
1329
1330static void
1331xf86InitialPanning (ScrnInfoPtr scrn)
1332{
1333    xf86CrtcConfigPtr	config = XF86_CRTC_CONFIG_PTR(scrn);
1334    int			o;
1335
1336    for (o = 0; o < config->num_output; o++)
1337    {
1338	xf86OutputPtr	output = config->output[o];
1339	char	       *panning = xf86GetOptValString (output->options, OPTION_PANNING);
1340	int		width, height, left, top;
1341	int		track_width, track_height, track_left, track_top;
1342	int		brdr[4];
1343
1344	memset (&output->initialTotalArea,    0, sizeof(BoxRec));
1345	memset (&output->initialTrackingArea, 0, sizeof(BoxRec));
1346	memset (output->initialBorder,        0, 4*sizeof(INT16));
1347
1348	if (! panning)
1349	    continue;
1350
1351	switch (sscanf (panning, "%dx%d+%d+%d/%dx%d+%d+%d/%d/%d/%d/%d",
1352			&width, &height, &left, &top,
1353			&track_width, &track_height, &track_left, &track_top,
1354			&brdr[0], &brdr[1], &brdr[2], &brdr[3])) {
1355	case 12:
1356	    output->initialBorder[0] = brdr[0];
1357	    output->initialBorder[1] = brdr[1];
1358	    output->initialBorder[2] = brdr[2];
1359	    output->initialBorder[3] = brdr[3];
1360	    /* fall through */
1361	case 8:
1362	    output->initialTrackingArea.x1 = track_left;
1363	    output->initialTrackingArea.y1 = track_top;
1364	    output->initialTrackingArea.x2 = track_left + track_width;
1365	    output->initialTrackingArea.y2 = track_top  + track_height;
1366	    /* fall through */
1367	case 4:
1368	    output->initialTotalArea.x1 = left;
1369	    output->initialTotalArea.y1 = top;
1370	    /* fall through */
1371	case 2:
1372	    output->initialTotalArea.x2 = output->initialTotalArea.x1 + width;
1373	    output->initialTotalArea.y2 = output->initialTotalArea.y1 + height;
1374	    break;
1375	default:
1376	    xf86DrvMsg (scrn->scrnIndex, X_ERROR,
1377			"Broken panning specification '%s' for output %s in config file\n",
1378			panning, output->name);
1379	}
1380    }
1381}
1382
1383/** Return - 0 + if a should be earlier, same or later than b in list
1384 */
1385static int
1386xf86ModeCompare (DisplayModePtr a, DisplayModePtr b)
1387{
1388    int	diff;
1389
1390    diff = ((b->type & M_T_PREFERRED) != 0) - ((a->type & M_T_PREFERRED) != 0);
1391    if (diff)
1392	return diff;
1393    diff = b->HDisplay * b->VDisplay - a->HDisplay * a->VDisplay;
1394    if (diff)
1395	return diff;
1396    diff = b->Clock - a->Clock;
1397    return diff;
1398}
1399
1400/**
1401 * Insertion sort input in-place and return the resulting head
1402 */
1403static DisplayModePtr
1404xf86SortModes (DisplayModePtr input)
1405{
1406    DisplayModePtr  output = NULL, i, o, n, *op, prev;
1407
1408    /* sort by preferred status and pixel area */
1409    while (input)
1410    {
1411	i = input;
1412	input = input->next;
1413	for (op = &output; (o = *op); op = &o->next)
1414	    if (xf86ModeCompare (o, i) > 0)
1415		break;
1416	i->next = *op;
1417	*op = i;
1418    }
1419    /* prune identical modes */
1420    for (o = output; o && (n = o->next); o = n)
1421    {
1422	if (!strcmp (o->name, n->name) && xf86ModesEqual (o, n))
1423	{
1424	    o->next = n->next;
1425	    free(n->name);
1426	    free(n);
1427	    n = o;
1428	}
1429    }
1430    /* hook up backward links */
1431    prev = NULL;
1432    for (o = output; o; o = o->next)
1433    {
1434	o->prev = prev;
1435	prev = o;
1436    }
1437    return output;
1438}
1439
1440static char *
1441preferredMode(ScrnInfoPtr pScrn, xf86OutputPtr output)
1442{
1443    char *preferred_mode = NULL;
1444
1445    /* Check for a configured preference for a particular mode */
1446    preferred_mode = xf86GetOptValString (output->options,
1447					  OPTION_PREFERRED_MODE);
1448    if (preferred_mode)
1449	return preferred_mode;
1450
1451    if (pScrn->display->modes && *pScrn->display->modes)
1452	preferred_mode = (char *)(intptr_t)*pScrn->display->modes;
1453
1454    return preferred_mode;
1455}
1456
1457static void
1458GuessRangeFromModes(MonPtr mon, DisplayModePtr mode)
1459{
1460    if (!mon || !mode)
1461       return;
1462
1463    mon->nHsync = 1;
1464    mon->hsync[0].lo = 1024.0;
1465    mon->hsync[0].hi = 0.0;
1466
1467    mon->nVrefresh = 1;
1468    mon->vrefresh[0].lo = 1024.0;
1469    mon->vrefresh[0].hi = 0.0;
1470
1471    while (mode) {
1472	if (!mode->HSync)
1473	    mode->HSync = ((float) mode->Clock ) / ((float) mode->HTotal);
1474
1475	if (!mode->VRefresh)
1476	    mode->VRefresh = (1000.0 * ((float) mode->Clock)) /
1477		((float) (mode->HTotal * mode->VTotal));
1478
1479	if (mode->HSync < mon->hsync[0].lo)
1480	    mon->hsync[0].lo = mode->HSync;
1481
1482	if (mode->HSync > mon->hsync[0].hi)
1483	    mon->hsync[0].hi = mode->HSync;
1484
1485	if (mode->VRefresh < mon->vrefresh[0].lo)
1486	    mon->vrefresh[0].lo = mode->VRefresh;
1487
1488	if (mode->VRefresh > mon->vrefresh[0].hi)
1489	    mon->vrefresh[0].hi = mode->VRefresh;
1490
1491	mode = mode->next;
1492    }
1493
1494    /* stretch out the bottom to fit 640x480@60 */
1495    if (mon->hsync[0].lo > 31.0)
1496       mon->hsync[0].lo = 31.0;
1497    if (mon->vrefresh[0].lo > 58.0)
1498       mon->vrefresh[0].lo = 58.0;
1499}
1500
1501enum det_monrec_source {
1502    sync_config, sync_edid, sync_default
1503};
1504
1505struct det_monrec_parameter {
1506    MonRec *mon_rec;
1507    int *max_clock;
1508    Bool set_hsync;
1509    Bool set_vrefresh;
1510    enum det_monrec_source *sync_source;
1511};
1512
1513static void handle_detailed_monrec(struct detailed_monitor_section *det_mon,
1514                                   void *data)
1515{
1516    struct det_monrec_parameter *p;
1517    p = (struct det_monrec_parameter *)data;
1518
1519    if (det_mon->type == DS_RANGES) {
1520        struct monitor_ranges *ranges = &det_mon->section.ranges;
1521        if (p->set_hsync && ranges->max_h) {
1522            p->mon_rec->hsync[p->mon_rec->nHsync].lo = ranges->min_h;
1523            p->mon_rec->hsync[p->mon_rec->nHsync].hi = ranges->max_h;
1524            p->mon_rec->nHsync++;
1525            if (*p->sync_source == sync_default)
1526                *p->sync_source = sync_edid;
1527        }
1528        if (p->set_vrefresh && ranges->max_v) {
1529            p->mon_rec->vrefresh[p->mon_rec->nVrefresh].lo = ranges->min_v;
1530            p->mon_rec->vrefresh[p->mon_rec->nVrefresh].hi = ranges->max_v;
1531            p->mon_rec->nVrefresh++;
1532            if (*p->sync_source == sync_default)
1533                *p->sync_source = sync_edid;
1534        }
1535        if (ranges->max_clock * 1000 > *p->max_clock)
1536            *p->max_clock = ranges->max_clock * 1000;
1537    }
1538}
1539
1540void
1541xf86ProbeOutputModes (ScrnInfoPtr scrn, int maxX, int maxY)
1542{
1543    xf86CrtcConfigPtr	config = XF86_CRTC_CONFIG_PTR(scrn);
1544    int			o;
1545
1546    /* When canGrow was TRUE in the initial configuration we have to
1547     * compare against the maximum values so that we don't drop modes.
1548     * When canGrow was FALSE, the maximum values would have been clamped
1549     * anyway.
1550     */
1551    if (maxX == 0 || maxY == 0) {
1552	maxX = config->maxWidth;
1553	maxY = config->maxHeight;
1554    }
1555
1556    /* Probe the list of modes for each output. */
1557    for (o = 0; o < config->num_output; o++)
1558    {
1559	xf86OutputPtr	    output = config->output[o];
1560	DisplayModePtr	    mode;
1561	DisplayModePtr	    config_modes = NULL, output_modes, default_modes = NULL;
1562	char		    *preferred_mode;
1563	xf86MonPtr	    edid_monitor;
1564	XF86ConfMonitorPtr  conf_monitor;
1565	MonRec		    mon_rec;
1566	int		    min_clock = 0;
1567	int		    max_clock = 0;
1568	double		    clock;
1569	Bool		    add_default_modes;
1570	Bool		    debug_modes = config->debug_modes ||
1571					  xf86Initialising;
1572	enum det_monrec_source sync_source = sync_default;
1573
1574	while (output->probed_modes != NULL)
1575	    xf86DeleteMode(&output->probed_modes, output->probed_modes);
1576
1577	/*
1578	 * Check connection status
1579	 */
1580	output->status = (*output->funcs->detect)(output);
1581
1582	if (output->status == XF86OutputStatusDisconnected &&
1583		!xf86ReturnOptValBool(output->options, OPTION_ENABLE, FALSE))
1584	{
1585	    xf86OutputSetEDID (output, NULL);
1586	    continue;
1587	}
1588
1589	memset (&mon_rec, '\0', sizeof (mon_rec));
1590
1591	conf_monitor = output->conf_monitor;
1592
1593	if (conf_monitor)
1594	{
1595	    int	i;
1596
1597	    for (i = 0; i < conf_monitor->mon_n_hsync; i++)
1598	    {
1599		mon_rec.hsync[mon_rec.nHsync].lo = conf_monitor->mon_hsync[i].lo;
1600		mon_rec.hsync[mon_rec.nHsync].hi = conf_monitor->mon_hsync[i].hi;
1601		mon_rec.nHsync++;
1602		sync_source = sync_config;
1603	    }
1604	    for (i = 0; i < conf_monitor->mon_n_vrefresh; i++)
1605	    {
1606		mon_rec.vrefresh[mon_rec.nVrefresh].lo = conf_monitor->mon_vrefresh[i].lo;
1607		mon_rec.vrefresh[mon_rec.nVrefresh].hi = conf_monitor->mon_vrefresh[i].hi;
1608		mon_rec.nVrefresh++;
1609		sync_source = sync_config;
1610	    }
1611	    config_modes = xf86GetMonitorModes (scrn, conf_monitor);
1612	}
1613
1614	output_modes = (*output->funcs->get_modes) (output);
1615
1616	/*
1617	 * If the user has a preference, respect it.
1618	 * Otherwise, don't second-guess the driver.
1619	 */
1620	if (!xf86GetOptValBool(output->options, OPTION_DEFAULT_MODES,
1621			       &add_default_modes))
1622	    add_default_modes = (output_modes == NULL);
1623
1624	edid_monitor = output->MonInfo;
1625
1626        if (edid_monitor)
1627        {
1628            struct det_monrec_parameter p;
1629            struct disp_features    *features = &edid_monitor->features;
1630
1631	    /* if display is not continuous-frequency, don't add default modes */
1632	    if (!GTF_SUPPORTED(features->msc))
1633		add_default_modes = FALSE;
1634
1635	    p.mon_rec = &mon_rec;
1636	    p.max_clock = &max_clock;
1637	    p.set_hsync = mon_rec.nHsync == 0;
1638	    p.set_vrefresh = mon_rec.nVrefresh == 0;
1639	    p.sync_source = &sync_source;
1640
1641	    xf86ForEachDetailedBlock(edid_monitor,
1642			             handle_detailed_monrec,
1643			             &p);
1644	}
1645
1646	if (xf86GetOptValFreq (output->options, OPTION_MIN_CLOCK,
1647			       OPTUNITS_KHZ, &clock))
1648	    min_clock = (int) clock;
1649	if (xf86GetOptValFreq (output->options, OPTION_MAX_CLOCK,
1650			       OPTUNITS_KHZ, &clock))
1651	    max_clock = (int) clock;
1652
1653	/* If we still don't have a sync range, guess wildly */
1654	if (!mon_rec.nHsync || !mon_rec.nVrefresh)
1655	    GuessRangeFromModes(&mon_rec, output_modes);
1656
1657	/*
1658	 * These limits will end up setting a 1024x768@60Hz mode by default,
1659	 * which seems like a fairly good mode to use when nothing else is
1660	 * specified
1661	 */
1662	if (mon_rec.nHsync == 0)
1663	{
1664	    mon_rec.hsync[0].lo = 31.0;
1665	    mon_rec.hsync[0].hi = 55.0;
1666	    mon_rec.nHsync = 1;
1667	}
1668	if (mon_rec.nVrefresh == 0)
1669	{
1670	    mon_rec.vrefresh[0].lo = 58.0;
1671	    mon_rec.vrefresh[0].hi = 62.0;
1672	    mon_rec.nVrefresh = 1;
1673	}
1674
1675	if (add_default_modes)
1676	    default_modes = xf86GetDefaultModes ();
1677
1678	/*
1679	 * If this is not an RB monitor, remove RB modes from the default
1680	 * pool.  RB modes from the config or the monitor itself are fine.
1681	 */
1682	if (!mon_rec.reducedblanking)
1683	    xf86ValidateModesReducedBlanking (scrn, default_modes);
1684
1685	if (sync_source == sync_config)
1686	{
1687	    /*
1688	     * Check output and config modes against sync range from config file
1689	     */
1690	    xf86ValidateModesSync (scrn, output_modes, &mon_rec);
1691	    xf86ValidateModesSync (scrn, config_modes, &mon_rec);
1692	}
1693	/*
1694	 * Check default modes against sync range
1695	 */
1696        xf86ValidateModesSync (scrn, default_modes, &mon_rec);
1697	/*
1698	 * Check default modes against monitor max clock
1699	 */
1700	if (max_clock) {
1701	    xf86ValidateModesClocks(scrn, default_modes,
1702				    &min_clock, &max_clock, 1);
1703	    xf86ValidateModesClocks(scrn, output_modes,
1704				    &min_clock, &max_clock, 1);
1705	}
1706
1707	output->probed_modes = NULL;
1708	output->probed_modes = xf86ModesAdd (output->probed_modes, config_modes);
1709	output->probed_modes = xf86ModesAdd (output->probed_modes, output_modes);
1710	output->probed_modes = xf86ModesAdd (output->probed_modes, default_modes);
1711
1712	/*
1713	 * Check all modes against max size, interlace, and doublescan
1714	 */
1715	if (maxX && maxY)
1716	    xf86ValidateModesSize (scrn, output->probed_modes,
1717				       maxX, maxY, 0);
1718
1719	{
1720	    int flags = (output->interlaceAllowed ? V_INTERLACE : 0) |
1721			(output->doubleScanAllowed ? V_DBLSCAN : 0);
1722	    xf86ValidateModesFlags (scrn, output->probed_modes, flags);
1723	}
1724
1725	/*
1726	 * Check all modes against output
1727	 */
1728	for (mode = output->probed_modes; mode != NULL; mode = mode->next)
1729	    if (mode->status == MODE_OK)
1730		mode->status = (*output->funcs->mode_valid)(output, mode);
1731
1732	xf86PruneInvalidModes(scrn, &output->probed_modes, debug_modes);
1733
1734	output->probed_modes = xf86SortModes (output->probed_modes);
1735
1736	/* Check for a configured preference for a particular mode */
1737	preferred_mode = preferredMode(scrn, output);
1738
1739	if (preferred_mode)
1740	{
1741	    for (mode = output->probed_modes; mode; mode = mode->next)
1742	    {
1743		if (!strcmp (preferred_mode, mode->name))
1744		{
1745		    if (mode != output->probed_modes)
1746		    {
1747			if (mode->prev)
1748			    mode->prev->next = mode->next;
1749			if (mode->next)
1750			    mode->next->prev = mode->prev;
1751			mode->next = output->probed_modes;
1752			output->probed_modes->prev = mode;
1753			mode->prev = NULL;
1754			output->probed_modes = mode;
1755		    }
1756		    mode->type |= (M_T_PREFERRED|M_T_USERPREF);
1757		    break;
1758		}
1759	    }
1760	}
1761
1762	output->initial_rotation = xf86OutputInitialRotation (output);
1763
1764	if (debug_modes) {
1765	    if (output->probed_modes != NULL) {
1766		xf86DrvMsg(scrn->scrnIndex, X_INFO,
1767			   "Printing probed modes for output %s\n",
1768			   output->name);
1769	    } else {
1770		xf86DrvMsg(scrn->scrnIndex, X_INFO,
1771			   "No remaining probed modes for output %s\n",
1772			   output->name);
1773	    }
1774	}
1775	for (mode = output->probed_modes; mode != NULL; mode = mode->next)
1776	{
1777	    /* The code to choose the best mode per pipe later on will require
1778	     * VRefresh to be set.
1779	     */
1780	    mode->VRefresh = xf86ModeVRefresh(mode);
1781	    xf86SetModeCrtc(mode, INTERLACE_HALVE_V);
1782
1783	    if (debug_modes)
1784		xf86PrintModeline(scrn->scrnIndex, mode);
1785	}
1786    }
1787}
1788
1789
1790/**
1791 * Copy one of the output mode lists to the ScrnInfo record
1792 */
1793
1794/* XXX where does this function belong? Here? */
1795void
1796xf86RandR12GetOriginalVirtualSize(ScrnInfoPtr scrn, int *x, int *y);
1797
1798static DisplayModePtr
1799biggestMode(DisplayModePtr a, DisplayModePtr b)
1800{
1801    int A, B;
1802
1803    if (!a)
1804	return b;
1805    if (!b)
1806	return a;
1807
1808    A = a->HDisplay * a->VDisplay;
1809    B = b->HDisplay * b->VDisplay;
1810
1811    if (A > B)
1812	return a;
1813
1814    return b;
1815}
1816
1817static xf86OutputPtr
1818SetCompatOutput(xf86CrtcConfigPtr config)
1819{
1820    xf86OutputPtr output = NULL, test = NULL;
1821    DisplayModePtr maxmode = NULL, testmode, mode;
1822    int o, compat = -1, count, mincount = 0;
1823
1824    /* Look for one that's definitely connected */
1825    for (o = 0; o < config->num_output; o++)
1826    {
1827	test = config->output[o];
1828	if (!test->crtc)
1829	    continue;
1830	if (test->status != XF86OutputStatusConnected)
1831	    continue;
1832	if (!test->probed_modes)
1833	    continue;
1834
1835	testmode = mode = test->probed_modes;
1836	for (count = 0; mode; mode = mode->next, count++)
1837	    testmode = biggestMode(testmode, mode);
1838
1839	if (!output) {
1840	    output = test;
1841	    compat = o;
1842	    maxmode = testmode;
1843	    mincount = count;
1844	} else if (maxmode == biggestMode(maxmode, testmode)) {
1845	    output = test;
1846	    compat = o;
1847	    maxmode = testmode;
1848	    mincount = count;
1849	} else if ((maxmode->HDisplay == testmode->HDisplay) &&
1850		(maxmode->VDisplay == testmode->VDisplay) &&
1851		count <= mincount) {
1852	    output = test;
1853	    compat = o;
1854	    maxmode = testmode;
1855	    mincount = count;
1856	}
1857    }
1858
1859    /* If we didn't find one, take anything we can get */
1860    if (!output)
1861    {
1862	for (o = 0; o < config->num_output; o++)
1863	{
1864	    test = config->output[o];
1865	    if (!test->crtc)
1866		continue;
1867	    if (!test->probed_modes)
1868		continue;
1869
1870	    if (!output) {
1871		output = test;
1872		compat = o;
1873	    } else if (test->probed_modes->HDisplay < output->probed_modes->HDisplay) {
1874		output = test;
1875		compat = o;
1876	    }
1877	}
1878    }
1879
1880    if (compat >= 0) {
1881	config->compat_output = compat;
1882    } else {
1883	/* Don't change the compat output when no valid outputs found */
1884	output = config->output[config->compat_output];
1885    }
1886
1887    return output;
1888}
1889
1890void
1891xf86SetScrnInfoModes (ScrnInfoPtr scrn)
1892{
1893    xf86CrtcConfigPtr	config = XF86_CRTC_CONFIG_PTR(scrn);
1894    xf86OutputPtr	output;
1895    xf86CrtcPtr		crtc;
1896    DisplayModePtr	last, mode = NULL;
1897
1898    output = SetCompatOutput(config);
1899
1900    if (!output)
1901	return; /* punt */
1902
1903    crtc = output->crtc;
1904
1905    /* Clear any existing modes from scrn->modes */
1906    while (scrn->modes != NULL)
1907	xf86DeleteMode(&scrn->modes, scrn->modes);
1908
1909    /* Set scrn->modes to the mode list for the 'compat' output */
1910    scrn->modes = xf86DuplicateModes(scrn, output->probed_modes);
1911
1912    if (crtc) {
1913	for (mode = scrn->modes; mode; mode = mode->next)
1914	    if (xf86ModesEqual (mode, &crtc->desiredMode))
1915		break;
1916    }
1917
1918    if (!scrn->modes) {
1919	scrn->modes = xf86ModesAdd(scrn->modes,
1920				   xf86CVTMode(scrn->display->virtualX,
1921					       scrn->display->virtualY,
1922					       60, 0, 0));
1923    }
1924
1925    /* For some reason, scrn->modes is circular, unlike the other mode
1926     * lists.  How great is that?
1927     */
1928    for (last = scrn->modes; last && last->next; last = last->next)
1929	;
1930    last->next = scrn->modes;
1931    scrn->modes->prev = last;
1932    if (mode) {
1933	while (scrn->modes != mode)
1934	    scrn->modes = scrn->modes->next;
1935    }
1936
1937    scrn->currentMode = scrn->modes;
1938#ifdef XFreeXDGA
1939    if (scrn->pScreen)
1940	    _xf86_di_dga_reinit_internal(scrn->pScreen);
1941#endif
1942}
1943
1944static Bool
1945xf86CollectEnabledOutputs(ScrnInfoPtr scrn, xf86CrtcConfigPtr config,
1946			  Bool *enabled)
1947{
1948    Bool any_enabled = FALSE;
1949    int o;
1950
1951    for (o = 0; o < config->num_output; o++)
1952	any_enabled |= enabled[o] = xf86OutputEnabled(config->output[o], TRUE);
1953
1954    if (!any_enabled) {
1955	xf86DrvMsg(scrn->scrnIndex, X_WARNING,
1956		   "No outputs definitely connected, trying again...\n");
1957
1958	for (o = 0; o < config->num_output; o++)
1959	    any_enabled |= enabled[o] = xf86OutputEnabled(config->output[o], FALSE);
1960    }
1961
1962    return any_enabled;
1963}
1964
1965static Bool
1966nextEnabledOutput(xf86CrtcConfigPtr config, Bool *enabled, int *index)
1967{
1968    int o = *index;
1969
1970    for (o++; o < config->num_output; o++) {
1971	if (enabled[o]) {
1972	    *index = o;
1973	    return TRUE;
1974	}
1975    }
1976
1977    return FALSE;
1978}
1979
1980static Bool
1981aspectMatch(float a, float b)
1982{
1983    return fabs(1 - (a / b)) < 0.05;
1984}
1985
1986static DisplayModePtr
1987nextAspectMode(xf86OutputPtr o, DisplayModePtr last, float aspect)
1988{
1989    DisplayModePtr m = NULL;
1990
1991    if (!o)
1992	return NULL;
1993
1994    if (!last)
1995	m = o->probed_modes;
1996    else
1997	m = last->next;
1998
1999    for (; m; m = m->next)
2000	if (aspectMatch(aspect, (float)m->HDisplay / (float)m->VDisplay))
2001	    return m;
2002
2003    return NULL;
2004}
2005
2006static DisplayModePtr
2007bestModeForAspect(xf86CrtcConfigPtr config, Bool *enabled, float aspect)
2008{
2009    int o = -1, p;
2010    DisplayModePtr mode = NULL, test = NULL, match = NULL;
2011
2012    if (!nextEnabledOutput(config, enabled, &o))
2013	return NULL;
2014    while ((mode = nextAspectMode(config->output[o], mode, aspect))) {
2015	test = mode;
2016	for (p = o; nextEnabledOutput(config, enabled, &p); ) {
2017	    test = xf86OutputFindClosestMode(config->output[p], mode);
2018	    if (!test)
2019		break;
2020	    if (test->HDisplay != mode->HDisplay ||
2021		    test->VDisplay != mode->VDisplay) {
2022		test = NULL;
2023		break;
2024	    }
2025	}
2026
2027	/* if we didn't match it on all outputs, try the next one */
2028	if (!test)
2029	    continue;
2030
2031	/* if it's bigger than the last one, save it */
2032	if (!match || (test->HDisplay > match->HDisplay))
2033	    match = test;
2034    }
2035
2036    /* return the biggest one found */
2037    return match;
2038}
2039
2040static Bool
2041xf86TargetPreferred(ScrnInfoPtr scrn, xf86CrtcConfigPtr config,
2042		    DisplayModePtr *modes, Bool *enabled,
2043		    int width, int height)
2044{
2045    int o, p;
2046    int max_pref_width = 0, max_pref_height = 0;
2047    DisplayModePtr *preferred, *preferred_match;
2048    Bool ret = FALSE;
2049
2050    preferred = xnfcalloc(config->num_output, sizeof(DisplayModePtr));
2051    preferred_match = xnfcalloc(config->num_output, sizeof(DisplayModePtr));
2052
2053    /* Check if the preferred mode is available on all outputs */
2054    for (p = -1; nextEnabledOutput(config, enabled, &p); ) {
2055	Rotation r = config->output[p]->initial_rotation;
2056	DisplayModePtr mode;
2057	if ((preferred[p] = xf86OutputHasPreferredMode(config->output[p],
2058			width, height))) {
2059	    int pref_width = xf86ModeWidth(preferred[p], r);
2060	    int pref_height = xf86ModeHeight(preferred[p], r);
2061	    Bool all_match = TRUE;
2062
2063	    for (o = -1; nextEnabledOutput(config, enabled, &o); ) {
2064		Bool match = FALSE;
2065		xf86OutputPtr output = config->output[o];
2066		if (o == p)
2067		    continue;
2068
2069		for (mode = output->probed_modes; mode; mode = mode->next) {
2070		    Rotation r = output->initial_rotation;
2071		    if (xf86ModeWidth(mode, r) == pref_width &&
2072			    xf86ModeHeight(mode, r) == pref_height) {
2073			preferred[o] = mode;
2074			match = TRUE;
2075		    }
2076		}
2077
2078		all_match &= match;
2079	    }
2080
2081	    if (all_match &&
2082		    (pref_width*pref_height > max_pref_width*max_pref_height)) {
2083		for (o = -1; nextEnabledOutput(config, enabled, &o); )
2084		    preferred_match[o] = preferred[o];
2085		max_pref_width = pref_width;
2086		max_pref_height = pref_height;
2087		ret = TRUE;
2088	    }
2089	}
2090    }
2091
2092    /*
2093     * If there's no preferred mode, but only one monitor, pick the
2094     * biggest mode for its aspect ratio, assuming one exists.
2095     */
2096    if (!ret) do {
2097	int i = 0;
2098	float aspect = 0.0;
2099
2100	/* count the number of enabled outputs */
2101	for (i = 0, p = -1; nextEnabledOutput(config, enabled, &p); i++) ;
2102
2103	if (i != 1)
2104	    break;
2105
2106	p = -1;
2107	nextEnabledOutput(config, enabled, &p);
2108	if (config->output[p]->mm_height)
2109	    aspect = (float)config->output[p]->mm_width /
2110		     (float)config->output[p]->mm_height;
2111
2112	if (aspect)
2113	    preferred_match[p] = bestModeForAspect(config, enabled, aspect);
2114
2115	if (preferred_match[p])
2116	    ret = TRUE;
2117
2118    } while (0);
2119
2120    if (ret) {
2121	/* oh good, there is a match.  stash the selected modes and return. */
2122	memcpy(modes, preferred_match,
2123		config->num_output * sizeof(DisplayModePtr));
2124    }
2125
2126    free(preferred);
2127    free(preferred_match);
2128    return ret;
2129}
2130
2131static Bool
2132xf86TargetAspect(ScrnInfoPtr scrn, xf86CrtcConfigPtr config,
2133		 DisplayModePtr *modes, Bool *enabled,
2134		 int width, int height)
2135{
2136    int o;
2137    float aspect = 0.0, *aspects;
2138    xf86OutputPtr output;
2139    Bool ret = FALSE;
2140    DisplayModePtr guess = NULL, aspect_guess = NULL, base_guess = NULL;
2141
2142    aspects = xnfcalloc(config->num_output, sizeof(float));
2143
2144    /* collect the aspect ratios */
2145    for (o = -1; nextEnabledOutput(config, enabled, &o); ) {
2146	output = config->output[o];
2147	if (output->mm_height)
2148	    aspects[o] = (float)output->mm_width / (float)output->mm_height;
2149	else
2150	    aspects[o] = 4.0 / 3.0;
2151    }
2152
2153    /* check that they're all the same */
2154    for (o = -1; nextEnabledOutput(config, enabled, &o); ) {
2155	output = config->output[o];
2156	if (!aspect) {
2157	    aspect = aspects[o];
2158	} else if (!aspectMatch(aspect, aspects[o])) {
2159	    goto no_aspect_match;
2160	}
2161    }
2162
2163    /* if they're all 4:3, just skip ahead and save effort */
2164    if (!aspectMatch(aspect, 4.0/3.0))
2165	aspect_guess = bestModeForAspect(config, enabled, aspect);
2166
2167no_aspect_match:
2168    base_guess = bestModeForAspect(config, enabled, 4.0/3.0);
2169
2170    guess = biggestMode(base_guess, aspect_guess);
2171
2172    if (!guess)
2173	goto out;
2174
2175    /* found a mode that works everywhere, now apply it */
2176    for (o = -1; nextEnabledOutput(config, enabled, &o); ) {
2177	modes[o] = xf86OutputFindClosestMode(config->output[o], guess);
2178    }
2179    ret = TRUE;
2180
2181out:
2182    free(aspects);
2183    return ret;
2184}
2185
2186static Bool
2187xf86TargetFallback(ScrnInfoPtr scrn, xf86CrtcConfigPtr config,
2188		   DisplayModePtr *modes, Bool *enabled,
2189		   int width, int height)
2190{
2191    DisplayModePtr target_mode = NULL;
2192    Rotation target_rotation = RR_Rotate_0;
2193    DisplayModePtr default_mode;
2194    int default_preferred, target_preferred = 0, o;
2195
2196    /* User preferred > preferred > other modes */
2197    for (o = -1; nextEnabledOutput(config, enabled, &o); ) {
2198	default_mode = xf86DefaultMode (config->output[o], width, height);
2199	if (!default_mode)
2200	    continue;
2201
2202	default_preferred = (((default_mode->type & M_T_PREFERRED) != 0) +
2203		((default_mode->type & M_T_USERPREF) != 0));
2204
2205	if (default_preferred > target_preferred || !target_mode) {
2206	    target_mode = default_mode;
2207	    target_preferred = default_preferred;
2208	    target_rotation = config->output[o]->initial_rotation;
2209	    config->compat_output = o;
2210	}
2211    }
2212
2213    if (target_mode)
2214	modes[config->compat_output] = target_mode;
2215
2216    /* Fill in other output modes */
2217    for (o = -1; nextEnabledOutput(config, enabled, &o); ) {
2218	if (!modes[o])
2219	    modes[o] = xf86ClosestMode(config->output[o], target_mode,
2220				       target_rotation, width, height);
2221    }
2222
2223    return target_mode != NULL;
2224}
2225
2226static Bool
2227xf86TargetUserpref(ScrnInfoPtr scrn, xf86CrtcConfigPtr config,
2228		   DisplayModePtr *modes, Bool *enabled,
2229		   int width, int height)
2230{
2231    int o;
2232
2233    if (xf86UserConfiguredOutputs(scrn, modes))
2234	return xf86TargetFallback(scrn, config, modes, enabled, width, height);
2235
2236    for (o = -1; nextEnabledOutput(config, enabled, &o); )
2237	if (xf86OutputHasUserPreferredMode(config->output[o]))
2238	    return
2239		xf86TargetFallback(scrn, config, modes, enabled, width, height);
2240
2241    return FALSE;
2242}
2243
2244static Bool
2245xf86CrtcSetInitialGamma(xf86CrtcPtr crtc, float gamma_red, float gamma_green,
2246        float gamma_blue)
2247{
2248    int i, size = 256;
2249    CARD16 *red, *green, *blue;
2250
2251    red = malloc(3 * size * sizeof(CARD16));
2252    green = red + size;
2253    blue = green + size;
2254
2255     /* Only cause warning if user wanted gamma to be set. */
2256    if (!crtc->funcs->gamma_set && (gamma_red != 1.0 || gamma_green != 1.0 || gamma_blue != 1.0)) {
2257        free(red);
2258        return FALSE;
2259    } else if (!crtc->funcs->gamma_set) {
2260        free(red);
2261        return TRUE;
2262      }
2263
2264    /* At this early stage none of the randr-interface stuff is up.
2265     * So take the default gamma size for lack of something better.
2266     */
2267    for (i = 0; i < size; i++) {
2268        if (gamma_red == 1.0)
2269            red[i] = i << 8;
2270        else
2271            red[i] = (CARD16)(pow((double)i/(double)(size - 1),
2272			1. / (double)gamma_red) * (double)(size - 1) * 256);
2273
2274        if (gamma_green == 1.0)
2275            green[i] = i << 8;
2276        else
2277            green[i] = (CARD16)(pow((double)i/(double)(size - 1),
2278			1. / (double)gamma_green) * (double)(size - 1) * 256);
2279
2280        if (gamma_blue == 1.0)
2281            blue[i] = i << 8;
2282        else
2283            blue[i] = (CARD16)(pow((double)i/(double)(size - 1),
2284			1. / (double)gamma_blue) * (double)(size - 1) * 256);
2285    }
2286
2287    /* Default size is 256, so anything else is failure. */
2288    if (size != crtc->gamma_size) {
2289        free(red);
2290        return FALSE;
2291      }
2292
2293    crtc->gamma_size = size;
2294    memcpy (crtc->gamma_red, red, crtc->gamma_size * sizeof (CARD16));
2295    memcpy (crtc->gamma_green, green, crtc->gamma_size * sizeof (CARD16));
2296    memcpy (crtc->gamma_blue, blue, crtc->gamma_size * sizeof (CARD16));
2297
2298    /* Do not set gamma now, delay until the crtc is activated. */
2299
2300    free(red);
2301
2302    return TRUE;
2303}
2304
2305static Bool
2306xf86OutputSetInitialGamma(xf86OutputPtr output)
2307{
2308    XF86ConfMonitorPtr mon = output->conf_monitor;
2309    float gamma_red = 1.0, gamma_green = 1.0, gamma_blue = 1.0;
2310
2311    if (!mon)
2312        return TRUE;
2313
2314    if (!output->crtc)
2315        return FALSE;
2316
2317    /* Get configured values, where they exist. */
2318    if (mon->mon_gamma_red >= GAMMA_MIN &&
2319        mon->mon_gamma_red <= GAMMA_MAX)
2320            gamma_red = mon->mon_gamma_red;
2321
2322    if (mon->mon_gamma_green >= GAMMA_MIN &&
2323        mon->mon_gamma_green <= GAMMA_MAX)
2324            gamma_green = mon->mon_gamma_green;
2325
2326    if (mon->mon_gamma_blue >= GAMMA_MIN &&
2327        mon->mon_gamma_blue <= GAMMA_MAX)
2328            gamma_blue = mon->mon_gamma_blue;
2329
2330    /* This avoids setting gamma 1.0 in case another cloned output on this crtc has a specific gamma. */
2331    if (gamma_red != 1.0 || gamma_green != 1.0 || gamma_blue != 1.0) {
2332	xf86DrvMsg(output->scrn->scrnIndex, X_INFO, "Output %s wants gamma correction (%.1f, %.1f, %.1f)\n", output->name, gamma_red, gamma_green, gamma_blue);
2333	return xf86CrtcSetInitialGamma(output->crtc, gamma_red, gamma_green, gamma_blue);
2334    }else
2335	return TRUE;
2336}
2337
2338/**
2339 * Construct default screen configuration
2340 *
2341 * Given auto-detected (and, eventually, configured) values,
2342 * construct a usable configuration for the system
2343 *
2344 * canGrow indicates that the driver can resize the screen to larger than its
2345 * initially configured size via the config->funcs->resize hook.  If TRUE, this
2346 * function will set virtualX and virtualY to match the initial configuration
2347 * and leave config->max{Width,Height} alone.  If FALSE, it will bloat
2348 * virtual[XY] to include the largest modes and set config->max{Width,Height}
2349 * accordingly.
2350 */
2351
2352Bool
2353xf86InitialConfiguration (ScrnInfoPtr scrn, Bool canGrow)
2354{
2355    xf86CrtcConfigPtr	config = XF86_CRTC_CONFIG_PTR(scrn);
2356    int			o, c;
2357    xf86CrtcPtr		*crtcs;
2358    DisplayModePtr	*modes;
2359    Bool		*enabled;
2360    int			width, height;
2361    int			i = scrn->scrnIndex;
2362    Bool have_outputs = TRUE;
2363    Bool ret;
2364    Bool success = FALSE;
2365
2366    /* Set up the device options */
2367    config->options = xnfalloc (sizeof (xf86DeviceOptions));
2368    memcpy (config->options, xf86DeviceOptions, sizeof (xf86DeviceOptions));
2369    xf86ProcessOptions (scrn->scrnIndex,
2370			scrn->options,
2371			config->options);
2372    config->debug_modes = xf86ReturnOptValBool (config->options,
2373						OPTION_MODEDEBUG, FALSE);
2374
2375    if (scrn->display->virtualX)
2376	width = scrn->display->virtualX;
2377    else
2378	width = config->maxWidth;
2379    if (scrn->display->virtualY)
2380	height = scrn->display->virtualY;
2381    else
2382	height = config->maxHeight;
2383
2384    xf86ProbeOutputModes (scrn, width, height);
2385
2386    crtcs = xnfcalloc (config->num_output, sizeof (xf86CrtcPtr));
2387    modes = xnfcalloc (config->num_output, sizeof (DisplayModePtr));
2388    enabled = xnfcalloc (config->num_output, sizeof (Bool));
2389
2390    ret = xf86CollectEnabledOutputs(scrn, config, enabled);
2391    if (ret == FALSE && canGrow) {
2392	xf86DrvMsg(i, X_WARNING, "Unable to find connected outputs - setting %dx%d initial framebuffer\n",
2393		   NO_OUTPUT_DEFAULT_WIDTH, NO_OUTPUT_DEFAULT_HEIGHT);
2394	have_outputs = FALSE;
2395    } else {
2396	if (xf86TargetUserpref(scrn, config, modes, enabled, width, height))
2397	    xf86DrvMsg(i, X_INFO, "Using user preference for initial modes\n");
2398	else if (xf86TargetPreferred(scrn, config, modes, enabled, width, height))
2399	    xf86DrvMsg(i, X_INFO, "Using exact sizes for initial modes\n");
2400	else if (xf86TargetAspect(scrn, config, modes, enabled, width, height))
2401	    xf86DrvMsg(i, X_INFO, "Using fuzzy aspect match for initial modes\n");
2402	else if (xf86TargetFallback(scrn, config, modes, enabled, width, height))
2403	    xf86DrvMsg(i, X_INFO, "Using sloppy heuristic for initial modes\n");
2404	else
2405	    xf86DrvMsg(i, X_WARNING, "Unable to find initial modes\n");
2406    }
2407
2408    for (o = -1; nextEnabledOutput(config, enabled, &o); ) {
2409	if (!modes[o])
2410	    xf86DrvMsg (scrn->scrnIndex, X_ERROR,
2411			"Output %s enabled but has no modes\n",
2412			config->output[o]->name);
2413	else
2414	    xf86DrvMsg (scrn->scrnIndex, X_INFO,
2415			"Output %s using initial mode %s\n",
2416			config->output[o]->name, modes[o]->name);
2417    }
2418
2419    /*
2420     * Set the position of each output
2421     */
2422    if (!xf86InitialOutputPositions (scrn, modes))
2423	goto bailout;
2424
2425    /*
2426     * Set initial panning of each output
2427     */
2428    xf86InitialPanning (scrn);
2429
2430    /*
2431     * Assign CRTCs to fit output configuration
2432     */
2433    if (have_outputs && !xf86PickCrtcs (scrn, crtcs, modes, 0, width, height))
2434	goto bailout;
2435
2436    /* XXX override xf86 common frame computation code */
2437
2438    scrn->display->frameX0 = 0;
2439    scrn->display->frameY0 = 0;
2440
2441    for (c = 0; c < config->num_crtc; c++)
2442    {
2443	xf86CrtcPtr	crtc = config->crtc[c];
2444
2445	crtc->enabled = FALSE;
2446	memset (&crtc->desiredMode, '\0', sizeof (crtc->desiredMode));
2447	/* Set default gamma for all crtc's. */
2448	/* This is done to avoid problems later on with cloned outputs. */
2449	xf86CrtcSetInitialGamma(crtc, 1.0, 1.0, 1.0);
2450    }
2451
2452    if (xf86_crtc_supports_gamma(scrn))
2453	xf86DrvMsg(scrn->scrnIndex, X_INFO, "Using default gamma of (1.0, 1.0, 1.0) unless otherwise stated.\n");
2454
2455    /*
2456     * Set initial configuration
2457     */
2458    for (o = 0; o < config->num_output; o++)
2459    {
2460	xf86OutputPtr	output = config->output[o];
2461	DisplayModePtr	mode = modes[o];
2462        xf86CrtcPtr	crtc = crtcs[o];
2463
2464	if (mode && crtc)
2465	{
2466	    crtc->desiredMode = *mode;
2467	    crtc->desiredRotation = output->initial_rotation;
2468	    crtc->desiredX = output->initial_x;
2469	    crtc->desiredY = output->initial_y;
2470	    crtc->desiredTransformPresent = FALSE;
2471	    crtc->enabled = TRUE;
2472	    memcpy (&crtc->panningTotalArea,    &output->initialTotalArea,    sizeof(BoxRec));
2473	    memcpy (&crtc->panningTrackingArea, &output->initialTrackingArea, sizeof(BoxRec));
2474	    memcpy (crtc->panningBorder,        output->initialBorder,        4*sizeof(INT16));
2475	    output->crtc = crtc;
2476	    if (!xf86OutputSetInitialGamma(output))
2477		xf86DrvMsg (scrn->scrnIndex, X_WARNING, "Initial gamma correction for output %s: failed.\n", output->name);
2478	} else {
2479	    output->crtc = NULL;
2480	}
2481    }
2482
2483    if (scrn->display->virtualX == 0)
2484    {
2485	/*
2486	 * Expand virtual size to cover the current config and potential mode
2487	 * switches, if the driver can't enlarge the screen later.
2488	 */
2489	xf86DefaultScreenLimits (scrn, &width, &height, canGrow);
2490
2491	if (have_outputs == FALSE) {
2492	    if (width < NO_OUTPUT_DEFAULT_WIDTH && height < NO_OUTPUT_DEFAULT_HEIGHT) {
2493		width = NO_OUTPUT_DEFAULT_WIDTH;
2494		height = NO_OUTPUT_DEFAULT_HEIGHT;
2495	    }
2496	}
2497
2498	scrn->display->virtualX = width;
2499	scrn->display->virtualY = height;
2500    }
2501
2502    if (width > scrn->virtualX)
2503	scrn->virtualX = width;
2504    if (height > scrn->virtualY)
2505	scrn->virtualY = height;
2506
2507    /*
2508     * Make sure the configuration isn't too small.
2509     */
2510    if (width < config->minWidth || height < config->minHeight)
2511	goto bailout;
2512
2513    /*
2514     * Limit the crtc config to virtual[XY] if the driver can't grow the
2515     * desktop.
2516     */
2517    if (!canGrow)
2518    {
2519	xf86CrtcSetSizeRange (scrn, config->minWidth, config->minHeight,
2520			      width, height);
2521    }
2522
2523    xf86SetScrnInfoModes (scrn);
2524
2525    success = TRUE;
2526 bailout:
2527    free(crtcs);
2528    free(modes);
2529    free(enabled);
2530    return success;
2531}
2532
2533/*
2534 * Check the CRTC we're going to map each output to vs. it's current
2535 * CRTC.  If they don't match, we have to disable the output and the CRTC
2536 * since the driver will have to re-route things.
2537 */
2538static void
2539xf86PrepareOutputs (ScrnInfoPtr scrn)
2540{
2541    xf86CrtcConfigPtr   config = XF86_CRTC_CONFIG_PTR(scrn);
2542    int			o;
2543
2544    for (o = 0; o < config->num_output; o++) {
2545	xf86OutputPtr output = config->output[o];
2546#if RANDR_GET_CRTC_INTERFACE
2547	/* Disable outputs that are unused or will be re-routed */
2548	if (!output->funcs->get_crtc ||
2549	    output->crtc != (*output->funcs->get_crtc)(output) ||
2550	    output->crtc == NULL)
2551#endif
2552	    (*output->funcs->dpms)(output, DPMSModeOff);
2553    }
2554}
2555
2556static void
2557xf86PrepareCrtcs (ScrnInfoPtr scrn)
2558{
2559    xf86CrtcConfigPtr   config = XF86_CRTC_CONFIG_PTR(scrn);
2560    int			c;
2561
2562    for (c = 0; c < config->num_crtc; c++) {
2563#if RANDR_GET_CRTC_INTERFACE
2564	xf86CrtcPtr	crtc = config->crtc[c];
2565	xf86OutputPtr	output = NULL;
2566	uint32_t	desired_outputs = 0, current_outputs = 0;
2567	int		o;
2568
2569	for (o = 0; o < config->num_output; o++) {
2570	    output = config->output[o];
2571	    if (output->crtc == crtc)
2572		desired_outputs |= (1<<o);
2573	    /* If we can't tell where it's mapped, force it off */
2574	    if (!output->funcs->get_crtc) {
2575		desired_outputs = 0;
2576		break;
2577	    }
2578	    if ((*output->funcs->get_crtc)(output) == crtc)
2579		current_outputs |= (1<<o);
2580	}
2581
2582	/*
2583	 * If mappings are different or the CRTC is unused,
2584	 * we need to disable it
2585	 */
2586	if (desired_outputs != current_outputs ||
2587	    !desired_outputs)
2588	    (*crtc->funcs->dpms)(crtc, DPMSModeOff);
2589#else
2590	(*crtc->funcs->dpms)(crtc, DPMSModeOff);
2591#endif
2592    }
2593}
2594
2595/*
2596 * Using the desired mode information in each crtc, set
2597 * modes (used in EnterVT functions, or at server startup)
2598 */
2599
2600Bool
2601xf86SetDesiredModes (ScrnInfoPtr scrn)
2602{
2603    xf86CrtcConfigPtr   config = XF86_CRTC_CONFIG_PTR(scrn);
2604    xf86CrtcPtr         crtc = config->crtc[0];
2605    int			c;
2606
2607    /* A driver with this hook will take care of this */
2608    if (!crtc->funcs->set_mode_major) {
2609	xf86PrepareOutputs(scrn);
2610	xf86PrepareCrtcs(scrn);
2611    }
2612
2613    for (c = 0; c < config->num_crtc; c++)
2614    {
2615	xf86OutputPtr	output = NULL;
2616	int		o;
2617	RRTransformPtr	transform;
2618
2619	crtc = config->crtc[c];
2620
2621	/* Skip disabled CRTCs */
2622	if (!crtc->enabled)
2623	    continue;
2624
2625	if (xf86CompatOutput(scrn) && xf86CompatCrtc(scrn) == crtc)
2626	    output = xf86CompatOutput(scrn);
2627	else
2628	{
2629	    for (o = 0; o < config->num_output; o++)
2630		if (config->output[o]->crtc == crtc)
2631		{
2632		    output = config->output[o];
2633		    break;
2634		}
2635	}
2636	/* paranoia */
2637	if (!output)
2638	    continue;
2639
2640	/* Mark that we'll need to re-set the mode for sure */
2641	memset(&crtc->mode, 0, sizeof(crtc->mode));
2642	if (!crtc->desiredMode.CrtcHDisplay)
2643	{
2644	    DisplayModePtr  mode = xf86OutputFindClosestMode (output, scrn->currentMode);
2645
2646	    if (!mode)
2647		return FALSE;
2648	    crtc->desiredMode = *mode;
2649	    crtc->desiredRotation = RR_Rotate_0;
2650	    crtc->desiredTransformPresent = FALSE;
2651	    crtc->desiredX = 0;
2652	    crtc->desiredY = 0;
2653	}
2654
2655	if (crtc->desiredTransformPresent)
2656	    transform = &crtc->desiredTransform;
2657	else
2658	    transform = NULL;
2659	if (!xf86CrtcSetModeTransform (crtc, &crtc->desiredMode, crtc->desiredRotation,
2660				       transform, crtc->desiredX, crtc->desiredY))
2661	    return FALSE;
2662    }
2663
2664    xf86DisableUnusedFunctions(scrn);
2665    return TRUE;
2666}
2667
2668/**
2669 * In the current world order, there are lists of modes per output, which may
2670 * or may not include the mode that was asked to be set by XFree86's mode
2671 * selection.  Find the closest one, in the following preference order:
2672 *
2673 * - Equality
2674 * - Closer in size to the requested mode, but no larger
2675 * - Closer in refresh rate to the requested mode.
2676 */
2677
2678DisplayModePtr
2679xf86OutputFindClosestMode (xf86OutputPtr output, DisplayModePtr desired)
2680{
2681    DisplayModePtr	best = NULL, scan = NULL;
2682
2683    for (scan = output->probed_modes; scan != NULL; scan = scan->next)
2684    {
2685	/* If there's an exact match, we're done. */
2686	if (xf86ModesEqual(scan, desired)) {
2687	    best = desired;
2688	    break;
2689	}
2690
2691	/* Reject if it's larger than the desired mode. */
2692	if (scan->HDisplay > desired->HDisplay ||
2693	    scan->VDisplay > desired->VDisplay)
2694	{
2695	    continue;
2696	}
2697
2698	/*
2699	 * If we haven't picked a best mode yet, use the first
2700	 * one in the size range
2701	 */
2702	if (best == NULL)
2703	{
2704	    best = scan;
2705	    continue;
2706	}
2707
2708	/* Find if it's closer to the right size than the current best
2709	 * option.
2710	 */
2711	if ((scan->HDisplay > best->HDisplay &&
2712	     scan->VDisplay >= best->VDisplay) ||
2713	    (scan->HDisplay >= best->HDisplay &&
2714	     scan->VDisplay > best->VDisplay))
2715	{
2716	    best = scan;
2717	    continue;
2718	}
2719
2720	/* Find if it's still closer to the right refresh than the current
2721	 * best resolution.
2722	 */
2723	if (scan->HDisplay == best->HDisplay &&
2724	    scan->VDisplay == best->VDisplay &&
2725	    (fabs(scan->VRefresh - desired->VRefresh) <
2726	     fabs(best->VRefresh - desired->VRefresh))) {
2727	    best = scan;
2728	}
2729    }
2730    return best;
2731}
2732
2733/**
2734 * When setting a mode through XFree86-VidModeExtension or XFree86-DGA,
2735 * take the specified mode and apply it to the crtc connected to the compat
2736 * output. Then, find similar modes for the other outputs, as with the
2737 * InitialConfiguration code above. The goal is to clone the desired
2738 * mode across all outputs that are currently active.
2739 */
2740
2741Bool
2742xf86SetSingleMode (ScrnInfoPtr pScrn, DisplayModePtr desired, Rotation rotation)
2743{
2744    xf86CrtcConfigPtr	config = XF86_CRTC_CONFIG_PTR(pScrn);
2745    Bool		ok = TRUE;
2746    xf86OutputPtr	compat_output;
2747    DisplayModePtr	compat_mode = NULL;
2748    int			c;
2749
2750    /*
2751     * Let the compat output drive the final mode selection
2752     */
2753    compat_output = xf86CompatOutput(pScrn);
2754    if (compat_output)
2755	compat_mode = xf86OutputFindClosestMode (compat_output, desired);
2756    if (compat_mode)
2757	desired = compat_mode;
2758
2759    for (c = 0; c < config->num_crtc; c++)
2760    {
2761	xf86CrtcPtr	crtc = config->crtc[c];
2762	DisplayModePtr	crtc_mode = NULL;
2763	int		o;
2764
2765	if (!crtc->enabled)
2766	    continue;
2767
2768	for (o = 0; o < config->num_output; o++)
2769	{
2770	    xf86OutputPtr   output = config->output[o];
2771	    DisplayModePtr  output_mode;
2772
2773	    /* skip outputs not on this crtc */
2774	    if (output->crtc != crtc)
2775		continue;
2776
2777	    if (crtc_mode)
2778	    {
2779		output_mode = xf86OutputFindClosestMode (output, crtc_mode);
2780		if (output_mode != crtc_mode)
2781		    output->crtc = NULL;
2782	    }
2783	    else
2784		crtc_mode = xf86OutputFindClosestMode (output, desired);
2785	}
2786	if (!crtc_mode)
2787	{
2788	    crtc->enabled = FALSE;
2789	    continue;
2790	}
2791	if (!xf86CrtcSetModeTransform (crtc, crtc_mode, rotation, NULL, 0, 0))
2792	    ok = FALSE;
2793	else
2794	{
2795	    crtc->desiredMode = *crtc_mode;
2796	    crtc->desiredRotation = rotation;
2797	    crtc->desiredTransformPresent = FALSE;
2798	    crtc->desiredX = 0;
2799	    crtc->desiredY = 0;
2800	}
2801    }
2802    xf86DisableUnusedFunctions(pScrn);
2803#ifdef RANDR_12_INTERFACE
2804    xf86RandR12TellChanged (pScrn->pScreen);
2805#endif
2806    return ok;
2807}
2808
2809
2810/**
2811 * Set the DPMS power mode of all outputs and CRTCs.
2812 *
2813 * If the new mode is off, it will turn off outputs and then CRTCs.
2814 * Otherwise, it will affect CRTCs before outputs.
2815 */
2816void
2817xf86DPMSSet(ScrnInfoPtr scrn, int mode, int flags)
2818{
2819    xf86CrtcConfigPtr	config = XF86_CRTC_CONFIG_PTR(scrn);
2820    int			i;
2821
2822    if (!scrn->vtSema)
2823	return;
2824
2825    if (mode == DPMSModeOff) {
2826	for (i = 0; i < config->num_output; i++) {
2827	    xf86OutputPtr output = config->output[i];
2828	    if (output->crtc != NULL)
2829		(*output->funcs->dpms) (output, mode);
2830	}
2831    }
2832
2833    for (i = 0; i < config->num_crtc; i++) {
2834	xf86CrtcPtr crtc = config->crtc[i];
2835	if (crtc->enabled)
2836	    (*crtc->funcs->dpms) (crtc, mode);
2837    }
2838
2839    if (mode != DPMSModeOff) {
2840	for (i = 0; i < config->num_output; i++) {
2841	    xf86OutputPtr output = config->output[i];
2842	    if (output->crtc != NULL)
2843		(*output->funcs->dpms) (output, mode);
2844	}
2845    }
2846}
2847
2848/**
2849 * Implement the screensaver by just calling down into the driver DPMS hooks.
2850 *
2851 * Even for monitors with no DPMS support, by the definition of our DPMS hooks,
2852 * the outputs will still get disabled (blanked).
2853 */
2854Bool
2855xf86SaveScreen(ScreenPtr pScreen, int mode)
2856{
2857    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
2858
2859    if (xf86IsUnblank(mode))
2860	xf86DPMSSet(pScrn, DPMSModeOn, 0);
2861    else
2862	xf86DPMSSet(pScrn, DPMSModeOff, 0);
2863
2864    return TRUE;
2865}
2866
2867/**
2868 * Disable all inactive crtcs and outputs
2869 */
2870void
2871xf86DisableUnusedFunctions(ScrnInfoPtr pScrn)
2872{
2873    xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
2874    int			o, c;
2875
2876    for (o = 0; o < xf86_config->num_output; o++)
2877    {
2878	xf86OutputPtr  output = xf86_config->output[o];
2879	if (!output->crtc)
2880	    (*output->funcs->dpms)(output, DPMSModeOff);
2881    }
2882
2883    for (c = 0; c < xf86_config->num_crtc; c++)
2884    {
2885	xf86CrtcPtr crtc = xf86_config->crtc[c];
2886
2887	if (!crtc->enabled)
2888	{
2889	    crtc->funcs->dpms(crtc, DPMSModeOff);
2890	    memset(&crtc->mode, 0, sizeof(crtc->mode));
2891	    xf86RotateDestroy(crtc);
2892	    crtc->active = FALSE;
2893	}
2894    }
2895    if (pScrn->pScreen)
2896	xf86_crtc_notify(pScrn->pScreen);
2897    if (pScrn->ModeSet)
2898	pScrn->ModeSet(pScrn);
2899}
2900
2901#ifdef RANDR_12_INTERFACE
2902
2903#define EDID_ATOM_NAME		"EDID"
2904
2905/**
2906 * Set the RandR EDID property
2907 */
2908static void
2909xf86OutputSetEDIDProperty (xf86OutputPtr output, void *data, int data_len)
2910{
2911    Atom edid_atom = MakeAtom(EDID_ATOM_NAME, sizeof(EDID_ATOM_NAME) - 1, TRUE);
2912
2913    /* This may get called before the RandR resources have been created */
2914    if (output->randr_output == NULL)
2915	return;
2916
2917    if (data_len != 0) {
2918	RRChangeOutputProperty(output->randr_output, edid_atom, XA_INTEGER, 8,
2919			       PropModeReplace, data_len, data, FALSE, TRUE);
2920    } else {
2921	RRDeleteOutputProperty(output->randr_output, edid_atom);
2922    }
2923}
2924
2925#endif
2926
2927/* Pull out a phyiscal size from a detailed timing if available. */
2928struct det_phySize_parameter {
2929    xf86OutputPtr output;
2930    ddc_quirk_t quirks;
2931    Bool ret;
2932};
2933
2934static void  handle_detailed_physical_size(struct detailed_monitor_section
2935		                          *det_mon, void *data)
2936{
2937    struct det_phySize_parameter *p;
2938    p = (struct det_phySize_parameter *)data;
2939
2940    if (p->ret == TRUE )
2941        return ;
2942
2943    xf86DetTimingApplyQuirks(det_mon, p->quirks,
2944                             p->output->MonInfo->features.hsize,
2945                             p->output->MonInfo->features.vsize);
2946    if (det_mon->type == DT &&
2947        det_mon->section.d_timings.h_size != 0 &&
2948        det_mon->section.d_timings.v_size != 0) {
2949
2950        p->output->mm_width = det_mon->section.d_timings.h_size;
2951        p->output->mm_height = det_mon->section.d_timings.v_size;
2952        p->ret = TRUE;
2953    }
2954}
2955
2956/**
2957 * Set the EDID information for the specified output
2958 */
2959void
2960xf86OutputSetEDID (xf86OutputPtr output, xf86MonPtr edid_mon)
2961{
2962    ScrnInfoPtr		scrn = output->scrn;
2963    xf86CrtcConfigPtr	config = XF86_CRTC_CONFIG_PTR(scrn);
2964    Bool		debug_modes = config->debug_modes || xf86Initialising;
2965#ifdef RANDR_12_INTERFACE
2966    int			size;
2967#endif
2968
2969    free(output->MonInfo);
2970
2971    output->MonInfo = edid_mon;
2972    output->mm_width = 0;
2973    output->mm_height = 0;
2974
2975    if (debug_modes) {
2976	xf86DrvMsg(scrn->scrnIndex, X_INFO, "EDID for output %s\n",
2977		   output->name);
2978	xf86PrintEDID(edid_mon);
2979    }
2980
2981    /* Set the DDC properties for the 'compat' output */
2982    if (output == xf86CompatOutput(scrn))
2983        xf86SetDDCproperties(scrn, edid_mon);
2984
2985#ifdef RANDR_12_INTERFACE
2986    /* Set the RandR output properties */
2987    size = 0;
2988    if (edid_mon)
2989    {
2990	if (edid_mon->ver.version == 1) {
2991	    size = 128;
2992	    if (edid_mon->flags & EDID_COMPLETE_RAWDATA)
2993		size += edid_mon->no_sections * 128;
2994	} else if (edid_mon->ver.version == 2)
2995	    size = 256;
2996    }
2997    xf86OutputSetEDIDProperty (output, edid_mon ? edid_mon->rawData : NULL, size);
2998#endif
2999
3000    if (edid_mon) {
3001
3002        struct det_phySize_parameter p;
3003        p.output = output;
3004        p.quirks = xf86DDCDetectQuirks(scrn->scrnIndex,edid_mon, FALSE);
3005        p.ret = FALSE;
3006        xf86ForEachDetailedBlock(edid_mon,
3007                                 handle_detailed_physical_size, &p);
3008
3009	/* if no mm size is available from a detailed timing, check the max size field */
3010	if ((!output->mm_width || !output->mm_height) &&
3011	    (edid_mon->features.hsize && edid_mon->features.vsize))
3012	{
3013	    output->mm_width = edid_mon->features.hsize * 10;
3014	    output->mm_height = edid_mon->features.vsize * 10;
3015	}
3016    }
3017}
3018
3019/**
3020 * Return the list of modes supported by the EDID information
3021 * stored in 'output'
3022 */
3023DisplayModePtr
3024xf86OutputGetEDIDModes (xf86OutputPtr output)
3025{
3026    ScrnInfoPtr	scrn = output->scrn;
3027    xf86MonPtr	edid_mon = output->MonInfo;
3028
3029    if (!edid_mon)
3030	return NULL;
3031    return xf86DDCGetModes(scrn->scrnIndex, edid_mon);
3032}
3033
3034/* maybe we should care about DDC1?  meh. */
3035xf86MonPtr
3036xf86OutputGetEDID (xf86OutputPtr output, I2CBusPtr pDDCBus)
3037{
3038    ScrnInfoPtr	scrn = output->scrn;
3039    xf86MonPtr mon;
3040
3041    mon = xf86DoEEDID(scrn->scrnIndex, pDDCBus, TRUE);
3042    if (mon)
3043        xf86DDCApplyQuirks(scrn->scrnIndex, mon);
3044
3045    return mon;
3046}
3047
3048static char *_xf86ConnectorNames[] = {
3049					"None", "VGA", "DVI-I", "DVI-D",
3050					"DVI-A", "Composite", "S-Video",
3051					"Component", "LFP", "Proprietary",
3052					"HDMI", "DisplayPort",
3053				     };
3054char *
3055xf86ConnectorGetName(xf86ConnectorType connector)
3056{
3057    return _xf86ConnectorNames[connector];
3058}
3059
3060static void
3061x86_crtc_box_intersect(BoxPtr dest, BoxPtr a, BoxPtr b)
3062{
3063    dest->x1 = a->x1 > b->x1 ? a->x1 : b->x1;
3064    dest->x2 = a->x2 < b->x2 ? a->x2 : b->x2;
3065    dest->y1 = a->y1 > b->y1 ? a->y1 : b->y1;
3066    dest->y2 = a->y2 < b->y2 ? a->y2 : b->y2;
3067
3068    if (dest->x1 >= dest->x2 || dest->y1 >= dest->y2)
3069	dest->x1 = dest->x2 = dest->y1 = dest->y2 = 0;
3070}
3071
3072static void
3073x86_crtc_box(xf86CrtcPtr crtc, BoxPtr crtc_box)
3074{
3075    if (crtc->enabled) {
3076	crtc_box->x1 = crtc->x;
3077	crtc_box->x2 = crtc->x + xf86ModeWidth(&crtc->mode, crtc->rotation);
3078	crtc_box->y1 = crtc->y;
3079	crtc_box->y2 = crtc->y + xf86ModeHeight(&crtc->mode, crtc->rotation);
3080    } else
3081	crtc_box->x1 = crtc_box->x2 = crtc_box->y1 = crtc_box->y2 = 0;
3082}
3083
3084static int
3085xf86_crtc_box_area(BoxPtr box)
3086{
3087    return (int) (box->x2 - box->x1) * (int) (box->y2 - box->y1);
3088}
3089
3090/*
3091 * Return the crtc covering 'box'. If two crtcs cover a portion of
3092 * 'box', then prefer 'desired'. If 'desired' is NULL, then prefer the crtc
3093 * with greater coverage
3094 */
3095
3096static xf86CrtcPtr
3097xf86_covering_crtc(ScrnInfoPtr pScrn,
3098		   BoxPtr      box,
3099		   xf86CrtcPtr desired,
3100		   BoxPtr      crtc_box_ret)
3101{
3102    xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
3103    xf86CrtcPtr		crtc, best_crtc;
3104    int			coverage, best_coverage;
3105    int			c;
3106    BoxRec		crtc_box, cover_box;
3107
3108    best_crtc = NULL;
3109    best_coverage = 0;
3110    crtc_box_ret->x1 = 0;
3111    crtc_box_ret->x2 = 0;
3112    crtc_box_ret->y1 = 0;
3113    crtc_box_ret->y2 = 0;
3114    for (c = 0; c < xf86_config->num_crtc; c++) {
3115	crtc = xf86_config->crtc[c];
3116	x86_crtc_box(crtc, &crtc_box);
3117	x86_crtc_box_intersect(&cover_box, &crtc_box, box);
3118	coverage = xf86_crtc_box_area(&cover_box);
3119	if (coverage && crtc == desired) {
3120	    *crtc_box_ret = crtc_box;
3121	    return crtc;
3122	} else if (coverage > best_coverage) {
3123	    *crtc_box_ret = crtc_box;
3124	    best_crtc = crtc;
3125	    best_coverage = coverage;
3126	}
3127    }
3128    return best_crtc;
3129}
3130
3131/*
3132 * For overlay video, compute the relevant CRTC and
3133 * clip video to that.
3134 *
3135 * returning FALSE means there was a memory failure of some kind,
3136 * not that the video shouldn't be displayed
3137 */
3138
3139Bool
3140xf86_crtc_clip_video_helper(ScrnInfoPtr pScrn,
3141			    xf86CrtcPtr *crtc_ret,
3142			    xf86CrtcPtr desired_crtc,
3143			    BoxPtr      dst,
3144			    INT32	*xa,
3145			    INT32	*xb,
3146			    INT32	*ya,
3147			    INT32	*yb,
3148			    RegionPtr   reg,
3149			    INT32	width,
3150			    INT32	height)
3151{
3152    Bool	ret;
3153    RegionRec	crtc_region_local;
3154    RegionPtr	crtc_region = reg;
3155
3156    if (crtc_ret) {
3157	BoxRec		crtc_box;
3158	xf86CrtcPtr	crtc = xf86_covering_crtc(pScrn, dst,
3159						  desired_crtc,
3160						  &crtc_box);
3161
3162	if (crtc) {
3163	    RegionInit(&crtc_region_local, &crtc_box, 1);
3164	    crtc_region = &crtc_region_local;
3165	    RegionIntersect(crtc_region, crtc_region, reg);
3166	}
3167	*crtc_ret = crtc;
3168    }
3169
3170    ret = xf86XVClipVideoHelper(dst, xa, xb, ya, yb,
3171				crtc_region, width, height);
3172
3173    if (crtc_region != reg)
3174	RegionUninit(&crtc_region_local);
3175
3176    return ret;
3177}
3178
3179xf86_crtc_notify_proc_ptr
3180xf86_wrap_crtc_notify (ScreenPtr screen, xf86_crtc_notify_proc_ptr new)
3181{
3182    if (xf86CrtcConfigPrivateIndex != -1)
3183    {
3184	ScrnInfoPtr		scrn = xf86Screens[screen->myNum];
3185	xf86CrtcConfigPtr	config = XF86_CRTC_CONFIG_PTR(scrn);
3186	xf86_crtc_notify_proc_ptr	old;
3187
3188	old = config->xf86_crtc_notify;
3189	config->xf86_crtc_notify = new;
3190	return old;
3191    }
3192    return NULL;
3193}
3194
3195void
3196xf86_unwrap_crtc_notify(ScreenPtr screen, xf86_crtc_notify_proc_ptr old)
3197{
3198    if (xf86CrtcConfigPrivateIndex != -1)
3199    {
3200	ScrnInfoPtr		scrn = xf86Screens[screen->myNum];
3201	xf86CrtcConfigPtr	config = XF86_CRTC_CONFIG_PTR(scrn);
3202
3203	config->xf86_crtc_notify = old;
3204    }
3205}
3206
3207void
3208xf86_crtc_notify(ScreenPtr screen)
3209{
3210    ScrnInfoPtr		scrn = xf86Screens[screen->myNum];
3211    xf86CrtcConfigPtr	config = XF86_CRTC_CONFIG_PTR(scrn);
3212
3213    if (config->xf86_crtc_notify)
3214	config->xf86_crtc_notify(screen);
3215}
3216
3217Bool
3218xf86_crtc_supports_gamma(ScrnInfoPtr pScrn)
3219{
3220    if (xf86CrtcConfigPrivateIndex != -1) {
3221	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
3222	xf86CrtcPtr crtc;
3223
3224	/* for multiple drivers loaded we need this */
3225	if (!xf86_config)
3226		return FALSE;
3227	if (xf86_config->num_crtc == 0)
3228	    return FALSE;
3229	crtc = xf86_config->crtc[0];
3230
3231	return crtc->funcs->gamma_set != NULL;
3232    }
3233
3234    return FALSE;
3235}
3236