1706f2543Smrg/*
2706f2543Smrg * Copyright (c) 1997-2003 by The XFree86 Project, Inc.
3706f2543Smrg *
4706f2543Smrg * Permission is hereby granted, free of charge, to any person obtaining a
5706f2543Smrg * copy of this software and associated documentation files (the "Software"),
6706f2543Smrg * to deal in the Software without restriction, including without limitation
7706f2543Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8706f2543Smrg * and/or sell copies of the Software, and to permit persons to whom the
9706f2543Smrg * Software is furnished to do so, subject to the following conditions:
10706f2543Smrg *
11706f2543Smrg * The above copyright notice and this permission notice shall be included in
12706f2543Smrg * all copies or substantial portions of the Software.
13706f2543Smrg *
14706f2543Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15706f2543Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16706f2543Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17706f2543Smrg * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18706f2543Smrg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19706f2543Smrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20706f2543Smrg * OTHER DEALINGS IN THE SOFTWARE.
21706f2543Smrg *
22706f2543Smrg * Except as contained in this notice, the name of the copyright holder(s)
23706f2543Smrg * and author(s) shall not be used in advertising or otherwise to promote
24706f2543Smrg * the sale, use or other dealings in this Software without prior written
25706f2543Smrg * authorization from the copyright holder(s) and author(s).
26706f2543Smrg */
27706f2543Smrg
28706f2543Smrg#ifdef HAVE_XORG_CONFIG_H
29706f2543Smrg#include <xorg-config.h>
30706f2543Smrg#else
31706f2543Smrg#ifdef HAVE_CONFIG_H
32706f2543Smrg#include <config.h>
33706f2543Smrg#endif
34706f2543Smrg#endif
35706f2543Smrg
36706f2543Smrg#include "xf86Modes.h"
37706f2543Smrg#include "xf86Priv.h"
38706f2543Smrg
39706f2543Smrgextern XF86ConfigPtr xf86configptr;
40706f2543Smrg
41706f2543Smrg/**
42706f2543Smrg * Calculates the horizontal sync rate of a mode.
43706f2543Smrg */
44706f2543Smrgdouble
45706f2543Smrgxf86ModeHSync(const DisplayModeRec *mode)
46706f2543Smrg{
47706f2543Smrg    double hsync = 0.0;
48706f2543Smrg
49706f2543Smrg    if (mode->HSync > 0.0)
50706f2543Smrg	    hsync = mode->HSync;
51706f2543Smrg    else if (mode->HTotal > 0)
52706f2543Smrg	    hsync = (float)mode->Clock / (float)mode->HTotal;
53706f2543Smrg
54706f2543Smrg    return hsync;
55706f2543Smrg}
56706f2543Smrg
57706f2543Smrg/**
58706f2543Smrg * Calculates the vertical refresh rate of a mode.
59706f2543Smrg */
60706f2543Smrgdouble
61706f2543Smrgxf86ModeVRefresh(const DisplayModeRec *mode)
62706f2543Smrg{
63706f2543Smrg    double refresh = 0.0;
64706f2543Smrg
65706f2543Smrg    if (mode->VRefresh > 0.0)
66706f2543Smrg	refresh = mode->VRefresh;
67706f2543Smrg    else if (mode->HTotal > 0 && mode->VTotal > 0) {
68706f2543Smrg	refresh = mode->Clock * 1000.0 / mode->HTotal / mode->VTotal;
69706f2543Smrg	if (mode->Flags & V_INTERLACE)
70706f2543Smrg	    refresh *= 2.0;
71706f2543Smrg	if (mode->Flags & V_DBLSCAN)
72706f2543Smrg	    refresh /= 2.0;
73706f2543Smrg	if (mode->VScan > 1)
74706f2543Smrg	    refresh /= (float)(mode->VScan);
75706f2543Smrg    }
76706f2543Smrg    return refresh;
77706f2543Smrg}
78706f2543Smrg
79706f2543Smrgint
80706f2543Smrgxf86ModeWidth (const DisplayModeRec *mode, Rotation rotation)
81706f2543Smrg{
82706f2543Smrg    switch (rotation & 0xf) {
83706f2543Smrg    case RR_Rotate_0:
84706f2543Smrg    case RR_Rotate_180:
85706f2543Smrg	return mode->HDisplay;
86706f2543Smrg    case RR_Rotate_90:
87706f2543Smrg    case RR_Rotate_270:
88706f2543Smrg	return mode->VDisplay;
89706f2543Smrg    default:
90706f2543Smrg	return 0;
91706f2543Smrg    }
92706f2543Smrg}
93706f2543Smrg
94706f2543Smrgint
95706f2543Smrgxf86ModeHeight (const DisplayModeRec *mode, Rotation rotation)
96706f2543Smrg{
97706f2543Smrg    switch (rotation & 0xf) {
98706f2543Smrg    case RR_Rotate_0:
99706f2543Smrg    case RR_Rotate_180:
100706f2543Smrg	return mode->VDisplay;
101706f2543Smrg    case RR_Rotate_90:
102706f2543Smrg    case RR_Rotate_270:
103706f2543Smrg	return mode->HDisplay;
104706f2543Smrg    default:
105706f2543Smrg	return 0;
106706f2543Smrg    }
107706f2543Smrg}
108706f2543Smrg
109706f2543Smrg/** Calculates the memory bandwidth (in MiB/sec) of a mode. */
110706f2543Smrgunsigned int
111706f2543Smrgxf86ModeBandwidth(DisplayModePtr mode, int depth)
112706f2543Smrg{
113706f2543Smrg    float a_active, a_total, active_percent, pixels_per_second;
114706f2543Smrg    int bytes_per_pixel = bits_to_bytes(depth);
115706f2543Smrg
116706f2543Smrg    if (!mode->HTotal || !mode->VTotal || !mode->Clock)
117706f2543Smrg	return 0;
118706f2543Smrg
119706f2543Smrg    a_active = mode->HDisplay * mode->VDisplay;
120706f2543Smrg    a_total = mode->HTotal * mode->VTotal;
121706f2543Smrg    active_percent = a_active / a_total;
122706f2543Smrg    pixels_per_second = active_percent * mode->Clock * 1000.0;
123706f2543Smrg
124706f2543Smrg    return (unsigned int)(pixels_per_second * bytes_per_pixel / (1024 * 1024));
125706f2543Smrg}
126706f2543Smrg
127706f2543Smrg/** Sets a default mode name of <width>x<height> on a mode. */
128706f2543Smrgvoid
129706f2543Smrgxf86SetModeDefaultName(DisplayModePtr mode)
130706f2543Smrg{
131706f2543Smrg    Bool interlaced = !!(mode->Flags & V_INTERLACE);
132706f2543Smrg
133706f2543Smrg    free(mode->name);
134706f2543Smrg
135706f2543Smrg    XNFasprintf(&mode->name, "%dx%d%s", mode->HDisplay, mode->VDisplay,
136706f2543Smrg		interlaced ? "i" : "");
137706f2543Smrg}
138706f2543Smrg
139706f2543Smrg/*
140706f2543Smrg * xf86SetModeCrtc
141706f2543Smrg *
142706f2543Smrg * Initialises the Crtc parameters for a mode.  The initialisation includes
143706f2543Smrg * adjustments for interlaced and double scan modes.
144706f2543Smrg */
145706f2543Smrgvoid
146706f2543Smrgxf86SetModeCrtc(DisplayModePtr p, int adjustFlags)
147706f2543Smrg{
148706f2543Smrg    if ((p == NULL) || ((p->type & M_T_CRTC_C) == M_T_BUILTIN))
149706f2543Smrg	return;
150706f2543Smrg
151706f2543Smrg    p->CrtcHDisplay             = p->HDisplay;
152706f2543Smrg    p->CrtcHSyncStart           = p->HSyncStart;
153706f2543Smrg    p->CrtcHSyncEnd             = p->HSyncEnd;
154706f2543Smrg    p->CrtcHTotal               = p->HTotal;
155706f2543Smrg    p->CrtcHSkew                = p->HSkew;
156706f2543Smrg    p->CrtcVDisplay             = p->VDisplay;
157706f2543Smrg    p->CrtcVSyncStart           = p->VSyncStart;
158706f2543Smrg    p->CrtcVSyncEnd             = p->VSyncEnd;
159706f2543Smrg    p->CrtcVTotal               = p->VTotal;
160706f2543Smrg    if (p->Flags & V_INTERLACE) {
161706f2543Smrg	if (adjustFlags & INTERLACE_HALVE_V) {
162706f2543Smrg	    p->CrtcVDisplay         /= 2;
163706f2543Smrg	    p->CrtcVSyncStart       /= 2;
164706f2543Smrg	    p->CrtcVSyncEnd         /= 2;
165706f2543Smrg	    p->CrtcVTotal           /= 2;
166706f2543Smrg	}
167706f2543Smrg	/* Force interlaced modes to have an odd VTotal */
168706f2543Smrg	/* maybe we should only do this when INTERLACE_HALVE_V is set? */
169706f2543Smrg	p->CrtcVTotal |= 1;
170706f2543Smrg    }
171706f2543Smrg
172706f2543Smrg    if (p->Flags & V_DBLSCAN) {
173706f2543Smrg        p->CrtcVDisplay         *= 2;
174706f2543Smrg        p->CrtcVSyncStart       *= 2;
175706f2543Smrg        p->CrtcVSyncEnd         *= 2;
176706f2543Smrg        p->CrtcVTotal           *= 2;
177706f2543Smrg    }
178706f2543Smrg    if (p->VScan > 1) {
179706f2543Smrg        p->CrtcVDisplay         *= p->VScan;
180706f2543Smrg        p->CrtcVSyncStart       *= p->VScan;
181706f2543Smrg        p->CrtcVSyncEnd         *= p->VScan;
182706f2543Smrg        p->CrtcVTotal           *= p->VScan;
183706f2543Smrg    }
184706f2543Smrg    p->CrtcVBlankStart = min(p->CrtcVSyncStart, p->CrtcVDisplay);
185706f2543Smrg    p->CrtcVBlankEnd = max(p->CrtcVSyncEnd, p->CrtcVTotal);
186706f2543Smrg    p->CrtcHBlankStart = min(p->CrtcHSyncStart, p->CrtcHDisplay);
187706f2543Smrg    p->CrtcHBlankEnd = max(p->CrtcHSyncEnd, p->CrtcHTotal);
188706f2543Smrg
189706f2543Smrg    p->CrtcHAdjusted = FALSE;
190706f2543Smrg    p->CrtcVAdjusted = FALSE;
191706f2543Smrg}
192706f2543Smrg
193706f2543Smrg/**
194706f2543Smrg * Allocates and returns a copy of pMode, including pointers within pMode.
195706f2543Smrg */
196706f2543SmrgDisplayModePtr
197706f2543Smrgxf86DuplicateMode(const DisplayModeRec *pMode)
198706f2543Smrg{
199706f2543Smrg    DisplayModePtr pNew;
200706f2543Smrg
201706f2543Smrg    pNew = xnfalloc(sizeof(DisplayModeRec));
202706f2543Smrg    *pNew = *pMode;
203706f2543Smrg    pNew->next = NULL;
204706f2543Smrg    pNew->prev = NULL;
205706f2543Smrg
206706f2543Smrg    if (pMode->name == NULL)
207706f2543Smrg	xf86SetModeDefaultName(pNew);
208706f2543Smrg    else
209706f2543Smrg	pNew->name = xnfstrdup(pMode->name);
210706f2543Smrg
211706f2543Smrg    return pNew;
212706f2543Smrg}
213706f2543Smrg
214706f2543Smrg/**
215706f2543Smrg * Duplicates every mode in the given list and returns a pointer to the first
216706f2543Smrg * mode.
217706f2543Smrg *
218706f2543Smrg * \param modeList doubly-linked mode list
219706f2543Smrg */
220706f2543SmrgDisplayModePtr
221706f2543Smrgxf86DuplicateModes(ScrnInfoPtr pScrn, DisplayModePtr modeList)
222706f2543Smrg{
223706f2543Smrg    DisplayModePtr first = NULL, last = NULL;
224706f2543Smrg    DisplayModePtr mode;
225706f2543Smrg
226706f2543Smrg    for (mode = modeList; mode != NULL; mode = mode->next) {
227706f2543Smrg	DisplayModePtr new;
228706f2543Smrg
229706f2543Smrg	new = xf86DuplicateMode(mode);
230706f2543Smrg
231706f2543Smrg	/* Insert pNew into modeList */
232706f2543Smrg	if (last) {
233706f2543Smrg	    last->next = new;
234706f2543Smrg	    new->prev = last;
235706f2543Smrg	} else {
236706f2543Smrg	    first = new;
237706f2543Smrg	    new->prev = NULL;
238706f2543Smrg	}
239706f2543Smrg	new->next = NULL;
240706f2543Smrg	last = new;
241706f2543Smrg    }
242706f2543Smrg
243706f2543Smrg    return first;
244706f2543Smrg}
245706f2543Smrg
246706f2543Smrg/**
247706f2543Smrg * Returns true if the given modes should program to the same timings.
248706f2543Smrg *
249706f2543Smrg * This doesn't use Crtc values, as it might be used on ModeRecs without the
250706f2543Smrg * Crtc values set.  So, it's assumed that the other numbers are enough.
251706f2543Smrg */
252706f2543SmrgBool
253706f2543Smrgxf86ModesEqual(const DisplayModeRec *pMode1, const DisplayModeRec *pMode2)
254706f2543Smrg{
255706f2543Smrg     if (pMode1->Clock == pMode2->Clock &&
256706f2543Smrg	 pMode1->HDisplay == pMode2->HDisplay &&
257706f2543Smrg	 pMode1->HSyncStart == pMode2->HSyncStart &&
258706f2543Smrg	 pMode1->HSyncEnd == pMode2->HSyncEnd &&
259706f2543Smrg	 pMode1->HTotal == pMode2->HTotal &&
260706f2543Smrg	 pMode1->HSkew == pMode2->HSkew &&
261706f2543Smrg	 pMode1->VDisplay == pMode2->VDisplay &&
262706f2543Smrg	 pMode1->VSyncStart == pMode2->VSyncStart &&
263706f2543Smrg	 pMode1->VSyncEnd == pMode2->VSyncEnd &&
264706f2543Smrg	 pMode1->VTotal == pMode2->VTotal &&
265706f2543Smrg	 pMode1->VScan == pMode2->VScan &&
266706f2543Smrg	 pMode1->Flags == pMode2->Flags)
267706f2543Smrg     {
268706f2543Smrg	return TRUE;
269706f2543Smrg     } else {
270706f2543Smrg	return FALSE;
271706f2543Smrg     }
272706f2543Smrg}
273706f2543Smrg
274706f2543Smrgstatic void
275706f2543Smrgadd(char **p, char *new)
276706f2543Smrg{
277706f2543Smrg    *p = xnfrealloc(*p, strlen(*p) + strlen(new) + 2);
278706f2543Smrg    strcat(*p, " ");
279706f2543Smrg    strcat(*p, new);
280706f2543Smrg}
281706f2543Smrg
282706f2543Smrg/**
283706f2543Smrg * Print out a modeline.
284706f2543Smrg */
285706f2543Smrgvoid
286706f2543Smrgxf86PrintModeline(int scrnIndex,DisplayModePtr mode)
287706f2543Smrg{
288706f2543Smrg    char tmp[256];
289706f2543Smrg    char *flags = xnfcalloc(1, 1);
290706f2543Smrg
291706f2543Smrg    if (mode->HSkew) {
292706f2543Smrg	snprintf(tmp, 256, "hskew %i", mode->HSkew);
293706f2543Smrg	add(&flags, tmp);
294706f2543Smrg    }
295706f2543Smrg    if (mode->VScan) {
296706f2543Smrg	snprintf(tmp, 256, "vscan %i", mode->VScan);
297706f2543Smrg	add(&flags, tmp);
298706f2543Smrg    }
299706f2543Smrg    if (mode->Flags & V_INTERLACE) add(&flags, "interlace");
300706f2543Smrg    if (mode->Flags & V_CSYNC) add(&flags, "composite");
301706f2543Smrg    if (mode->Flags & V_DBLSCAN) add(&flags, "doublescan");
302706f2543Smrg    if (mode->Flags & V_BCAST) add(&flags, "bcast");
303706f2543Smrg    if (mode->Flags & V_PHSYNC) add(&flags, "+hsync");
304706f2543Smrg    if (mode->Flags & V_NHSYNC) add(&flags, "-hsync");
305706f2543Smrg    if (mode->Flags & V_PVSYNC) add(&flags, "+vsync");
306706f2543Smrg    if (mode->Flags & V_NVSYNC) add(&flags, "-vsync");
307706f2543Smrg    if (mode->Flags & V_PCSYNC) add(&flags, "+csync");
308706f2543Smrg    if (mode->Flags & V_NCSYNC) add(&flags, "-csync");
309706f2543Smrg#if 0
310706f2543Smrg    if (mode->Flags & V_CLKDIV2) add(&flags, "vclk/2");
311706f2543Smrg#endif
312706f2543Smrg    xf86DrvMsg(scrnIndex, X_INFO,
313706f2543Smrg		   "Modeline \"%s\"x%.01f  %6.2f  %i %i %i %i  %i %i %i %i%s "
314706f2543Smrg		   "(%.01f kHz)\n",
315706f2543Smrg		   mode->name, mode->VRefresh, mode->Clock/1000., mode->HDisplay,
316706f2543Smrg		   mode->HSyncStart, mode->HSyncEnd, mode->HTotal,
317706f2543Smrg		   mode->VDisplay, mode->VSyncStart, mode->VSyncEnd,
318706f2543Smrg		   mode->VTotal, flags, xf86ModeHSync(mode));
319706f2543Smrg    free(flags);
320706f2543Smrg}
321706f2543Smrg
322706f2543Smrg/**
323706f2543Smrg * Marks as bad any modes with unsupported flags.
324706f2543Smrg *
325706f2543Smrg * \param modeList doubly-linked list of modes.
326706f2543Smrg * \param flags flags supported by the driver.
327706f2543Smrg *
328706f2543Smrg * \bug only V_INTERLACE and V_DBLSCAN are supported.  Is that enough?
329706f2543Smrg */
330706f2543Smrgvoid
331706f2543Smrgxf86ValidateModesFlags(ScrnInfoPtr pScrn, DisplayModePtr modeList,
332706f2543Smrg			    int flags)
333706f2543Smrg{
334706f2543Smrg    DisplayModePtr mode;
335706f2543Smrg
336706f2543Smrg    if (flags == (V_INTERLACE | V_DBLSCAN))
337706f2543Smrg	return;
338706f2543Smrg
339706f2543Smrg    for (mode = modeList; mode != NULL; mode = mode->next) {
340706f2543Smrg	if (mode->Flags & V_INTERLACE && !(flags & V_INTERLACE))
341706f2543Smrg	    mode->status = MODE_NO_INTERLACE;
342706f2543Smrg	if (mode->Flags & V_DBLSCAN && !(flags & V_DBLSCAN))
343706f2543Smrg	    mode->status = MODE_NO_DBLESCAN;
344706f2543Smrg    }
345706f2543Smrg}
346706f2543Smrg
347706f2543Smrg/**
348706f2543Smrg * Marks as bad any modes extending beyond the given max X, Y, or pitch.
349706f2543Smrg *
350706f2543Smrg * \param modeList doubly-linked list of modes.
351706f2543Smrg */
352706f2543Smrgvoid
353706f2543Smrgxf86ValidateModesSize(ScrnInfoPtr pScrn, DisplayModePtr modeList,
354706f2543Smrg			  int maxX, int maxY, int maxPitch)
355706f2543Smrg{
356706f2543Smrg    DisplayModePtr mode;
357706f2543Smrg
358706f2543Smrg    if (maxPitch <= 0)
359706f2543Smrg	    maxPitch = MAXINT;
360706f2543Smrg    if (maxX <= 0)
361706f2543Smrg	    maxX = MAXINT;
362706f2543Smrg    if (maxY <= 0)
363706f2543Smrg	    maxY = MAXINT;
364706f2543Smrg
365706f2543Smrg    for (mode = modeList; mode != NULL; mode = mode->next) {
366706f2543Smrg	if ((xf86ModeWidth(mode, RR_Rotate_0) > maxPitch ||
367706f2543Smrg	     xf86ModeWidth(mode, RR_Rotate_0) > maxX ||
368706f2543Smrg	     xf86ModeHeight(mode, RR_Rotate_0) > maxY) &&
369706f2543Smrg	    (xf86ModeWidth(mode, RR_Rotate_90) > maxPitch ||
370706f2543Smrg	     xf86ModeWidth(mode, RR_Rotate_90) > maxX ||
371706f2543Smrg	     xf86ModeHeight(mode, RR_Rotate_90) > maxY)) {
372706f2543Smrg	    if (xf86ModeWidth(mode, RR_Rotate_0) > maxPitch ||
373706f2543Smrg		xf86ModeWidth(mode, RR_Rotate_90) > maxPitch)
374706f2543Smrg		mode->status = MODE_BAD_WIDTH;
375706f2543Smrg
376706f2543Smrg	    if (xf86ModeWidth(mode, RR_Rotate_0) > maxX ||
377706f2543Smrg		xf86ModeWidth(mode, RR_Rotate_90) > maxX)
378706f2543Smrg		mode->status = MODE_VIRTUAL_X;
379706f2543Smrg
380706f2543Smrg	    if (xf86ModeHeight(mode, RR_Rotate_0) > maxY ||
381706f2543Smrg		xf86ModeHeight(mode, RR_Rotate_90) > maxY)
382706f2543Smrg		mode->status = MODE_VIRTUAL_Y;
383706f2543Smrg	}
384706f2543Smrg
385706f2543Smrg	if (mode->next == modeList)
386706f2543Smrg	    break;
387706f2543Smrg    }
388706f2543Smrg}
389706f2543Smrg
390706f2543Smrg/**
391706f2543Smrg * Marks as bad any modes that aren't supported by the given monitor's
392706f2543Smrg * hsync and vrefresh ranges.
393706f2543Smrg *
394706f2543Smrg * \param modeList doubly-linked list of modes.
395706f2543Smrg */
396706f2543Smrgvoid
397706f2543Smrgxf86ValidateModesSync(ScrnInfoPtr pScrn, DisplayModePtr modeList,
398706f2543Smrg			  MonPtr mon)
399706f2543Smrg{
400706f2543Smrg    DisplayModePtr mode;
401706f2543Smrg
402706f2543Smrg    for (mode = modeList; mode != NULL; mode = mode->next) {
403706f2543Smrg	Bool bad;
404706f2543Smrg	int i;
405706f2543Smrg
406706f2543Smrg	bad = TRUE;
407706f2543Smrg	for (i = 0; i < mon->nHsync; i++) {
408706f2543Smrg	    if (xf86ModeHSync(mode) >= mon->hsync[i].lo * (1-SYNC_TOLERANCE) &&
409706f2543Smrg		xf86ModeHSync(mode) <= mon->hsync[i].hi * (1+SYNC_TOLERANCE))
410706f2543Smrg	    {
411706f2543Smrg		bad = FALSE;
412706f2543Smrg	    }
413706f2543Smrg	}
414706f2543Smrg	if (bad)
415706f2543Smrg	    mode->status = MODE_HSYNC;
416706f2543Smrg
417706f2543Smrg	bad = TRUE;
418706f2543Smrg	for (i = 0; i < mon->nVrefresh; i++) {
419706f2543Smrg	    if (xf86ModeVRefresh(mode) >= mon->vrefresh[i].lo * (1-SYNC_TOLERANCE) &&
420706f2543Smrg		xf86ModeVRefresh(mode) <= mon->vrefresh[i].hi * (1+SYNC_TOLERANCE))
421706f2543Smrg	    {
422706f2543Smrg		bad = FALSE;
423706f2543Smrg	    }
424706f2543Smrg	}
425706f2543Smrg	if (bad)
426706f2543Smrg	    mode->status = MODE_VSYNC;
427706f2543Smrg
428706f2543Smrg	if (mode->next == modeList)
429706f2543Smrg	    break;
430706f2543Smrg    }
431706f2543Smrg}
432706f2543Smrg
433706f2543Smrg/**
434706f2543Smrg * Marks as bad any modes extending beyond outside of the given clock ranges.
435706f2543Smrg *
436706f2543Smrg * \param modeList doubly-linked list of modes.
437706f2543Smrg * \param min pointer to minimums of clock ranges
438706f2543Smrg * \param max pointer to maximums of clock ranges
439706f2543Smrg * \param n_ranges number of ranges.
440706f2543Smrg */
441706f2543Smrgvoid
442706f2543Smrgxf86ValidateModesClocks(ScrnInfoPtr pScrn, DisplayModePtr modeList,
443706f2543Smrg			    int *min, int *max, int n_ranges)
444706f2543Smrg{
445706f2543Smrg    DisplayModePtr mode;
446706f2543Smrg    int i;
447706f2543Smrg
448706f2543Smrg    for (mode = modeList; mode != NULL; mode = mode->next) {
449706f2543Smrg	Bool good = FALSE;
450706f2543Smrg	for (i = 0; i < n_ranges; i++) {
451706f2543Smrg	    if (mode->Clock >= min[i] * (1-SYNC_TOLERANCE) &&
452706f2543Smrg		mode->Clock <= max[i] * (1+SYNC_TOLERANCE)) {
453706f2543Smrg		good = TRUE;
454706f2543Smrg		break;
455706f2543Smrg	    }
456706f2543Smrg	}
457706f2543Smrg	if (!good)
458706f2543Smrg	    mode->status = MODE_CLOCK_RANGE;
459706f2543Smrg    }
460706f2543Smrg}
461706f2543Smrg
462706f2543Smrg/**
463706f2543Smrg * If the user has specified a set of mode names to use, mark as bad any modes
464706f2543Smrg * not listed.
465706f2543Smrg *
466706f2543Smrg * The user mode names specified are prefixes to names of modes, so "1024x768"
467706f2543Smrg * will match modes named "1024x768", "1024x768x75", "1024x768-good", but
468706f2543Smrg * "1024x768x75" would only match "1024x768x75" from that list.
469706f2543Smrg *
470706f2543Smrg * MODE_BAD is used as the rejection flag, for lack of a better flag.
471706f2543Smrg *
472706f2543Smrg * \param modeList doubly-linked list of modes.
473706f2543Smrg */
474706f2543Smrgvoid
475706f2543Smrgxf86ValidateModesUserConfig(ScrnInfoPtr pScrn, DisplayModePtr modeList)
476706f2543Smrg{
477706f2543Smrg    DisplayModePtr mode;
478706f2543Smrg
479706f2543Smrg    if (pScrn->display->modes[0] == NULL)
480706f2543Smrg	return;
481706f2543Smrg
482706f2543Smrg    for (mode = modeList; mode != NULL; mode = mode->next) {
483706f2543Smrg	int i;
484706f2543Smrg	Bool good = FALSE;
485706f2543Smrg
486706f2543Smrg	for (i = 0; pScrn->display->modes[i] != NULL; i++) {
487706f2543Smrg	    if (strncmp(pScrn->display->modes[i], mode->name,
488706f2543Smrg			strlen(pScrn->display->modes[i])) == 0) {
489706f2543Smrg		good = TRUE;
490706f2543Smrg		break;
491706f2543Smrg	    }
492706f2543Smrg	}
493706f2543Smrg	if (!good)
494706f2543Smrg	    mode->status = MODE_BAD;
495706f2543Smrg    }
496706f2543Smrg}
497706f2543Smrg
498706f2543Smrg
499706f2543Smrg/**
500706f2543Smrg * Marks as bad any modes exceeding the given bandwidth.
501706f2543Smrg *
502706f2543Smrg * \param modeList doubly-linked list of modes.
503706f2543Smrg * \param bandwidth bandwidth in MHz.
504706f2543Smrg * \param depth color depth.
505706f2543Smrg */
506706f2543Smrgvoid
507706f2543Smrgxf86ValidateModesBandwidth(ScrnInfoPtr pScrn, DisplayModePtr modeList,
508706f2543Smrg			   unsigned int bandwidth, int depth)
509706f2543Smrg{
510706f2543Smrg    DisplayModePtr mode;
511706f2543Smrg
512706f2543Smrg    for (mode = modeList; mode != NULL; mode = mode->next) {
513706f2543Smrg	if (xf86ModeBandwidth(mode, depth) > bandwidth)
514706f2543Smrg	    mode->status = MODE_BANDWIDTH;
515706f2543Smrg    }
516706f2543Smrg}
517706f2543Smrg
518706f2543SmrgBool
519706f2543Smrgxf86ModeIsReduced(const DisplayModeRec *mode)
520706f2543Smrg{
521706f2543Smrg    if ((((mode->HDisplay * 5 / 4) & ~0x07) > mode->HTotal) &&
522706f2543Smrg        ((mode->HTotal - mode->HDisplay) == 160) &&
523706f2543Smrg	((mode->HSyncEnd - mode->HDisplay) == 80) &&
524706f2543Smrg	((mode->HSyncEnd - mode->HSyncStart) == 32) &&
525706f2543Smrg	((mode->VSyncStart - mode->VDisplay) == 3))
526706f2543Smrg	return TRUE;
527706f2543Smrg    return FALSE;
528706f2543Smrg}
529706f2543Smrg
530706f2543Smrg/**
531706f2543Smrg * Marks as bad any reduced-blanking modes.
532706f2543Smrg *
533706f2543Smrg * \param modeList doubly-linked list of modes.
534706f2543Smrg */
535706f2543Smrgvoid
536706f2543Smrgxf86ValidateModesReducedBlanking(ScrnInfoPtr pScrn, DisplayModePtr modeList)
537706f2543Smrg{
538706f2543Smrg    for (; modeList != NULL; modeList = modeList->next)
539706f2543Smrg	if (xf86ModeIsReduced(modeList))
540706f2543Smrg	    modeList->status = MODE_NO_REDUCED;
541706f2543Smrg}
542706f2543Smrg
543706f2543Smrg/**
544706f2543Smrg * Frees any modes from the list with a status other than MODE_OK.
545706f2543Smrg *
546706f2543Smrg * \param modeList pointer to a doubly-linked or circular list of modes.
547706f2543Smrg * \param verbose determines whether the reason for mode invalidation is
548706f2543Smrg *	  printed.
549706f2543Smrg */
550706f2543Smrgvoid
551706f2543Smrgxf86PruneInvalidModes(ScrnInfoPtr pScrn, DisplayModePtr *modeList,
552706f2543Smrg			  Bool verbose)
553706f2543Smrg{
554706f2543Smrg    DisplayModePtr mode;
555706f2543Smrg
556706f2543Smrg    for (mode = *modeList; mode != NULL;) {
557706f2543Smrg	DisplayModePtr next = mode->next, first = *modeList;
558706f2543Smrg
559706f2543Smrg	if (mode->status != MODE_OK) {
560706f2543Smrg	    if (verbose) {
561706f2543Smrg		char *type = "";
562706f2543Smrg		if (mode->type & M_T_BUILTIN)
563706f2543Smrg		    type = "built-in ";
564706f2543Smrg		else if (mode->type & M_T_DEFAULT)
565706f2543Smrg		    type = "default ";
566706f2543Smrg		xf86DrvMsg(pScrn->scrnIndex, X_INFO,
567706f2543Smrg			   "Not using %smode \"%s\" (%s)\n", type, mode->name,
568706f2543Smrg			   xf86ModeStatusToString(mode->status));
569706f2543Smrg	    }
570706f2543Smrg	    xf86DeleteMode(modeList, mode);
571706f2543Smrg	}
572706f2543Smrg
573706f2543Smrg	if (next == first)
574706f2543Smrg	    break;
575706f2543Smrg	mode = next;
576706f2543Smrg    }
577706f2543Smrg}
578706f2543Smrg
579706f2543Smrg/**
580706f2543Smrg * Adds the new mode into the mode list, and returns the new list
581706f2543Smrg *
582706f2543Smrg * \param modes doubly-linked mode list.
583706f2543Smrg */
584706f2543SmrgDisplayModePtr
585706f2543Smrgxf86ModesAdd(DisplayModePtr modes, DisplayModePtr new)
586706f2543Smrg{
587706f2543Smrg    if (modes == NULL)
588706f2543Smrg	return new;
589706f2543Smrg
590706f2543Smrg    if (new) {
591706f2543Smrg	DisplayModePtr mode = modes;
592706f2543Smrg
593706f2543Smrg	while (mode->next)
594706f2543Smrg	    mode = mode->next;
595706f2543Smrg
596706f2543Smrg	mode->next = new;
597706f2543Smrg	new->prev = mode;
598706f2543Smrg    }
599706f2543Smrg
600706f2543Smrg    return modes;
601706f2543Smrg}
602706f2543Smrg
603706f2543Smrg/**
604706f2543Smrg * Build a mode list from a list of config file modes
605706f2543Smrg */
606706f2543Smrgstatic DisplayModePtr
607706f2543Smrgxf86GetConfigModes (XF86ConfModeLinePtr conf_mode)
608706f2543Smrg{
609706f2543Smrg    DisplayModePtr  head = NULL, prev = NULL, mode;
610706f2543Smrg
611706f2543Smrg    for (; conf_mode; conf_mode = (XF86ConfModeLinePtr) conf_mode->list.next)
612706f2543Smrg    {
613706f2543Smrg        mode = calloc(1, sizeof(DisplayModeRec));
614706f2543Smrg	if (!mode)
615706f2543Smrg	    continue;
616706f2543Smrg        mode->name       = xstrdup(conf_mode->ml_identifier);
617706f2543Smrg	if (!mode->name)
618706f2543Smrg	{
619706f2543Smrg	    free(mode);
620706f2543Smrg	    continue;
621706f2543Smrg	}
622706f2543Smrg	mode->type       = 0;
623706f2543Smrg        mode->Clock      = conf_mode->ml_clock;
624706f2543Smrg        mode->HDisplay   = conf_mode->ml_hdisplay;
625706f2543Smrg        mode->HSyncStart = conf_mode->ml_hsyncstart;
626706f2543Smrg        mode->HSyncEnd   = conf_mode->ml_hsyncend;
627706f2543Smrg        mode->HTotal     = conf_mode->ml_htotal;
628706f2543Smrg        mode->VDisplay   = conf_mode->ml_vdisplay;
629706f2543Smrg        mode->VSyncStart = conf_mode->ml_vsyncstart;
630706f2543Smrg        mode->VSyncEnd   = conf_mode->ml_vsyncend;
631706f2543Smrg        mode->VTotal     = conf_mode->ml_vtotal;
632706f2543Smrg        mode->Flags      = conf_mode->ml_flags;
633706f2543Smrg        mode->HSkew      = conf_mode->ml_hskew;
634706f2543Smrg        mode->VScan      = conf_mode->ml_vscan;
635706f2543Smrg
636706f2543Smrg        mode->prev = prev;
637706f2543Smrg	mode->next = NULL;
638706f2543Smrg	if (prev)
639706f2543Smrg	    prev->next = mode;
640706f2543Smrg	else
641706f2543Smrg	    head = mode;
642706f2543Smrg	prev = mode;
643706f2543Smrg    }
644706f2543Smrg    return head;
645706f2543Smrg}
646706f2543Smrg
647706f2543Smrg/**
648706f2543Smrg * Build a mode list from a monitor configuration
649706f2543Smrg */
650706f2543SmrgDisplayModePtr
651706f2543Smrgxf86GetMonitorModes (ScrnInfoPtr pScrn, XF86ConfMonitorPtr conf_monitor)
652706f2543Smrg{
653706f2543Smrg    DisplayModePtr	    modes = NULL;
654706f2543Smrg    XF86ConfModesLinkPtr    modes_link;
655706f2543Smrg
656706f2543Smrg    if (!conf_monitor)
657706f2543Smrg	return NULL;
658706f2543Smrg
659706f2543Smrg    /*
660706f2543Smrg     * first we collect the mode lines from the UseModes directive
661706f2543Smrg     */
662706f2543Smrg    for (modes_link = conf_monitor->mon_modes_sect_lst;
663706f2543Smrg	 modes_link;
664706f2543Smrg	 modes_link = modes_link->list.next)
665706f2543Smrg    {
666706f2543Smrg	/* If this modes link hasn't been resolved, go look it up now */
667706f2543Smrg	if (!modes_link->ml_modes)
668706f2543Smrg	    modes_link->ml_modes = xf86findModes (modes_link->ml_modes_str,
669706f2543Smrg						  xf86configptr->conf_modes_lst);
670706f2543Smrg	if (modes_link->ml_modes)
671706f2543Smrg	    modes = xf86ModesAdd (modes,
672706f2543Smrg				  xf86GetConfigModes (modes_link->ml_modes->mon_modeline_lst));
673706f2543Smrg    }
674706f2543Smrg
675706f2543Smrg    return xf86ModesAdd (modes,
676706f2543Smrg			 xf86GetConfigModes (conf_monitor->mon_modeline_lst));
677706f2543Smrg}
678706f2543Smrg
679706f2543Smrg/**
680706f2543Smrg * Build a mode list containing all of the default modes
681706f2543Smrg */
682706f2543SmrgDisplayModePtr
683706f2543Smrgxf86GetDefaultModes (void)
684706f2543Smrg{
685706f2543Smrg    DisplayModePtr  head = NULL, mode;
686706f2543Smrg    int		    i;
687706f2543Smrg
688706f2543Smrg    for (i = 0; i < xf86NumDefaultModes; i++)
689706f2543Smrg    {
690706f2543Smrg	const DisplayModeRec	*defMode = &xf86DefaultModes[i];
691706f2543Smrg
692706f2543Smrg	mode = xf86DuplicateMode(defMode);
693706f2543Smrg	head = xf86ModesAdd(head, mode);
694706f2543Smrg    }
695706f2543Smrg    return head;
696706f2543Smrg}
697706f2543Smrg
698706f2543Smrg/*
699706f2543Smrg * Walk a mode list and prune out duplicates.  Will preserve the preferred
700706f2543Smrg * mode of an otherwise-duplicate pair.
701706f2543Smrg *
702706f2543Smrg * Probably best to call this on lists that are all of a single class
703706f2543Smrg * (driver, default, user, etc.), otherwise, which mode gets deleted is
704706f2543Smrg * not especially well defined.
705706f2543Smrg *
706706f2543Smrg * Returns the new list.
707706f2543Smrg */
708706f2543Smrg
709706f2543SmrgDisplayModePtr
710706f2543Smrgxf86PruneDuplicateModes(DisplayModePtr modes)
711706f2543Smrg{
712706f2543Smrg    DisplayModePtr m, n, o;
713706f2543Smrg
714706f2543Smrgtop:
715706f2543Smrg    for (m = modes; m; m = m->next) {
716706f2543Smrg	for (n = m->next; n; n = o) {
717706f2543Smrg	    o = n->next;
718706f2543Smrg	    if (xf86ModesEqual(m, n)) {
719706f2543Smrg		if (n->type & M_T_PREFERRED) {
720706f2543Smrg		    xf86DeleteMode(&modes, m);
721706f2543Smrg		    goto top;
722706f2543Smrg		}
723706f2543Smrg		else
724706f2543Smrg		    xf86DeleteMode(&modes, n);
725706f2543Smrg	    }
726706f2543Smrg	}
727706f2543Smrg    }
728706f2543Smrg
729706f2543Smrg    return modes;
730706f2543Smrg}
731