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