/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/ati/atiadjust.c,v 1.14 2003/01/01 19:16:30 tsi Exp $ */
/*
 * Copyright 1997 through 2003 by Marc Aurele La France (TSI @ UQV), tsi@xfree86.org
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that copyright
 * notice and this permission notice appear in supporting documentation, and
 * that the name of Marc Aurele La France not be used in advertising or
 * publicity pertaining to distribution of the software without specific,
 * written prior permission.  Marc Aurele La France makes no representations
 * about the suitability of this software for any purpose.  It is provided
 * "as-is" without express or implied warranty.
 *
 * MARC AURELE LA FRANCE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO
 * EVENT SHALL MARC AURELE LA FRANCE BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

#include "ati.h"
#include "atiadjust.h"
#include "atichip.h"
#include "aticrtc.h"
#include "atilock.h"
#include "atimach64io.h"
#include "atiwonderio.h"

/*
 * The display start address is expressed in units of 32-bit (VGA) or 64-bit
 * (accelerator) words where all planar modes are considered as 4bpp modes.
 * These functions ensure the start address does not exceed architectural
 * limits.  Also, to avoid colour changes while panning, these 32-bit or 64-bit
 * boundaries may not fall within a pixel.
 */

/*
 * ATIAjustPreInit  --
 *
 * This function calculates values needed to speed up the setting of the
 * display start address.
 */
void
ATIAdjustPreInit
(
    ATIPtr      pATI
)
{
    unsigned long MaxBase;

#ifndef AVOID_CPIO

    if ((pATI->CPIO_VGAWonder) &&
        (pATI->Chip <= ATI_CHIP_18800_1) &&
        (pATI->VideoRAM == 256) &&
        (pATI->depth >= 8))
    {
        /* Strange, to say the least ... */
        pATI->AdjustDepth = (pATI->bitsPerPixel + 3) >> 2;
        pATI->AdjustMask = (unsigned long)(-32);
    }
    else

#endif /* AVOID_CPIO */

    {
        pATI->AdjustDepth = (pATI->bitsPerPixel + 7) >> 3;

        pATI->AdjustMask = 64;
        while (pATI->AdjustMask % (unsigned long)(pATI->AdjustDepth))
            pATI->AdjustMask += 64;
        pATI->AdjustMask =
            ~(((pATI->AdjustMask / (unsigned long)(pATI->AdjustDepth)) >> 3) -
              1);
    }

    switch (pATI->NewHW.crtc)
    {

#ifndef AVOID_CPIO

        case ATI_CRTC_VGA:
            if (pATI->Chip >= ATI_CHIP_264CT)
            {
                pATI->AdjustMaxBase = MaxBits(CRTC_OFFSET_VGA) << 2;
                if (pATI->depth <= 4)
                    pATI->AdjustMaxBase <<= 1;
            }
            else if (!pATI->CPIO_VGAWonder)
                pATI->AdjustMaxBase = 0xFFFFU << 3;
            else if (pATI->Chip <= ATI_CHIP_28800_6)
                pATI->AdjustMaxBase = 0x03FFFFU << 3;
            else /* Mach32 & Mach64 */
                pATI->AdjustMaxBase = 0x0FFFFFU << 3;
            break;

#endif /* AVOID_CPIO */

        case ATI_CRTC_MACH64:
            pATI->AdjustMaxBase = MaxBits(CRTC_OFFSET) << 3;
            break;

        default:
            pATI->AdjustMaxBase = 0;
            break;
    }

    MaxBase = (pATI->AdjustMaxBase / (unsigned long)pATI->AdjustDepth) |
        ~pATI->AdjustMask;

    pATI->AdjustMaxX = MaxBase % pATI->displayWidth;
    pATI->AdjustMaxY = MaxBase / pATI->displayWidth;
}

/*
 * ATIAdjustFrame --
 *
 * This function is used to initialise the SVGA Start Address - the first
 * displayed location in video memory.  This is used to implement the virtual
 * window.
 */
void
ATIAdjustFrame
(
    int scrnIndex,
    int x,
    int y,
    int flags
)
{
    ScrnInfoPtr pScreenInfo = xf86Screens[scrnIndex];
    ATIPtr      pATI = ATIPTR(pScreenInfo);
    int         Base, xy;

    /*
     * Assume the caller has already done its homework in ensuring the physical
     * screen is still contained in the virtual resolution.
     */
    if (y >= pATI->AdjustMaxY)
    {
        y = pATI->AdjustMaxY;
        if (x > pATI->AdjustMaxX)
            y--;
    }

    Base = ((((y * pATI->displayWidth) + x) & pATI->AdjustMask) *
            pATI->AdjustDepth) >> 3;

    if (!pATI->currentMode)
    {
        /*
         * Not in DGA.  This reverse-calculates pScreenInfo->frame[XY][01] so
         * that the cursor does not move on mode switches.
         */
        xy = (Base << 3) / pATI->AdjustDepth;
        pScreenInfo->frameX0 = xy % pATI->displayWidth;
        pScreenInfo->frameY0 = xy / pATI->displayWidth;
        pScreenInfo->frameX1 =
            pScreenInfo->frameX0 + pScreenInfo->currentMode->HDisplay - 1;
        pScreenInfo->frameY1 =
            pScreenInfo->frameY0 + pScreenInfo->currentMode->VDisplay - 1;
    }

    /* Unlock registers */
    ATIUnlock(pATI);

#ifndef AVOID_CPIO

    if ((pATI->NewHW.crtc == ATI_CRTC_VGA) && (pATI->Chip < ATI_CHIP_264CT))
    {
        PutReg(CRTX(pATI->CPIO_VGABase), 0x0CU, GetByte(Base, 1));
        PutReg(CRTX(pATI->CPIO_VGABase), 0x0DU, GetByte(Base, 0));

        if (pATI->CPIO_VGAWonder)
        {
            if (pATI->Chip <= ATI_CHIP_18800_1)
                ATIModifyExtReg(pATI, 0xB0U, -1, 0x3FU, Base >> 10);
            else
            {
                ATIModifyExtReg(pATI, 0xB0U, -1, 0xBFU, Base >> 10);
                ATIModifyExtReg(pATI, 0xA3U, -1, 0xEFU, Base >> 13);

                /*
                 * I don't know if this also applies to Mach64's, but give it a
                 * shot...
                 */
                if (pATI->Chip >= ATI_CHIP_68800)
                    ATIModifyExtReg(pATI, 0xADU, -1, 0xF3U, Base >> 16);
            }
        }
    }
    else
    /*
     * On integrated controllers, there is only one set of CRTC control bits,
     * many of which are simultaneously accessible through both VGA and
     * accelerator I/O ports.  Given VGA's architectural limitations, setting
     * the CRTC's offset register to more than 256k needs to be done through
     * the accelerator port.
     */
    if (pATI->depth <= 4)
    {
        outr(CRTC_OFF_PITCH, SetBits(pATI->displayWidth >> 4, CRTC_PITCH) |
            SetBits(Base, CRTC_OFFSET));
    }
    else

#endif /* AVOID_CPIO */

    {

#ifndef AVOID_CPIO

        if (pATI->NewHW.crtc == ATI_CRTC_VGA)
            Base <<= 1;                 /* LSBit must be zero */

#endif /* AVOID_CPIO */

        outr(CRTC_OFF_PITCH, SetBits(pATI->displayWidth >> 3, CRTC_PITCH) |
            SetBits(Base, CRTC_OFFSET));
    }
}
