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