/*
 * Copyright 1997 through 2004 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.
 */
/*
 * DRI support by:
 *    Manuel Teira
 *    Leif Delgass <ldelgass@retinalburn.net>
 */
#include "config.h"

#include <string.h>

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

#include <X11/extensions/dpmsconst.h>

/*
 * 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
)
{
    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 = 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 = inr(CONFIG_CNTL);

#ifndef AVOID_CPIO

    if (pATI->VGAAdapter)
    {
        pATIHW->config_cntl |= CFG_MEM_VGA_AP_EN;
    }
    else

#endif /* AVOID_CPIO */

    {
        pATIHW->config_cntl &= ~CFG_MEM_VGA_AP_EN;
    }

    if ((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_buf_cntl = inr(MEM_BUF_CNTL) | INVALIDATE_RB_CACHE;
        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->Block0Base)
    {
        CARD32 bus_cntl = inr(BUS_CNTL);
        CARD32 config_cntl = inr(CONFIG_CNTL);

        /* 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)

#ifdef XF86DRI_DEVEL

	    /* Changing the FIFO depth seems to interfere with DMA, so use
	     * default of 128 entries (0x01)
	     */
	    pATIHW->gui_cntl = (inm(GUI_CNTL) & ~CMDFIFO_SIZE_MODE) | 0x01;

#else /* XF86DRI_DEVEL */

            pATIHW->gui_cntl = inm(GUI_CNTL) & ~CMDFIFO_SIZE_MODE;

#endif /* XF86DRI_DEVEL */

        /* 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 */

#ifdef USE_EXA
        if (pATI->useEXA)
        {
            pATIHW->sc_right = ATIMach64MaxX;
            pATIHW->sc_bottom = ATIMach64MaxY;
        }
#endif /* USE_EXA */

        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;

        if (pATI->Block1Base)
        {
            pATIHW->overlay_y_x_start = SetBits(0, OVERLAY_Y_START) |
                SetBits(0, OVERLAY_X_START) | OVERLAY_LOCK_START;
            pATIHW->overlay_y_x_end = SetBits(0, OVERLAY_Y_END) |
                SetBits(0, OVERLAY_X_END) | OVERLAY_LOCK_END;

            pATIHW->overlay_graphics_key_clr =
                (3 << ((2 * pATI->depth) / 3)) |
                (2 << ((1 * pATI->depth) / 3)) |
                (1 << ((0 * pATI->depth) / 3));
            pATIHW->overlay_graphics_key_msk = (1 << pATI->depth) - 1;

            pATIHW->overlay_key_cntl =
                SetBits(OVERLAY_MIX_FALSE, OVERLAY_VIDEO_FN) |
                SetBits(OVERLAY_MIX_EQUAL, OVERLAY_GRAPHICS_FN);

            pATIHW->overlay_scale_cntl = SCALE_EN;

            pATIHW->video_format = VIDEO_IN_VYUY422 | SCALER_IN_VYUY422;

            if (pATI->Chip >= ATI_CHIP_264GTPRO)
            {
                /* These values are documented voodoo */
                pATIHW->scaler_h_coeff0 = SetByte(0x20U, 1);
                pATIHW->scaler_h_coeff1 = SetByte(0x0DU, 0) |
                    SetByte(0x20U, 1) | SetByte(0x06U, 2) | SetByte(0x0DU, 3);
                pATIHW->scaler_h_coeff2 = SetByte(0x0DU, 0) |
                    SetByte(0x1CU, 1) | SetByte(0x0AU, 2) | SetByte(0x0DU, 3);
                pATIHW->scaler_h_coeff3 = SetByte(0x0CU, 0) |
                    SetByte(0x1AU, 1) | SetByte(0x0EU, 2) | SetByte(0x0CU, 3);
                pATIHW->scaler_h_coeff4 = SetByte(0x0CU, 0) |
                    SetByte(0x14U, 1) | SetByte(0x14U, 2) | SetByte(0x0CU, 3);
            }
        }

        /* 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_buf_cntl = inr(MEM_BUF_CNTL) | INVALIDATE_RB_CACHE;
        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->Block0Base && (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);

        if (pATI->Chip >= ATI_CHIP_264GTPRO)
        {
            CARD32 offset = TEX_LEVEL(inm(TEX_SIZE_PITCH));

            /* Save 3D control & texture registers */
            pATIHW->tex_offset = inm(TEX_0_OFF + offset);
            pATIHW->scale_3d_cntl = inm(SCALE_3D_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);

        if (pATI->Chip >= ATI_CHIP_264GTPRO)
        {
            /* Save texture setup registers */
            pATIHW->tex_size_pitch = inm(TEX_SIZE_PITCH);
            pATIHW->tex_cntl = inm(TEX_CNTL);
        }

        if (pATI->Block1Base)
        {
            /* Save overlay & scaler registers */
            pATIHW->overlay_y_x_start = inm(OVERLAY_Y_X_START);
            pATIHW->overlay_y_x_end = inm(OVERLAY_Y_X_END);

            pATIHW->overlay_graphics_key_clr = inm(OVERLAY_GRAPHICS_KEY_CLR);
            pATIHW->overlay_graphics_key_msk = inm(OVERLAY_GRAPHICS_KEY_MSK);

            pATIHW->overlay_key_cntl = inm(OVERLAY_KEY_CNTL);

            pATIHW->overlay_scale_inc = inm(OVERLAY_SCALE_INC);
            pATIHW->overlay_scale_cntl = inm(OVERLAY_SCALE_CNTL);

            pATIHW->scaler_height_width = inm(SCALER_HEIGHT_WIDTH);

            pATIHW->scaler_test = inm(SCALER_TEST);

            pATIHW->video_format = inm(VIDEO_FORMAT);

            if (pATI->Chip < ATI_CHIP_264VTB)
            {
                pATIHW->buf0_offset = inm(BUF0_OFFSET);
                pATIHW->buf0_pitch = inm(BUF0_PITCH);
                pATIHW->buf1_offset = inm(BUF1_OFFSET);
                pATIHW->buf1_pitch = inm(BUF1_PITCH);
            }
            else
            {
                pATIHW->scaler_buf0_offset = inm(SCALER_BUF0_OFFSET);
                pATIHW->scaler_buf1_offset = inm(SCALER_BUF1_OFFSET);
                pATIHW->scaler_buf_pitch = inm(SCALER_BUF_PITCH);

                pATIHW->overlay_exclusive_horz = inm(OVERLAY_EXCLUSIVE_HORZ);
                pATIHW->overlay_exclusive_vert = inm(OVERLAY_EXCLUSIVE_VERT);

                if (pATI->Chip >= ATI_CHIP_264GTPRO)
                {
                    pATIHW->scaler_colour_cntl = inm(SCALER_COLOUR_CNTL);

                    pATIHW->scaler_h_coeff0 = inm(SCALER_H_COEFF0);
                    pATIHW->scaler_h_coeff1 = inm(SCALER_H_COEFF1);
                    pATIHW->scaler_h_coeff2 = inm(SCALER_H_COEFF2);
                    pATIHW->scaler_h_coeff3 = inm(SCALER_H_COEFF3);
                    pATIHW->scaler_h_coeff4 = inm(SCALER_H_COEFF4);

                    pATIHW->scaler_buf0_offset_u = inm(SCALER_BUF0_OFFSET_U);
                    pATIHW->scaler_buf0_offset_v = inm(SCALER_BUF0_OFFSET_V);
                    pATIHW->scaler_buf1_offset_u = inm(SCALER_BUF1_OFFSET_U);
                    pATIHW->scaler_buf1_offset_v = inm(SCALER_BUF1_OFFSET_V);
                }
            }
        }

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

/*
 * ATIMach64ModeAdjust --
 *
 * This function is called to adjust horizontal and vertical timings.
 */
static void
ATIMach64ModeAdjust
(
    ATIPtr         pATI,
    ATIHWPtr       pATIHW,
    DisplayModePtr pMode
)
{
    int VDisplay;

    /* Clobber mode timings */
    if (pATI->OptionPanelDisplay && (pATI->LCDPanelID >= 0))
    {
        if (!pMode->CrtcHAdjusted && !pMode->CrtcVAdjusted &&
            (!pATI->OptionLCDSync || (pMode->type & M_T_BUILTIN)))
        {
        int VScan;

        pMode->Clock = pATI->LCDClock;
        pMode->Flags &= ~(V_DBLSCAN | V_INTERLACE | V_CLKDIV2);

        pMode->VScan = 0;

        /*
         * Use doublescanning or multiscanning to get around vertical blending
         * limitations.
         */
        VScan = pATI->LCDVertical / pMode->VDisplay;
        if (VScan > 1)
        {
            VScan = 2;
            pMode->Flags |= V_DBLSCAN;
        }

        pMode->HSyncStart = pMode->HDisplay + pATI->LCDHSyncStart;
        pMode->HSyncEnd = pMode->HSyncStart + pATI->LCDHSyncWidth;
        pMode->HTotal = pMode->HDisplay + pATI->LCDHBlankWidth;

        pMode->VSyncStart = pMode->VDisplay +
            ATIDivide(pATI->LCDVSyncStart, VScan, 0, 0);
        pMode->VSyncEnd = pMode->VSyncStart +
            ATIDivide(pATI->LCDVSyncWidth, VScan, 0, 1);
        pMode->VTotal = pMode->VDisplay +
            ATIDivide(pATI->LCDVBlankWidth, VScan, 0, 0);
        }
    }

    /* 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;

#ifdef TV_OUT
        if (pATI->tvActive)
            VDisplay = pMode->CrtcVDisplay;
#endif

        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 */
}

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

    /* 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_off_pitch = SetBits(pATI->displayWidth >> 3, CRTC_PITCH);

    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)
    {
        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
)
{

#ifndef AVOID_CPIO

    if (pATIHW->crtc == ATI_CRTC_MACH64)

#endif /* AVOID_CPIO */

    {
        if ((pATIHW->FeedbackDivider > 0))
            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->Block0Base)
    {
        /* 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);

        if (pATI->Chip >= ATI_CHIP_264GTPRO)
        {
            /* Load ROP unit registers */
            ATIMach64WaitForFIFO(pATI, 2);
            outf(Z_CNTL, 0);
            outf(ALPHA_TST_CNTL, 0);
        }

        /* 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);

        if (pATI->Chip >= ATI_CHIP_264GTPRO)
        {
            CARD32 offset = TEX_LEVEL(pATIHW->tex_size_pitch);

            /* Load 3D control & texture registers */
            ATIMach64WaitForFIFO(pATI, 2);
            outf(TEX_0_OFF + offset, pATIHW->tex_offset);
            outf(SCALE_3D_CNTL, pATIHW->scale_3d_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 = ATIHostDataAddr(HOST_DATA_0);
        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);

        if (pATI->Chip >= ATI_CHIP_264GTPRO)
        {
            /* Load texture setup registers */
            ATIMach64WaitForFIFO(pATI, 2);
            outf(TEX_SIZE_PITCH, pATIHW->tex_size_pitch);
            outf(TEX_CNTL, pATIHW->tex_cntl);
        }

        if (pATI->Block1Base)
        {
            /* Load overlay & scaler registers */
            ATIMach64WaitForFIFO(pATI, 10);
            outf(OVERLAY_Y_X_START, pATIHW->overlay_y_x_start);
            outf(OVERLAY_Y_X_END, pATIHW->overlay_y_x_end);

            outf(OVERLAY_GRAPHICS_KEY_CLR, pATIHW->overlay_graphics_key_clr);
            outf(OVERLAY_GRAPHICS_KEY_MSK, pATIHW->overlay_graphics_key_msk);

            outf(OVERLAY_KEY_CNTL, pATIHW->overlay_key_cntl);

            outf(OVERLAY_SCALE_INC, pATIHW->overlay_scale_inc);
            outf(OVERLAY_SCALE_CNTL, pATIHW->overlay_scale_cntl);

            outf(SCALER_HEIGHT_WIDTH, pATIHW->scaler_height_width);

            outf(SCALER_TEST, pATIHW->scaler_test);

            outf(VIDEO_FORMAT, pATIHW->video_format);

            if (pATI->Chip < ATI_CHIP_264VTB)
            {
                ATIMach64WaitForFIFO(pATI, 4);
                outf(BUF0_OFFSET, pATIHW->buf0_offset);
                outf(BUF0_PITCH, pATIHW->buf0_pitch);
                outf(BUF1_OFFSET, pATIHW->buf1_offset);
                outf(BUF1_PITCH, pATIHW->buf1_pitch);
            }
            else
            {
                ATIMach64WaitForFIFO(pATI, 5);
                outf(SCALER_BUF0_OFFSET, pATIHW->scaler_buf0_offset);
                outf(SCALER_BUF1_OFFSET, pATIHW->scaler_buf1_offset);
                outf(SCALER_BUF_PITCH, pATIHW->scaler_buf_pitch);

                outf(OVERLAY_EXCLUSIVE_HORZ, pATIHW->overlay_exclusive_horz);
                outf(OVERLAY_EXCLUSIVE_VERT, pATIHW->overlay_exclusive_vert);

                if (pATI->Chip >= ATI_CHIP_264GTPRO)
                {
                    ATIMach64WaitForFIFO(pATI, 10);
                    outf(SCALER_COLOUR_CNTL, pATIHW->scaler_colour_cntl);

                    outf(SCALER_H_COEFF0, pATIHW->scaler_h_coeff0);
                    outf(SCALER_H_COEFF1, pATIHW->scaler_h_coeff1);
                    outf(SCALER_H_COEFF2, pATIHW->scaler_h_coeff2);
                    outf(SCALER_H_COEFF3, pATIHW->scaler_h_coeff3);
                    outf(SCALER_H_COEFF4, pATIHW->scaler_h_coeff4);

                    outf(SCALER_BUF0_OFFSET_U, pATIHW->scaler_buf0_offset_u);
                    outf(SCALER_BUF0_OFFSET_V, pATIHW->scaler_buf0_offset_v);
                    outf(SCALER_BUF1_OFFSET_U, pATIHW->scaler_buf1_offset_u);
                    outf(SCALER_BUF1_OFFSET_V, pATIHW->scaler_buf1_offset_v);
                }
            }
        }

        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);

            if (pATI->Chip >= ATI_CHIP_264GTPRO)
            {
                CacheRegister(SCALE_3D_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_PIX_WIDTH);
            CacheRegister(DP_MIX);

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

            if (pATI->Chip >= ATI_CHIP_264GTPRO)
            {
                CacheRegister(TEX_SIZE_PITCH);
            }

            if (pATI->Block1Base)
            {
                CacheRegister(OVERLAY_Y_X_START);
                CacheRegister(OVERLAY_Y_X_END);

                CacheRegister(OVERLAY_GRAPHICS_KEY_CLR);
                CacheRegister(OVERLAY_GRAPHICS_KEY_MSK);

                CacheRegister(OVERLAY_KEY_CNTL);

                CacheRegister(OVERLAY_SCALE_INC);
                CacheRegister(OVERLAY_SCALE_CNTL);

                CacheRegister(SCALER_HEIGHT_WIDTH);

                CacheRegister(SCALER_TEST);

                CacheRegister(VIDEO_FORMAT);

                if (pATI->Chip < ATI_CHIP_264VTB)
                {
                    CacheRegister(BUF0_OFFSET);
                    CacheRegister(BUF0_PITCH);
                    CacheRegister(BUF1_OFFSET);
                    CacheRegister(BUF1_PITCH);
                }
                else
                {
                    CacheRegister(SCALER_BUF0_OFFSET);
                    CacheRegister(SCALER_BUF1_OFFSET);
                    CacheRegister(SCALER_BUF_PITCH);

                    CacheRegister(OVERLAY_EXCLUSIVE_HORZ);
                    CacheRegister(OVERLAY_EXCLUSIVE_VERT);

                    if (pATI->Chip >= ATI_CHIP_264GTPRO)
                    {
                        CacheRegister(SCALER_COLOUR_CNTL);

                        CacheRegister(SCALER_H_COEFF0);
                        CacheRegister(SCALER_H_COEFF1);
                        CacheRegister(SCALER_H_COEFF2);
                        CacheRegister(SCALER_H_COEFF3);
                        CacheRegister(SCALER_H_COEFF4);

                        CacheRegister(SCALER_BUF0_OFFSET_U);
                        CacheRegister(SCALER_BUF0_OFFSET_V);
                        CacheRegister(SCALER_BUF1_OFFSET_U);
                        CacheRegister(SCALER_BUF1_OFFSET_V);
                    }
                }
            }
        }
    }

#ifndef AVOID_CPIO

    if (pATIHW->crtc == ATI_CRTC_MACH64)

#endif /* AVOID_CPIO */

    {
        /* 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_BUF_CNTL, pATIHW->mem_buf_cntl);
            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;
    }

#ifdef XF86DRI_DEVEL

    /* XAA Sync requires the DRM lock if DRI enabled */
    ATIDRILock(pScreenInfo);

#endif /* XF86DRI_DEVEL */

    ATIMach64Sync(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 = ATIMach64GetLCDReg(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)) */
            {
                ATIMach64PutLCDReg(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 = ATIMach64GetLCDReg(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)) */
            {
                ATIMach64PutLCDReg(LCD_GEN_CNTL, lcd_gen_ctrl);
                outr(LCD_INDEX, lcd_index);
            }
        }
    }

#ifdef XF86DRI_DEVEL

    ATIDRIUnlock(pScreenInfo);

#endif /* XF86DRI_DEVEL */

}
