2 * Copyright (c) 1997-2003 by The XFree86 Project, Inc.
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:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
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.
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).
28 #ifdef HAVE_XORG_CONFIG_H
29 #include <xorg-config.h>
36 #include "xf86Modes.h"
39 extern XF86ConfigPtr xf86configptr
;
42 * Calculates the horizontal sync rate of a mode.
45 xf86ModeHSync(const DisplayModeRec
* mode
)
49 if (mode
->HSync
> 0.0)
51 else if (mode
->HTotal
> 0)
52 hsync
= (float) mode
->Clock
/ (float) mode
->HTotal
;
58 * Calculates the vertical refresh rate of a mode.
61 xf86ModeVRefresh(const DisplayModeRec
* mode
)
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
)
71 if (mode
->Flags
& V_DBLSCAN
)
74 refresh
/= (float) (mode
->VScan
);
80 xf86ModeWidth(const DisplayModeRec
* mode
, Rotation rotation
)
82 switch (rotation
& 0xf) {
85 return mode
->HDisplay
;
88 return mode
->VDisplay
;
95 xf86ModeHeight(const DisplayModeRec
* mode
, Rotation rotation
)
97 switch (rotation
& 0xf) {
100 return mode
->VDisplay
;
103 return mode
->HDisplay
;
109 /** Calculates the memory bandwidth (in MiB/sec) of a mode. */
111 xf86ModeBandwidth(DisplayModePtr mode
, int depth
)
113 float a_active
, a_total
, active_percent
, pixels_per_second
;
114 int bytes_per_pixel
= bits_to_bytes(depth
);
116 if (!mode
->HTotal
|| !mode
->VTotal
|| !mode
->Clock
)
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;
124 return (unsigned int) (pixels_per_second
* bytes_per_pixel
/ (1024 * 1024));
127 /** Sets a default mode name of <width>x<height> on a mode. */
129 xf86SetModeDefaultName(DisplayModePtr mode
)
131 Bool interlaced
= ! !(mode
->Flags
& V_INTERLACE
);
135 XNFasprintf(&mode
->name
, "%dx%d%s", mode
->HDisplay
, mode
->VDisplay
,
136 interlaced
? "i" : "");
142 * Initialises the Crtc parameters for a mode. The initialisation includes
143 * adjustments for interlaced and double scan modes.
146 xf86SetModeCrtc(DisplayModePtr p
, int adjustFlags
)
148 if ((p
== NULL
) || ((p
->type
& M_T_CRTC_C
) == M_T_BUILTIN
))
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;
167 /* Force interlaced modes to have an odd VTotal */
168 /* maybe we should only do this when INTERLACE_HALVE_V is set? */
172 if (p
->Flags
& V_DBLSCAN
) {
173 p
->CrtcVDisplay
*= 2;
174 p
->CrtcVSyncStart
*= 2;
175 p
->CrtcVSyncEnd
*= 2;
179 p
->CrtcVDisplay
*= p
->VScan
;
180 p
->CrtcVSyncStart
*= p
->VScan
;
181 p
->CrtcVSyncEnd
*= p
->VScan
;
182 p
->CrtcVTotal
*= p
->VScan
;
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
);
189 p
->CrtcHAdjusted
= FALSE
;
190 p
->CrtcVAdjusted
= FALSE
;
194 * Fills in a copy of mode, removing all stale pointer references.
195 * xf86ModesEqual will return true when comparing with original mode.
198 xf86SaveModeContents(DisplayModePtr intern
, const DisplayModeRec
*mode
)
201 intern
->prev
= intern
->next
= NULL
;
203 intern
->PrivSize
= 0;
204 intern
->PrivFlags
= 0;
205 intern
->Private
= NULL
;
209 * Allocates and returns a copy of pMode, including pointers within pMode.
212 xf86DuplicateMode(const DisplayModeRec
* pMode
)
216 pNew
= xnfalloc(sizeof(DisplayModeRec
));
221 if (pMode
->name
== NULL
)
222 xf86SetModeDefaultName(pNew
);
224 pNew
->name
= xnfstrdup(pMode
->name
);
230 * Duplicates every mode in the given list and returns a pointer to the first
233 * \param modeList doubly-linked mode list
236 xf86DuplicateModes(ScrnInfoPtr pScrn
, DisplayModePtr modeList
)
238 DisplayModePtr first
= NULL
, last
= NULL
;
241 for (mode
= modeList
; mode
!= NULL
; mode
= mode
->next
) {
244 new = xf86DuplicateMode(mode
);
246 /* Insert pNew into modeList */
263 * Returns true if the given modes should program to the same timings.
265 * This doesn't use Crtc values, as it might be used on ModeRecs without the
266 * Crtc values set. So, it's assumed that the other numbers are enough.
269 xf86ModesEqual(const DisplayModeRec
* pMode1
, const DisplayModeRec
* pMode2
)
271 if (pMode1
->Clock
== pMode2
->Clock
&&
272 pMode1
->HDisplay
== pMode2
->HDisplay
&&
273 pMode1
->HSyncStart
== pMode2
->HSyncStart
&&
274 pMode1
->HSyncEnd
== pMode2
->HSyncEnd
&&
275 pMode1
->HTotal
== pMode2
->HTotal
&&
276 pMode1
->HSkew
== pMode2
->HSkew
&&
277 pMode1
->VDisplay
== pMode2
->VDisplay
&&
278 pMode1
->VSyncStart
== pMode2
->VSyncStart
&&
279 pMode1
->VSyncEnd
== pMode2
->VSyncEnd
&&
280 pMode1
->VTotal
== pMode2
->VTotal
&&
281 pMode1
->VScan
== pMode2
->VScan
&& pMode1
->Flags
== pMode2
->Flags
) {
290 add(char **p
, const char *new)
292 *p
= xnfrealloc(*p
, strlen(*p
) + strlen(new) + 2);
298 * Print out a modeline.
300 * The mode type bits are informational except for the capitalized U
301 * and P bits which give sort order priority. Letter map:
303 * USERPREF, U, user preferred is set from the xorg.conf Monitor
304 * Option "PreferredMode" or from the Screen Display Modes statement.
305 * This unique modeline is moved to the head of the list after sorting.
307 * DRIVER, e, is set by the video driver, EDID or flat panel native.
309 * USERDEF, z, a configured zoom mode Ctrl+Alt+Keypad-{Plus,Minus}.
311 * DEFAULT, d, a compiled-in default.
313 * PREFERRED, P, driver preferred is set by the video device driver,
314 * e.g. the EDID detailed timing modeline. This is a true sort
315 * priority and multiple P modes form a sorted sublist at the list
318 * BUILTIN, b, a hardware fixed CRTC mode.
320 * See modes/xf86Crtc.c: xf86ProbeOutputModes().
323 xf86PrintModeline(int scrnIndex
, DisplayModePtr mode
)
326 char *flags
= xnfcalloc(1, 1);
329 const char tchar
[TBITS
+ 1] = "UezdPb";
332 M_T_USERPREF
, M_T_DRIVER
, M_T_USERDEF
,
333 M_T_DEFAULT
, M_T_PREFERRED
, M_T_BUILTIN
335 char type
[TBITS
+ 2]; /* +1 for leading space */
344 for (i
= 0; tchar
[i
]; i
++)
345 if (mode
->type
& tbit
[i
])
346 type
[tlen
++] = tchar
[i
];
351 snprintf(tmp
, 256, "hskew %i", mode
->HSkew
);
355 snprintf(tmp
, 256, "vscan %i", mode
->VScan
);
358 if (mode
->Flags
& V_INTERLACE
)
359 add(&flags
, "interlace");
360 if (mode
->Flags
& V_CSYNC
)
361 add(&flags
, "composite");
362 if (mode
->Flags
& V_DBLSCAN
)
363 add(&flags
, "doublescan");
364 if (mode
->Flags
& V_BCAST
)
365 add(&flags
, "bcast");
366 if (mode
->Flags
& V_PHSYNC
)
367 add(&flags
, "+hsync");
368 if (mode
->Flags
& V_NHSYNC
)
369 add(&flags
, "-hsync");
370 if (mode
->Flags
& V_PVSYNC
)
371 add(&flags
, "+vsync");
372 if (mode
->Flags
& V_NVSYNC
)
373 add(&flags
, "-vsync");
374 if (mode
->Flags
& V_PCSYNC
)
375 add(&flags
, "+csync");
376 if (mode
->Flags
& V_NCSYNC
)
377 add(&flags
, "-csync");
379 if (mode
->Flags
& V_CLKDIV2
)
380 add(&flags
, "vclk/2");
382 xf86DrvMsg(scrnIndex
, X_INFO
,
383 "Modeline \"%s\"x%.01f %6.2f %i %i %i %i %i %i %i %i%s"
385 mode
->name
, mode
->VRefresh
, mode
->Clock
/ 1000.,
386 mode
->HDisplay
, mode
->HSyncStart
, mode
->HSyncEnd
, mode
->HTotal
,
387 mode
->VDisplay
, mode
->VSyncStart
, mode
->VSyncEnd
, mode
->VTotal
,
388 flags
, xf86ModeHSync(mode
), type
);
393 * Marks as bad any modes with unsupported flags.
395 * \param modeList doubly-linked list of modes.
396 * \param flags flags supported by the driver.
398 * \bug only V_INTERLACE and V_DBLSCAN are supported. Is that enough?
401 xf86ValidateModesFlags(ScrnInfoPtr pScrn
, DisplayModePtr modeList
, int flags
)
405 if (flags
== (V_INTERLACE
| V_DBLSCAN
))
408 for (mode
= modeList
; mode
!= NULL
; mode
= mode
->next
) {
409 if (mode
->Flags
& V_INTERLACE
&& !(flags
& V_INTERLACE
))
410 mode
->status
= MODE_NO_INTERLACE
;
411 if (mode
->Flags
& V_DBLSCAN
&& !(flags
& V_DBLSCAN
))
412 mode
->status
= MODE_NO_DBLESCAN
;
417 * Marks as bad any modes extending beyond the given max X, Y, or pitch.
419 * \param modeList doubly-linked list of modes.
422 xf86ValidateModesSize(ScrnInfoPtr pScrn
, DisplayModePtr modeList
,
423 int maxX
, int maxY
, int maxPitch
)
434 for (mode
= modeList
; mode
!= NULL
; mode
= mode
->next
) {
435 if ((xf86ModeWidth(mode
, RR_Rotate_0
) > maxPitch
||
436 xf86ModeWidth(mode
, RR_Rotate_0
) > maxX
||
437 xf86ModeHeight(mode
, RR_Rotate_0
) > maxY
) &&
438 (xf86ModeWidth(mode
, RR_Rotate_90
) > maxPitch
||
439 xf86ModeWidth(mode
, RR_Rotate_90
) > maxX
||
440 xf86ModeHeight(mode
, RR_Rotate_90
) > maxY
)) {
441 if (xf86ModeWidth(mode
, RR_Rotate_0
) > maxPitch
||
442 xf86ModeWidth(mode
, RR_Rotate_90
) > maxPitch
)
443 mode
->status
= MODE_BAD_WIDTH
;
445 if (xf86ModeWidth(mode
, RR_Rotate_0
) > maxX
||
446 xf86ModeWidth(mode
, RR_Rotate_90
) > maxX
)
447 mode
->status
= MODE_VIRTUAL_X
;
449 if (xf86ModeHeight(mode
, RR_Rotate_0
) > maxY
||
450 xf86ModeHeight(mode
, RR_Rotate_90
) > maxY
)
451 mode
->status
= MODE_VIRTUAL_Y
;
454 if (mode
->next
== modeList
)
460 * Marks as bad any modes that aren't supported by the given monitor's
461 * hsync and vrefresh ranges.
463 * \param modeList doubly-linked list of modes.
466 xf86ValidateModesSync(ScrnInfoPtr pScrn
, DisplayModePtr modeList
, MonPtr mon
)
470 for (mode
= modeList
; mode
!= NULL
; mode
= mode
->next
) {
475 for (i
= 0; i
< mon
->nHsync
; i
++) {
476 if (xf86ModeHSync(mode
) >= mon
->hsync
[i
].lo
* (1 - SYNC_TOLERANCE
)
477 && xf86ModeHSync(mode
) <=
478 mon
->hsync
[i
].hi
* (1 + SYNC_TOLERANCE
)) {
483 mode
->status
= MODE_HSYNC
;
486 for (i
= 0; i
< mon
->nVrefresh
; i
++) {
487 if (xf86ModeVRefresh(mode
) >=
488 mon
->vrefresh
[i
].lo
* (1 - SYNC_TOLERANCE
) &&
489 xf86ModeVRefresh(mode
) <=
490 mon
->vrefresh
[i
].hi
* (1 + SYNC_TOLERANCE
)) {
495 mode
->status
= MODE_VSYNC
;
497 if (mode
->next
== modeList
)
503 * Marks as bad any modes extending beyond outside of the given clock ranges.
505 * \param modeList doubly-linked list of modes.
506 * \param min pointer to minimums of clock ranges
507 * \param max pointer to maximums of clock ranges
508 * \param n_ranges number of ranges.
511 xf86ValidateModesClocks(ScrnInfoPtr pScrn
, DisplayModePtr modeList
,
512 int *min
, int *max
, int n_ranges
)
517 for (mode
= modeList
; mode
!= NULL
; mode
= mode
->next
) {
520 for (i
= 0; i
< n_ranges
; i
++) {
521 if (mode
->Clock
>= min
[i
] * (1 - SYNC_TOLERANCE
) &&
522 mode
->Clock
<= max
[i
] * (1 + SYNC_TOLERANCE
)) {
528 mode
->status
= MODE_CLOCK_RANGE
;
533 * If the user has specified a set of mode names to use, mark as bad any modes
536 * The user mode names specified are prefixes to names of modes, so "1024x768"
537 * will match modes named "1024x768", "1024x768x75", "1024x768-good", but
538 * "1024x768x75" would only match "1024x768x75" from that list.
540 * MODE_BAD is used as the rejection flag, for lack of a better flag.
542 * \param modeList doubly-linked list of modes.
545 xf86ValidateModesUserConfig(ScrnInfoPtr pScrn
, DisplayModePtr modeList
)
549 if (pScrn
->display
->modes
[0] == NULL
)
552 for (mode
= modeList
; mode
!= NULL
; mode
= mode
->next
) {
556 for (i
= 0; pScrn
->display
->modes
[i
] != NULL
; i
++) {
557 if (strncmp(pScrn
->display
->modes
[i
], mode
->name
,
558 strlen(pScrn
->display
->modes
[i
])) == 0) {
564 mode
->status
= MODE_BAD
;
569 * Marks as bad any modes exceeding the given bandwidth.
571 * \param modeList doubly-linked list of modes.
572 * \param bandwidth bandwidth in MHz.
573 * \param depth color depth.
576 xf86ValidateModesBandwidth(ScrnInfoPtr pScrn
, DisplayModePtr modeList
,
577 unsigned int bandwidth
, int depth
)
581 for (mode
= modeList
; mode
!= NULL
; mode
= mode
->next
) {
582 if (xf86ModeBandwidth(mode
, depth
) > bandwidth
)
583 mode
->status
= MODE_BANDWIDTH
;
588 xf86ModeIsReduced(const DisplayModeRec
* mode
)
590 if ((((mode
->HDisplay
* 5 / 4) & ~0x07) > mode
->HTotal
) &&
591 ((mode
->HTotal
- mode
->HDisplay
) == 160) &&
592 ((mode
->HSyncEnd
- mode
->HDisplay
) == 80) &&
593 ((mode
->HSyncEnd
- mode
->HSyncStart
) == 32) &&
594 ((mode
->VSyncStart
- mode
->VDisplay
) == 3))
600 * Marks as bad any reduced-blanking modes.
602 * \param modeList doubly-linked list of modes.
605 xf86ValidateModesReducedBlanking(ScrnInfoPtr pScrn
, DisplayModePtr modeList
)
607 for (; modeList
!= NULL
; modeList
= modeList
->next
)
608 if (xf86ModeIsReduced(modeList
))
609 modeList
->status
= MODE_NO_REDUCED
;
613 * Frees any modes from the list with a status other than MODE_OK.
615 * \param modeList pointer to a doubly-linked or circular list of modes.
616 * \param verbose determines whether the reason for mode invalidation is
620 xf86PruneInvalidModes(ScrnInfoPtr pScrn
, DisplayModePtr
* modeList
,
625 for (mode
= *modeList
; mode
!= NULL
;) {
626 DisplayModePtr next
= mode
->next
, first
= *modeList
;
628 if (mode
->status
!= MODE_OK
) {
630 const char *type
= "";
632 if (mode
->type
& M_T_BUILTIN
)
634 else if (mode
->type
& M_T_DEFAULT
)
636 xf86DrvMsg(pScrn
->scrnIndex
, X_INFO
,
637 "Not using %smode \"%s\" (%s)\n", type
, mode
->name
,
638 xf86ModeStatusToString(mode
->status
));
640 xf86DeleteMode(modeList
, mode
);
650 * Adds the new mode into the mode list, and returns the new list
652 * \param modes doubly-linked mode list.
655 xf86ModesAdd(DisplayModePtr modes
, DisplayModePtr
new)
661 DisplayModePtr mode
= modes
;
674 * Build a mode list from a list of config file modes
676 static DisplayModePtr
677 xf86GetConfigModes(XF86ConfModeLinePtr conf_mode
)
679 DisplayModePtr head
= NULL
, prev
= NULL
, mode
;
681 for (; conf_mode
; conf_mode
= (XF86ConfModeLinePtr
) conf_mode
->list
.next
) {
682 mode
= calloc(1, sizeof(DisplayModeRec
));
685 mode
->name
= xstrdup(conf_mode
->ml_identifier
);
691 mode
->Clock
= conf_mode
->ml_clock
;
692 mode
->HDisplay
= conf_mode
->ml_hdisplay
;
693 mode
->HSyncStart
= conf_mode
->ml_hsyncstart
;
694 mode
->HSyncEnd
= conf_mode
->ml_hsyncend
;
695 mode
->HTotal
= conf_mode
->ml_htotal
;
696 mode
->VDisplay
= conf_mode
->ml_vdisplay
;
697 mode
->VSyncStart
= conf_mode
->ml_vsyncstart
;
698 mode
->VSyncEnd
= conf_mode
->ml_vsyncend
;
699 mode
->VTotal
= conf_mode
->ml_vtotal
;
700 mode
->Flags
= conf_mode
->ml_flags
;
701 mode
->HSkew
= conf_mode
->ml_hskew
;
702 mode
->VScan
= conf_mode
->ml_vscan
;
716 * Build a mode list from a monitor configuration
719 xf86GetMonitorModes(ScrnInfoPtr pScrn
, XF86ConfMonitorPtr conf_monitor
)
721 DisplayModePtr modes
= NULL
;
722 XF86ConfModesLinkPtr modes_link
;
728 * first we collect the mode lines from the UseModes directive
730 for (modes_link
= conf_monitor
->mon_modes_sect_lst
;
731 modes_link
; modes_link
= modes_link
->list
.next
) {
732 /* If this modes link hasn't been resolved, go look it up now */
733 if (!modes_link
->ml_modes
)
734 modes_link
->ml_modes
= xf86findModes(modes_link
->ml_modes_str
,
735 xf86configptr
->conf_modes_lst
);
736 if (modes_link
->ml_modes
)
737 modes
= xf86ModesAdd(modes
,
738 xf86GetConfigModes(modes_link
->ml_modes
->
742 return xf86ModesAdd(modes
,
743 xf86GetConfigModes(conf_monitor
->mon_modeline_lst
));
747 * Build a mode list containing all of the default modes
750 xf86GetDefaultModes(void)
752 DisplayModePtr head
= NULL
, mode
;
755 for (i
= 0; i
< xf86NumDefaultModes
; i
++) {
756 const DisplayModeRec
*defMode
= &xf86DefaultModes
[i
];
758 mode
= xf86DuplicateMode(defMode
);
759 head
= xf86ModesAdd(head
, mode
);
765 * Walk a mode list and prune out duplicates. Will preserve the preferred
766 * mode of an otherwise-duplicate pair.
768 * Probably best to call this on lists that are all of a single class
769 * (driver, default, user, etc.), otherwise, which mode gets deleted is
770 * not especially well defined.
772 * Returns the new list.
776 xf86PruneDuplicateModes(DisplayModePtr modes
)
778 DisplayModePtr m
, n
, o
;
781 for (m
= modes
; m
; m
= m
->next
) {
782 for (n
= m
->next
; n
; n
= o
) {
784 if (xf86ModesEqual(m
, n
)) {
785 if (n
->type
& M_T_PREFERRED
) {
786 xf86DeleteMode(&modes
, m
);
790 xf86DeleteMode(&modes
, n
);