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