xf86Modes.c 22 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
/*
 * Copyright (c) 1997-2003 by The XFree86 Project, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * Except as contained in this notice, the name of the copyright holder(s)
 * and author(s) shall not be used in advertising or otherwise to promote
 * the sale, use or other dealings in this Software without prior written
 * authorization from the copyright holder(s) and author(s).
 */

#ifdef HAVE_XORG_CONFIG_H
#include <xorg-config.h>
#endif

#include "xf86Modes.h"
#include "xf86Priv.h"

extern XF86ConfigPtr xf86configptr;

/**
 * Calculates the horizontal sync rate of a mode.
 */
40
double
41
xf86ModeHSync(const DisplayModeRec * mode)
42 43
{
    double hsync = 0.0;
44

45
    if (mode->HSync > 0.0)
46
        hsync = mode->HSync;
47
    else if (mode->HTotal > 0)
48
        hsync = (float) mode->Clock / (float) mode->HTotal;
49 50 51 52 53 54 55

    return hsync;
}

/**
 * Calculates the vertical refresh rate of a mode.
 */
56
double
57
xf86ModeVRefresh(const DisplayModeRec * mode)
58 59 60 61
{
    double refresh = 0.0;

    if (mode->VRefresh > 0.0)
62
        refresh = mode->VRefresh;
63
    else if (mode->HTotal > 0 && mode->VTotal > 0) {
64 65 66 67 68 69 70
        refresh = mode->Clock * 1000.0 / mode->HTotal / mode->VTotal;
        if (mode->Flags & V_INTERLACE)
            refresh *= 2.0;
        if (mode->Flags & V_DBLSCAN)
            refresh /= 2.0;
        if (mode->VScan > 1)
            refresh /= (float) (mode->VScan);
71 72 73 74
    }
    return refresh;
}

75
int
76
xf86ModeWidth(const DisplayModeRec * mode, Rotation rotation)
77 78 79 80
{
    switch (rotation & 0xf) {
    case RR_Rotate_0:
    case RR_Rotate_180:
81
        return mode->HDisplay;
82 83
    case RR_Rotate_90:
    case RR_Rotate_270:
84
        return mode->VDisplay;
85
    default:
86
        return 0;
87 88 89
    }
}

90
int
91
xf86ModeHeight(const DisplayModeRec * mode, Rotation rotation)
92 93 94 95
{
    switch (rotation & 0xf) {
    case RR_Rotate_0:
    case RR_Rotate_180:
96
        return mode->VDisplay;
97 98
    case RR_Rotate_90:
    case RR_Rotate_270:
99
        return mode->HDisplay;
100
    default:
101
        return 0;
102 103 104
    }
}

105
/** Calculates the memory bandwidth (in MiB/sec) of a mode. */
106
unsigned int
107 108 109
xf86ModeBandwidth(DisplayModePtr mode, int depth)
{
    float a_active, a_total, active_percent, pixels_per_second;
110
    int bytes_per_pixel = bits_to_bytes(depth);
111 112

    if (!mode->HTotal || !mode->VTotal || !mode->Clock)
113
        return 0;
114 115 116 117 118 119

    a_active = mode->HDisplay * mode->VDisplay;
    a_total = mode->HTotal * mode->VTotal;
    active_percent = a_active / a_total;
    pixels_per_second = active_percent * mode->Clock * 1000.0;

120
    return (unsigned int) (pixels_per_second * bytes_per_pixel / (1024 * 1024));
121 122
}

123
/** Sets a default mode name of <width>x<height> on a mode. */
124
void
125 126
xf86SetModeDefaultName(DisplayModePtr mode)
{
127
    Bool interlaced = ! !(mode->Flags & V_INTERLACE);
Keith Packard's avatar
Keith Packard committed
128
    char *tmp;
129

Keith Packard's avatar
Keith Packard committed
130
    free((void *) mode->name);
131

Keith Packard's avatar
Keith Packard committed
132
    XNFasprintf(&tmp, "%dx%d%s", mode->HDisplay, mode->VDisplay,
133
                interlaced ? "i" : "");
Keith Packard's avatar
Keith Packard committed
134
    mode->name = tmp;
135 136 137 138 139 140 141 142
}

/*
 * xf86SetModeCrtc
 *
 * Initialises the Crtc parameters for a mode.  The initialisation includes
 * adjustments for interlaced and double scan modes.
 */
143
void
144 145 146
xf86SetModeCrtc(DisplayModePtr p, int adjustFlags)
{
    if ((p == NULL) || ((p->type & M_T_CRTC_C) == M_T_BUILTIN))
147 148 149 150 151 152 153 154 155 156 157
        return;

    p->CrtcHDisplay = p->HDisplay;
    p->CrtcHSyncStart = p->HSyncStart;
    p->CrtcHSyncEnd = p->HSyncEnd;
    p->CrtcHTotal = p->HTotal;
    p->CrtcHSkew = p->HSkew;
    p->CrtcVDisplay = p->VDisplay;
    p->CrtcVSyncStart = p->VSyncStart;
    p->CrtcVSyncEnd = p->VSyncEnd;
    p->CrtcVTotal = p->VTotal;
158
    if (p->Flags & V_INTERLACE) {
159 160 161 162 163 164 165 166 167
        if (adjustFlags & INTERLACE_HALVE_V) {
            p->CrtcVDisplay /= 2;
            p->CrtcVSyncStart /= 2;
            p->CrtcVSyncEnd /= 2;
            p->CrtcVTotal /= 2;
        }
        /* Force interlaced modes to have an odd VTotal */
        /* maybe we should only do this when INTERLACE_HALVE_V is set? */
        p->CrtcVTotal |= 1;
168 169 170
    }

    if (p->Flags & V_DBLSCAN) {
171 172 173 174
        p->CrtcVDisplay *= 2;
        p->CrtcVSyncStart *= 2;
        p->CrtcVSyncEnd *= 2;
        p->CrtcVTotal *= 2;
175 176
    }
    if (p->VScan > 1) {
177 178 179 180
        p->CrtcVDisplay *= p->VScan;
        p->CrtcVSyncStart *= p->VScan;
        p->CrtcVSyncEnd *= p->VScan;
        p->CrtcVTotal *= p->VScan;
181 182 183 184 185 186 187 188 189 190
    }
    p->CrtcVBlankStart = min(p->CrtcVSyncStart, p->CrtcVDisplay);
    p->CrtcVBlankEnd = max(p->CrtcVSyncEnd, p->CrtcVTotal);
    p->CrtcHBlankStart = min(p->CrtcHSyncStart, p->CrtcHDisplay);
    p->CrtcHBlankEnd = max(p->CrtcHSyncEnd, p->CrtcHTotal);

    p->CrtcHAdjusted = FALSE;
    p->CrtcVAdjusted = FALSE;
}

191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
/**
 * Fills in a copy of mode, removing all stale pointer references.
 * xf86ModesEqual will return true when comparing with original mode.
 */
void
xf86SaveModeContents(DisplayModePtr intern, const DisplayModeRec *mode)
{
    *intern = *mode;
    intern->prev = intern->next = NULL;
    intern->name = NULL;
    intern->PrivSize = 0;
    intern->PrivFlags = 0;
    intern->Private = NULL;
}

206 207 208
/**
 * Allocates and returns a copy of pMode, including pointers within pMode.
 */
209
DisplayModePtr
210
xf86DuplicateMode(const DisplayModeRec * pMode)
211 212 213 214 215 216 217
{
    DisplayModePtr pNew;

    pNew = xnfalloc(sizeof(DisplayModeRec));
    *pNew = *pMode;
    pNew->next = NULL;
    pNew->prev = NULL;
218

219
    if (pMode->name == NULL)
220
        xf86SetModeDefaultName(pNew);
221
    else
222
        pNew->name = xnfstrdup(pMode->name);
223 224 225 226 227 228 229 230 231 232

    return pNew;
}

/**
 * Duplicates every mode in the given list and returns a pointer to the first
 * mode.
 *
 * \param modeList doubly-linked mode list
 */
233
DisplayModePtr
234 235 236 237 238 239
xf86DuplicateModes(ScrnInfoPtr pScrn, DisplayModePtr modeList)
{
    DisplayModePtr first = NULL, last = NULL;
    DisplayModePtr mode;

    for (mode = modeList; mode != NULL; mode = mode->next) {
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
        DisplayModePtr new;

        new = xf86DuplicateMode(mode);

        /* Insert pNew into modeList */
        if (last) {
            last->next = new;
            new->prev = last;
        }
        else {
            first = new;
            new->prev = NULL;
        }
        new->next = NULL;
        last = new;
255 256 257 258 259 260 261 262 263 264 265
    }

    return first;
}

/**
 * Returns true if the given modes should program to the same timings.
 *
 * This doesn't use Crtc values, as it might be used on ModeRecs without the
 * Crtc values set.  So, it's assumed that the other numbers are enough.
 */
266
Bool
267
xf86ModesEqual(const DisplayModeRec * pMode1, const DisplayModeRec * pMode2)
268
{
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
    if (pMode1->Clock == pMode2->Clock &&
        pMode1->HDisplay == pMode2->HDisplay &&
        pMode1->HSyncStart == pMode2->HSyncStart &&
        pMode1->HSyncEnd == pMode2->HSyncEnd &&
        pMode1->HTotal == pMode2->HTotal &&
        pMode1->HSkew == pMode2->HSkew &&
        pMode1->VDisplay == pMode2->VDisplay &&
        pMode1->VSyncStart == pMode2->VSyncStart &&
        pMode1->VSyncEnd == pMode2->VSyncEnd &&
        pMode1->VTotal == pMode2->VTotal &&
        pMode1->VScan == pMode2->VScan && pMode1->Flags == pMode2->Flags) {
        return TRUE;
    }
    else {
        return FALSE;
    }
285 286 287
}

static void
288
add(char **p, const char *new)
289 290 291 292 293 294 295 296
{
    *p = xnfrealloc(*p, strlen(*p) + strlen(new) + 2);
    strcat(*p, " ");
    strcat(*p, new);
}

/**
 * Print out a modeline.
vdb@picaros.org's avatar
vdb@picaros.org committed
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
 *
 * The mode type bits are informational except for the capitalized U
 * and P bits which give sort order priority.  Letter map:
 *
 * USERPREF, U, user preferred is set from the xorg.conf Monitor
 * Option "PreferredMode" or from the Screen Display Modes statement.
 * This unique modeline is moved to the head of the list after sorting.
 *
 * DRIVER, e, is set by the video driver, EDID or flat panel native.
 *
 * USERDEF, z, a configured zoom mode Ctrl+Alt+Keypad-{Plus,Minus}.
 *
 * DEFAULT, d, a compiled-in default.
 *
 * PREFERRED, P, driver preferred is set by the video device driver,
 * e.g. the EDID detailed timing modeline.  This is a true sort
 * priority and multiple P modes form a sorted sublist at the list
 * head.
 *
 * BUILTIN, b, a hardware fixed CRTC mode.
 *
 * See modes/xf86Crtc.c: xf86ProbeOutputModes().
319
 */
320
void
vdb@picaros.org's avatar
vdb@picaros.org committed
321
xf86PrintModeline(int scrnIndex, DisplayModePtr mode)
322 323 324
{
    char tmp[256];
    char *flags = xnfcalloc(1, 1);
325

vdb@picaros.org's avatar
vdb@picaros.org committed
326
#define TBITS 6
327 328
    const char tchar[TBITS + 1] = "UezdPb";

vdb@picaros.org's avatar
vdb@picaros.org committed
329
    int tbit[TBITS] = {
330 331
        M_T_USERPREF, M_T_DRIVER, M_T_USERDEF,
        M_T_DEFAULT, M_T_PREFERRED, M_T_BUILTIN
vdb@picaros.org's avatar
vdb@picaros.org committed
332
    };
333 334
    char type[TBITS + 2];       /* +1 for leading space */

vdb@picaros.org's avatar
vdb@picaros.org committed
335 336 337 338
#undef TBITS
    int tlen = 0;

    if (mode->type) {
339
        int i;
vdb@picaros.org's avatar
vdb@picaros.org committed
340

341 342 343 344
        type[tlen++] = ' ';
        for (i = 0; tchar[i]; i++)
            if (mode->type & tbit[i])
                type[tlen++] = tchar[i];
vdb@picaros.org's avatar
vdb@picaros.org committed
345 346
    }
    type[tlen] = '\0';
347

348 349 350
    if (mode->HSkew) {
        snprintf(tmp, 256, "hskew %i", mode->HSkew);
        add(&flags, tmp);
351
    }
352 353 354
    if (mode->VScan) {
        snprintf(tmp, 256, "vscan %i", mode->VScan);
        add(&flags, tmp);
355
    }
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375
    if (mode->Flags & V_INTERLACE)
        add(&flags, "interlace");
    if (mode->Flags & V_CSYNC)
        add(&flags, "composite");
    if (mode->Flags & V_DBLSCAN)
        add(&flags, "doublescan");
    if (mode->Flags & V_BCAST)
        add(&flags, "bcast");
    if (mode->Flags & V_PHSYNC)
        add(&flags, "+hsync");
    if (mode->Flags & V_NHSYNC)
        add(&flags, "-hsync");
    if (mode->Flags & V_PVSYNC)
        add(&flags, "+vsync");
    if (mode->Flags & V_NVSYNC)
        add(&flags, "-vsync");
    if (mode->Flags & V_PCSYNC)
        add(&flags, "+csync");
    if (mode->Flags & V_NCSYNC)
        add(&flags, "-csync");
376
#if 0
377 378
    if (mode->Flags & V_CLKDIV2)
        add(&flags, "vclk/2");
379 380
#endif
    xf86DrvMsg(scrnIndex, X_INFO,
381 382 383 384 385 386
               "Modeline \"%s\"x%.01f  %6.2f  %i %i %i %i  %i %i %i %i%s"
               " (%.01f kHz%s)\n",
               mode->name, mode->VRefresh, mode->Clock / 1000.,
               mode->HDisplay, mode->HSyncStart, mode->HSyncEnd, mode->HTotal,
               mode->VDisplay, mode->VSyncStart, mode->VSyncEnd, mode->VTotal,
               flags, xf86ModeHSync(mode), type);
387
    free(flags);
388 389 390 391 392
}

/**
 * Marks as bad any modes with unsupported flags.
 *
393
 * \param modeList doubly-linked list of modes.
394 395 396 397
 * \param flags flags supported by the driver.
 *
 * \bug only V_INTERLACE and V_DBLSCAN are supported.  Is that enough?
 */
398
void
399
xf86ValidateModesFlags(ScrnInfoPtr pScrn, DisplayModePtr modeList, int flags)
400 401 402
{
    DisplayModePtr mode;

403
    if (flags == (V_INTERLACE | V_DBLSCAN))
404
        return;
405

406
    for (mode = modeList; mode != NULL; mode = mode->next) {
407 408 409 410
        if (mode->Flags & V_INTERLACE && !(flags & V_INTERLACE))
            mode->status = MODE_NO_INTERLACE;
        if (mode->Flags & V_DBLSCAN && !(flags & V_DBLSCAN))
            mode->status = MODE_NO_DBLESCAN;
411 412 413 414 415 416
    }
}

/**
 * Marks as bad any modes extending beyond the given max X, Y, or pitch.
 *
417
 * \param modeList doubly-linked list of modes.
418
 */
419
void
420
xf86ValidateModesSize(ScrnInfoPtr pScrn, DisplayModePtr modeList,
421
                      int maxX, int maxY, int maxPitch)
422 423 424
{
    DisplayModePtr mode;

425
    if (maxPitch <= 0)
426
        maxPitch = MAXINT;
427
    if (maxX <= 0)
428
        maxX = MAXINT;
429
    if (maxY <= 0)
430
        maxY = MAXINT;
431

432
    for (mode = modeList; mode != NULL; mode = mode->next) {
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453
        if ((xf86ModeWidth(mode, RR_Rotate_0) > maxPitch ||
             xf86ModeWidth(mode, RR_Rotate_0) > maxX ||
             xf86ModeHeight(mode, RR_Rotate_0) > maxY) &&
            (xf86ModeWidth(mode, RR_Rotate_90) > maxPitch ||
             xf86ModeWidth(mode, RR_Rotate_90) > maxX ||
             xf86ModeHeight(mode, RR_Rotate_90) > maxY)) {
            if (xf86ModeWidth(mode, RR_Rotate_0) > maxPitch ||
                xf86ModeWidth(mode, RR_Rotate_90) > maxPitch)
                mode->status = MODE_BAD_WIDTH;

            if (xf86ModeWidth(mode, RR_Rotate_0) > maxX ||
                xf86ModeWidth(mode, RR_Rotate_90) > maxX)
                mode->status = MODE_VIRTUAL_X;

            if (xf86ModeHeight(mode, RR_Rotate_0) > maxY ||
                xf86ModeHeight(mode, RR_Rotate_90) > maxY)
                mode->status = MODE_VIRTUAL_Y;
        }

        if (mode->next == modeList)
            break;
454 455 456 457 458 459 460
    }
}

/**
 * Marks as bad any modes that aren't supported by the given monitor's
 * hsync and vrefresh ranges.
 *
461
 * \param modeList doubly-linked list of modes.
462
 */
463
void
464
xf86ValidateModesSync(ScrnInfoPtr pScrn, DisplayModePtr modeList, MonPtr mon)
465 466 467 468
{
    DisplayModePtr mode;

    for (mode = modeList; mode != NULL; mode = mode->next) {
469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496
        Bool bad;
        int i;

        bad = TRUE;
        for (i = 0; i < mon->nHsync; i++) {
            if (xf86ModeHSync(mode) >= mon->hsync[i].lo * (1 - SYNC_TOLERANCE)
                && xf86ModeHSync(mode) <=
                mon->hsync[i].hi * (1 + SYNC_TOLERANCE)) {
                bad = FALSE;
            }
        }
        if (bad)
            mode->status = MODE_HSYNC;

        bad = TRUE;
        for (i = 0; i < mon->nVrefresh; i++) {
            if (xf86ModeVRefresh(mode) >=
                mon->vrefresh[i].lo * (1 - SYNC_TOLERANCE) &&
                xf86ModeVRefresh(mode) <=
                mon->vrefresh[i].hi * (1 + SYNC_TOLERANCE)) {
                bad = FALSE;
            }
        }
        if (bad)
            mode->status = MODE_VSYNC;

        if (mode->next == modeList)
            break;
497 498 499 500 501 502
    }
}

/**
 * Marks as bad any modes extending beyond outside of the given clock ranges.
 *
503
 * \param modeList doubly-linked list of modes.
504 505 506 507
 * \param min pointer to minimums of clock ranges
 * \param max pointer to maximums of clock ranges
 * \param n_ranges number of ranges.
 */
508
void
509
xf86ValidateModesClocks(ScrnInfoPtr pScrn, DisplayModePtr modeList,
510
                        int *min, int *max, int n_ranges)
511 512 513 514 515
{
    DisplayModePtr mode;
    int i;

    for (mode = modeList; mode != NULL; mode = mode->next) {
516 517 518 519 520 521 522 523 524 525 526
        Bool good = FALSE;

        for (i = 0; i < n_ranges; i++) {
            if (mode->Clock >= min[i] * (1 - SYNC_TOLERANCE) &&
                mode->Clock <= max[i] * (1 + SYNC_TOLERANCE)) {
                good = TRUE;
                break;
            }
        }
        if (!good)
            mode->status = MODE_CLOCK_RANGE;
527 528 529 530 531 532 533 534 535 536 537 538 539
    }
}

/**
 * If the user has specified a set of mode names to use, mark as bad any modes
 * not listed.
 *
 * The user mode names specified are prefixes to names of modes, so "1024x768"
 * will match modes named "1024x768", "1024x768x75", "1024x768-good", but
 * "1024x768x75" would only match "1024x768x75" from that list.
 *
 * MODE_BAD is used as the rejection flag, for lack of a better flag.
 *
540
 * \param modeList doubly-linked list of modes.
541
 */
542
void
543 544 545 546 547
xf86ValidateModesUserConfig(ScrnInfoPtr pScrn, DisplayModePtr modeList)
{
    DisplayModePtr mode;

    if (pScrn->display->modes[0] == NULL)
548
        return;
549 550

    for (mode = modeList; mode != NULL; mode = mode->next) {
551 552 553 554 555 556 557 558 559 560 561 562
        int i;
        Bool good = FALSE;

        for (i = 0; pScrn->display->modes[i] != NULL; i++) {
            if (strncmp(pScrn->display->modes[i], mode->name,
                        strlen(pScrn->display->modes[i])) == 0) {
                good = TRUE;
                break;
            }
        }
        if (!good)
            mode->status = MODE_BAD;
563 564 565
    }
}

566 567 568
/**
 * Marks as bad any modes exceeding the given bandwidth.
 *
569
 * \param modeList doubly-linked list of modes.
570 571 572
 * \param bandwidth bandwidth in MHz.
 * \param depth color depth.
 */
573
void
574
xf86ValidateModesBandwidth(ScrnInfoPtr pScrn, DisplayModePtr modeList,
575
                           unsigned int bandwidth, int depth)
576 577 578 579
{
    DisplayModePtr mode;

    for (mode = modeList; mode != NULL; mode = mode->next) {
580 581
        if (xf86ModeBandwidth(mode, depth) > bandwidth)
            mode->status = MODE_BANDWIDTH;
582 583 584
    }
}

585
Bool
586
xf86ModeIsReduced(const DisplayModeRec * mode)
587 588 589
{
    if ((((mode->HDisplay * 5 / 4) & ~0x07) > mode->HTotal) &&
        ((mode->HTotal - mode->HDisplay) == 160) &&
590 591 592 593
        ((mode->HSyncEnd - mode->HDisplay) == 80) &&
        ((mode->HSyncEnd - mode->HSyncStart) == 32) &&
        ((mode->VSyncStart - mode->VDisplay) == 3))
        return TRUE;
594 595 596
    return FALSE;
}

597 598 599 600 601
/**
 * Marks as bad any reduced-blanking modes.
 *
 * \param modeList doubly-linked list of modes.
 */
602
void
603 604
xf86ValidateModesReducedBlanking(ScrnInfoPtr pScrn, DisplayModePtr modeList)
{
605
    for (; modeList != NULL; modeList = modeList->next)
606 607
        if (xf86ModeIsReduced(modeList))
            modeList->status = MODE_NO_REDUCED;
608 609
}

610 611 612 613 614 615 616
/**
 * Frees any modes from the list with a status other than MODE_OK.
 *
 * \param modeList pointer to a doubly-linked or circular list of modes.
 * \param verbose determines whether the reason for mode invalidation is
 *	  printed.
 */
617
void
618 619
xf86PruneInvalidModes(ScrnInfoPtr pScrn, DisplayModePtr * modeList,
                      Bool verbose)
620 621 622 623
{
    DisplayModePtr mode;

    for (mode = *modeList; mode != NULL;) {
624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643
        DisplayModePtr next = mode->next, first = *modeList;

        if (mode->status != MODE_OK) {
            if (verbose) {
                const char *type = "";

                if (mode->type & M_T_BUILTIN)
                    type = "built-in ";
                else if (mode->type & M_T_DEFAULT)
                    type = "default ";
                xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                           "Not using %smode \"%s\" (%s)\n", type, mode->name,
                           xf86ModeStatusToString(mode->status));
            }
            xf86DeleteMode(modeList, mode);
        }

        if (next == first)
            break;
        mode = next;
644 645 646 647 648 649 650 651
    }
}

/**
 * Adds the new mode into the mode list, and returns the new list
 *
 * \param modes doubly-linked mode list.
 */
652
DisplayModePtr
653 654 655
xf86ModesAdd(DisplayModePtr modes, DisplayModePtr new)
{
    if (modes == NULL)
656
        return new;
657 658

    if (new) {
659
        DisplayModePtr mode = modes;
660

661 662
        while (mode->next)
            mode = mode->next;
663

664 665
        mode->next = new;
        new->prev = mode;
666 667 668 669 670 671 672 673 674
    }

    return modes;
}

/**
 * Build a mode list from a list of config file modes
 */
static DisplayModePtr
675
xf86GetConfigModes(XF86ConfModeLinePtr conf_mode)
676
{
677 678 679
    DisplayModePtr head = NULL, prev = NULL, mode;

    for (; conf_mode; conf_mode = (XF86ConfModeLinePtr) conf_mode->list.next) {
680
        mode = calloc(1, sizeof(DisplayModeRec));
681 682 683 684 685 686 687 688 689 690
        if (!mode)
            continue;
        mode->name = xstrdup(conf_mode->ml_identifier);
        if (!mode->name) {
            free(mode);
            continue;
        }
        mode->type = 0;
        mode->Clock = conf_mode->ml_clock;
        mode->HDisplay = conf_mode->ml_hdisplay;
691
        mode->HSyncStart = conf_mode->ml_hsyncstart;
692 693 694
        mode->HSyncEnd = conf_mode->ml_hsyncend;
        mode->HTotal = conf_mode->ml_htotal;
        mode->VDisplay = conf_mode->ml_vdisplay;
695
        mode->VSyncStart = conf_mode->ml_vsyncstart;
696 697 698 699 700
        mode->VSyncEnd = conf_mode->ml_vsyncend;
        mode->VTotal = conf_mode->ml_vtotal;
        mode->Flags = conf_mode->ml_flags;
        mode->HSkew = conf_mode->ml_hskew;
        mode->VScan = conf_mode->ml_vscan;
701 702

        mode->prev = prev;
703 704 705 706 707 708
        mode->next = NULL;
        if (prev)
            prev->next = mode;
        else
            head = mode;
        prev = mode;
709 710 711 712 713 714 715
    }
    return head;
}

/**
 * Build a mode list from a monitor configuration
 */
716
DisplayModePtr
717
xf86GetMonitorModes(ScrnInfoPtr pScrn, XF86ConfMonitorPtr conf_monitor)
718
{
719 720 721
    DisplayModePtr modes = NULL;
    XF86ConfModesLinkPtr modes_link;

722
    if (!conf_monitor)
723
        return NULL;
724 725 726 727

    /*
     * first we collect the mode lines from the UseModes directive
     */
728 729 730 731 732 733 734 735 736 737
    for (modes_link = conf_monitor->mon_modes_sect_lst;
         modes_link; modes_link = modes_link->list.next) {
        /* If this modes link hasn't been resolved, go look it up now */
        if (!modes_link->ml_modes)
            modes_link->ml_modes = xf86findModes(modes_link->ml_modes_str,
                                                 xf86configptr->conf_modes_lst);
        if (modes_link->ml_modes)
            modes = xf86ModesAdd(modes,
                                 xf86GetConfigModes(modes_link->ml_modes->
                                                    mon_modeline_lst));
738 739
    }

740 741
    return xf86ModesAdd(modes,
                        xf86GetConfigModes(conf_monitor->mon_modeline_lst));
742 743 744 745 746
}

/**
 * Build a mode list containing all of the default modes
 */
747
DisplayModePtr
748
xf86GetDefaultModes(void)
749
{
750 751 752 753 754 755 756 757
    DisplayModePtr head = NULL, mode;
    int i;

    for (i = 0; i < xf86NumDefaultModes; i++) {
        const DisplayModeRec *defMode = &xf86DefaultModes[i];

        mode = xf86DuplicateMode(defMode);
        head = xf86ModesAdd(head, mode);
758 759 760
    }
    return head;
}
761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777

/*
 * Walk a mode list and prune out duplicates.  Will preserve the preferred
 * mode of an otherwise-duplicate pair.
 *
 * Probably best to call this on lists that are all of a single class
 * (driver, default, user, etc.), otherwise, which mode gets deleted is
 * not especially well defined.
 *
 * Returns the new list.
 */

DisplayModePtr
xf86PruneDuplicateModes(DisplayModePtr modes)
{
    DisplayModePtr m, n, o;

778
 top:
779
    for (m = modes; m; m = m->next) {
780 781 782 783 784 785 786 787 788 789 790
        for (n = m->next; n; n = o) {
            o = n->next;
            if (xf86ModesEqual(m, n)) {
                if (n->type & M_T_PREFERRED) {
                    xf86DeleteMode(&modes, m);
                    goto top;
                }
                else
                    xf86DeleteMode(&modes, n);
            }
        }
791 792 793 794
    }

    return modes;
}