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