105b261ecSmrg/* 205b261ecSmrg * Copyright (c) 1997-2003 by The XFree86 Project, Inc. 305b261ecSmrg * 405b261ecSmrg * Permission is hereby granted, free of charge, to any person obtaining a 505b261ecSmrg * copy of this software and associated documentation files (the "Software"), 605b261ecSmrg * to deal in the Software without restriction, including without limitation 705b261ecSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 805b261ecSmrg * and/or sell copies of the Software, and to permit persons to whom the 905b261ecSmrg * Software is furnished to do so, subject to the following conditions: 1005b261ecSmrg * 1105b261ecSmrg * The above copyright notice and this permission notice shall be included in 1205b261ecSmrg * all copies or substantial portions of the Software. 1305b261ecSmrg * 1405b261ecSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1505b261ecSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1605b261ecSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1705b261ecSmrg * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 1805b261ecSmrg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 1905b261ecSmrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 2005b261ecSmrg * OTHER DEALINGS IN THE SOFTWARE. 2105b261ecSmrg * 2205b261ecSmrg * Except as contained in this notice, the name of the copyright holder(s) 2305b261ecSmrg * and author(s) shall not be used in advertising or otherwise to promote 2405b261ecSmrg * the sale, use or other dealings in this Software without prior written 2505b261ecSmrg * authorization from the copyright holder(s) and author(s). 2605b261ecSmrg */ 2705b261ecSmrg 286747b715Smrg/* 296747b715Smrg * LCM() and scanLineWidth() are: 306747b715Smrg * 316747b715Smrg * Copyright 1997 through 2004 by Marc Aurele La France (TSI @ UQV), tsi@xfree86.org 326747b715Smrg * 336747b715Smrg * Permission to use, copy, modify, distribute, and sell this software and its 346747b715Smrg * documentation for any purpose is hereby granted without fee, provided that 356747b715Smrg * the above copyright notice appear in all copies and that both that copyright 366747b715Smrg * notice and this permission notice appear in supporting documentation, and 376747b715Smrg * that the name of Marc Aurele La France not be used in advertising or 386747b715Smrg * publicity pertaining to distribution of the software without specific, 396747b715Smrg * written prior permission. Marc Aurele La France makes no representations 406747b715Smrg * about the suitability of this software for any purpose. It is provided 416747b715Smrg * "as-is" without express or implied warranty. 426747b715Smrg * 436747b715Smrg * MARC AURELE LA FRANCE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 446747b715Smrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO 456747b715Smrg * EVENT SHALL MARC AURELE LA FRANCE BE LIABLE FOR ANY SPECIAL, INDIRECT OR 466747b715Smrg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 476747b715Smrg * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 486747b715Smrg * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 496747b715Smrg * PERFORMANCE OF THIS SOFTWARE. 506747b715Smrg * 516747b715Smrg * Copyright 1990,91,92,93 by Thomas Roell, Germany. 526747b715Smrg * Copyright 1991,92,93 by SGCS (Snitily Graphics Consulting Services), USA. 536747b715Smrg * 546747b715Smrg * Permission to use, copy, modify, distribute, and sell this software 556747b715Smrg * and its documentation for any purpose is hereby granted without fee, 566747b715Smrg * provided that the above copyright notice appear in all copies and 576747b715Smrg * that both that copyright notice and this permission notice appear 586747b715Smrg * in supporting documentation, and that the name of Thomas Roell nor 596747b715Smrg * SGCS be used in advertising or publicity pertaining to distribution 606747b715Smrg * of the software without specific, written prior permission. 616747b715Smrg * Thomas Roell nor SGCS makes no representations about the suitability 626747b715Smrg * of this software for any purpose. It is provided "as is" without 636747b715Smrg * express or implied warranty. 646747b715Smrg * 656747b715Smrg * THOMAS ROELL AND SGCS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS 666747b715Smrg * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 676747b715Smrg * FITNESS, IN NO EVENT SHALL THOMAS ROELL OR SGCS BE LIABLE FOR ANY 686747b715Smrg * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER 696747b715Smrg * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF 706747b715Smrg * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 716747b715Smrg * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 726747b715Smrg */ 736747b715Smrg 7405b261ecSmrg/* 7505b261ecSmrg * Authors: Dirk Hohndel <hohndel@XFree86.Org> 7605b261ecSmrg * David Dawes <dawes@XFree86.Org> 7705b261ecSmrg * Marc La France <tsi@XFree86.Org> 7805b261ecSmrg * ... and others 7905b261ecSmrg * 8005b261ecSmrg * This file includes helper functions for mode related things. 8105b261ecSmrg */ 8205b261ecSmrg 8305b261ecSmrg#ifdef HAVE_XORG_CONFIG_H 8405b261ecSmrg#include <xorg-config.h> 8505b261ecSmrg#endif 8605b261ecSmrg 8705b261ecSmrg#include <X11/X.h> 884642e01fSmrg#include "xf86Modes.h" 891b5d61b8Smrg#include "xf86Crtc.h" 9005b261ecSmrg#include "os.h" 9105b261ecSmrg#include "servermd.h" 9205b261ecSmrg#include "globals.h" 9305b261ecSmrg#include "xf86.h" 9405b261ecSmrg#include "xf86Priv.h" 9505b261ecSmrg#include "edid.h" 9605b261ecSmrg 9705b261ecSmrgstatic void 9805b261ecSmrgprintModeRejectMessage(int index, DisplayModePtr p, int status) 9905b261ecSmrg{ 10035c4bbdfSmrg const char *type; 10105b261ecSmrg 10205b261ecSmrg if (p->type & M_T_BUILTIN) 10335c4bbdfSmrg type = "built-in "; 10405b261ecSmrg else if (p->type & M_T_DEFAULT) 10535c4bbdfSmrg type = "default "; 10605b261ecSmrg else if (p->type & M_T_DRIVER) 10735c4bbdfSmrg type = "driver "; 10805b261ecSmrg else 10935c4bbdfSmrg type = ""; 11005b261ecSmrg 11105b261ecSmrg xf86DrvMsg(index, X_INFO, "Not using %smode \"%s\" (%s)\n", type, p->name, 11235c4bbdfSmrg xf86ModeStatusToString(status)); 11305b261ecSmrg} 11405b261ecSmrg 11505b261ecSmrg/* 11635c4bbdfSmrg * Find closest clock to given frequency (in kHz). This assumes the 11735c4bbdfSmrg * number of clocks is greater than zero. 11805b261ecSmrg */ 11935c4bbdfSmrgstatic int 12005b261ecSmrgxf86GetNearestClock(ScrnInfoPtr scrp, int freq, Bool allowDiv2, 12135c4bbdfSmrg int DivFactor, int MulFactor, int *divider) 12205b261ecSmrg{ 12305b261ecSmrg int nearestClock = 0, nearestDiv = 1; 12405b261ecSmrg int minimumGap = abs(freq - scrp->clock[0]); 12505b261ecSmrg int i, j, k, gap; 12605b261ecSmrg 12705b261ecSmrg if (allowDiv2) 12835c4bbdfSmrg k = 2; 12905b261ecSmrg else 13035c4bbdfSmrg k = 1; 13105b261ecSmrg 13205b261ecSmrg /* Must set this here in case the best match is scrp->clock[0] */ 13305b261ecSmrg if (divider != NULL) 13435c4bbdfSmrg *divider = 0; 13535c4bbdfSmrg 13635c4bbdfSmrg for (i = 0; i < scrp->numClocks; i++) { 13735c4bbdfSmrg for (j = 1; j <= k; j++) { 13835c4bbdfSmrg gap = abs((freq * j) - ((scrp->clock[i] * DivFactor) / MulFactor)); 13935c4bbdfSmrg if ((gap < minimumGap) || ((gap == minimumGap) && (j < nearestDiv))) { 14035c4bbdfSmrg minimumGap = gap; 14135c4bbdfSmrg nearestClock = i; 14235c4bbdfSmrg nearestDiv = j; 14335c4bbdfSmrg if (divider != NULL) 14435c4bbdfSmrg *divider = (j - 1) * V_CLKDIV2; 14535c4bbdfSmrg } 14635c4bbdfSmrg } 14705b261ecSmrg } 14805b261ecSmrg return nearestClock; 14905b261ecSmrg} 15005b261ecSmrg 15105b261ecSmrg/* 15205b261ecSmrg * xf86ModeStatusToString 15305b261ecSmrg * 15405b261ecSmrg * Convert a ModeStatus value to a printable message 15505b261ecSmrg */ 15605b261ecSmrg 1576747b715Smrgconst char * 15805b261ecSmrgxf86ModeStatusToString(ModeStatus status) 15905b261ecSmrg{ 16005b261ecSmrg switch (status) { 16105b261ecSmrg case MODE_OK: 16235c4bbdfSmrg return "Mode OK"; 16305b261ecSmrg case MODE_HSYNC: 16435c4bbdfSmrg return "hsync out of range"; 16505b261ecSmrg case MODE_VSYNC: 16635c4bbdfSmrg return "vrefresh out of range"; 16705b261ecSmrg case MODE_H_ILLEGAL: 16835c4bbdfSmrg return "illegal horizontal timings"; 16905b261ecSmrg case MODE_V_ILLEGAL: 17035c4bbdfSmrg return "illegal vertical timings"; 17105b261ecSmrg case MODE_BAD_WIDTH: 17235c4bbdfSmrg return "width requires unsupported line pitch"; 17305b261ecSmrg case MODE_NOMODE: 17435c4bbdfSmrg return "no mode of this name"; 17505b261ecSmrg case MODE_NO_INTERLACE: 17635c4bbdfSmrg return "interlace mode not supported"; 17705b261ecSmrg case MODE_NO_DBLESCAN: 17835c4bbdfSmrg return "doublescan mode not supported"; 17905b261ecSmrg case MODE_NO_VSCAN: 18035c4bbdfSmrg return "multiscan mode not supported"; 18105b261ecSmrg case MODE_MEM: 18235c4bbdfSmrg return "insufficient memory for mode"; 18305b261ecSmrg case MODE_VIRTUAL_X: 18435c4bbdfSmrg return "width too large for virtual size"; 18505b261ecSmrg case MODE_VIRTUAL_Y: 18635c4bbdfSmrg return "height too large for virtual size"; 18705b261ecSmrg case MODE_MEM_VIRT: 18835c4bbdfSmrg return "insufficient memory given virtual size"; 18905b261ecSmrg case MODE_NOCLOCK: 19035c4bbdfSmrg return "no clock available for mode"; 19105b261ecSmrg case MODE_CLOCK_HIGH: 19235c4bbdfSmrg return "mode clock too high"; 19305b261ecSmrg case MODE_CLOCK_LOW: 19435c4bbdfSmrg return "mode clock too low"; 19505b261ecSmrg case MODE_CLOCK_RANGE: 19635c4bbdfSmrg return "bad mode clock/interlace/doublescan"; 19705b261ecSmrg case MODE_BAD_HVALUE: 19835c4bbdfSmrg return "horizontal timing out of range"; 19905b261ecSmrg case MODE_BAD_VVALUE: 20035c4bbdfSmrg return "vertical timing out of range"; 20105b261ecSmrg case MODE_BAD_VSCAN: 20235c4bbdfSmrg return "VScan value out of range"; 20305b261ecSmrg case MODE_HSYNC_NARROW: 20435c4bbdfSmrg return "horizontal sync too narrow"; 20505b261ecSmrg case MODE_HSYNC_WIDE: 20635c4bbdfSmrg return "horizontal sync too wide"; 20705b261ecSmrg case MODE_HBLANK_NARROW: 20835c4bbdfSmrg return "horizontal blanking too narrow"; 20905b261ecSmrg case MODE_HBLANK_WIDE: 21035c4bbdfSmrg return "horizontal blanking too wide"; 21105b261ecSmrg case MODE_VSYNC_NARROW: 21235c4bbdfSmrg return "vertical sync too narrow"; 21305b261ecSmrg case MODE_VSYNC_WIDE: 21435c4bbdfSmrg return "vertical sync too wide"; 21505b261ecSmrg case MODE_VBLANK_NARROW: 21635c4bbdfSmrg return "vertical blanking too narrow"; 21705b261ecSmrg case MODE_VBLANK_WIDE: 21835c4bbdfSmrg return "vertical blanking too wide"; 21905b261ecSmrg case MODE_PANEL: 22035c4bbdfSmrg return "exceeds panel dimensions"; 22105b261ecSmrg case MODE_INTERLACE_WIDTH: 22235c4bbdfSmrg return "width too large for interlaced mode"; 22305b261ecSmrg case MODE_ONE_WIDTH: 22405b261ecSmrg return "all modes must have the same width"; 22505b261ecSmrg case MODE_ONE_HEIGHT: 22605b261ecSmrg return "all modes must have the same height"; 22705b261ecSmrg case MODE_ONE_SIZE: 22805b261ecSmrg return "all modes must have the same resolution"; 22905b261ecSmrg case MODE_NO_REDUCED: 23005b261ecSmrg return "monitor doesn't support reduced blanking"; 2314642e01fSmrg case MODE_BANDWIDTH: 23235c4bbdfSmrg return "mode requires too much memory bandwidth"; 23305b261ecSmrg case MODE_BAD: 23435c4bbdfSmrg return "unknown reason"; 23505b261ecSmrg case MODE_ERROR: 23635c4bbdfSmrg return "internal error"; 23705b261ecSmrg default: 23835c4bbdfSmrg return "unknown"; 23905b261ecSmrg } 24005b261ecSmrg} 24105b261ecSmrg 24205b261ecSmrg/* 24305b261ecSmrg * xf86ShowClockRanges() -- Print the clock ranges allowed 24405b261ecSmrg * and the clock values scaled by ClockMulFactor and ClockDivFactor 24505b261ecSmrg */ 2466747b715Smrgvoid 24705b261ecSmrgxf86ShowClockRanges(ScrnInfoPtr scrp, ClockRangePtr clockRanges) 24805b261ecSmrg{ 24905b261ecSmrg ClockRangePtr cp; 25005b261ecSmrg int MulFactor = 1; 25105b261ecSmrg int DivFactor = 1; 25205b261ecSmrg int i, j; 25305b261ecSmrg int scaledClock; 25405b261ecSmrg 25505b261ecSmrg for (cp = clockRanges; cp != NULL; cp = cp->next) { 25635c4bbdfSmrg DivFactor = max(1, cp->ClockDivFactor); 25735c4bbdfSmrg MulFactor = max(1, cp->ClockMulFactor); 25835c4bbdfSmrg if (scrp->progClock) { 25935c4bbdfSmrg if (cp->minClock) { 26035c4bbdfSmrg if (cp->maxClock) { 26135c4bbdfSmrg xf86DrvMsg(scrp->scrnIndex, X_INFO, 26235c4bbdfSmrg "Clock range: %6.2f to %6.2f MHz\n", 26335c4bbdfSmrg (double) cp->minClock / 1000.0, 26435c4bbdfSmrg (double) cp->maxClock / 1000.0); 26535c4bbdfSmrg } 26635c4bbdfSmrg else { 26735c4bbdfSmrg xf86DrvMsg(scrp->scrnIndex, X_INFO, 26835c4bbdfSmrg "Minimum clock: %6.2f MHz\n", 26935c4bbdfSmrg (double) cp->minClock / 1000.0); 27035c4bbdfSmrg } 27135c4bbdfSmrg } 27235c4bbdfSmrg else { 27335c4bbdfSmrg if (cp->maxClock) { 27435c4bbdfSmrg xf86DrvMsg(scrp->scrnIndex, X_INFO, 27535c4bbdfSmrg "Maximum clock: %6.2f MHz\n", 27635c4bbdfSmrg (double) cp->maxClock / 1000.0); 27735c4bbdfSmrg } 27835c4bbdfSmrg } 27935c4bbdfSmrg } 28035c4bbdfSmrg else if (DivFactor > 1 || MulFactor > 1) { 28135c4bbdfSmrg j = 0; 28235c4bbdfSmrg for (i = 0; i < scrp->numClocks; i++) { 28335c4bbdfSmrg scaledClock = (scrp->clock[i] * DivFactor) / MulFactor; 28435c4bbdfSmrg if (scaledClock >= cp->minClock && scaledClock <= cp->maxClock) { 28535c4bbdfSmrg if ((j % 8) == 0) { 28635c4bbdfSmrg if (j > 0) 28735c4bbdfSmrg xf86ErrorF("\n"); 28835c4bbdfSmrg xf86DrvMsg(scrp->scrnIndex, X_INFO, "scaled clocks:"); 28935c4bbdfSmrg } 29035c4bbdfSmrg xf86ErrorF(" %6.2f", (double) scaledClock / 1000.0); 29135c4bbdfSmrg j++; 29235c4bbdfSmrg } 29335c4bbdfSmrg } 29435c4bbdfSmrg xf86ErrorF("\n"); 29535c4bbdfSmrg } 29605b261ecSmrg } 29705b261ecSmrg} 29805b261ecSmrg 2996747b715Smrgstatic Bool 3006747b715SmrgmodeInClockRange(ClockRangePtr cp, DisplayModePtr p) 3016747b715Smrg{ 3026747b715Smrg return ((p->Clock >= cp->minClock) && 30335c4bbdfSmrg (p->Clock <= cp->maxClock) && 30435c4bbdfSmrg (cp->interlaceAllowed || !(p->Flags & V_INTERLACE)) && 30535c4bbdfSmrg (cp->doubleScanAllowed || 30635c4bbdfSmrg ((p->VScan <= 1) && !(p->Flags & V_DBLSCAN)))); 3076747b715Smrg} 30805b261ecSmrg 30905b261ecSmrg/* 31005b261ecSmrg * xf86FindClockRangeForMode() [... like the name says ...] 31105b261ecSmrg */ 31205b261ecSmrgstatic ClockRangePtr 31305b261ecSmrgxf86FindClockRangeForMode(ClockRangePtr clockRanges, DisplayModePtr p) 31405b261ecSmrg{ 31505b261ecSmrg ClockRangePtr cp; 31605b261ecSmrg 31735c4bbdfSmrg for (cp = clockRanges;; cp = cp->next) 31835c4bbdfSmrg if (!cp || modeInClockRange(cp, p)) 31935c4bbdfSmrg return cp; 32005b261ecSmrg} 32105b261ecSmrg 32205b261ecSmrg/* 32305b261ecSmrg * xf86HandleBuiltinMode() - handles built-in modes 32405b261ecSmrg */ 32505b261ecSmrgstatic ModeStatus 32605b261ecSmrgxf86HandleBuiltinMode(ScrnInfoPtr scrp, 32735c4bbdfSmrg DisplayModePtr p, 32835c4bbdfSmrg DisplayModePtr modep, 32935c4bbdfSmrg ClockRangePtr clockRanges, Bool allowDiv2) 33005b261ecSmrg{ 33105b261ecSmrg ClockRangePtr cp; 33205b261ecSmrg int extraFlags = 0; 33305b261ecSmrg int MulFactor = 1; 33405b261ecSmrg int DivFactor = 1; 33505b261ecSmrg int clockIndex; 33635c4bbdfSmrg 33705b261ecSmrg /* Reject previously rejected modes */ 33805b261ecSmrg if (p->status != MODE_OK) 33935c4bbdfSmrg return p->status; 34005b261ecSmrg 34105b261ecSmrg /* Reject previously considered modes */ 34205b261ecSmrg if (p->prev) 34305b261ecSmrg return MODE_NOMODE; 34405b261ecSmrg 34505b261ecSmrg if ((p->type & M_T_CLOCK_C) == M_T_CLOCK_C) { 34635c4bbdfSmrg /* Check clock is in range */ 34735c4bbdfSmrg cp = xf86FindClockRangeForMode(clockRanges, p); 34835c4bbdfSmrg if (cp == NULL) { 34935c4bbdfSmrg modep->type = p->type; 35035c4bbdfSmrg p->status = MODE_CLOCK_RANGE; 35135c4bbdfSmrg return MODE_CLOCK_RANGE; 35235c4bbdfSmrg } 35335c4bbdfSmrg DivFactor = cp->ClockDivFactor; 35435c4bbdfSmrg MulFactor = cp->ClockMulFactor; 35535c4bbdfSmrg if (!scrp->progClock) { 35635c4bbdfSmrg clockIndex = xf86GetNearestClock(scrp, p->Clock, allowDiv2, 35735c4bbdfSmrg cp->ClockDivFactor, 35835c4bbdfSmrg cp->ClockMulFactor, &extraFlags); 35935c4bbdfSmrg modep->Clock = (scrp->clock[clockIndex] * DivFactor) 36035c4bbdfSmrg / MulFactor; 36135c4bbdfSmrg modep->ClockIndex = clockIndex; 36235c4bbdfSmrg modep->SynthClock = scrp->clock[clockIndex]; 36335c4bbdfSmrg if (extraFlags & V_CLKDIV2) { 36435c4bbdfSmrg modep->Clock /= 2; 36535c4bbdfSmrg modep->SynthClock /= 2; 36635c4bbdfSmrg } 36735c4bbdfSmrg } 36835c4bbdfSmrg else { 36905b261ecSmrg modep->Clock = p->Clock; 37035c4bbdfSmrg modep->ClockIndex = -1; 37135c4bbdfSmrg modep->SynthClock = (modep->Clock * MulFactor) 37235c4bbdfSmrg / DivFactor; 37335c4bbdfSmrg } 37435c4bbdfSmrg modep->PrivFlags = cp->PrivFlags; 37505b261ecSmrg } 37635c4bbdfSmrg else { 37735c4bbdfSmrg if (!scrp->progClock) { 37835c4bbdfSmrg modep->Clock = p->Clock; 37935c4bbdfSmrg modep->ClockIndex = p->ClockIndex; 38035c4bbdfSmrg modep->SynthClock = p->SynthClock; 38135c4bbdfSmrg } 38235c4bbdfSmrg else { 38335c4bbdfSmrg modep->Clock = p->Clock; 38435c4bbdfSmrg modep->ClockIndex = -1; 38535c4bbdfSmrg modep->SynthClock = p->SynthClock; 38635c4bbdfSmrg } 38735c4bbdfSmrg modep->PrivFlags = p->PrivFlags; 38835c4bbdfSmrg } 38935c4bbdfSmrg modep->type = p->type; 39035c4bbdfSmrg modep->HDisplay = p->HDisplay; 39135c4bbdfSmrg modep->HSyncStart = p->HSyncStart; 39235c4bbdfSmrg modep->HSyncEnd = p->HSyncEnd; 39335c4bbdfSmrg modep->HTotal = p->HTotal; 39435c4bbdfSmrg modep->HSkew = p->HSkew; 39535c4bbdfSmrg modep->VDisplay = p->VDisplay; 39635c4bbdfSmrg modep->VSyncStart = p->VSyncStart; 39735c4bbdfSmrg modep->VSyncEnd = p->VSyncEnd; 39835c4bbdfSmrg modep->VTotal = p->VTotal; 39935c4bbdfSmrg modep->VScan = p->VScan; 40035c4bbdfSmrg modep->Flags = p->Flags | extraFlags; 40135c4bbdfSmrg modep->CrtcHDisplay = p->CrtcHDisplay; 40205b261ecSmrg modep->CrtcHBlankStart = p->CrtcHBlankStart; 40335c4bbdfSmrg modep->CrtcHSyncStart = p->CrtcHSyncStart; 40435c4bbdfSmrg modep->CrtcHSyncEnd = p->CrtcHSyncEnd; 40535c4bbdfSmrg modep->CrtcHBlankEnd = p->CrtcHBlankEnd; 40635c4bbdfSmrg modep->CrtcHTotal = p->CrtcHTotal; 40735c4bbdfSmrg modep->CrtcHSkew = p->CrtcHSkew; 40835c4bbdfSmrg modep->CrtcVDisplay = p->CrtcVDisplay; 40905b261ecSmrg modep->CrtcVBlankStart = p->CrtcVBlankStart; 41035c4bbdfSmrg modep->CrtcVSyncStart = p->CrtcVSyncStart; 41135c4bbdfSmrg modep->CrtcVSyncEnd = p->CrtcVSyncEnd; 41235c4bbdfSmrg modep->CrtcVBlankEnd = p->CrtcVBlankEnd; 41335c4bbdfSmrg modep->CrtcVTotal = p->CrtcVTotal; 41435c4bbdfSmrg modep->CrtcHAdjusted = p->CrtcHAdjusted; 41535c4bbdfSmrg modep->CrtcVAdjusted = p->CrtcVAdjusted; 41635c4bbdfSmrg modep->HSync = p->HSync; 41735c4bbdfSmrg modep->VRefresh = p->VRefresh; 41835c4bbdfSmrg modep->Private = p->Private; 41935c4bbdfSmrg modep->PrivSize = p->PrivSize; 42005b261ecSmrg 42105b261ecSmrg p->prev = modep; 42235c4bbdfSmrg 42305b261ecSmrg return MODE_OK; 42405b261ecSmrg} 42505b261ecSmrg 42605b261ecSmrg/* 42705b261ecSmrg * xf86LookupMode 42805b261ecSmrg * 42905b261ecSmrg * This function returns a mode from the given list which matches the 43005b261ecSmrg * given name. When multiple modes with the same name are available, 43105b261ecSmrg * the method of picking the matching mode is determined by the 43205b261ecSmrg * strategy selected. 43305b261ecSmrg * 43405b261ecSmrg * This function takes the following parameters: 43505b261ecSmrg * scrp ScrnInfoPtr 43605b261ecSmrg * modep pointer to the returned mode, which must have the name 43705b261ecSmrg * field filled in. 43805b261ecSmrg * clockRanges a list of clock ranges. This is optional when all the 43905b261ecSmrg * modes are built-in modes. 44005b261ecSmrg * strategy how to decide which mode to use from multiple modes with 44105b261ecSmrg * the same name 44205b261ecSmrg * 44305b261ecSmrg * In addition, the following fields from the ScrnInfoRec are used: 44405b261ecSmrg * modePool the list of monitor modes compatible with the driver 44505b261ecSmrg * clocks a list of discrete clocks 44605b261ecSmrg * numClocks number of discrete clocks 44705b261ecSmrg * progClock clock is programmable 44805b261ecSmrg * 44905b261ecSmrg * If a mode was found, its values are filled in to the area pointed to 45005b261ecSmrg * by modep, If a mode was not found the return value indicates the 45105b261ecSmrg * reason. 45205b261ecSmrg */ 45305b261ecSmrg 45435c4bbdfSmrgstatic ModeStatus 45505b261ecSmrgxf86LookupMode(ScrnInfoPtr scrp, DisplayModePtr modep, 45635c4bbdfSmrg ClockRangePtr clockRanges, LookupModeFlags strategy) 45705b261ecSmrg{ 45805b261ecSmrg DisplayModePtr p, bestMode = NULL; 45905b261ecSmrg ClockRangePtr cp; 46005b261ecSmrg int i, k, gap, minimumGap = CLOCK_TOLERANCE + 1; 46105b261ecSmrg double refresh, bestRefresh = 0.0; 46205b261ecSmrg Bool found = FALSE; 46305b261ecSmrg int extraFlags = 0; 46405b261ecSmrg int clockIndex = -1; 46505b261ecSmrg int MulFactor = 1; 46605b261ecSmrg int DivFactor = 1; 46705b261ecSmrg int ModePrivFlags = 0; 46805b261ecSmrg ModeStatus status = MODE_NOMODE; 46905b261ecSmrg Bool allowDiv2 = (strategy & LOOKUP_CLKDIV2) != 0; 47005b261ecSmrg int n; 47135c4bbdfSmrg 47205b261ecSmrg const int types[] = { 47335c4bbdfSmrg M_T_BUILTIN | M_T_PREFERRED, 47435c4bbdfSmrg M_T_BUILTIN, 47535c4bbdfSmrg M_T_USERDEF | M_T_PREFERRED, 47635c4bbdfSmrg M_T_USERDEF, 47735c4bbdfSmrg M_T_DRIVER | M_T_PREFERRED, 47835c4bbdfSmrg M_T_DRIVER, 47935c4bbdfSmrg 0 48005b261ecSmrg }; 4811b5d61b8Smrg const int ntypes = ARRAY_SIZE(types); 48205b261ecSmrg 48305b261ecSmrg strategy &= ~(LOOKUP_CLKDIV2 | LOOKUP_OPTIONAL_TOLERANCES); 48405b261ecSmrg 48505b261ecSmrg /* Some sanity checking */ 48605b261ecSmrg if (scrp == NULL || scrp->modePool == NULL || 48735c4bbdfSmrg (!scrp->progClock && scrp->numClocks == 0)) { 48835c4bbdfSmrg ErrorF("xf86LookupMode: called with invalid scrnInfoRec\n"); 48935c4bbdfSmrg return MODE_ERROR; 49005b261ecSmrg } 49105b261ecSmrg if (modep == NULL || modep->name == NULL) { 49235c4bbdfSmrg ErrorF("xf86LookupMode: called with invalid modep\n"); 49335c4bbdfSmrg return MODE_ERROR; 49405b261ecSmrg } 49505b261ecSmrg for (cp = clockRanges; cp != NULL; cp = cp->next) { 49635c4bbdfSmrg /* DivFactor and MulFactor must be > 0 */ 49735c4bbdfSmrg cp->ClockDivFactor = max(1, cp->ClockDivFactor); 49835c4bbdfSmrg cp->ClockMulFactor = max(1, cp->ClockMulFactor); 49905b261ecSmrg } 50005b261ecSmrg 50105b261ecSmrg /* Scan the mode pool for matching names */ 50205b261ecSmrg for (n = 0; n < ntypes; n++) { 50335c4bbdfSmrg int type = types[n]; 50435c4bbdfSmrg 50535c4bbdfSmrg for (p = scrp->modePool; p != NULL; p = p->next) { 50635c4bbdfSmrg 50735c4bbdfSmrg /* scan through the modes in the sort order above */ 50835c4bbdfSmrg if ((p->type & type) != type) 50935c4bbdfSmrg continue; 510d9252ffbSmrg if (p->name == NULL) 511d9252ffbSmrg continue; 51235c4bbdfSmrg 51335c4bbdfSmrg if (strcmp(p->name, modep->name) == 0) { 51435c4bbdfSmrg 51535c4bbdfSmrg /* Skip over previously rejected modes */ 51635c4bbdfSmrg if (p->status != MODE_OK) { 51735c4bbdfSmrg if (!found) 51835c4bbdfSmrg status = p->status; 51935c4bbdfSmrg continue; 52035c4bbdfSmrg } 52135c4bbdfSmrg 52235c4bbdfSmrg /* Skip over previously considered modes */ 52335c4bbdfSmrg if (p->prev) 52435c4bbdfSmrg continue; 52535c4bbdfSmrg 52635c4bbdfSmrg if (p->type & M_T_BUILTIN) { 52735c4bbdfSmrg return xf86HandleBuiltinMode(scrp, p, modep, clockRanges, 52835c4bbdfSmrg allowDiv2); 52935c4bbdfSmrg } 53035c4bbdfSmrg 53135c4bbdfSmrg /* Check clock is in range */ 53235c4bbdfSmrg cp = xf86FindClockRangeForMode(clockRanges, p); 53335c4bbdfSmrg if (cp == NULL) { 53435c4bbdfSmrg /* 53535c4bbdfSmrg * XXX Could do more here to provide a more detailed 53635c4bbdfSmrg * reason for not finding a mode. 53735c4bbdfSmrg */ 53835c4bbdfSmrg p->status = MODE_CLOCK_RANGE; 53935c4bbdfSmrg if (!found) 54035c4bbdfSmrg status = MODE_CLOCK_RANGE; 54135c4bbdfSmrg continue; 54235c4bbdfSmrg } 54335c4bbdfSmrg 54435c4bbdfSmrg /* 54535c4bbdfSmrg * If programmable clock and strategy is not 54635c4bbdfSmrg * LOOKUP_BEST_REFRESH, the required mode has been found, 54735c4bbdfSmrg * otherwise record the refresh and continue looking. 54835c4bbdfSmrg */ 54935c4bbdfSmrg if (scrp->progClock) { 55035c4bbdfSmrg found = TRUE; 55135c4bbdfSmrg if (strategy != LOOKUP_BEST_REFRESH) { 55235c4bbdfSmrg bestMode = p; 55335c4bbdfSmrg DivFactor = cp->ClockDivFactor; 55435c4bbdfSmrg MulFactor = cp->ClockMulFactor; 55535c4bbdfSmrg ModePrivFlags = cp->PrivFlags; 55635c4bbdfSmrg break; 55735c4bbdfSmrg } 55835c4bbdfSmrg refresh = xf86ModeVRefresh(p); 55935c4bbdfSmrg if (p->Flags & V_INTERLACE) 56035c4bbdfSmrg refresh /= INTERLACE_REFRESH_WEIGHT; 56135c4bbdfSmrg if (refresh > bestRefresh) { 56235c4bbdfSmrg bestMode = p; 56335c4bbdfSmrg DivFactor = cp->ClockDivFactor; 56435c4bbdfSmrg MulFactor = cp->ClockMulFactor; 56535c4bbdfSmrg ModePrivFlags = cp->PrivFlags; 56635c4bbdfSmrg bestRefresh = refresh; 56735c4bbdfSmrg } 56835c4bbdfSmrg continue; 56935c4bbdfSmrg } 57035c4bbdfSmrg 57135c4bbdfSmrg /* 57235c4bbdfSmrg * Clock is in range, so if it is not a programmable clock, find 57335c4bbdfSmrg * a matching clock. 57435c4bbdfSmrg */ 57535c4bbdfSmrg 57635c4bbdfSmrg i = xf86GetNearestClock(scrp, p->Clock, allowDiv2, 57735c4bbdfSmrg cp->ClockDivFactor, cp->ClockMulFactor, 57835c4bbdfSmrg &k); 57935c4bbdfSmrg /* 58035c4bbdfSmrg * If the clock is too far from the requested clock, this 58135c4bbdfSmrg * mode is no good. 58235c4bbdfSmrg */ 58335c4bbdfSmrg if (k & V_CLKDIV2) 58435c4bbdfSmrg gap = abs((p->Clock * 2) - 58535c4bbdfSmrg ((scrp->clock[i] * cp->ClockDivFactor) / 58635c4bbdfSmrg cp->ClockMulFactor)); 58735c4bbdfSmrg else 58835c4bbdfSmrg gap = abs(p->Clock - 58935c4bbdfSmrg ((scrp->clock[i] * cp->ClockDivFactor) / 59035c4bbdfSmrg cp->ClockMulFactor)); 59135c4bbdfSmrg if (gap > minimumGap) { 59235c4bbdfSmrg p->status = MODE_NOCLOCK; 59335c4bbdfSmrg if (!found) 59435c4bbdfSmrg status = MODE_NOCLOCK; 59535c4bbdfSmrg continue; 59635c4bbdfSmrg } 59735c4bbdfSmrg found = TRUE; 59835c4bbdfSmrg 59935c4bbdfSmrg if (strategy == LOOKUP_BEST_REFRESH) { 60035c4bbdfSmrg refresh = xf86ModeVRefresh(p); 60135c4bbdfSmrg if (p->Flags & V_INTERLACE) 60235c4bbdfSmrg refresh /= INTERLACE_REFRESH_WEIGHT; 60335c4bbdfSmrg if (refresh > bestRefresh) { 60435c4bbdfSmrg bestMode = p; 60535c4bbdfSmrg DivFactor = cp->ClockDivFactor; 60635c4bbdfSmrg MulFactor = cp->ClockMulFactor; 60735c4bbdfSmrg ModePrivFlags = cp->PrivFlags; 60835c4bbdfSmrg extraFlags = k; 60935c4bbdfSmrg clockIndex = i; 61035c4bbdfSmrg bestRefresh = refresh; 61135c4bbdfSmrg } 61235c4bbdfSmrg continue; 61335c4bbdfSmrg } 61435c4bbdfSmrg if (strategy == LOOKUP_CLOSEST_CLOCK) { 61535c4bbdfSmrg if (gap < minimumGap) { 61635c4bbdfSmrg bestMode = p; 61735c4bbdfSmrg DivFactor = cp->ClockDivFactor; 61835c4bbdfSmrg MulFactor = cp->ClockMulFactor; 61935c4bbdfSmrg ModePrivFlags = cp->PrivFlags; 62035c4bbdfSmrg extraFlags = k; 62135c4bbdfSmrg clockIndex = i; 62235c4bbdfSmrg minimumGap = gap; 62335c4bbdfSmrg } 62435c4bbdfSmrg continue; 62535c4bbdfSmrg } 62635c4bbdfSmrg /* 62735c4bbdfSmrg * If strategy is neither LOOKUP_BEST_REFRESH or 62835c4bbdfSmrg * LOOKUP_CLOSEST_CLOCK the required mode has been found. 62935c4bbdfSmrg */ 63035c4bbdfSmrg bestMode = p; 63135c4bbdfSmrg DivFactor = cp->ClockDivFactor; 63235c4bbdfSmrg MulFactor = cp->ClockMulFactor; 63335c4bbdfSmrg ModePrivFlags = cp->PrivFlags; 63435c4bbdfSmrg extraFlags = k; 63535c4bbdfSmrg clockIndex = i; 63635c4bbdfSmrg break; 63735c4bbdfSmrg } 63835c4bbdfSmrg } 63935c4bbdfSmrg if (found) 64035c4bbdfSmrg break; 64105b261ecSmrg } 64205b261ecSmrg if (!found || bestMode == NULL) 64335c4bbdfSmrg return status; 64405b261ecSmrg 64505b261ecSmrg /* Fill in the mode parameters */ 64605b261ecSmrg if (scrp->progClock) { 64735c4bbdfSmrg modep->Clock = bestMode->Clock; 64835c4bbdfSmrg modep->ClockIndex = -1; 64935c4bbdfSmrg modep->SynthClock = (modep->Clock * MulFactor) / DivFactor; 65035c4bbdfSmrg } 65135c4bbdfSmrg else { 65235c4bbdfSmrg modep->Clock = (scrp->clock[clockIndex] * DivFactor) / MulFactor; 65335c4bbdfSmrg modep->ClockIndex = clockIndex; 65435c4bbdfSmrg modep->SynthClock = scrp->clock[clockIndex]; 65535c4bbdfSmrg if (extraFlags & V_CLKDIV2) { 65635c4bbdfSmrg modep->Clock /= 2; 65735c4bbdfSmrg modep->SynthClock /= 2; 65835c4bbdfSmrg } 65905b261ecSmrg } 66035c4bbdfSmrg modep->type = bestMode->type; 66135c4bbdfSmrg modep->PrivFlags = ModePrivFlags; 66235c4bbdfSmrg modep->HDisplay = bestMode->HDisplay; 66335c4bbdfSmrg modep->HSyncStart = bestMode->HSyncStart; 66435c4bbdfSmrg modep->HSyncEnd = bestMode->HSyncEnd; 66535c4bbdfSmrg modep->HTotal = bestMode->HTotal; 66635c4bbdfSmrg modep->HSkew = bestMode->HSkew; 66735c4bbdfSmrg modep->VDisplay = bestMode->VDisplay; 66835c4bbdfSmrg modep->VSyncStart = bestMode->VSyncStart; 66935c4bbdfSmrg modep->VSyncEnd = bestMode->VSyncEnd; 67035c4bbdfSmrg modep->VTotal = bestMode->VTotal; 67135c4bbdfSmrg modep->VScan = bestMode->VScan; 67235c4bbdfSmrg modep->Flags = bestMode->Flags | extraFlags; 67335c4bbdfSmrg modep->CrtcHDisplay = bestMode->CrtcHDisplay; 67435c4bbdfSmrg modep->CrtcHBlankStart = bestMode->CrtcHBlankStart; 67535c4bbdfSmrg modep->CrtcHSyncStart = bestMode->CrtcHSyncStart; 67635c4bbdfSmrg modep->CrtcHSyncEnd = bestMode->CrtcHSyncEnd; 67735c4bbdfSmrg modep->CrtcHBlankEnd = bestMode->CrtcHBlankEnd; 67835c4bbdfSmrg modep->CrtcHTotal = bestMode->CrtcHTotal; 67935c4bbdfSmrg modep->CrtcHSkew = bestMode->CrtcHSkew; 68035c4bbdfSmrg modep->CrtcVDisplay = bestMode->CrtcVDisplay; 68135c4bbdfSmrg modep->CrtcVBlankStart = bestMode->CrtcVBlankStart; 68235c4bbdfSmrg modep->CrtcVSyncStart = bestMode->CrtcVSyncStart; 68335c4bbdfSmrg modep->CrtcVSyncEnd = bestMode->CrtcVSyncEnd; 68435c4bbdfSmrg modep->CrtcVBlankEnd = bestMode->CrtcVBlankEnd; 68535c4bbdfSmrg modep->CrtcVTotal = bestMode->CrtcVTotal; 68635c4bbdfSmrg modep->CrtcHAdjusted = bestMode->CrtcHAdjusted; 68735c4bbdfSmrg modep->CrtcVAdjusted = bestMode->CrtcVAdjusted; 68835c4bbdfSmrg modep->HSync = bestMode->HSync; 68935c4bbdfSmrg modep->VRefresh = bestMode->VRefresh; 69035c4bbdfSmrg modep->Private = bestMode->Private; 69135c4bbdfSmrg modep->PrivSize = bestMode->PrivSize; 69205b261ecSmrg 69305b261ecSmrg bestMode->prev = modep; 69405b261ecSmrg 69505b261ecSmrg return MODE_OK; 69605b261ecSmrg} 69705b261ecSmrg 69805b261ecSmrg/* 69905b261ecSmrg * xf86CheckModeForMonitor 70005b261ecSmrg * 70105b261ecSmrg * This function takes a mode and monitor description, and determines 70205b261ecSmrg * if the mode is valid for the monitor. 70305b261ecSmrg */ 7046747b715SmrgModeStatus 70505b261ecSmrgxf86CheckModeForMonitor(DisplayModePtr mode, MonPtr monitor) 70605b261ecSmrg{ 70705b261ecSmrg int i; 70805b261ecSmrg 70905b261ecSmrg /* Sanity checks */ 71005b261ecSmrg if (mode == NULL || monitor == NULL) { 71135c4bbdfSmrg ErrorF("xf86CheckModeForMonitor: called with invalid parameters\n"); 71235c4bbdfSmrg return MODE_ERROR; 71305b261ecSmrg } 71405b261ecSmrg 7156747b715Smrg DebugF("xf86CheckModeForMonitor(%p %s, %p %s)\n", 71635c4bbdfSmrg mode, mode->name, monitor, monitor->id); 71705b261ecSmrg 71805b261ecSmrg /* Some basic mode validity checks */ 71905b261ecSmrg if (0 >= mode->HDisplay || mode->HDisplay > mode->HSyncStart || 72035c4bbdfSmrg mode->HSyncStart >= mode->HSyncEnd || mode->HSyncEnd >= mode->HTotal) 72135c4bbdfSmrg return MODE_H_ILLEGAL; 72205b261ecSmrg 72305b261ecSmrg if (0 >= mode->VDisplay || mode->VDisplay > mode->VSyncStart || 72435c4bbdfSmrg mode->VSyncStart >= mode->VSyncEnd || mode->VSyncEnd >= mode->VTotal) 72535c4bbdfSmrg return MODE_V_ILLEGAL; 72605b261ecSmrg 72705b261ecSmrg if (monitor->nHsync > 0) { 72835c4bbdfSmrg /* Check hsync against the allowed ranges */ 72935c4bbdfSmrg float hsync = xf86ModeHSync(mode); 73035c4bbdfSmrg 73135c4bbdfSmrg for (i = 0; i < monitor->nHsync; i++) 73235c4bbdfSmrg if ((hsync > monitor->hsync[i].lo * (1.0 - SYNC_TOLERANCE)) && 73335c4bbdfSmrg (hsync < monitor->hsync[i].hi * (1.0 + SYNC_TOLERANCE))) 73435c4bbdfSmrg break; 73535c4bbdfSmrg 73635c4bbdfSmrg /* Now see whether we ran out of sync ranges without finding a match */ 73735c4bbdfSmrg if (i == monitor->nHsync) 73835c4bbdfSmrg return MODE_HSYNC; 73905b261ecSmrg } 74005b261ecSmrg 74105b261ecSmrg if (monitor->nVrefresh > 0) { 74235c4bbdfSmrg /* Check vrefresh against the allowed ranges */ 74335c4bbdfSmrg float vrefrsh = xf86ModeVRefresh(mode); 74435c4bbdfSmrg 74535c4bbdfSmrg for (i = 0; i < monitor->nVrefresh; i++) 74635c4bbdfSmrg if ((vrefrsh > monitor->vrefresh[i].lo * (1.0 - SYNC_TOLERANCE)) && 74735c4bbdfSmrg (vrefrsh < monitor->vrefresh[i].hi * (1.0 + SYNC_TOLERANCE))) 74835c4bbdfSmrg break; 74935c4bbdfSmrg 75035c4bbdfSmrg /* Now see whether we ran out of refresh ranges without finding a match */ 75135c4bbdfSmrg if (i == monitor->nVrefresh) 75235c4bbdfSmrg return MODE_VSYNC; 75305b261ecSmrg } 75405b261ecSmrg 75505b261ecSmrg /* Force interlaced modes to have an odd VTotal */ 75605b261ecSmrg if (mode->Flags & V_INTERLACE) 75735c4bbdfSmrg mode->CrtcVTotal = mode->VTotal |= 1; 75805b261ecSmrg 75905b261ecSmrg /* 76005b261ecSmrg * This code stops cvt -r modes, and only cvt -r modes, from hitting 15y+ 76105b261ecSmrg * old CRTs which might, when there is a lot of solar flare activity and 76205b261ecSmrg * when the celestial bodies are unfavourably aligned, implode trying to 76305b261ecSmrg * sync to it. It's called "Protecting the user from doing anything stupid". 76405b261ecSmrg * -- libv 76505b261ecSmrg */ 76605b261ecSmrg 7674642e01fSmrg if (xf86ModeIsReduced(mode)) { 7684642e01fSmrg if (!monitor->reducedblanking && !(mode->type & M_T_DRIVER)) 7694642e01fSmrg return MODE_NO_REDUCED; 77005b261ecSmrg } 77105b261ecSmrg 77205b261ecSmrg if ((monitor->maxPixClock) && (mode->Clock > monitor->maxPixClock)) 77335c4bbdfSmrg return MODE_CLOCK_HIGH; 77405b261ecSmrg 77505b261ecSmrg return MODE_OK; 77605b261ecSmrg} 77705b261ecSmrg 77805b261ecSmrg/* 77905b261ecSmrg * xf86CheckModeSize 78005b261ecSmrg * 78105b261ecSmrg * An internal routine to check if a mode fits in video memory. This tries to 78205b261ecSmrg * avoid overflows that would otherwise occur when video memory size is greater 78305b261ecSmrg * than 256MB. 78405b261ecSmrg */ 78505b261ecSmrgstatic Bool 78605b261ecSmrgxf86CheckModeSize(ScrnInfoPtr scrp, int w, int x, int y) 78705b261ecSmrg{ 78835c4bbdfSmrg int bpp = scrp->fbFormat.bitsPerPixel, pad = scrp->fbFormat.scanlinePad; 78905b261ecSmrg int lineWidth, lastWidth; 79005b261ecSmrg 79105b261ecSmrg if (scrp->depth == 4) 79235c4bbdfSmrg pad *= 4; /* 4 planes */ 79305b261ecSmrg 79405b261ecSmrg /* Sanity check */ 79505b261ecSmrg if ((w < 0) || (x < 0) || (y <= 0)) 79635c4bbdfSmrg return FALSE; 79705b261ecSmrg 79805b261ecSmrg lineWidth = (((w * bpp) + pad - 1) / pad) * pad; 79905b261ecSmrg lastWidth = x * bpp; 80005b261ecSmrg 80105b261ecSmrg /* 80205b261ecSmrg * At this point, we need to compare 80305b261ecSmrg * 80435c4bbdfSmrg * (lineWidth * (y - 1)) + lastWidth 80505b261ecSmrg * 80605b261ecSmrg * against 80705b261ecSmrg * 80835c4bbdfSmrg * scrp->videoRam * (1024 * 8) 80905b261ecSmrg * 81005b261ecSmrg * These are bit quantities. To avoid overflows, do the comparison in 81105b261ecSmrg * terms of BITMAP_SCANLINE_PAD units. This assumes BITMAP_SCANLINE_PAD 81205b261ecSmrg * is a power of 2. We currently use 32, which limits us to a video 81305b261ecSmrg * memory size of 8GB. 81405b261ecSmrg */ 81505b261ecSmrg 81605b261ecSmrg lineWidth = (lineWidth + (BITMAP_SCANLINE_PAD - 1)) / BITMAP_SCANLINE_PAD; 81705b261ecSmrg lastWidth = (lastWidth + (BITMAP_SCANLINE_PAD - 1)) / BITMAP_SCANLINE_PAD; 81805b261ecSmrg 81905b261ecSmrg if ((lineWidth * (y - 1) + lastWidth) > 82035c4bbdfSmrg (scrp->videoRam * ((1024 * 8) / BITMAP_SCANLINE_PAD))) 82135c4bbdfSmrg return FALSE; 82205b261ecSmrg 82305b261ecSmrg return TRUE; 82405b261ecSmrg} 82505b261ecSmrg 82605b261ecSmrg/* 82705b261ecSmrg * xf86InitialCheckModeForDriver 82805b261ecSmrg * 82905b261ecSmrg * This function checks if a mode satisfies a driver's initial requirements: 83005b261ecSmrg * - mode size fits within the available pixel area (memory) 83105b261ecSmrg * - width lies within the range of supported line pitches 83205b261ecSmrg * - mode size fits within virtual size (if fixed) 83305b261ecSmrg * - horizontal timings are in range 83405b261ecSmrg * 83505b261ecSmrg * This function takes the following parameters: 83605b261ecSmrg * scrp ScrnInfoPtr 83705b261ecSmrg * mode mode to check 83805b261ecSmrg * maxPitch (optional) maximum line pitch 83905b261ecSmrg * virtualX (optional) virtual width requested 84005b261ecSmrg * virtualY (optional) virtual height requested 84105b261ecSmrg * 84205b261ecSmrg * In addition, the following fields from the ScrnInfoRec are used: 84305b261ecSmrg * monitor pointer to structure for monitor section 84405b261ecSmrg * fbFormat pixel format for the framebuffer 84505b261ecSmrg * videoRam video memory size (in kB) 84605b261ecSmrg */ 84705b261ecSmrg 84835c4bbdfSmrgstatic ModeStatus 84905b261ecSmrgxf86InitialCheckModeForDriver(ScrnInfoPtr scrp, DisplayModePtr mode, 85035c4bbdfSmrg ClockRangePtr clockRanges, 85135c4bbdfSmrg LookupModeFlags strategy, 85235c4bbdfSmrg int maxPitch, int virtualX, int virtualY) 85305b261ecSmrg{ 85405b261ecSmrg ClockRangePtr cp; 85505b261ecSmrg ModeStatus status; 85605b261ecSmrg Bool allowDiv2 = (strategy & LOOKUP_CLKDIV2) != 0; 85705b261ecSmrg int i, needDiv2; 85835c4bbdfSmrg 85905b261ecSmrg /* Sanity checks */ 86005b261ecSmrg if (!scrp || !mode || !clockRanges) { 86135c4bbdfSmrg ErrorF("xf86InitialCheckModeForDriver: " 86235c4bbdfSmrg "called with invalid parameters\n"); 86335c4bbdfSmrg return MODE_ERROR; 86405b261ecSmrg } 86505b261ecSmrg 8666747b715Smrg DebugF("xf86InitialCheckModeForDriver(%p, %p %s, %p, 0x%x, %d, %d, %d)\n", 86735c4bbdfSmrg scrp, mode, mode->name, clockRanges, strategy, maxPitch, virtualX, 86835c4bbdfSmrg virtualY); 86905b261ecSmrg 87005b261ecSmrg /* Some basic mode validity checks */ 87105b261ecSmrg if (0 >= mode->HDisplay || mode->HDisplay > mode->HSyncStart || 87235c4bbdfSmrg mode->HSyncStart >= mode->HSyncEnd || mode->HSyncEnd >= mode->HTotal) 87335c4bbdfSmrg return MODE_H_ILLEGAL; 87405b261ecSmrg 87505b261ecSmrg if (0 >= mode->VDisplay || mode->VDisplay > mode->VSyncStart || 87635c4bbdfSmrg mode->VSyncStart >= mode->VSyncEnd || mode->VSyncEnd >= mode->VTotal) 87735c4bbdfSmrg return MODE_V_ILLEGAL; 87805b261ecSmrg 87905b261ecSmrg if (!xf86CheckModeSize(scrp, mode->HDisplay, mode->HDisplay, 88035c4bbdfSmrg mode->VDisplay)) 88105b261ecSmrg return MODE_MEM; 88205b261ecSmrg 88305b261ecSmrg if (maxPitch > 0 && mode->HDisplay > maxPitch) 88435c4bbdfSmrg return MODE_BAD_WIDTH; 88505b261ecSmrg 88605b261ecSmrg if (virtualX > 0 && mode->HDisplay > virtualX) 88735c4bbdfSmrg return MODE_VIRTUAL_X; 88805b261ecSmrg 88905b261ecSmrg if (virtualY > 0 && mode->VDisplay > virtualY) 89035c4bbdfSmrg return MODE_VIRTUAL_Y; 89105b261ecSmrg 89205b261ecSmrg /* 89305b261ecSmrg * The use of the DisplayModeRec's Crtc* and SynthClock elements below is 89405b261ecSmrg * provisional, in that they are later reused by the driver at mode-set 89505b261ecSmrg * time. Here, they are temporarily enlisted to contain the mode timings 89605b261ecSmrg * as seen by the CRT or panel (rather than the CRTC). The driver's 89705b261ecSmrg * ValidMode() is allowed to modify these so it can deal with such things 89805b261ecSmrg * as mode stretching and/or centering. The driver should >NOT< modify the 89905b261ecSmrg * user-supplied values as these are reported back when mode validation is 90005b261ecSmrg * said and done. 90105b261ecSmrg */ 90205b261ecSmrg /* 90305b261ecSmrg * NOTE: We (ab)use the mode->Crtc* values here to store timing 90405b261ecSmrg * information for the calculation of Hsync and Vrefresh. Before 90505b261ecSmrg * these values are calculated the driver is given the opportunity 90605b261ecSmrg * to either set these HSync and VRefresh itself or modify the timing 90705b261ecSmrg * values. 90805b261ecSmrg * The difference to the final calculation is small but imortand: 90905b261ecSmrg * here we pass the flag INTERLACE_HALVE_V regardless if the driver 91005b261ecSmrg * sets it or not. This way our calculation of VRefresh has the same 91105b261ecSmrg * effect as if we do if (flags & V_INTERLACE) refresh *= 2.0 91205b261ecSmrg * This dual use of the mode->Crtc* values will certainly create 91305b261ecSmrg * confusion and is bad software design. However since it's part of 91405b261ecSmrg * the driver API it's hard to change. 91505b261ecSmrg */ 91635c4bbdfSmrg 91705b261ecSmrg if (scrp->ValidMode) { 91835c4bbdfSmrg 91935c4bbdfSmrg xf86SetModeCrtc(mode, INTERLACE_HALVE_V); 92035c4bbdfSmrg 92135c4bbdfSmrg cp = xf86FindClockRangeForMode(clockRanges, mode); 92235c4bbdfSmrg if (!cp) 92335c4bbdfSmrg return MODE_CLOCK_RANGE; 92435c4bbdfSmrg 92535c4bbdfSmrg if (cp->ClockMulFactor < 1) 92635c4bbdfSmrg cp->ClockMulFactor = 1; 92735c4bbdfSmrg if (cp->ClockDivFactor < 1) 92835c4bbdfSmrg cp->ClockDivFactor = 1; 92935c4bbdfSmrg 93035c4bbdfSmrg /* 93135c4bbdfSmrg * XXX The effect of clock dividers and multipliers on the monitor's 93235c4bbdfSmrg * pixel clock needs to be verified. 93335c4bbdfSmrg */ 93435c4bbdfSmrg if (scrp->progClock) { 93535c4bbdfSmrg mode->SynthClock = mode->Clock; 93635c4bbdfSmrg } 93735c4bbdfSmrg else { 93835c4bbdfSmrg i = xf86GetNearestClock(scrp, mode->Clock, allowDiv2, 93935c4bbdfSmrg cp->ClockDivFactor, cp->ClockMulFactor, 94035c4bbdfSmrg &needDiv2); 94135c4bbdfSmrg mode->SynthClock = (scrp->clock[i] * cp->ClockDivFactor) / 94235c4bbdfSmrg cp->ClockMulFactor; 94335c4bbdfSmrg if (needDiv2 & V_CLKDIV2) 94435c4bbdfSmrg mode->SynthClock /= 2; 94535c4bbdfSmrg } 94635c4bbdfSmrg 94735c4bbdfSmrg status = (*scrp->ValidMode) (scrp, mode, FALSE, 94835c4bbdfSmrg MODECHECK_INITIAL); 94935c4bbdfSmrg if (status != MODE_OK) 95035c4bbdfSmrg return status; 95135c4bbdfSmrg 95235c4bbdfSmrg if (mode->HSync <= 0.0) 95335c4bbdfSmrg mode->HSync = (float) mode->SynthClock / (float) mode->CrtcHTotal; 95435c4bbdfSmrg if (mode->VRefresh <= 0.0) 95535c4bbdfSmrg mode->VRefresh = (mode->SynthClock * 1000.0) 95635c4bbdfSmrg / (mode->CrtcHTotal * mode->CrtcVTotal); 95705b261ecSmrg } 95835c4bbdfSmrg 95905b261ecSmrg mode->HSync = xf86ModeHSync(mode); 96005b261ecSmrg mode->VRefresh = xf86ModeVRefresh(mode); 96105b261ecSmrg 96205b261ecSmrg /* Assume it is OK */ 96305b261ecSmrg return MODE_OK; 96405b261ecSmrg} 96505b261ecSmrg 96605b261ecSmrg/* 96705b261ecSmrg * xf86CheckModeForDriver 96805b261ecSmrg * 96905b261ecSmrg * This function is for checking modes while the server is running (for 97005b261ecSmrg * use mainly by the VidMode extension). 97105b261ecSmrg * 97205b261ecSmrg * This function checks if a mode satisfies a driver's requirements: 97305b261ecSmrg * - width lies within the line pitch 97405b261ecSmrg * - mode size fits within virtual size 97505b261ecSmrg * - horizontal/vertical timings are in range 97605b261ecSmrg * 97705b261ecSmrg * This function takes the following parameters: 97805b261ecSmrg * scrp ScrnInfoPtr 97905b261ecSmrg * mode mode to check 98005b261ecSmrg * flags not (currently) used 98105b261ecSmrg * 98205b261ecSmrg * In addition, the following fields from the ScrnInfoRec are used: 98305b261ecSmrg * virtualX virtual width 98405b261ecSmrg * virtualY virtual height 98505b261ecSmrg * clockRanges allowable clock ranges 98605b261ecSmrg */ 98705b261ecSmrg 9886747b715SmrgModeStatus 98905b261ecSmrgxf86CheckModeForDriver(ScrnInfoPtr scrp, DisplayModePtr mode, int flags) 99005b261ecSmrg{ 9916747b715Smrg ClockRangePtr cp; 99205b261ecSmrg int i, k, gap, minimumGap = CLOCK_TOLERANCE + 1; 99305b261ecSmrg int extraFlags = 0; 99405b261ecSmrg int clockIndex = -1; 99505b261ecSmrg int MulFactor = 1; 99605b261ecSmrg int DivFactor = 1; 99705b261ecSmrg int ModePrivFlags = 0; 99805b261ecSmrg ModeStatus status = MODE_NOMODE; 99905b261ecSmrg 100005b261ecSmrg /* Some sanity checking */ 100135c4bbdfSmrg if (scrp == NULL || (!scrp->progClock && scrp->numClocks == 0)) { 100235c4bbdfSmrg ErrorF("xf86CheckModeForDriver: called with invalid scrnInfoRec\n"); 100335c4bbdfSmrg return MODE_ERROR; 100405b261ecSmrg } 100505b261ecSmrg if (mode == NULL) { 100635c4bbdfSmrg ErrorF("xf86CheckModeForDriver: called with invalid modep\n"); 100735c4bbdfSmrg return MODE_ERROR; 100805b261ecSmrg } 100905b261ecSmrg 101005b261ecSmrg /* Check the mode size */ 101105b261ecSmrg if (mode->HDisplay > scrp->virtualX) 101235c4bbdfSmrg return MODE_VIRTUAL_X; 101305b261ecSmrg 101405b261ecSmrg if (mode->VDisplay > scrp->virtualY) 101535c4bbdfSmrg return MODE_VIRTUAL_Y; 101605b261ecSmrg 101705b261ecSmrg for (cp = scrp->clockRanges; cp != NULL; cp = cp->next) { 101835c4bbdfSmrg /* DivFactor and MulFactor must be > 0 */ 101935c4bbdfSmrg cp->ClockDivFactor = max(1, cp->ClockDivFactor); 102035c4bbdfSmrg cp->ClockMulFactor = max(1, cp->ClockMulFactor); 102105b261ecSmrg } 102205b261ecSmrg 102305b261ecSmrg if (scrp->progClock) { 102435c4bbdfSmrg /* Check clock is in range */ 102535c4bbdfSmrg for (cp = scrp->clockRanges; cp != NULL; cp = cp->next) { 102635c4bbdfSmrg if (modeInClockRange(cp, mode)) 102735c4bbdfSmrg break; 102835c4bbdfSmrg } 102935c4bbdfSmrg if (cp == NULL) { 103035c4bbdfSmrg return MODE_CLOCK_RANGE; 103135c4bbdfSmrg } 103235c4bbdfSmrg /* 103335c4bbdfSmrg * If programmable clock the required mode has been found 103435c4bbdfSmrg */ 103535c4bbdfSmrg DivFactor = cp->ClockDivFactor; 103635c4bbdfSmrg MulFactor = cp->ClockMulFactor; 103735c4bbdfSmrg ModePrivFlags = cp->PrivFlags; 103835c4bbdfSmrg } 103935c4bbdfSmrg else { 104035c4bbdfSmrg status = MODE_CLOCK_RANGE; 104135c4bbdfSmrg /* Check clock is in range */ 104235c4bbdfSmrg for (cp = scrp->clockRanges; cp != NULL; cp = cp->next) { 104335c4bbdfSmrg if (modeInClockRange(cp, mode)) { 104435c4bbdfSmrg /* 104535c4bbdfSmrg * Clock is in range, so if it is not a programmable clock, 104635c4bbdfSmrg * find a matching clock. 104735c4bbdfSmrg */ 104835c4bbdfSmrg 104935c4bbdfSmrg i = xf86GetNearestClock(scrp, mode->Clock, 0, 105035c4bbdfSmrg cp->ClockDivFactor, cp->ClockMulFactor, 105135c4bbdfSmrg &k); 105235c4bbdfSmrg /* 105335c4bbdfSmrg * If the clock is too far from the requested clock, this 105435c4bbdfSmrg * mode is no good. 105535c4bbdfSmrg */ 105635c4bbdfSmrg if (k & V_CLKDIV2) 105735c4bbdfSmrg gap = abs((mode->Clock * 2) - 105835c4bbdfSmrg ((scrp->clock[i] * cp->ClockDivFactor) / 105935c4bbdfSmrg cp->ClockMulFactor)); 106035c4bbdfSmrg else 106135c4bbdfSmrg gap = abs(mode->Clock - 106235c4bbdfSmrg ((scrp->clock[i] * cp->ClockDivFactor) / 106335c4bbdfSmrg cp->ClockMulFactor)); 106435c4bbdfSmrg if (gap > minimumGap) { 106535c4bbdfSmrg status = MODE_NOCLOCK; 106635c4bbdfSmrg continue; 106735c4bbdfSmrg } 106835c4bbdfSmrg 106935c4bbdfSmrg DivFactor = cp->ClockDivFactor; 107035c4bbdfSmrg MulFactor = cp->ClockMulFactor; 107135c4bbdfSmrg ModePrivFlags = cp->PrivFlags; 107235c4bbdfSmrg extraFlags = k; 107335c4bbdfSmrg clockIndex = i; 107435c4bbdfSmrg break; 107535c4bbdfSmrg } 107635c4bbdfSmrg } 107735c4bbdfSmrg if (cp == NULL) 107835c4bbdfSmrg return status; 107905b261ecSmrg } 108005b261ecSmrg 108105b261ecSmrg /* Fill in the mode parameters */ 108205b261ecSmrg if (scrp->progClock) { 108335c4bbdfSmrg mode->ClockIndex = -1; 108435c4bbdfSmrg mode->SynthClock = (mode->Clock * MulFactor) / DivFactor; 108535c4bbdfSmrg } 108635c4bbdfSmrg else { 108735c4bbdfSmrg mode->Clock = (scrp->clock[clockIndex] * DivFactor) / MulFactor; 108835c4bbdfSmrg mode->ClockIndex = clockIndex; 108935c4bbdfSmrg mode->SynthClock = scrp->clock[clockIndex]; 109035c4bbdfSmrg if (extraFlags & V_CLKDIV2) { 109135c4bbdfSmrg mode->Clock /= 2; 109235c4bbdfSmrg mode->SynthClock /= 2; 109335c4bbdfSmrg } 109405b261ecSmrg } 109535c4bbdfSmrg mode->PrivFlags = ModePrivFlags; 109605b261ecSmrg 109705b261ecSmrg return MODE_OK; 109805b261ecSmrg} 109905b261ecSmrg 110035c4bbdfSmrgstatic int 110105b261ecSmrginferVirtualSize(ScrnInfoPtr scrp, DisplayModePtr modes, int *vx, int *vy) 110205b261ecSmrg{ 110305b261ecSmrg float aspect = 0.0; 110405b261ecSmrg MonPtr mon = scrp->monitor; 11054642e01fSmrg xf86MonPtr DDC; 110605b261ecSmrg int x = 0, y = 0; 110705b261ecSmrg DisplayModePtr mode; 110805b261ecSmrg 110935c4bbdfSmrg if (!mon) 111035c4bbdfSmrg return 0; 11114642e01fSmrg DDC = mon->DDC; 11124642e01fSmrg 11134642e01fSmrg if (DDC && DDC->ver.revision >= 4) { 111435c4bbdfSmrg /* For 1.4, we might actually get native pixel format. How novel. */ 111535c4bbdfSmrg if (PREFERRED_TIMING_MODE(DDC->features.msc)) { 111635c4bbdfSmrg for (mode = modes; mode; mode = mode->next) { 111735c4bbdfSmrg if (mode->type & (M_T_DRIVER | M_T_PREFERRED)) { 111835c4bbdfSmrg x = mode->HDisplay; 111935c4bbdfSmrg y = mode->VDisplay; 112035c4bbdfSmrg goto found; 112135c4bbdfSmrg } 112235c4bbdfSmrg } 112335c4bbdfSmrg } 112435c4bbdfSmrg /* 112535c4bbdfSmrg * Even if we don't, we might get aspect ratio from extra CVT info 112635c4bbdfSmrg * or from the monitor size fields. TODO. 112735c4bbdfSmrg */ 11284642e01fSmrg } 112905b261ecSmrg 113005b261ecSmrg /* 11314642e01fSmrg * Technically this triggers if either is zero. That wasn't legal 11324642e01fSmrg * before EDID 1.4, but right now we'll get that wrong. TODO. 113305b261ecSmrg */ 11344642e01fSmrg if (!aspect) { 113535c4bbdfSmrg if (!mon->widthmm || !mon->heightmm) 113635c4bbdfSmrg aspect = 4.0 / 3.0; 113735c4bbdfSmrg else 113835c4bbdfSmrg aspect = (float) mon->widthmm / (float) mon->heightmm; 11394642e01fSmrg } 114005b261ecSmrg 114105b261ecSmrg /* find the largest M_T_DRIVER mode with that aspect ratio */ 114205b261ecSmrg for (mode = modes; mode; mode = mode->next) { 114335c4bbdfSmrg float mode_aspect, metaspect; 114435c4bbdfSmrg 114535c4bbdfSmrg if (!(mode->type & (M_T_DRIVER | M_T_USERDEF))) 114635c4bbdfSmrg continue; 114735c4bbdfSmrg mode_aspect = (float) mode->HDisplay / (float) mode->VDisplay; 114835c4bbdfSmrg metaspect = aspect / mode_aspect; 114935c4bbdfSmrg /* 5% slop or so, since we only get size in centimeters */ 115035c4bbdfSmrg if (fabs(1.0 - metaspect) < 0.05) { 115135c4bbdfSmrg if ((mode->HDisplay > x) && (mode->VDisplay > y)) { 115235c4bbdfSmrg x = mode->HDisplay; 115335c4bbdfSmrg y = mode->VDisplay; 115435c4bbdfSmrg } 115535c4bbdfSmrg } 115605b261ecSmrg } 115705b261ecSmrg 115805b261ecSmrg if (!x || !y) { 115935c4bbdfSmrg xf86DrvMsg(scrp->scrnIndex, X_WARNING, 116035c4bbdfSmrg "Unable to estimate virtual size\n"); 116135c4bbdfSmrg return 0; 116205b261ecSmrg } 116305b261ecSmrg 116435c4bbdfSmrg found: 116505b261ecSmrg *vx = x; 116605b261ecSmrg *vy = y; 116705b261ecSmrg 116805b261ecSmrg xf86DrvMsg(scrp->scrnIndex, X_INFO, 116935c4bbdfSmrg "Estimated virtual size for aspect ratio %.4f is %dx%d\n", 117035c4bbdfSmrg aspect, *vx, *vy); 117105b261ecSmrg 117205b261ecSmrg return 1; 117305b261ecSmrg} 117405b261ecSmrg 11756747b715Smrg/* Least common multiple */ 11766747b715Smrgstatic unsigned int 11776747b715SmrgLCM(unsigned int x, unsigned int y) 11786747b715Smrg{ 11796747b715Smrg unsigned int m = x, n = y, o; 11806747b715Smrg 118135c4bbdfSmrg while ((o = m % n)) { 11826747b715Smrg m = n; 11836747b715Smrg n = o; 11846747b715Smrg } 11856747b715Smrg 11866747b715Smrg return (x / n) * y; 11876747b715Smrg} 11886747b715Smrg 11896747b715Smrg/* 11906747b715Smrg * Given various screen attributes, determine the minimum scanline width such 1191ed6184dfSmrg * that each scanline is server and DDX padded and any pixels with embedded 11926747b715Smrg * bank boundaries are off-screen. This function returns -1 if such a width 11936747b715Smrg * cannot exist. 11946747b715Smrg */ 11956747b715Smrgstatic int 119635c4bbdfSmrgscanLineWidth(unsigned int xsize, /* pixels */ 119735c4bbdfSmrg unsigned int ysize, /* pixels */ 119835c4bbdfSmrg unsigned int width, /* pixels */ 119935c4bbdfSmrg unsigned long BankSize, /* char's */ 120035c4bbdfSmrg PixmapFormatRec * pBankFormat, unsigned int nWidthUnit /* bits */ 120135c4bbdfSmrg ) 12026747b715Smrg{ 12036747b715Smrg unsigned long nBitsPerBank, nBitsPerScanline, nBitsPerScanlinePadUnit; 12046747b715Smrg unsigned long minBitsPerScanline, maxBitsPerScanline; 12056747b715Smrg 12066747b715Smrg /* Sanity checks */ 12076747b715Smrg 12086747b715Smrg if (!nWidthUnit || !pBankFormat) 12096747b715Smrg return -1; 12106747b715Smrg 12116747b715Smrg nBitsPerBank = BankSize * 8; 12126747b715Smrg if (nBitsPerBank % pBankFormat->scanlinePad) 12136747b715Smrg return -1; 12146747b715Smrg 12156747b715Smrg if (xsize > width) 12166747b715Smrg width = xsize; 12176747b715Smrg nBitsPerScanlinePadUnit = LCM(pBankFormat->scanlinePad, nWidthUnit); 12186747b715Smrg nBitsPerScanline = 12196747b715Smrg (((width * pBankFormat->bitsPerPixel) + nBitsPerScanlinePadUnit - 1) / 12206747b715Smrg nBitsPerScanlinePadUnit) * nBitsPerScanlinePadUnit; 12216747b715Smrg width = nBitsPerScanline / pBankFormat->bitsPerPixel; 12226747b715Smrg 12236747b715Smrg if (!xsize || !(nBitsPerBank % pBankFormat->bitsPerPixel)) 122435c4bbdfSmrg return (int) width; 12256747b715Smrg 12266747b715Smrg /* 12276747b715Smrg * Scanlines will be server-pad aligned at this point. They will also be 1228ed6184dfSmrg * a multiple of nWidthUnit bits long. Ensure that pixels with embedded 12296747b715Smrg * bank boundaries are off-screen. 12306747b715Smrg * 12316747b715Smrg * It seems reasonable to limit total frame buffer size to 1/16 of the 12326747b715Smrg * theoretical maximum address space size. On a machine with 32-bit 12336747b715Smrg * addresses (to 8-bit quantities) this turns out to be 256MB. Not only 12346747b715Smrg * does this provide a simple limiting condition for the loops below, but 12356747b715Smrg * it also prevents unsigned long wraparounds. 12366747b715Smrg */ 12376747b715Smrg if (!ysize) 12386747b715Smrg return -1; 12396747b715Smrg 12406747b715Smrg minBitsPerScanline = xsize * pBankFormat->bitsPerPixel; 12416747b715Smrg if (minBitsPerScanline > nBitsPerBank) 12426747b715Smrg return -1; 12436747b715Smrg 12446747b715Smrg if (ysize == 1) 124535c4bbdfSmrg return (int) width; 12466747b715Smrg 12476747b715Smrg maxBitsPerScanline = 124835c4bbdfSmrg (((unsigned long) (-1) >> 1) - minBitsPerScanline) / (ysize - 1); 124935c4bbdfSmrg while (nBitsPerScanline <= maxBitsPerScanline) { 12506747b715Smrg unsigned long BankBase, BankUnit; 12516747b715Smrg 12526747b715Smrg BankUnit = ((nBitsPerBank + nBitsPerScanline - 1) / nBitsPerBank) * 12536747b715Smrg nBitsPerBank; 12546747b715Smrg if (!(BankUnit % nBitsPerScanline)) 125535c4bbdfSmrg return (int) width; 12566747b715Smrg 125735c4bbdfSmrg for (BankBase = BankUnit;; BankBase += nBitsPerBank) { 12586747b715Smrg unsigned long x, y; 12596747b715Smrg 12606747b715Smrg y = BankBase / nBitsPerScanline; 12616747b715Smrg if (y >= ysize) 126235c4bbdfSmrg return (int) width; 12636747b715Smrg 12646747b715Smrg x = BankBase % nBitsPerScanline; 12656747b715Smrg if (!(x % pBankFormat->bitsPerPixel)) 12666747b715Smrg continue; 12676747b715Smrg 126835c4bbdfSmrg if (x < minBitsPerScanline) { 12696747b715Smrg /* 12706747b715Smrg * Skip ahead certain widths by dividing the excess scanline 12716747b715Smrg * amongst the y's. 12726747b715Smrg */ 12736747b715Smrg y *= nBitsPerScanlinePadUnit; 127435c4bbdfSmrg nBitsPerScanline += ((x + y - 1) / y) * nBitsPerScanlinePadUnit; 12756747b715Smrg width = nBitsPerScanline / pBankFormat->bitsPerPixel; 12766747b715Smrg break; 12776747b715Smrg } 12786747b715Smrg 12796747b715Smrg if (BankBase != BankUnit) 12806747b715Smrg continue; 12816747b715Smrg 12826747b715Smrg if (!(nBitsPerScanline % x)) 128335c4bbdfSmrg return (int) width; 12846747b715Smrg 12856747b715Smrg BankBase = ((nBitsPerScanline - minBitsPerScanline) / 128635c4bbdfSmrg (nBitsPerScanline - x)) * BankUnit; 12876747b715Smrg } 12886747b715Smrg } 12896747b715Smrg 12906747b715Smrg return -1; 12916747b715Smrg} 12926747b715Smrg 129305b261ecSmrg/* 129405b261ecSmrg * xf86ValidateModes 129505b261ecSmrg * 129605b261ecSmrg * This function takes a set of mode names, modes and limiting conditions, 129705b261ecSmrg * and selects a set of modes and parameters based on those conditions. 129805b261ecSmrg * 129905b261ecSmrg * This function takes the following parameters: 130005b261ecSmrg * scrp ScrnInfoPtr 130105b261ecSmrg * availModes the list of modes available for the monitor 130205b261ecSmrg * modeNames (optional) list of mode names that the screen is requesting 130305b261ecSmrg * clockRanges a list of clock ranges 130405b261ecSmrg * linePitches (optional) a list of line pitches 130505b261ecSmrg * minPitch (optional) minimum line pitch (in pixels) 130605b261ecSmrg * maxPitch (optional) maximum line pitch (in pixels) 130705b261ecSmrg * pitchInc (mandatory) pitch increment (in bits) 130805b261ecSmrg * minHeight (optional) minimum virtual height (in pixels) 130905b261ecSmrg * maxHeight (optional) maximum virtual height (in pixels) 131005b261ecSmrg * virtualX (optional) virtual width requested (in pixels) 131105b261ecSmrg * virtualY (optional) virtual height requested (in pixels) 131205b261ecSmrg * apertureSize size of video aperture (in bytes) 131305b261ecSmrg * strategy how to decide which mode to use from multiple modes with 131405b261ecSmrg * the same name 131505b261ecSmrg * 131605b261ecSmrg * In addition, the following fields from the ScrnInfoRec are used: 131705b261ecSmrg * clocks a list of discrete clocks 131805b261ecSmrg * numClocks number of discrete clocks 131905b261ecSmrg * progClock clock is programmable 132005b261ecSmrg * monitor pointer to structure for monitor section 132105b261ecSmrg * fbFormat format of the framebuffer 132205b261ecSmrg * videoRam video memory size 132305b261ecSmrg * xInc horizontal timing increment (defaults to 8 pixels) 132405b261ecSmrg * 132505b261ecSmrg * The function fills in the following ScrnInfoRec fields: 132605b261ecSmrg * modePool A subset of the modes available to the monitor which 132705b261ecSmrg * are compatible with the driver. 132805b261ecSmrg * modes one mode entry for each of the requested modes, with the 132905b261ecSmrg * status field filled in to indicate if the mode has been 133005b261ecSmrg * accepted or not. 133105b261ecSmrg * virtualX the resulting virtual width 133205b261ecSmrg * virtualY the resulting virtual height 133305b261ecSmrg * displayWidth the resulting line pitch 133405b261ecSmrg * 133505b261ecSmrg * The function's return value is the number of matching modes found, or -1 133605b261ecSmrg * if an unrecoverable error was encountered. 133705b261ecSmrg */ 133805b261ecSmrg 13396747b715Smrgint 134005b261ecSmrgxf86ValidateModes(ScrnInfoPtr scrp, DisplayModePtr availModes, 134135c4bbdfSmrg const char **modeNames, ClockRangePtr clockRanges, 134235c4bbdfSmrg int *linePitches, int minPitch, int maxPitch, int pitchInc, 134335c4bbdfSmrg int minHeight, int maxHeight, int virtualX, int virtualY, 134435c4bbdfSmrg int apertureSize, LookupModeFlags strategy) 134505b261ecSmrg{ 134605b261ecSmrg DisplayModePtr p, q, r, new, last, *endp; 134705b261ecSmrg int i, numModes = 0; 134805b261ecSmrg ModeStatus status; 134905b261ecSmrg int linePitch = -1, virtX = 0, virtY = 0; 135005b261ecSmrg int newLinePitch, newVirtX, newVirtY; 135135c4bbdfSmrg int modeSize; /* in pixels */ 135205b261ecSmrg Bool validateAllDefaultModes = FALSE; 135305b261ecSmrg Bool userModes = FALSE; 135405b261ecSmrg int saveType; 135505b261ecSmrg PixmapFormatRec *BankFormat; 135605b261ecSmrg ClockRangePtr cp; 135705b261ecSmrg int numTimings = 0; 135805b261ecSmrg range hsync[MAX_HSYNC]; 135905b261ecSmrg range vrefresh[MAX_VREFRESH]; 136005b261ecSmrg Bool inferred_virtual = FALSE; 136105b261ecSmrg 136235c4bbdfSmrg DebugF 136335c4bbdfSmrg ("xf86ValidateModes(%p, %p, %p, %p,\n\t\t %p, %d, %d, %d, %d, %d, %d, %d, %d, 0x%x)\n", 136435c4bbdfSmrg scrp, availModes, modeNames, clockRanges, linePitches, minPitch, 136535c4bbdfSmrg maxPitch, pitchInc, minHeight, maxHeight, virtualX, virtualY, 136635c4bbdfSmrg apertureSize, strategy); 136705b261ecSmrg 136805b261ecSmrg /* Some sanity checking */ 136905b261ecSmrg if (scrp == NULL || scrp->name == NULL || !scrp->monitor || 137035c4bbdfSmrg (!scrp->progClock && scrp->numClocks == 0)) { 137135c4bbdfSmrg ErrorF("xf86ValidateModes: called with invalid scrnInfoRec\n"); 137235c4bbdfSmrg return -1; 137305b261ecSmrg } 137405b261ecSmrg if (linePitches != NULL && linePitches[0] <= 0) { 137535c4bbdfSmrg ErrorF("xf86ValidateModes: called with invalid linePitches\n"); 137635c4bbdfSmrg return -1; 137705b261ecSmrg } 137805b261ecSmrg if (pitchInc <= 0) { 137935c4bbdfSmrg ErrorF("xf86ValidateModes: called with invalid pitchInc\n"); 138035c4bbdfSmrg return -1; 138105b261ecSmrg } 138205b261ecSmrg if ((virtualX > 0) != (virtualY > 0)) { 138335c4bbdfSmrg ErrorF("xf86ValidateModes: called with invalid virtual resolution\n"); 138435c4bbdfSmrg return -1; 138505b261ecSmrg } 138605b261ecSmrg 138705b261ecSmrg /* 138805b261ecSmrg * If requested by the driver, allow missing hsync and/or vrefresh ranges 138905b261ecSmrg * in the monitor section. 139005b261ecSmrg */ 139105b261ecSmrg if (strategy & LOOKUP_OPTIONAL_TOLERANCES) { 139235c4bbdfSmrg strategy &= ~LOOKUP_OPTIONAL_TOLERANCES; 139335c4bbdfSmrg } 139435c4bbdfSmrg else { 139535c4bbdfSmrg const char *type = ""; 13969ace9065Smrg Bool specified = FALSE; 139705b261ecSmrg 139835c4bbdfSmrg if (scrp->monitor->nHsync <= 0) { 139935c4bbdfSmrg if (numTimings > 0) { 140035c4bbdfSmrg scrp->monitor->nHsync = numTimings; 140135c4bbdfSmrg for (i = 0; i < numTimings; i++) { 140235c4bbdfSmrg scrp->monitor->hsync[i].lo = hsync[i].lo; 140335c4bbdfSmrg scrp->monitor->hsync[i].hi = hsync[i].hi; 140435c4bbdfSmrg } 140535c4bbdfSmrg } 140635c4bbdfSmrg else { 140735c4bbdfSmrg scrp->monitor->hsync[0].lo = 31.5; 140835c4bbdfSmrg scrp->monitor->hsync[0].hi = 48.0; 140935c4bbdfSmrg scrp->monitor->nHsync = 1; 141035c4bbdfSmrg } 141135c4bbdfSmrg type = "default "; 141235c4bbdfSmrg } 141335c4bbdfSmrg else { 14149ace9065Smrg specified = TRUE; 14159ace9065Smrg } 141635c4bbdfSmrg for (i = 0; i < scrp->monitor->nHsync; i++) { 141735c4bbdfSmrg if (scrp->monitor->hsync[i].lo == scrp->monitor->hsync[i].hi) 141835c4bbdfSmrg xf86DrvMsg(scrp->scrnIndex, X_INFO, 141935c4bbdfSmrg "%s: Using %shsync value of %.2f kHz\n", 142035c4bbdfSmrg scrp->monitor->id, type, scrp->monitor->hsync[i].lo); 142135c4bbdfSmrg else 142235c4bbdfSmrg xf86DrvMsg(scrp->scrnIndex, X_INFO, 142335c4bbdfSmrg "%s: Using %shsync range of %.2f-%.2f kHz\n", 142435c4bbdfSmrg scrp->monitor->id, type, 142535c4bbdfSmrg scrp->monitor->hsync[i].lo, 142635c4bbdfSmrg scrp->monitor->hsync[i].hi); 142735c4bbdfSmrg } 142835c4bbdfSmrg 142935c4bbdfSmrg type = ""; 143035c4bbdfSmrg if (scrp->monitor->nVrefresh <= 0) { 143135c4bbdfSmrg if (numTimings > 0) { 143235c4bbdfSmrg scrp->monitor->nVrefresh = numTimings; 143335c4bbdfSmrg for (i = 0; i < numTimings; i++) { 143435c4bbdfSmrg scrp->monitor->vrefresh[i].lo = vrefresh[i].lo; 143535c4bbdfSmrg scrp->monitor->vrefresh[i].hi = vrefresh[i].hi; 143635c4bbdfSmrg } 143735c4bbdfSmrg } 143835c4bbdfSmrg else { 143935c4bbdfSmrg scrp->monitor->vrefresh[0].lo = 50; 144035c4bbdfSmrg scrp->monitor->vrefresh[0].hi = 70; 144135c4bbdfSmrg scrp->monitor->nVrefresh = 1; 144235c4bbdfSmrg } 144335c4bbdfSmrg type = "default "; 144435c4bbdfSmrg } 144535c4bbdfSmrg else { 14469ace9065Smrg specified = TRUE; 14479ace9065Smrg } 144835c4bbdfSmrg for (i = 0; i < scrp->monitor->nVrefresh; i++) { 144935c4bbdfSmrg if (scrp->monitor->vrefresh[i].lo == scrp->monitor->vrefresh[i].hi) 145035c4bbdfSmrg xf86DrvMsg(scrp->scrnIndex, X_INFO, 145135c4bbdfSmrg "%s: Using %svrefresh value of %.2f Hz\n", 145235c4bbdfSmrg scrp->monitor->id, type, 145335c4bbdfSmrg scrp->monitor->vrefresh[i].lo); 145435c4bbdfSmrg else 145535c4bbdfSmrg xf86DrvMsg(scrp->scrnIndex, X_INFO, 145635c4bbdfSmrg "%s: Using %svrefresh range of %.2f-%.2f Hz\n", 145735c4bbdfSmrg scrp->monitor->id, type, 145835c4bbdfSmrg scrp->monitor->vrefresh[i].lo, 145935c4bbdfSmrg scrp->monitor->vrefresh[i].hi); 146035c4bbdfSmrg } 14619ace9065Smrg 14629ace9065Smrg type = ""; 146335c4bbdfSmrg if (!scrp->monitor->maxPixClock && !specified) { 14649ace9065Smrg type = "default "; 14659ace9065Smrg scrp->monitor->maxPixClock = 65000.0; 14669ace9065Smrg } 146735c4bbdfSmrg if (scrp->monitor->maxPixClock) { 146835c4bbdfSmrg xf86DrvMsg(scrp->scrnIndex, X_INFO, 146935c4bbdfSmrg "%s: Using %smaximum pixel clock of %.2f MHz\n", 147035c4bbdfSmrg scrp->monitor->id, type, 147135c4bbdfSmrg (float) scrp->monitor->maxPixClock / 1000.0); 147235c4bbdfSmrg } 147305b261ecSmrg } 147405b261ecSmrg 147505b261ecSmrg /* 14766747b715Smrg * Store the clockRanges for later use by the VidMode extension. 147705b261ecSmrg */ 147835c4bbdfSmrg nt_list_for_each_entry(cp, clockRanges, next) { 147935c4bbdfSmrg ClockRangePtr newCR = xnfalloc(sizeof(ClockRange)); 148035c4bbdfSmrg memcpy(newCR, cp, sizeof(ClockRange)); 148135c4bbdfSmrg newCR->next = NULL; 148235c4bbdfSmrg if (scrp->clockRanges == NULL) 148335c4bbdfSmrg scrp->clockRanges = newCR; 148435c4bbdfSmrg else 148535c4bbdfSmrg nt_list_append(newCR, scrp->clockRanges, ClockRange, next); 148605b261ecSmrg } 148705b261ecSmrg 14886747b715Smrg /* Determine which pixmap format to pass to scanLineWidth() */ 148905b261ecSmrg if (scrp->depth > 4) 149035c4bbdfSmrg BankFormat = &scrp->fbFormat; 149105b261ecSmrg else 149235c4bbdfSmrg BankFormat = xf86GetPixFormat(scrp, 1); /* >not< scrp->depth! */ 149305b261ecSmrg 149405b261ecSmrg if (scrp->xInc <= 0) 149535c4bbdfSmrg scrp->xInc = 8; /* Suitable for VGA and others */ 149605b261ecSmrg 149705b261ecSmrg#define _VIRTUALX(x) ((((x) + scrp->xInc - 1) / scrp->xInc) * scrp->xInc) 149805b261ecSmrg 149905b261ecSmrg /* 150005b261ecSmrg * Determine maxPitch if it wasn't given explicitly. Note linePitches 150105b261ecSmrg * always takes precedence if is non-NULL. In that case the minPitch and 150205b261ecSmrg * maxPitch values passed are ignored. 150305b261ecSmrg */ 150405b261ecSmrg if (linePitches) { 150535c4bbdfSmrg minPitch = maxPitch = linePitches[0]; 150635c4bbdfSmrg for (i = 1; linePitches[i] > 0; i++) { 150735c4bbdfSmrg if (linePitches[i] > maxPitch) 150835c4bbdfSmrg maxPitch = linePitches[i]; 150935c4bbdfSmrg if (linePitches[i] < minPitch) 151035c4bbdfSmrg minPitch = linePitches[i]; 151135c4bbdfSmrg } 151205b261ecSmrg } 151305b261ecSmrg 151405b261ecSmrg /* 151505b261ecSmrg * Initialise virtX and virtY if the values are fixed. 151605b261ecSmrg */ 151705b261ecSmrg if (virtualY > 0) { 151835c4bbdfSmrg if (maxHeight > 0 && virtualY > maxHeight) { 151935c4bbdfSmrg xf86DrvMsg(scrp->scrnIndex, X_ERROR, 152035c4bbdfSmrg "Virtual height (%d) is too large for the hardware " 152135c4bbdfSmrg "(max %d)\n", virtualY, maxHeight); 152235c4bbdfSmrg return -1; 152335c4bbdfSmrg } 152435c4bbdfSmrg 152535c4bbdfSmrg if (minHeight > 0 && virtualY < minHeight) { 152635c4bbdfSmrg xf86DrvMsg(scrp->scrnIndex, X_ERROR, 152735c4bbdfSmrg "Virtual height (%d) is too small for the hardware " 152835c4bbdfSmrg "(min %d)\n", virtualY, minHeight); 152935c4bbdfSmrg return -1; 153035c4bbdfSmrg } 153135c4bbdfSmrg 153235c4bbdfSmrg virtualX = _VIRTUALX(virtualX); 153335c4bbdfSmrg if (linePitches != NULL) { 153435c4bbdfSmrg for (i = 0; linePitches[i] != 0; i++) { 153535c4bbdfSmrg if ((linePitches[i] >= virtualX) && 153635c4bbdfSmrg (linePitches[i] == 153735c4bbdfSmrg scanLineWidth(virtualX, virtualY, linePitches[i], 153835c4bbdfSmrg apertureSize, BankFormat, pitchInc))) { 153935c4bbdfSmrg linePitch = linePitches[i]; 154035c4bbdfSmrg break; 154135c4bbdfSmrg } 154235c4bbdfSmrg } 154335c4bbdfSmrg } 154435c4bbdfSmrg else { 154535c4bbdfSmrg linePitch = scanLineWidth(virtualX, virtualY, minPitch, 154635c4bbdfSmrg apertureSize, BankFormat, pitchInc); 154735c4bbdfSmrg } 154835c4bbdfSmrg 154935c4bbdfSmrg if ((linePitch < minPitch) || (linePitch > maxPitch)) { 155035c4bbdfSmrg xf86DrvMsg(scrp->scrnIndex, X_ERROR, 155135c4bbdfSmrg "Virtual width (%d) is too large for the hardware " 155235c4bbdfSmrg "(max %d)\n", virtualX, maxPitch); 155335c4bbdfSmrg return -1; 155435c4bbdfSmrg } 155535c4bbdfSmrg 155635c4bbdfSmrg if (!xf86CheckModeSize(scrp, linePitch, virtualX, virtualY)) { 155735c4bbdfSmrg xf86DrvMsg(scrp->scrnIndex, X_ERROR, 155835c4bbdfSmrg "Virtual size (%dx%d) (pitch %d) exceeds video memory\n", 155935c4bbdfSmrg virtualX, virtualY, linePitch); 156035c4bbdfSmrg return -1; 156135c4bbdfSmrg } 156235c4bbdfSmrg 156335c4bbdfSmrg virtX = virtualX; 156435c4bbdfSmrg virtY = virtualY; 156535c4bbdfSmrg } 156635c4bbdfSmrg else if (!modeNames || !*modeNames) { 156735c4bbdfSmrg /* No virtual size given in the config, try to infer */ 156835c4bbdfSmrg /* XXX this doesn't take m{in,ax}Pitch into account; oh well */ 156935c4bbdfSmrg inferred_virtual = inferVirtualSize(scrp, availModes, &virtX, &virtY); 157035c4bbdfSmrg if (inferred_virtual) 157135c4bbdfSmrg linePitch = scanLineWidth(virtX, virtY, minPitch, apertureSize, 157235c4bbdfSmrg BankFormat, pitchInc); 157305b261ecSmrg } 157405b261ecSmrg 157505b261ecSmrg /* Print clock ranges and scaled clocks */ 157605b261ecSmrg xf86ShowClockRanges(scrp, clockRanges); 157705b261ecSmrg 157805b261ecSmrg /* 157905b261ecSmrg * If scrp->modePool hasn't been setup yet, set it up now. This allows the 158005b261ecSmrg * modes that the driver definitely can't use to be weeded out early. Note 158105b261ecSmrg * that a modePool mode's prev field is used to hold a pointer to the 158205b261ecSmrg * member of the scrp->modes list for which a match was considered. 158305b261ecSmrg */ 158405b261ecSmrg if (scrp->modePool == NULL) { 158535c4bbdfSmrg q = NULL; 158635c4bbdfSmrg for (p = availModes; p != NULL; p = p->next) { 158735c4bbdfSmrg status = xf86InitialCheckModeForDriver(scrp, p, clockRanges, 158835c4bbdfSmrg strategy, maxPitch, 158935c4bbdfSmrg virtX, virtY); 159035c4bbdfSmrg 159135c4bbdfSmrg if (status == MODE_OK) { 159235c4bbdfSmrg status = xf86CheckModeForMonitor(p, scrp->monitor); 159335c4bbdfSmrg } 159435c4bbdfSmrg 159535c4bbdfSmrg if (status == MODE_OK) { 159635c4bbdfSmrg new = xnfalloc(sizeof(DisplayModeRec)); 159735c4bbdfSmrg *new = *p; 159835c4bbdfSmrg new->next = NULL; 159935c4bbdfSmrg if (!q) { 160035c4bbdfSmrg scrp->modePool = new; 160135c4bbdfSmrg } 160235c4bbdfSmrg else { 160335c4bbdfSmrg q->next = new; 160435c4bbdfSmrg } 160535c4bbdfSmrg new->prev = NULL; 160635c4bbdfSmrg q = new; 160735c4bbdfSmrg q->name = xnfstrdup(p->name); 160835c4bbdfSmrg q->status = MODE_OK; 160935c4bbdfSmrg } 161035c4bbdfSmrg else { 161135c4bbdfSmrg printModeRejectMessage(scrp->scrnIndex, p, status); 161235c4bbdfSmrg } 161335c4bbdfSmrg } 161435c4bbdfSmrg 161535c4bbdfSmrg if (scrp->modePool == NULL) { 161635c4bbdfSmrg xf86DrvMsg(scrp->scrnIndex, X_WARNING, "Mode pool is empty\n"); 161735c4bbdfSmrg return 0; 161835c4bbdfSmrg } 161935c4bbdfSmrg } 162035c4bbdfSmrg else { 162135c4bbdfSmrg for (p = scrp->modePool; p != NULL; p = p->next) { 162235c4bbdfSmrg p->prev = NULL; 162335c4bbdfSmrg p->status = MODE_OK; 162435c4bbdfSmrg } 162505b261ecSmrg } 162605b261ecSmrg 162705b261ecSmrg /* 162805b261ecSmrg * Allocate one entry in scrp->modes for each named mode. 162905b261ecSmrg */ 163005b261ecSmrg while (scrp->modes) 163135c4bbdfSmrg xf86DeleteMode(&scrp->modes, scrp->modes); 163205b261ecSmrg endp = &scrp->modes; 163305b261ecSmrg last = NULL; 163405b261ecSmrg if (modeNames != NULL) { 163535c4bbdfSmrg for (i = 0; modeNames[i] != NULL; i++) { 163635c4bbdfSmrg userModes = TRUE; 163735c4bbdfSmrg new = xnfcalloc(1, sizeof(DisplayModeRec)); 163835c4bbdfSmrg new->prev = last; 163935c4bbdfSmrg new->type = M_T_USERDEF; 164035c4bbdfSmrg new->name = xnfstrdup(modeNames[i]); 164135c4bbdfSmrg if (new->prev) 164235c4bbdfSmrg new->prev->next = new; 164335c4bbdfSmrg *endp = last = new; 164435c4bbdfSmrg endp = &new->next; 164535c4bbdfSmrg } 164605b261ecSmrg } 164705b261ecSmrg 164805b261ecSmrg /* Lookup each mode */ 164905b261ecSmrg#ifdef PANORAMIX 16501b5d61b8Smrg if (noPanoramiXExtension) 165135c4bbdfSmrg validateAllDefaultModes = TRUE; 165205b261ecSmrg#endif 165305b261ecSmrg 165435c4bbdfSmrg for (p = scrp->modes;; p = p->next) { 165535c4bbdfSmrg Bool repeat; 165635c4bbdfSmrg 165735c4bbdfSmrg /* 165835c4bbdfSmrg * If the supplied mode names don't produce a valid mode, scan through 165935c4bbdfSmrg * unconsidered modePool members until one survives validation. This 166035c4bbdfSmrg * is done in decreasing order by mode pixel area. 166135c4bbdfSmrg */ 166235c4bbdfSmrg 166335c4bbdfSmrg if (p == NULL) { 166435c4bbdfSmrg if ((numModes > 0) && !validateAllDefaultModes) 166535c4bbdfSmrg break; 166635c4bbdfSmrg 166735c4bbdfSmrg validateAllDefaultModes = TRUE; 166835c4bbdfSmrg r = NULL; 166935c4bbdfSmrg modeSize = 0; 167035c4bbdfSmrg for (q = scrp->modePool; q != NULL; q = q->next) { 167135c4bbdfSmrg if ((q->prev == NULL) && (q->status == MODE_OK)) { 167235c4bbdfSmrg /* 167335c4bbdfSmrg * Deal with the case where this mode wasn't considered 167435c4bbdfSmrg * because of a builtin mode of the same name. 167535c4bbdfSmrg */ 167635c4bbdfSmrg for (p = scrp->modes; p != NULL; p = p->next) { 167735c4bbdfSmrg if ((p->status != MODE_OK) && !strcmp(p->name, q->name)) 167835c4bbdfSmrg break; 167935c4bbdfSmrg } 168035c4bbdfSmrg 168135c4bbdfSmrg if (p != NULL) 168235c4bbdfSmrg q->prev = p; 168335c4bbdfSmrg else { 168435c4bbdfSmrg /* 168535c4bbdfSmrg * A quick check to not allow default modes with 168635c4bbdfSmrg * horizontal timing parameters that CRTs may have 168735c4bbdfSmrg * problems with. 168835c4bbdfSmrg */ 168935c4bbdfSmrg if (!scrp->monitor->reducedblanking && 169035c4bbdfSmrg (q->type & M_T_DEFAULT) && 169135c4bbdfSmrg ((double) q->HTotal / (double) q->HDisplay) < 1.15) 169235c4bbdfSmrg continue; 169335c4bbdfSmrg 169435c4bbdfSmrg if (modeSize < (q->HDisplay * q->VDisplay)) { 169535c4bbdfSmrg r = q; 169635c4bbdfSmrg modeSize = q->HDisplay * q->VDisplay; 169735c4bbdfSmrg } 169835c4bbdfSmrg } 169935c4bbdfSmrg } 170035c4bbdfSmrg } 170135c4bbdfSmrg 170235c4bbdfSmrg if (r == NULL) 170335c4bbdfSmrg break; 170435c4bbdfSmrg 170535c4bbdfSmrg p = xnfcalloc(1, sizeof(DisplayModeRec)); 170635c4bbdfSmrg p->prev = last; 170735c4bbdfSmrg p->name = xnfstrdup(r->name); 170835c4bbdfSmrg if (!userModes) 170935c4bbdfSmrg p->type = M_T_USERDEF; 171035c4bbdfSmrg if (p->prev) 171135c4bbdfSmrg p->prev->next = p; 171235c4bbdfSmrg *endp = last = p; 171335c4bbdfSmrg endp = &p->next; 171435c4bbdfSmrg } 171535c4bbdfSmrg 171635c4bbdfSmrg repeat = FALSE; 171735c4bbdfSmrg lookupNext: 171835c4bbdfSmrg if (repeat && ((status = p->status) != MODE_OK)) 171935c4bbdfSmrg printModeRejectMessage(scrp->scrnIndex, p, status); 172035c4bbdfSmrg saveType = p->type; 172135c4bbdfSmrg status = xf86LookupMode(scrp, p, clockRanges, strategy); 172235c4bbdfSmrg if (repeat && status == MODE_NOMODE) 172335c4bbdfSmrg continue; 172435c4bbdfSmrg if (status != MODE_OK) 172535c4bbdfSmrg printModeRejectMessage(scrp->scrnIndex, p, status); 172635c4bbdfSmrg if (status == MODE_ERROR) { 172735c4bbdfSmrg ErrorF("xf86ValidateModes: " 172835c4bbdfSmrg "unexpected result from xf86LookupMode()\n"); 172935c4bbdfSmrg return -1; 173035c4bbdfSmrg } 173135c4bbdfSmrg if (status != MODE_OK) { 173235c4bbdfSmrg if (p->status == MODE_OK) 173335c4bbdfSmrg p->status = status; 173435c4bbdfSmrg continue; 173535c4bbdfSmrg } 173635c4bbdfSmrg p->type |= saveType; 173735c4bbdfSmrg repeat = TRUE; 173835c4bbdfSmrg 173935c4bbdfSmrg newLinePitch = linePitch; 174035c4bbdfSmrg newVirtX = virtX; 174135c4bbdfSmrg newVirtY = virtY; 174235c4bbdfSmrg 174335c4bbdfSmrg /* 174435c4bbdfSmrg * Don't let non-user defined modes increase the virtual size 174535c4bbdfSmrg */ 174635c4bbdfSmrg if (!(p->type & M_T_USERDEF) && (numModes > 0)) { 174735c4bbdfSmrg if (p->HDisplay > virtX) { 174835c4bbdfSmrg p->status = MODE_VIRTUAL_X; 174935c4bbdfSmrg goto lookupNext; 175035c4bbdfSmrg } 175135c4bbdfSmrg if (p->VDisplay > virtY) { 175235c4bbdfSmrg p->status = MODE_VIRTUAL_Y; 175335c4bbdfSmrg goto lookupNext; 175435c4bbdfSmrg } 175535c4bbdfSmrg } 175635c4bbdfSmrg /* 175735c4bbdfSmrg * Adjust virtual width and height if the mode is too large for the 175835c4bbdfSmrg * current values and if they are not fixed. 175935c4bbdfSmrg */ 176035c4bbdfSmrg if (virtualX <= 0 && p->HDisplay > newVirtX) 176135c4bbdfSmrg newVirtX = _VIRTUALX(p->HDisplay); 176235c4bbdfSmrg if (virtualY <= 0 && p->VDisplay > newVirtY) { 176335c4bbdfSmrg if (maxHeight > 0 && p->VDisplay > maxHeight) { 176435c4bbdfSmrg p->status = MODE_VIRTUAL_Y; /* ? */ 176535c4bbdfSmrg goto lookupNext; 176635c4bbdfSmrg } 176735c4bbdfSmrg newVirtY = p->VDisplay; 176835c4bbdfSmrg } 176935c4bbdfSmrg 177035c4bbdfSmrg /* 177135c4bbdfSmrg * If virtual resolution is to be increased, revalidate it. 177235c4bbdfSmrg */ 177335c4bbdfSmrg if ((virtX != newVirtX) || (virtY != newVirtY)) { 177435c4bbdfSmrg if (linePitches != NULL) { 177535c4bbdfSmrg newLinePitch = -1; 177635c4bbdfSmrg for (i = 0; linePitches[i] != 0; i++) { 177735c4bbdfSmrg if ((linePitches[i] >= newVirtX) && 177835c4bbdfSmrg (linePitches[i] >= linePitch) && 177935c4bbdfSmrg (linePitches[i] == 178035c4bbdfSmrg scanLineWidth(newVirtX, newVirtY, linePitches[i], 178135c4bbdfSmrg apertureSize, BankFormat, pitchInc))) { 178235c4bbdfSmrg newLinePitch = linePitches[i]; 178335c4bbdfSmrg break; 178435c4bbdfSmrg } 178535c4bbdfSmrg } 178635c4bbdfSmrg } 178735c4bbdfSmrg else { 178835c4bbdfSmrg if (linePitch < minPitch) 178935c4bbdfSmrg linePitch = minPitch; 179035c4bbdfSmrg newLinePitch = scanLineWidth(newVirtX, newVirtY, linePitch, 179135c4bbdfSmrg apertureSize, BankFormat, 179235c4bbdfSmrg pitchInc); 179335c4bbdfSmrg } 179435c4bbdfSmrg if ((newLinePitch < minPitch) || (newLinePitch > maxPitch)) { 179535c4bbdfSmrg p->status = MODE_BAD_WIDTH; 179635c4bbdfSmrg goto lookupNext; 179735c4bbdfSmrg } 179835c4bbdfSmrg 179935c4bbdfSmrg /* 180035c4bbdfSmrg * Check that the pixel area required by the new virtual height 180135c4bbdfSmrg * and line pitch isn't too large. 180235c4bbdfSmrg */ 180335c4bbdfSmrg if (!xf86CheckModeSize(scrp, newLinePitch, newVirtX, newVirtY)) { 180435c4bbdfSmrg p->status = MODE_MEM_VIRT; 180535c4bbdfSmrg goto lookupNext; 180635c4bbdfSmrg } 180735c4bbdfSmrg } 180835c4bbdfSmrg 180935c4bbdfSmrg if (scrp->ValidMode) { 181035c4bbdfSmrg /* 181135c4bbdfSmrg * Give the driver a final say, passing it the proposed virtual 181235c4bbdfSmrg * geometry. 181335c4bbdfSmrg */ 181435c4bbdfSmrg scrp->virtualX = newVirtX; 181535c4bbdfSmrg scrp->virtualY = newVirtY; 181635c4bbdfSmrg scrp->displayWidth = newLinePitch; 181735c4bbdfSmrg p->status = (scrp->ValidMode) (scrp, p, FALSE, 181835c4bbdfSmrg MODECHECK_FINAL); 181935c4bbdfSmrg 182035c4bbdfSmrg if (p->status != MODE_OK) { 182135c4bbdfSmrg goto lookupNext; 182235c4bbdfSmrg } 182335c4bbdfSmrg } 182435c4bbdfSmrg 182535c4bbdfSmrg /* Mode has passed all the tests */ 182635c4bbdfSmrg virtX = newVirtX; 182735c4bbdfSmrg virtY = newVirtY; 182835c4bbdfSmrg linePitch = newLinePitch; 182935c4bbdfSmrg p->status = MODE_OK; 183035c4bbdfSmrg numModes++; 183105b261ecSmrg } 183205b261ecSmrg 183305b261ecSmrg /* 183405b261ecSmrg * If we estimated the virtual size above, we may have filtered away all 183505b261ecSmrg * the modes that maximally match that size; scan again to find out and 183605b261ecSmrg * fix up if so. 183705b261ecSmrg */ 183805b261ecSmrg if (inferred_virtual) { 183935c4bbdfSmrg int vx = 0, vy = 0; 184035c4bbdfSmrg 184135c4bbdfSmrg for (p = scrp->modes; p; p = p->next) { 184235c4bbdfSmrg if (p->HDisplay > vx && p->VDisplay > vy) { 184335c4bbdfSmrg vx = p->HDisplay; 184435c4bbdfSmrg vy = p->VDisplay; 184535c4bbdfSmrg } 184635c4bbdfSmrg } 184735c4bbdfSmrg if (vx < virtX || vy < virtY) { 184835c4bbdfSmrg const int types[] = { 184935c4bbdfSmrg M_T_BUILTIN | M_T_PREFERRED, 185035c4bbdfSmrg M_T_BUILTIN, 185135c4bbdfSmrg M_T_DRIVER | M_T_PREFERRED, 185235c4bbdfSmrg M_T_DRIVER, 185335c4bbdfSmrg 0 185435c4bbdfSmrg }; 18551b5d61b8Smrg const int ntypes = ARRAY_SIZE(types); 185635c4bbdfSmrg int n; 185735c4bbdfSmrg 185835c4bbdfSmrg /* 185935c4bbdfSmrg * We did not find the estimated virtual size. So now we want to 186035c4bbdfSmrg * find the largest mode available, but we want to search in the 186135c4bbdfSmrg * modes in the order of "types" listed above. 186235c4bbdfSmrg */ 186335c4bbdfSmrg for (n = 0; n < ntypes; n++) { 186435c4bbdfSmrg int type = types[n]; 186535c4bbdfSmrg 186635c4bbdfSmrg vx = 0; 186735c4bbdfSmrg vy = 0; 186835c4bbdfSmrg for (p = scrp->modes; p; p = p->next) { 186935c4bbdfSmrg /* scan through the modes in the sort order above */ 187035c4bbdfSmrg if ((p->type & type) != type) 187135c4bbdfSmrg continue; 187235c4bbdfSmrg if (p->HDisplay > vx && p->VDisplay > vy) { 187335c4bbdfSmrg vx = p->HDisplay; 187435c4bbdfSmrg vy = p->VDisplay; 187535c4bbdfSmrg } 187635c4bbdfSmrg } 187735c4bbdfSmrg if (vx && vy) 187835c4bbdfSmrg /* Found one */ 187935c4bbdfSmrg break; 188035c4bbdfSmrg } 188135c4bbdfSmrg xf86DrvMsg(scrp->scrnIndex, X_WARNING, 188235c4bbdfSmrg "Shrinking virtual size estimate from %dx%d to %dx%d\n", 188335c4bbdfSmrg virtX, virtY, vx, vy); 188435c4bbdfSmrg virtX = _VIRTUALX(vx); 188535c4bbdfSmrg virtY = vy; 188635c4bbdfSmrg for (p = scrp->modes; p; p = p->next) { 188735c4bbdfSmrg if (numModes > 0) { 188835c4bbdfSmrg if (p->HDisplay > virtX) 188935c4bbdfSmrg p->status = MODE_VIRTUAL_X; 189035c4bbdfSmrg if (p->VDisplay > virtY) 189135c4bbdfSmrg p->status = MODE_VIRTUAL_Y; 189235c4bbdfSmrg if (p->status != MODE_OK) { 189335c4bbdfSmrg numModes--; 189435c4bbdfSmrg printModeRejectMessage(scrp->scrnIndex, p, p->status); 189535c4bbdfSmrg } 189635c4bbdfSmrg } 189735c4bbdfSmrg } 189835c4bbdfSmrg if (linePitches != NULL) { 189935c4bbdfSmrg for (i = 0; linePitches[i] != 0; i++) { 190035c4bbdfSmrg if ((linePitches[i] >= virtX) && 190135c4bbdfSmrg (linePitches[i] == 190235c4bbdfSmrg scanLineWidth(virtX, virtY, linePitches[i], 190335c4bbdfSmrg apertureSize, BankFormat, pitchInc))) { 190435c4bbdfSmrg linePitch = linePitches[i]; 190535c4bbdfSmrg break; 190635c4bbdfSmrg } 190735c4bbdfSmrg } 190835c4bbdfSmrg } 190935c4bbdfSmrg else { 191035c4bbdfSmrg linePitch = scanLineWidth(virtX, virtY, minPitch, 191135c4bbdfSmrg apertureSize, BankFormat, pitchInc); 191235c4bbdfSmrg } 191335c4bbdfSmrg } 191405b261ecSmrg } 191505b261ecSmrg 191605b261ecSmrg /* Update the ScrnInfoRec parameters */ 191735c4bbdfSmrg 191805b261ecSmrg scrp->virtualX = virtX; 191905b261ecSmrg scrp->virtualY = virtY; 192005b261ecSmrg scrp->displayWidth = linePitch; 192105b261ecSmrg 192205b261ecSmrg if (numModes <= 0) 192335c4bbdfSmrg return 0; 192435c4bbdfSmrg 192505b261ecSmrg /* Make the mode list into a circular list by joining up the ends */ 192605b261ecSmrg p = scrp->modes; 192705b261ecSmrg while (p->next != NULL) 192835c4bbdfSmrg p = p->next; 192905b261ecSmrg /* p is now the last mode on the list */ 193005b261ecSmrg p->next = scrp->modes; 193105b261ecSmrg scrp->modes->prev = p; 193205b261ecSmrg 193305b261ecSmrg if (minHeight > 0 && virtY < minHeight) { 193435c4bbdfSmrg xf86DrvMsg(scrp->scrnIndex, X_ERROR, 193535c4bbdfSmrg "Virtual height (%d) is too small for the hardware " 193635c4bbdfSmrg "(min %d)\n", virtY, minHeight); 193735c4bbdfSmrg return -1; 193805b261ecSmrg } 193905b261ecSmrg 194005b261ecSmrg return numModes; 194105b261ecSmrg} 194205b261ecSmrg 194305b261ecSmrg/* 194405b261ecSmrg * xf86DeleteMode 194505b261ecSmrg * 194605b261ecSmrg * This function removes a mode from a list of modes. 194705b261ecSmrg * 194805b261ecSmrg * There are different types of mode lists: 194905b261ecSmrg * 195005b261ecSmrg * - singly linked linear lists, ending in NULL 195105b261ecSmrg * - doubly linked linear lists, starting and ending in NULL 195205b261ecSmrg * - doubly linked circular lists 195305b261ecSmrg * 195405b261ecSmrg */ 195535c4bbdfSmrg 19566747b715Smrgvoid 195735c4bbdfSmrgxf86DeleteMode(DisplayModePtr * modeList, DisplayModePtr mode) 195805b261ecSmrg{ 195905b261ecSmrg /* Catch the easy/insane cases */ 196005b261ecSmrg if (modeList == NULL || *modeList == NULL || mode == NULL) 196135c4bbdfSmrg return; 196205b261ecSmrg 196305b261ecSmrg /* If the mode is at the start of the list, move the start of the list */ 196405b261ecSmrg if (*modeList == mode) 196535c4bbdfSmrg *modeList = mode->next; 196605b261ecSmrg 196705b261ecSmrg /* If mode is the only one on the list, set the list to NULL */ 196805b261ecSmrg if ((mode == mode->prev) && (mode == mode->next)) { 196935c4bbdfSmrg *modeList = NULL; 197035c4bbdfSmrg } 197135c4bbdfSmrg else { 197235c4bbdfSmrg if ((mode->prev != NULL) && (mode->prev->next == mode)) 197335c4bbdfSmrg mode->prev->next = mode->next; 197435c4bbdfSmrg if ((mode->next != NULL) && (mode->next->prev == mode)) 197535c4bbdfSmrg mode->next->prev = mode->prev; 197605b261ecSmrg } 197705b261ecSmrg 197835c4bbdfSmrg free((void *) mode->name); 19796747b715Smrg free(mode); 198005b261ecSmrg} 198105b261ecSmrg 198205b261ecSmrg/* 198305b261ecSmrg * xf86PruneDriverModes 198405b261ecSmrg * 198505b261ecSmrg * Remove modes from the driver's mode list which have been marked as 198605b261ecSmrg * invalid. 198705b261ecSmrg */ 198805b261ecSmrg 19896747b715Smrgvoid 199005b261ecSmrgxf86PruneDriverModes(ScrnInfoPtr scrp) 199105b261ecSmrg{ 199205b261ecSmrg DisplayModePtr first, p, n; 199305b261ecSmrg 199405b261ecSmrg p = scrp->modes; 199505b261ecSmrg if (p == NULL) 199635c4bbdfSmrg return; 199705b261ecSmrg 199805b261ecSmrg do { 199935c4bbdfSmrg if (!(first = scrp->modes)) 200035c4bbdfSmrg return; 200135c4bbdfSmrg n = p->next; 200235c4bbdfSmrg if (p->status != MODE_OK) { 200335c4bbdfSmrg xf86DeleteMode(&(scrp->modes), p); 200435c4bbdfSmrg } 200535c4bbdfSmrg p = n; 200605b261ecSmrg } while (p != NULL && p != first); 200705b261ecSmrg 200805b261ecSmrg /* modePool is no longer needed, turf it */ 200905b261ecSmrg while (scrp->modePool) { 201035c4bbdfSmrg /* 201135c4bbdfSmrg * A modePool mode's prev field is used to hold a pointer to the 201235c4bbdfSmrg * member of the scrp->modes list for which a match was considered. 201335c4bbdfSmrg * Clear that pointer first, otherwise xf86DeleteMode might get 201435c4bbdfSmrg * confused 201535c4bbdfSmrg */ 201635c4bbdfSmrg scrp->modePool->prev = NULL; 201735c4bbdfSmrg xf86DeleteMode(&scrp->modePool, scrp->modePool); 201805b261ecSmrg } 201905b261ecSmrg} 202005b261ecSmrg 202105b261ecSmrg/* 202205b261ecSmrg * xf86SetCrtcForModes 202305b261ecSmrg * 202405b261ecSmrg * Goes through the screen's mode list, and initialises the Crtc 202505b261ecSmrg * parameters for each mode. The initialisation includes adjustments 202605b261ecSmrg * for interlaced and double scan modes. 202705b261ecSmrg */ 20286747b715Smrgvoid 202905b261ecSmrgxf86SetCrtcForModes(ScrnInfoPtr scrp, int adjustFlags) 203005b261ecSmrg{ 203105b261ecSmrg DisplayModePtr p; 203205b261ecSmrg 203305b261ecSmrg /* 203405b261ecSmrg * Store adjustFlags for use with the VidMode extension. There is an 203505b261ecSmrg * implicit assumption here that SetCrtcForModes is called once. 203605b261ecSmrg */ 203705b261ecSmrg scrp->adjustFlags = adjustFlags; 203805b261ecSmrg 203905b261ecSmrg p = scrp->modes; 204005b261ecSmrg if (p == NULL) 204135c4bbdfSmrg return; 204205b261ecSmrg 204305b261ecSmrg do { 204435c4bbdfSmrg xf86SetModeCrtc(p, adjustFlags); 204535c4bbdfSmrg DebugF("%sMode %s: %d (%d) %d %d (%d) %d %d (%d) %d %d (%d) %d\n", 204635c4bbdfSmrg (p->type & M_T_DEFAULT) ? "Default " : "", 204735c4bbdfSmrg p->name, p->CrtcHDisplay, p->CrtcHBlankStart, 204835c4bbdfSmrg p->CrtcHSyncStart, p->CrtcHSyncEnd, p->CrtcHBlankEnd, 204935c4bbdfSmrg p->CrtcHTotal, p->CrtcVDisplay, p->CrtcVBlankStart, 205035c4bbdfSmrg p->CrtcVSyncStart, p->CrtcVSyncEnd, p->CrtcVBlankEnd, 205135c4bbdfSmrg p->CrtcVTotal); 205235c4bbdfSmrg p = p->next; 205305b261ecSmrg } while (p != NULL && p != scrp->modes); 205405b261ecSmrg} 205505b261ecSmrg 20566747b715Smrgvoid 205705b261ecSmrgxf86PrintModes(ScrnInfoPtr scrp) 205805b261ecSmrg{ 205905b261ecSmrg DisplayModePtr p; 206005b261ecSmrg float hsync, refresh = 0; 206135c4bbdfSmrg const char *desc, *desc2, *prefix, *uprefix; 206205b261ecSmrg 206305b261ecSmrg if (scrp == NULL) 206435c4bbdfSmrg return; 206505b261ecSmrg 20661b5d61b8Smrg xf86DrvMsg(scrp->scrnIndex, X_INFO, "Virtual size is %dx%d (pitch %d)\n", 20671b5d61b8Smrg scrp->virtualX, scrp->virtualY, scrp->displayWidth); 206835c4bbdfSmrg 206905b261ecSmrg p = scrp->modes; 207005b261ecSmrg if (p == NULL) 207135c4bbdfSmrg return; 207205b261ecSmrg 207305b261ecSmrg do { 207435c4bbdfSmrg desc = desc2 = ""; 207535c4bbdfSmrg hsync = xf86ModeHSync(p); 207635c4bbdfSmrg refresh = xf86ModeVRefresh(p); 207735c4bbdfSmrg if (p->Flags & V_INTERLACE) { 207835c4bbdfSmrg desc = " (I)"; 207935c4bbdfSmrg } 208035c4bbdfSmrg if (p->Flags & V_DBLSCAN) { 208135c4bbdfSmrg desc = " (D)"; 208235c4bbdfSmrg } 208335c4bbdfSmrg if (p->VScan > 1) { 208435c4bbdfSmrg desc2 = " (VScan)"; 208535c4bbdfSmrg } 208635c4bbdfSmrg if (p->type & M_T_BUILTIN) 208735c4bbdfSmrg prefix = "Built-in mode"; 208835c4bbdfSmrg else if (p->type & M_T_DEFAULT) 208935c4bbdfSmrg prefix = "Default mode"; 209035c4bbdfSmrg else if (p->type & M_T_DRIVER) 209135c4bbdfSmrg prefix = "Driver mode"; 209235c4bbdfSmrg else 209335c4bbdfSmrg prefix = "Mode"; 209435c4bbdfSmrg if (p->type & M_T_USERDEF) 209535c4bbdfSmrg uprefix = "*"; 209635c4bbdfSmrg else 209735c4bbdfSmrg uprefix = " "; 209835c4bbdfSmrg if (hsync == 0 || refresh == 0) { 209935c4bbdfSmrg if (p->name) 210035c4bbdfSmrg xf86DrvMsg(scrp->scrnIndex, X_CONFIG, 210135c4bbdfSmrg "%s%s \"%s\"\n", uprefix, prefix, p->name); 210235c4bbdfSmrg else 210335c4bbdfSmrg xf86DrvMsg(scrp->scrnIndex, X_PROBED, 210435c4bbdfSmrg "%s%s %dx%d (unnamed)\n", 210535c4bbdfSmrg uprefix, prefix, p->HDisplay, p->VDisplay); 210635c4bbdfSmrg } 210735c4bbdfSmrg else if (p->Clock == p->SynthClock) { 210835c4bbdfSmrg xf86DrvMsg(scrp->scrnIndex, X_CONFIG, 210935c4bbdfSmrg "%s%s \"%s\": %.1f MHz, %.1f kHz, %.1f Hz%s%s\n", 211035c4bbdfSmrg uprefix, prefix, p->name, p->Clock / 1000.0, 211135c4bbdfSmrg hsync, refresh, desc, desc2); 211235c4bbdfSmrg } 211335c4bbdfSmrg else { 211435c4bbdfSmrg xf86DrvMsg(scrp->scrnIndex, X_CONFIG, 211535c4bbdfSmrg "%s%s \"%s\": %.1f MHz (scaled from %.1f MHz), " 211635c4bbdfSmrg "%.1f kHz, %.1f Hz%s%s\n", 211735c4bbdfSmrg uprefix, prefix, p->name, p->Clock / 1000.0, 211835c4bbdfSmrg p->SynthClock / 1000.0, hsync, refresh, desc, desc2); 211935c4bbdfSmrg } 212035c4bbdfSmrg if (hsync != 0 && refresh != 0) 212135c4bbdfSmrg xf86PrintModeline(scrp->scrnIndex, p); 212235c4bbdfSmrg p = p->next; 212305b261ecSmrg } while (p != NULL && p != scrp->modes); 212405b261ecSmrg} 2125