1 /*
2  * Copyright 1997 through 2004 by Marc Aurele La France (TSI @ UQV), tsi@xfree86.org
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that copyright
7  * notice and this permission notice appear in supporting documentation, and
8  * that the name of Marc Aurele La France not be used in advertising or
9  * publicity pertaining to distribution of the software without specific,
10  * written prior permission.  Marc Aurele La France makes no representations
11  * about the suitability of this software for any purpose.  It is provided
12  * "as-is" without express or implied warranty.
13  *
14  * MARC AURELE LA FRANCE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO
16  * EVENT SHALL MARC AURELE LA FRANCE BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20  * PERFORMANCE OF THIS SOFTWARE.
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 
27 #include "xf86.h"
28 #include "atichip.h"
29 #include "atistruct.h"
30 #include "ativalid.h"
31 
32 
33 /*
34  * ATIValidMode --
35  *
36  * This checks for hardware-related limits on mode timings.
37  */
38 ModeStatus
ATIValidMode(SCRN_ARG_TYPE arg,DisplayModePtr pMode,Bool Verbose,int flags)39 ATIValidMode
40 (
41     SCRN_ARG_TYPE arg,
42     DisplayModePtr pMode,
43     Bool Verbose,
44     int flags
45 )
46 {
47     SCRN_INFO_PTR(arg);
48     ATIPtr      pATI        = ATIPTR(pScreenInfo);
49     int         HBlankWidth, HAdjust, VScan, VInterlace;
50 
51     if (flags & MODECHECK_FINAL)
52     {
53         return MODE_OK;
54     }
55 
56     {
57         int maxHValue, maxVValue;
58 
59         maxHValue = (MaxBits(CRTC_H_TOTAL) + 1) << 3;
60         if (pATI->Chip < ATI_CHIP_264VT)
61         {
62             /* CRTC_H_TOTAL is one bit narrower */
63             maxHValue >>= 1;
64         }
65         if (pMode->HTotal > maxHValue)
66             return MODE_BAD_HVALUE;
67 
68         maxVValue = MaxBits(CRTC_V_TOTAL) + 1;
69         if (pMode->VTotal > maxVValue)
70             return MODE_BAD_VVALUE;
71     }
72 
73     /*
74      * The following is done for every mode in the monitor section that
75      * survives the common layer's basic checks.
76      */
77     if (pMode->VScan <= 1)
78         VScan = 1;
79     else
80         VScan = pMode->VScan;
81 
82     if (pMode->Flags & V_DBLSCAN)
83         VScan <<= 1;
84 
85     if (pATI->OptionPanelDisplay && (pATI->LCDPanelID >= 0))
86     {
87         if ((pMode->CrtcHDisplay > pATI->LCDHorizontal) ||
88             (pMode->CrtcVDisplay > pATI->LCDVertical))
89             return MODE_PANEL;
90 
91         if (!pATI->OptionLCDSync || (pMode->type & M_T_BUILTIN))
92         {
93             if ((pMode->HDisplay > pATI->LCDHorizontal) ||
94                 (pMode->VDisplay > pATI->LCDVertical))
95                 return MODE_PANEL;
96 
97             return MODE_OK;
98         }
99 
100         /*
101          * Adjust effective timings for monitor checks.  Here the modeline
102          * clock is ignored.  Horizontal timings are scaled by the stretch
103          * ratio used for the displayed area.  The vertical porch is scaled by
104          * the native resolution's aspect ratio.  This seems rather arbitrary,
105          * and it is, but it does make all applicable VESA modes sync on a
106          * panel after stretching.  This has the unfortunate, but necessary,
107          * side-effect of changing the mode's horizontal sync and vertical
108          * refresh rates.  With some exceptions, this tends to increase the
109          * mode's horizontal sync rate, and decrease its vertical refresh rate.
110          */
111         pMode->SynthClock = pATI->LCDClock;
112 
113         pMode->CrtcHTotal = pMode->CrtcHBlankEnd =
114             ATIDivide(pMode->CrtcHTotal * pATI->LCDHorizontal,
115                 pMode->CrtcHDisplay, -3, 1) << 3;
116         pMode->CrtcHSyncEnd =
117             ATIDivide(pMode->CrtcHSyncEnd * pATI->LCDHorizontal,
118                 pMode->CrtcHDisplay, -3, 1) << 3;
119         pMode->CrtcHSyncStart =
120             ATIDivide(pMode->CrtcHSyncStart * pATI->LCDHorizontal,
121                 pMode->CrtcHDisplay, -3, -1) << 3;
122         pMode->CrtcHDisplay = pMode->CrtcHBlankStart = pATI->LCDHorizontal;
123 
124         pMode->CrtcVTotal = pMode->CrtcVBlankEnd =
125             ATIDivide((pMode->CrtcVTotal - pMode->CrtcVDisplay) *
126                 pATI->LCDVertical, pATI->LCDHorizontal, 0, 1) +
127                 pATI->LCDVertical;
128         pMode->CrtcVSyncEnd =
129             ATIDivide((pMode->CrtcVSyncEnd - pMode->CrtcVDisplay) *
130                 pATI->LCDVertical, pATI->LCDHorizontal, 0, 1) +
131                 pATI->LCDVertical;
132         pMode->CrtcVSyncStart =
133             ATIDivide((pMode->CrtcVSyncStart - pMode->CrtcVDisplay) *
134                 pATI->LCDVertical, pATI->LCDHorizontal, 0, -1) +
135                 pATI->LCDVertical;
136         pMode->CrtcVDisplay = pMode->CrtcVBlankStart = pATI->LCDVertical;
137 
138         /*
139          * The CRTC only stretches the mode's displayed area, not its porches.
140          * Reverse-engineer the mode's timings back into the user specified
141          * values so that the stretched mode is produced when the CRTC is
142          * eventually programmed.  The reverse-engineered mode is then checked
143          * against CRTC limits below.
144          */
145         pMode->Clock = pATI->LCDClock;
146 
147         HAdjust = pATI->LCDHorizontal - pMode->HDisplay;
148 #       define ATIReverseHorizontal(_x) \
149             (pMode->_x - HAdjust)
150 
151         pMode->HSyncStart = ATIReverseHorizontal(CrtcHSyncStart);
152         pMode->HSyncEnd = ATIReverseHorizontal(CrtcHSyncEnd);
153         pMode->HTotal = ATIReverseHorizontal(CrtcHTotal);
154 
155         VInterlace = GetBits(pMode->Flags, V_INTERLACE) + 1;
156 #       define ATIReverseVertical(_y) \
157             ((((pMode->_y - pATI->LCDVertical) * VInterlace) / VScan) + \
158              pMode->VDisplay)
159 
160         pMode->VSyncStart = ATIReverseVertical(CrtcVSyncStart);
161         pMode->VSyncEnd = ATIReverseVertical(CrtcVSyncEnd);
162         pMode->VTotal = ATIReverseVertical(CrtcVTotal);
163 
164 #       undef ATIReverseHorizontal
165 #       undef ATIReverseVertical
166     }
167 
168     HBlankWidth = (pMode->HTotal >> 3) - (pMode->HDisplay >> 3);
169     if (!HBlankWidth)
170         return MODE_HBLANK_NARROW;
171 
172     {
173             if (VScan > 2)
174                 return MODE_NO_VSCAN;
175     }
176 
177     return MODE_OK;
178 }
179