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