1/* 2 * Copyright (c) 1997-2003 by The XFree86 Project, Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 * OTHER DEALINGS IN THE SOFTWARE. 21 * 22 * Except as contained in this notice, the name of the copyright holder(s) 23 * and author(s) shall not be used in advertising or otherwise to promote 24 * the sale, use or other dealings in this Software without prior written 25 * authorization from the copyright holder(s) and author(s). 26 */ 27 28#ifdef HAVE_XORG_CONFIG_H 29#include <xorg-config.h> 30#else 31#ifdef HAVE_CONFIG_H 32#include <config.h> 33#endif 34#endif 35 36#include "xf86Modes.h" 37#include "xf86Priv.h" 38 39extern XF86ConfigPtr xf86configptr; 40 41/** 42 * Calculates the horizontal sync rate of a mode. 43 */ 44double 45xf86ModeHSync(const DisplayModeRec *mode) 46{ 47 double hsync = 0.0; 48 49 if (mode->HSync > 0.0) 50 hsync = mode->HSync; 51 else if (mode->HTotal > 0) 52 hsync = (float)mode->Clock / (float)mode->HTotal; 53 54 return hsync; 55} 56 57/** 58 * Calculates the vertical refresh rate of a mode. 59 */ 60double 61xf86ModeVRefresh(const DisplayModeRec *mode) 62{ 63 double refresh = 0.0; 64 65 if (mode->VRefresh > 0.0) 66 refresh = mode->VRefresh; 67 else if (mode->HTotal > 0 && mode->VTotal > 0) { 68 refresh = mode->Clock * 1000.0 / mode->HTotal / mode->VTotal; 69 if (mode->Flags & V_INTERLACE) 70 refresh *= 2.0; 71 if (mode->Flags & V_DBLSCAN) 72 refresh /= 2.0; 73 if (mode->VScan > 1) 74 refresh /= (float)(mode->VScan); 75 } 76 return refresh; 77} 78 79int 80xf86ModeWidth (const DisplayModeRec *mode, Rotation rotation) 81{ 82 switch (rotation & 0xf) { 83 case RR_Rotate_0: 84 case RR_Rotate_180: 85 return mode->HDisplay; 86 case RR_Rotate_90: 87 case RR_Rotate_270: 88 return mode->VDisplay; 89 default: 90 return 0; 91 } 92} 93 94int 95xf86ModeHeight (const DisplayModeRec *mode, Rotation rotation) 96{ 97 switch (rotation & 0xf) { 98 case RR_Rotate_0: 99 case RR_Rotate_180: 100 return mode->VDisplay; 101 case RR_Rotate_90: 102 case RR_Rotate_270: 103 return mode->HDisplay; 104 default: 105 return 0; 106 } 107} 108 109/** Calculates the memory bandwidth (in MiB/sec) of a mode. */ 110unsigned int 111xf86ModeBandwidth(DisplayModePtr mode, int depth) 112{ 113 float a_active, a_total, active_percent, pixels_per_second; 114 int bytes_per_pixel = bits_to_bytes(depth); 115 116 if (!mode->HTotal || !mode->VTotal || !mode->Clock) 117 return 0; 118 119 a_active = mode->HDisplay * mode->VDisplay; 120 a_total = mode->HTotal * mode->VTotal; 121 active_percent = a_active / a_total; 122 pixels_per_second = active_percent * mode->Clock * 1000.0; 123 124 return (unsigned int)(pixels_per_second * bytes_per_pixel / (1024 * 1024)); 125} 126 127/** Sets a default mode name of <width>x<height> on a mode. */ 128void 129xf86SetModeDefaultName(DisplayModePtr mode) 130{ 131 Bool interlaced = !!(mode->Flags & V_INTERLACE); 132 133 free(mode->name); 134 135 XNFasprintf(&mode->name, "%dx%d%s", mode->HDisplay, mode->VDisplay, 136 interlaced ? "i" : ""); 137} 138 139/* 140 * xf86SetModeCrtc 141 * 142 * Initialises the Crtc parameters for a mode. The initialisation includes 143 * adjustments for interlaced and double scan modes. 144 */ 145void 146xf86SetModeCrtc(DisplayModePtr p, int adjustFlags) 147{ 148 if ((p == NULL) || ((p->type & M_T_CRTC_C) == M_T_BUILTIN)) 149 return; 150 151 p->CrtcHDisplay = p->HDisplay; 152 p->CrtcHSyncStart = p->HSyncStart; 153 p->CrtcHSyncEnd = p->HSyncEnd; 154 p->CrtcHTotal = p->HTotal; 155 p->CrtcHSkew = p->HSkew; 156 p->CrtcVDisplay = p->VDisplay; 157 p->CrtcVSyncStart = p->VSyncStart; 158 p->CrtcVSyncEnd = p->VSyncEnd; 159 p->CrtcVTotal = p->VTotal; 160 if (p->Flags & V_INTERLACE) { 161 if (adjustFlags & INTERLACE_HALVE_V) { 162 p->CrtcVDisplay /= 2; 163 p->CrtcVSyncStart /= 2; 164 p->CrtcVSyncEnd /= 2; 165 p->CrtcVTotal /= 2; 166 } 167 /* Force interlaced modes to have an odd VTotal */ 168 /* maybe we should only do this when INTERLACE_HALVE_V is set? */ 169 p->CrtcVTotal |= 1; 170 } 171 172 if (p->Flags & V_DBLSCAN) { 173 p->CrtcVDisplay *= 2; 174 p->CrtcVSyncStart *= 2; 175 p->CrtcVSyncEnd *= 2; 176 p->CrtcVTotal *= 2; 177 } 178 if (p->VScan > 1) { 179 p->CrtcVDisplay *= p->VScan; 180 p->CrtcVSyncStart *= p->VScan; 181 p->CrtcVSyncEnd *= p->VScan; 182 p->CrtcVTotal *= p->VScan; 183 } 184 p->CrtcVBlankStart = min(p->CrtcVSyncStart, p->CrtcVDisplay); 185 p->CrtcVBlankEnd = max(p->CrtcVSyncEnd, p->CrtcVTotal); 186 p->CrtcHBlankStart = min(p->CrtcHSyncStart, p->CrtcHDisplay); 187 p->CrtcHBlankEnd = max(p->CrtcHSyncEnd, p->CrtcHTotal); 188 189 p->CrtcHAdjusted = FALSE; 190 p->CrtcVAdjusted = FALSE; 191} 192 193/** 194 * Allocates and returns a copy of pMode, including pointers within pMode. 195 */ 196DisplayModePtr 197xf86DuplicateMode(const DisplayModeRec *pMode) 198{ 199 DisplayModePtr pNew; 200 201 pNew = xnfalloc(sizeof(DisplayModeRec)); 202 *pNew = *pMode; 203 pNew->next = NULL; 204 pNew->prev = NULL; 205 206 if (pMode->name == NULL) 207 xf86SetModeDefaultName(pNew); 208 else 209 pNew->name = xnfstrdup(pMode->name); 210 211 return pNew; 212} 213 214/** 215 * Duplicates every mode in the given list and returns a pointer to the first 216 * mode. 217 * 218 * \param modeList doubly-linked mode list 219 */ 220DisplayModePtr 221xf86DuplicateModes(ScrnInfoPtr pScrn, DisplayModePtr modeList) 222{ 223 DisplayModePtr first = NULL, last = NULL; 224 DisplayModePtr mode; 225 226 for (mode = modeList; mode != NULL; mode = mode->next) { 227 DisplayModePtr new; 228 229 new = xf86DuplicateMode(mode); 230 231 /* Insert pNew into modeList */ 232 if (last) { 233 last->next = new; 234 new->prev = last; 235 } else { 236 first = new; 237 new->prev = NULL; 238 } 239 new->next = NULL; 240 last = new; 241 } 242 243 return first; 244} 245 246/** 247 * Returns true if the given modes should program to the same timings. 248 * 249 * This doesn't use Crtc values, as it might be used on ModeRecs without the 250 * Crtc values set. So, it's assumed that the other numbers are enough. 251 */ 252Bool 253xf86ModesEqual(const DisplayModeRec *pMode1, const DisplayModeRec *pMode2) 254{ 255 if (pMode1->Clock == pMode2->Clock && 256 pMode1->HDisplay == pMode2->HDisplay && 257 pMode1->HSyncStart == pMode2->HSyncStart && 258 pMode1->HSyncEnd == pMode2->HSyncEnd && 259 pMode1->HTotal == pMode2->HTotal && 260 pMode1->HSkew == pMode2->HSkew && 261 pMode1->VDisplay == pMode2->VDisplay && 262 pMode1->VSyncStart == pMode2->VSyncStart && 263 pMode1->VSyncEnd == pMode2->VSyncEnd && 264 pMode1->VTotal == pMode2->VTotal && 265 pMode1->VScan == pMode2->VScan && 266 pMode1->Flags == pMode2->Flags) 267 { 268 return TRUE; 269 } else { 270 return FALSE; 271 } 272} 273 274static void 275add(char **p, char *new) 276{ 277 *p = xnfrealloc(*p, strlen(*p) + strlen(new) + 2); 278 strcat(*p, " "); 279 strcat(*p, new); 280} 281 282/** 283 * Print out a modeline. 284 */ 285void 286xf86PrintModeline(int scrnIndex,DisplayModePtr mode) 287{ 288 char tmp[256]; 289 char *flags = xnfcalloc(1, 1); 290 291 if (mode->HSkew) { 292 snprintf(tmp, 256, "hskew %i", mode->HSkew); 293 add(&flags, tmp); 294 } 295 if (mode->VScan) { 296 snprintf(tmp, 256, "vscan %i", mode->VScan); 297 add(&flags, tmp); 298 } 299 if (mode->Flags & V_INTERLACE) add(&flags, "interlace"); 300 if (mode->Flags & V_CSYNC) add(&flags, "composite"); 301 if (mode->Flags & V_DBLSCAN) add(&flags, "doublescan"); 302 if (mode->Flags & V_BCAST) add(&flags, "bcast"); 303 if (mode->Flags & V_PHSYNC) add(&flags, "+hsync"); 304 if (mode->Flags & V_NHSYNC) add(&flags, "-hsync"); 305 if (mode->Flags & V_PVSYNC) add(&flags, "+vsync"); 306 if (mode->Flags & V_NVSYNC) add(&flags, "-vsync"); 307 if (mode->Flags & V_PCSYNC) add(&flags, "+csync"); 308 if (mode->Flags & V_NCSYNC) add(&flags, "-csync"); 309#if 0 310 if (mode->Flags & V_CLKDIV2) add(&flags, "vclk/2"); 311#endif 312 xf86DrvMsg(scrnIndex, X_INFO, 313 "Modeline \"%s\"x%.01f %6.2f %i %i %i %i %i %i %i %i%s " 314 "(%.01f kHz)\n", 315 mode->name, mode->VRefresh, mode->Clock/1000., mode->HDisplay, 316 mode->HSyncStart, mode->HSyncEnd, mode->HTotal, 317 mode->VDisplay, mode->VSyncStart, mode->VSyncEnd, 318 mode->VTotal, flags, xf86ModeHSync(mode)); 319 free(flags); 320} 321 322/** 323 * Marks as bad any modes with unsupported flags. 324 * 325 * \param modeList doubly-linked list of modes. 326 * \param flags flags supported by the driver. 327 * 328 * \bug only V_INTERLACE and V_DBLSCAN are supported. Is that enough? 329 */ 330void 331xf86ValidateModesFlags(ScrnInfoPtr pScrn, DisplayModePtr modeList, 332 int flags) 333{ 334 DisplayModePtr mode; 335 336 if (flags == (V_INTERLACE | V_DBLSCAN)) 337 return; 338 339 for (mode = modeList; mode != NULL; mode = mode->next) { 340 if (mode->Flags & V_INTERLACE && !(flags & V_INTERLACE)) 341 mode->status = MODE_NO_INTERLACE; 342 if (mode->Flags & V_DBLSCAN && !(flags & V_DBLSCAN)) 343 mode->status = MODE_NO_DBLESCAN; 344 } 345} 346 347/** 348 * Marks as bad any modes extending beyond the given max X, Y, or pitch. 349 * 350 * \param modeList doubly-linked list of modes. 351 */ 352void 353xf86ValidateModesSize(ScrnInfoPtr pScrn, DisplayModePtr modeList, 354 int maxX, int maxY, int maxPitch) 355{ 356 DisplayModePtr mode; 357 358 if (maxPitch <= 0) 359 maxPitch = MAXINT; 360 if (maxX <= 0) 361 maxX = MAXINT; 362 if (maxY <= 0) 363 maxY = MAXINT; 364 365 for (mode = modeList; mode != NULL; mode = mode->next) { 366 if ((xf86ModeWidth(mode, RR_Rotate_0) > maxPitch || 367 xf86ModeWidth(mode, RR_Rotate_0) > maxX || 368 xf86ModeHeight(mode, RR_Rotate_0) > maxY) && 369 (xf86ModeWidth(mode, RR_Rotate_90) > maxPitch || 370 xf86ModeWidth(mode, RR_Rotate_90) > maxX || 371 xf86ModeHeight(mode, RR_Rotate_90) > maxY)) { 372 if (xf86ModeWidth(mode, RR_Rotate_0) > maxPitch || 373 xf86ModeWidth(mode, RR_Rotate_90) > maxPitch) 374 mode->status = MODE_BAD_WIDTH; 375 376 if (xf86ModeWidth(mode, RR_Rotate_0) > maxX || 377 xf86ModeWidth(mode, RR_Rotate_90) > maxX) 378 mode->status = MODE_VIRTUAL_X; 379 380 if (xf86ModeHeight(mode, RR_Rotate_0) > maxY || 381 xf86ModeHeight(mode, RR_Rotate_90) > maxY) 382 mode->status = MODE_VIRTUAL_Y; 383 } 384 385 if (mode->next == modeList) 386 break; 387 } 388} 389 390/** 391 * Marks as bad any modes that aren't supported by the given monitor's 392 * hsync and vrefresh ranges. 393 * 394 * \param modeList doubly-linked list of modes. 395 */ 396void 397xf86ValidateModesSync(ScrnInfoPtr pScrn, DisplayModePtr modeList, 398 MonPtr mon) 399{ 400 DisplayModePtr mode; 401 402 for (mode = modeList; mode != NULL; mode = mode->next) { 403 Bool bad; 404 int i; 405 406 bad = TRUE; 407 for (i = 0; i < mon->nHsync; i++) { 408 if (xf86ModeHSync(mode) >= mon->hsync[i].lo * (1-SYNC_TOLERANCE) && 409 xf86ModeHSync(mode) <= mon->hsync[i].hi * (1+SYNC_TOLERANCE)) 410 { 411 bad = FALSE; 412 } 413 } 414 if (bad) 415 mode->status = MODE_HSYNC; 416 417 bad = TRUE; 418 for (i = 0; i < mon->nVrefresh; i++) { 419 if (xf86ModeVRefresh(mode) >= mon->vrefresh[i].lo * (1-SYNC_TOLERANCE) && 420 xf86ModeVRefresh(mode) <= mon->vrefresh[i].hi * (1+SYNC_TOLERANCE)) 421 { 422 bad = FALSE; 423 } 424 } 425 if (bad) 426 mode->status = MODE_VSYNC; 427 428 if (mode->next == modeList) 429 break; 430 } 431} 432 433/** 434 * Marks as bad any modes extending beyond outside of the given clock ranges. 435 * 436 * \param modeList doubly-linked list of modes. 437 * \param min pointer to minimums of clock ranges 438 * \param max pointer to maximums of clock ranges 439 * \param n_ranges number of ranges. 440 */ 441void 442xf86ValidateModesClocks(ScrnInfoPtr pScrn, DisplayModePtr modeList, 443 int *min, int *max, int n_ranges) 444{ 445 DisplayModePtr mode; 446 int i; 447 448 for (mode = modeList; mode != NULL; mode = mode->next) { 449 Bool good = FALSE; 450 for (i = 0; i < n_ranges; i++) { 451 if (mode->Clock >= min[i] * (1-SYNC_TOLERANCE) && 452 mode->Clock <= max[i] * (1+SYNC_TOLERANCE)) { 453 good = TRUE; 454 break; 455 } 456 } 457 if (!good) 458 mode->status = MODE_CLOCK_RANGE; 459 } 460} 461 462/** 463 * If the user has specified a set of mode names to use, mark as bad any modes 464 * not listed. 465 * 466 * The user mode names specified are prefixes to names of modes, so "1024x768" 467 * will match modes named "1024x768", "1024x768x75", "1024x768-good", but 468 * "1024x768x75" would only match "1024x768x75" from that list. 469 * 470 * MODE_BAD is used as the rejection flag, for lack of a better flag. 471 * 472 * \param modeList doubly-linked list of modes. 473 */ 474void 475xf86ValidateModesUserConfig(ScrnInfoPtr pScrn, DisplayModePtr modeList) 476{ 477 DisplayModePtr mode; 478 479 if (pScrn->display->modes[0] == NULL) 480 return; 481 482 for (mode = modeList; mode != NULL; mode = mode->next) { 483 int i; 484 Bool good = FALSE; 485 486 for (i = 0; pScrn->display->modes[i] != NULL; i++) { 487 if (strncmp(pScrn->display->modes[i], mode->name, 488 strlen(pScrn->display->modes[i])) == 0) { 489 good = TRUE; 490 break; 491 } 492 } 493 if (!good) 494 mode->status = MODE_BAD; 495 } 496} 497 498 499/** 500 * Marks as bad any modes exceeding the given bandwidth. 501 * 502 * \param modeList doubly-linked list of modes. 503 * \param bandwidth bandwidth in MHz. 504 * \param depth color depth. 505 */ 506void 507xf86ValidateModesBandwidth(ScrnInfoPtr pScrn, DisplayModePtr modeList, 508 unsigned int bandwidth, int depth) 509{ 510 DisplayModePtr mode; 511 512 for (mode = modeList; mode != NULL; mode = mode->next) { 513 if (xf86ModeBandwidth(mode, depth) > bandwidth) 514 mode->status = MODE_BANDWIDTH; 515 } 516} 517 518Bool 519xf86ModeIsReduced(const DisplayModeRec *mode) 520{ 521 if ((((mode->HDisplay * 5 / 4) & ~0x07) > mode->HTotal) && 522 ((mode->HTotal - mode->HDisplay) == 160) && 523 ((mode->HSyncEnd - mode->HDisplay) == 80) && 524 ((mode->HSyncEnd - mode->HSyncStart) == 32) && 525 ((mode->VSyncStart - mode->VDisplay) == 3)) 526 return TRUE; 527 return FALSE; 528} 529 530/** 531 * Marks as bad any reduced-blanking modes. 532 * 533 * \param modeList doubly-linked list of modes. 534 */ 535void 536xf86ValidateModesReducedBlanking(ScrnInfoPtr pScrn, DisplayModePtr modeList) 537{ 538 for (; modeList != NULL; modeList = modeList->next) 539 if (xf86ModeIsReduced(modeList)) 540 modeList->status = MODE_NO_REDUCED; 541} 542 543/** 544 * Frees any modes from the list with a status other than MODE_OK. 545 * 546 * \param modeList pointer to a doubly-linked or circular list of modes. 547 * \param verbose determines whether the reason for mode invalidation is 548 * printed. 549 */ 550void 551xf86PruneInvalidModes(ScrnInfoPtr pScrn, DisplayModePtr *modeList, 552 Bool verbose) 553{ 554 DisplayModePtr mode; 555 556 for (mode = *modeList; mode != NULL;) { 557 DisplayModePtr next = mode->next, first = *modeList; 558 559 if (mode->status != MODE_OK) { 560 if (verbose) { 561 char *type = ""; 562 if (mode->type & M_T_BUILTIN) 563 type = "built-in "; 564 else if (mode->type & M_T_DEFAULT) 565 type = "default "; 566 xf86DrvMsg(pScrn->scrnIndex, X_INFO, 567 "Not using %smode \"%s\" (%s)\n", type, mode->name, 568 xf86ModeStatusToString(mode->status)); 569 } 570 xf86DeleteMode(modeList, mode); 571 } 572 573 if (next == first) 574 break; 575 mode = next; 576 } 577} 578 579/** 580 * Adds the new mode into the mode list, and returns the new list 581 * 582 * \param modes doubly-linked mode list. 583 */ 584DisplayModePtr 585xf86ModesAdd(DisplayModePtr modes, DisplayModePtr new) 586{ 587 if (modes == NULL) 588 return new; 589 590 if (new) { 591 DisplayModePtr mode = modes; 592 593 while (mode->next) 594 mode = mode->next; 595 596 mode->next = new; 597 new->prev = mode; 598 } 599 600 return modes; 601} 602 603/** 604 * Build a mode list from a list of config file modes 605 */ 606static DisplayModePtr 607xf86GetConfigModes (XF86ConfModeLinePtr conf_mode) 608{ 609 DisplayModePtr head = NULL, prev = NULL, mode; 610 611 for (; conf_mode; conf_mode = (XF86ConfModeLinePtr) conf_mode->list.next) 612 { 613 mode = calloc(1, sizeof(DisplayModeRec)); 614 if (!mode) 615 continue; 616 mode->name = xstrdup(conf_mode->ml_identifier); 617 if (!mode->name) 618 { 619 free(mode); 620 continue; 621 } 622 mode->type = 0; 623 mode->Clock = conf_mode->ml_clock; 624 mode->HDisplay = conf_mode->ml_hdisplay; 625 mode->HSyncStart = conf_mode->ml_hsyncstart; 626 mode->HSyncEnd = conf_mode->ml_hsyncend; 627 mode->HTotal = conf_mode->ml_htotal; 628 mode->VDisplay = conf_mode->ml_vdisplay; 629 mode->VSyncStart = conf_mode->ml_vsyncstart; 630 mode->VSyncEnd = conf_mode->ml_vsyncend; 631 mode->VTotal = conf_mode->ml_vtotal; 632 mode->Flags = conf_mode->ml_flags; 633 mode->HSkew = conf_mode->ml_hskew; 634 mode->VScan = conf_mode->ml_vscan; 635 636 mode->prev = prev; 637 mode->next = NULL; 638 if (prev) 639 prev->next = mode; 640 else 641 head = mode; 642 prev = mode; 643 } 644 return head; 645} 646 647/** 648 * Build a mode list from a monitor configuration 649 */ 650DisplayModePtr 651xf86GetMonitorModes (ScrnInfoPtr pScrn, XF86ConfMonitorPtr conf_monitor) 652{ 653 DisplayModePtr modes = NULL; 654 XF86ConfModesLinkPtr modes_link; 655 656 if (!conf_monitor) 657 return NULL; 658 659 /* 660 * first we collect the mode lines from the UseModes directive 661 */ 662 for (modes_link = conf_monitor->mon_modes_sect_lst; 663 modes_link; 664 modes_link = modes_link->list.next) 665 { 666 /* If this modes link hasn't been resolved, go look it up now */ 667 if (!modes_link->ml_modes) 668 modes_link->ml_modes = xf86findModes (modes_link->ml_modes_str, 669 xf86configptr->conf_modes_lst); 670 if (modes_link->ml_modes) 671 modes = xf86ModesAdd (modes, 672 xf86GetConfigModes (modes_link->ml_modes->mon_modeline_lst)); 673 } 674 675 return xf86ModesAdd (modes, 676 xf86GetConfigModes (conf_monitor->mon_modeline_lst)); 677} 678 679/** 680 * Build a mode list containing all of the default modes 681 */ 682DisplayModePtr 683xf86GetDefaultModes (void) 684{ 685 DisplayModePtr head = NULL, mode; 686 int i; 687 688 for (i = 0; i < xf86NumDefaultModes; i++) 689 { 690 const DisplayModeRec *defMode = &xf86DefaultModes[i]; 691 692 mode = xf86DuplicateMode(defMode); 693 head = xf86ModesAdd(head, mode); 694 } 695 return head; 696} 697 698/* 699 * Walk a mode list and prune out duplicates. Will preserve the preferred 700 * mode of an otherwise-duplicate pair. 701 * 702 * Probably best to call this on lists that are all of a single class 703 * (driver, default, user, etc.), otherwise, which mode gets deleted is 704 * not especially well defined. 705 * 706 * Returns the new list. 707 */ 708 709DisplayModePtr 710xf86PruneDuplicateModes(DisplayModePtr modes) 711{ 712 DisplayModePtr m, n, o; 713 714top: 715 for (m = modes; m; m = m->next) { 716 for (n = m->next; n; n = o) { 717 o = n->next; 718 if (xf86ModesEqual(m, n)) { 719 if (n->type & M_T_PREFERRED) { 720 xf86DeleteMode(&modes, m); 721 goto top; 722 } 723 else 724 xf86DeleteMode(&modes, n); 725 } 726 } 727 } 728 729 return modes; 730} 731