132b578d3Smrg/*
232b578d3Smrg * Copyright 1997 through 2004 by Marc Aurele La France (TSI @ UQV), tsi@xfree86.org
332b578d3Smrg *
432b578d3Smrg * Permission to use, copy, modify, distribute, and sell this software and its
532b578d3Smrg * documentation for any purpose is hereby granted without fee, provided that
632b578d3Smrg * the above copyright notice appear in all copies and that both that copyright
732b578d3Smrg * notice and this permission notice appear in supporting documentation, and
832b578d3Smrg * that the name of Marc Aurele La France not be used in advertising or
932b578d3Smrg * publicity pertaining to distribution of the software without specific,
1032b578d3Smrg * written prior permission.  Marc Aurele La France makes no representations
1132b578d3Smrg * about the suitability of this software for any purpose.  It is provided
1232b578d3Smrg * "as-is" without express or implied warranty.
1332b578d3Smrg *
1432b578d3Smrg * MARC AURELE LA FRANCE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
1532b578d3Smrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO
1632b578d3Smrg * EVENT SHALL MARC AURELE LA FRANCE BE LIABLE FOR ANY SPECIAL, INDIRECT OR
1732b578d3Smrg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
1832b578d3Smrg * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
1932b578d3Smrg * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
2032b578d3Smrg * PERFORMANCE OF THIS SOFTWARE.
2132b578d3Smrg */
2232b578d3Smrg
2332b578d3Smrg#ifdef HAVE_CONFIG_H
2432b578d3Smrg#include "config.h"
2532b578d3Smrg#endif
2632b578d3Smrg
27e35d4d8eSmrg#include "xf86.h"
2832b578d3Smrg#include "atichip.h"
2932b578d3Smrg#include "atistruct.h"
3032b578d3Smrg#include "ativalid.h"
3132b578d3Smrg
3232b578d3Smrg
3332b578d3Smrg/*
3432b578d3Smrg * ATIValidMode --
3532b578d3Smrg *
3632b578d3Smrg * This checks for hardware-related limits on mode timings.
3732b578d3Smrg */
3832b578d3SmrgModeStatus
3932b578d3SmrgATIValidMode
4032b578d3Smrg(
41e35d4d8eSmrg    SCRN_ARG_TYPE arg,
4232b578d3Smrg    DisplayModePtr pMode,
4332b578d3Smrg    Bool Verbose,
4432b578d3Smrg    int flags
4532b578d3Smrg)
4632b578d3Smrg{
47e35d4d8eSmrg    SCRN_INFO_PTR(arg);
4832b578d3Smrg    ATIPtr      pATI        = ATIPTR(pScreenInfo);
4932b578d3Smrg    int         HBlankWidth, HAdjust, VScan, VInterlace;
5032b578d3Smrg
5132b578d3Smrg    if (flags & MODECHECK_FINAL)
5232b578d3Smrg    {
5332b578d3Smrg        return MODE_OK;
5432b578d3Smrg    }
5532b578d3Smrg
56d2b10af6Smrg    {
57d2b10af6Smrg        int maxHValue, maxVValue;
58d2b10af6Smrg
59d2b10af6Smrg        maxHValue = (MaxBits(CRTC_H_TOTAL) + 1) << 3;
60d2b10af6Smrg        if (pATI->Chip < ATI_CHIP_264VT)
61d2b10af6Smrg        {
62d2b10af6Smrg            /* CRTC_H_TOTAL is one bit narrower */
63d2b10af6Smrg            maxHValue >>= 1;
64d2b10af6Smrg        }
65d2b10af6Smrg        if (pMode->HTotal > maxHValue)
66d2b10af6Smrg            return MODE_BAD_HVALUE;
67d2b10af6Smrg
68d2b10af6Smrg        maxVValue = MaxBits(CRTC_V_TOTAL) + 1;
69d2b10af6Smrg        if (pMode->VTotal > maxVValue)
70d2b10af6Smrg            return MODE_BAD_VVALUE;
71d2b10af6Smrg    }
72d2b10af6Smrg
7332b578d3Smrg    /*
7432b578d3Smrg     * The following is done for every mode in the monitor section that
7532b578d3Smrg     * survives the common layer's basic checks.
7632b578d3Smrg     */
7732b578d3Smrg    if (pMode->VScan <= 1)
7832b578d3Smrg        VScan = 1;
7932b578d3Smrg    else
8032b578d3Smrg        VScan = pMode->VScan;
8132b578d3Smrg
8232b578d3Smrg    if (pMode->Flags & V_DBLSCAN)
8332b578d3Smrg        VScan <<= 1;
8432b578d3Smrg
8532b578d3Smrg    if (pATI->OptionPanelDisplay && (pATI->LCDPanelID >= 0))
8632b578d3Smrg    {
8732b578d3Smrg        if ((pMode->CrtcHDisplay > pATI->LCDHorizontal) ||
8832b578d3Smrg            (pMode->CrtcVDisplay > pATI->LCDVertical))
8932b578d3Smrg            return MODE_PANEL;
9032b578d3Smrg
9132b578d3Smrg        if (!pATI->OptionLCDSync || (pMode->type & M_T_BUILTIN))
9232b578d3Smrg        {
9332b578d3Smrg            if ((pMode->HDisplay > pATI->LCDHorizontal) ||
9432b578d3Smrg                (pMode->VDisplay > pATI->LCDVertical))
9532b578d3Smrg                return MODE_PANEL;
9632b578d3Smrg
9732b578d3Smrg            return MODE_OK;
9832b578d3Smrg        }
9932b578d3Smrg
10032b578d3Smrg        /*
10132b578d3Smrg         * Adjust effective timings for monitor checks.  Here the modeline
10232b578d3Smrg         * clock is ignored.  Horizontal timings are scaled by the stretch
10332b578d3Smrg         * ratio used for the displayed area.  The vertical porch is scaled by
10432b578d3Smrg         * the native resolution's aspect ratio.  This seems rather arbitrary,
10532b578d3Smrg         * and it is, but it does make all applicable VESA modes sync on a
10632b578d3Smrg         * panel after stretching.  This has the unfortunate, but necessary,
10732b578d3Smrg         * side-effect of changing the mode's horizontal sync and vertical
10832b578d3Smrg         * refresh rates.  With some exceptions, this tends to increase the
10932b578d3Smrg         * mode's horizontal sync rate, and decrease its vertical refresh rate.
11032b578d3Smrg         */
11132b578d3Smrg        pMode->SynthClock = pATI->LCDClock;
11232b578d3Smrg
11332b578d3Smrg        pMode->CrtcHTotal = pMode->CrtcHBlankEnd =
11432b578d3Smrg            ATIDivide(pMode->CrtcHTotal * pATI->LCDHorizontal,
11532b578d3Smrg                pMode->CrtcHDisplay, -3, 1) << 3;
11632b578d3Smrg        pMode->CrtcHSyncEnd =
11732b578d3Smrg            ATIDivide(pMode->CrtcHSyncEnd * pATI->LCDHorizontal,
11832b578d3Smrg                pMode->CrtcHDisplay, -3, 1) << 3;
11932b578d3Smrg        pMode->CrtcHSyncStart =
12032b578d3Smrg            ATIDivide(pMode->CrtcHSyncStart * pATI->LCDHorizontal,
12132b578d3Smrg                pMode->CrtcHDisplay, -3, -1) << 3;
12232b578d3Smrg        pMode->CrtcHDisplay = pMode->CrtcHBlankStart = pATI->LCDHorizontal;
12332b578d3Smrg
12432b578d3Smrg        pMode->CrtcVTotal = pMode->CrtcVBlankEnd =
12532b578d3Smrg            ATIDivide((pMode->CrtcVTotal - pMode->CrtcVDisplay) *
12632b578d3Smrg                pATI->LCDVertical, pATI->LCDHorizontal, 0, 1) +
12732b578d3Smrg                pATI->LCDVertical;
12832b578d3Smrg        pMode->CrtcVSyncEnd =
12932b578d3Smrg            ATIDivide((pMode->CrtcVSyncEnd - pMode->CrtcVDisplay) *
13032b578d3Smrg                pATI->LCDVertical, pATI->LCDHorizontal, 0, 1) +
13132b578d3Smrg                pATI->LCDVertical;
13232b578d3Smrg        pMode->CrtcVSyncStart =
13332b578d3Smrg            ATIDivide((pMode->CrtcVSyncStart - pMode->CrtcVDisplay) *
13432b578d3Smrg                pATI->LCDVertical, pATI->LCDHorizontal, 0, -1) +
13532b578d3Smrg                pATI->LCDVertical;
13632b578d3Smrg        pMode->CrtcVDisplay = pMode->CrtcVBlankStart = pATI->LCDVertical;
13732b578d3Smrg
13832b578d3Smrg        /*
13932b578d3Smrg         * The CRTC only stretches the mode's displayed area, not its porches.
14032b578d3Smrg         * Reverse-engineer the mode's timings back into the user specified
14132b578d3Smrg         * values so that the stretched mode is produced when the CRTC is
14232b578d3Smrg         * eventually programmed.  The reverse-engineered mode is then checked
14332b578d3Smrg         * against CRTC limits below.
14432b578d3Smrg         */
14532b578d3Smrg        pMode->Clock = pATI->LCDClock;
14632b578d3Smrg
14732b578d3Smrg        HAdjust = pATI->LCDHorizontal - pMode->HDisplay;
14832b578d3Smrg#       define ATIReverseHorizontal(_x) \
14932b578d3Smrg            (pMode->_x - HAdjust)
15032b578d3Smrg
15132b578d3Smrg        pMode->HSyncStart = ATIReverseHorizontal(CrtcHSyncStart);
15232b578d3Smrg        pMode->HSyncEnd = ATIReverseHorizontal(CrtcHSyncEnd);
15332b578d3Smrg        pMode->HTotal = ATIReverseHorizontal(CrtcHTotal);
15432b578d3Smrg
15532b578d3Smrg        VInterlace = GetBits(pMode->Flags, V_INTERLACE) + 1;
15632b578d3Smrg#       define ATIReverseVertical(_y) \
15732b578d3Smrg            ((((pMode->_y - pATI->LCDVertical) * VInterlace) / VScan) + \
15832b578d3Smrg             pMode->VDisplay)
15932b578d3Smrg
16032b578d3Smrg        pMode->VSyncStart = ATIReverseVertical(CrtcVSyncStart);
16132b578d3Smrg        pMode->VSyncEnd = ATIReverseVertical(CrtcVSyncEnd);
16232b578d3Smrg        pMode->VTotal = ATIReverseVertical(CrtcVTotal);
16332b578d3Smrg
16432b578d3Smrg#       undef ATIReverseHorizontal
16532b578d3Smrg#       undef ATIReverseVertical
16632b578d3Smrg    }
16732b578d3Smrg
16832b578d3Smrg    HBlankWidth = (pMode->HTotal >> 3) - (pMode->HDisplay >> 3);
16932b578d3Smrg    if (!HBlankWidth)
17032b578d3Smrg        return MODE_HBLANK_NARROW;
17132b578d3Smrg
17232b578d3Smrg    {
17332b578d3Smrg            if (VScan > 2)
17432b578d3Smrg                return MODE_NO_VSCAN;
17532b578d3Smrg    }
17632b578d3Smrg
17732b578d3Smrg    return MODE_OK;
17832b578d3Smrg}
179