105b261ecSmrg/*
205b261ecSmrg * Copyright (c) 1997-2003 by The XFree86 Project, Inc.
305b261ecSmrg *
405b261ecSmrg * Permission is hereby granted, free of charge, to any person obtaining a
505b261ecSmrg * copy of this software and associated documentation files (the "Software"),
605b261ecSmrg * to deal in the Software without restriction, including without limitation
705b261ecSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
805b261ecSmrg * and/or sell copies of the Software, and to permit persons to whom the
905b261ecSmrg * Software is furnished to do so, subject to the following conditions:
1005b261ecSmrg *
1105b261ecSmrg * The above copyright notice and this permission notice shall be included in
1205b261ecSmrg * all copies or substantial portions of the Software.
1305b261ecSmrg *
1405b261ecSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1505b261ecSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1605b261ecSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1705b261ecSmrg * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
1805b261ecSmrg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
1905b261ecSmrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
2005b261ecSmrg * OTHER DEALINGS IN THE SOFTWARE.
2105b261ecSmrg *
2205b261ecSmrg * Except as contained in this notice, the name of the copyright holder(s)
2305b261ecSmrg * and author(s) shall not be used in advertising or otherwise to promote
2405b261ecSmrg * the sale, use or other dealings in this Software without prior written
2505b261ecSmrg * authorization from the copyright holder(s) and author(s).
2605b261ecSmrg */
2705b261ecSmrg
286747b715Smrg/*
296747b715Smrg * LCM() and scanLineWidth() are:
306747b715Smrg *
316747b715Smrg * Copyright 1997 through 2004 by Marc Aurele La France (TSI @ UQV), tsi@xfree86.org
326747b715Smrg *
336747b715Smrg * Permission to use, copy, modify, distribute, and sell this software and its
346747b715Smrg * documentation for any purpose is hereby granted without fee, provided that
356747b715Smrg * the above copyright notice appear in all copies and that both that copyright
366747b715Smrg * notice and this permission notice appear in supporting documentation, and
376747b715Smrg * that the name of Marc Aurele La France not be used in advertising or
386747b715Smrg * publicity pertaining to distribution of the software without specific,
396747b715Smrg * written prior permission.  Marc Aurele La France makes no representations
406747b715Smrg * about the suitability of this software for any purpose.  It is provided
416747b715Smrg * "as-is" without express or implied warranty.
426747b715Smrg *
436747b715Smrg * MARC AURELE LA FRANCE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
446747b715Smrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO
456747b715Smrg * EVENT SHALL MARC AURELE LA FRANCE BE LIABLE FOR ANY SPECIAL, INDIRECT OR
466747b715Smrg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
476747b715Smrg * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
486747b715Smrg * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
496747b715Smrg * PERFORMANCE OF THIS SOFTWARE.
506747b715Smrg *
516747b715Smrg * Copyright 1990,91,92,93 by Thomas Roell, Germany.
526747b715Smrg * Copyright 1991,92,93    by SGCS (Snitily Graphics Consulting Services), USA.
536747b715Smrg *
546747b715Smrg * Permission to use, copy, modify, distribute, and sell this software
556747b715Smrg * and its documentation for any purpose is hereby granted without fee,
566747b715Smrg * provided that the above copyright notice appear in all copies and
576747b715Smrg * that both that copyright notice and this  permission notice appear
586747b715Smrg * in supporting documentation, and that the name of Thomas Roell nor
596747b715Smrg * SGCS be used in advertising or publicity pertaining to distribution
606747b715Smrg * of the software without specific, written prior permission.
616747b715Smrg * Thomas Roell nor SGCS makes no representations about the suitability
626747b715Smrg * of this software for any purpose. It is provided "as is" without
636747b715Smrg * express or implied warranty.
646747b715Smrg *
656747b715Smrg * THOMAS ROELL AND SGCS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
666747b715Smrg * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
676747b715Smrg * FITNESS, IN NO EVENT SHALL THOMAS ROELL OR SGCS BE LIABLE FOR ANY
686747b715Smrg * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
696747b715Smrg * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
706747b715Smrg * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
716747b715Smrg * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
726747b715Smrg */
736747b715Smrg
7405b261ecSmrg/*
7505b261ecSmrg * Authors: Dirk Hohndel <hohndel@XFree86.Org>
7605b261ecSmrg *          David Dawes <dawes@XFree86.Org>
7705b261ecSmrg *          Marc La France <tsi@XFree86.Org>
7805b261ecSmrg *          ... and others
7905b261ecSmrg *
8005b261ecSmrg * This file includes helper functions for mode related things.
8105b261ecSmrg */
8205b261ecSmrg
8305b261ecSmrg#ifdef HAVE_XORG_CONFIG_H
8405b261ecSmrg#include <xorg-config.h>
8505b261ecSmrg#endif
8605b261ecSmrg
8705b261ecSmrg#include <X11/X.h>
884642e01fSmrg#include "xf86Modes.h"
891b5d61b8Smrg#include "xf86Crtc.h"
9005b261ecSmrg#include "os.h"
9105b261ecSmrg#include "servermd.h"
9205b261ecSmrg#include "globals.h"
9305b261ecSmrg#include "xf86.h"
9405b261ecSmrg#include "xf86Priv.h"
9505b261ecSmrg#include "edid.h"
9605b261ecSmrg
9705b261ecSmrgstatic void
9805b261ecSmrgprintModeRejectMessage(int index, DisplayModePtr p, int status)
9905b261ecSmrg{
10035c4bbdfSmrg    const char *type;
10105b261ecSmrg
10205b261ecSmrg    if (p->type & M_T_BUILTIN)
10335c4bbdfSmrg        type = "built-in ";
10405b261ecSmrg    else if (p->type & M_T_DEFAULT)
10535c4bbdfSmrg        type = "default ";
10605b261ecSmrg    else if (p->type & M_T_DRIVER)
10735c4bbdfSmrg        type = "driver ";
10805b261ecSmrg    else
10935c4bbdfSmrg        type = "";
11005b261ecSmrg
11105b261ecSmrg    xf86DrvMsg(index, X_INFO, "Not using %smode \"%s\" (%s)\n", type, p->name,
11235c4bbdfSmrg               xf86ModeStatusToString(status));
11305b261ecSmrg}
11405b261ecSmrg
11505b261ecSmrg/*
11635c4bbdfSmrg * Find closest clock to given frequency (in kHz).  This assumes the
11735c4bbdfSmrg * number of clocks is greater than zero.
11805b261ecSmrg */
11935c4bbdfSmrgstatic int
12005b261ecSmrgxf86GetNearestClock(ScrnInfoPtr scrp, int freq, Bool allowDiv2,
12135c4bbdfSmrg                    int DivFactor, int MulFactor, int *divider)
12205b261ecSmrg{
12305b261ecSmrg    int nearestClock = 0, nearestDiv = 1;
12405b261ecSmrg    int minimumGap = abs(freq - scrp->clock[0]);
12505b261ecSmrg    int i, j, k, gap;
12605b261ecSmrg
12705b261ecSmrg    if (allowDiv2)
12835c4bbdfSmrg        k = 2;
12905b261ecSmrg    else
13035c4bbdfSmrg        k = 1;
13105b261ecSmrg
13205b261ecSmrg    /* Must set this here in case the best match is scrp->clock[0] */
13305b261ecSmrg    if (divider != NULL)
13435c4bbdfSmrg        *divider = 0;
13535c4bbdfSmrg
13635c4bbdfSmrg    for (i = 0; i < scrp->numClocks; i++) {
13735c4bbdfSmrg        for (j = 1; j <= k; j++) {
13835c4bbdfSmrg            gap = abs((freq * j) - ((scrp->clock[i] * DivFactor) / MulFactor));
13935c4bbdfSmrg            if ((gap < minimumGap) || ((gap == minimumGap) && (j < nearestDiv))) {
14035c4bbdfSmrg                minimumGap = gap;
14135c4bbdfSmrg                nearestClock = i;
14235c4bbdfSmrg                nearestDiv = j;
14335c4bbdfSmrg                if (divider != NULL)
14435c4bbdfSmrg                    *divider = (j - 1) * V_CLKDIV2;
14535c4bbdfSmrg            }
14635c4bbdfSmrg        }
14705b261ecSmrg    }
14805b261ecSmrg    return nearestClock;
14905b261ecSmrg}
15005b261ecSmrg
15105b261ecSmrg/*
15205b261ecSmrg * xf86ModeStatusToString
15305b261ecSmrg *
15405b261ecSmrg * Convert a ModeStatus value to a printable message
15505b261ecSmrg */
15605b261ecSmrg
1576747b715Smrgconst char *
15805b261ecSmrgxf86ModeStatusToString(ModeStatus status)
15905b261ecSmrg{
16005b261ecSmrg    switch (status) {
16105b261ecSmrg    case MODE_OK:
16235c4bbdfSmrg        return "Mode OK";
16305b261ecSmrg    case MODE_HSYNC:
16435c4bbdfSmrg        return "hsync out of range";
16505b261ecSmrg    case MODE_VSYNC:
16635c4bbdfSmrg        return "vrefresh out of range";
16705b261ecSmrg    case MODE_H_ILLEGAL:
16835c4bbdfSmrg        return "illegal horizontal timings";
16905b261ecSmrg    case MODE_V_ILLEGAL:
17035c4bbdfSmrg        return "illegal vertical timings";
17105b261ecSmrg    case MODE_BAD_WIDTH:
17235c4bbdfSmrg        return "width requires unsupported line pitch";
17305b261ecSmrg    case MODE_NOMODE:
17435c4bbdfSmrg        return "no mode of this name";
17505b261ecSmrg    case MODE_NO_INTERLACE:
17635c4bbdfSmrg        return "interlace mode not supported";
17705b261ecSmrg    case MODE_NO_DBLESCAN:
17835c4bbdfSmrg        return "doublescan mode not supported";
17905b261ecSmrg    case MODE_NO_VSCAN:
18035c4bbdfSmrg        return "multiscan mode not supported";
18105b261ecSmrg    case MODE_MEM:
18235c4bbdfSmrg        return "insufficient memory for mode";
18305b261ecSmrg    case MODE_VIRTUAL_X:
18435c4bbdfSmrg        return "width too large for virtual size";
18505b261ecSmrg    case MODE_VIRTUAL_Y:
18635c4bbdfSmrg        return "height too large for virtual size";
18705b261ecSmrg    case MODE_MEM_VIRT:
18835c4bbdfSmrg        return "insufficient memory given virtual size";
18905b261ecSmrg    case MODE_NOCLOCK:
19035c4bbdfSmrg        return "no clock available for mode";
19105b261ecSmrg    case MODE_CLOCK_HIGH:
19235c4bbdfSmrg        return "mode clock too high";
19305b261ecSmrg    case MODE_CLOCK_LOW:
19435c4bbdfSmrg        return "mode clock too low";
19505b261ecSmrg    case MODE_CLOCK_RANGE:
19635c4bbdfSmrg        return "bad mode clock/interlace/doublescan";
19705b261ecSmrg    case MODE_BAD_HVALUE:
19835c4bbdfSmrg        return "horizontal timing out of range";
19905b261ecSmrg    case MODE_BAD_VVALUE:
20035c4bbdfSmrg        return "vertical timing out of range";
20105b261ecSmrg    case MODE_BAD_VSCAN:
20235c4bbdfSmrg        return "VScan value out of range";
20305b261ecSmrg    case MODE_HSYNC_NARROW:
20435c4bbdfSmrg        return "horizontal sync too narrow";
20505b261ecSmrg    case MODE_HSYNC_WIDE:
20635c4bbdfSmrg        return "horizontal sync too wide";
20705b261ecSmrg    case MODE_HBLANK_NARROW:
20835c4bbdfSmrg        return "horizontal blanking too narrow";
20905b261ecSmrg    case MODE_HBLANK_WIDE:
21035c4bbdfSmrg        return "horizontal blanking too wide";
21105b261ecSmrg    case MODE_VSYNC_NARROW:
21235c4bbdfSmrg        return "vertical sync too narrow";
21305b261ecSmrg    case MODE_VSYNC_WIDE:
21435c4bbdfSmrg        return "vertical sync too wide";
21505b261ecSmrg    case MODE_VBLANK_NARROW:
21635c4bbdfSmrg        return "vertical blanking too narrow";
21705b261ecSmrg    case MODE_VBLANK_WIDE:
21835c4bbdfSmrg        return "vertical blanking too wide";
21905b261ecSmrg    case MODE_PANEL:
22035c4bbdfSmrg        return "exceeds panel dimensions";
22105b261ecSmrg    case MODE_INTERLACE_WIDTH:
22235c4bbdfSmrg        return "width too large for interlaced mode";
22305b261ecSmrg    case MODE_ONE_WIDTH:
22405b261ecSmrg        return "all modes must have the same width";
22505b261ecSmrg    case MODE_ONE_HEIGHT:
22605b261ecSmrg        return "all modes must have the same height";
22705b261ecSmrg    case MODE_ONE_SIZE:
22805b261ecSmrg        return "all modes must have the same resolution";
22905b261ecSmrg    case MODE_NO_REDUCED:
23005b261ecSmrg        return "monitor doesn't support reduced blanking";
2314642e01fSmrg    case MODE_BANDWIDTH:
23235c4bbdfSmrg        return "mode requires too much memory bandwidth";
23305b261ecSmrg    case MODE_BAD:
23435c4bbdfSmrg        return "unknown reason";
23505b261ecSmrg    case MODE_ERROR:
23635c4bbdfSmrg        return "internal error";
23705b261ecSmrg    default:
23835c4bbdfSmrg        return "unknown";
23905b261ecSmrg    }
24005b261ecSmrg}
24105b261ecSmrg
24205b261ecSmrg/*
24305b261ecSmrg * xf86ShowClockRanges() -- Print the clock ranges allowed
24405b261ecSmrg * and the clock values scaled by ClockMulFactor and ClockDivFactor
24505b261ecSmrg */
2466747b715Smrgvoid
24705b261ecSmrgxf86ShowClockRanges(ScrnInfoPtr scrp, ClockRangePtr clockRanges)
24805b261ecSmrg{
24905b261ecSmrg    ClockRangePtr cp;
25005b261ecSmrg    int MulFactor = 1;
25105b261ecSmrg    int DivFactor = 1;
25205b261ecSmrg    int i, j;
25305b261ecSmrg    int scaledClock;
25405b261ecSmrg
25505b261ecSmrg    for (cp = clockRanges; cp != NULL; cp = cp->next) {
25635c4bbdfSmrg        DivFactor = max(1, cp->ClockDivFactor);
25735c4bbdfSmrg        MulFactor = max(1, cp->ClockMulFactor);
25835c4bbdfSmrg        if (scrp->progClock) {
25935c4bbdfSmrg            if (cp->minClock) {
26035c4bbdfSmrg                if (cp->maxClock) {
26135c4bbdfSmrg                    xf86DrvMsg(scrp->scrnIndex, X_INFO,
26235c4bbdfSmrg                               "Clock range: %6.2f to %6.2f MHz\n",
26335c4bbdfSmrg                               (double) cp->minClock / 1000.0,
26435c4bbdfSmrg                               (double) cp->maxClock / 1000.0);
26535c4bbdfSmrg                }
26635c4bbdfSmrg                else {
26735c4bbdfSmrg                    xf86DrvMsg(scrp->scrnIndex, X_INFO,
26835c4bbdfSmrg                               "Minimum clock: %6.2f MHz\n",
26935c4bbdfSmrg                               (double) cp->minClock / 1000.0);
27035c4bbdfSmrg                }
27135c4bbdfSmrg            }
27235c4bbdfSmrg            else {
27335c4bbdfSmrg                if (cp->maxClock) {
27435c4bbdfSmrg                    xf86DrvMsg(scrp->scrnIndex, X_INFO,
27535c4bbdfSmrg                               "Maximum clock: %6.2f MHz\n",
27635c4bbdfSmrg                               (double) cp->maxClock / 1000.0);
27735c4bbdfSmrg                }
27835c4bbdfSmrg            }
27935c4bbdfSmrg        }
28035c4bbdfSmrg        else if (DivFactor > 1 || MulFactor > 1) {
28135c4bbdfSmrg            j = 0;
28235c4bbdfSmrg            for (i = 0; i < scrp->numClocks; i++) {
28335c4bbdfSmrg                scaledClock = (scrp->clock[i] * DivFactor) / MulFactor;
28435c4bbdfSmrg                if (scaledClock >= cp->minClock && scaledClock <= cp->maxClock) {
28535c4bbdfSmrg                    if ((j % 8) == 0) {
28635c4bbdfSmrg                        if (j > 0)
28735c4bbdfSmrg                            xf86ErrorF("\n");
28835c4bbdfSmrg                        xf86DrvMsg(scrp->scrnIndex, X_INFO, "scaled clocks:");
28935c4bbdfSmrg                    }
29035c4bbdfSmrg                    xf86ErrorF(" %6.2f", (double) scaledClock / 1000.0);
29135c4bbdfSmrg                    j++;
29235c4bbdfSmrg                }
29335c4bbdfSmrg            }
29435c4bbdfSmrg            xf86ErrorF("\n");
29535c4bbdfSmrg        }
29605b261ecSmrg    }
29705b261ecSmrg}
29805b261ecSmrg
2996747b715Smrgstatic Bool
3006747b715SmrgmodeInClockRange(ClockRangePtr cp, DisplayModePtr p)
3016747b715Smrg{
3026747b715Smrg    return ((p->Clock >= cp->minClock) &&
30335c4bbdfSmrg            (p->Clock <= cp->maxClock) &&
30435c4bbdfSmrg            (cp->interlaceAllowed || !(p->Flags & V_INTERLACE)) &&
30535c4bbdfSmrg            (cp->doubleScanAllowed ||
30635c4bbdfSmrg             ((p->VScan <= 1) && !(p->Flags & V_DBLSCAN))));
3076747b715Smrg}
30805b261ecSmrg
30905b261ecSmrg/*
31005b261ecSmrg * xf86FindClockRangeForMode()    [... like the name says ...]
31105b261ecSmrg */
31205b261ecSmrgstatic ClockRangePtr
31305b261ecSmrgxf86FindClockRangeForMode(ClockRangePtr clockRanges, DisplayModePtr p)
31405b261ecSmrg{
31505b261ecSmrg    ClockRangePtr cp;
31605b261ecSmrg
31735c4bbdfSmrg    for (cp = clockRanges;; cp = cp->next)
31835c4bbdfSmrg        if (!cp || modeInClockRange(cp, p))
31935c4bbdfSmrg            return cp;
32005b261ecSmrg}
32105b261ecSmrg
32205b261ecSmrg/*
32305b261ecSmrg * xf86HandleBuiltinMode() - handles built-in modes
32405b261ecSmrg */
32505b261ecSmrgstatic ModeStatus
32605b261ecSmrgxf86HandleBuiltinMode(ScrnInfoPtr scrp,
32735c4bbdfSmrg                      DisplayModePtr p,
32835c4bbdfSmrg                      DisplayModePtr modep,
32935c4bbdfSmrg                      ClockRangePtr clockRanges, Bool allowDiv2)
33005b261ecSmrg{
33105b261ecSmrg    ClockRangePtr cp;
33205b261ecSmrg    int extraFlags = 0;
33305b261ecSmrg    int MulFactor = 1;
33405b261ecSmrg    int DivFactor = 1;
33505b261ecSmrg    int clockIndex;
33635c4bbdfSmrg
33705b261ecSmrg    /* Reject previously rejected modes */
33805b261ecSmrg    if (p->status != MODE_OK)
33935c4bbdfSmrg        return p->status;
34005b261ecSmrg
34105b261ecSmrg    /* Reject previously considered modes */
34205b261ecSmrg    if (p->prev)
34305b261ecSmrg        return MODE_NOMODE;
34405b261ecSmrg
34505b261ecSmrg    if ((p->type & M_T_CLOCK_C) == M_T_CLOCK_C) {
34635c4bbdfSmrg        /* Check clock is in range */
34735c4bbdfSmrg        cp = xf86FindClockRangeForMode(clockRanges, p);
34835c4bbdfSmrg        if (cp == NULL) {
34935c4bbdfSmrg            modep->type = p->type;
35035c4bbdfSmrg            p->status = MODE_CLOCK_RANGE;
35135c4bbdfSmrg            return MODE_CLOCK_RANGE;
35235c4bbdfSmrg        }
35335c4bbdfSmrg        DivFactor = cp->ClockDivFactor;
35435c4bbdfSmrg        MulFactor = cp->ClockMulFactor;
35535c4bbdfSmrg        if (!scrp->progClock) {
35635c4bbdfSmrg            clockIndex = xf86GetNearestClock(scrp, p->Clock, allowDiv2,
35735c4bbdfSmrg                                             cp->ClockDivFactor,
35835c4bbdfSmrg                                             cp->ClockMulFactor, &extraFlags);
35935c4bbdfSmrg            modep->Clock = (scrp->clock[clockIndex] * DivFactor)
36035c4bbdfSmrg                / MulFactor;
36135c4bbdfSmrg            modep->ClockIndex = clockIndex;
36235c4bbdfSmrg            modep->SynthClock = scrp->clock[clockIndex];
36335c4bbdfSmrg            if (extraFlags & V_CLKDIV2) {
36435c4bbdfSmrg                modep->Clock /= 2;
36535c4bbdfSmrg                modep->SynthClock /= 2;
36635c4bbdfSmrg            }
36735c4bbdfSmrg        }
36835c4bbdfSmrg        else {
36905b261ecSmrg            modep->Clock = p->Clock;
37035c4bbdfSmrg            modep->ClockIndex = -1;
37135c4bbdfSmrg            modep->SynthClock = (modep->Clock * MulFactor)
37235c4bbdfSmrg                / DivFactor;
37335c4bbdfSmrg        }
37435c4bbdfSmrg        modep->PrivFlags = cp->PrivFlags;
37505b261ecSmrg    }
37635c4bbdfSmrg    else {
37735c4bbdfSmrg        if (!scrp->progClock) {
37835c4bbdfSmrg            modep->Clock = p->Clock;
37935c4bbdfSmrg            modep->ClockIndex = p->ClockIndex;
38035c4bbdfSmrg            modep->SynthClock = p->SynthClock;
38135c4bbdfSmrg        }
38235c4bbdfSmrg        else {
38335c4bbdfSmrg            modep->Clock = p->Clock;
38435c4bbdfSmrg            modep->ClockIndex = -1;
38535c4bbdfSmrg            modep->SynthClock = p->SynthClock;
38635c4bbdfSmrg        }
38735c4bbdfSmrg        modep->PrivFlags = p->PrivFlags;
38835c4bbdfSmrg    }
38935c4bbdfSmrg    modep->type = p->type;
39035c4bbdfSmrg    modep->HDisplay = p->HDisplay;
39135c4bbdfSmrg    modep->HSyncStart = p->HSyncStart;
39235c4bbdfSmrg    modep->HSyncEnd = p->HSyncEnd;
39335c4bbdfSmrg    modep->HTotal = p->HTotal;
39435c4bbdfSmrg    modep->HSkew = p->HSkew;
39535c4bbdfSmrg    modep->VDisplay = p->VDisplay;
39635c4bbdfSmrg    modep->VSyncStart = p->VSyncStart;
39735c4bbdfSmrg    modep->VSyncEnd = p->VSyncEnd;
39835c4bbdfSmrg    modep->VTotal = p->VTotal;
39935c4bbdfSmrg    modep->VScan = p->VScan;
40035c4bbdfSmrg    modep->Flags = p->Flags | extraFlags;
40135c4bbdfSmrg    modep->CrtcHDisplay = p->CrtcHDisplay;
40205b261ecSmrg    modep->CrtcHBlankStart = p->CrtcHBlankStart;
40335c4bbdfSmrg    modep->CrtcHSyncStart = p->CrtcHSyncStart;
40435c4bbdfSmrg    modep->CrtcHSyncEnd = p->CrtcHSyncEnd;
40535c4bbdfSmrg    modep->CrtcHBlankEnd = p->CrtcHBlankEnd;
40635c4bbdfSmrg    modep->CrtcHTotal = p->CrtcHTotal;
40735c4bbdfSmrg    modep->CrtcHSkew = p->CrtcHSkew;
40835c4bbdfSmrg    modep->CrtcVDisplay = p->CrtcVDisplay;
40905b261ecSmrg    modep->CrtcVBlankStart = p->CrtcVBlankStart;
41035c4bbdfSmrg    modep->CrtcVSyncStart = p->CrtcVSyncStart;
41135c4bbdfSmrg    modep->CrtcVSyncEnd = p->CrtcVSyncEnd;
41235c4bbdfSmrg    modep->CrtcVBlankEnd = p->CrtcVBlankEnd;
41335c4bbdfSmrg    modep->CrtcVTotal = p->CrtcVTotal;
41435c4bbdfSmrg    modep->CrtcHAdjusted = p->CrtcHAdjusted;
41535c4bbdfSmrg    modep->CrtcVAdjusted = p->CrtcVAdjusted;
41635c4bbdfSmrg    modep->HSync = p->HSync;
41735c4bbdfSmrg    modep->VRefresh = p->VRefresh;
41835c4bbdfSmrg    modep->Private = p->Private;
41935c4bbdfSmrg    modep->PrivSize = p->PrivSize;
42005b261ecSmrg
42105b261ecSmrg    p->prev = modep;
42235c4bbdfSmrg
42305b261ecSmrg    return MODE_OK;
42405b261ecSmrg}
42505b261ecSmrg
42605b261ecSmrg/*
42705b261ecSmrg * xf86LookupMode
42805b261ecSmrg *
42905b261ecSmrg * This function returns a mode from the given list which matches the
43005b261ecSmrg * given name.  When multiple modes with the same name are available,
43105b261ecSmrg * the method of picking the matching mode is determined by the
43205b261ecSmrg * strategy selected.
43305b261ecSmrg *
43405b261ecSmrg * This function takes the following parameters:
43505b261ecSmrg *    scrp         ScrnInfoPtr
43605b261ecSmrg *    modep        pointer to the returned mode, which must have the name
43705b261ecSmrg *                 field filled in.
43805b261ecSmrg *    clockRanges  a list of clock ranges.   This is optional when all the
43905b261ecSmrg *                 modes are built-in modes.
44005b261ecSmrg *    strategy     how to decide which mode to use from multiple modes with
44105b261ecSmrg *                 the same name
44205b261ecSmrg *
44305b261ecSmrg * In addition, the following fields from the ScrnInfoRec are used:
44405b261ecSmrg *    modePool     the list of monitor modes compatible with the driver
44505b261ecSmrg *    clocks       a list of discrete clocks
44605b261ecSmrg *    numClocks    number of discrete clocks
44705b261ecSmrg *    progClock    clock is programmable
44805b261ecSmrg *
44905b261ecSmrg * If a mode was found, its values are filled in to the area pointed to
45005b261ecSmrg * by modep,  If a mode was not found the return value indicates the
45105b261ecSmrg * reason.
45205b261ecSmrg */
45305b261ecSmrg
45435c4bbdfSmrgstatic ModeStatus
45505b261ecSmrgxf86LookupMode(ScrnInfoPtr scrp, DisplayModePtr modep,
45635c4bbdfSmrg               ClockRangePtr clockRanges, LookupModeFlags strategy)
45705b261ecSmrg{
45805b261ecSmrg    DisplayModePtr p, bestMode = NULL;
45905b261ecSmrg    ClockRangePtr cp;
46005b261ecSmrg    int i, k, gap, minimumGap = CLOCK_TOLERANCE + 1;
46105b261ecSmrg    double refresh, bestRefresh = 0.0;
46205b261ecSmrg    Bool found = FALSE;
46305b261ecSmrg    int extraFlags = 0;
46405b261ecSmrg    int clockIndex = -1;
46505b261ecSmrg    int MulFactor = 1;
46605b261ecSmrg    int DivFactor = 1;
46705b261ecSmrg    int ModePrivFlags = 0;
46805b261ecSmrg    ModeStatus status = MODE_NOMODE;
46905b261ecSmrg    Bool allowDiv2 = (strategy & LOOKUP_CLKDIV2) != 0;
47005b261ecSmrg    int n;
47135c4bbdfSmrg
47205b261ecSmrg    const int types[] = {
47335c4bbdfSmrg        M_T_BUILTIN | M_T_PREFERRED,
47435c4bbdfSmrg        M_T_BUILTIN,
47535c4bbdfSmrg        M_T_USERDEF | M_T_PREFERRED,
47635c4bbdfSmrg        M_T_USERDEF,
47735c4bbdfSmrg        M_T_DRIVER | M_T_PREFERRED,
47835c4bbdfSmrg        M_T_DRIVER,
47935c4bbdfSmrg        0
48005b261ecSmrg    };
4811b5d61b8Smrg    const int ntypes = ARRAY_SIZE(types);
48205b261ecSmrg
48305b261ecSmrg    strategy &= ~(LOOKUP_CLKDIV2 | LOOKUP_OPTIONAL_TOLERANCES);
48405b261ecSmrg
48505b261ecSmrg    /* Some sanity checking */
48605b261ecSmrg    if (scrp == NULL || scrp->modePool == NULL ||
48735c4bbdfSmrg        (!scrp->progClock && scrp->numClocks == 0)) {
48835c4bbdfSmrg        ErrorF("xf86LookupMode: called with invalid scrnInfoRec\n");
48935c4bbdfSmrg        return MODE_ERROR;
49005b261ecSmrg    }
49105b261ecSmrg    if (modep == NULL || modep->name == NULL) {
49235c4bbdfSmrg        ErrorF("xf86LookupMode: called with invalid modep\n");
49335c4bbdfSmrg        return MODE_ERROR;
49405b261ecSmrg    }
49505b261ecSmrg    for (cp = clockRanges; cp != NULL; cp = cp->next) {
49635c4bbdfSmrg        /* DivFactor and MulFactor must be > 0 */
49735c4bbdfSmrg        cp->ClockDivFactor = max(1, cp->ClockDivFactor);
49835c4bbdfSmrg        cp->ClockMulFactor = max(1, cp->ClockMulFactor);
49905b261ecSmrg    }
50005b261ecSmrg
50105b261ecSmrg    /* Scan the mode pool for matching names */
50205b261ecSmrg    for (n = 0; n < ntypes; n++) {
50335c4bbdfSmrg        int type = types[n];
50435c4bbdfSmrg
50535c4bbdfSmrg        for (p = scrp->modePool; p != NULL; p = p->next) {
50635c4bbdfSmrg
50735c4bbdfSmrg            /* scan through the modes in the sort order above */
50835c4bbdfSmrg            if ((p->type & type) != type)
50935c4bbdfSmrg                continue;
510d9252ffbSmrg            if (p->name == NULL)
511d9252ffbSmrg                continue;
51235c4bbdfSmrg
51335c4bbdfSmrg            if (strcmp(p->name, modep->name) == 0) {
51435c4bbdfSmrg
51535c4bbdfSmrg                /* Skip over previously rejected modes */
51635c4bbdfSmrg                if (p->status != MODE_OK) {
51735c4bbdfSmrg                    if (!found)
51835c4bbdfSmrg                        status = p->status;
51935c4bbdfSmrg                    continue;
52035c4bbdfSmrg                }
52135c4bbdfSmrg
52235c4bbdfSmrg                /* Skip over previously considered modes */
52335c4bbdfSmrg                if (p->prev)
52435c4bbdfSmrg                    continue;
52535c4bbdfSmrg
52635c4bbdfSmrg                if (p->type & M_T_BUILTIN) {
52735c4bbdfSmrg                    return xf86HandleBuiltinMode(scrp, p, modep, clockRanges,
52835c4bbdfSmrg                                                 allowDiv2);
52935c4bbdfSmrg                }
53035c4bbdfSmrg
53135c4bbdfSmrg                /* Check clock is in range */
53235c4bbdfSmrg                cp = xf86FindClockRangeForMode(clockRanges, p);
53335c4bbdfSmrg                if (cp == NULL) {
53435c4bbdfSmrg                    /*
53535c4bbdfSmrg                     * XXX Could do more here to provide a more detailed
53635c4bbdfSmrg                     * reason for not finding a mode.
53735c4bbdfSmrg                     */
53835c4bbdfSmrg                    p->status = MODE_CLOCK_RANGE;
53935c4bbdfSmrg                    if (!found)
54035c4bbdfSmrg                        status = MODE_CLOCK_RANGE;
54135c4bbdfSmrg                    continue;
54235c4bbdfSmrg                }
54335c4bbdfSmrg
54435c4bbdfSmrg                /*
54535c4bbdfSmrg                 * If programmable clock and strategy is not
54635c4bbdfSmrg                 * LOOKUP_BEST_REFRESH, the required mode has been found,
54735c4bbdfSmrg                 * otherwise record the refresh and continue looking.
54835c4bbdfSmrg                 */
54935c4bbdfSmrg                if (scrp->progClock) {
55035c4bbdfSmrg                    found = TRUE;
55135c4bbdfSmrg                    if (strategy != LOOKUP_BEST_REFRESH) {
55235c4bbdfSmrg                        bestMode = p;
55335c4bbdfSmrg                        DivFactor = cp->ClockDivFactor;
55435c4bbdfSmrg                        MulFactor = cp->ClockMulFactor;
55535c4bbdfSmrg                        ModePrivFlags = cp->PrivFlags;
55635c4bbdfSmrg                        break;
55735c4bbdfSmrg                    }
55835c4bbdfSmrg                    refresh = xf86ModeVRefresh(p);
55935c4bbdfSmrg                    if (p->Flags & V_INTERLACE)
56035c4bbdfSmrg                        refresh /= INTERLACE_REFRESH_WEIGHT;
56135c4bbdfSmrg                    if (refresh > bestRefresh) {
56235c4bbdfSmrg                        bestMode = p;
56335c4bbdfSmrg                        DivFactor = cp->ClockDivFactor;
56435c4bbdfSmrg                        MulFactor = cp->ClockMulFactor;
56535c4bbdfSmrg                        ModePrivFlags = cp->PrivFlags;
56635c4bbdfSmrg                        bestRefresh = refresh;
56735c4bbdfSmrg                    }
56835c4bbdfSmrg                    continue;
56935c4bbdfSmrg                }
57035c4bbdfSmrg
57135c4bbdfSmrg                /*
57235c4bbdfSmrg                 * Clock is in range, so if it is not a programmable clock, find
57335c4bbdfSmrg                 * a matching clock.
57435c4bbdfSmrg                 */
57535c4bbdfSmrg
57635c4bbdfSmrg                i = xf86GetNearestClock(scrp, p->Clock, allowDiv2,
57735c4bbdfSmrg                                        cp->ClockDivFactor, cp->ClockMulFactor,
57835c4bbdfSmrg                                        &k);
57935c4bbdfSmrg                /*
58035c4bbdfSmrg                 * If the clock is too far from the requested clock, this
58135c4bbdfSmrg                 * mode is no good.
58235c4bbdfSmrg                 */
58335c4bbdfSmrg                if (k & V_CLKDIV2)
58435c4bbdfSmrg                    gap = abs((p->Clock * 2) -
58535c4bbdfSmrg                              ((scrp->clock[i] * cp->ClockDivFactor) /
58635c4bbdfSmrg                               cp->ClockMulFactor));
58735c4bbdfSmrg                else
58835c4bbdfSmrg                    gap = abs(p->Clock -
58935c4bbdfSmrg                              ((scrp->clock[i] * cp->ClockDivFactor) /
59035c4bbdfSmrg                               cp->ClockMulFactor));
59135c4bbdfSmrg                if (gap > minimumGap) {
59235c4bbdfSmrg                    p->status = MODE_NOCLOCK;
59335c4bbdfSmrg                    if (!found)
59435c4bbdfSmrg                        status = MODE_NOCLOCK;
59535c4bbdfSmrg                    continue;
59635c4bbdfSmrg                }
59735c4bbdfSmrg                found = TRUE;
59835c4bbdfSmrg
59935c4bbdfSmrg                if (strategy == LOOKUP_BEST_REFRESH) {
60035c4bbdfSmrg                    refresh = xf86ModeVRefresh(p);
60135c4bbdfSmrg                    if (p->Flags & V_INTERLACE)
60235c4bbdfSmrg                        refresh /= INTERLACE_REFRESH_WEIGHT;
60335c4bbdfSmrg                    if (refresh > bestRefresh) {
60435c4bbdfSmrg                        bestMode = p;
60535c4bbdfSmrg                        DivFactor = cp->ClockDivFactor;
60635c4bbdfSmrg                        MulFactor = cp->ClockMulFactor;
60735c4bbdfSmrg                        ModePrivFlags = cp->PrivFlags;
60835c4bbdfSmrg                        extraFlags = k;
60935c4bbdfSmrg                        clockIndex = i;
61035c4bbdfSmrg                        bestRefresh = refresh;
61135c4bbdfSmrg                    }
61235c4bbdfSmrg                    continue;
61335c4bbdfSmrg                }
61435c4bbdfSmrg                if (strategy == LOOKUP_CLOSEST_CLOCK) {
61535c4bbdfSmrg                    if (gap < minimumGap) {
61635c4bbdfSmrg                        bestMode = p;
61735c4bbdfSmrg                        DivFactor = cp->ClockDivFactor;
61835c4bbdfSmrg                        MulFactor = cp->ClockMulFactor;
61935c4bbdfSmrg                        ModePrivFlags = cp->PrivFlags;
62035c4bbdfSmrg                        extraFlags = k;
62135c4bbdfSmrg                        clockIndex = i;
62235c4bbdfSmrg                        minimumGap = gap;
62335c4bbdfSmrg                    }
62435c4bbdfSmrg                    continue;
62535c4bbdfSmrg                }
62635c4bbdfSmrg                /*
62735c4bbdfSmrg                 * If strategy is neither LOOKUP_BEST_REFRESH or
62835c4bbdfSmrg                 * LOOKUP_CLOSEST_CLOCK the required mode has been found.
62935c4bbdfSmrg                 */
63035c4bbdfSmrg                bestMode = p;
63135c4bbdfSmrg                DivFactor = cp->ClockDivFactor;
63235c4bbdfSmrg                MulFactor = cp->ClockMulFactor;
63335c4bbdfSmrg                ModePrivFlags = cp->PrivFlags;
63435c4bbdfSmrg                extraFlags = k;
63535c4bbdfSmrg                clockIndex = i;
63635c4bbdfSmrg                break;
63735c4bbdfSmrg            }
63835c4bbdfSmrg        }
63935c4bbdfSmrg        if (found)
64035c4bbdfSmrg            break;
64105b261ecSmrg    }
64205b261ecSmrg    if (!found || bestMode == NULL)
64335c4bbdfSmrg        return status;
64405b261ecSmrg
64505b261ecSmrg    /* Fill in the mode parameters */
64605b261ecSmrg    if (scrp->progClock) {
64735c4bbdfSmrg        modep->Clock = bestMode->Clock;
64835c4bbdfSmrg        modep->ClockIndex = -1;
64935c4bbdfSmrg        modep->SynthClock = (modep->Clock * MulFactor) / DivFactor;
65035c4bbdfSmrg    }
65135c4bbdfSmrg    else {
65235c4bbdfSmrg        modep->Clock = (scrp->clock[clockIndex] * DivFactor) / MulFactor;
65335c4bbdfSmrg        modep->ClockIndex = clockIndex;
65435c4bbdfSmrg        modep->SynthClock = scrp->clock[clockIndex];
65535c4bbdfSmrg        if (extraFlags & V_CLKDIV2) {
65635c4bbdfSmrg            modep->Clock /= 2;
65735c4bbdfSmrg            modep->SynthClock /= 2;
65835c4bbdfSmrg        }
65905b261ecSmrg    }
66035c4bbdfSmrg    modep->type = bestMode->type;
66135c4bbdfSmrg    modep->PrivFlags = ModePrivFlags;
66235c4bbdfSmrg    modep->HDisplay = bestMode->HDisplay;
66335c4bbdfSmrg    modep->HSyncStart = bestMode->HSyncStart;
66435c4bbdfSmrg    modep->HSyncEnd = bestMode->HSyncEnd;
66535c4bbdfSmrg    modep->HTotal = bestMode->HTotal;
66635c4bbdfSmrg    modep->HSkew = bestMode->HSkew;
66735c4bbdfSmrg    modep->VDisplay = bestMode->VDisplay;
66835c4bbdfSmrg    modep->VSyncStart = bestMode->VSyncStart;
66935c4bbdfSmrg    modep->VSyncEnd = bestMode->VSyncEnd;
67035c4bbdfSmrg    modep->VTotal = bestMode->VTotal;
67135c4bbdfSmrg    modep->VScan = bestMode->VScan;
67235c4bbdfSmrg    modep->Flags = bestMode->Flags | extraFlags;
67335c4bbdfSmrg    modep->CrtcHDisplay = bestMode->CrtcHDisplay;
67435c4bbdfSmrg    modep->CrtcHBlankStart = bestMode->CrtcHBlankStart;
67535c4bbdfSmrg    modep->CrtcHSyncStart = bestMode->CrtcHSyncStart;
67635c4bbdfSmrg    modep->CrtcHSyncEnd = bestMode->CrtcHSyncEnd;
67735c4bbdfSmrg    modep->CrtcHBlankEnd = bestMode->CrtcHBlankEnd;
67835c4bbdfSmrg    modep->CrtcHTotal = bestMode->CrtcHTotal;
67935c4bbdfSmrg    modep->CrtcHSkew = bestMode->CrtcHSkew;
68035c4bbdfSmrg    modep->CrtcVDisplay = bestMode->CrtcVDisplay;
68135c4bbdfSmrg    modep->CrtcVBlankStart = bestMode->CrtcVBlankStart;
68235c4bbdfSmrg    modep->CrtcVSyncStart = bestMode->CrtcVSyncStart;
68335c4bbdfSmrg    modep->CrtcVSyncEnd = bestMode->CrtcVSyncEnd;
68435c4bbdfSmrg    modep->CrtcVBlankEnd = bestMode->CrtcVBlankEnd;
68535c4bbdfSmrg    modep->CrtcVTotal = bestMode->CrtcVTotal;
68635c4bbdfSmrg    modep->CrtcHAdjusted = bestMode->CrtcHAdjusted;
68735c4bbdfSmrg    modep->CrtcVAdjusted = bestMode->CrtcVAdjusted;
68835c4bbdfSmrg    modep->HSync = bestMode->HSync;
68935c4bbdfSmrg    modep->VRefresh = bestMode->VRefresh;
69035c4bbdfSmrg    modep->Private = bestMode->Private;
69135c4bbdfSmrg    modep->PrivSize = bestMode->PrivSize;
69205b261ecSmrg
69305b261ecSmrg    bestMode->prev = modep;
69405b261ecSmrg
69505b261ecSmrg    return MODE_OK;
69605b261ecSmrg}
69705b261ecSmrg
69805b261ecSmrg/*
69905b261ecSmrg * xf86CheckModeForMonitor
70005b261ecSmrg *
70105b261ecSmrg * This function takes a mode and monitor description, and determines
70205b261ecSmrg * if the mode is valid for the monitor.
70305b261ecSmrg */
7046747b715SmrgModeStatus
70505b261ecSmrgxf86CheckModeForMonitor(DisplayModePtr mode, MonPtr monitor)
70605b261ecSmrg{
70705b261ecSmrg    int i;
70805b261ecSmrg
70905b261ecSmrg    /* Sanity checks */
71005b261ecSmrg    if (mode == NULL || monitor == NULL) {
71135c4bbdfSmrg        ErrorF("xf86CheckModeForMonitor: called with invalid parameters\n");
71235c4bbdfSmrg        return MODE_ERROR;
71305b261ecSmrg    }
71405b261ecSmrg
7156747b715Smrg    DebugF("xf86CheckModeForMonitor(%p %s, %p %s)\n",
71635c4bbdfSmrg           mode, mode->name, monitor, monitor->id);
71705b261ecSmrg
71805b261ecSmrg    /* Some basic mode validity checks */
71905b261ecSmrg    if (0 >= mode->HDisplay || mode->HDisplay > mode->HSyncStart ||
72035c4bbdfSmrg        mode->HSyncStart >= mode->HSyncEnd || mode->HSyncEnd >= mode->HTotal)
72135c4bbdfSmrg        return MODE_H_ILLEGAL;
72205b261ecSmrg
72305b261ecSmrg    if (0 >= mode->VDisplay || mode->VDisplay > mode->VSyncStart ||
72435c4bbdfSmrg        mode->VSyncStart >= mode->VSyncEnd || mode->VSyncEnd >= mode->VTotal)
72535c4bbdfSmrg        return MODE_V_ILLEGAL;
72605b261ecSmrg
72705b261ecSmrg    if (monitor->nHsync > 0) {
72835c4bbdfSmrg        /* Check hsync against the allowed ranges */
72935c4bbdfSmrg        float hsync = xf86ModeHSync(mode);
73035c4bbdfSmrg
73135c4bbdfSmrg        for (i = 0; i < monitor->nHsync; i++)
73235c4bbdfSmrg            if ((hsync > monitor->hsync[i].lo * (1.0 - SYNC_TOLERANCE)) &&
73335c4bbdfSmrg                (hsync < monitor->hsync[i].hi * (1.0 + SYNC_TOLERANCE)))
73435c4bbdfSmrg                break;
73535c4bbdfSmrg
73635c4bbdfSmrg        /* Now see whether we ran out of sync ranges without finding a match */
73735c4bbdfSmrg        if (i == monitor->nHsync)
73835c4bbdfSmrg            return MODE_HSYNC;
73905b261ecSmrg    }
74005b261ecSmrg
74105b261ecSmrg    if (monitor->nVrefresh > 0) {
74235c4bbdfSmrg        /* Check vrefresh against the allowed ranges */
74335c4bbdfSmrg        float vrefrsh = xf86ModeVRefresh(mode);
74435c4bbdfSmrg
74535c4bbdfSmrg        for (i = 0; i < monitor->nVrefresh; i++)
74635c4bbdfSmrg            if ((vrefrsh > monitor->vrefresh[i].lo * (1.0 - SYNC_TOLERANCE)) &&
74735c4bbdfSmrg                (vrefrsh < monitor->vrefresh[i].hi * (1.0 + SYNC_TOLERANCE)))
74835c4bbdfSmrg                break;
74935c4bbdfSmrg
75035c4bbdfSmrg        /* Now see whether we ran out of refresh ranges without finding a match */
75135c4bbdfSmrg        if (i == monitor->nVrefresh)
75235c4bbdfSmrg            return MODE_VSYNC;
75305b261ecSmrg    }
75405b261ecSmrg
75505b261ecSmrg    /* Force interlaced modes to have an odd VTotal */
75605b261ecSmrg    if (mode->Flags & V_INTERLACE)
75735c4bbdfSmrg        mode->CrtcVTotal = mode->VTotal |= 1;
75805b261ecSmrg
75905b261ecSmrg    /*
76005b261ecSmrg     * This code stops cvt -r modes, and only cvt -r modes, from hitting 15y+
76105b261ecSmrg     * old CRTs which might, when there is a lot of solar flare activity and
76205b261ecSmrg     * when the celestial bodies are unfavourably aligned, implode trying to
76305b261ecSmrg     * sync to it. It's called "Protecting the user from doing anything stupid".
76405b261ecSmrg     * -- libv
76505b261ecSmrg     */
76605b261ecSmrg
7674642e01fSmrg    if (xf86ModeIsReduced(mode)) {
7684642e01fSmrg        if (!monitor->reducedblanking && !(mode->type & M_T_DRIVER))
7694642e01fSmrg            return MODE_NO_REDUCED;
77005b261ecSmrg    }
77105b261ecSmrg
77205b261ecSmrg    if ((monitor->maxPixClock) && (mode->Clock > monitor->maxPixClock))
77335c4bbdfSmrg        return MODE_CLOCK_HIGH;
77405b261ecSmrg
77505b261ecSmrg    return MODE_OK;
77605b261ecSmrg}
77705b261ecSmrg
77805b261ecSmrg/*
77905b261ecSmrg * xf86CheckModeSize
78005b261ecSmrg *
78105b261ecSmrg * An internal routine to check if a mode fits in video memory.  This tries to
78205b261ecSmrg * avoid overflows that would otherwise occur when video memory size is greater
78305b261ecSmrg * than 256MB.
78405b261ecSmrg */
78505b261ecSmrgstatic Bool
78605b261ecSmrgxf86CheckModeSize(ScrnInfoPtr scrp, int w, int x, int y)
78705b261ecSmrg{
78835c4bbdfSmrg    int bpp = scrp->fbFormat.bitsPerPixel, pad = scrp->fbFormat.scanlinePad;
78905b261ecSmrg    int lineWidth, lastWidth;
79005b261ecSmrg
79105b261ecSmrg    if (scrp->depth == 4)
79235c4bbdfSmrg        pad *= 4;               /* 4 planes */
79305b261ecSmrg
79405b261ecSmrg    /* Sanity check */
79505b261ecSmrg    if ((w < 0) || (x < 0) || (y <= 0))
79635c4bbdfSmrg        return FALSE;
79705b261ecSmrg
79805b261ecSmrg    lineWidth = (((w * bpp) + pad - 1) / pad) * pad;
79905b261ecSmrg    lastWidth = x * bpp;
80005b261ecSmrg
80105b261ecSmrg    /*
80205b261ecSmrg     * At this point, we need to compare
80305b261ecSmrg     *
80435c4bbdfSmrg     *  (lineWidth * (y - 1)) + lastWidth
80505b261ecSmrg     *
80605b261ecSmrg     * against
80705b261ecSmrg     *
80835c4bbdfSmrg     *  scrp->videoRam * (1024 * 8)
80905b261ecSmrg     *
81005b261ecSmrg     * These are bit quantities.  To avoid overflows, do the comparison in
81105b261ecSmrg     * terms of BITMAP_SCANLINE_PAD units.  This assumes BITMAP_SCANLINE_PAD
81205b261ecSmrg     * is a power of 2.  We currently use 32, which limits us to a video
81305b261ecSmrg     * memory size of 8GB.
81405b261ecSmrg     */
81505b261ecSmrg
81605b261ecSmrg    lineWidth = (lineWidth + (BITMAP_SCANLINE_PAD - 1)) / BITMAP_SCANLINE_PAD;
81705b261ecSmrg    lastWidth = (lastWidth + (BITMAP_SCANLINE_PAD - 1)) / BITMAP_SCANLINE_PAD;
81805b261ecSmrg
81905b261ecSmrg    if ((lineWidth * (y - 1) + lastWidth) >
82035c4bbdfSmrg        (scrp->videoRam * ((1024 * 8) / BITMAP_SCANLINE_PAD)))
82135c4bbdfSmrg        return FALSE;
82205b261ecSmrg
82305b261ecSmrg    return TRUE;
82405b261ecSmrg}
82505b261ecSmrg
82605b261ecSmrg/*
82705b261ecSmrg * xf86InitialCheckModeForDriver
82805b261ecSmrg *
82905b261ecSmrg * This function checks if a mode satisfies a driver's initial requirements:
83005b261ecSmrg *   -  mode size fits within the available pixel area (memory)
83105b261ecSmrg *   -  width lies within the range of supported line pitches
83205b261ecSmrg *   -  mode size fits within virtual size (if fixed)
83305b261ecSmrg *   -  horizontal timings are in range
83405b261ecSmrg *
83505b261ecSmrg * This function takes the following parameters:
83605b261ecSmrg *    scrp         ScrnInfoPtr
83705b261ecSmrg *    mode         mode to check
83805b261ecSmrg *    maxPitch     (optional) maximum line pitch
83905b261ecSmrg *    virtualX     (optional) virtual width requested
84005b261ecSmrg *    virtualY     (optional) virtual height requested
84105b261ecSmrg *
84205b261ecSmrg * In addition, the following fields from the ScrnInfoRec are used:
84305b261ecSmrg *    monitor      pointer to structure for monitor section
84405b261ecSmrg *    fbFormat     pixel format for the framebuffer
84505b261ecSmrg *    videoRam     video memory size (in kB)
84605b261ecSmrg */
84705b261ecSmrg
84835c4bbdfSmrgstatic ModeStatus
84905b261ecSmrgxf86InitialCheckModeForDriver(ScrnInfoPtr scrp, DisplayModePtr mode,
85035c4bbdfSmrg                              ClockRangePtr clockRanges,
85135c4bbdfSmrg                              LookupModeFlags strategy,
85235c4bbdfSmrg                              int maxPitch, int virtualX, int virtualY)
85305b261ecSmrg{
85405b261ecSmrg    ClockRangePtr cp;
85505b261ecSmrg    ModeStatus status;
85605b261ecSmrg    Bool allowDiv2 = (strategy & LOOKUP_CLKDIV2) != 0;
85705b261ecSmrg    int i, needDiv2;
85835c4bbdfSmrg
85905b261ecSmrg    /* Sanity checks */
86005b261ecSmrg    if (!scrp || !mode || !clockRanges) {
86135c4bbdfSmrg        ErrorF("xf86InitialCheckModeForDriver: "
86235c4bbdfSmrg               "called with invalid parameters\n");
86335c4bbdfSmrg        return MODE_ERROR;
86405b261ecSmrg    }
86505b261ecSmrg
8666747b715Smrg    DebugF("xf86InitialCheckModeForDriver(%p, %p %s, %p, 0x%x, %d, %d, %d)\n",
86735c4bbdfSmrg           scrp, mode, mode->name, clockRanges, strategy, maxPitch, virtualX,
86835c4bbdfSmrg           virtualY);
86905b261ecSmrg
87005b261ecSmrg    /* Some basic mode validity checks */
87105b261ecSmrg    if (0 >= mode->HDisplay || mode->HDisplay > mode->HSyncStart ||
87235c4bbdfSmrg        mode->HSyncStart >= mode->HSyncEnd || mode->HSyncEnd >= mode->HTotal)
87335c4bbdfSmrg        return MODE_H_ILLEGAL;
87405b261ecSmrg
87505b261ecSmrg    if (0 >= mode->VDisplay || mode->VDisplay > mode->VSyncStart ||
87635c4bbdfSmrg        mode->VSyncStart >= mode->VSyncEnd || mode->VSyncEnd >= mode->VTotal)
87735c4bbdfSmrg        return MODE_V_ILLEGAL;
87805b261ecSmrg
87905b261ecSmrg    if (!xf86CheckModeSize(scrp, mode->HDisplay, mode->HDisplay,
88035c4bbdfSmrg                           mode->VDisplay))
88105b261ecSmrg        return MODE_MEM;
88205b261ecSmrg
88305b261ecSmrg    if (maxPitch > 0 && mode->HDisplay > maxPitch)
88435c4bbdfSmrg        return MODE_BAD_WIDTH;
88505b261ecSmrg
88605b261ecSmrg    if (virtualX > 0 && mode->HDisplay > virtualX)
88735c4bbdfSmrg        return MODE_VIRTUAL_X;
88805b261ecSmrg
88905b261ecSmrg    if (virtualY > 0 && mode->VDisplay > virtualY)
89035c4bbdfSmrg        return MODE_VIRTUAL_Y;
89105b261ecSmrg
89205b261ecSmrg    /*
89305b261ecSmrg     * The use of the DisplayModeRec's Crtc* and SynthClock elements below is
89405b261ecSmrg     * provisional, in that they are later reused by the driver at mode-set
89505b261ecSmrg     * time.  Here, they are temporarily enlisted to contain the mode timings
89605b261ecSmrg     * as seen by the CRT or panel (rather than the CRTC).  The driver's
89705b261ecSmrg     * ValidMode() is allowed to modify these so it can deal with such things
89805b261ecSmrg     * as mode stretching and/or centering.  The driver should >NOT< modify the
89905b261ecSmrg     * user-supplied values as these are reported back when mode validation is
90005b261ecSmrg     * said and done.
90105b261ecSmrg     */
90205b261ecSmrg    /*
90305b261ecSmrg     * NOTE: We (ab)use the mode->Crtc* values here to store timing
90405b261ecSmrg     * information for the calculation of Hsync and Vrefresh. Before
90505b261ecSmrg     * these values are calculated the driver is given the opportunity
90605b261ecSmrg     * to either set these HSync and VRefresh itself or modify the timing
90705b261ecSmrg     * values.
90805b261ecSmrg     * The difference to the final calculation is small but imortand:
90905b261ecSmrg     * here we pass the flag INTERLACE_HALVE_V regardless if the driver
91005b261ecSmrg     * sets it or not. This way our calculation of VRefresh has the same
91105b261ecSmrg     * effect as if we do if (flags & V_INTERLACE) refresh *= 2.0
91205b261ecSmrg     * This dual use of the mode->Crtc* values will certainly create
91305b261ecSmrg     * confusion and is bad software design. However since it's part of
91405b261ecSmrg     * the driver API it's hard to change.
91505b261ecSmrg     */
91635c4bbdfSmrg
91705b261ecSmrg    if (scrp->ValidMode) {
91835c4bbdfSmrg
91935c4bbdfSmrg        xf86SetModeCrtc(mode, INTERLACE_HALVE_V);
92035c4bbdfSmrg
92135c4bbdfSmrg        cp = xf86FindClockRangeForMode(clockRanges, mode);
92235c4bbdfSmrg        if (!cp)
92335c4bbdfSmrg            return MODE_CLOCK_RANGE;
92435c4bbdfSmrg
92535c4bbdfSmrg        if (cp->ClockMulFactor < 1)
92635c4bbdfSmrg            cp->ClockMulFactor = 1;
92735c4bbdfSmrg        if (cp->ClockDivFactor < 1)
92835c4bbdfSmrg            cp->ClockDivFactor = 1;
92935c4bbdfSmrg
93035c4bbdfSmrg        /*
93135c4bbdfSmrg         * XXX  The effect of clock dividers and multipliers on the monitor's
93235c4bbdfSmrg         *      pixel clock needs to be verified.
93335c4bbdfSmrg         */
93435c4bbdfSmrg        if (scrp->progClock) {
93535c4bbdfSmrg            mode->SynthClock = mode->Clock;
93635c4bbdfSmrg        }
93735c4bbdfSmrg        else {
93835c4bbdfSmrg            i = xf86GetNearestClock(scrp, mode->Clock, allowDiv2,
93935c4bbdfSmrg                                    cp->ClockDivFactor, cp->ClockMulFactor,
94035c4bbdfSmrg                                    &needDiv2);
94135c4bbdfSmrg            mode->SynthClock = (scrp->clock[i] * cp->ClockDivFactor) /
94235c4bbdfSmrg                cp->ClockMulFactor;
94335c4bbdfSmrg            if (needDiv2 & V_CLKDIV2)
94435c4bbdfSmrg                mode->SynthClock /= 2;
94535c4bbdfSmrg        }
94635c4bbdfSmrg
94735c4bbdfSmrg        status = (*scrp->ValidMode) (scrp, mode, FALSE,
94835c4bbdfSmrg                                     MODECHECK_INITIAL);
94935c4bbdfSmrg        if (status != MODE_OK)
95035c4bbdfSmrg            return status;
95135c4bbdfSmrg
95235c4bbdfSmrg        if (mode->HSync <= 0.0)
95335c4bbdfSmrg            mode->HSync = (float) mode->SynthClock / (float) mode->CrtcHTotal;
95435c4bbdfSmrg        if (mode->VRefresh <= 0.0)
95535c4bbdfSmrg            mode->VRefresh = (mode->SynthClock * 1000.0)
95635c4bbdfSmrg                / (mode->CrtcHTotal * mode->CrtcVTotal);
95705b261ecSmrg    }
95835c4bbdfSmrg
95905b261ecSmrg    mode->HSync = xf86ModeHSync(mode);
96005b261ecSmrg    mode->VRefresh = xf86ModeVRefresh(mode);
96105b261ecSmrg
96205b261ecSmrg    /* Assume it is OK */
96305b261ecSmrg    return MODE_OK;
96405b261ecSmrg}
96505b261ecSmrg
96605b261ecSmrg/*
96705b261ecSmrg * xf86CheckModeForDriver
96805b261ecSmrg *
96905b261ecSmrg * This function is for checking modes while the server is running (for
97005b261ecSmrg * use mainly by the VidMode extension).
97105b261ecSmrg *
97205b261ecSmrg * This function checks if a mode satisfies a driver's requirements:
97305b261ecSmrg *   -  width lies within the line pitch
97405b261ecSmrg *   -  mode size fits within virtual size
97505b261ecSmrg *   -  horizontal/vertical timings are in range
97605b261ecSmrg *
97705b261ecSmrg * This function takes the following parameters:
97805b261ecSmrg *    scrp         ScrnInfoPtr
97905b261ecSmrg *    mode         mode to check
98005b261ecSmrg *    flags        not (currently) used
98105b261ecSmrg *
98205b261ecSmrg * In addition, the following fields from the ScrnInfoRec are used:
98305b261ecSmrg *    virtualX     virtual width
98405b261ecSmrg *    virtualY     virtual height
98505b261ecSmrg *    clockRanges  allowable clock ranges
98605b261ecSmrg */
98705b261ecSmrg
9886747b715SmrgModeStatus
98905b261ecSmrgxf86CheckModeForDriver(ScrnInfoPtr scrp, DisplayModePtr mode, int flags)
99005b261ecSmrg{
9916747b715Smrg    ClockRangePtr cp;
99205b261ecSmrg    int i, k, gap, minimumGap = CLOCK_TOLERANCE + 1;
99305b261ecSmrg    int extraFlags = 0;
99405b261ecSmrg    int clockIndex = -1;
99505b261ecSmrg    int MulFactor = 1;
99605b261ecSmrg    int DivFactor = 1;
99705b261ecSmrg    int ModePrivFlags = 0;
99805b261ecSmrg    ModeStatus status = MODE_NOMODE;
99905b261ecSmrg
100005b261ecSmrg    /* Some sanity checking */
100135c4bbdfSmrg    if (scrp == NULL || (!scrp->progClock && scrp->numClocks == 0)) {
100235c4bbdfSmrg        ErrorF("xf86CheckModeForDriver: called with invalid scrnInfoRec\n");
100335c4bbdfSmrg        return MODE_ERROR;
100405b261ecSmrg    }
100505b261ecSmrg    if (mode == NULL) {
100635c4bbdfSmrg        ErrorF("xf86CheckModeForDriver: called with invalid modep\n");
100735c4bbdfSmrg        return MODE_ERROR;
100805b261ecSmrg    }
100905b261ecSmrg
101005b261ecSmrg    /* Check the mode size */
101105b261ecSmrg    if (mode->HDisplay > scrp->virtualX)
101235c4bbdfSmrg        return MODE_VIRTUAL_X;
101305b261ecSmrg
101405b261ecSmrg    if (mode->VDisplay > scrp->virtualY)
101535c4bbdfSmrg        return MODE_VIRTUAL_Y;
101605b261ecSmrg
101705b261ecSmrg    for (cp = scrp->clockRanges; cp != NULL; cp = cp->next) {
101835c4bbdfSmrg        /* DivFactor and MulFactor must be > 0 */
101935c4bbdfSmrg        cp->ClockDivFactor = max(1, cp->ClockDivFactor);
102035c4bbdfSmrg        cp->ClockMulFactor = max(1, cp->ClockMulFactor);
102105b261ecSmrg    }
102205b261ecSmrg
102305b261ecSmrg    if (scrp->progClock) {
102435c4bbdfSmrg        /* Check clock is in range */
102535c4bbdfSmrg        for (cp = scrp->clockRanges; cp != NULL; cp = cp->next) {
102635c4bbdfSmrg            if (modeInClockRange(cp, mode))
102735c4bbdfSmrg                break;
102835c4bbdfSmrg        }
102935c4bbdfSmrg        if (cp == NULL) {
103035c4bbdfSmrg            return MODE_CLOCK_RANGE;
103135c4bbdfSmrg        }
103235c4bbdfSmrg        /*
103335c4bbdfSmrg         * If programmable clock the required mode has been found
103435c4bbdfSmrg         */
103535c4bbdfSmrg        DivFactor = cp->ClockDivFactor;
103635c4bbdfSmrg        MulFactor = cp->ClockMulFactor;
103735c4bbdfSmrg        ModePrivFlags = cp->PrivFlags;
103835c4bbdfSmrg    }
103935c4bbdfSmrg    else {
104035c4bbdfSmrg        status = MODE_CLOCK_RANGE;
104135c4bbdfSmrg        /* Check clock is in range */
104235c4bbdfSmrg        for (cp = scrp->clockRanges; cp != NULL; cp = cp->next) {
104335c4bbdfSmrg            if (modeInClockRange(cp, mode)) {
104435c4bbdfSmrg                /*
104535c4bbdfSmrg                 * Clock is in range, so if it is not a programmable clock,
104635c4bbdfSmrg                 * find a matching clock.
104735c4bbdfSmrg                 */
104835c4bbdfSmrg
104935c4bbdfSmrg                i = xf86GetNearestClock(scrp, mode->Clock, 0,
105035c4bbdfSmrg                                        cp->ClockDivFactor, cp->ClockMulFactor,
105135c4bbdfSmrg                                        &k);
105235c4bbdfSmrg                /*
105335c4bbdfSmrg                 * If the clock is too far from the requested clock, this
105435c4bbdfSmrg                 * mode is no good.
105535c4bbdfSmrg                 */
105635c4bbdfSmrg                if (k & V_CLKDIV2)
105735c4bbdfSmrg                    gap = abs((mode->Clock * 2) -
105835c4bbdfSmrg                              ((scrp->clock[i] * cp->ClockDivFactor) /
105935c4bbdfSmrg                               cp->ClockMulFactor));
106035c4bbdfSmrg                else
106135c4bbdfSmrg                    gap = abs(mode->Clock -
106235c4bbdfSmrg                              ((scrp->clock[i] * cp->ClockDivFactor) /
106335c4bbdfSmrg                               cp->ClockMulFactor));
106435c4bbdfSmrg                if (gap > minimumGap) {
106535c4bbdfSmrg                    status = MODE_NOCLOCK;
106635c4bbdfSmrg                    continue;
106735c4bbdfSmrg                }
106835c4bbdfSmrg
106935c4bbdfSmrg                DivFactor = cp->ClockDivFactor;
107035c4bbdfSmrg                MulFactor = cp->ClockMulFactor;
107135c4bbdfSmrg                ModePrivFlags = cp->PrivFlags;
107235c4bbdfSmrg                extraFlags = k;
107335c4bbdfSmrg                clockIndex = i;
107435c4bbdfSmrg                break;
107535c4bbdfSmrg            }
107635c4bbdfSmrg        }
107735c4bbdfSmrg        if (cp == NULL)
107835c4bbdfSmrg            return status;
107905b261ecSmrg    }
108005b261ecSmrg
108105b261ecSmrg    /* Fill in the mode parameters */
108205b261ecSmrg    if (scrp->progClock) {
108335c4bbdfSmrg        mode->ClockIndex = -1;
108435c4bbdfSmrg        mode->SynthClock = (mode->Clock * MulFactor) / DivFactor;
108535c4bbdfSmrg    }
108635c4bbdfSmrg    else {
108735c4bbdfSmrg        mode->Clock = (scrp->clock[clockIndex] * DivFactor) / MulFactor;
108835c4bbdfSmrg        mode->ClockIndex = clockIndex;
108935c4bbdfSmrg        mode->SynthClock = scrp->clock[clockIndex];
109035c4bbdfSmrg        if (extraFlags & V_CLKDIV2) {
109135c4bbdfSmrg            mode->Clock /= 2;
109235c4bbdfSmrg            mode->SynthClock /= 2;
109335c4bbdfSmrg        }
109405b261ecSmrg    }
109535c4bbdfSmrg    mode->PrivFlags = ModePrivFlags;
109605b261ecSmrg
109705b261ecSmrg    return MODE_OK;
109805b261ecSmrg}
109905b261ecSmrg
110035c4bbdfSmrgstatic int
110105b261ecSmrginferVirtualSize(ScrnInfoPtr scrp, DisplayModePtr modes, int *vx, int *vy)
110205b261ecSmrg{
110305b261ecSmrg    float aspect = 0.0;
110405b261ecSmrg    MonPtr mon = scrp->monitor;
11054642e01fSmrg    xf86MonPtr DDC;
110605b261ecSmrg    int x = 0, y = 0;
110705b261ecSmrg    DisplayModePtr mode;
110805b261ecSmrg
110935c4bbdfSmrg    if (!mon)
111035c4bbdfSmrg        return 0;
11114642e01fSmrg    DDC = mon->DDC;
11124642e01fSmrg
11134642e01fSmrg    if (DDC && DDC->ver.revision >= 4) {
111435c4bbdfSmrg        /* For 1.4, we might actually get native pixel format.  How novel. */
111535c4bbdfSmrg        if (PREFERRED_TIMING_MODE(DDC->features.msc)) {
111635c4bbdfSmrg            for (mode = modes; mode; mode = mode->next) {
111735c4bbdfSmrg                if (mode->type & (M_T_DRIVER | M_T_PREFERRED)) {
111835c4bbdfSmrg                    x = mode->HDisplay;
111935c4bbdfSmrg                    y = mode->VDisplay;
112035c4bbdfSmrg                    goto found;
112135c4bbdfSmrg                }
112235c4bbdfSmrg            }
112335c4bbdfSmrg        }
112435c4bbdfSmrg        /*
112535c4bbdfSmrg         * Even if we don't, we might get aspect ratio from extra CVT info
112635c4bbdfSmrg         * or from the monitor size fields.  TODO.
112735c4bbdfSmrg         */
11284642e01fSmrg    }
112905b261ecSmrg
113005b261ecSmrg    /*
11314642e01fSmrg     * Technically this triggers if either is zero.  That wasn't legal
11324642e01fSmrg     * before EDID 1.4, but right now we'll get that wrong. TODO.
113305b261ecSmrg     */
11344642e01fSmrg    if (!aspect) {
113535c4bbdfSmrg        if (!mon->widthmm || !mon->heightmm)
113635c4bbdfSmrg            aspect = 4.0 / 3.0;
113735c4bbdfSmrg        else
113835c4bbdfSmrg            aspect = (float) mon->widthmm / (float) mon->heightmm;
11394642e01fSmrg    }
114005b261ecSmrg
114105b261ecSmrg    /* find the largest M_T_DRIVER mode with that aspect ratio */
114205b261ecSmrg    for (mode = modes; mode; mode = mode->next) {
114335c4bbdfSmrg        float mode_aspect, metaspect;
114435c4bbdfSmrg
114535c4bbdfSmrg        if (!(mode->type & (M_T_DRIVER | M_T_USERDEF)))
114635c4bbdfSmrg            continue;
114735c4bbdfSmrg        mode_aspect = (float) mode->HDisplay / (float) mode->VDisplay;
114835c4bbdfSmrg        metaspect = aspect / mode_aspect;
114935c4bbdfSmrg        /* 5% slop or so, since we only get size in centimeters */
115035c4bbdfSmrg        if (fabs(1.0 - metaspect) < 0.05) {
115135c4bbdfSmrg            if ((mode->HDisplay > x) && (mode->VDisplay > y)) {
115235c4bbdfSmrg                x = mode->HDisplay;
115335c4bbdfSmrg                y = mode->VDisplay;
115435c4bbdfSmrg            }
115535c4bbdfSmrg        }
115605b261ecSmrg    }
115705b261ecSmrg
115805b261ecSmrg    if (!x || !y) {
115935c4bbdfSmrg        xf86DrvMsg(scrp->scrnIndex, X_WARNING,
116035c4bbdfSmrg                   "Unable to estimate virtual size\n");
116135c4bbdfSmrg        return 0;
116205b261ecSmrg    }
116305b261ecSmrg
116435c4bbdfSmrg found:
116505b261ecSmrg    *vx = x;
116605b261ecSmrg    *vy = y;
116705b261ecSmrg
116805b261ecSmrg    xf86DrvMsg(scrp->scrnIndex, X_INFO,
116935c4bbdfSmrg               "Estimated virtual size for aspect ratio %.4f is %dx%d\n",
117035c4bbdfSmrg               aspect, *vx, *vy);
117105b261ecSmrg
117205b261ecSmrg    return 1;
117305b261ecSmrg}
117405b261ecSmrg
11756747b715Smrg/* Least common multiple */
11766747b715Smrgstatic unsigned int
11776747b715SmrgLCM(unsigned int x, unsigned int y)
11786747b715Smrg{
11796747b715Smrg    unsigned int m = x, n = y, o;
11806747b715Smrg
118135c4bbdfSmrg    while ((o = m % n)) {
11826747b715Smrg        m = n;
11836747b715Smrg        n = o;
11846747b715Smrg    }
11856747b715Smrg
11866747b715Smrg    return (x / n) * y;
11876747b715Smrg}
11886747b715Smrg
11896747b715Smrg/*
11906747b715Smrg * Given various screen attributes, determine the minimum scanline width such
1191ed6184dfSmrg * that each scanline is server and DDX padded and any pixels with embedded
11926747b715Smrg * bank boundaries are off-screen.  This function returns -1 if such a width
11936747b715Smrg * cannot exist.
11946747b715Smrg */
11956747b715Smrgstatic int
119635c4bbdfSmrgscanLineWidth(unsigned int xsize,       /* pixels */
119735c4bbdfSmrg              unsigned int ysize,       /* pixels */
119835c4bbdfSmrg              unsigned int width,       /* pixels */
119935c4bbdfSmrg              unsigned long BankSize,   /* char's */
120035c4bbdfSmrg              PixmapFormatRec * pBankFormat, unsigned int nWidthUnit    /* bits */
120135c4bbdfSmrg    )
12026747b715Smrg{
12036747b715Smrg    unsigned long nBitsPerBank, nBitsPerScanline, nBitsPerScanlinePadUnit;
12046747b715Smrg    unsigned long minBitsPerScanline, maxBitsPerScanline;
12056747b715Smrg
12066747b715Smrg    /* Sanity checks */
12076747b715Smrg
12086747b715Smrg    if (!nWidthUnit || !pBankFormat)
12096747b715Smrg        return -1;
12106747b715Smrg
12116747b715Smrg    nBitsPerBank = BankSize * 8;
12126747b715Smrg    if (nBitsPerBank % pBankFormat->scanlinePad)
12136747b715Smrg        return -1;
12146747b715Smrg
12156747b715Smrg    if (xsize > width)
12166747b715Smrg        width = xsize;
12176747b715Smrg    nBitsPerScanlinePadUnit = LCM(pBankFormat->scanlinePad, nWidthUnit);
12186747b715Smrg    nBitsPerScanline =
12196747b715Smrg        (((width * pBankFormat->bitsPerPixel) + nBitsPerScanlinePadUnit - 1) /
12206747b715Smrg         nBitsPerScanlinePadUnit) * nBitsPerScanlinePadUnit;
12216747b715Smrg    width = nBitsPerScanline / pBankFormat->bitsPerPixel;
12226747b715Smrg
12236747b715Smrg    if (!xsize || !(nBitsPerBank % pBankFormat->bitsPerPixel))
122435c4bbdfSmrg        return (int) width;
12256747b715Smrg
12266747b715Smrg    /*
12276747b715Smrg     * Scanlines will be server-pad aligned at this point.  They will also be
1228ed6184dfSmrg     * a multiple of nWidthUnit bits long.  Ensure that pixels with embedded
12296747b715Smrg     * bank boundaries are off-screen.
12306747b715Smrg     *
12316747b715Smrg     * It seems reasonable to limit total frame buffer size to 1/16 of the
12326747b715Smrg     * theoretical maximum address space size.  On a machine with 32-bit
12336747b715Smrg     * addresses (to 8-bit quantities) this turns out to be 256MB.  Not only
12346747b715Smrg     * does this provide a simple limiting condition for the loops below, but
12356747b715Smrg     * it also prevents unsigned long wraparounds.
12366747b715Smrg     */
12376747b715Smrg    if (!ysize)
12386747b715Smrg        return -1;
12396747b715Smrg
12406747b715Smrg    minBitsPerScanline = xsize * pBankFormat->bitsPerPixel;
12416747b715Smrg    if (minBitsPerScanline > nBitsPerBank)
12426747b715Smrg        return -1;
12436747b715Smrg
12446747b715Smrg    if (ysize == 1)
124535c4bbdfSmrg        return (int) width;
12466747b715Smrg
12476747b715Smrg    maxBitsPerScanline =
124835c4bbdfSmrg        (((unsigned long) (-1) >> 1) - minBitsPerScanline) / (ysize - 1);
124935c4bbdfSmrg    while (nBitsPerScanline <= maxBitsPerScanline) {
12506747b715Smrg        unsigned long BankBase, BankUnit;
12516747b715Smrg
12526747b715Smrg        BankUnit = ((nBitsPerBank + nBitsPerScanline - 1) / nBitsPerBank) *
12536747b715Smrg            nBitsPerBank;
12546747b715Smrg        if (!(BankUnit % nBitsPerScanline))
125535c4bbdfSmrg            return (int) width;
12566747b715Smrg
125735c4bbdfSmrg        for (BankBase = BankUnit;; BankBase += nBitsPerBank) {
12586747b715Smrg            unsigned long x, y;
12596747b715Smrg
12606747b715Smrg            y = BankBase / nBitsPerScanline;
12616747b715Smrg            if (y >= ysize)
126235c4bbdfSmrg                return (int) width;
12636747b715Smrg
12646747b715Smrg            x = BankBase % nBitsPerScanline;
12656747b715Smrg            if (!(x % pBankFormat->bitsPerPixel))
12666747b715Smrg                continue;
12676747b715Smrg
126835c4bbdfSmrg            if (x < minBitsPerScanline) {
12696747b715Smrg                /*
12706747b715Smrg                 * Skip ahead certain widths by dividing the excess scanline
12716747b715Smrg                 * amongst the y's.
12726747b715Smrg                 */
12736747b715Smrg                y *= nBitsPerScanlinePadUnit;
127435c4bbdfSmrg                nBitsPerScanline += ((x + y - 1) / y) * nBitsPerScanlinePadUnit;
12756747b715Smrg                width = nBitsPerScanline / pBankFormat->bitsPerPixel;
12766747b715Smrg                break;
12776747b715Smrg            }
12786747b715Smrg
12796747b715Smrg            if (BankBase != BankUnit)
12806747b715Smrg                continue;
12816747b715Smrg
12826747b715Smrg            if (!(nBitsPerScanline % x))
128335c4bbdfSmrg                return (int) width;
12846747b715Smrg
12856747b715Smrg            BankBase = ((nBitsPerScanline - minBitsPerScanline) /
128635c4bbdfSmrg                        (nBitsPerScanline - x)) * BankUnit;
12876747b715Smrg        }
12886747b715Smrg    }
12896747b715Smrg
12906747b715Smrg    return -1;
12916747b715Smrg}
12926747b715Smrg
129305b261ecSmrg/*
129405b261ecSmrg * xf86ValidateModes
129505b261ecSmrg *
129605b261ecSmrg * This function takes a set of mode names, modes and limiting conditions,
129705b261ecSmrg * and selects a set of modes and parameters based on those conditions.
129805b261ecSmrg *
129905b261ecSmrg * This function takes the following parameters:
130005b261ecSmrg *    scrp         ScrnInfoPtr
130105b261ecSmrg *    availModes   the list of modes available for the monitor
130205b261ecSmrg *    modeNames    (optional) list of mode names that the screen is requesting
130305b261ecSmrg *    clockRanges  a list of clock ranges
130405b261ecSmrg *    linePitches  (optional) a list of line pitches
130505b261ecSmrg *    minPitch     (optional) minimum line pitch (in pixels)
130605b261ecSmrg *    maxPitch     (optional) maximum line pitch (in pixels)
130705b261ecSmrg *    pitchInc     (mandatory) pitch increment (in bits)
130805b261ecSmrg *    minHeight    (optional) minimum virtual height (in pixels)
130905b261ecSmrg *    maxHeight    (optional) maximum virtual height (in pixels)
131005b261ecSmrg *    virtualX     (optional) virtual width requested (in pixels)
131105b261ecSmrg *    virtualY     (optional) virtual height requested (in pixels)
131205b261ecSmrg *    apertureSize size of video aperture (in bytes)
131305b261ecSmrg *    strategy     how to decide which mode to use from multiple modes with
131405b261ecSmrg *                 the same name
131505b261ecSmrg *
131605b261ecSmrg * In addition, the following fields from the ScrnInfoRec are used:
131705b261ecSmrg *    clocks       a list of discrete clocks
131805b261ecSmrg *    numClocks    number of discrete clocks
131905b261ecSmrg *    progClock    clock is programmable
132005b261ecSmrg *    monitor      pointer to structure for monitor section
132105b261ecSmrg *    fbFormat     format of the framebuffer
132205b261ecSmrg *    videoRam     video memory size
132305b261ecSmrg *    xInc         horizontal timing increment (defaults to 8 pixels)
132405b261ecSmrg *
132505b261ecSmrg * The function fills in the following ScrnInfoRec fields:
132605b261ecSmrg *    modePool     A subset of the modes available to the monitor which
132705b261ecSmrg *		   are compatible with the driver.
132805b261ecSmrg *    modes        one mode entry for each of the requested modes, with the
132905b261ecSmrg *                 status field filled in to indicate if the mode has been
133005b261ecSmrg *                 accepted or not.
133105b261ecSmrg *    virtualX     the resulting virtual width
133205b261ecSmrg *    virtualY     the resulting virtual height
133305b261ecSmrg *    displayWidth the resulting line pitch
133405b261ecSmrg *
133505b261ecSmrg * The function's return value is the number of matching modes found, or -1
133605b261ecSmrg * if an unrecoverable error was encountered.
133705b261ecSmrg */
133805b261ecSmrg
13396747b715Smrgint
134005b261ecSmrgxf86ValidateModes(ScrnInfoPtr scrp, DisplayModePtr availModes,
134135c4bbdfSmrg                  const char **modeNames, ClockRangePtr clockRanges,
134235c4bbdfSmrg                  int *linePitches, int minPitch, int maxPitch, int pitchInc,
134335c4bbdfSmrg                  int minHeight, int maxHeight, int virtualX, int virtualY,
134435c4bbdfSmrg                  int apertureSize, LookupModeFlags strategy)
134505b261ecSmrg{
134605b261ecSmrg    DisplayModePtr p, q, r, new, last, *endp;
134705b261ecSmrg    int i, numModes = 0;
134805b261ecSmrg    ModeStatus status;
134905b261ecSmrg    int linePitch = -1, virtX = 0, virtY = 0;
135005b261ecSmrg    int newLinePitch, newVirtX, newVirtY;
135135c4bbdfSmrg    int modeSize;               /* in pixels */
135205b261ecSmrg    Bool validateAllDefaultModes = FALSE;
135305b261ecSmrg    Bool userModes = FALSE;
135405b261ecSmrg    int saveType;
135505b261ecSmrg    PixmapFormatRec *BankFormat;
135605b261ecSmrg    ClockRangePtr cp;
135705b261ecSmrg    int numTimings = 0;
135805b261ecSmrg    range hsync[MAX_HSYNC];
135905b261ecSmrg    range vrefresh[MAX_VREFRESH];
136005b261ecSmrg    Bool inferred_virtual = FALSE;
136105b261ecSmrg
136235c4bbdfSmrg    DebugF
136335c4bbdfSmrg        ("xf86ValidateModes(%p, %p, %p, %p,\n\t\t  %p, %d, %d, %d, %d, %d, %d, %d, %d, 0x%x)\n",
136435c4bbdfSmrg         scrp, availModes, modeNames, clockRanges, linePitches, minPitch,
136535c4bbdfSmrg         maxPitch, pitchInc, minHeight, maxHeight, virtualX, virtualY,
136635c4bbdfSmrg         apertureSize, strategy);
136705b261ecSmrg
136805b261ecSmrg    /* Some sanity checking */
136905b261ecSmrg    if (scrp == NULL || scrp->name == NULL || !scrp->monitor ||
137035c4bbdfSmrg        (!scrp->progClock && scrp->numClocks == 0)) {
137135c4bbdfSmrg        ErrorF("xf86ValidateModes: called with invalid scrnInfoRec\n");
137235c4bbdfSmrg        return -1;
137305b261ecSmrg    }
137405b261ecSmrg    if (linePitches != NULL && linePitches[0] <= 0) {
137535c4bbdfSmrg        ErrorF("xf86ValidateModes: called with invalid linePitches\n");
137635c4bbdfSmrg        return -1;
137705b261ecSmrg    }
137805b261ecSmrg    if (pitchInc <= 0) {
137935c4bbdfSmrg        ErrorF("xf86ValidateModes: called with invalid pitchInc\n");
138035c4bbdfSmrg        return -1;
138105b261ecSmrg    }
138205b261ecSmrg    if ((virtualX > 0) != (virtualY > 0)) {
138335c4bbdfSmrg        ErrorF("xf86ValidateModes: called with invalid virtual resolution\n");
138435c4bbdfSmrg        return -1;
138505b261ecSmrg    }
138605b261ecSmrg
138705b261ecSmrg    /*
138805b261ecSmrg     * If requested by the driver, allow missing hsync and/or vrefresh ranges
138905b261ecSmrg     * in the monitor section.
139005b261ecSmrg     */
139105b261ecSmrg    if (strategy & LOOKUP_OPTIONAL_TOLERANCES) {
139235c4bbdfSmrg        strategy &= ~LOOKUP_OPTIONAL_TOLERANCES;
139335c4bbdfSmrg    }
139435c4bbdfSmrg    else {
139535c4bbdfSmrg        const char *type = "";
13969ace9065Smrg        Bool specified = FALSE;
139705b261ecSmrg
139835c4bbdfSmrg        if (scrp->monitor->nHsync <= 0) {
139935c4bbdfSmrg            if (numTimings > 0) {
140035c4bbdfSmrg                scrp->monitor->nHsync = numTimings;
140135c4bbdfSmrg                for (i = 0; i < numTimings; i++) {
140235c4bbdfSmrg                    scrp->monitor->hsync[i].lo = hsync[i].lo;
140335c4bbdfSmrg                    scrp->monitor->hsync[i].hi = hsync[i].hi;
140435c4bbdfSmrg                }
140535c4bbdfSmrg            }
140635c4bbdfSmrg            else {
140735c4bbdfSmrg                scrp->monitor->hsync[0].lo = 31.5;
140835c4bbdfSmrg                scrp->monitor->hsync[0].hi = 48.0;
140935c4bbdfSmrg                scrp->monitor->nHsync = 1;
141035c4bbdfSmrg            }
141135c4bbdfSmrg            type = "default ";
141235c4bbdfSmrg        }
141335c4bbdfSmrg        else {
14149ace9065Smrg            specified = TRUE;
14159ace9065Smrg        }
141635c4bbdfSmrg        for (i = 0; i < scrp->monitor->nHsync; i++) {
141735c4bbdfSmrg            if (scrp->monitor->hsync[i].lo == scrp->monitor->hsync[i].hi)
141835c4bbdfSmrg                xf86DrvMsg(scrp->scrnIndex, X_INFO,
141935c4bbdfSmrg                           "%s: Using %shsync value of %.2f kHz\n",
142035c4bbdfSmrg                           scrp->monitor->id, type, scrp->monitor->hsync[i].lo);
142135c4bbdfSmrg            else
142235c4bbdfSmrg                xf86DrvMsg(scrp->scrnIndex, X_INFO,
142335c4bbdfSmrg                           "%s: Using %shsync range of %.2f-%.2f kHz\n",
142435c4bbdfSmrg                           scrp->monitor->id, type,
142535c4bbdfSmrg                           scrp->monitor->hsync[i].lo,
142635c4bbdfSmrg                           scrp->monitor->hsync[i].hi);
142735c4bbdfSmrg        }
142835c4bbdfSmrg
142935c4bbdfSmrg        type = "";
143035c4bbdfSmrg        if (scrp->monitor->nVrefresh <= 0) {
143135c4bbdfSmrg            if (numTimings > 0) {
143235c4bbdfSmrg                scrp->monitor->nVrefresh = numTimings;
143335c4bbdfSmrg                for (i = 0; i < numTimings; i++) {
143435c4bbdfSmrg                    scrp->monitor->vrefresh[i].lo = vrefresh[i].lo;
143535c4bbdfSmrg                    scrp->monitor->vrefresh[i].hi = vrefresh[i].hi;
143635c4bbdfSmrg                }
143735c4bbdfSmrg            }
143835c4bbdfSmrg            else {
143935c4bbdfSmrg                scrp->monitor->vrefresh[0].lo = 50;
144035c4bbdfSmrg                scrp->monitor->vrefresh[0].hi = 70;
144135c4bbdfSmrg                scrp->monitor->nVrefresh = 1;
144235c4bbdfSmrg            }
144335c4bbdfSmrg            type = "default ";
144435c4bbdfSmrg        }
144535c4bbdfSmrg        else {
14469ace9065Smrg            specified = TRUE;
14479ace9065Smrg        }
144835c4bbdfSmrg        for (i = 0; i < scrp->monitor->nVrefresh; i++) {
144935c4bbdfSmrg            if (scrp->monitor->vrefresh[i].lo == scrp->monitor->vrefresh[i].hi)
145035c4bbdfSmrg                xf86DrvMsg(scrp->scrnIndex, X_INFO,
145135c4bbdfSmrg                           "%s: Using %svrefresh value of %.2f Hz\n",
145235c4bbdfSmrg                           scrp->monitor->id, type,
145335c4bbdfSmrg                           scrp->monitor->vrefresh[i].lo);
145435c4bbdfSmrg            else
145535c4bbdfSmrg                xf86DrvMsg(scrp->scrnIndex, X_INFO,
145635c4bbdfSmrg                           "%s: Using %svrefresh range of %.2f-%.2f Hz\n",
145735c4bbdfSmrg                           scrp->monitor->id, type,
145835c4bbdfSmrg                           scrp->monitor->vrefresh[i].lo,
145935c4bbdfSmrg                           scrp->monitor->vrefresh[i].hi);
146035c4bbdfSmrg        }
14619ace9065Smrg
14629ace9065Smrg        type = "";
146335c4bbdfSmrg        if (!scrp->monitor->maxPixClock && !specified) {
14649ace9065Smrg            type = "default ";
14659ace9065Smrg            scrp->monitor->maxPixClock = 65000.0;
14669ace9065Smrg        }
146735c4bbdfSmrg        if (scrp->monitor->maxPixClock) {
146835c4bbdfSmrg            xf86DrvMsg(scrp->scrnIndex, X_INFO,
146935c4bbdfSmrg                       "%s: Using %smaximum pixel clock of %.2f MHz\n",
147035c4bbdfSmrg                       scrp->monitor->id, type,
147135c4bbdfSmrg                       (float) scrp->monitor->maxPixClock / 1000.0);
147235c4bbdfSmrg        }
147305b261ecSmrg    }
147405b261ecSmrg
147505b261ecSmrg    /*
14766747b715Smrg     * Store the clockRanges for later use by the VidMode extension.
147705b261ecSmrg     */
147835c4bbdfSmrg    nt_list_for_each_entry(cp, clockRanges, next) {
147935c4bbdfSmrg        ClockRangePtr newCR = xnfalloc(sizeof(ClockRange));
148035c4bbdfSmrg        memcpy(newCR, cp, sizeof(ClockRange));
148135c4bbdfSmrg        newCR->next = NULL;
148235c4bbdfSmrg        if (scrp->clockRanges == NULL)
148335c4bbdfSmrg            scrp->clockRanges = newCR;
148435c4bbdfSmrg        else
148535c4bbdfSmrg            nt_list_append(newCR, scrp->clockRanges, ClockRange, next);
148605b261ecSmrg    }
148705b261ecSmrg
14886747b715Smrg    /* Determine which pixmap format to pass to scanLineWidth() */
148905b261ecSmrg    if (scrp->depth > 4)
149035c4bbdfSmrg        BankFormat = &scrp->fbFormat;
149105b261ecSmrg    else
149235c4bbdfSmrg        BankFormat = xf86GetPixFormat(scrp, 1); /* >not< scrp->depth! */
149305b261ecSmrg
149405b261ecSmrg    if (scrp->xInc <= 0)
149535c4bbdfSmrg        scrp->xInc = 8;         /* Suitable for VGA and others */
149605b261ecSmrg
149705b261ecSmrg#define _VIRTUALX(x) ((((x) + scrp->xInc - 1) / scrp->xInc) * scrp->xInc)
149805b261ecSmrg
149905b261ecSmrg    /*
150005b261ecSmrg     * Determine maxPitch if it wasn't given explicitly.  Note linePitches
150105b261ecSmrg     * always takes precedence if is non-NULL.  In that case the minPitch and
150205b261ecSmrg     * maxPitch values passed are ignored.
150305b261ecSmrg     */
150405b261ecSmrg    if (linePitches) {
150535c4bbdfSmrg        minPitch = maxPitch = linePitches[0];
150635c4bbdfSmrg        for (i = 1; linePitches[i] > 0; i++) {
150735c4bbdfSmrg            if (linePitches[i] > maxPitch)
150835c4bbdfSmrg                maxPitch = linePitches[i];
150935c4bbdfSmrg            if (linePitches[i] < minPitch)
151035c4bbdfSmrg                minPitch = linePitches[i];
151135c4bbdfSmrg        }
151205b261ecSmrg    }
151305b261ecSmrg
151405b261ecSmrg    /*
151505b261ecSmrg     * Initialise virtX and virtY if the values are fixed.
151605b261ecSmrg     */
151705b261ecSmrg    if (virtualY > 0) {
151835c4bbdfSmrg        if (maxHeight > 0 && virtualY > maxHeight) {
151935c4bbdfSmrg            xf86DrvMsg(scrp->scrnIndex, X_ERROR,
152035c4bbdfSmrg                       "Virtual height (%d) is too large for the hardware "
152135c4bbdfSmrg                       "(max %d)\n", virtualY, maxHeight);
152235c4bbdfSmrg            return -1;
152335c4bbdfSmrg        }
152435c4bbdfSmrg
152535c4bbdfSmrg        if (minHeight > 0 && virtualY < minHeight) {
152635c4bbdfSmrg            xf86DrvMsg(scrp->scrnIndex, X_ERROR,
152735c4bbdfSmrg                       "Virtual height (%d) is too small for the hardware "
152835c4bbdfSmrg                       "(min %d)\n", virtualY, minHeight);
152935c4bbdfSmrg            return -1;
153035c4bbdfSmrg        }
153135c4bbdfSmrg
153235c4bbdfSmrg        virtualX = _VIRTUALX(virtualX);
153335c4bbdfSmrg        if (linePitches != NULL) {
153435c4bbdfSmrg            for (i = 0; linePitches[i] != 0; i++) {
153535c4bbdfSmrg                if ((linePitches[i] >= virtualX) &&
153635c4bbdfSmrg                    (linePitches[i] ==
153735c4bbdfSmrg                     scanLineWidth(virtualX, virtualY, linePitches[i],
153835c4bbdfSmrg                                   apertureSize, BankFormat, pitchInc))) {
153935c4bbdfSmrg                    linePitch = linePitches[i];
154035c4bbdfSmrg                    break;
154135c4bbdfSmrg                }
154235c4bbdfSmrg            }
154335c4bbdfSmrg        }
154435c4bbdfSmrg        else {
154535c4bbdfSmrg            linePitch = scanLineWidth(virtualX, virtualY, minPitch,
154635c4bbdfSmrg                                      apertureSize, BankFormat, pitchInc);
154735c4bbdfSmrg        }
154835c4bbdfSmrg
154935c4bbdfSmrg        if ((linePitch < minPitch) || (linePitch > maxPitch)) {
155035c4bbdfSmrg            xf86DrvMsg(scrp->scrnIndex, X_ERROR,
155135c4bbdfSmrg                       "Virtual width (%d) is too large for the hardware "
155235c4bbdfSmrg                       "(max %d)\n", virtualX, maxPitch);
155335c4bbdfSmrg            return -1;
155435c4bbdfSmrg        }
155535c4bbdfSmrg
155635c4bbdfSmrg        if (!xf86CheckModeSize(scrp, linePitch, virtualX, virtualY)) {
155735c4bbdfSmrg            xf86DrvMsg(scrp->scrnIndex, X_ERROR,
155835c4bbdfSmrg                       "Virtual size (%dx%d) (pitch %d) exceeds video memory\n",
155935c4bbdfSmrg                       virtualX, virtualY, linePitch);
156035c4bbdfSmrg            return -1;
156135c4bbdfSmrg        }
156235c4bbdfSmrg
156335c4bbdfSmrg        virtX = virtualX;
156435c4bbdfSmrg        virtY = virtualY;
156535c4bbdfSmrg    }
156635c4bbdfSmrg    else if (!modeNames || !*modeNames) {
156735c4bbdfSmrg        /* No virtual size given in the config, try to infer */
156835c4bbdfSmrg        /* XXX this doesn't take m{in,ax}Pitch into account; oh well */
156935c4bbdfSmrg        inferred_virtual = inferVirtualSize(scrp, availModes, &virtX, &virtY);
157035c4bbdfSmrg        if (inferred_virtual)
157135c4bbdfSmrg            linePitch = scanLineWidth(virtX, virtY, minPitch, apertureSize,
157235c4bbdfSmrg                                      BankFormat, pitchInc);
157305b261ecSmrg    }
157405b261ecSmrg
157505b261ecSmrg    /* Print clock ranges and scaled clocks */
157605b261ecSmrg    xf86ShowClockRanges(scrp, clockRanges);
157705b261ecSmrg
157805b261ecSmrg    /*
157905b261ecSmrg     * If scrp->modePool hasn't been setup yet, set it up now.  This allows the
158005b261ecSmrg     * modes that the driver definitely can't use to be weeded out early.  Note
158105b261ecSmrg     * that a modePool mode's prev field is used to hold a pointer to the
158205b261ecSmrg     * member of the scrp->modes list for which a match was considered.
158305b261ecSmrg     */
158405b261ecSmrg    if (scrp->modePool == NULL) {
158535c4bbdfSmrg        q = NULL;
158635c4bbdfSmrg        for (p = availModes; p != NULL; p = p->next) {
158735c4bbdfSmrg            status = xf86InitialCheckModeForDriver(scrp, p, clockRanges,
158835c4bbdfSmrg                                                   strategy, maxPitch,
158935c4bbdfSmrg                                                   virtX, virtY);
159035c4bbdfSmrg
159135c4bbdfSmrg            if (status == MODE_OK) {
159235c4bbdfSmrg                status = xf86CheckModeForMonitor(p, scrp->monitor);
159335c4bbdfSmrg            }
159435c4bbdfSmrg
159535c4bbdfSmrg            if (status == MODE_OK) {
159635c4bbdfSmrg                new = xnfalloc(sizeof(DisplayModeRec));
159735c4bbdfSmrg                *new = *p;
159835c4bbdfSmrg                new->next = NULL;
159935c4bbdfSmrg                if (!q) {
160035c4bbdfSmrg                    scrp->modePool = new;
160135c4bbdfSmrg                }
160235c4bbdfSmrg                else {
160335c4bbdfSmrg                    q->next = new;
160435c4bbdfSmrg                }
160535c4bbdfSmrg                new->prev = NULL;
160635c4bbdfSmrg                q = new;
160735c4bbdfSmrg                q->name = xnfstrdup(p->name);
160835c4bbdfSmrg                q->status = MODE_OK;
160935c4bbdfSmrg            }
161035c4bbdfSmrg            else {
161135c4bbdfSmrg                printModeRejectMessage(scrp->scrnIndex, p, status);
161235c4bbdfSmrg            }
161335c4bbdfSmrg        }
161435c4bbdfSmrg
161535c4bbdfSmrg        if (scrp->modePool == NULL) {
161635c4bbdfSmrg            xf86DrvMsg(scrp->scrnIndex, X_WARNING, "Mode pool is empty\n");
161735c4bbdfSmrg            return 0;
161835c4bbdfSmrg        }
161935c4bbdfSmrg    }
162035c4bbdfSmrg    else {
162135c4bbdfSmrg        for (p = scrp->modePool; p != NULL; p = p->next) {
162235c4bbdfSmrg            p->prev = NULL;
162335c4bbdfSmrg            p->status = MODE_OK;
162435c4bbdfSmrg        }
162505b261ecSmrg    }
162605b261ecSmrg
162705b261ecSmrg    /*
162805b261ecSmrg     * Allocate one entry in scrp->modes for each named mode.
162905b261ecSmrg     */
163005b261ecSmrg    while (scrp->modes)
163135c4bbdfSmrg        xf86DeleteMode(&scrp->modes, scrp->modes);
163205b261ecSmrg    endp = &scrp->modes;
163305b261ecSmrg    last = NULL;
163405b261ecSmrg    if (modeNames != NULL) {
163535c4bbdfSmrg        for (i = 0; modeNames[i] != NULL; i++) {
163635c4bbdfSmrg            userModes = TRUE;
163735c4bbdfSmrg            new = xnfcalloc(1, sizeof(DisplayModeRec));
163835c4bbdfSmrg            new->prev = last;
163935c4bbdfSmrg            new->type = M_T_USERDEF;
164035c4bbdfSmrg            new->name = xnfstrdup(modeNames[i]);
164135c4bbdfSmrg            if (new->prev)
164235c4bbdfSmrg                new->prev->next = new;
164335c4bbdfSmrg            *endp = last = new;
164435c4bbdfSmrg            endp = &new->next;
164535c4bbdfSmrg        }
164605b261ecSmrg    }
164705b261ecSmrg
164805b261ecSmrg    /* Lookup each mode */
164905b261ecSmrg#ifdef PANORAMIX
16501b5d61b8Smrg    if (noPanoramiXExtension)
165135c4bbdfSmrg        validateAllDefaultModes = TRUE;
165205b261ecSmrg#endif
165305b261ecSmrg
165435c4bbdfSmrg    for (p = scrp->modes;; p = p->next) {
165535c4bbdfSmrg        Bool repeat;
165635c4bbdfSmrg
165735c4bbdfSmrg        /*
165835c4bbdfSmrg         * If the supplied mode names don't produce a valid mode, scan through
165935c4bbdfSmrg         * unconsidered modePool members until one survives validation.  This
166035c4bbdfSmrg         * is done in decreasing order by mode pixel area.
166135c4bbdfSmrg         */
166235c4bbdfSmrg
166335c4bbdfSmrg        if (p == NULL) {
166435c4bbdfSmrg            if ((numModes > 0) && !validateAllDefaultModes)
166535c4bbdfSmrg                break;
166635c4bbdfSmrg
166735c4bbdfSmrg            validateAllDefaultModes = TRUE;
166835c4bbdfSmrg            r = NULL;
166935c4bbdfSmrg            modeSize = 0;
167035c4bbdfSmrg            for (q = scrp->modePool; q != NULL; q = q->next) {
167135c4bbdfSmrg                if ((q->prev == NULL) && (q->status == MODE_OK)) {
167235c4bbdfSmrg                    /*
167335c4bbdfSmrg                     * Deal with the case where this mode wasn't considered
167435c4bbdfSmrg                     * because of a builtin mode of the same name.
167535c4bbdfSmrg                     */
167635c4bbdfSmrg                    for (p = scrp->modes; p != NULL; p = p->next) {
167735c4bbdfSmrg                        if ((p->status != MODE_OK) && !strcmp(p->name, q->name))
167835c4bbdfSmrg                            break;
167935c4bbdfSmrg                    }
168035c4bbdfSmrg
168135c4bbdfSmrg                    if (p != NULL)
168235c4bbdfSmrg                        q->prev = p;
168335c4bbdfSmrg                    else {
168435c4bbdfSmrg                        /*
168535c4bbdfSmrg                         * A quick check to not allow default modes with
168635c4bbdfSmrg                         * horizontal timing parameters that CRTs may have
168735c4bbdfSmrg                         * problems with.
168835c4bbdfSmrg                         */
168935c4bbdfSmrg                        if (!scrp->monitor->reducedblanking &&
169035c4bbdfSmrg                            (q->type & M_T_DEFAULT) &&
169135c4bbdfSmrg                            ((double) q->HTotal / (double) q->HDisplay) < 1.15)
169235c4bbdfSmrg                            continue;
169335c4bbdfSmrg
169435c4bbdfSmrg                        if (modeSize < (q->HDisplay * q->VDisplay)) {
169535c4bbdfSmrg                            r = q;
169635c4bbdfSmrg                            modeSize = q->HDisplay * q->VDisplay;
169735c4bbdfSmrg                        }
169835c4bbdfSmrg                    }
169935c4bbdfSmrg                }
170035c4bbdfSmrg            }
170135c4bbdfSmrg
170235c4bbdfSmrg            if (r == NULL)
170335c4bbdfSmrg                break;
170435c4bbdfSmrg
170535c4bbdfSmrg            p = xnfcalloc(1, sizeof(DisplayModeRec));
170635c4bbdfSmrg            p->prev = last;
170735c4bbdfSmrg            p->name = xnfstrdup(r->name);
170835c4bbdfSmrg            if (!userModes)
170935c4bbdfSmrg                p->type = M_T_USERDEF;
171035c4bbdfSmrg            if (p->prev)
171135c4bbdfSmrg                p->prev->next = p;
171235c4bbdfSmrg            *endp = last = p;
171335c4bbdfSmrg            endp = &p->next;
171435c4bbdfSmrg        }
171535c4bbdfSmrg
171635c4bbdfSmrg        repeat = FALSE;
171735c4bbdfSmrg lookupNext:
171835c4bbdfSmrg        if (repeat && ((status = p->status) != MODE_OK))
171935c4bbdfSmrg            printModeRejectMessage(scrp->scrnIndex, p, status);
172035c4bbdfSmrg        saveType = p->type;
172135c4bbdfSmrg        status = xf86LookupMode(scrp, p, clockRanges, strategy);
172235c4bbdfSmrg        if (repeat && status == MODE_NOMODE)
172335c4bbdfSmrg            continue;
172435c4bbdfSmrg        if (status != MODE_OK)
172535c4bbdfSmrg            printModeRejectMessage(scrp->scrnIndex, p, status);
172635c4bbdfSmrg        if (status == MODE_ERROR) {
172735c4bbdfSmrg            ErrorF("xf86ValidateModes: "
172835c4bbdfSmrg                   "unexpected result from xf86LookupMode()\n");
172935c4bbdfSmrg            return -1;
173035c4bbdfSmrg        }
173135c4bbdfSmrg        if (status != MODE_OK) {
173235c4bbdfSmrg            if (p->status == MODE_OK)
173335c4bbdfSmrg                p->status = status;
173435c4bbdfSmrg            continue;
173535c4bbdfSmrg        }
173635c4bbdfSmrg        p->type |= saveType;
173735c4bbdfSmrg        repeat = TRUE;
173835c4bbdfSmrg
173935c4bbdfSmrg        newLinePitch = linePitch;
174035c4bbdfSmrg        newVirtX = virtX;
174135c4bbdfSmrg        newVirtY = virtY;
174235c4bbdfSmrg
174335c4bbdfSmrg        /*
174435c4bbdfSmrg         * Don't let non-user defined modes increase the virtual size
174535c4bbdfSmrg         */
174635c4bbdfSmrg        if (!(p->type & M_T_USERDEF) && (numModes > 0)) {
174735c4bbdfSmrg            if (p->HDisplay > virtX) {
174835c4bbdfSmrg                p->status = MODE_VIRTUAL_X;
174935c4bbdfSmrg                goto lookupNext;
175035c4bbdfSmrg            }
175135c4bbdfSmrg            if (p->VDisplay > virtY) {
175235c4bbdfSmrg                p->status = MODE_VIRTUAL_Y;
175335c4bbdfSmrg                goto lookupNext;
175435c4bbdfSmrg            }
175535c4bbdfSmrg        }
175635c4bbdfSmrg        /*
175735c4bbdfSmrg         * Adjust virtual width and height if the mode is too large for the
175835c4bbdfSmrg         * current values and if they are not fixed.
175935c4bbdfSmrg         */
176035c4bbdfSmrg        if (virtualX <= 0 && p->HDisplay > newVirtX)
176135c4bbdfSmrg            newVirtX = _VIRTUALX(p->HDisplay);
176235c4bbdfSmrg        if (virtualY <= 0 && p->VDisplay > newVirtY) {
176335c4bbdfSmrg            if (maxHeight > 0 && p->VDisplay > maxHeight) {
176435c4bbdfSmrg                p->status = MODE_VIRTUAL_Y;     /* ? */
176535c4bbdfSmrg                goto lookupNext;
176635c4bbdfSmrg            }
176735c4bbdfSmrg            newVirtY = p->VDisplay;
176835c4bbdfSmrg        }
176935c4bbdfSmrg
177035c4bbdfSmrg        /*
177135c4bbdfSmrg         * If virtual resolution is to be increased, revalidate it.
177235c4bbdfSmrg         */
177335c4bbdfSmrg        if ((virtX != newVirtX) || (virtY != newVirtY)) {
177435c4bbdfSmrg            if (linePitches != NULL) {
177535c4bbdfSmrg                newLinePitch = -1;
177635c4bbdfSmrg                for (i = 0; linePitches[i] != 0; i++) {
177735c4bbdfSmrg                    if ((linePitches[i] >= newVirtX) &&
177835c4bbdfSmrg                        (linePitches[i] >= linePitch) &&
177935c4bbdfSmrg                        (linePitches[i] ==
178035c4bbdfSmrg                         scanLineWidth(newVirtX, newVirtY, linePitches[i],
178135c4bbdfSmrg                                       apertureSize, BankFormat, pitchInc))) {
178235c4bbdfSmrg                        newLinePitch = linePitches[i];
178335c4bbdfSmrg                        break;
178435c4bbdfSmrg                    }
178535c4bbdfSmrg                }
178635c4bbdfSmrg            }
178735c4bbdfSmrg            else {
178835c4bbdfSmrg                if (linePitch < minPitch)
178935c4bbdfSmrg                    linePitch = minPitch;
179035c4bbdfSmrg                newLinePitch = scanLineWidth(newVirtX, newVirtY, linePitch,
179135c4bbdfSmrg                                             apertureSize, BankFormat,
179235c4bbdfSmrg                                             pitchInc);
179335c4bbdfSmrg            }
179435c4bbdfSmrg            if ((newLinePitch < minPitch) || (newLinePitch > maxPitch)) {
179535c4bbdfSmrg                p->status = MODE_BAD_WIDTH;
179635c4bbdfSmrg                goto lookupNext;
179735c4bbdfSmrg            }
179835c4bbdfSmrg
179935c4bbdfSmrg            /*
180035c4bbdfSmrg             * Check that the pixel area required by the new virtual height
180135c4bbdfSmrg             * and line pitch isn't too large.
180235c4bbdfSmrg             */
180335c4bbdfSmrg            if (!xf86CheckModeSize(scrp, newLinePitch, newVirtX, newVirtY)) {
180435c4bbdfSmrg                p->status = MODE_MEM_VIRT;
180535c4bbdfSmrg                goto lookupNext;
180635c4bbdfSmrg            }
180735c4bbdfSmrg        }
180835c4bbdfSmrg
180935c4bbdfSmrg        if (scrp->ValidMode) {
181035c4bbdfSmrg            /*
181135c4bbdfSmrg             * Give the driver a final say, passing it the proposed virtual
181235c4bbdfSmrg             * geometry.
181335c4bbdfSmrg             */
181435c4bbdfSmrg            scrp->virtualX = newVirtX;
181535c4bbdfSmrg            scrp->virtualY = newVirtY;
181635c4bbdfSmrg            scrp->displayWidth = newLinePitch;
181735c4bbdfSmrg            p->status = (scrp->ValidMode) (scrp, p, FALSE,
181835c4bbdfSmrg                                           MODECHECK_FINAL);
181935c4bbdfSmrg
182035c4bbdfSmrg            if (p->status != MODE_OK) {
182135c4bbdfSmrg                goto lookupNext;
182235c4bbdfSmrg            }
182335c4bbdfSmrg        }
182435c4bbdfSmrg
182535c4bbdfSmrg        /* Mode has passed all the tests */
182635c4bbdfSmrg        virtX = newVirtX;
182735c4bbdfSmrg        virtY = newVirtY;
182835c4bbdfSmrg        linePitch = newLinePitch;
182935c4bbdfSmrg        p->status = MODE_OK;
183035c4bbdfSmrg        numModes++;
183105b261ecSmrg    }
183205b261ecSmrg
183305b261ecSmrg    /*
183405b261ecSmrg     * If we estimated the virtual size above, we may have filtered away all
183505b261ecSmrg     * the modes that maximally match that size; scan again to find out and
183605b261ecSmrg     * fix up if so.
183705b261ecSmrg     */
183805b261ecSmrg    if (inferred_virtual) {
183935c4bbdfSmrg        int vx = 0, vy = 0;
184035c4bbdfSmrg
184135c4bbdfSmrg        for (p = scrp->modes; p; p = p->next) {
184235c4bbdfSmrg            if (p->HDisplay > vx && p->VDisplay > vy) {
184335c4bbdfSmrg                vx = p->HDisplay;
184435c4bbdfSmrg                vy = p->VDisplay;
184535c4bbdfSmrg            }
184635c4bbdfSmrg        }
184735c4bbdfSmrg        if (vx < virtX || vy < virtY) {
184835c4bbdfSmrg            const int types[] = {
184935c4bbdfSmrg                M_T_BUILTIN | M_T_PREFERRED,
185035c4bbdfSmrg                M_T_BUILTIN,
185135c4bbdfSmrg                M_T_DRIVER | M_T_PREFERRED,
185235c4bbdfSmrg                M_T_DRIVER,
185335c4bbdfSmrg                0
185435c4bbdfSmrg            };
18551b5d61b8Smrg            const int ntypes = ARRAY_SIZE(types);
185635c4bbdfSmrg            int n;
185735c4bbdfSmrg
185835c4bbdfSmrg            /*
185935c4bbdfSmrg             * We did not find the estimated virtual size. So now we want to
186035c4bbdfSmrg             * find the largest mode available, but we want to search in the
186135c4bbdfSmrg             * modes in the order of "types" listed above.
186235c4bbdfSmrg             */
186335c4bbdfSmrg            for (n = 0; n < ntypes; n++) {
186435c4bbdfSmrg                int type = types[n];
186535c4bbdfSmrg
186635c4bbdfSmrg                vx = 0;
186735c4bbdfSmrg                vy = 0;
186835c4bbdfSmrg                for (p = scrp->modes; p; p = p->next) {
186935c4bbdfSmrg                    /* scan through the modes in the sort order above */
187035c4bbdfSmrg                    if ((p->type & type) != type)
187135c4bbdfSmrg                        continue;
187235c4bbdfSmrg                    if (p->HDisplay > vx && p->VDisplay > vy) {
187335c4bbdfSmrg                        vx = p->HDisplay;
187435c4bbdfSmrg                        vy = p->VDisplay;
187535c4bbdfSmrg                    }
187635c4bbdfSmrg                }
187735c4bbdfSmrg                if (vx && vy)
187835c4bbdfSmrg                    /* Found one */
187935c4bbdfSmrg                    break;
188035c4bbdfSmrg            }
188135c4bbdfSmrg            xf86DrvMsg(scrp->scrnIndex, X_WARNING,
188235c4bbdfSmrg                       "Shrinking virtual size estimate from %dx%d to %dx%d\n",
188335c4bbdfSmrg                       virtX, virtY, vx, vy);
188435c4bbdfSmrg            virtX = _VIRTUALX(vx);
188535c4bbdfSmrg            virtY = vy;
188635c4bbdfSmrg            for (p = scrp->modes; p; p = p->next) {
188735c4bbdfSmrg                if (numModes > 0) {
188835c4bbdfSmrg                    if (p->HDisplay > virtX)
188935c4bbdfSmrg                        p->status = MODE_VIRTUAL_X;
189035c4bbdfSmrg                    if (p->VDisplay > virtY)
189135c4bbdfSmrg                        p->status = MODE_VIRTUAL_Y;
189235c4bbdfSmrg                    if (p->status != MODE_OK) {
189335c4bbdfSmrg                        numModes--;
189435c4bbdfSmrg                        printModeRejectMessage(scrp->scrnIndex, p, p->status);
189535c4bbdfSmrg                    }
189635c4bbdfSmrg                }
189735c4bbdfSmrg            }
189835c4bbdfSmrg            if (linePitches != NULL) {
189935c4bbdfSmrg                for (i = 0; linePitches[i] != 0; i++) {
190035c4bbdfSmrg                    if ((linePitches[i] >= virtX) &&
190135c4bbdfSmrg                        (linePitches[i] ==
190235c4bbdfSmrg                         scanLineWidth(virtX, virtY, linePitches[i],
190335c4bbdfSmrg                                       apertureSize, BankFormat, pitchInc))) {
190435c4bbdfSmrg                        linePitch = linePitches[i];
190535c4bbdfSmrg                        break;
190635c4bbdfSmrg                    }
190735c4bbdfSmrg                }
190835c4bbdfSmrg            }
190935c4bbdfSmrg            else {
191035c4bbdfSmrg                linePitch = scanLineWidth(virtX, virtY, minPitch,
191135c4bbdfSmrg                                          apertureSize, BankFormat, pitchInc);
191235c4bbdfSmrg            }
191335c4bbdfSmrg        }
191405b261ecSmrg    }
191505b261ecSmrg
191605b261ecSmrg    /* Update the ScrnInfoRec parameters */
191735c4bbdfSmrg
191805b261ecSmrg    scrp->virtualX = virtX;
191905b261ecSmrg    scrp->virtualY = virtY;
192005b261ecSmrg    scrp->displayWidth = linePitch;
192105b261ecSmrg
192205b261ecSmrg    if (numModes <= 0)
192335c4bbdfSmrg        return 0;
192435c4bbdfSmrg
192505b261ecSmrg    /* Make the mode list into a circular list by joining up the ends */
192605b261ecSmrg    p = scrp->modes;
192705b261ecSmrg    while (p->next != NULL)
192835c4bbdfSmrg        p = p->next;
192905b261ecSmrg    /* p is now the last mode on the list */
193005b261ecSmrg    p->next = scrp->modes;
193105b261ecSmrg    scrp->modes->prev = p;
193205b261ecSmrg
193305b261ecSmrg    if (minHeight > 0 && virtY < minHeight) {
193435c4bbdfSmrg        xf86DrvMsg(scrp->scrnIndex, X_ERROR,
193535c4bbdfSmrg                   "Virtual height (%d) is too small for the hardware "
193635c4bbdfSmrg                   "(min %d)\n", virtY, minHeight);
193735c4bbdfSmrg        return -1;
193805b261ecSmrg    }
193905b261ecSmrg
194005b261ecSmrg    return numModes;
194105b261ecSmrg}
194205b261ecSmrg
194305b261ecSmrg/*
194405b261ecSmrg * xf86DeleteMode
194505b261ecSmrg *
194605b261ecSmrg * This function removes a mode from a list of modes.
194705b261ecSmrg *
194805b261ecSmrg * There are different types of mode lists:
194905b261ecSmrg *
195005b261ecSmrg *  - singly linked linear lists, ending in NULL
195105b261ecSmrg *  - doubly linked linear lists, starting and ending in NULL
195205b261ecSmrg *  - doubly linked circular lists
195305b261ecSmrg *
195405b261ecSmrg */
195535c4bbdfSmrg
19566747b715Smrgvoid
195735c4bbdfSmrgxf86DeleteMode(DisplayModePtr * modeList, DisplayModePtr mode)
195805b261ecSmrg{
195905b261ecSmrg    /* Catch the easy/insane cases */
196005b261ecSmrg    if (modeList == NULL || *modeList == NULL || mode == NULL)
196135c4bbdfSmrg        return;
196205b261ecSmrg
196305b261ecSmrg    /* If the mode is at the start of the list, move the start of the list */
196405b261ecSmrg    if (*modeList == mode)
196535c4bbdfSmrg        *modeList = mode->next;
196605b261ecSmrg
196705b261ecSmrg    /* If mode is the only one on the list, set the list to NULL */
196805b261ecSmrg    if ((mode == mode->prev) && (mode == mode->next)) {
196935c4bbdfSmrg        *modeList = NULL;
197035c4bbdfSmrg    }
197135c4bbdfSmrg    else {
197235c4bbdfSmrg        if ((mode->prev != NULL) && (mode->prev->next == mode))
197335c4bbdfSmrg            mode->prev->next = mode->next;
197435c4bbdfSmrg        if ((mode->next != NULL) && (mode->next->prev == mode))
197535c4bbdfSmrg            mode->next->prev = mode->prev;
197605b261ecSmrg    }
197705b261ecSmrg
197835c4bbdfSmrg    free((void *) mode->name);
19796747b715Smrg    free(mode);
198005b261ecSmrg}
198105b261ecSmrg
198205b261ecSmrg/*
198305b261ecSmrg * xf86PruneDriverModes
198405b261ecSmrg *
198505b261ecSmrg * Remove modes from the driver's mode list which have been marked as
198605b261ecSmrg * invalid.
198705b261ecSmrg */
198805b261ecSmrg
19896747b715Smrgvoid
199005b261ecSmrgxf86PruneDriverModes(ScrnInfoPtr scrp)
199105b261ecSmrg{
199205b261ecSmrg    DisplayModePtr first, p, n;
199305b261ecSmrg
199405b261ecSmrg    p = scrp->modes;
199505b261ecSmrg    if (p == NULL)
199635c4bbdfSmrg        return;
199705b261ecSmrg
199805b261ecSmrg    do {
199935c4bbdfSmrg        if (!(first = scrp->modes))
200035c4bbdfSmrg            return;
200135c4bbdfSmrg        n = p->next;
200235c4bbdfSmrg        if (p->status != MODE_OK) {
200335c4bbdfSmrg            xf86DeleteMode(&(scrp->modes), p);
200435c4bbdfSmrg        }
200535c4bbdfSmrg        p = n;
200605b261ecSmrg    } while (p != NULL && p != first);
200705b261ecSmrg
200805b261ecSmrg    /* modePool is no longer needed, turf it */
200905b261ecSmrg    while (scrp->modePool) {
201035c4bbdfSmrg        /*
201135c4bbdfSmrg         * A modePool mode's prev field is used to hold a pointer to the
201235c4bbdfSmrg         * member of the scrp->modes list for which a match was considered.
201335c4bbdfSmrg         * Clear that pointer first, otherwise xf86DeleteMode might get
201435c4bbdfSmrg         * confused
201535c4bbdfSmrg         */
201635c4bbdfSmrg        scrp->modePool->prev = NULL;
201735c4bbdfSmrg        xf86DeleteMode(&scrp->modePool, scrp->modePool);
201805b261ecSmrg    }
201905b261ecSmrg}
202005b261ecSmrg
202105b261ecSmrg/*
202205b261ecSmrg * xf86SetCrtcForModes
202305b261ecSmrg *
202405b261ecSmrg * Goes through the screen's mode list, and initialises the Crtc
202505b261ecSmrg * parameters for each mode.  The initialisation includes adjustments
202605b261ecSmrg * for interlaced and double scan modes.
202705b261ecSmrg */
20286747b715Smrgvoid
202905b261ecSmrgxf86SetCrtcForModes(ScrnInfoPtr scrp, int adjustFlags)
203005b261ecSmrg{
203105b261ecSmrg    DisplayModePtr p;
203205b261ecSmrg
203305b261ecSmrg    /*
203405b261ecSmrg     * Store adjustFlags for use with the VidMode extension. There is an
203505b261ecSmrg     * implicit assumption here that SetCrtcForModes is called once.
203605b261ecSmrg     */
203705b261ecSmrg    scrp->adjustFlags = adjustFlags;
203805b261ecSmrg
203905b261ecSmrg    p = scrp->modes;
204005b261ecSmrg    if (p == NULL)
204135c4bbdfSmrg        return;
204205b261ecSmrg
204305b261ecSmrg    do {
204435c4bbdfSmrg        xf86SetModeCrtc(p, adjustFlags);
204535c4bbdfSmrg        DebugF("%sMode %s: %d (%d) %d %d (%d) %d %d (%d) %d %d (%d) %d\n",
204635c4bbdfSmrg               (p->type & M_T_DEFAULT) ? "Default " : "",
204735c4bbdfSmrg               p->name, p->CrtcHDisplay, p->CrtcHBlankStart,
204835c4bbdfSmrg               p->CrtcHSyncStart, p->CrtcHSyncEnd, p->CrtcHBlankEnd,
204935c4bbdfSmrg               p->CrtcHTotal, p->CrtcVDisplay, p->CrtcVBlankStart,
205035c4bbdfSmrg               p->CrtcVSyncStart, p->CrtcVSyncEnd, p->CrtcVBlankEnd,
205135c4bbdfSmrg               p->CrtcVTotal);
205235c4bbdfSmrg        p = p->next;
205305b261ecSmrg    } while (p != NULL && p != scrp->modes);
205405b261ecSmrg}
205505b261ecSmrg
20566747b715Smrgvoid
205705b261ecSmrgxf86PrintModes(ScrnInfoPtr scrp)
205805b261ecSmrg{
205905b261ecSmrg    DisplayModePtr p;
206005b261ecSmrg    float hsync, refresh = 0;
206135c4bbdfSmrg    const char *desc, *desc2, *prefix, *uprefix;
206205b261ecSmrg
206305b261ecSmrg    if (scrp == NULL)
206435c4bbdfSmrg        return;
206505b261ecSmrg
20661b5d61b8Smrg    xf86DrvMsg(scrp->scrnIndex, X_INFO, "Virtual size is %dx%d (pitch %d)\n",
20671b5d61b8Smrg               scrp->virtualX, scrp->virtualY, scrp->displayWidth);
206835c4bbdfSmrg
206905b261ecSmrg    p = scrp->modes;
207005b261ecSmrg    if (p == NULL)
207135c4bbdfSmrg        return;
207205b261ecSmrg
207305b261ecSmrg    do {
207435c4bbdfSmrg        desc = desc2 = "";
207535c4bbdfSmrg        hsync = xf86ModeHSync(p);
207635c4bbdfSmrg        refresh = xf86ModeVRefresh(p);
207735c4bbdfSmrg        if (p->Flags & V_INTERLACE) {
207835c4bbdfSmrg            desc = " (I)";
207935c4bbdfSmrg        }
208035c4bbdfSmrg        if (p->Flags & V_DBLSCAN) {
208135c4bbdfSmrg            desc = " (D)";
208235c4bbdfSmrg        }
208335c4bbdfSmrg        if (p->VScan > 1) {
208435c4bbdfSmrg            desc2 = " (VScan)";
208535c4bbdfSmrg        }
208635c4bbdfSmrg        if (p->type & M_T_BUILTIN)
208735c4bbdfSmrg            prefix = "Built-in mode";
208835c4bbdfSmrg        else if (p->type & M_T_DEFAULT)
208935c4bbdfSmrg            prefix = "Default mode";
209035c4bbdfSmrg        else if (p->type & M_T_DRIVER)
209135c4bbdfSmrg            prefix = "Driver mode";
209235c4bbdfSmrg        else
209335c4bbdfSmrg            prefix = "Mode";
209435c4bbdfSmrg        if (p->type & M_T_USERDEF)
209535c4bbdfSmrg            uprefix = "*";
209635c4bbdfSmrg        else
209735c4bbdfSmrg            uprefix = " ";
209835c4bbdfSmrg        if (hsync == 0 || refresh == 0) {
209935c4bbdfSmrg            if (p->name)
210035c4bbdfSmrg                xf86DrvMsg(scrp->scrnIndex, X_CONFIG,
210135c4bbdfSmrg                           "%s%s \"%s\"\n", uprefix, prefix, p->name);
210235c4bbdfSmrg            else
210335c4bbdfSmrg                xf86DrvMsg(scrp->scrnIndex, X_PROBED,
210435c4bbdfSmrg                           "%s%s %dx%d (unnamed)\n",
210535c4bbdfSmrg                           uprefix, prefix, p->HDisplay, p->VDisplay);
210635c4bbdfSmrg        }
210735c4bbdfSmrg        else if (p->Clock == p->SynthClock) {
210835c4bbdfSmrg            xf86DrvMsg(scrp->scrnIndex, X_CONFIG,
210935c4bbdfSmrg                       "%s%s \"%s\": %.1f MHz, %.1f kHz, %.1f Hz%s%s\n",
211035c4bbdfSmrg                       uprefix, prefix, p->name, p->Clock / 1000.0,
211135c4bbdfSmrg                       hsync, refresh, desc, desc2);
211235c4bbdfSmrg        }
211335c4bbdfSmrg        else {
211435c4bbdfSmrg            xf86DrvMsg(scrp->scrnIndex, X_CONFIG,
211535c4bbdfSmrg                       "%s%s \"%s\": %.1f MHz (scaled from %.1f MHz), "
211635c4bbdfSmrg                       "%.1f kHz, %.1f Hz%s%s\n",
211735c4bbdfSmrg                       uprefix, prefix, p->name, p->Clock / 1000.0,
211835c4bbdfSmrg                       p->SynthClock / 1000.0, hsync, refresh, desc, desc2);
211935c4bbdfSmrg        }
212035c4bbdfSmrg        if (hsync != 0 && refresh != 0)
212135c4bbdfSmrg            xf86PrintModeline(scrp->scrnIndex, p);
212235c4bbdfSmrg        p = p->next;
212305b261ecSmrg    } while (p != NULL && p != scrp->modes);
212405b261ecSmrg}
2125