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