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 */
38ModeStatus
39ATIValidMode
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