/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/ati/atimach64.c,v 1.51 2003/02/24 20:46:54 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.
 */
/*
 * Copyright 1999-2000 Precision Insight, Inc., Cedar Park, Texas.
 * All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
 * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#include "ati.h"
#include "atibus.h"
#include "atichip.h"
#include "atidac.h"
#include "atimach64.h"
#include "atimach64io.h"
#include "atirgb514.h"

#include "miline.h"

#ifndef DPMS_SERVER
# define DPMS_SERVER
#endif
#include "extensions/dpms.h"

/*
 * X-to-Mach64 mix translation table.
 */
static CARD8 ATIMach64ALU[16] =
{
    MIX_0,
    MIX_AND,
    MIX_SRC_AND_NOT_DST,
    MIX_SRC,
    MIX_NOT_SRC_AND_DST,
    MIX_DST,
    MIX_XOR,
    MIX_OR,
    MIX_NOR,
    MIX_XNOR,
    MIX_NOT_DST,
    MIX_SRC_OR_NOT_DST,
    MIX_NOT_SRC,
    MIX_NOT_SRC_OR_DST,
    MIX_NAND,
    MIX_1
};

/*
 * ATIMach64PreInit --
 *
 * This function fills in the Mach64 portion of an ATIHWRec that is common to
 * all video modes generated by the driver.
 */
void
ATIMach64PreInit
(
    ScrnInfoPtr pScreenInfo,
    ATIPtr      pATI,
    ATIHWPtr    pATIHW
)
{
    CARD32 bus_cntl, config_cntl;
    int    tmp;

#ifndef AVOID_CPIO

    if (pATI->depth <= 4)
        pATIHW->crtc_off_pitch = SetBits(pATI->displayWidth >> 4, CRTC_PITCH);
    else

#endif /* AVOID_CPIO */

    {
        pATIHW->crtc_off_pitch = SetBits(pATI->displayWidth >> 3, CRTC_PITCH);
    }

    if ((pATI->LockData.crtc_gen_cntl & CRTC_CSYNC_EN) && !pATI->OptionCSync)
    {
        xf86DrvMsg(pScreenInfo->scrnIndex, X_NOTICE,
            "Using composite sync to match input timing.\n");
        pATI->OptionCSync = TRUE;
    }

    pATIHW->bus_cntl = bus_cntl = inr(BUS_CNTL);
    if (pATI->Chip < ATI_CHIP_264VT4)
        pATIHW->bus_cntl = (pATIHW->bus_cntl & ~BUS_HOST_ERR_INT_EN) |
            BUS_HOST_ERR_INT;
    if (pATI->Chip < ATI_CHIP_264VTB)
    {
        pATIHW->bus_cntl &= ~(BUS_FIFO_ERR_INT_EN | BUS_ROM_DIS);
        pATIHW->bus_cntl |= SetBits(15, BUS_FIFO_WS) | BUS_FIFO_ERR_INT;
    }
    else if (pATI->MMIOInLinear)
    {
        pATIHW->bus_cntl &= ~BUS_APER_REG_DIS;
    }
    else
    {
        pATIHW->bus_cntl |= BUS_APER_REG_DIS;
    }
    if (pATI->Chip >= ATI_CHIP_264VT)
        pATIHW->bus_cntl |= BUS_EXT_REG_EN;     /* Enable Block 1 */

#ifdef AVOID_CPIO

    pATIHW->mem_vga_wp_sel = SetBits(0, MEM_VGA_WPS0) |
        SetBits(1, MEM_VGA_WPS1);
    pATIHW->mem_vga_rp_sel = SetBits(0, MEM_VGA_RPS0) |
        SetBits(1, MEM_VGA_RPS1);

#else /* AVOID_CPIO */

    pATIHW->mem_vga_wp_sel = SetBits(0, MEM_VGA_WPS0) |
        SetBits(pATIHW->nPlane, MEM_VGA_WPS1);
    pATIHW->mem_vga_rp_sel = SetBits(0, MEM_VGA_RPS0) |
        SetBits(pATIHW->nPlane, MEM_VGA_RPS1);

#endif /* AVOID_CPIO */

    pATIHW->dac_cntl = inr(DAC_CNTL) &
        ~(DAC1_CLK_SEL | DAC_PALETTE_ACCESS_CNTL | DAC_8BIT_EN);
    if (pATI->Chip >= ATI_CHIP_264CT)
        pATIHW->dac_cntl &= ~DAC_FEA_CON_EN;
    if (pATI->rgbBits == 8)
        pATIHW->dac_cntl |= DAC_8BIT_EN;

    pATIHW->gen_test_cntl = pATI->LockData.gen_test_cntl & ~GEN_CUR_EN;
    if (pATI->DAC == ATI_DAC_IBMRGB514)
        pATIHW->gen_test_cntl |= GEN_OVR_OUTPUT_EN;

    pATIHW->config_cntl = config_cntl = inr(CONFIG_CNTL);

#ifndef AVOID_CPIO

    if (pATI->UseSmallApertures)
        pATIHW->config_cntl |= CFG_MEM_VGA_AP_EN;
    else

#endif /* AVOID_CPIO */

    {
        pATIHW->config_cntl &= ~CFG_MEM_VGA_AP_EN;
    }

    if (pATI->LinearBase && (pATI->Chip < ATI_CHIP_264CT))
    {
        /* Replace linear aperture size and address */
        pATIHW->config_cntl &= ~(CFG_MEM_AP_LOC | CFG_MEM_AP_SIZE);
        pATIHW->config_cntl |= SetBits(pATI->LinearBase >> 22, CFG_MEM_AP_LOC);
        if ((pATI->Chip < ATI_CHIP_264CT) && (pATI->VideoRAM < 4096))
            pATIHW->config_cntl |= SetBits(1, CFG_MEM_AP_SIZE);
        else
            pATIHW->config_cntl |= SetBits(2, CFG_MEM_AP_SIZE);
    }

    if (pATI->Chip >= ATI_CHIP_264VTB)
    {
        pATIHW->mem_cntl = (pATI->LockData.mem_cntl &
            ~(CTL_MEM_LOWER_APER_ENDIAN | CTL_MEM_UPPER_APER_ENDIAN)) |
            SetBits(CTL_MEM_APER_BYTE_ENDIAN, CTL_MEM_LOWER_APER_ENDIAN);

        switch (pATI->bitsPerPixel)
        {
            default:
                pATIHW->mem_cntl |= SetBits(CTL_MEM_APER_BYTE_ENDIAN,
                    CTL_MEM_UPPER_APER_ENDIAN);
                break;

            case 16:
                pATIHW->mem_cntl |= SetBits(CTL_MEM_APER_WORD_ENDIAN,
                    CTL_MEM_UPPER_APER_ENDIAN);
                break;

            case 32:
                pATIHW->mem_cntl |= SetBits(CTL_MEM_APER_LONG_ENDIAN,
                    CTL_MEM_UPPER_APER_ENDIAN);
                break;
        }

        pATIHW->mpp_config = inr(MPP_CONFIG);
        pATIHW->mpp_config &=
            ~(MPP_PRESCALE | MPP_NSTATES | MPP_FORMAT | MPP_WAIT_STATE |
              MPP_INSERT_WAIT | MPP_TRISTATE_ADDR | MPP_AUTO_INC_EN |
              MPP_CHKREQ_EN | MPP_BUFFER_SIZE | MPP_BUFFER_MODE | MPP_BUSY);
        pATIHW->mpp_config |=
            (MPP_NSTATES_8 | MPP_FORMAT_DA8 | SetBits(4, MPP_WAIT_STATE) |
             MPP_CHKRDY_EN | MPP_READ_EARLY | MPP_RW_MODE | MPP_EN);
        pATIHW->mpp_strobe_seq = inr(MPP_STROBE_SEQ);
        pATIHW->mpp_strobe_seq &= ~(MPP_STB0_SEQ | MPP_STB1_SEQ);
        pATIHW->mpp_strobe_seq |=
            SetBits(0x0087U, MPP_STB0_SEQ) | SetBits(0x0083U, MPP_STB1_SEQ);
        pATIHW->tvo_cntl = 0;
    }

    /* Draw engine setup */
    if (pATI->OptionAccel)
    {
        /* Ensure apertures are enabled */
        outr(BUS_CNTL, pATIHW->bus_cntl);
        outr(CONFIG_CNTL, pATIHW->config_cntl);

        /*
         * When possible, max out command FIFO size.
         */
        if (pATI->Chip >= ATI_CHIP_264VT4)
            pATIHW->gui_cntl = inm(GUI_CNTL) & ~CMDFIFO_SIZE_MODE;

        /* Initialise destination registers */
        pATIHW->dst_off_pitch =
            SetBits((pATI->displayWidth * pATI->XModifier) >> 3, DST_PITCH);
        pATIHW->dst_cntl = DST_X_DIR | DST_Y_DIR | DST_LAST_PEL;

        /* Initialise source registers */
        pATIHW->src_off_pitch = pATIHW->dst_off_pitch;
        pATIHW->src_width1 = pATIHW->src_height1 =
            pATIHW->src_width2 = pATIHW->src_height2 = 1;
        pATIHW->src_cntl = SRC_LINE_X_DIR;

        /* Initialise scissor, allowing for offscreen areas */
        pATIHW->sc_right = (pATI->displayWidth * pATI->XModifier) - 1;
        tmp = (pScreenInfo->videoRam * (1024 * 8) /
            pATI->displayWidth / pATI->bitsPerPixel) - 1;
        if (tmp > ATIMach64MaxY)
            tmp = ATIMach64MaxY;
        pATIHW->sc_bottom = tmp;
        pATI->sc_left_right = SetWord(pATI->NewHW.sc_right, 1) |
            SetWord(pATI->NewHW.sc_left, 0);
        pATI->sc_top_bottom =  SetWord(pATI->NewHW.sc_bottom, 1) |
            SetWord(pATI->NewHW.sc_top, 0);

        /* Initialise data path */
        pATIHW->dp_frgd_clr = (CARD32)(-1);
        pATIHW->dp_write_mask = (CARD32)(-1);

        switch (pATI->depth)
        {
            case 8:
                pATIHW->dp_chain_mask = DP_CHAIN_8BPP;
                pATIHW->dp_pix_width =
                    SetBits(PIX_WIDTH_8BPP, DP_DST_PIX_WIDTH) |
                    SetBits(PIX_WIDTH_8BPP, DP_SRC_PIX_WIDTH) |
                    SetBits(PIX_WIDTH_1BPP, DP_HOST_PIX_WIDTH);
                break;

            case 15:
                pATIHW->dp_chain_mask = DP_CHAIN_15BPP_1555;
                pATIHW->dp_pix_width =
                    SetBits(PIX_WIDTH_15BPP, DP_DST_PIX_WIDTH) |
                    SetBits(PIX_WIDTH_15BPP, DP_SRC_PIX_WIDTH) |
                    SetBits(PIX_WIDTH_1BPP, DP_HOST_PIX_WIDTH);
                break;

            case 16:
                pATIHW->dp_chain_mask = DP_CHAIN_16BPP_565;
                pATIHW->dp_pix_width =
                    SetBits(PIX_WIDTH_16BPP, DP_DST_PIX_WIDTH) |
                    SetBits(PIX_WIDTH_16BPP, DP_SRC_PIX_WIDTH) |
                    SetBits(PIX_WIDTH_1BPP, DP_HOST_PIX_WIDTH);
                break;

            case 24:
                if (pATI->bitsPerPixel == 24)
                {
                    pATIHW->dp_chain_mask = DP_CHAIN_24BPP_888;
                    pATIHW->dp_pix_width =
                        SetBits(PIX_WIDTH_8BPP, DP_DST_PIX_WIDTH) |
                        SetBits(PIX_WIDTH_8BPP, DP_SRC_PIX_WIDTH) |
                        SetBits(PIX_WIDTH_1BPP, DP_HOST_PIX_WIDTH);
                }
                else
                {
                    pATIHW->dp_chain_mask = DP_CHAIN_32BPP_8888;
                    pATIHW->dp_pix_width =
                        SetBits(PIX_WIDTH_32BPP, DP_DST_PIX_WIDTH) |
                        SetBits(PIX_WIDTH_32BPP, DP_SRC_PIX_WIDTH) |
                        SetBits(PIX_WIDTH_1BPP, DP_HOST_PIX_WIDTH);
                }
                break;

            default:
                break;
        }

#if X_BYTE_ORDER == X_LITTLE_ENDIAN

        pATIHW->dp_pix_width |= DP_BYTE_PIX_ORDER;

#endif /* X_BYTE_ORDER */

        pATIHW->dp_mix = SetBits(MIX_SRC, DP_FRGD_MIX) |
            SetBits(MIX_DST, DP_BKGD_MIX);
        pATIHW->dp_src = DP_MONO_SRC_ALLONES |
            SetBits(SRC_FRGD, DP_FRGD_SRC) | SetBits(SRC_BKGD, DP_BKGD_SRC);

        /* Initialise colour compare */
        pATIHW->clr_cmp_msk = (1 << pATI->depth) - 1;

        /* Restore aperture enablement */
        outr(BUS_CNTL, bus_cntl);
        outr(CONFIG_CNTL, config_cntl);
    }
}

/*
 * ATIMach64Save --
 *
 * This function is called to save the Mach64 portion of the current video
 * state.
 */
void
ATIMach64Save
(
    ATIPtr      pATI,
    ATIHWPtr    pATIHW
)
{
    pATIHW->crtc_h_total_disp = inr(CRTC_H_TOTAL_DISP);
    pATIHW->crtc_h_sync_strt_wid = inr(CRTC_H_SYNC_STRT_WID);
    pATIHW->crtc_v_total_disp = inr(CRTC_V_TOTAL_DISP);
    pATIHW->crtc_v_sync_strt_wid = inr(CRTC_V_SYNC_STRT_WID);

    pATIHW->crtc_off_pitch = inr(CRTC_OFF_PITCH);

    pATIHW->crtc_gen_cntl = inr(CRTC_GEN_CNTL);

    pATIHW->ovr_clr = inr(OVR_CLR);
    pATIHW->ovr_wid_left_right = inr(OVR_WID_LEFT_RIGHT);
    pATIHW->ovr_wid_top_bottom = inr(OVR_WID_TOP_BOTTOM);

    pATIHW->cur_clr0 = inr(CUR_CLR0);
    pATIHW->cur_clr1 = inr(CUR_CLR1);
    pATIHW->cur_offset = inr(CUR_OFFSET);
    pATIHW->cur_horz_vert_posn = inr(CUR_HORZ_VERT_POSN);
    pATIHW->cur_horz_vert_off = inr(CUR_HORZ_VERT_OFF);

    pATIHW->clock_cntl = inr(CLOCK_CNTL);

    pATIHW->bus_cntl = inr(BUS_CNTL);

    pATIHW->mem_vga_wp_sel = inr(MEM_VGA_WP_SEL);
    pATIHW->mem_vga_rp_sel = inr(MEM_VGA_RP_SEL);

    pATIHW->dac_cntl = inr(DAC_CNTL);

    pATIHW->config_cntl = inr(CONFIG_CNTL);

    pATIHW->gen_test_cntl = inr(GEN_TEST_CNTL) & ~GEN_CUR_EN;

    if (pATI->Chip >= ATI_CHIP_264VTB)
    {
        pATIHW->mem_cntl = inr(MEM_CNTL);
        pATIHW->mpp_config = inr(MPP_CONFIG);
        pATIHW->mpp_strobe_seq = inr(MPP_STROBE_SEQ);
        pATIHW->tvo_cntl = inr(TVO_CNTL);
    }

    /* Save draw engine state */
    if (pATI->OptionAccel && (pATIHW == &pATI->OldHW))
    {
        /* Ensure apertures are enabled */
        outr(BUS_CNTL, pATI->NewHW.bus_cntl);
        outr(CONFIG_CNTL, pATI->NewHW.config_cntl);

        ATIMach64WaitForIdle(pATI);

        /* Save FIFO size */
        if (pATI->Chip >= ATI_CHIP_264VT4)
            pATIHW->gui_cntl = inm(GUI_CNTL);

        /* Save destination registers */
        pATIHW->dst_off_pitch = inm(DST_OFF_PITCH);
        pATIHW->dst_x = inm(DST_X);
        pATIHW->dst_y = inm(DST_Y);
        pATIHW->dst_height = inm(DST_HEIGHT);
        pATIHW->dst_bres_err = inm(DST_BRES_ERR);
        pATIHW->dst_bres_inc = inm(DST_BRES_INC);
        pATIHW->dst_bres_dec = inm(DST_BRES_DEC);
        pATIHW->dst_cntl = inm(DST_CNTL);

        /* Save source registers */
        pATIHW->src_off_pitch = inm(SRC_OFF_PITCH);
        pATIHW->src_x = inm(SRC_X);
        pATIHW->src_y = inm(SRC_Y);
        pATIHW->src_width1 = inm(SRC_WIDTH1);
        pATIHW->src_height1 = inm(SRC_HEIGHT1);
        pATIHW->src_x_start = inm(SRC_X_START);
        pATIHW->src_y_start = inm(SRC_Y_START);
        pATIHW->src_width2 = inm(SRC_WIDTH2);
        pATIHW->src_height2 = inm(SRC_HEIGHT2);
        pATIHW->src_cntl = inm(SRC_CNTL);

        /* Save host data register */
        pATIHW->host_cntl = inm(HOST_CNTL);

        /* Save pattern registers */
        pATIHW->pat_reg0 = inm(PAT_REG0);
        pATIHW->pat_reg1 = inm(PAT_REG1);
        pATIHW->pat_cntl = inm(PAT_CNTL);

        /* Save scissor registers */
        pATIHW->sc_left = pATI->sc_left = inm(SC_LEFT);
        pATIHW->sc_right = pATI->sc_right = inm(SC_RIGHT);
        pATIHW->sc_top = pATI->sc_top = inm(SC_TOP);
        pATIHW->sc_bottom = pATI->sc_bottom = inm(SC_BOTTOM);

        /* Save data path registers */
        pATIHW->dp_bkgd_clr = inm(DP_BKGD_CLR);
        pATIHW->dp_frgd_clr = inm(DP_FRGD_CLR);
        pATIHW->dp_write_mask = inm(DP_WRITE_MASK);
        pATIHW->dp_chain_mask = inm(DP_CHAIN_MASK);
        pATIHW->dp_pix_width = inm(DP_PIX_WIDTH);
        pATIHW->dp_mix = inm(DP_MIX);
        pATIHW->dp_src = inm(DP_SRC);

        /* Save colour compare registers */
        pATIHW->clr_cmp_clr = inm(CLR_CMP_CLR);
        pATIHW->clr_cmp_msk = inm(CLR_CMP_MSK);
        pATIHW->clr_cmp_cntl = inm(CLR_CMP_CNTL);

        /* Save context */
        pATIHW->context_mask = inm(CONTEXT_MASK);

        /* Restore aperture enablement */
        outr(BUS_CNTL, pATIHW->bus_cntl);
        outr(CONFIG_CNTL, pATIHW->config_cntl);
    }
}

/*
 * ATIMach64Calculate --
 *
 * This function is called to fill in the Mach64 portion of an ATIHWRec.
 */
void
ATIMach64Calculate
(
    ATIPtr         pATI,
    ATIHWPtr       pATIHW,
    DisplayModePtr pMode
)
{
    int VDisplay;

    /* If not already done adjust horizontal timings */
    if (!pMode->CrtcHAdjusted)
    {
        pMode->CrtcHAdjusted = TRUE;
        /* XXX Deal with Blank Start/End and overscan later */
        pMode->CrtcHDisplay = (pMode->HDisplay >> 3) - 1;
        pMode->CrtcHSyncStart = (pMode->HSyncStart >> 3) - 1;
        pMode->CrtcHSyncEnd = (pMode->HSyncEnd >> 3) - 1;
        pMode->CrtcHTotal = (pMode->HTotal >> 3) - 1;

        /* Make adjustments if sync pulse width is out-of-bounds */
        if ((pMode->CrtcHSyncEnd - pMode->CrtcHSyncStart) >
            (int)MaxBits(CRTC_H_SYNC_WID))
            pMode->CrtcHSyncEnd =
                pMode->CrtcHSyncStart + MaxBits(CRTC_H_SYNC_WID);
        else if (pMode->CrtcHSyncStart == pMode->CrtcHSyncEnd)
        {
            if (pMode->CrtcHDisplay < pMode->CrtcHSyncStart)
                pMode->CrtcHSyncStart--;
            else if (pMode->CrtcHSyncEnd < pMode->CrtcHTotal)
                pMode->CrtcHSyncEnd++;
        }
    }

    /*
     * Always re-do vertical adjustments.
     */
    pMode->CrtcVDisplay = pMode->VDisplay;
    pMode->CrtcVSyncStart = pMode->VSyncStart;
    pMode->CrtcVSyncEnd = pMode->VSyncEnd;
    pMode->CrtcVTotal = pMode->VTotal;

    if ((pATI->Chip >= ATI_CHIP_264CT) &&
        ((pMode->Flags & V_DBLSCAN) || (pMode->VScan > 1)))
    {
        pMode->CrtcVDisplay <<= 1;
        pMode->CrtcVSyncStart <<= 1;
        pMode->CrtcVSyncEnd <<= 1;
        pMode->CrtcVTotal <<= 1;
    }

    /*
     * Might as well default to the same as VGA with respect to sync
     * polarities.
     */
    if ((!(pMode->Flags & (V_PHSYNC | V_NHSYNC))) ||
        (!(pMode->Flags & (V_PVSYNC | V_NVSYNC))))
    {
        pMode->Flags &= ~(V_PHSYNC | V_NHSYNC | V_PVSYNC | V_NVSYNC);

        if (pATI->OptionPanelDisplay && (pATI->LCDPanelID >= 0))
            VDisplay = pATI->LCDVertical;
        else
            VDisplay = pMode->CrtcVDisplay;

        if (VDisplay < 400)
            pMode->Flags |= V_PHSYNC | V_NVSYNC;
        else if (VDisplay < 480)
            pMode->Flags |= V_NHSYNC | V_PVSYNC;
        else if (VDisplay < 768)
            pMode->Flags |= V_NHSYNC | V_NVSYNC;
        else
            pMode->Flags |= V_PHSYNC | V_PVSYNC;
    }

    pMode->CrtcVDisplay--;
    pMode->CrtcVSyncStart--;
    pMode->CrtcVSyncEnd--;
    pMode->CrtcVTotal--;
    /* Make sure sync pulse is not too wide */
    if ((pMode->CrtcVSyncEnd - pMode->CrtcVSyncStart) >
         (int)MaxBits(CRTC_V_SYNC_WID))
        pMode->CrtcVSyncEnd = pMode->CrtcVSyncStart + MaxBits(CRTC_V_SYNC_WID);
    pMode->CrtcVAdjusted = TRUE;                /* Redundant */

    /* Build register contents */
    pATIHW->crtc_h_total_disp =
        SetBits(pMode->CrtcHTotal, CRTC_H_TOTAL) |
            SetBits(pMode->CrtcHDisplay, CRTC_H_DISP);
    pATIHW->crtc_h_sync_strt_wid =
        SetBits(pMode->CrtcHSyncStart, CRTC_H_SYNC_STRT) |
            SetBits(pMode->CrtcHSkew, CRTC_H_SYNC_DLY) |         /* ? */
            SetBits(GetBits(pMode->CrtcHSyncStart, 0x0100U),
                CRTC_H_SYNC_STRT_HI) |
            SetBits(pMode->CrtcHSyncEnd - pMode->CrtcHSyncStart,
                CRTC_H_SYNC_WID);
    if (pMode->Flags & V_NHSYNC)
        pATIHW->crtc_h_sync_strt_wid |= CRTC_H_SYNC_POL;

    pATIHW->crtc_v_total_disp =
        SetBits(pMode->CrtcVTotal, CRTC_V_TOTAL) |
            SetBits(pMode->CrtcVDisplay, CRTC_V_DISP);
    pATIHW->crtc_v_sync_strt_wid =
        SetBits(pMode->CrtcVSyncStart, CRTC_V_SYNC_STRT) |
            SetBits(pMode->CrtcVSyncEnd - pMode->CrtcVSyncStart,
                CRTC_V_SYNC_WID);
    if (pMode->Flags & V_NVSYNC)
        pATIHW->crtc_v_sync_strt_wid |= CRTC_V_SYNC_POL;

    pATIHW->crtc_gen_cntl = inr(CRTC_GEN_CNTL) &
        ~(CRTC_DBL_SCAN_EN | CRTC_INTERLACE_EN |
          CRTC_HSYNC_DIS | CRTC_VSYNC_DIS | CRTC_CSYNC_EN |
          CRTC_PIX_BY_2_EN | CRTC_DISPLAY_DIS | CRTC_VGA_XOVERSCAN |
          CRTC_PIX_WIDTH | CRTC_BYTE_PIX_ORDER |
          CRTC_VGA_128KAP_PAGING | CRTC_VFC_SYNC_TRISTATE |
          CRTC_LOCK_REGS |              /* Already off, but ... */
          CRTC_SYNC_TRISTATE | CRTC_DISP_REQ_EN |
          CRTC_VGA_TEXT_132 | CRTC_CUR_B_TEST);
    pATIHW->crtc_gen_cntl |=
        CRTC_EXT_DISP_EN | CRTC_EN | CRTC_VGA_LINEAR | CRTC_CNT_EN;
    switch (pATI->depth)
    {

#ifndef AVOID_CPIO

        case 1:
            pATIHW->crtc_gen_cntl |= SetBits(PIX_WIDTH_1BPP, CRTC_PIX_WIDTH);
            break;

        case 4:
            pATIHW->crtc_gen_cntl |= SetBits(PIX_WIDTH_4BPP, CRTC_PIX_WIDTH);
            break;

#endif /* AVOID_CPIO */

        case 8:
            pATIHW->crtc_gen_cntl |= SetBits(PIX_WIDTH_8BPP, CRTC_PIX_WIDTH);
            break;

        case 15:
            pATIHW->crtc_gen_cntl |= SetBits(PIX_WIDTH_15BPP, CRTC_PIX_WIDTH);
            break;

        case 16:
            pATIHW->crtc_gen_cntl |= SetBits(PIX_WIDTH_16BPP, CRTC_PIX_WIDTH);
            break;

        case 24:
            if (pATI->bitsPerPixel == 24)
            {
                pATIHW->crtc_gen_cntl |=
                    SetBits(PIX_WIDTH_24BPP, CRTC_PIX_WIDTH);
                break;
            }
            if (pATI->bitsPerPixel != 32)
                break;
            /* Fall through */

        case 32:
            pATIHW->crtc_gen_cntl |= SetBits(PIX_WIDTH_32BPP, CRTC_PIX_WIDTH);
            break;

        default:
            break;
    }
    if ((pMode->Flags & V_DBLSCAN) || (pMode->VScan > 1))
        pATIHW->crtc_gen_cntl |= CRTC_DBL_SCAN_EN;
    if (pMode->Flags & V_INTERLACE)
        pATIHW->crtc_gen_cntl |= CRTC_INTERLACE_EN;
    if (pATI->OptionCSync || (pMode->Flags & (V_CSYNC | V_PCSYNC)))
        pATIHW->crtc_gen_cntl |= CRTC_CSYNC_EN;
    /* For now, set display FIFO low water mark as high as possible */
    if (pATI->Chip < ATI_CHIP_264VTB)
        pATIHW->crtc_gen_cntl |= CRTC_FIFO_LWM;
}

/*
 * ATIMach64Set --
 *
 * This function is called to load a Mach64's accelerator CRTC and draw engine.
 */
void
ATIMach64Set
(
    ATIPtr      pATI,
    ATIHWPtr    pATIHW
)
{
    /* First, turn off the display */
    outr(CRTC_GEN_CNTL, pATIHW->crtc_gen_cntl & ~CRTC_EN);

    if ((pATIHW->FeedbackDivider > 0) &&
        (pATI->ProgrammableClock != ATI_CLOCK_NONE))
        ATIClockSet(pATI, pATIHW);              /* Programme clock */

    if (pATI->DAC == ATI_DAC_IBMRGB514)
        ATIRGB514Set(pATI, pATIHW);

    /* Load Mach64 CRTC registers */
    outr(CRTC_H_TOTAL_DISP, pATIHW->crtc_h_total_disp);
    outr(CRTC_H_SYNC_STRT_WID, pATIHW->crtc_h_sync_strt_wid);
    outr(CRTC_V_TOTAL_DISP, pATIHW->crtc_v_total_disp);
    outr(CRTC_V_SYNC_STRT_WID, pATIHW->crtc_v_sync_strt_wid);

    outr(CRTC_OFF_PITCH, pATIHW->crtc_off_pitch);

    /* Load overscan registers */
    outr(OVR_CLR, pATIHW->ovr_clr);
    outr(OVR_WID_LEFT_RIGHT, pATIHW->ovr_wid_left_right);
    outr(OVR_WID_TOP_BOTTOM, pATIHW->ovr_wid_top_bottom);

    /* Load hardware cursor registers */
    outr(CUR_CLR0, pATIHW->cur_clr0);
    outr(CUR_CLR1, pATIHW->cur_clr1);
    outr(CUR_OFFSET, pATIHW->cur_offset);
    outr(CUR_HORZ_VERT_POSN, pATIHW->cur_horz_vert_posn);
    outr(CUR_HORZ_VERT_OFF, pATIHW->cur_horz_vert_off);

    /* Set pixel clock */
    outr(CLOCK_CNTL, pATIHW->clock_cntl | CLOCK_STROBE);

    outr(GEN_TEST_CNTL, pATIHW->gen_test_cntl | GEN_GUI_EN);
    outr(GEN_TEST_CNTL, pATIHW->gen_test_cntl);
    outr(GEN_TEST_CNTL, pATIHW->gen_test_cntl | GEN_GUI_EN);

    /* Finalise CRTC setup and turn on the screen */
    outr(CRTC_GEN_CNTL, pATIHW->crtc_gen_cntl);

    /* Load draw engine */
    if (pATI->OptionAccel)
    {
        /* Clobber MMIO cache */
        (void)memset(pATI->MMIOCached, 0, SizeOf(pATI->MMIOCached));

        /* Ensure apertures are enabled */
        outr(BUS_CNTL, pATI->NewHW.bus_cntl);
        outr(CONFIG_CNTL, pATI->NewHW.config_cntl);

        pATI->EngineIsBusy = TRUE;      /* Force engine poll */
        ATIMach64WaitForIdle(pATI);

        /* Load FIFO size */
        if (pATI->Chip >= ATI_CHIP_264VT4)
        {
            outm(GUI_CNTL, pATIHW->gui_cntl);
            pATI->nAvailableFIFOEntries = 0;
            ATIMach64PollEngineStatus(pATI);
        }

        /* Set FIFO depth */
        pATI->nFIFOEntries = pATI->nAvailableFIFOEntries;

        /* Load destination registers */
        ATIMach64WaitForFIFO(pATI, 7);
        outf(DST_OFF_PITCH, pATIHW->dst_off_pitch);
        outf(DST_Y_X, SetWord(pATIHW->dst_x, 1) | SetWord(pATIHW->dst_y, 0));
        outf(DST_HEIGHT, pATIHW->dst_height);
        outf(DST_BRES_ERR, pATIHW->dst_bres_err);
        outf(DST_BRES_INC, pATIHW->dst_bres_inc);
        outf(DST_BRES_DEC, pATIHW->dst_bres_dec);
        outf(DST_CNTL, pATIHW->dst_cntl);

        /* Load source registers */
        ATIMach64WaitForFIFO(pATI, 6);
        outf(SRC_OFF_PITCH, pATIHW->src_off_pitch);
        outf(SRC_Y_X, SetWord(pATIHW->src_x, 1) | SetWord(pATIHW->src_y, 0));
        outf(SRC_HEIGHT1_WIDTH1,
            SetWord(pATIHW->src_width1, 1) | SetWord(pATIHW->src_height1, 0));
        outf(SRC_Y_X_START,
            SetWord(pATIHW->src_x_start, 1) | SetWord(pATIHW->src_y_start, 0));
        outf(SRC_HEIGHT2_WIDTH2,
            SetWord(pATIHW->src_width2, 1) | SetWord(pATIHW->src_height2, 0));
        outf(SRC_CNTL, pATIHW->src_cntl);

        /* Load host data register */
        ATIMach64WaitForFIFO(pATI, 1);
        outf(HOST_CNTL, pATIHW->host_cntl);

        /* Set host transfer window address and size clamp */
        pATI->pHOST_DATA =
            (CARD8 *)pATI->pBlock[GetBits(HOST_DATA_0, BLOCK_SELECT)] +
            (HOST_DATA_0 & MM_IO_SELECT);
        pATI->nHostFIFOEntries = pATI->nFIFOEntries >> 1;
        if (pATI->nHostFIFOEntries > 16)
            pATI->nHostFIFOEntries = 16;

        /* Load pattern registers */
        ATIMach64WaitForFIFO(pATI, 3);
        outf(PAT_REG0, pATIHW->pat_reg0);
        outf(PAT_REG1, pATIHW->pat_reg1);
        outf(PAT_CNTL, pATIHW->pat_cntl);

        /* Load scissor registers */
        ATIMach64WaitForFIFO(pATI, 2);
        outf(SC_LEFT_RIGHT,
            SetWord(pATIHW->sc_right, 1) | SetWord(pATIHW->sc_left, 0));
        outf(SC_TOP_BOTTOM,
            SetWord(pATIHW->sc_bottom, 1) | SetWord(pATIHW->sc_top, 0));
        pATI->sc_left = pATIHW->sc_left;
        pATI->sc_right = pATIHW->sc_right;
        pATI->sc_top = pATIHW->sc_top;
        pATI->sc_bottom = pATIHW->sc_bottom;

        /* Load data path registers */
        ATIMach64WaitForFIFO(pATI, 7);
        outf(DP_BKGD_CLR, pATIHW->dp_bkgd_clr);
        outf(DP_FRGD_CLR, pATIHW->dp_frgd_clr);
        outf(DP_WRITE_MASK, pATIHW->dp_write_mask);
        outf(DP_CHAIN_MASK, pATIHW->dp_chain_mask);
        outf(DP_PIX_WIDTH, pATIHW->dp_pix_width);
        outf(DP_MIX, pATIHW->dp_mix);
        outf(DP_SRC, pATIHW->dp_src);

        /* Load colour compare registers */
        ATIMach64WaitForFIFO(pATI, 3);
        outf(CLR_CMP_CLR, pATIHW->clr_cmp_clr);
        outf(CLR_CMP_MSK, pATIHW->clr_cmp_msk);
        outf(CLR_CMP_CNTL, pATIHW->clr_cmp_cntl);

        /* Load context mask */
        ATIMach64WaitForFIFO(pATI, 1);
        outf(CONTEXT_MASK, pATIHW->context_mask);

        ATIMach64WaitForIdle(pATI);

        if (pATI->OptionMMIOCache)
        {
            /*
             * Enable write caching for selected MMIO registers.  This can only
             * be done for those registers whose value does not change without
             * driver intervention.
             */

            CacheRegister(SRC_CNTL);

            CacheRegister(HOST_CNTL);

            CacheRegister(PAT_REG0);
            CacheRegister(PAT_REG1);
            CacheRegister(PAT_CNTL);

            CacheRegister(SC_LEFT_RIGHT);
            CacheRegister(SC_TOP_BOTTOM);

            CacheRegister(DP_BKGD_CLR);
            CacheRegister(DP_FRGD_CLR);
            CacheRegister(DP_WRITE_MASK);
            CacheRegister(DP_MIX);

            CacheRegister(CLR_CMP_CLR);
            CacheRegister(CLR_CMP_MSK);
            CacheRegister(CLR_CMP_CNTL);
        }
    }

    /* Aperture setup */
    outr(MEM_VGA_WP_SEL, pATIHW->mem_vga_wp_sel);
    outr(MEM_VGA_RP_SEL, pATIHW->mem_vga_rp_sel);

    outr(DAC_CNTL, pATIHW->dac_cntl);

    outr(CONFIG_CNTL, pATIHW->config_cntl);
    outr(BUS_CNTL, pATIHW->bus_cntl);

    if (pATI->Chip >= ATI_CHIP_264VTB)
    {
        outr(MEM_CNTL, pATIHW->mem_cntl);
        outr(MPP_CONFIG, pATIHW->mpp_config);
        outr(MPP_STROBE_SEQ, pATIHW->mpp_strobe_seq);
        outr(TVO_CNTL, pATIHW->tvo_cntl);
    }
}

/*
 * ATIMach64SaveScreen --
 *
 * This function blanks or unblanks a Mach64 screen.
 */
void
ATIMach64SaveScreen
(
    ATIPtr pATI,
    int    Mode
)
{
    CARD32 crtc_gen_cntl = inr(CRTC_GEN_CNTL);

    switch (Mode)
    {
        case SCREEN_SAVER_OFF:
        case SCREEN_SAVER_FORCER:
            outr(CRTC_GEN_CNTL, crtc_gen_cntl & ~CRTC_DISPLAY_DIS);
            break;

        case SCREEN_SAVER_ON:
        case SCREEN_SAVER_CYCLE:
            outr(CRTC_GEN_CNTL, crtc_gen_cntl | CRTC_DISPLAY_DIS);
            break;

        default:
            break;
    }
}

/*
 * ATIMach64SetDPMSMode --
 *
 * This function sets a Mach64's VESA Display Power Management Signaling mode.
 */
void
ATIMach64SetDPMSMode
(
    ScrnInfoPtr pScreenInfo,
    ATIPtr      pATI,
    int         DPMSMode
)
{
    CARD32 crtc_gen_cntl =
        inr(CRTC_GEN_CNTL) & ~(CRTC_HSYNC_DIS | CRTC_VSYNC_DIS);

    switch (DPMSMode)
    {
        case DPMSModeOn:        /* HSync on, VSync on */
            break;

        case DPMSModeStandby:   /* HSync off, VSync on */
            crtc_gen_cntl |= CRTC_HSYNC_DIS;
            break;

        case DPMSModeSuspend:   /* HSync on, VSync off */
            crtc_gen_cntl |= CRTC_VSYNC_DIS;
            break;

        case DPMSModeOff:       /* HSync off, VSync off */
            crtc_gen_cntl |= CRTC_HSYNC_DIS | CRTC_VSYNC_DIS;
            break;

        default:                /* Muffle compiler */
            return;
    }

    if (pATI->pXAAInfo && pATI->pXAAInfo->NeedToSync)
        (*pATI->pXAAInfo->Sync)(pScreenInfo);

    outr(CRTC_GEN_CNTL, crtc_gen_cntl);

    if (pATI->OptionPanelDisplay && (pATI->LCDPanelID >= 0))
    {
        CARD32 lcd_index = 0;

        /*
         * ATI's BIOS simply turns the panel on and off, so do the same by
         * default, but keep the previous behaviour around for reference.
         */
        if (pATI->OptionDevel)
        {
            CARD32 power_management;

            if (pATI->Chip == ATI_CHIP_264LT)
                power_management = inr(POWER_MANAGEMENT);
            else /* if ((pATI->Chip == ATI_CHIP_264LTPRO) ||
                        (pATI->Chip == ATI_CHIP_264XL) ||
                        (pATI->Chip == ATI_CHIP_MOBILITY)) */
            {
                lcd_index = inr(LCD_INDEX);
                power_management = ATIGetMach64LCDReg(LCD_POWER_MANAGEMENT);
            }

            power_management &= ~(STANDBY_NOW | SUSPEND_NOW);

            switch (DPMSMode)
            {
                case DPMSModeOn:
                    break;

                case DPMSModeStandby:
                    power_management |= STANDBY_NOW;
                    break;

                case DPMSModeSuspend:
                    power_management |= SUSPEND_NOW;
                    break;

                case DPMSModeOff:
                    power_management |= STANDBY_NOW | SUSPEND_NOW;  /* ? */
                    break;

                default:        /* Muffle compiler */
                    return;
            }

            if (pATI->Chip == ATI_CHIP_264LT)
                outr(POWER_MANAGEMENT, power_management);
            else /* if ((pATI->Chip == ATI_CHIP_264LTPRO) ||
                        (pATI->Chip == ATI_CHIP_264XL) ||
                        (pATI->Chip == ATI_CHIP_MOBILITY)) */
            {
                ATIPutMach64LCDReg(LCD_POWER_MANAGEMENT, power_management);
                outr(LCD_INDEX, lcd_index);
            }
        }
        else
        {
            CARD32 lcd_gen_ctrl;

            if (pATI->Chip == ATI_CHIP_264LT)
                lcd_gen_ctrl = inr(LCD_GEN_CTRL);
            else /* if ((pATI->Chip == ATI_CHIP_264LTPRO) ||
                        (pATI->Chip == ATI_CHIP_264XL) ||
                        (pATI->Chip == ATI_CHIP_MOBILITY)) */
            {
                lcd_index = inr(LCD_INDEX);
                lcd_gen_ctrl = ATIGetMach64LCDReg(LCD_GEN_CNTL);
            }

            if (DPMSMode == DPMSModeOn)
                lcd_gen_ctrl |= LCD_ON;
            else
                lcd_gen_ctrl &= ~LCD_ON;

            if (pATI->Chip == ATI_CHIP_264LT)
                outr(LCD_GEN_CTRL, lcd_gen_ctrl);
            else /* if ((pATI->Chip == ATI_CHIP_264LTPRO) ||
                        (pATI->Chip == ATI_CHIP_264XL) ||
                        (pATI->Chip == ATI_CHIP_MOBILITY)) */
            {
                ATIPutMach64LCDReg(LCD_GEN_CNTL, lcd_gen_ctrl);
                outr(LCD_INDEX, lcd_index);
            }
        }
    }
}

/*
 * ATIMach64ValidateClip --
 *
 * This function ensures the current scissor settings do not interfere with
 * the current draw request.
 */
static void
ATIMach64ValidateClip
(
    ATIPtr pATI,
    int    sc_left,
    int    sc_right,
    int    sc_top,
    int    sc_bottom
)
{
    if ((sc_left < (int)pATI->sc_left) || (sc_right > (int)pATI->sc_right))
    {
        outf(SC_LEFT_RIGHT, pATI->sc_left_right);
        pATI->sc_left = pATI->NewHW.sc_left;
        pATI->sc_right = pATI->NewHW.sc_right;
    }

    if ((sc_top < (int)pATI->sc_top) || (sc_bottom > (int)pATI->sc_bottom))
    {
        outf(SC_TOP_BOTTOM, pATI->sc_top_bottom);
        pATI->sc_top = pATI->NewHW.sc_top;
        pATI->sc_bottom = pATI->NewHW.sc_bottom;
    }
}

/*
 * ATIMach64Sync --
 *
 * This is called to wait for the draw engine to become idle.
 */
static void
ATIMach64Sync
(
    ScrnInfoPtr pScreenInfo
)
{
    ATIPtr pATI = ATIPTR(pScreenInfo);

    ATIMach64WaitForIdle(pATI);

    if (pATI->OptionMMIOCache)
    {
        /*
         * For debugging purposes, attempt to verify that each cached register
         * should actually be cached.
         */
        if (RegisterIsCached(SRC_CNTL) &&
            (CacheSlot(SRC_CNTL) != inm(SRC_CNTL)))
        {
            UncacheRegister(SRC_CNTL);
            xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
                "SRC_CNTL write cache disabled!\n");
        }

        if (RegisterIsCached(HOST_CNTL) &&
            (CacheSlot(HOST_CNTL) != inm(HOST_CNTL)))
        {
            UncacheRegister(HOST_CNTL);
            xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
                "HOST_CNTL write cache disabled!\n");
        }

        if (RegisterIsCached(PAT_REG0) &&
            (CacheSlot(PAT_REG0) != inm(PAT_REG0)))
        {
            UncacheRegister(PAT_REG0);
            xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
                "PAT_REG0 write cache disabled!\n");
        }

        if (RegisterIsCached(PAT_REG1) &&
            (CacheSlot(PAT_REG1) != inm(PAT_REG1)))
        {
            UncacheRegister(PAT_REG1);
            xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
                "PAT_REG1 write cache disabled!\n");
        }

        if (RegisterIsCached(PAT_CNTL) &&
            (CacheSlot(PAT_CNTL) != inm(PAT_CNTL)))
        {
            UncacheRegister(PAT_CNTL);
            xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
                "PAT_CNTL write cache disabled!\n");
        }

        if (RegisterIsCached(SC_LEFT_RIGHT) &&
            (CacheSlot(SC_LEFT_RIGHT) !=
             (SetWord(inm(SC_RIGHT), 1) | SetWord(inm(SC_LEFT), 0))))
        {
            UncacheRegister(SC_LEFT_RIGHT);
            xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
                "SC_LEFT_RIGHT write cache disabled!\n");
        }

        if (RegisterIsCached(SC_TOP_BOTTOM) &&
            (CacheSlot(SC_TOP_BOTTOM) !=
             (SetWord(inm(SC_BOTTOM), 1) | SetWord(inm(SC_TOP), 0))))
        {
            UncacheRegister(SC_TOP_BOTTOM);
            xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
                "SC_TOP_BOTTOM write cache disabled!\n");
        }

        if (RegisterIsCached(DP_BKGD_CLR) &&
            (CacheSlot(DP_BKGD_CLR) != inm(DP_BKGD_CLR)))
        {
            UncacheRegister(DP_BKGD_CLR);
            xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
                "DP_BKGD_CLR write cache disabled!\n");
        }

        if (RegisterIsCached(DP_FRGD_CLR) &&
            (CacheSlot(DP_FRGD_CLR) != inm(DP_FRGD_CLR)))
        {
            UncacheRegister(DP_FRGD_CLR);
            xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
                "DP_FRGD_CLR write cache disabled!\n");
        }

        if (RegisterIsCached(DP_WRITE_MASK) &&
            (CacheSlot(DP_WRITE_MASK) != inm(DP_WRITE_MASK)))
        {
            UncacheRegister(DP_WRITE_MASK);
            xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
                "DP_WRITE_MASK write cache disabled!\n");
        }

        if (RegisterIsCached(DP_MIX) &&
            (CacheSlot(DP_MIX) != inm(DP_MIX)))
        {
            UncacheRegister(DP_MIX);
            xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
                "DP_MIX write cache disabled!\n");
        }

        if (RegisterIsCached(CLR_CMP_CLR) &&
            (CacheSlot(CLR_CMP_CLR) != inm(CLR_CMP_CLR)))
        {
            UncacheRegister(CLR_CMP_CLR);
            xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
                "CLR_CMP_CLR write cache disabled!\n");
        }

        if (RegisterIsCached(CLR_CMP_MSK) &&
            (CacheSlot(CLR_CMP_MSK) != inm(CLR_CMP_MSK)))
        {
            UncacheRegister(CLR_CMP_MSK);
            xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
                "CLR_CMP_MSK write cache disabled!\n");
        }

        if (RegisterIsCached(CLR_CMP_CNTL) &&
            (CacheSlot(CLR_CMP_CNTL) != inm(CLR_CMP_CNTL)))
        {
            UncacheRegister(CLR_CMP_CNTL);
            xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
                "CLR_CMP_CNTL write cache disabled!\n");
        }
    }

    /*
     * For VTB's and later, the first CPU read of the framebuffer will return
     * zeroes, so do it here.  This appears to be due to some kind of engine
     * caching of framebuffer data I haven't found any way of disabling, or
     * otherwise circumventing.  Thanks to Mark Vojkovich for the suggestion.
     */
    pATI->pXAAInfo->NeedToSync = FALSE;
    pATI = *(volatile ATIPtr *)pATI->pMemory;
}

/*
 * ATIMach64SetupForScreenToScreenCopy --
 *
 * This function sets up the draw engine for a series of screen-to-screen copy
 * operations.
 */
static void
ATIMach64SetupForScreenToScreenCopy
(
    ScrnInfoPtr  pScreenInfo,
    int          xdir,
    int          ydir,
    int          rop,
    unsigned int planemask,
    int          TransparencyColour
)
{
    ATIPtr pATI = ATIPTR(pScreenInfo);

    ATIMach64WaitForFIFO(pATI, 3);
    outf(DP_WRITE_MASK, planemask);
    outf(DP_SRC, DP_MONO_SRC_ALLONES |
        SetBits(SRC_BLIT, DP_FRGD_SRC) | SetBits(SRC_BKGD, DP_BKGD_SRC));
    outf(DP_MIX, SetBits(ATIMach64ALU[rop], DP_FRGD_MIX));

#ifdef AVOID_DGA

    if (TransparencyColour == -1)

#else /* AVOID_DGA */

    if (!pATI->XAAForceTransBlit && (TransparencyColour == -1))

#endif /* AVOID_DGA */

        outf(CLR_CMP_CNTL, CLR_CMP_FN_FALSE);
    else
    {
        ATIMach64WaitForFIFO(pATI, 2);
        outf(CLR_CMP_CLR, TransparencyColour);
        outf(CLR_CMP_CNTL, CLR_CMP_FN_EQUAL | CLR_CMP_SRC_2D);
    }

    pATI->dst_cntl = 0;

    if (ydir > 0)
        pATI->dst_cntl |= DST_Y_DIR;
    if (xdir > 0)
        pATI->dst_cntl |= DST_X_DIR;

    if (pATI->XModifier == 1)
        outf(DST_CNTL, pATI->dst_cntl);
    else
        pATI->dst_cntl |= DST_24_ROT_EN;
}

/*
 * ATIMach64SubsequentScreenToScreenCopy --
 *
 * This function performs a screen-to-screen copy operation.
 */
static void
ATIMach64SubsequentScreenToScreenCopy
(
    ScrnInfoPtr pScreenInfo,
    int         xSrc,
    int         ySrc,
    int         xDst,
    int         yDst,
    int         w,
    int         h
)
{
    ATIPtr pATI = ATIPTR(pScreenInfo);

    xSrc *= pATI->XModifier;
    xDst *= pATI->XModifier;
    w    *= pATI->XModifier;

    /* Disable clipping if it gets in the way */
    ATIMach64ValidateClip(pATI, xDst, xDst + w - 1, yDst, yDst + h - 1);

    if (!(pATI->dst_cntl & DST_X_DIR))
    {
        xSrc += w - 1;
        xDst += w - 1;
    }

    if (!(pATI->dst_cntl & DST_Y_DIR))
    {
        ySrc += h - 1;
        yDst += h - 1;
    }

    if (pATI->XModifier != 1)
        outf(DST_CNTL, pATI->dst_cntl | SetBits((xDst / 4) % 6, DST_24_ROT));

    ATIMach64WaitForFIFO(pATI, 4);
    outf(SRC_Y_X, SetWord(xSrc, 1) | SetWord(ySrc, 0));
    outf(SRC_WIDTH1, w);
    outf(DST_Y_X, SetWord(xDst, 1) | SetWord(yDst, 0));
    outf(DST_HEIGHT_WIDTH, SetWord(w, 1) | SetWord(h, 0));
}

/*
 * ATIMach64SetupForSolidFill --
 *
 * This function sets up the draw engine for a series of solid fills.
 */
static void
ATIMach64SetupForSolidFill
(
    ScrnInfoPtr  pScreenInfo,
    int          colour,
    int          rop,
    unsigned int planemask
)
{
    ATIPtr pATI = ATIPTR(pScreenInfo);

    ATIMach64WaitForFIFO(pATI, 5);
    outf(DP_WRITE_MASK, planemask);
    outf(DP_SRC, DP_MONO_SRC_ALLONES |
        SetBits(SRC_FRGD, DP_FRGD_SRC) | SetBits(SRC_BKGD, DP_BKGD_SRC));
    outf(DP_FRGD_CLR, colour);
    outf(DP_MIX, SetBits(ATIMach64ALU[rop], DP_FRGD_MIX));

    outf(CLR_CMP_CNTL, CLR_CMP_FN_FALSE);

    if (pATI->XModifier == 1)
        outf(DST_CNTL, DST_X_DIR | DST_Y_DIR);
}

/*
 * ATIMach64SubsequentSolidFillRect --
 *
 * This function performs a solid rectangle fill.
 */
static void
ATIMach64SubsequentSolidFillRect
(
    ScrnInfoPtr pScreenInfo,
    int         x,
    int         y,
    int         w,
    int         h
)
{
    ATIPtr pATI = ATIPTR(pScreenInfo);

    if (pATI->XModifier != 1)
    {
        x *= pATI->XModifier;
        w *= pATI->XModifier;

        outf(DST_CNTL, SetBits((x / 4) % 6, DST_24_ROT) |
            (DST_X_DIR | DST_Y_DIR | DST_24_ROT_EN));
    }

    /* Disable clipping if it gets in the way */
    ATIMach64ValidateClip(pATI, x, x + w - 1, y, y + h - 1);

    ATIMach64WaitForFIFO(pATI, 2);
    outf(DST_Y_X, SetWord(x, 1) | SetWord(y, 0));
    outf(DST_HEIGHT_WIDTH, SetWord(w, 1) | SetWord(h, 0));
}

/*
 * ATIMach64SetupForSolidLine --
 *
 * This function sets up the draw engine for a series of solid lines.  It is
 * not used for 24bpp because the engine doesn't support it.
 */
static void
ATIMach64SetupForSolidLine
(
    ScrnInfoPtr  pScreenInfo,
    int          colour,
    int          rop,
    unsigned int planemask
)
{
    ATIPtr pATI = ATIPTR(pScreenInfo);

    ATIMach64WaitForFIFO(pATI, 5);
    outf(DP_WRITE_MASK, planemask);
    outf(DP_SRC, DP_MONO_SRC_ALLONES |
        SetBits(SRC_FRGD, DP_FRGD_SRC) | SetBits(SRC_BKGD, DP_BKGD_SRC));
    outf(DP_FRGD_CLR, colour);
    outf(DP_MIX, SetBits(ATIMach64ALU[rop], DP_FRGD_MIX));

    outf(CLR_CMP_CNTL, CLR_CMP_FN_FALSE);

    ATIMach64ValidateClip(pATI, pATI->NewHW.sc_left, pATI->NewHW.sc_right,
        pATI->NewHW.sc_top, pATI->NewHW.sc_bottom);
}

/*
 * ATIMach64SubsequentSolidHorVertLine --
 *
 * This is called to draw a solid horizontal or vertical line.  This does a
 * one-pixel wide solid fill.
 */
static void
ATIMach64SubsequentSolidHorVertLine
(
    ScrnInfoPtr pScreenInfo,
    int         x,
    int         y,
    int         len,
    int         dir
)
{
    ATIPtr pATI = ATIPTR(pScreenInfo);

    ATIMach64WaitForFIFO(pATI, 3);
    outf(DST_CNTL, DST_X_DIR | DST_Y_DIR);
    outf(DST_Y_X, SetWord(x, 1) | SetWord(y, 0));

    if (dir == DEGREES_0)
        outf(DST_HEIGHT_WIDTH, SetWord(len, 1) | SetWord(1, 0));
    else /* if (dir == DEGREES_270) */
        outf(DST_HEIGHT_WIDTH, SetWord(1, 1) | SetWord(len, 0));
}

/*
 * ATIMach64SubsequentSolidBresenhamLine --
 *
 * This function draws a line using the Bresenham line engine.
 */
static void
ATIMach64SubsequentSolidBresenhamLine
(
    ScrnInfoPtr pScreenInfo,
    int         x,
    int         y,
    int         major,
    int         minor,
    int         err,
    int         len,
    int         octant
)
{
    ATIPtr pATI = ATIPTR(pScreenInfo);
    CARD32 dst_cntl = DST_LAST_PEL;

    if (octant & YMAJOR)
        dst_cntl |= DST_Y_MAJOR;

    if (!(octant & XDECREASING))
        dst_cntl |= DST_X_DIR;

    if (!(octant & YDECREASING))
        dst_cntl |= DST_Y_DIR;

    ATIMach64WaitForFIFO(pATI, 6);
    outf(DST_CNTL, dst_cntl);
    outf(DST_Y_X, SetWord(x, 1) | SetWord(y, 0));
    outf(DST_BRES_ERR, minor + err);
    outf(DST_BRES_INC, minor);
    outf(DST_BRES_DEC, minor - major);
    outf(DST_BRES_LNTH, len);
}

/*
 * ATIMach64SetupForMono8x8PatternFill --
 *
 * This function sets up the draw engine for a series of 8x8 1bpp pattern
 * fills.
 */
static void
ATIMach64SetupForMono8x8PatternFill
(
    ScrnInfoPtr  pScreenInfo,
    int          patx,
    int          paty,
    int          fg,
    int          bg,
    int          rop,
    unsigned int planemask
)
{
    ATIPtr pATI = ATIPTR(pScreenInfo);

    ATIMach64WaitForFIFO(pATI, 3);
    outf(DP_WRITE_MASK, planemask);
    outf(DP_SRC, DP_MONO_SRC_PATTERN |
        SetBits(SRC_FRGD, DP_FRGD_SRC) | SetBits(SRC_BKGD, DP_BKGD_SRC));
    outf(DP_FRGD_CLR, fg);

    if (bg == -1)
        outf(DP_MIX, SetBits(ATIMach64ALU[rop], DP_FRGD_MIX) |
            SetBits(MIX_DST, DP_BKGD_MIX));
    else
    {
        ATIMach64WaitForFIFO(pATI, 2);
        outf(DP_BKGD_CLR, bg);
        outf(DP_MIX, SetBits(ATIMach64ALU[rop], DP_FRGD_MIX) |
            SetBits(ATIMach64ALU[rop], DP_BKGD_MIX));
    }

    ATIMach64WaitForFIFO(pATI, 4);
    outf(PAT_REG0, patx);
    outf(PAT_REG1, paty);
    outf(PAT_CNTL, PAT_MONO_EN);

    outf(CLR_CMP_CNTL, CLR_CMP_FN_FALSE);

    if (pATI->XModifier == 1)
        outf(DST_CNTL, DST_X_DIR | DST_Y_DIR);
}

/*
 * ATIMach64SubsequentMono8x8PatternFillRect --
 *
 * This function performs an 8x8 1bpp pattern fill.
 */
static void
ATIMach64SubsequentMono8x8PatternFillRect
(
    ScrnInfoPtr pScreenInfo,
    int         patx,
    int         paty,
    int         x,
    int         y,
    int         w,
    int         h
)
{
    ATIPtr pATI = ATIPTR(pScreenInfo);

    if (pATI->XModifier != 1)
    {
        x *= pATI->XModifier;
        w *= pATI->XModifier;

        outf(DST_CNTL, SetBits((x / 4) % 6, DST_24_ROT) |
            (DST_X_DIR | DST_Y_DIR | DST_24_ROT_EN));
    }

    /* Disable clipping if it gets in the way */
    ATIMach64ValidateClip(pATI, x, x + w - 1, y, y + h - 1);

    ATIMach64WaitForFIFO(pATI, 2);
    outf(DST_Y_X, SetWord(x, 1) | SetWord(y, 0));
    outf(DST_HEIGHT_WIDTH, SetWord(w, 1) | SetWord(h, 0));
}

/*
 * ATIMach64SetupForScanlineCPUToScreenColorExpandFill --
 *
 * This function sets up the engine for a series of colour expansion fills.
 */
static void
ATIMach64SetupForScanlineCPUToScreenColorExpandFill
(
    ScrnInfoPtr  pScreenInfo,
    int          fg,
    int          bg,
    int          rop,
    unsigned int planemask
)
{
    ATIPtr pATI = ATIPTR(pScreenInfo);

    ATIMach64WaitForFIFO(pATI, 3);
    outf(DP_WRITE_MASK, planemask);
    outf(DP_SRC, DP_MONO_SRC_HOST |
        SetBits(SRC_FRGD, DP_FRGD_SRC) | SetBits(SRC_BKGD, DP_BKGD_SRC));
    outf(DP_FRGD_CLR, fg);

    if (bg == -1)
        outf(DP_MIX, SetBits(ATIMach64ALU[rop], DP_FRGD_MIX) |
            SetBits(MIX_DST, DP_BKGD_MIX));
    else
    {
        ATIMach64WaitForFIFO(pATI, 2);
        outf(DP_BKGD_CLR, bg);
        outf(DP_MIX, SetBits(ATIMach64ALU[rop], DP_FRGD_MIX) |
            SetBits(ATIMach64ALU[rop], DP_BKGD_MIX));
    }

    outf(CLR_CMP_CNTL, CLR_CMP_FN_FALSE);

    if (pATI->XModifier == 1)
        outf(DST_CNTL, DST_X_DIR | DST_Y_DIR);
}

/*
 * ATIMach64SubsequentScanlineCPUToScreenColorExpandFill --
 *
 * This function sets up the engine for a single colour expansion fill.
 */
static void
ATIMach64SubsequentScanlineCPUToScreenColorExpandFill
(
    ScrnInfoPtr pScreenInfo,
    int         x,
    int         y,
    int         w,
    int         h,
    int         skipleft
)
{
    ATIPtr pATI = ATIPTR(pScreenInfo);

    if (pATI->XModifier != 1)
    {
        x *= pATI->XModifier;
        w *= pATI->XModifier;
        skipleft *= pATI->XModifier;

        outf(DST_CNTL, SetBits((x / 4) % 6, DST_24_ROT) |
            (DST_X_DIR | DST_Y_DIR | DST_24_ROT_EN));
    }

    pATI->ExpansionBitmapWidth = (w + 31) / 32;

    ATIMach64WaitForFIFO(pATI, 3);
    pATI->sc_left = x + skipleft;
    pATI->sc_right = x + w - 1;
    outf(SC_LEFT_RIGHT,
        SetWord(pATI->sc_right, 1) | SetWord(pATI->sc_left, 0));
    outf(DST_Y_X, SetWord(x, 1) | SetWord(y, 0));
    outf(DST_HEIGHT_WIDTH,
        SetWord(pATI->ExpansionBitmapWidth * 32, 1) | SetWord(h, 0));
}

/*
 * ATIMach64SubsequentColorExpandScanline --
 *
 * This function feeds a bitmap scanline to the engine for a colour expansion
 * fill.  This is written to do burst transfers for those platforms that can do
 * them, and to improve CPU/engine concurrency.
 */
static void
ATIMach64SubsequentColorExpandScanline
(
    ScrnInfoPtr pScreenInfo,
    int         iBuffer
)
{
    ATIPtr          pATI         = ATIPTR(pScreenInfo);
    CARD32          *pBitmapData = pATI->ExpansionBitmapScanlinePtr[iBuffer];
    int             w            = pATI->ExpansionBitmapWidth;
    int             nDWord;

    while (w > 0)
    {
        /*
         * Transfers are done in chunks of up to 64 bytes in length (32 on
         * earlier controllers).
         */
        nDWord = w;
        if (nDWord > pATI->nHostFIFOEntries)
            nDWord = pATI->nHostFIFOEntries;

        /* Make enough FIFO slots available */
        ATIMach64WaitForFIFO(pATI, nDWord);

        /*
         * Always start transfers on a chuck-sized boundary.  Note that
         * HOST_DATA_0 is actually on a 512-byte boundary, but *pBitmapData can
         * only be guaranteed to be on a chunk-sized boundary.
         *
         * Transfer current chunk.  With any luck, the compiler won't mangle
         * this too badly...
         */

#       if defined(ATIMove32)

            ATIMove32(pATI->pHOST_DATA, pBitmapData, nDWord);

#       else

        {
            volatile CARD32 *pDst;
            CARD32          *pSrc;
            unsigned int    iDWord;

            iDWord = 16 - nDWord;
            pDst = (volatile CARD32 *)pATI->pHOST_DATA - iDWord;
            pSrc = pBitmapData - iDWord;

            switch (iDWord)
            {
                case  0:  MMIO_MOVE32(pDst +  0, 0, *(pSrc +  0));
                case  1:  MMIO_MOVE32(pDst +  1, 0, *(pSrc +  1));
                case  2:  MMIO_MOVE32(pDst +  2, 0, *(pSrc +  2));
                case  3:  MMIO_MOVE32(pDst +  3, 0, *(pSrc +  3));
                case  4:  MMIO_MOVE32(pDst +  4, 0, *(pSrc +  4));
                case  5:  MMIO_MOVE32(pDst +  5, 0, *(pSrc +  5));
                case  6:  MMIO_MOVE32(pDst +  6, 0, *(pSrc +  6));
                case  7:  MMIO_MOVE32(pDst +  7, 0, *(pSrc +  7));
                case  8:  MMIO_MOVE32(pDst +  8, 0, *(pSrc +  8));
                case  9:  MMIO_MOVE32(pDst +  9, 0, *(pSrc +  9));
                case 10:  MMIO_MOVE32(pDst + 10, 0, *(pSrc + 10));
                case 11:  MMIO_MOVE32(pDst + 11, 0, *(pSrc + 11));
                case 12:  MMIO_MOVE32(pDst + 12, 0, *(pSrc + 12));
                case 13:  MMIO_MOVE32(pDst + 13, 0, *(pSrc + 13));
                case 14:  MMIO_MOVE32(pDst + 14, 0, *(pSrc + 14));
                case 15:  MMIO_MOVE32(pDst + 15, 0, *(pSrc + 15));

                default:    /* Muffle compiler */
                    break;
            }
        }

#       endif

        /* Step to next chunk */
        pBitmapData += nDWord;
        w -= nDWord;
        pATI->nAvailableFIFOEntries -= nDWord;
    }

    pATI->EngineIsBusy = TRUE;
}

/*
 * ATIMach64AccelInit --
 *
 * This function fills in structure fields needed for acceleration on Mach64
 * variants.
 */
int
ATIMach64AccelInit
(
    ATIPtr        pATI,
    XAAInfoRecPtr pXAAInfo
)
{
    /* This doesn't seem quite right... */
    if (pATI->XModifier == 1)
    {
        pXAAInfo->Flags = PIXMAP_CACHE | OFFSCREEN_PIXMAPS;

#ifndef AVOID_CPIO

        if (!pATI->BankInfo.BankSize)

#endif /* AVOID_CPIO */

        {
            pXAAInfo->Flags |= LINEAR_FRAMEBUFFER;
        }
    }

    /* Sync */
    pXAAInfo->Sync = ATIMach64Sync;

    /* Screen-to-screen copy */
    pXAAInfo->SetupForScreenToScreenCopy = ATIMach64SetupForScreenToScreenCopy;
    pXAAInfo->SubsequentScreenToScreenCopy =
        ATIMach64SubsequentScreenToScreenCopy;

    /* Solid fills */
    pXAAInfo->SetupForSolidFill = ATIMach64SetupForSolidFill;
    pXAAInfo->SubsequentSolidFillRect = ATIMach64SubsequentSolidFillRect;

    /* 8x8 mono pattern fills */
    pXAAInfo->Mono8x8PatternFillFlags =

#if X_BYTE_ORDER != X_LITTLE_ENDIAN

        BIT_ORDER_IN_BYTE_MSBFIRST |

#endif /* X_BYTE_ORDER */

        HARDWARE_PATTERN_PROGRAMMED_BITS | HARDWARE_PATTERN_SCREEN_ORIGIN;
    pXAAInfo->SetupForMono8x8PatternFill = ATIMach64SetupForMono8x8PatternFill;
    pXAAInfo->SubsequentMono8x8PatternFillRect =
        ATIMach64SubsequentMono8x8PatternFillRect;

    /*
     * Use scanline version of colour expansion, not only for the non-ix86
     * case, but also to avoid PCI retries.
     */
    pXAAInfo->ScanlineCPUToScreenColorExpandFillFlags =
        LEFT_EDGE_CLIPPING | LEFT_EDGE_CLIPPING_NEGATIVE_X |
        CPU_TRANSFER_PAD_DWORD | SCANLINE_PAD_DWORD;
    if (pATI->XModifier != 1)
        pXAAInfo->ScanlineCPUToScreenColorExpandFillFlags |= TRIPLE_BITS_24BPP;
    pXAAInfo->NumScanlineColorExpandBuffers = 1;

    /* Align bitmap data on a 64-byte boundary */
    pATI->ExpansionBitmapWidth =        /* DWord size in bits */
        ((pATI->displayWidth * pATI->XModifier) + 31) & ~31U;
    pATI->ExpansionBitmapScanlinePtr[1] =
        (CARD32 *)xnfalloc((pATI->ExpansionBitmapWidth >> 3) + 63);
    pATI->ExpansionBitmapScanlinePtr[0] =
        (pointer)(((unsigned long)pATI->ExpansionBitmapScanlinePtr[1] + 63) &
                  ~63UL);
    pXAAInfo->ScanlineColorExpandBuffers =
        (CARD8 **)pATI->ExpansionBitmapScanlinePtr;
    pXAAInfo->SetupForScanlineCPUToScreenColorExpandFill =
        ATIMach64SetupForScanlineCPUToScreenColorExpandFill;
    pXAAInfo->SubsequentScanlineCPUToScreenColorExpandFill =
        ATIMach64SubsequentScanlineCPUToScreenColorExpandFill;
    pXAAInfo->SubsequentColorExpandScanline =
        ATIMach64SubsequentColorExpandScanline;

    /* The engine does not support the following primitives for 24bpp */
    if (pATI->XModifier != 1)
        return ATIMach64MaxY;

    /* Solid lines */
    pXAAInfo->SetupForSolidLine = ATIMach64SetupForSolidLine;
    pXAAInfo->SubsequentSolidHorVertLine = ATIMach64SubsequentSolidHorVertLine;
    pXAAInfo->SubsequentSolidBresenhamLine =
        ATIMach64SubsequentSolidBresenhamLine;

    return ATIMach64MaxY;
}

/*
 * ATIMach64SetCursorColours --
 *
 * Set hardware cursor foreground and background colours.
 */
static void
ATIMach64SetCursorColours
(
    ScrnInfoPtr pScreenInfo,
    int         fg,
    int         bg
)
{
    ATIPtr pATI = ATIPTR(pScreenInfo);

    outr(CUR_CLR0, SetBits(fg, CUR_CLR));
    outr(CUR_CLR1, SetBits(bg, CUR_CLR));
}

/*
 * ATIMach64SetCursorPosition --
 *
 * Set position of hardware cursor.
 */
static void
ATIMach64SetCursorPosition
(
    ScrnInfoPtr pScreenInfo,
    int         x,
    int         y
)
{
    ATIPtr pATI = ATIPTR(pScreenInfo);
    CARD16 CursorXOffset, CursorYOffset;

    /* Adjust x & y when the cursor is partially obscured */
    if (x < 0)
    {
        if ((CursorXOffset = -x) > 63)
            CursorXOffset = 63;
        x = 0;
    }
    else
    {
        CursorXOffset = pScreenInfo->frameX1 - pScreenInfo->frameX0;
        if (x > CursorXOffset)
            x = CursorXOffset;
        CursorXOffset = 0;
    }

    if (y < 0)
    {
        if ((CursorYOffset = -y) > 63)
            CursorYOffset = 63;
        y = 0;
    }
    else
    {
        CursorYOffset = pScreenInfo->frameY1 - pScreenInfo->frameY0;
        if (y > CursorYOffset)
            y = CursorYOffset;
        CursorYOffset = 0;
    }

    /* Adjust for multiscanned modes */
    if (pScreenInfo->currentMode->Flags & V_DBLSCAN)
        y *= 2;
    if (pScreenInfo->currentMode->VScan > 1)
        y *= pScreenInfo->currentMode->VScan;

    do
    {
        if (CursorYOffset != pATI->CursorYOffset)
        {
            pATI->CursorYOffset = CursorYOffset;
            outr(CUR_OFFSET, ((CursorYOffset << 4) + pATI->CursorOffset) >> 3);
        }
        else if (CursorXOffset == pATI->CursorXOffset)
            break;

        pATI->CursorXOffset = CursorXOffset;
        outr(CUR_HORZ_VERT_OFF, SetBits(CursorXOffset, CUR_HORZ_OFF) |
            SetBits(CursorYOffset, CUR_VERT_OFF));
    } while (0);

    outr(CUR_HORZ_VERT_POSN,
        SetBits(x, CUR_HORZ_POSN) | SetBits(y, CUR_VERT_POSN));
}

/*
 * ATIMach64LoadCursorImage --
 *
 * Copy hardware cursor image into offscreen video memory.
 */
static void
ATIMach64LoadCursorImage
(
    ScrnInfoPtr pScreenInfo,
    CARD8       *pImage
)
{
    ATIPtr           pATI     = ATIPTR(pScreenInfo);
    XAAInfoRecPtr    pXAAInfo = pATI->pXAAInfo;
    CARD32          *pSrc     = (pointer)pImage;
    volatile CARD32 *pDst     = pATI->pCursorImage;

    /* Synchronise video memory accesses */
    if (pXAAInfo && pXAAInfo->NeedToSync)
        (*pXAAInfo->Sync)(pScreenInfo);

#   if defined(ATIMove32)

        ATIMove32(pDst, pSrc, 256);

#   else

        /* This is lengthy, but it does maximise burst modes */
        pDst[  0] = pSrc[  0];  pDst[  1] = pSrc[  1];
        pDst[  2] = pSrc[  2];  pDst[  3] = pSrc[  3];
        pDst[  4] = pSrc[  4];  pDst[  5] = pSrc[  5];
        pDst[  6] = pSrc[  6];  pDst[  7] = pSrc[  7];
        pDst[  8] = pSrc[  8];  pDst[  9] = pSrc[  9];
        pDst[ 10] = pSrc[ 10];  pDst[ 11] = pSrc[ 11];
        pDst[ 12] = pSrc[ 12];  pDst[ 13] = pSrc[ 13];
        pDst[ 14] = pSrc[ 14];  pDst[ 15] = pSrc[ 15];
        pDst[ 16] = pSrc[ 16];  pDst[ 17] = pSrc[ 17];
        pDst[ 18] = pSrc[ 18];  pDst[ 19] = pSrc[ 19];
        pDst[ 20] = pSrc[ 20];  pDst[ 21] = pSrc[ 21];
        pDst[ 22] = pSrc[ 22];  pDst[ 23] = pSrc[ 23];
        pDst[ 24] = pSrc[ 24];  pDst[ 25] = pSrc[ 25];
        pDst[ 26] = pSrc[ 26];  pDst[ 27] = pSrc[ 27];
        pDst[ 28] = pSrc[ 28];  pDst[ 29] = pSrc[ 29];
        pDst[ 30] = pSrc[ 30];  pDst[ 31] = pSrc[ 31];
        pDst[ 32] = pSrc[ 32];  pDst[ 33] = pSrc[ 33];
        pDst[ 34] = pSrc[ 34];  pDst[ 35] = pSrc[ 35];
        pDst[ 36] = pSrc[ 36];  pDst[ 37] = pSrc[ 37];
        pDst[ 38] = pSrc[ 38];  pDst[ 39] = pSrc[ 39];
        pDst[ 40] = pSrc[ 40];  pDst[ 41] = pSrc[ 41];
        pDst[ 42] = pSrc[ 42];  pDst[ 43] = pSrc[ 43];
        pDst[ 44] = pSrc[ 44];  pDst[ 45] = pSrc[ 45];
        pDst[ 46] = pSrc[ 46];  pDst[ 47] = pSrc[ 47];
        pDst[ 48] = pSrc[ 48];  pDst[ 49] = pSrc[ 49];
        pDst[ 50] = pSrc[ 50];  pDst[ 51] = pSrc[ 51];
        pDst[ 52] = pSrc[ 52];  pDst[ 53] = pSrc[ 53];
        pDst[ 54] = pSrc[ 54];  pDst[ 55] = pSrc[ 55];
        pDst[ 56] = pSrc[ 56];  pDst[ 57] = pSrc[ 57];
        pDst[ 58] = pSrc[ 58];  pDst[ 59] = pSrc[ 59];
        pDst[ 60] = pSrc[ 60];  pDst[ 61] = pSrc[ 61];
        pDst[ 62] = pSrc[ 62];  pDst[ 63] = pSrc[ 63];
        pDst[ 64] = pSrc[ 64];  pDst[ 65] = pSrc[ 65];
        pDst[ 66] = pSrc[ 66];  pDst[ 67] = pSrc[ 67];
        pDst[ 68] = pSrc[ 68];  pDst[ 69] = pSrc[ 69];
        pDst[ 70] = pSrc[ 70];  pDst[ 71] = pSrc[ 71];
        pDst[ 72] = pSrc[ 72];  pDst[ 73] = pSrc[ 73];
        pDst[ 74] = pSrc[ 74];  pDst[ 75] = pSrc[ 75];
        pDst[ 76] = pSrc[ 76];  pDst[ 77] = pSrc[ 77];
        pDst[ 78] = pSrc[ 78];  pDst[ 79] = pSrc[ 79];
        pDst[ 80] = pSrc[ 80];  pDst[ 81] = pSrc[ 81];
        pDst[ 82] = pSrc[ 82];  pDst[ 83] = pSrc[ 83];
        pDst[ 84] = pSrc[ 84];  pDst[ 85] = pSrc[ 85];
        pDst[ 86] = pSrc[ 86];  pDst[ 87] = pSrc[ 87];
        pDst[ 88] = pSrc[ 88];  pDst[ 89] = pSrc[ 89];
        pDst[ 90] = pSrc[ 90];  pDst[ 91] = pSrc[ 91];
        pDst[ 92] = pSrc[ 92];  pDst[ 93] = pSrc[ 93];
        pDst[ 94] = pSrc[ 94];  pDst[ 95] = pSrc[ 95];
        pDst[ 96] = pSrc[ 96];  pDst[ 97] = pSrc[ 97];
        pDst[ 98] = pSrc[ 98];  pDst[ 99] = pSrc[ 99];
        pDst[100] = pSrc[100];  pDst[101] = pSrc[101];
        pDst[102] = pSrc[102];  pDst[103] = pSrc[103];
        pDst[104] = pSrc[104];  pDst[105] = pSrc[105];
        pDst[106] = pSrc[106];  pDst[107] = pSrc[107];
        pDst[108] = pSrc[108];  pDst[109] = pSrc[109];
        pDst[110] = pSrc[110];  pDst[111] = pSrc[111];
        pDst[112] = pSrc[112];  pDst[113] = pSrc[113];
        pDst[114] = pSrc[114];  pDst[115] = pSrc[115];
        pDst[116] = pSrc[116];  pDst[117] = pSrc[117];
        pDst[118] = pSrc[118];  pDst[119] = pSrc[119];
        pDst[120] = pSrc[120];  pDst[121] = pSrc[121];
        pDst[122] = pSrc[122];  pDst[123] = pSrc[123];
        pDst[124] = pSrc[124];  pDst[125] = pSrc[125];
        pDst[126] = pSrc[126];  pDst[127] = pSrc[127];
        pDst[128] = pSrc[128];  pDst[129] = pSrc[129];
        pDst[130] = pSrc[130];  pDst[131] = pSrc[131];
        pDst[132] = pSrc[132];  pDst[133] = pSrc[133];
        pDst[134] = pSrc[134];  pDst[135] = pSrc[135];
        pDst[136] = pSrc[136];  pDst[137] = pSrc[137];
        pDst[138] = pSrc[138];  pDst[139] = pSrc[139];
        pDst[140] = pSrc[140];  pDst[141] = pSrc[141];
        pDst[142] = pSrc[142];  pDst[143] = pSrc[143];
        pDst[144] = pSrc[144];  pDst[145] = pSrc[145];
        pDst[146] = pSrc[146];  pDst[147] = pSrc[147];
        pDst[148] = pSrc[148];  pDst[149] = pSrc[149];
        pDst[150] = pSrc[150];  pDst[151] = pSrc[151];
        pDst[152] = pSrc[152];  pDst[153] = pSrc[153];
        pDst[154] = pSrc[154];  pDst[155] = pSrc[155];
        pDst[156] = pSrc[156];  pDst[157] = pSrc[157];
        pDst[158] = pSrc[158];  pDst[159] = pSrc[159];
        pDst[160] = pSrc[160];  pDst[161] = pSrc[161];
        pDst[162] = pSrc[162];  pDst[163] = pSrc[163];
        pDst[164] = pSrc[164];  pDst[165] = pSrc[165];
        pDst[166] = pSrc[166];  pDst[167] = pSrc[167];
        pDst[168] = pSrc[168];  pDst[169] = pSrc[169];
        pDst[170] = pSrc[170];  pDst[171] = pSrc[171];
        pDst[172] = pSrc[172];  pDst[173] = pSrc[173];
        pDst[174] = pSrc[174];  pDst[175] = pSrc[175];
        pDst[176] = pSrc[176];  pDst[177] = pSrc[177];
        pDst[178] = pSrc[178];  pDst[179] = pSrc[179];
        pDst[180] = pSrc[180];  pDst[181] = pSrc[181];
        pDst[182] = pSrc[182];  pDst[183] = pSrc[183];
        pDst[184] = pSrc[184];  pDst[185] = pSrc[185];
        pDst[186] = pSrc[186];  pDst[187] = pSrc[187];
        pDst[188] = pSrc[188];  pDst[189] = pSrc[189];
        pDst[190] = pSrc[190];  pDst[191] = pSrc[191];
        pDst[192] = pSrc[192];  pDst[193] = pSrc[193];
        pDst[194] = pSrc[194];  pDst[195] = pSrc[195];
        pDst[196] = pSrc[196];  pDst[197] = pSrc[197];
        pDst[198] = pSrc[198];  pDst[199] = pSrc[199];
        pDst[200] = pSrc[200];  pDst[201] = pSrc[201];
        pDst[202] = pSrc[202];  pDst[203] = pSrc[203];
        pDst[204] = pSrc[204];  pDst[205] = pSrc[205];
        pDst[206] = pSrc[206];  pDst[207] = pSrc[207];
        pDst[208] = pSrc[208];  pDst[209] = pSrc[209];
        pDst[210] = pSrc[210];  pDst[211] = pSrc[211];
        pDst[212] = pSrc[212];  pDst[213] = pSrc[213];
        pDst[214] = pSrc[214];  pDst[215] = pSrc[215];
        pDst[216] = pSrc[216];  pDst[217] = pSrc[217];
        pDst[218] = pSrc[218];  pDst[219] = pSrc[219];
        pDst[220] = pSrc[220];  pDst[221] = pSrc[221];
        pDst[222] = pSrc[222];  pDst[223] = pSrc[223];
        pDst[224] = pSrc[224];  pDst[225] = pSrc[225];
        pDst[226] = pSrc[226];  pDst[227] = pSrc[227];
        pDst[228] = pSrc[228];  pDst[229] = pSrc[229];
        pDst[230] = pSrc[230];  pDst[231] = pSrc[231];
        pDst[232] = pSrc[232];  pDst[233] = pSrc[233];
        pDst[234] = pSrc[234];  pDst[235] = pSrc[235];
        pDst[236] = pSrc[236];  pDst[237] = pSrc[237];
        pDst[238] = pSrc[238];  pDst[239] = pSrc[239];
        pDst[240] = pSrc[240];  pDst[241] = pSrc[241];
        pDst[242] = pSrc[242];  pDst[243] = pSrc[243];
        pDst[244] = pSrc[244];  pDst[245] = pSrc[245];
        pDst[246] = pSrc[246];  pDst[247] = pSrc[247];
        pDst[248] = pSrc[248];  pDst[249] = pSrc[249];
        pDst[250] = pSrc[250];  pDst[251] = pSrc[251];
        pDst[252] = pSrc[252];  pDst[253] = pSrc[253];
        pDst[254] = pSrc[254];  pDst[255] = pSrc[255];

#endif

}

/*
 * ATIMach64HideCursor --
 *
 * Turn off hardware cursor.
 */
static void
ATIMach64HideCursor
(
    ScrnInfoPtr pScreenInfo
)
{
    ATIPtr pATI = ATIPTR(pScreenInfo);

    if (!(pATI->NewHW.gen_test_cntl & GEN_CUR_EN))
        return;

    pATI->NewHW.gen_test_cntl &= ~GEN_CUR_EN;
    out8(GEN_TEST_CNTL, GetByte(pATI->NewHW.gen_test_cntl, 0));
}

/*
 * ATIMach64ShowCursor --
 *
 * Turn on hardware cursor.
 */
static void
ATIMach64ShowCursor
(
    ScrnInfoPtr pScreenInfo
)
{
    ATIPtr pATI = ATIPTR(pScreenInfo);

    if (pATI->NewHW.gen_test_cntl & GEN_CUR_EN)
        return;

    pATI->NewHW.gen_test_cntl |= GEN_CUR_EN;
    out8(GEN_TEST_CNTL, GetByte(pATI->NewHW.gen_test_cntl, 0));
}

/*
 * ATIMach64UseHWCursor --
 *
 * Notify cursor layer whether a hardware cursor is configured.
 */
static Bool
ATIMach64UseHWCursor
(
    ScreenPtr pScreen,
    CursorPtr pCursor
)
{
    ScrnInfoPtr pScreenInfo = xf86Screens[pScreen->myNum];
    ATIPtr      pATI        = ATIPTR(pScreenInfo);

    if (!pATI->CursorBase)
        return FALSE;

#ifndef AVOID_CPIO

    /*
     * For some reason, the hardware cursor isn't vertically scaled when a VGA
     * doublescanned or multiscanned mode is in effect.
     */
    if (pATI->NewHW.crtc == ATI_CRTC_MACH64)
        return TRUE;
    if ((pScreenInfo->currentMode->Flags & V_DBLSCAN) ||
        (pScreenInfo->currentMode->VScan > 1))
        return FALSE;

#endif /* AVOID_CPIO */

    return TRUE;
}

/*
 * ATIMach64CursorInit --
 *
 * Initialise xf86CursorInfoRec fields with information specific to Mach64
 * variants.
 */
Bool
ATIMach64CursorInit
(
    xf86CursorInfoPtr pCursorInfo
)
{
    /*
     * For Mach64 variants, toggling hardware cursors on and off causes
     * display artifacts.  Ask the cursor support layers to always paint the
     * cursor (whether or not it is entirely transparent) and to not hide the
     * cursor when reloading its image.  The three reasons behind turning off
     * the hardware cursor that remain are when it moves to a different screen,
     * on a switch to a software cursor or to a different virtual console.
     */
    pCursorInfo->Flags = HARDWARE_CURSOR_TRUECOLOR_AT_8BPP |
        HARDWARE_CURSOR_INVERT_MASK |
        HARDWARE_CURSOR_SHOW_TRANSPARENT |
        HARDWARE_CURSOR_UPDATE_UNHIDDEN |
        HARDWARE_CURSOR_AND_SOURCE_WITH_MASK |

#if X_BYTE_ORDER != X_LITTLE_ENDIAN

        HARDWARE_CURSOR_BIT_ORDER_MSBFIRST |

#endif /* X_BYTE_ORDER */

        HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_1;
    pCursorInfo->MaxWidth = pCursorInfo->MaxHeight = 64;

    pCursorInfo->SetCursorColors = ATIMach64SetCursorColours;
    pCursorInfo->SetCursorPosition = ATIMach64SetCursorPosition;
    pCursorInfo->LoadCursorImage = ATIMach64LoadCursorImage;
    pCursorInfo->HideCursor = ATIMach64HideCursor;
    pCursorInfo->ShowCursor = ATIMach64ShowCursor;
    pCursorInfo->UseHWCursor = ATIMach64UseHWCursor;

    return TRUE;
}
