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