| 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 | |
| 39 | extern XF86ConfigPtr xf86configptr; |
| 40 | |
| 41 | /** |
| 42 | * Calculates the horizontal sync rate of a mode. |
| 43 | */ |
| 44 | double |
| 45 | xf86ModeHSync(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 | */ |
| 60 | double |
| 61 | xf86ModeVRefresh(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 | |
| 79 | int |
| 80 | xf86ModeWidth(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 | |
| 94 | int |
| 95 | xf86ModeHeight(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. */ |
| 110 | unsigned int |
| 111 | xf86ModeBandwidth(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. */ |
| 128 | void |
| 129 | xf86SetModeDefaultName(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 | */ |
| 145 | void |
| 146 | xf86SetModeCrtc(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 | * Fills in a copy of mode, removing all stale pointer references. |
| 195 | * xf86ModesEqual will return true when comparing with original mode. |
| 196 | */ |
| 197 | void |
| 198 | xf86SaveModeContents(DisplayModePtr intern, const DisplayModeRec *mode) |
| 199 | { |
| 200 | *intern = *mode; |
| 201 | intern->prev = intern->next = NULL; |
| 202 | intern->name = NULL; |
| 203 | intern->PrivSize = 0; |
| 204 | intern->PrivFlags = 0; |
| 205 | intern->Private = NULL; |
| 206 | } |
| 207 | |
| 208 | /** |
| 209 | * Allocates and returns a copy of pMode, including pointers within pMode. |
| 210 | */ |
| 211 | DisplayModePtr |
| 212 | xf86DuplicateMode(const DisplayModeRec * pMode) |
| 213 | { |
| 214 | DisplayModePtr pNew; |
| 215 | |
| 216 | pNew = xnfalloc(sizeof(DisplayModeRec)); |
| 217 | *pNew = *pMode; |
| 218 | pNew->next = NULL; |
| 219 | pNew->prev = NULL; |
| 220 | |
| 221 | if (pMode->name == NULL) |
| 222 | xf86SetModeDefaultName(pNew); |
| 223 | else |
| 224 | pNew->name = xnfstrdup(pMode->name); |
| 225 | |
| 226 | return pNew; |
| 227 | } |
| 228 | |
| 229 | /** |
| 230 | * Duplicates every mode in the given list and returns a pointer to the first |
| 231 | * mode. |
| 232 | * |
| 233 | * \param modeList doubly-linked mode list |
| 234 | */ |
| 235 | DisplayModePtr |
| 236 | xf86DuplicateModes(ScrnInfoPtr pScrn, DisplayModePtr modeList) |
| 237 | { |
| 238 | DisplayModePtr first = NULL, last = NULL; |
| 239 | DisplayModePtr mode; |
| 240 | |
| 241 | for (mode = modeList; mode != NULL; mode = mode->next) { |
| 242 | DisplayModePtr new; |
| 243 | |
| 244 | new = xf86DuplicateMode(mode); |
| 245 | |
| 246 | /* Insert pNew into modeList */ |
| 247 | if (last) { |
| 248 | last->next = new; |
| 249 | new->prev = last; |
| 250 | } |
| 251 | else { |
| 252 | first = new; |
| 253 | new->prev = NULL; |
| 254 | } |
| 255 | new->next = NULL; |
| 256 | last = new; |
| 257 | } |
| 258 | |
| 259 | return first; |
| 260 | } |
| 261 | |
| 262 | /** |
| 263 | * Returns true if the given modes should program to the same timings. |
| 264 | * |
| 265 | * This doesn't use Crtc values, as it might be used on ModeRecs without the |
| 266 | * Crtc values set. So, it's assumed that the other numbers are enough. |
| 267 | */ |
| 268 | Bool |
| 269 | xf86ModesEqual(const DisplayModeRec * pMode1, const DisplayModeRec * pMode2) |
| 270 | { |
| 271 | if (pMode1->Clock == pMode2->Clock && |
| 272 | pMode1->HDisplay == pMode2->HDisplay && |
| 273 | pMode1->HSyncStart == pMode2->HSyncStart && |
| 274 | pMode1->HSyncEnd == pMode2->HSyncEnd && |
| 275 | pMode1->HTotal == pMode2->HTotal && |
| 276 | pMode1->HSkew == pMode2->HSkew && |
| 277 | pMode1->VDisplay == pMode2->VDisplay && |
| 278 | pMode1->VSyncStart == pMode2->VSyncStart && |
| 279 | pMode1->VSyncEnd == pMode2->VSyncEnd && |
| 280 | pMode1->VTotal == pMode2->VTotal && |
| 281 | pMode1->VScan == pMode2->VScan && pMode1->Flags == pMode2->Flags) { |
| 282 | return TRUE; |
| 283 | } |
| 284 | else { |
| 285 | return FALSE; |
| 286 | } |
| 287 | } |
| 288 | |
| 289 | static void |
| 290 | add(char **p, const char *new) |
| 291 | { |
| 292 | *p = xnfrealloc(*p, strlen(*p) + strlen(new) + 2); |
| 293 | strcat(*p, " "); |
| 294 | strcat(*p, new); |
| 295 | } |
| 296 | |
| 297 | /** |
| 298 | * Print out a modeline. |
| 299 | * |
| 300 | * The mode type bits are informational except for the capitalized U |
| 301 | * and P bits which give sort order priority. Letter map: |
| 302 | * |
| 303 | * USERPREF, U, user preferred is set from the xorg.conf Monitor |
| 304 | * Option "PreferredMode" or from the Screen Display Modes statement. |
| 305 | * This unique modeline is moved to the head of the list after sorting. |
| 306 | * |
| 307 | * DRIVER, e, is set by the video driver, EDID or flat panel native. |
| 308 | * |
| 309 | * USERDEF, z, a configured zoom mode Ctrl+Alt+Keypad-{Plus,Minus}. |
| 310 | * |
| 311 | * DEFAULT, d, a compiled-in default. |
| 312 | * |
| 313 | * PREFERRED, P, driver preferred is set by the video device driver, |
| 314 | * e.g. the EDID detailed timing modeline. This is a true sort |
| 315 | * priority and multiple P modes form a sorted sublist at the list |
| 316 | * head. |
| 317 | * |
| 318 | * BUILTIN, b, a hardware fixed CRTC mode. |
| 319 | * |
| 320 | * See modes/xf86Crtc.c: xf86ProbeOutputModes(). |
| 321 | */ |
| 322 | void |
| 323 | xf86PrintModeline(int scrnIndex, DisplayModePtr mode) |
| 324 | { |
| 325 | char tmp[256]; |
| 326 | char *flags = xnfcalloc(1, 1); |
| 327 | |
| 328 | #define TBITS 6 |
| 329 | const char tchar[TBITS + 1] = "UezdPb"; |
| 330 | |
| 331 | int tbit[TBITS] = { |
| 332 | M_T_USERPREF, M_T_DRIVER, M_T_USERDEF, |
| 333 | M_T_DEFAULT, M_T_PREFERRED, M_T_BUILTIN |
| 334 | }; |
| 335 | char type[TBITS + 2]; /* +1 for leading space */ |
| 336 | |
| 337 | #undef TBITS |
| 338 | int tlen = 0; |
| 339 | |
| 340 | if (mode->type) { |
| 341 | int i; |
| 342 | |
| 343 | type[tlen++] = ' '; |
| 344 | for (i = 0; tchar[i]; i++) |
| 345 | if (mode->type & tbit[i]) |
| 346 | type[tlen++] = tchar[i]; |
| 347 | } |
| 348 | type[tlen] = '\0'; |
| 349 | |
| 350 | if (mode->HSkew) { |
| 351 | snprintf(tmp, 256, "hskew %i", mode->HSkew); |
| 352 | add(&flags, tmp); |
| 353 | } |
| 354 | if (mode->VScan) { |
| 355 | snprintf(tmp, 256, "vscan %i", mode->VScan); |
| 356 | add(&flags, tmp); |
| 357 | } |
| 358 | if (mode->Flags & V_INTERLACE) |
| 359 | add(&flags, "interlace"); |
| 360 | if (mode->Flags & V_CSYNC) |
| 361 | add(&flags, "composite"); |
| 362 | if (mode->Flags & V_DBLSCAN) |
| 363 | add(&flags, "doublescan"); |
| 364 | if (mode->Flags & V_BCAST) |
| 365 | add(&flags, "bcast"); |
| 366 | if (mode->Flags & V_PHSYNC) |
| 367 | add(&flags, "+hsync"); |
| 368 | if (mode->Flags & V_NHSYNC) |
| 369 | add(&flags, "-hsync"); |
| 370 | if (mode->Flags & V_PVSYNC) |
| 371 | add(&flags, "+vsync"); |
| 372 | if (mode->Flags & V_NVSYNC) |
| 373 | add(&flags, "-vsync"); |
| 374 | if (mode->Flags & V_PCSYNC) |
| 375 | add(&flags, "+csync"); |
| 376 | if (mode->Flags & V_NCSYNC) |
| 377 | add(&flags, "-csync"); |
| 378 | #if 0 |
| 379 | if (mode->Flags & V_CLKDIV2) |
| 380 | add(&flags, "vclk/2"); |
| 381 | #endif |
| 382 | xf86DrvMsg(scrnIndex, X_INFO, |
| 383 | "Modeline \"%s\"x%.01f %6.2f %i %i %i %i %i %i %i %i%s" |
| 384 | " (%.01f kHz%s)\n", |
| 385 | mode->name, mode->VRefresh, mode->Clock / 1000., |
| 386 | mode->HDisplay, mode->HSyncStart, mode->HSyncEnd, mode->HTotal, |
| 387 | mode->VDisplay, mode->VSyncStart, mode->VSyncEnd, mode->VTotal, |
| 388 | flags, xf86ModeHSync(mode), type); |
| 389 | free(flags); |
| 390 | } |
| 391 | |
| 392 | /** |
| 393 | * Marks as bad any modes with unsupported flags. |
| 394 | * |
| 395 | * \param modeList doubly-linked list of modes. |
| 396 | * \param flags flags supported by the driver. |
| 397 | * |
| 398 | * \bug only V_INTERLACE and V_DBLSCAN are supported. Is that enough? |
| 399 | */ |
| 400 | void |
| 401 | xf86ValidateModesFlags(ScrnInfoPtr pScrn, DisplayModePtr modeList, int flags) |
| 402 | { |
| 403 | DisplayModePtr mode; |
| 404 | |
| 405 | if (flags == (V_INTERLACE | V_DBLSCAN)) |
| 406 | return; |
| 407 | |
| 408 | for (mode = modeList; mode != NULL; mode = mode->next) { |
| 409 | if (mode->Flags & V_INTERLACE && !(flags & V_INTERLACE)) |
| 410 | mode->status = MODE_NO_INTERLACE; |
| 411 | if (mode->Flags & V_DBLSCAN && !(flags & V_DBLSCAN)) |
| 412 | mode->status = MODE_NO_DBLESCAN; |
| 413 | } |
| 414 | } |
| 415 | |
| 416 | /** |
| 417 | * Marks as bad any modes extending beyond the given max X, Y, or pitch. |
| 418 | * |
| 419 | * \param modeList doubly-linked list of modes. |
| 420 | */ |
| 421 | void |
| 422 | xf86ValidateModesSize(ScrnInfoPtr pScrn, DisplayModePtr modeList, |
| 423 | int maxX, int maxY, int maxPitch) |
| 424 | { |
| 425 | DisplayModePtr mode; |
| 426 | |
| 427 | if (maxPitch <= 0) |
| 428 | maxPitch = MAXINT; |
| 429 | if (maxX <= 0) |
| 430 | maxX = MAXINT; |
| 431 | if (maxY <= 0) |
| 432 | maxY = MAXINT; |
| 433 | |
| 434 | for (mode = modeList; mode != NULL; mode = mode->next) { |
| 435 | if ((xf86ModeWidth(mode, RR_Rotate_0) > maxPitch || |
| 436 | xf86ModeWidth(mode, RR_Rotate_0) > maxX || |
| 437 | xf86ModeHeight(mode, RR_Rotate_0) > maxY) && |
| 438 | (xf86ModeWidth(mode, RR_Rotate_90) > maxPitch || |
| 439 | xf86ModeWidth(mode, RR_Rotate_90) > maxX || |
| 440 | xf86ModeHeight(mode, RR_Rotate_90) > maxY)) { |
| 441 | if (xf86ModeWidth(mode, RR_Rotate_0) > maxPitch || |
| 442 | xf86ModeWidth(mode, RR_Rotate_90) > maxPitch) |
| 443 | mode->status = MODE_BAD_WIDTH; |
| 444 | |
| 445 | if (xf86ModeWidth(mode, RR_Rotate_0) > maxX || |
| 446 | xf86ModeWidth(mode, RR_Rotate_90) > maxX) |
| 447 | mode->status = MODE_VIRTUAL_X; |
| 448 | |
| 449 | if (xf86ModeHeight(mode, RR_Rotate_0) > maxY || |
| 450 | xf86ModeHeight(mode, RR_Rotate_90) > maxY) |
| 451 | mode->status = MODE_VIRTUAL_Y; |
| 452 | } |
| 453 | |
| 454 | if (mode->next == modeList) |
| 455 | break; |
| 456 | } |
| 457 | } |
| 458 | |
| 459 | /** |
| 460 | * Marks as bad any modes that aren't supported by the given monitor's |
| 461 | * hsync and vrefresh ranges. |
| 462 | * |
| 463 | * \param modeList doubly-linked list of modes. |
| 464 | */ |
| 465 | void |
| 466 | xf86ValidateModesSync(ScrnInfoPtr pScrn, DisplayModePtr modeList, MonPtr mon) |
| 467 | { |
| 468 | DisplayModePtr mode; |
| 469 | |
| 470 | for (mode = modeList; mode != NULL; mode = mode->next) { |
| 471 | Bool bad; |
| 472 | int i; |
| 473 | |
| 474 | bad = TRUE; |
| 475 | for (i = 0; i < mon->nHsync; i++) { |
| 476 | if (xf86ModeHSync(mode) >= mon->hsync[i].lo * (1 - SYNC_TOLERANCE) |
| 477 | && xf86ModeHSync(mode) <= |
| 478 | mon->hsync[i].hi * (1 + SYNC_TOLERANCE)) { |
| 479 | bad = FALSE; |
| 480 | } |
| 481 | } |
| 482 | if (bad) |
| 483 | mode->status = MODE_HSYNC; |
| 484 | |
| 485 | bad = TRUE; |
| 486 | for (i = 0; i < mon->nVrefresh; i++) { |
| 487 | if (xf86ModeVRefresh(mode) >= |
| 488 | mon->vrefresh[i].lo * (1 - SYNC_TOLERANCE) && |
| 489 | xf86ModeVRefresh(mode) <= |
| 490 | mon->vrefresh[i].hi * (1 + SYNC_TOLERANCE)) { |
| 491 | bad = FALSE; |
| 492 | } |
| 493 | } |
| 494 | if (bad) |
| 495 | mode->status = MODE_VSYNC; |
| 496 | |
| 497 | if (mode->next == modeList) |
| 498 | break; |
| 499 | } |
| 500 | } |
| 501 | |
| 502 | /** |
| 503 | * Marks as bad any modes extending beyond outside of the given clock ranges. |
| 504 | * |
| 505 | * \param modeList doubly-linked list of modes. |
| 506 | * \param min pointer to minimums of clock ranges |
| 507 | * \param max pointer to maximums of clock ranges |
| 508 | * \param n_ranges number of ranges. |
| 509 | */ |
| 510 | void |
| 511 | xf86ValidateModesClocks(ScrnInfoPtr pScrn, DisplayModePtr modeList, |
| 512 | int *min, int *max, int n_ranges) |
| 513 | { |
| 514 | DisplayModePtr mode; |
| 515 | int i; |
| 516 | |
| 517 | for (mode = modeList; mode != NULL; mode = mode->next) { |
| 518 | Bool good = FALSE; |
| 519 | |
| 520 | for (i = 0; i < n_ranges; i++) { |
| 521 | if (mode->Clock >= min[i] * (1 - SYNC_TOLERANCE) && |
| 522 | mode->Clock <= max[i] * (1 + SYNC_TOLERANCE)) { |
| 523 | good = TRUE; |
| 524 | break; |
| 525 | } |
| 526 | } |
| 527 | if (!good) |
| 528 | mode->status = MODE_CLOCK_RANGE; |
| 529 | } |
| 530 | } |
| 531 | |
| 532 | /** |
| 533 | * If the user has specified a set of mode names to use, mark as bad any modes |
| 534 | * not listed. |
| 535 | * |
| 536 | * The user mode names specified are prefixes to names of modes, so "1024x768" |
| 537 | * will match modes named "1024x768", "1024x768x75", "1024x768-good", but |
| 538 | * "1024x768x75" would only match "1024x768x75" from that list. |
| 539 | * |
| 540 | * MODE_BAD is used as the rejection flag, for lack of a better flag. |
| 541 | * |
| 542 | * \param modeList doubly-linked list of modes. |
| 543 | */ |
| 544 | void |
| 545 | xf86ValidateModesUserConfig(ScrnInfoPtr pScrn, DisplayModePtr modeList) |
| 546 | { |
| 547 | DisplayModePtr mode; |
| 548 | |
| 549 | if (pScrn->display->modes[0] == NULL) |
| 550 | return; |
| 551 | |
| 552 | for (mode = modeList; mode != NULL; mode = mode->next) { |
| 553 | int i; |
| 554 | Bool good = FALSE; |
| 555 | |
| 556 | for (i = 0; pScrn->display->modes[i] != NULL; i++) { |
| 557 | if (strncmp(pScrn->display->modes[i], mode->name, |
| 558 | strlen(pScrn->display->modes[i])) == 0) { |
| 559 | good = TRUE; |
| 560 | break; |
| 561 | } |
| 562 | } |
| 563 | if (!good) |
| 564 | mode->status = MODE_BAD; |
| 565 | } |
| 566 | } |
| 567 | |
| 568 | /** |
| 569 | * Marks as bad any modes exceeding the given bandwidth. |
| 570 | * |
| 571 | * \param modeList doubly-linked list of modes. |
| 572 | * \param bandwidth bandwidth in MHz. |
| 573 | * \param depth color depth. |
| 574 | */ |
| 575 | void |
| 576 | xf86ValidateModesBandwidth(ScrnInfoPtr pScrn, DisplayModePtr modeList, |
| 577 | unsigned int bandwidth, int depth) |
| 578 | { |
| 579 | DisplayModePtr mode; |
| 580 | |
| 581 | for (mode = modeList; mode != NULL; mode = mode->next) { |
| 582 | if (xf86ModeBandwidth(mode, depth) > bandwidth) |
| 583 | mode->status = MODE_BANDWIDTH; |
| 584 | } |
| 585 | } |
| 586 | |
| 587 | Bool |
| 588 | xf86ModeIsReduced(const DisplayModeRec * mode) |
| 589 | { |
| 590 | if ((((mode->HDisplay * 5 / 4) & ~0x07) > mode->HTotal) && |
| 591 | ((mode->HTotal - mode->HDisplay) == 160) && |
| 592 | ((mode->HSyncEnd - mode->HDisplay) == 80) && |
| 593 | ((mode->HSyncEnd - mode->HSyncStart) == 32) && |
| 594 | ((mode->VSyncStart - mode->VDisplay) == 3)) |
| 595 | return TRUE; |
| 596 | return FALSE; |
| 597 | } |
| 598 | |
| 599 | /** |
| 600 | * Marks as bad any reduced-blanking modes. |
| 601 | * |
| 602 | * \param modeList doubly-linked list of modes. |
| 603 | */ |
| 604 | void |
| 605 | xf86ValidateModesReducedBlanking(ScrnInfoPtr pScrn, DisplayModePtr modeList) |
| 606 | { |
| 607 | for (; modeList != NULL; modeList = modeList->next) |
| 608 | if (xf86ModeIsReduced(modeList)) |
| 609 | modeList->status = MODE_NO_REDUCED; |
| 610 | } |
| 611 | |
| 612 | /** |
| 613 | * Frees any modes from the list with a status other than MODE_OK. |
| 614 | * |
| 615 | * \param modeList pointer to a doubly-linked or circular list of modes. |
| 616 | * \param verbose determines whether the reason for mode invalidation is |
| 617 | * printed. |
| 618 | */ |
| 619 | void |
| 620 | xf86PruneInvalidModes(ScrnInfoPtr pScrn, DisplayModePtr * modeList, |
| 621 | Bool verbose) |
| 622 | { |
| 623 | DisplayModePtr mode; |
| 624 | |
| 625 | for (mode = *modeList; mode != NULL;) { |
| 626 | DisplayModePtr next = mode->next, first = *modeList; |
| 627 | |
| 628 | if (mode->status != MODE_OK) { |
| 629 | if (verbose) { |
| 630 | const char *type = ""; |
| 631 | |
| 632 | if (mode->type & M_T_BUILTIN) |
| 633 | type = "built-in "; |
| 634 | else if (mode->type & M_T_DEFAULT) |
| 635 | type = "default "; |
| 636 | xf86DrvMsg(pScrn->scrnIndex, X_INFO, |
| 637 | "Not using %smode \"%s\" (%s)\n", type, mode->name, |
| 638 | xf86ModeStatusToString(mode->status)); |
| 639 | } |
| 640 | xf86DeleteMode(modeList, mode); |
| 641 | } |
| 642 | |
| 643 | if (next == first) |
| 644 | break; |
| 645 | mode = next; |
| 646 | } |
| 647 | } |
| 648 | |
| 649 | /** |
| 650 | * Adds the new mode into the mode list, and returns the new list |
| 651 | * |
| 652 | * \param modes doubly-linked mode list. |
| 653 | */ |
| 654 | DisplayModePtr |
| 655 | xf86ModesAdd(DisplayModePtr modes, DisplayModePtr new) |
| 656 | { |
| 657 | if (modes == NULL) |
| 658 | return new; |
| 659 | |
| 660 | if (new) { |
| 661 | DisplayModePtr mode = modes; |
| 662 | |
| 663 | while (mode->next) |
| 664 | mode = mode->next; |
| 665 | |
| 666 | mode->next = new; |
| 667 | new->prev = mode; |
| 668 | } |
| 669 | |
| 670 | return modes; |
| 671 | } |
| 672 | |
| 673 | /** |
| 674 | * Build a mode list from a list of config file modes |
| 675 | */ |
| 676 | static DisplayModePtr |
| 677 | xf86GetConfigModes(XF86ConfModeLinePtr conf_mode) |
| 678 | { |
| 679 | DisplayModePtr head = NULL, prev = NULL, mode; |
| 680 | |
| 681 | for (; conf_mode; conf_mode = (XF86ConfModeLinePtr) conf_mode->list.next) { |
| 682 | mode = calloc(1, sizeof(DisplayModeRec)); |
| 683 | if (!mode) |
| 684 | continue; |
| 685 | mode->name = xstrdup(conf_mode->ml_identifier); |
| 686 | if (!mode->name) { |
| 687 | free(mode); |
| 688 | continue; |
| 689 | } |
| 690 | mode->type = 0; |
| 691 | mode->Clock = conf_mode->ml_clock; |
| 692 | mode->HDisplay = conf_mode->ml_hdisplay; |
| 693 | mode->HSyncStart = conf_mode->ml_hsyncstart; |
| 694 | mode->HSyncEnd = conf_mode->ml_hsyncend; |
| 695 | mode->HTotal = conf_mode->ml_htotal; |
| 696 | mode->VDisplay = conf_mode->ml_vdisplay; |
| 697 | mode->VSyncStart = conf_mode->ml_vsyncstart; |
| 698 | mode->VSyncEnd = conf_mode->ml_vsyncend; |
| 699 | mode->VTotal = conf_mode->ml_vtotal; |
| 700 | mode->Flags = conf_mode->ml_flags; |
| 701 | mode->HSkew = conf_mode->ml_hskew; |
| 702 | mode->VScan = conf_mode->ml_vscan; |
| 703 | |
| 704 | mode->prev = prev; |
| 705 | mode->next = NULL; |
| 706 | if (prev) |
| 707 | prev->next = mode; |
| 708 | else |
| 709 | head = mode; |
| 710 | prev = mode; |
| 711 | } |
| 712 | return head; |
| 713 | } |
| 714 | |
| 715 | /** |
| 716 | * Build a mode list from a monitor configuration |
| 717 | */ |
| 718 | DisplayModePtr |
| 719 | xf86GetMonitorModes(ScrnInfoPtr pScrn, XF86ConfMonitorPtr conf_monitor) |
| 720 | { |
| 721 | DisplayModePtr modes = NULL; |
| 722 | XF86ConfModesLinkPtr modes_link; |
| 723 | |
| 724 | if (!conf_monitor) |
| 725 | return NULL; |
| 726 | |
| 727 | /* |
| 728 | * first we collect the mode lines from the UseModes directive |
| 729 | */ |
| 730 | for (modes_link = conf_monitor->mon_modes_sect_lst; |
| 731 | modes_link; modes_link = modes_link->list.next) { |
| 732 | /* If this modes link hasn't been resolved, go look it up now */ |
| 733 | if (!modes_link->ml_modes) |
| 734 | modes_link->ml_modes = xf86findModes(modes_link->ml_modes_str, |
| 735 | xf86configptr->conf_modes_lst); |
| 736 | if (modes_link->ml_modes) |
| 737 | modes = xf86ModesAdd(modes, |
| 738 | xf86GetConfigModes(modes_link->ml_modes-> |
| 739 | mon_modeline_lst)); |
| 740 | } |
| 741 | |
| 742 | return xf86ModesAdd(modes, |
| 743 | xf86GetConfigModes(conf_monitor->mon_modeline_lst)); |
| 744 | } |
| 745 | |
| 746 | /** |
| 747 | * Build a mode list containing all of the default modes |
| 748 | */ |
| 749 | DisplayModePtr |
| 750 | xf86GetDefaultModes(void) |
| 751 | { |
| 752 | DisplayModePtr head = NULL, mode; |
| 753 | int i; |
| 754 | |
| 755 | for (i = 0; i < xf86NumDefaultModes; i++) { |
| 756 | const DisplayModeRec *defMode = &xf86DefaultModes[i]; |
| 757 | |
| 758 | mode = xf86DuplicateMode(defMode); |
| 759 | head = xf86ModesAdd(head, mode); |
| 760 | } |
| 761 | return head; |
| 762 | } |
| 763 | |
| 764 | /* |
| 765 | * Walk a mode list and prune out duplicates. Will preserve the preferred |
| 766 | * mode of an otherwise-duplicate pair. |
| 767 | * |
| 768 | * Probably best to call this on lists that are all of a single class |
| 769 | * (driver, default, user, etc.), otherwise, which mode gets deleted is |
| 770 | * not especially well defined. |
| 771 | * |
| 772 | * Returns the new list. |
| 773 | */ |
| 774 | |
| 775 | DisplayModePtr |
| 776 | xf86PruneDuplicateModes(DisplayModePtr modes) |
| 777 | { |
| 778 | DisplayModePtr m, n, o; |
| 779 | |
| 780 | top: |
| 781 | for (m = modes; m; m = m->next) { |
| 782 | for (n = m->next; n; n = o) { |
| 783 | o = n->next; |
| 784 | if (xf86ModesEqual(m, n)) { |
| 785 | if (n->type & M_T_PREFERRED) { |
| 786 | xf86DeleteMode(&modes, m); |
| 787 | goto top; |
| 788 | } |
| 789 | else |
| 790 | xf86DeleteMode(&modes, n); |
| 791 | } |
| 792 | } |
| 793 | } |
| 794 | |
| 795 | return modes; |
| 796 | } |