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
2805b261ecSmrg#ifdef HAVE_XORG_CONFIG_H
2905b261ecSmrg#include <xorg-config.h>
3005b261ecSmrg#endif
3105b261ecSmrg
32ed6184dfSmrg#include <libxcvt/libxcvt.h>
3305b261ecSmrg#include "xf86Modes.h"
3405b261ecSmrg#include "xf86Priv.h"
3505b261ecSmrg
3605b261ecSmrgextern XF86ConfigPtr xf86configptr;
3705b261ecSmrg
3805b261ecSmrg/**
3905b261ecSmrg * Calculates the horizontal sync rate of a mode.
4005b261ecSmrg */
416747b715Smrgdouble
4235c4bbdfSmrgxf86ModeHSync(const DisplayModeRec * mode)
4305b261ecSmrg{
4405b261ecSmrg    double hsync = 0.0;
4535c4bbdfSmrg
4605b261ecSmrg    if (mode->HSync > 0.0)
4735c4bbdfSmrg        hsync = mode->HSync;
4805b261ecSmrg    else if (mode->HTotal > 0)
4935c4bbdfSmrg        hsync = (float) mode->Clock / (float) mode->HTotal;
5005b261ecSmrg
5105b261ecSmrg    return hsync;
5205b261ecSmrg}
5305b261ecSmrg
5405b261ecSmrg/**
5505b261ecSmrg * Calculates the vertical refresh rate of a mode.
5605b261ecSmrg */
576747b715Smrgdouble
5835c4bbdfSmrgxf86ModeVRefresh(const DisplayModeRec * mode)
5905b261ecSmrg{
6005b261ecSmrg    double refresh = 0.0;
6105b261ecSmrg
6205b261ecSmrg    if (mode->VRefresh > 0.0)
6335c4bbdfSmrg        refresh = mode->VRefresh;
6405b261ecSmrg    else if (mode->HTotal > 0 && mode->VTotal > 0) {
6535c4bbdfSmrg        refresh = mode->Clock * 1000.0 / mode->HTotal / mode->VTotal;
6635c4bbdfSmrg        if (mode->Flags & V_INTERLACE)
6735c4bbdfSmrg            refresh *= 2.0;
6835c4bbdfSmrg        if (mode->Flags & V_DBLSCAN)
6935c4bbdfSmrg            refresh /= 2.0;
7035c4bbdfSmrg        if (mode->VScan > 1)
7135c4bbdfSmrg            refresh /= (float) (mode->VScan);
7205b261ecSmrg    }
7305b261ecSmrg    return refresh;
7405b261ecSmrg}
7505b261ecSmrg
766747b715Smrgint
7735c4bbdfSmrgxf86ModeWidth(const DisplayModeRec * mode, Rotation rotation)
7805b261ecSmrg{
7905b261ecSmrg    switch (rotation & 0xf) {
8005b261ecSmrg    case RR_Rotate_0:
8105b261ecSmrg    case RR_Rotate_180:
8235c4bbdfSmrg        return mode->HDisplay;
8305b261ecSmrg    case RR_Rotate_90:
8405b261ecSmrg    case RR_Rotate_270:
8535c4bbdfSmrg        return mode->VDisplay;
8605b261ecSmrg    default:
8735c4bbdfSmrg        return 0;
8805b261ecSmrg    }
8905b261ecSmrg}
9005b261ecSmrg
916747b715Smrgint
9235c4bbdfSmrgxf86ModeHeight(const DisplayModeRec * mode, Rotation rotation)
9305b261ecSmrg{
9405b261ecSmrg    switch (rotation & 0xf) {
9505b261ecSmrg    case RR_Rotate_0:
9605b261ecSmrg    case RR_Rotate_180:
9735c4bbdfSmrg        return mode->VDisplay;
9805b261ecSmrg    case RR_Rotate_90:
9905b261ecSmrg    case RR_Rotate_270:
10035c4bbdfSmrg        return mode->HDisplay;
10105b261ecSmrg    default:
10235c4bbdfSmrg        return 0;
10305b261ecSmrg    }
10405b261ecSmrg}
10505b261ecSmrg
1064642e01fSmrg/** Calculates the memory bandwidth (in MiB/sec) of a mode. */
1076747b715Smrgunsigned int
1084642e01fSmrgxf86ModeBandwidth(DisplayModePtr mode, int depth)
1094642e01fSmrg{
1104642e01fSmrg    float a_active, a_total, active_percent, pixels_per_second;
1116747b715Smrg    int bytes_per_pixel = bits_to_bytes(depth);
1124642e01fSmrg
1134642e01fSmrg    if (!mode->HTotal || !mode->VTotal || !mode->Clock)
11435c4bbdfSmrg        return 0;
1154642e01fSmrg
1164642e01fSmrg    a_active = mode->HDisplay * mode->VDisplay;
1174642e01fSmrg    a_total = mode->HTotal * mode->VTotal;
1184642e01fSmrg    active_percent = a_active / a_total;
1194642e01fSmrg    pixels_per_second = active_percent * mode->Clock * 1000.0;
1204642e01fSmrg
12135c4bbdfSmrg    return (unsigned int) (pixels_per_second * bytes_per_pixel / (1024 * 1024));
1224642e01fSmrg}
1234642e01fSmrg
12405b261ecSmrg/** Sets a default mode name of <width>x<height> on a mode. */
1256747b715Smrgvoid
12605b261ecSmrgxf86SetModeDefaultName(DisplayModePtr mode)
12705b261ecSmrg{
12835c4bbdfSmrg    Bool interlaced = ! !(mode->Flags & V_INTERLACE);
12935c4bbdfSmrg    char *tmp;
13005b261ecSmrg
13135c4bbdfSmrg    free((void *) mode->name);
1326747b715Smrg
13335c4bbdfSmrg    XNFasprintf(&tmp, "%dx%d%s", mode->HDisplay, mode->VDisplay,
13435c4bbdfSmrg                interlaced ? "i" : "");
13535c4bbdfSmrg    mode->name = tmp;
13605b261ecSmrg}
13705b261ecSmrg
13805b261ecSmrg/*
13905b261ecSmrg * xf86SetModeCrtc
14005b261ecSmrg *
14105b261ecSmrg * Initialises the Crtc parameters for a mode.  The initialisation includes
14205b261ecSmrg * adjustments for interlaced and double scan modes.
14305b261ecSmrg */
1446747b715Smrgvoid
14505b261ecSmrgxf86SetModeCrtc(DisplayModePtr p, int adjustFlags)
14605b261ecSmrg{
14705b261ecSmrg    if ((p == NULL) || ((p->type & M_T_CRTC_C) == M_T_BUILTIN))
14835c4bbdfSmrg        return;
14935c4bbdfSmrg
15035c4bbdfSmrg    p->CrtcHDisplay = p->HDisplay;
15135c4bbdfSmrg    p->CrtcHSyncStart = p->HSyncStart;
15235c4bbdfSmrg    p->CrtcHSyncEnd = p->HSyncEnd;
15335c4bbdfSmrg    p->CrtcHTotal = p->HTotal;
15435c4bbdfSmrg    p->CrtcHSkew = p->HSkew;
15535c4bbdfSmrg    p->CrtcVDisplay = p->VDisplay;
15635c4bbdfSmrg    p->CrtcVSyncStart = p->VSyncStart;
15735c4bbdfSmrg    p->CrtcVSyncEnd = p->VSyncEnd;
15835c4bbdfSmrg    p->CrtcVTotal = p->VTotal;
15905b261ecSmrg    if (p->Flags & V_INTERLACE) {
16035c4bbdfSmrg        if (adjustFlags & INTERLACE_HALVE_V) {
16135c4bbdfSmrg            p->CrtcVDisplay /= 2;
16235c4bbdfSmrg            p->CrtcVSyncStart /= 2;
16335c4bbdfSmrg            p->CrtcVSyncEnd /= 2;
16435c4bbdfSmrg            p->CrtcVTotal /= 2;
16535c4bbdfSmrg        }
16635c4bbdfSmrg        /* Force interlaced modes to have an odd VTotal */
16735c4bbdfSmrg        /* maybe we should only do this when INTERLACE_HALVE_V is set? */
16835c4bbdfSmrg        p->CrtcVTotal |= 1;
16905b261ecSmrg    }
17005b261ecSmrg
17105b261ecSmrg    if (p->Flags & V_DBLSCAN) {
17235c4bbdfSmrg        p->CrtcVDisplay *= 2;
17335c4bbdfSmrg        p->CrtcVSyncStart *= 2;
17435c4bbdfSmrg        p->CrtcVSyncEnd *= 2;
17535c4bbdfSmrg        p->CrtcVTotal *= 2;
17605b261ecSmrg    }
17705b261ecSmrg    if (p->VScan > 1) {
17835c4bbdfSmrg        p->CrtcVDisplay *= p->VScan;
17935c4bbdfSmrg        p->CrtcVSyncStart *= p->VScan;
18035c4bbdfSmrg        p->CrtcVSyncEnd *= p->VScan;
18135c4bbdfSmrg        p->CrtcVTotal *= p->VScan;
18205b261ecSmrg    }
18305b261ecSmrg    p->CrtcVBlankStart = min(p->CrtcVSyncStart, p->CrtcVDisplay);
18405b261ecSmrg    p->CrtcVBlankEnd = max(p->CrtcVSyncEnd, p->CrtcVTotal);
18505b261ecSmrg    p->CrtcHBlankStart = min(p->CrtcHSyncStart, p->CrtcHDisplay);
18605b261ecSmrg    p->CrtcHBlankEnd = max(p->CrtcHSyncEnd, p->CrtcHTotal);
18705b261ecSmrg
18805b261ecSmrg    p->CrtcHAdjusted = FALSE;
18905b261ecSmrg    p->CrtcVAdjusted = FALSE;
19005b261ecSmrg}
19105b261ecSmrg
19235c4bbdfSmrg/**
19335c4bbdfSmrg * Fills in a copy of mode, removing all stale pointer references.
19435c4bbdfSmrg * xf86ModesEqual will return true when comparing with original mode.
19535c4bbdfSmrg */
19635c4bbdfSmrgvoid
19735c4bbdfSmrgxf86SaveModeContents(DisplayModePtr intern, const DisplayModeRec *mode)
19835c4bbdfSmrg{
19935c4bbdfSmrg    *intern = *mode;
20035c4bbdfSmrg    intern->prev = intern->next = NULL;
20135c4bbdfSmrg    intern->name = NULL;
20235c4bbdfSmrg    intern->PrivSize = 0;
20335c4bbdfSmrg    intern->PrivFlags = 0;
20435c4bbdfSmrg    intern->Private = NULL;
20535c4bbdfSmrg}
20635c4bbdfSmrg
20705b261ecSmrg/**
20805b261ecSmrg * Allocates and returns a copy of pMode, including pointers within pMode.
20905b261ecSmrg */
2106747b715SmrgDisplayModePtr
21135c4bbdfSmrgxf86DuplicateMode(const DisplayModeRec * pMode)
21205b261ecSmrg{
21305b261ecSmrg    DisplayModePtr pNew;
21405b261ecSmrg
21505b261ecSmrg    pNew = xnfalloc(sizeof(DisplayModeRec));
21605b261ecSmrg    *pNew = *pMode;
21705b261ecSmrg    pNew->next = NULL;
21805b261ecSmrg    pNew->prev = NULL;
2194642e01fSmrg
2204642e01fSmrg    if (pMode->name == NULL)
22135c4bbdfSmrg        xf86SetModeDefaultName(pNew);
2224642e01fSmrg    else
22335c4bbdfSmrg        pNew->name = xnfstrdup(pMode->name);
22405b261ecSmrg
22505b261ecSmrg    return pNew;
22605b261ecSmrg}
22705b261ecSmrg
22805b261ecSmrg/**
22905b261ecSmrg * Duplicates every mode in the given list and returns a pointer to the first
23005b261ecSmrg * mode.
23105b261ecSmrg *
23205b261ecSmrg * \param modeList doubly-linked mode list
23305b261ecSmrg */
2346747b715SmrgDisplayModePtr
23505b261ecSmrgxf86DuplicateModes(ScrnInfoPtr pScrn, DisplayModePtr modeList)
23605b261ecSmrg{
23705b261ecSmrg    DisplayModePtr first = NULL, last = NULL;
23805b261ecSmrg    DisplayModePtr mode;
23905b261ecSmrg
24005b261ecSmrg    for (mode = modeList; mode != NULL; mode = mode->next) {
24135c4bbdfSmrg        DisplayModePtr new;
24235c4bbdfSmrg
24335c4bbdfSmrg        new = xf86DuplicateMode(mode);
24435c4bbdfSmrg
24535c4bbdfSmrg        /* Insert pNew into modeList */
24635c4bbdfSmrg        if (last) {
24735c4bbdfSmrg            last->next = new;
24835c4bbdfSmrg            new->prev = last;
24935c4bbdfSmrg        }
25035c4bbdfSmrg        else {
25135c4bbdfSmrg            first = new;
25235c4bbdfSmrg            new->prev = NULL;
25335c4bbdfSmrg        }
25435c4bbdfSmrg        new->next = NULL;
25535c4bbdfSmrg        last = new;
25605b261ecSmrg    }
25705b261ecSmrg
25805b261ecSmrg    return first;
25905b261ecSmrg}
26005b261ecSmrg
26105b261ecSmrg/**
26205b261ecSmrg * Returns true if the given modes should program to the same timings.
26305b261ecSmrg *
26405b261ecSmrg * This doesn't use Crtc values, as it might be used on ModeRecs without the
26505b261ecSmrg * Crtc values set.  So, it's assumed that the other numbers are enough.
26605b261ecSmrg */
2676747b715SmrgBool
26835c4bbdfSmrgxf86ModesEqual(const DisplayModeRec * pMode1, const DisplayModeRec * pMode2)
26905b261ecSmrg{
27035c4bbdfSmrg    if (pMode1->Clock == pMode2->Clock &&
27135c4bbdfSmrg        pMode1->HDisplay == pMode2->HDisplay &&
27235c4bbdfSmrg        pMode1->HSyncStart == pMode2->HSyncStart &&
27335c4bbdfSmrg        pMode1->HSyncEnd == pMode2->HSyncEnd &&
27435c4bbdfSmrg        pMode1->HTotal == pMode2->HTotal &&
27535c4bbdfSmrg        pMode1->HSkew == pMode2->HSkew &&
27635c4bbdfSmrg        pMode1->VDisplay == pMode2->VDisplay &&
27735c4bbdfSmrg        pMode1->VSyncStart == pMode2->VSyncStart &&
27835c4bbdfSmrg        pMode1->VSyncEnd == pMode2->VSyncEnd &&
27935c4bbdfSmrg        pMode1->VTotal == pMode2->VTotal &&
28035c4bbdfSmrg        pMode1->VScan == pMode2->VScan && pMode1->Flags == pMode2->Flags) {
28135c4bbdfSmrg        return TRUE;
28235c4bbdfSmrg    }
28335c4bbdfSmrg    else {
28435c4bbdfSmrg        return FALSE;
28535c4bbdfSmrg    }
28605b261ecSmrg}
28705b261ecSmrg
28805b261ecSmrgstatic void
28935c4bbdfSmrgadd(char **p, const char *new)
29005b261ecSmrg{
29105b261ecSmrg    *p = xnfrealloc(*p, strlen(*p) + strlen(new) + 2);
29205b261ecSmrg    strcat(*p, " ");
29305b261ecSmrg    strcat(*p, new);
29405b261ecSmrg}
29505b261ecSmrg
29605b261ecSmrg/**
29705b261ecSmrg * Print out a modeline.
29835c4bbdfSmrg *
29935c4bbdfSmrg * The mode type bits are informational except for the capitalized U
30035c4bbdfSmrg * and P bits which give sort order priority.  Letter map:
30135c4bbdfSmrg *
30235c4bbdfSmrg * USERPREF, U, user preferred is set from the xorg.conf Monitor
30335c4bbdfSmrg * Option "PreferredMode" or from the Screen Display Modes statement.
30435c4bbdfSmrg * This unique modeline is moved to the head of the list after sorting.
30535c4bbdfSmrg *
30635c4bbdfSmrg * DRIVER, e, is set by the video driver, EDID or flat panel native.
30735c4bbdfSmrg *
30835c4bbdfSmrg * USERDEF, z, a configured zoom mode Ctrl+Alt+Keypad-{Plus,Minus}.
30935c4bbdfSmrg *
31035c4bbdfSmrg * DEFAULT, d, a compiled-in default.
31135c4bbdfSmrg *
31235c4bbdfSmrg * PREFERRED, P, driver preferred is set by the video device driver,
31335c4bbdfSmrg * e.g. the EDID detailed timing modeline.  This is a true sort
31435c4bbdfSmrg * priority and multiple P modes form a sorted sublist at the list
31535c4bbdfSmrg * head.
31635c4bbdfSmrg *
31735c4bbdfSmrg * BUILTIN, b, a hardware fixed CRTC mode.
31835c4bbdfSmrg *
31935c4bbdfSmrg * See modes/xf86Crtc.c: xf86ProbeOutputModes().
32005b261ecSmrg */
3216747b715Smrgvoid
32235c4bbdfSmrgxf86PrintModeline(int scrnIndex, DisplayModePtr mode)
32305b261ecSmrg{
32405b261ecSmrg    char tmp[256];
32505b261ecSmrg    char *flags = xnfcalloc(1, 1);
32605b261ecSmrg
32735c4bbdfSmrg#define TBITS 6
32835c4bbdfSmrg    const char tchar[TBITS + 1] = "UezdPb";
32935c4bbdfSmrg
33035c4bbdfSmrg    int tbit[TBITS] = {
33135c4bbdfSmrg        M_T_USERPREF, M_T_DRIVER, M_T_USERDEF,
33235c4bbdfSmrg        M_T_DEFAULT, M_T_PREFERRED, M_T_BUILTIN
33335c4bbdfSmrg    };
33435c4bbdfSmrg    char type[TBITS + 2];       /* +1 for leading space */
33535c4bbdfSmrg
33635c4bbdfSmrg#undef TBITS
33735c4bbdfSmrg    int tlen = 0;
33835c4bbdfSmrg
33935c4bbdfSmrg    if (mode->type) {
34035c4bbdfSmrg        int i;
34135c4bbdfSmrg
34235c4bbdfSmrg        type[tlen++] = ' ';
34335c4bbdfSmrg        for (i = 0; tchar[i]; i++)
34435c4bbdfSmrg            if (mode->type & tbit[i])
34535c4bbdfSmrg                type[tlen++] = tchar[i];
34635c4bbdfSmrg    }
34735c4bbdfSmrg    type[tlen] = '\0';
34835c4bbdfSmrg
34935c4bbdfSmrg    if (mode->HSkew) {
35035c4bbdfSmrg        snprintf(tmp, 256, "hskew %i", mode->HSkew);
35135c4bbdfSmrg        add(&flags, tmp);
35205b261ecSmrg    }
35335c4bbdfSmrg    if (mode->VScan) {
35435c4bbdfSmrg        snprintf(tmp, 256, "vscan %i", mode->VScan);
35535c4bbdfSmrg        add(&flags, tmp);
35605b261ecSmrg    }
35735c4bbdfSmrg    if (mode->Flags & V_INTERLACE)
35835c4bbdfSmrg        add(&flags, "interlace");
35935c4bbdfSmrg    if (mode->Flags & V_CSYNC)
36035c4bbdfSmrg        add(&flags, "composite");
36135c4bbdfSmrg    if (mode->Flags & V_DBLSCAN)
36235c4bbdfSmrg        add(&flags, "doublescan");
36335c4bbdfSmrg    if (mode->Flags & V_BCAST)
36435c4bbdfSmrg        add(&flags, "bcast");
36535c4bbdfSmrg    if (mode->Flags & V_PHSYNC)
36635c4bbdfSmrg        add(&flags, "+hsync");
36735c4bbdfSmrg    if (mode->Flags & V_NHSYNC)
36835c4bbdfSmrg        add(&flags, "-hsync");
36935c4bbdfSmrg    if (mode->Flags & V_PVSYNC)
37035c4bbdfSmrg        add(&flags, "+vsync");
37135c4bbdfSmrg    if (mode->Flags & V_NVSYNC)
37235c4bbdfSmrg        add(&flags, "-vsync");
37335c4bbdfSmrg    if (mode->Flags & V_PCSYNC)
37435c4bbdfSmrg        add(&flags, "+csync");
37535c4bbdfSmrg    if (mode->Flags & V_NCSYNC)
37635c4bbdfSmrg        add(&flags, "-csync");
37705b261ecSmrg#if 0
37835c4bbdfSmrg    if (mode->Flags & V_CLKDIV2)
37935c4bbdfSmrg        add(&flags, "vclk/2");
38005b261ecSmrg#endif
38105b261ecSmrg    xf86DrvMsg(scrnIndex, X_INFO,
38235c4bbdfSmrg               "Modeline \"%s\"x%.01f  %6.2f  %i %i %i %i  %i %i %i %i%s"
38335c4bbdfSmrg               " (%.01f kHz%s)\n",
38435c4bbdfSmrg               mode->name, mode->VRefresh, mode->Clock / 1000.,
38535c4bbdfSmrg               mode->HDisplay, mode->HSyncStart, mode->HSyncEnd, mode->HTotal,
38635c4bbdfSmrg               mode->VDisplay, mode->VSyncStart, mode->VSyncEnd, mode->VTotal,
38735c4bbdfSmrg               flags, xf86ModeHSync(mode), type);
3886747b715Smrg    free(flags);
38905b261ecSmrg}
39005b261ecSmrg
39105b261ecSmrg/**
39205b261ecSmrg * Marks as bad any modes with unsupported flags.
39305b261ecSmrg *
3944642e01fSmrg * \param modeList doubly-linked list of modes.
39505b261ecSmrg * \param flags flags supported by the driver.
39605b261ecSmrg *
39705b261ecSmrg * \bug only V_INTERLACE and V_DBLSCAN are supported.  Is that enough?
39805b261ecSmrg */
3996747b715Smrgvoid
40035c4bbdfSmrgxf86ValidateModesFlags(ScrnInfoPtr pScrn, DisplayModePtr modeList, int flags)
40105b261ecSmrg{
40205b261ecSmrg    DisplayModePtr mode;
40305b261ecSmrg
4046747b715Smrg    if (flags == (V_INTERLACE | V_DBLSCAN))
40535c4bbdfSmrg        return;
4066747b715Smrg
40705b261ecSmrg    for (mode = modeList; mode != NULL; mode = mode->next) {
40835c4bbdfSmrg        if (mode->Flags & V_INTERLACE && !(flags & V_INTERLACE))
40935c4bbdfSmrg            mode->status = MODE_NO_INTERLACE;
41035c4bbdfSmrg        if (mode->Flags & V_DBLSCAN && !(flags & V_DBLSCAN))
41135c4bbdfSmrg            mode->status = MODE_NO_DBLESCAN;
41205b261ecSmrg    }
41305b261ecSmrg}
41405b261ecSmrg
41505b261ecSmrg/**
41605b261ecSmrg * Marks as bad any modes extending beyond the given max X, Y, or pitch.
41705b261ecSmrg *
4184642e01fSmrg * \param modeList doubly-linked list of modes.
41905b261ecSmrg */
4206747b715Smrgvoid
42105b261ecSmrgxf86ValidateModesSize(ScrnInfoPtr pScrn, DisplayModePtr modeList,
42235c4bbdfSmrg                      int maxX, int maxY, int maxPitch)
42305b261ecSmrg{
42405b261ecSmrg    DisplayModePtr mode;
42505b261ecSmrg
4268223e2f2Smrg    if (maxPitch <= 0)
42735c4bbdfSmrg        maxPitch = MAXINT;
4288223e2f2Smrg    if (maxX <= 0)
42935c4bbdfSmrg        maxX = MAXINT;
4308223e2f2Smrg    if (maxY <= 0)
43135c4bbdfSmrg        maxY = MAXINT;
43205b261ecSmrg
4338223e2f2Smrg    for (mode = modeList; mode != NULL; mode = mode->next) {
43435c4bbdfSmrg        if ((xf86ModeWidth(mode, RR_Rotate_0) > maxPitch ||
43535c4bbdfSmrg             xf86ModeWidth(mode, RR_Rotate_0) > maxX ||
43635c4bbdfSmrg             xf86ModeHeight(mode, RR_Rotate_0) > maxY) &&
43735c4bbdfSmrg            (xf86ModeWidth(mode, RR_Rotate_90) > maxPitch ||
43835c4bbdfSmrg             xf86ModeWidth(mode, RR_Rotate_90) > maxX ||
43935c4bbdfSmrg             xf86ModeHeight(mode, RR_Rotate_90) > maxY)) {
44035c4bbdfSmrg            if (xf86ModeWidth(mode, RR_Rotate_0) > maxPitch ||
44135c4bbdfSmrg                xf86ModeWidth(mode, RR_Rotate_90) > maxPitch)
44235c4bbdfSmrg                mode->status = MODE_BAD_WIDTH;
44335c4bbdfSmrg
44435c4bbdfSmrg            if (xf86ModeWidth(mode, RR_Rotate_0) > maxX ||
44535c4bbdfSmrg                xf86ModeWidth(mode, RR_Rotate_90) > maxX)
44635c4bbdfSmrg                mode->status = MODE_VIRTUAL_X;
44735c4bbdfSmrg
44835c4bbdfSmrg            if (xf86ModeHeight(mode, RR_Rotate_0) > maxY ||
44935c4bbdfSmrg                xf86ModeHeight(mode, RR_Rotate_90) > maxY)
45035c4bbdfSmrg                mode->status = MODE_VIRTUAL_Y;
45135c4bbdfSmrg        }
45235c4bbdfSmrg
45335c4bbdfSmrg        if (mode->next == modeList)
45435c4bbdfSmrg            break;
45505b261ecSmrg    }
45605b261ecSmrg}
45705b261ecSmrg
45805b261ecSmrg/**
45905b261ecSmrg * Marks as bad any modes that aren't supported by the given monitor's
46005b261ecSmrg * hsync and vrefresh ranges.
46105b261ecSmrg *
4624642e01fSmrg * \param modeList doubly-linked list of modes.
46305b261ecSmrg */
4646747b715Smrgvoid
46535c4bbdfSmrgxf86ValidateModesSync(ScrnInfoPtr pScrn, DisplayModePtr modeList, MonPtr mon)
46605b261ecSmrg{
46705b261ecSmrg    DisplayModePtr mode;
46805b261ecSmrg
46905b261ecSmrg    for (mode = modeList; mode != NULL; mode = mode->next) {
47035c4bbdfSmrg        Bool bad;
47135c4bbdfSmrg        int i;
47235c4bbdfSmrg
47335c4bbdfSmrg        bad = TRUE;
47435c4bbdfSmrg        for (i = 0; i < mon->nHsync; i++) {
47535c4bbdfSmrg            if (xf86ModeHSync(mode) >= mon->hsync[i].lo * (1 - SYNC_TOLERANCE)
47635c4bbdfSmrg                && xf86ModeHSync(mode) <=
47735c4bbdfSmrg                mon->hsync[i].hi * (1 + SYNC_TOLERANCE)) {
47835c4bbdfSmrg                bad = FALSE;
47935c4bbdfSmrg            }
48035c4bbdfSmrg        }
48135c4bbdfSmrg        if (bad)
48235c4bbdfSmrg            mode->status = MODE_HSYNC;
48335c4bbdfSmrg
48435c4bbdfSmrg        bad = TRUE;
48535c4bbdfSmrg        for (i = 0; i < mon->nVrefresh; i++) {
48635c4bbdfSmrg            if (xf86ModeVRefresh(mode) >=
48735c4bbdfSmrg                mon->vrefresh[i].lo * (1 - SYNC_TOLERANCE) &&
48835c4bbdfSmrg                xf86ModeVRefresh(mode) <=
48935c4bbdfSmrg                mon->vrefresh[i].hi * (1 + SYNC_TOLERANCE)) {
49035c4bbdfSmrg                bad = FALSE;
49135c4bbdfSmrg            }
49235c4bbdfSmrg        }
49335c4bbdfSmrg        if (bad)
49435c4bbdfSmrg            mode->status = MODE_VSYNC;
49535c4bbdfSmrg
49635c4bbdfSmrg        if (mode->next == modeList)
49735c4bbdfSmrg            break;
49805b261ecSmrg    }
49905b261ecSmrg}
50005b261ecSmrg
50105b261ecSmrg/**
50205b261ecSmrg * Marks as bad any modes extending beyond outside of the given clock ranges.
50305b261ecSmrg *
5044642e01fSmrg * \param modeList doubly-linked list of modes.
50505b261ecSmrg * \param min pointer to minimums of clock ranges
50605b261ecSmrg * \param max pointer to maximums of clock ranges
50705b261ecSmrg * \param n_ranges number of ranges.
50805b261ecSmrg */
5096747b715Smrgvoid
51005b261ecSmrgxf86ValidateModesClocks(ScrnInfoPtr pScrn, DisplayModePtr modeList,
51135c4bbdfSmrg                        int *min, int *max, int n_ranges)
51205b261ecSmrg{
51305b261ecSmrg    DisplayModePtr mode;
51405b261ecSmrg    int i;
51505b261ecSmrg
51605b261ecSmrg    for (mode = modeList; mode != NULL; mode = mode->next) {
51735c4bbdfSmrg        Bool good = FALSE;
51835c4bbdfSmrg
51935c4bbdfSmrg        for (i = 0; i < n_ranges; i++) {
52035c4bbdfSmrg            if (mode->Clock >= min[i] * (1 - SYNC_TOLERANCE) &&
52135c4bbdfSmrg                mode->Clock <= max[i] * (1 + SYNC_TOLERANCE)) {
52235c4bbdfSmrg                good = TRUE;
52335c4bbdfSmrg                break;
52435c4bbdfSmrg            }
52535c4bbdfSmrg        }
52635c4bbdfSmrg        if (!good)
52735c4bbdfSmrg            mode->status = MODE_CLOCK_RANGE;
52805b261ecSmrg    }
52905b261ecSmrg}
53005b261ecSmrg
53105b261ecSmrg/**
53205b261ecSmrg * If the user has specified a set of mode names to use, mark as bad any modes
53305b261ecSmrg * not listed.
53405b261ecSmrg *
53505b261ecSmrg * The user mode names specified are prefixes to names of modes, so "1024x768"
53605b261ecSmrg * will match modes named "1024x768", "1024x768x75", "1024x768-good", but
53705b261ecSmrg * "1024x768x75" would only match "1024x768x75" from that list.
53805b261ecSmrg *
53905b261ecSmrg * MODE_BAD is used as the rejection flag, for lack of a better flag.
54005b261ecSmrg *
5414642e01fSmrg * \param modeList doubly-linked list of modes.
54205b261ecSmrg */
5436747b715Smrgvoid
54405b261ecSmrgxf86ValidateModesUserConfig(ScrnInfoPtr pScrn, DisplayModePtr modeList)
54505b261ecSmrg{
54605b261ecSmrg    DisplayModePtr mode;
54705b261ecSmrg
54805b261ecSmrg    if (pScrn->display->modes[0] == NULL)
54935c4bbdfSmrg        return;
55005b261ecSmrg
55105b261ecSmrg    for (mode = modeList; mode != NULL; mode = mode->next) {
55235c4bbdfSmrg        int i;
55335c4bbdfSmrg        Bool good = FALSE;
55435c4bbdfSmrg
55535c4bbdfSmrg        for (i = 0; pScrn->display->modes[i] != NULL; i++) {
55635c4bbdfSmrg            if (strncmp(pScrn->display->modes[i], mode->name,
55735c4bbdfSmrg                        strlen(pScrn->display->modes[i])) == 0) {
55835c4bbdfSmrg                good = TRUE;
55935c4bbdfSmrg                break;
56035c4bbdfSmrg            }
56135c4bbdfSmrg        }
56235c4bbdfSmrg        if (!good)
56335c4bbdfSmrg            mode->status = MODE_BAD;
56405b261ecSmrg    }
56505b261ecSmrg}
56605b261ecSmrg
5674642e01fSmrg/**
5684642e01fSmrg * Marks as bad any modes exceeding the given bandwidth.
5694642e01fSmrg *
5704642e01fSmrg * \param modeList doubly-linked list of modes.
5714642e01fSmrg * \param bandwidth bandwidth in MHz.
5724642e01fSmrg * \param depth color depth.
5734642e01fSmrg */
5746747b715Smrgvoid
5754642e01fSmrgxf86ValidateModesBandwidth(ScrnInfoPtr pScrn, DisplayModePtr modeList,
57635c4bbdfSmrg                           unsigned int bandwidth, int depth)
5774642e01fSmrg{
5784642e01fSmrg    DisplayModePtr mode;
5794642e01fSmrg
5804642e01fSmrg    for (mode = modeList; mode != NULL; mode = mode->next) {
58135c4bbdfSmrg        if (xf86ModeBandwidth(mode, depth) > bandwidth)
58235c4bbdfSmrg            mode->status = MODE_BANDWIDTH;
5834642e01fSmrg    }
5844642e01fSmrg}
5854642e01fSmrg
5864642e01fSmrgBool
58735c4bbdfSmrgxf86ModeIsReduced(const DisplayModeRec * mode)
5884642e01fSmrg{
5894642e01fSmrg    if ((((mode->HDisplay * 5 / 4) & ~0x07) > mode->HTotal) &&
5904642e01fSmrg        ((mode->HTotal - mode->HDisplay) == 160) &&
59135c4bbdfSmrg        ((mode->HSyncEnd - mode->HDisplay) == 80) &&
59235c4bbdfSmrg        ((mode->HSyncEnd - mode->HSyncStart) == 32) &&
59335c4bbdfSmrg        ((mode->VSyncStart - mode->VDisplay) == 3))
59435c4bbdfSmrg        return TRUE;
5954642e01fSmrg    return FALSE;
5964642e01fSmrg}
5974642e01fSmrg
5984642e01fSmrg/**
5994642e01fSmrg * Marks as bad any reduced-blanking modes.
6004642e01fSmrg *
6014642e01fSmrg * \param modeList doubly-linked list of modes.
6024642e01fSmrg */
6036747b715Smrgvoid
6044642e01fSmrgxf86ValidateModesReducedBlanking(ScrnInfoPtr pScrn, DisplayModePtr modeList)
6054642e01fSmrg{
6066747b715Smrg    for (; modeList != NULL; modeList = modeList->next)
60735c4bbdfSmrg        if (xf86ModeIsReduced(modeList))
60835c4bbdfSmrg            modeList->status = MODE_NO_REDUCED;
6094642e01fSmrg}
6104642e01fSmrg
61105b261ecSmrg/**
61205b261ecSmrg * Frees any modes from the list with a status other than MODE_OK.
61305b261ecSmrg *
61405b261ecSmrg * \param modeList pointer to a doubly-linked or circular list of modes.
61505b261ecSmrg * \param verbose determines whether the reason for mode invalidation is
61605b261ecSmrg *	  printed.
61705b261ecSmrg */
6186747b715Smrgvoid
61935c4bbdfSmrgxf86PruneInvalidModes(ScrnInfoPtr pScrn, DisplayModePtr * modeList,
62035c4bbdfSmrg                      Bool verbose)
62105b261ecSmrg{
62205b261ecSmrg    DisplayModePtr mode;
62305b261ecSmrg
62405b261ecSmrg    for (mode = *modeList; mode != NULL;) {
62535c4bbdfSmrg        DisplayModePtr next = mode->next, first = *modeList;
62635c4bbdfSmrg
62735c4bbdfSmrg        if (mode->status != MODE_OK) {
62835c4bbdfSmrg            if (verbose) {
62935c4bbdfSmrg                const char *type = "";
63035c4bbdfSmrg
63135c4bbdfSmrg                if (mode->type & M_T_BUILTIN)
63235c4bbdfSmrg                    type = "built-in ";
63335c4bbdfSmrg                else if (mode->type & M_T_DEFAULT)
63435c4bbdfSmrg                    type = "default ";
63535c4bbdfSmrg                xf86DrvMsg(pScrn->scrnIndex, X_INFO,
63635c4bbdfSmrg                           "Not using %smode \"%s\" (%s)\n", type, mode->name,
63735c4bbdfSmrg                           xf86ModeStatusToString(mode->status));
63835c4bbdfSmrg            }
63935c4bbdfSmrg            xf86DeleteMode(modeList, mode);
64035c4bbdfSmrg        }
64135c4bbdfSmrg
64235c4bbdfSmrg        if (next == first)
64335c4bbdfSmrg            break;
64435c4bbdfSmrg        mode = next;
64505b261ecSmrg    }
64605b261ecSmrg}
64705b261ecSmrg
64805b261ecSmrg/**
64905b261ecSmrg * Adds the new mode into the mode list, and returns the new list
65005b261ecSmrg *
65105b261ecSmrg * \param modes doubly-linked mode list.
65205b261ecSmrg */
6536747b715SmrgDisplayModePtr
65405b261ecSmrgxf86ModesAdd(DisplayModePtr modes, DisplayModePtr new)
65505b261ecSmrg{
65605b261ecSmrg    if (modes == NULL)
65735c4bbdfSmrg        return new;
65805b261ecSmrg
65905b261ecSmrg    if (new) {
66035c4bbdfSmrg        DisplayModePtr mode = modes;
66105b261ecSmrg
66235c4bbdfSmrg        while (mode->next)
66335c4bbdfSmrg            mode = mode->next;
66405b261ecSmrg
66535c4bbdfSmrg        mode->next = new;
66635c4bbdfSmrg        new->prev = mode;
66705b261ecSmrg    }
66805b261ecSmrg
66905b261ecSmrg    return modes;
67005b261ecSmrg}
67105b261ecSmrg
67205b261ecSmrg/**
67305b261ecSmrg * Build a mode list from a list of config file modes
67405b261ecSmrg */
67505b261ecSmrgstatic DisplayModePtr
67635c4bbdfSmrgxf86GetConfigModes(XF86ConfModeLinePtr conf_mode)
67705b261ecSmrg{
67835c4bbdfSmrg    DisplayModePtr head = NULL, prev = NULL, mode;
67935c4bbdfSmrg
68035c4bbdfSmrg    for (; conf_mode; conf_mode = (XF86ConfModeLinePtr) conf_mode->list.next) {
6816747b715Smrg        mode = calloc(1, sizeof(DisplayModeRec));
68235c4bbdfSmrg        if (!mode)
68335c4bbdfSmrg            continue;
68435c4bbdfSmrg        mode->name = xstrdup(conf_mode->ml_identifier);
68535c4bbdfSmrg        if (!mode->name) {
68635c4bbdfSmrg            free(mode);
68735c4bbdfSmrg            continue;
68835c4bbdfSmrg        }
68935c4bbdfSmrg        mode->type = 0;
69035c4bbdfSmrg        mode->Clock = conf_mode->ml_clock;
69135c4bbdfSmrg        mode->HDisplay = conf_mode->ml_hdisplay;
69205b261ecSmrg        mode->HSyncStart = conf_mode->ml_hsyncstart;
69335c4bbdfSmrg        mode->HSyncEnd = conf_mode->ml_hsyncend;
69435c4bbdfSmrg        mode->HTotal = conf_mode->ml_htotal;
69535c4bbdfSmrg        mode->VDisplay = conf_mode->ml_vdisplay;
69605b261ecSmrg        mode->VSyncStart = conf_mode->ml_vsyncstart;
69735c4bbdfSmrg        mode->VSyncEnd = conf_mode->ml_vsyncend;
69835c4bbdfSmrg        mode->VTotal = conf_mode->ml_vtotal;
69935c4bbdfSmrg        mode->Flags = conf_mode->ml_flags;
70035c4bbdfSmrg        mode->HSkew = conf_mode->ml_hskew;
70135c4bbdfSmrg        mode->VScan = conf_mode->ml_vscan;
70205b261ecSmrg
70305b261ecSmrg        mode->prev = prev;
70435c4bbdfSmrg        mode->next = NULL;
70535c4bbdfSmrg        if (prev)
70635c4bbdfSmrg            prev->next = mode;
70735c4bbdfSmrg        else
70835c4bbdfSmrg            head = mode;
70935c4bbdfSmrg        prev = mode;
71005b261ecSmrg    }
71105b261ecSmrg    return head;
71205b261ecSmrg}
71305b261ecSmrg
71405b261ecSmrg/**
71505b261ecSmrg * Build a mode list from a monitor configuration
71605b261ecSmrg */
7176747b715SmrgDisplayModePtr
71835c4bbdfSmrgxf86GetMonitorModes(ScrnInfoPtr pScrn, XF86ConfMonitorPtr conf_monitor)
71905b261ecSmrg{
72035c4bbdfSmrg    DisplayModePtr modes = NULL;
72135c4bbdfSmrg    XF86ConfModesLinkPtr modes_link;
72235c4bbdfSmrg
72305b261ecSmrg    if (!conf_monitor)
72435c4bbdfSmrg        return NULL;
72505b261ecSmrg
72605b261ecSmrg    /*
72705b261ecSmrg     * first we collect the mode lines from the UseModes directive
72805b261ecSmrg     */
72935c4bbdfSmrg    for (modes_link = conf_monitor->mon_modes_sect_lst;
73035c4bbdfSmrg         modes_link; modes_link = modes_link->list.next) {
73135c4bbdfSmrg        /* If this modes link hasn't been resolved, go look it up now */
73235c4bbdfSmrg        if (!modes_link->ml_modes)
73335c4bbdfSmrg            modes_link->ml_modes = xf86findModes(modes_link->ml_modes_str,
73435c4bbdfSmrg                                                 xf86configptr->conf_modes_lst);
73535c4bbdfSmrg        if (modes_link->ml_modes)
73635c4bbdfSmrg            modes = xf86ModesAdd(modes,
73735c4bbdfSmrg                                 xf86GetConfigModes(modes_link->ml_modes->
73835c4bbdfSmrg                                                    mon_modeline_lst));
73905b261ecSmrg    }
74005b261ecSmrg
74135c4bbdfSmrg    return xf86ModesAdd(modes,
74235c4bbdfSmrg                        xf86GetConfigModes(conf_monitor->mon_modeline_lst));
74305b261ecSmrg}
74405b261ecSmrg
74505b261ecSmrg/**
74605b261ecSmrg * Build a mode list containing all of the default modes
74705b261ecSmrg */
7486747b715SmrgDisplayModePtr
74935c4bbdfSmrgxf86GetDefaultModes(void)
75005b261ecSmrg{
75135c4bbdfSmrg    DisplayModePtr head = NULL, mode;
75235c4bbdfSmrg    int i;
75335c4bbdfSmrg
75435c4bbdfSmrg    for (i = 0; i < xf86NumDefaultModes; i++) {
75535c4bbdfSmrg        const DisplayModeRec *defMode = &xf86DefaultModes[i];
75635c4bbdfSmrg
75735c4bbdfSmrg        mode = xf86DuplicateMode(defMode);
75835c4bbdfSmrg        head = xf86ModesAdd(head, mode);
75905b261ecSmrg    }
76005b261ecSmrg    return head;
76105b261ecSmrg}
7626747b715Smrg
7636747b715Smrg/*
7646747b715Smrg * Walk a mode list and prune out duplicates.  Will preserve the preferred
7656747b715Smrg * mode of an otherwise-duplicate pair.
7666747b715Smrg *
7676747b715Smrg * Probably best to call this on lists that are all of a single class
7686747b715Smrg * (driver, default, user, etc.), otherwise, which mode gets deleted is
7696747b715Smrg * not especially well defined.
7706747b715Smrg *
7716747b715Smrg * Returns the new list.
7726747b715Smrg */
7736747b715Smrg
7746747b715SmrgDisplayModePtr
7756747b715Smrgxf86PruneDuplicateModes(DisplayModePtr modes)
7766747b715Smrg{
7776747b715Smrg    DisplayModePtr m, n, o;
7786747b715Smrg
77935c4bbdfSmrg top:
7806747b715Smrg    for (m = modes; m; m = m->next) {
78135c4bbdfSmrg        for (n = m->next; n; n = o) {
78235c4bbdfSmrg            o = n->next;
78335c4bbdfSmrg            if (xf86ModesEqual(m, n)) {
78435c4bbdfSmrg                if (n->type & M_T_PREFERRED) {
78535c4bbdfSmrg                    xf86DeleteMode(&modes, m);
78635c4bbdfSmrg                    goto top;
78735c4bbdfSmrg                }
78835c4bbdfSmrg                else
78935c4bbdfSmrg                    xf86DeleteMode(&modes, n);
79035c4bbdfSmrg            }
79135c4bbdfSmrg        }
7926747b715Smrg    }
7936747b715Smrg
7946747b715Smrg    return modes;
7956747b715Smrg}
796ed6184dfSmrg
797ed6184dfSmrg/*
798ed6184dfSmrg * Generate a CVT standard mode from HDisplay, VDisplay and VRefresh.
799ed6184dfSmrg */
800ed6184dfSmrgDisplayModePtr
801ed6184dfSmrgxf86CVTMode(int HDisplay, int VDisplay, float VRefresh, Bool Reduced,
802ed6184dfSmrg            Bool Interlaced)
803ed6184dfSmrg{
804ed6184dfSmrg    struct libxcvt_mode_info *libxcvt_mode_info;
805ed6184dfSmrg    DisplayModeRec *Mode = xnfcalloc(1, sizeof(DisplayModeRec));
806875c6e4fSmrg    char *tmp;
807ed6184dfSmrg
808ed6184dfSmrg    libxcvt_mode_info =
809ed6184dfSmrg        libxcvt_gen_mode_info(HDisplay, VDisplay, VRefresh, Reduced, Interlaced);
810ed6184dfSmrg
811875c6e4fSmrg    XNFasprintf(&tmp, "%dx%d", HDisplay, VDisplay);
812875c6e4fSmrg    Mode->name = tmp;
813875c6e4fSmrg
814ed6184dfSmrg    Mode->VDisplay   = libxcvt_mode_info->vdisplay;
815ed6184dfSmrg    Mode->HDisplay   = libxcvt_mode_info->hdisplay;
816ed6184dfSmrg    Mode->Clock      = libxcvt_mode_info->dot_clock;
817ed6184dfSmrg    Mode->HSyncStart = libxcvt_mode_info->hsync_start;
818ed6184dfSmrg    Mode->HSyncEnd   = libxcvt_mode_info->hsync_end;
819ed6184dfSmrg    Mode->HTotal     = libxcvt_mode_info->htotal;
820ed6184dfSmrg    Mode->VSyncStart = libxcvt_mode_info->vsync_start;
821ed6184dfSmrg    Mode->VSyncEnd   = libxcvt_mode_info->vsync_end;
822ed6184dfSmrg    Mode->VTotal     = libxcvt_mode_info->vtotal;
823ed6184dfSmrg    Mode->VRefresh   = libxcvt_mode_info->vrefresh;
824ed6184dfSmrg    Mode->Flags      = libxcvt_mode_info->mode_flags;
825bf7d8d8dSmacallan    xf86SetModeDefaultName(Mode);
826ed6184dfSmrg
827ed6184dfSmrg    free(libxcvt_mode_info);
828ed6184dfSmrg
829ed6184dfSmrg    return Mode;
830ed6184dfSmrg}
831