/*
 * Copyright 1999, 2000 ATI Technologies Inc., Markham, Ontario,
 *                      Precision Insight, Inc., Cedar Park, Texas, and
 *                      VA Linux Systems Inc., Fremont, California.
 *
 * 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 on 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 ATI, PRECISION INSIGHT, VA LINUX
 * SYSTEMS AND/OR THEIR 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 "config.h"

/*
 * Authors:
 *   Rickard E. Faith <faith@valinux.com>
 *   Kevin E. Martin <martin@valinux.com>
 *   Gareth Hughes <gareth@valinux.com>
 *
 * Credits:
 *
 *   Thanks to Alan Hourihane <alanh@fairlite.demon..co.uk> and SuSE for
 *   providing source code to their 3.3.x Rage 128 driver.  Portions of
 *   this file are based on the initialization code for that driver.
 *
 * References:
 *
 *   RAGE 128 VR/ RAGE 128 GL Register Reference Manual (Technical
 *   Reference Manual P/N RRG-G04100-C Rev. 0.04), ATI Technologies: April
 *   1999.
 *
 *   RAGE 128 Software Development Manual (Technical Reference Manual P/N
 *   SDK-G04000 Rev. 0.01), ATI Technologies: June 1999.
 *
 * This server does not yet support these XFree86 4.0 features:
 *   DDC1 & DDC2
 *   shadowfb
 *   overlay planes
 *
 * Modified by Marc Aurele La France <tsi@xfree86.org> for ATI driver merge.
 *
 * Dualhead support - Alex Deucher <agd5f@yahoo.com>
 */

#include <string.h>
#include <stdio.h>

				/* Driver data structures */
#include "r128.h"
#include "r128_probe.h"
#include "r128_reg.h"
#include "r128_version.h"
#include "xf86Priv.h"

#ifdef HAVE_DEV_WSCONS_WSCONSIO_H
#include <sys/time.h>
#include <sys/ioctl.h>
#include <dev/wscons/wsconsio.h>
#endif

#ifdef R128DRI
#define _XF86DRI_SERVER_
#include "r128_dri.h"
#include "r128_common.h"
#include "r128_sarea.h"
#endif

				/* colormap initialization */
#include "micmap.h"

				/* X and server generic header files */
#include "xf86.h"
#include "xf86_OSproc.h"
#include "xf86RandR12.h"
#include "xf86cmap.h"
#include "xf86xv.h"
				/* VBE and int10 are only available on x86. */
#if defined(__i386__) || defined(__x86_64__)
#include "vbe.h"
#else
typedef void *xf86Int10InfoPtr;
#endif

				/* fbdevhw & vgahw */
#ifdef WITH_VGAHW
#include "vgaHW.h"
#endif
#include "fbdevhw.h"
#include "dixstruct.h"

				/* DPMS support. */
#include <X11/extensions/dpmsconst.h>

static Bool R128CloseScreen(ScreenPtr pScreen);
static Bool R128SaveScreen(ScreenPtr pScreen, int mode);
static void R128Save(ScrnInfoPtr pScrn);
static void R128Restore(ScrnInfoPtr pScrn);

typedef enum {
  OPTION_NOACCEL,
  OPTION_FBDEV,
  OPTION_DAC_6BIT,
  OPTION_VGA_ACCESS,
  OPTION_SHOW_CACHE,
  OPTION_SW_CURSOR,
  OPTION_VIDEO_KEY,
  OPTION_PANEL_WIDTH,
  OPTION_PANEL_HEIGHT,
  OPTION_PROG_FP_REGS,
#ifdef R128DRI
  OPTION_XV_DMA,
  OPTION_IS_PCI,
  OPTION_CCE_PIO,
  OPTION_NO_SECURITY,
  OPTION_USEC_TIMEOUT,
  OPTION_AGP_MODE,
  OPTION_AGP_SIZE,
  OPTION_RING_SIZE,
  OPTION_BUFFER_SIZE,
  OPTION_PAGE_FLIP,
#endif
  OPTION_ACCELMETHOD,
  OPTION_RENDERACCEL
} R128Opts;

static const OptionInfoRec R128Options[] = {
{ OPTION_NOACCEL,      "NoAccel",          OPTV_BOOLEAN, {0}, FALSE },
{ OPTION_FBDEV,        "UseFBDev",         OPTV_BOOLEAN, {0}, FALSE },
{ OPTION_DAC_6BIT,     "Dac6Bit",          OPTV_BOOLEAN, {0}, FALSE },
{ OPTION_VGA_ACCESS,   "VGAAccess",        OPTV_BOOLEAN, {0}, TRUE  },
{ OPTION_SHOW_CACHE,   "ShowCache",        OPTV_BOOLEAN, {0}, FALSE },
{ OPTION_SW_CURSOR,    "SWcursor",         OPTV_BOOLEAN, {0}, FALSE },
{ OPTION_VIDEO_KEY,    "VideoKey",         OPTV_INTEGER, {0}, FALSE },
{ OPTION_PANEL_WIDTH,  "PanelWidth",       OPTV_INTEGER, {0}, FALSE },
{ OPTION_PANEL_HEIGHT, "PanelHeight",      OPTV_INTEGER, {0}, FALSE },
{ OPTION_PROG_FP_REGS, "ProgramFPRegs",    OPTV_BOOLEAN, {0}, FALSE },
#ifdef R128DRI
  { OPTION_XV_DMA,       "DMAForXv",         OPTV_BOOLEAN, {0}, FALSE },
  { OPTION_IS_PCI,       "ForcePCIMode",     OPTV_BOOLEAN, {0}, FALSE },
  { OPTION_CCE_PIO,      "CCEPIOMode",       OPTV_BOOLEAN, {0}, FALSE },
  { OPTION_NO_SECURITY,  "CCENoSecurity",    OPTV_BOOLEAN, {0}, FALSE },
  { OPTION_USEC_TIMEOUT, "CCEusecTimeout",   OPTV_INTEGER, {0}, FALSE },
  { OPTION_AGP_MODE,     "AGPMode",          OPTV_INTEGER, {0}, FALSE },
  { OPTION_AGP_SIZE,     "AGPSize",          OPTV_INTEGER, {0}, FALSE },
  { OPTION_RING_SIZE,    "RingSize",         OPTV_INTEGER, {0}, FALSE },
  { OPTION_BUFFER_SIZE,  "BufferSize",       OPTV_INTEGER, {0}, FALSE },
  { OPTION_PAGE_FLIP,    "EnablePageFlip",   OPTV_BOOLEAN, {0}, FALSE },
#endif
  { OPTION_ACCELMETHOD,  "AccelMethod",      OPTV_STRING,  {0}, FALSE },
  { OPTION_RENDERACCEL,  "RenderAccel",      OPTV_BOOLEAN, {0}, FALSE },
  { -1,                  NULL,               OPTV_NONE,    {0}, FALSE }
};

const OptionInfoRec *R128OptionsWeak(void) { return R128Options; }

R128RAMRec R128RAM[] = {        /* Memory Specifications
				   From RAGE 128 Software Development
				   Manual (Technical Reference Manual P/N
				   SDK-G04000 Rev 0.01), page 3-21.  */
    { 4, 4, 3, 3, 1, 3, 1, 16, 12, "128-bit SDR SGRAM 1:1" },
    { 4, 8, 3, 3, 1, 3, 1, 17, 13, "64-bit SDR SGRAM 1:1" },
    { 4, 4, 1, 2, 1, 2, 1, 16, 12, "64-bit SDR SGRAM 2:1" },
    { 4, 4, 3, 3, 2, 3, 1, 16, 12, "64-bit DDR SGRAM" },
};

extern _X_EXPORT int gR128EntityIndex;

int getR128EntityIndex(void)
{
    return gR128EntityIndex;
}

R128EntPtr R128EntPriv(ScrnInfoPtr pScrn)
{
    DevUnion     *pPriv;
    R128InfoPtr  info   = R128PTR(pScrn);
    pPriv = xf86GetEntityPrivate(info->pEnt->index,
                                 getR128EntityIndex());
    return pPriv->ptr;
}

/* Allocate our private R128InfoRec. */
static Bool R128GetRec(ScrnInfoPtr pScrn)
{
    if (pScrn->driverPrivate) return TRUE;

    pScrn->driverPrivate = XNFcallocarray(1, sizeof(R128InfoRec));
    return TRUE;
}

/* Free our private R128InfoRec. */
static void R128FreeRec(ScrnInfoPtr pScrn)
{
    if (!pScrn || !pScrn->driverPrivate) return;
    free(pScrn->driverPrivate);
    pScrn->driverPrivate = NULL;
}

/* Memory map the MMIO region.  Used during pre-init and by R128MapMem,
   below. */
static Bool R128MapMMIO(ScrnInfoPtr pScrn)
{
    R128InfoPtr info          = R128PTR(pScrn);

    if (info->FBDev) {
	info->MMIO = fbdevHWMapMMIO(pScrn);
    } else {
	int err = pci_device_map_range(info->PciInfo,
				       info->MMIOAddr,
				       R128_MMIOSIZE,
				       PCI_DEV_MAP_FLAG_WRITABLE,
				       &info->MMIO);

	if (err) {
	    xf86DrvMsg (pScrn->scrnIndex, X_ERROR,
                        "Unable to map MMIO aperture. %s (%d)\n",
                        strerror (err), err);
	    return FALSE;
	}
    }

    return TRUE;
}

/* Unmap the MMIO region.  Used during pre-init and by R128UnmapMem,
   below. */
static Bool R128UnmapMMIO(ScrnInfoPtr pScrn)
{
    R128InfoPtr info          = R128PTR(pScrn);

    if (info->FBDev)
	fbdevHWUnmapMMIO(pScrn);
    else {
	pci_device_unmap_range(info->PciInfo, info->MMIO, R128_MMIOSIZE);
    }
    info->MMIO = NULL;
    return TRUE;
}

/* Memory map the frame buffer.  Used by R128MapMem, below. */
static Bool R128MapFB(ScrnInfoPtr pScrn)
{
    R128InfoPtr info          = R128PTR(pScrn);

    if (info->FBDev) {
	info->FB = fbdevHWMapVidmem(pScrn);
    } else {
	int err = pci_device_map_range(info->PciInfo,
				       info->LinearAddr,
				       info->FbMapSize,
				       PCI_DEV_MAP_FLAG_WRITABLE |
				       PCI_DEV_MAP_FLAG_WRITE_COMBINE,
				       &info->FB);

	if (err) {
	    xf86DrvMsg (pScrn->scrnIndex, X_ERROR,
                        "Unable to map FB aperture. %s (%d)\n",
                        strerror (err), err);
	    return FALSE;
	}
    }

    if (!info->FB) return FALSE;
    return TRUE;
}

/* Unmap the frame buffer.  Used by R128UnmapMem, below. */
static Bool R128UnmapFB(ScrnInfoPtr pScrn)
{
    R128InfoPtr info          = R128PTR(pScrn);

    if (info->FBDev)
	fbdevHWUnmapVidmem(pScrn);
    else
	pci_device_unmap_range(info->PciInfo, info->FB, info->FbMapSize);
    info->FB = NULL;
    return TRUE;
}

/* Memory map the MMIO region and the frame buffer. */
static Bool R128MapMem(ScrnInfoPtr pScrn)
{
    if (!R128MapMMIO(pScrn)) return FALSE;
    if (!R128MapFB(pScrn)) {
	R128UnmapMMIO(pScrn);
	return FALSE;
    }
    return TRUE;
}

/* Unmap the MMIO region and the frame buffer. */
static Bool R128UnmapMem(ScrnInfoPtr pScrn)
{
    if (!R128UnmapMMIO(pScrn) || !R128UnmapFB(pScrn)) return FALSE;
    return TRUE;
}

/* Read PLL information */
unsigned R128INPLL(ScrnInfoPtr pScrn, int addr)
{
    R128InfoPtr   info      = R128PTR(pScrn);
    unsigned char *R128MMIO = info->MMIO;

    OUTREG8(R128_CLOCK_CNTL_INDEX, addr & 0x3f);
    return INREG(R128_CLOCK_CNTL_DATA);
}

#if 0
/* Read PAL information (only used for debugging). */
static int R128INPAL(int idx)
{
    R128InfoPtr   info      = R128PTR(pScrn);
    unsigned char *R128MMIO = info->MMIO;

    OUTREG(R128_PALETTE_INDEX, idx << 16);
    return INREG(R128_PALETTE_DATA);
}
#endif

/* Wait for vertical sync. */
void R128WaitForVerticalSync(ScrnInfoPtr pScrn)
{
    R128InfoPtr   info      = R128PTR(pScrn);
    unsigned char *R128MMIO = info->MMIO;
    int           i;

    OUTREG(R128_GEN_INT_STATUS, R128_VSYNC_INT_AK);
    for (i = 0; i < R128_TIMEOUT; i++) {
	if (INREG(R128_GEN_INT_STATUS) & R128_VSYNC_INT) break;
    }
}

/* Compute log base 2 of val. */
int R128MinBits(int val)
{
    int bits;

    if (!val) return 1;
    for (bits = 0; val; val >>= 1, ++bits);
    return bits;
}

/* Finds the first output using a given crtc. */
xf86OutputPtr R128FirstOutput(xf86CrtcPtr crtc)
{
    ScrnInfoPtr pScrn = crtc->scrn;
    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
    xf86OutputPtr output = xf86_config->output[0];
    int o;

    for (o = 0; o < xf86_config->num_output; o++) {
        output = xf86_config->output[o];
        if (output->crtc == crtc) break;
    }

    return output;
}

/* Read the Video BIOS block. */
static Bool R128GetBIOSParameters(ScrnInfoPtr pScrn, xf86Int10InfoPtr pInt10)
{
    R128InfoPtr info = R128PTR(pScrn);

    int size = info->PciInfo->rom_size > R128_VBIOS_SIZE ? info->PciInfo->rom_size : R128_VBIOS_SIZE;
    info->VBIOS = malloc(size);

    if (!info->VBIOS) {
	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
		   "Cannot allocate space for hold Video BIOS!\n");
	return FALSE;
    }

    /* VBE and int10 are only available on x86. */
#if defined(__i386__) || defined(__x86_64__)
    if (pInt10) {
	info->BIOSAddr = pInt10->BIOSseg << 4;
	(void)memcpy(info->VBIOS, xf86int10Addr(pInt10, info->BIOSAddr),
		     R128_VBIOS_SIZE);
    } else
#endif
    {
	if (pci_device_read_rom(info->PciInfo, info->VBIOS)) {
	    xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
		       "Failed to read PCI ROM!\n");
	}
    }
    if (info->VBIOS[0] != 0x55 || info->VBIOS[1] != 0xaa) {
	info->BIOSAddr = 0x00000000;
	free(info->VBIOS);
	info->VBIOS = NULL;
	xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
		   "Video BIOS not found!\n");
    }

    return TRUE;
}

/* Read the FP parameters if an LVDS panel is expected. */
void R128GetPanelInfoFromBIOS(xf86OutputPtr output)
{
    ScrnInfoPtr pScrn = output->scrn;
    R128InfoPtr info  = R128PTR(pScrn);
    R128OutputPrivatePtr r128_output = output->driver_private;
    int FPHeader = 0;
    int i;

    r128_output->PanelPwrDly = 200;
    xf86GetOptValInteger(info->Options, OPTION_PANEL_WIDTH,  &(r128_output->PanelXRes));
    xf86GetOptValInteger(info->Options, OPTION_PANEL_HEIGHT, &(r128_output->PanelYRes));

    if (!info->VBIOS) goto fallback;
    info->FPBIOSstart = 0;

    /* FIXME: There should be direct access to the start of the FP info
     * tables, but until we find out where that offset is stored, we
     * must search for the ATI signature string: "M3      ".
     */
    for (i = 4; i < R128_VBIOS_SIZE - 8; i++) {
        if (R128_BIOS8(i)     == 'M' &&
            R128_BIOS8(i + 1) == '3' &&
            R128_BIOS8(i + 2) == ' ' &&
            R128_BIOS8(i + 3) == ' ' &&
            R128_BIOS8(i + 4) == ' ' &&
            R128_BIOS8(i + 5) == ' ' &&
            R128_BIOS8(i + 6) == ' ' &&
            R128_BIOS8(i + 7) == ' ') {
            FPHeader = i - 2;
            break;
        }
    }

    if (!FPHeader) goto fallback;

    /* Assume that only one panel is attached and supported */
    for (i = FPHeader + 20; i < FPHeader + 84; i += 2) {
        if (R128_BIOS16(i) != 0) {
            info->FPBIOSstart = R128_BIOS16(i);
            break;
        }
    }

    if (!info->FPBIOSstart) return;

    if (!r128_output->PanelXRes)
        r128_output->PanelXRes = R128_BIOS16(info->FPBIOSstart + 25);
    if (!r128_output->PanelYRes)
        r128_output->PanelYRes = R128_BIOS16(info->FPBIOSstart + 27);
    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Panel size: %dx%d\n",
               r128_output->PanelXRes, r128_output->PanelYRes);

    r128_output->PanelPwrDly = R128_BIOS8(info->FPBIOSstart + 56);

    if (!r128_output->PanelXRes || !r128_output->PanelYRes) {
        info->HasPanelRegs = FALSE;
        xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
		   "Can't determine panel dimensions, and none specified.\n"
		   "\tDisabling programming of FP registers.\n");
    }

    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Panel ID: ");
    for (i = 1; i <= 24; i++)
        ErrorF("%c", R128_BIOS8(info->FPBIOSstart + i));

    ErrorF("\n");

    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Panel Type: ");
    i = R128_BIOS16(info->FPBIOSstart + 29);
    if (i & 1) ErrorF("Color, ");
    else       ErrorF("Monochrome, ");
    if (i & 2) ErrorF("Dual(split), ");
    else       ErrorF("Single, ");

    switch ((i >> 2) & 0x3f) {
    case 0:  ErrorF("STN");        break;
    case 1:  ErrorF("TFT");        break;
    case 2:  ErrorF("Active STN"); break;
    case 3:  ErrorF("EL");         break;
    case 4:  ErrorF("Plasma");     break;
    default: ErrorF("UNKNOWN");    break;
    }

    ErrorF("\n");

    if (R128_BIOS8(info->FPBIOSstart + 61) & 1) {
        xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Panel Interface: LVDS\n");
    } else {
        /* FIXME: Add Non-LVDS flat panel support */
        xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
                   "Non-LVDS panel interface detected!  "
                   "This support is untested and may not "
                   "function properly\n");
    }
    return;

fallback:
#ifdef WSDISPLAYIO_GINFO
    if ((!r128_output->PanelXRes || !r128_output->PanelYRes) &&
        (info->HaveWSDisplay)) {
	/*
	 * we may not be on x86 so check wsdisplay for panel dimensions
	 * this assumes that the r128 is the console, although that should
	 * be the case in the vast majority of cases where an LCD is hooked up
	 * directly
	 * We should probably just check the relevant registers but I'm not
	 * sure they're available at this point.
	 */
	struct wsdisplay_fbinfo fbinfo;

	if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_GINFO, &fbinfo) == 0) {
	    r128_output->PanelXRes = fbinfo.width;
	    r128_output->PanelYRes = fbinfo.height;
	}
    }
#endif
    return;
}

/* Read PLL parameters from BIOS block.  Default to typical values if there
   is no BIOS. */
static Bool R128GetPLLParameters(ScrnInfoPtr pScrn)
{
    R128InfoPtr   info = R128PTR(pScrn);
    R128PLLPtr    pll  = &info->pll;

#if defined(__powerpc__) || defined(__alpha__)
    /* there is no bios under Linux PowerPC but Open Firmware
       does set up the PLL registers properly and we can use
       those to calculate xclk and find the reference divider */

    unsigned x_mpll_ref_fb_div;
    unsigned xclk_cntl;
    unsigned Nx, M;
    unsigned PostDivSet[] = {0, 1, 2, 4, 8, 3, 6, 12};

    /* Assume REF clock is 2950 (in units of 10khz) */
    /* and that all pllclk must be between 125 Mhz and 250Mhz */
    pll->reference_freq = 2950;
    pll->min_pll_freq   = 12500;
    pll->max_pll_freq   = 25000;

    x_mpll_ref_fb_div = INPLL(pScrn, R128_X_MPLL_REF_FB_DIV);
    xclk_cntl = INPLL(pScrn, R128_XCLK_CNTL) & 0x7;
    pll->reference_div =
	INPLL(pScrn,R128_PPLL_REF_DIV) & R128_PPLL_REF_DIV_MASK;

    Nx = (x_mpll_ref_fb_div & 0x00FF00) >> 8;
    M =  (x_mpll_ref_fb_div & 0x0000FF);

    pll->xclk =  R128Div((2 * Nx * pll->reference_freq),
			 (M * PostDivSet[xclk_cntl]));

#else /* !defined(__powerpc__) */

    if (!info->VBIOS) {
	xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
		   "Video BIOS not detected, using default PLL parameters!\n");
				/* These probably aren't going to work for
				   the card you are using.  Specifically,
				   reference freq can be 29.50MHz,
				   28.63MHz, or 14.32MHz.  YMMV. */
	pll->reference_freq = 2950;
	pll->reference_div  = 65;
	pll->min_pll_freq   = 12500;
	pll->max_pll_freq   = 25000;
	pll->xclk           = 10300;
    } else {
	uint16_t bios_header    = R128_BIOS16(0x48);
	uint16_t pll_info_block = R128_BIOS16(bios_header + 0x30);
    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                        "Header at 0x%04x; PLL Information at 0x%04x\n",
                        bios_header, pll_info_block));

	pll->reference_freq = R128_BIOS16(pll_info_block + 0x0e);
	pll->reference_div  = R128_BIOS16(pll_info_block + 0x10);
	pll->min_pll_freq   = R128_BIOS32(pll_info_block + 0x12);
	pll->max_pll_freq   = R128_BIOS32(pll_info_block + 0x16);
	pll->xclk           = R128_BIOS16(pll_info_block + 0x08);
    }
#endif /* __powerpc__ */

    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
	       "PLL parameters: rf=%d rd=%d min=%d max=%d; xclk=%d\n",
	       pll->reference_freq,
	       pll->reference_div,
	       pll->min_pll_freq,
	       pll->max_pll_freq,
	       pll->xclk);

    return TRUE;
}

/* This is called by R128PreInit to set up the default visual. */
static Bool R128PreInitVisual(ScrnInfoPtr pScrn)
{
    if (!xf86SetDepthBpp(pScrn, 0, 0, 0, (Support24bppFb
					  | Support32bppFb
					  | SupportConvert32to24
					  )))
	return FALSE;

    switch (pScrn->depth) {
    case 8:
    case 15:
    case 16:
    case 24:
	break;
    default:
	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
		   "Given depth (%d) is not supported by %s driver\n",
		   pScrn->depth, R128_DRIVER_NAME);
	return FALSE;
    }

    xf86PrintDepthBpp(pScrn);

    if (!xf86SetDefaultVisual(pScrn, -1)) return FALSE;

    if (pScrn->depth > 8 && pScrn->defaultVisual != TrueColor) {
	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
		   "Default visual (%s) is not supported at depth %d\n",
		   xf86GetVisualName(pScrn->defaultVisual), pScrn->depth);
	return FALSE;
    }
    return TRUE;

}

/* This is called by R128PreInit to handle all color weight issues. */
static Bool R128PreInitWeight(ScrnInfoPtr pScrn)
{
    R128InfoPtr info          = R128PTR(pScrn);
    rgb defaultWeight = { 0, 0, 0 };

    /*
     * Save flag for 6 bit DAC to use for setting CRTC registers.
     * Otherwise use an 8 bit DAC, even if xf86SetWeight sets
     * pScrn->rgbBits to some value other than 8.
     */
    if (pScrn->depth <= 8) {
        if (info->dac6bits) {
            pScrn->rgbBits = 6;
        } else {
            pScrn->rgbBits = 8;
        }
    } else {
        info->dac6bits = FALSE;
        pScrn->rgbBits = 8;
    }

    if (pScrn->depth > 8) {
        if (!xf86SetWeight(pScrn, defaultWeight, defaultWeight)) return FALSE;
    }

    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
               "Using %d bits per RGB (%d bit DAC)\n",
               pScrn->rgbBits, info->dac6bits ? 6 : 8);

    return TRUE;
}

/* This is called by R128PreInit to handle config file overrides for things
   like chipset and memory regions.  Also determine memory size and type.
   If memory type ever needs an override, put it in this routine. */
static Bool R128PreInitConfig(ScrnInfoPtr pScrn)
{
    R128InfoPtr   info      = R128PTR(pScrn);
    R128EntPtr    pR128Ent  = R128EntPriv(pScrn);
    unsigned char *R128MMIO = info->MMIO;
    EntityInfoPtr pEnt      = info->pEnt;
    GDevPtr       dev       = pEnt->device;
    int           offset    = 0;        /* RAM Type */
    MessageType   from;

				/* Chipset */
    from = X_PROBED;
    if (dev->chipset && *dev->chipset) {
	info->Chipset  = xf86StringToToken(R128Chipsets, dev->chipset);
	from           = X_CONFIG;
    } else if (dev->chipID >= 0) {
	info->Chipset  = dev->chipID;
	from           = X_CONFIG;
    } else {
	info->Chipset = PCI_DEV_DEVICE_ID(info->PciInfo);
    }
    pScrn->chipset = (char *)xf86TokenToString(R128Chipsets, info->Chipset);

    if (!pScrn->chipset) {
	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
		   "ChipID 0x%04x is not recognized\n", info->Chipset);
	return FALSE;
    }

    if (info->Chipset < 0) {
	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
		   "Chipset \"%s\" is not recognized\n", pScrn->chipset);
	return FALSE;
    }

    xf86DrvMsg(pScrn->scrnIndex, from,
	       "Chipset: \"%s\" (ChipID = 0x%04x)\n",
	       pScrn->chipset,
	       info->Chipset);

				/* Framebuffer */

    from             = X_PROBED;
    info->LinearAddr = PCI_REGION_BASE(info->PciInfo, 0, REGION_MEM) & 0xfc000000;
    pScrn->memPhysBase = info->LinearAddr;
    if (dev->MemBase) {
	xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		   "Linear address override, using 0x%08lx instead of 0x%08lx\n",
		   dev->MemBase,
		   info->LinearAddr);
	info->LinearAddr = dev->MemBase;
	from             = X_CONFIG;
    } else if (!info->LinearAddr) {
	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
		   "No valid linear framebuffer address\n");
	return FALSE;
    }
    xf86DrvMsg(pScrn->scrnIndex, from,
	       "Linear framebuffer at 0x%08lx\n", info->LinearAddr);

				/* MMIO registers */
    from             = X_PROBED;
    info->MMIOAddr   = PCI_REGION_BASE(info->PciInfo, 2, REGION_MEM) & 0xffffff00;
    if (dev->IOBase) {
	xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		   "MMIO address override, using 0x%08lx instead of 0x%08lx\n",
		   dev->IOBase,
		   info->MMIOAddr);
	info->MMIOAddr = dev->IOBase;
	from           = X_CONFIG;
    } else if (!info->MMIOAddr) {
	xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No valid MMIO address\n");
	return FALSE;
    }
    xf86DrvMsg(pScrn->scrnIndex, from,
	       "MMIO registers at 0x%08lx\n", info->MMIOAddr);

				/* Flat panel (part 1) */
    if (xf86GetOptValBool(info->Options, OPTION_PROG_FP_REGS,
			  &info->HasPanelRegs)) {
	xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
		   "Turned flat panel register programming %s\n",
		   info->HasPanelRegs ? "on" : "off");
	xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
		   "\n\nWARNING: Forcing the driver to use/not use the flat panel registers\nmight damage your flat panel.  Use at your *OWN* *RISK*.\n\n");
    } else {
        info->isDFP = FALSE;
        info->isPro2 = FALSE;
        pR128Ent->HasCRTC2 = FALSE;
	switch (info->Chipset) {
	/* R128 Pro and Pro2 can have DFP, we will deal with it.
	   No support for dual-head/xinerama yet.
           M3 can also have DFP, no support for now */
	case PCI_CHIP_RAGE128TF:
	case PCI_CHIP_RAGE128TL:
	case PCI_CHIP_RAGE128TR:
	/* FIXME: RAGE128 TS/TT/TU are assumed to be PRO2 as all 6 chips came
	 *        out at the same time, so are of the same family likely.
	 *        This requires confirmation however to be fully correct.
	 *        Mike A. Harris <mharris@redhat.com>
	 */
	case PCI_CHIP_RAGE128TS:
	case PCI_CHIP_RAGE128TT:
	case PCI_CHIP_RAGE128TU: info->isPro2 = TRUE;
	/* FIXME: RAGE128 P[ABCEGHIJKLMNOQSTUVWX] are assumed to have DFP
	 *        capability, as the comment at the top suggests.
	 *        This requires confirmation however to be fully correct.
	 *        Mike A. Harris <mharris@redhat.com>
	 */
	case PCI_CHIP_RAGE128PA:
	case PCI_CHIP_RAGE128PB:
	case PCI_CHIP_RAGE128PC:
	case PCI_CHIP_RAGE128PE:
	case PCI_CHIP_RAGE128PG:
	case PCI_CHIP_RAGE128PH:
	case PCI_CHIP_RAGE128PI:
	case PCI_CHIP_RAGE128PJ:
	case PCI_CHIP_RAGE128PK:
	case PCI_CHIP_RAGE128PL:
	case PCI_CHIP_RAGE128PM:
	case PCI_CHIP_RAGE128PN:
	case PCI_CHIP_RAGE128PO:
	case PCI_CHIP_RAGE128PQ:
	case PCI_CHIP_RAGE128PS:
	case PCI_CHIP_RAGE128PT:
	case PCI_CHIP_RAGE128PU:
	case PCI_CHIP_RAGE128PV:
	case PCI_CHIP_RAGE128PW:
	case PCI_CHIP_RAGE128PX:

	case PCI_CHIP_RAGE128PD:
	case PCI_CHIP_RAGE128PF:
	case PCI_CHIP_RAGE128PP:
	case PCI_CHIP_RAGE128PR: info->isDFP = TRUE; break;

	case PCI_CHIP_RAGE128LE:
	case PCI_CHIP_RAGE128LF:
	case PCI_CHIP_RAGE128MF:
	case PCI_CHIP_RAGE128ML:
			info->HasPanelRegs = TRUE;
			/* which chips support dualhead? */
			pR128Ent->HasCRTC2 = TRUE;
			break;
	case PCI_CHIP_RAGE128RE:
	case PCI_CHIP_RAGE128RF:
	case PCI_CHIP_RAGE128RG:
	case PCI_CHIP_RAGE128RK:
	case PCI_CHIP_RAGE128RL:
	case PCI_CHIP_RAGE128SM:
	/* FIXME: RAGE128 S[EFGHKLN] are assumed to be like the SM above as
	 *        all of them are listed as "Rage 128 4x" in ATI docs.
	 *        This requires confirmation however to be fully correct.
	 *        Mike A. Harris <mharris@redhat.com>
	 */
	case PCI_CHIP_RAGE128SE:
	case PCI_CHIP_RAGE128SF:
	case PCI_CHIP_RAGE128SG:
	case PCI_CHIP_RAGE128SH:
	case PCI_CHIP_RAGE128SK:
	case PCI_CHIP_RAGE128SL:
	case PCI_CHIP_RAGE128SN:
	default:                 info->HasPanelRegs = FALSE; break;
	}
    }

				/* Read registers used to determine options */
    from                      = X_PROBED;
    if (!R128MapMMIO(pScrn)) return FALSE;
    R128MMIO                  = info->MMIO;

    if (info->FBDev)
	pScrn->videoRam       = fbdevHWGetVidmem(pScrn) / 1024;
    else
	pScrn->videoRam       = INREG(R128_CONFIG_MEMSIZE) / 1024;

    info->MemCntl             = INREG(R128_MEM_CNTL);
    info->BusCntl             = INREG(R128_BUS_CNTL);

				/* RAM */
    switch (info->MemCntl & 0x3) {
    case 0:                     /* SDR SGRAM 1:1 */
	switch (info->Chipset) {
	case PCI_CHIP_RAGE128TF:
	case PCI_CHIP_RAGE128TL:
	case PCI_CHIP_RAGE128TR:
	case PCI_CHIP_RAGE128LE:
	case PCI_CHIP_RAGE128LF:
	case PCI_CHIP_RAGE128MF:
	case PCI_CHIP_RAGE128ML:
	case PCI_CHIP_RAGE128RE:
	case PCI_CHIP_RAGE128RF:
	case PCI_CHIP_RAGE128RG: offset = 0; break; /* 128-bit SDR SGRAM 1:1 */
	case PCI_CHIP_RAGE128RK:
	case PCI_CHIP_RAGE128RL:
	case PCI_CHIP_RAGE128SM:
	default:                 offset = 1; break; /*  64-bit SDR SGRAM 1:1 */
	}
	break;
    case 1:                      offset = 2; break; /*  64-bit SDR SGRAM 2:1 */
    case 2:                      offset = 3; break; /*  64-bit DDR SGRAM     */
    default:                     offset = 1; break; /*  64-bit SDR SGRAM 1:1 */
    }
    info->ram = &R128RAM[offset];

    if (dev->videoRam) {
	xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		   "Video RAM override, using %d kB instead of %d kB\n",
		   dev->videoRam,
		   pScrn->videoRam);
	from             = X_CONFIG;
	pScrn->videoRam  = dev->videoRam;
    }

    xf86DrvMsg(pScrn->scrnIndex, from,
	       "VideoRAM: %d kByte (%s)\n", pScrn->videoRam, info->ram->name);

    pScrn->videoRam  &= ~1023;
    info->FbMapSize  = pScrn->videoRam * 1024;

#ifdef R128DRI
    /* AGP/PCI */
    if (!info->IsPCI) {
	switch (info->Chipset) {
	case PCI_CHIP_RAGE128LE:
	case PCI_CHIP_RAGE128RE:
	case PCI_CHIP_RAGE128RK:
	case PCI_CHIP_RAGE128PD:
	case PCI_CHIP_RAGE128PR:
	case PCI_CHIP_RAGE128PP: info->IsPCI = TRUE;  break;
	case PCI_CHIP_RAGE128LF:
	case PCI_CHIP_RAGE128MF:
	case PCI_CHIP_RAGE128ML:
	case PCI_CHIP_RAGE128PF:
	case PCI_CHIP_RAGE128RF:
	case PCI_CHIP_RAGE128RG:
	case PCI_CHIP_RAGE128RL:
	case PCI_CHIP_RAGE128SM:
	case PCI_CHIP_RAGE128TF:
	case PCI_CHIP_RAGE128TL:
	case PCI_CHIP_RAGE128TR:
	/* FIXME: Rage 128 S[EFGHKLN], T[STU], P[ABCEGHIJKLMNOQSTUVWX] are
	 * believed to be AGP, but need confirmation. <mharris@redhat.com>
	 */
	case PCI_CHIP_RAGE128PA:
	case PCI_CHIP_RAGE128PB:
	case PCI_CHIP_RAGE128PC:
	case PCI_CHIP_RAGE128PE:
	case PCI_CHIP_RAGE128PG:
	case PCI_CHIP_RAGE128PH:
	case PCI_CHIP_RAGE128PI:
	case PCI_CHIP_RAGE128PJ:
	case PCI_CHIP_RAGE128PK:
	case PCI_CHIP_RAGE128PL:
	case PCI_CHIP_RAGE128PM:
	case PCI_CHIP_RAGE128PN:
	case PCI_CHIP_RAGE128PO:
	case PCI_CHIP_RAGE128PQ:
	case PCI_CHIP_RAGE128PS:
	case PCI_CHIP_RAGE128PT:
	case PCI_CHIP_RAGE128PU:
	case PCI_CHIP_RAGE128PV:
	case PCI_CHIP_RAGE128PW:
	case PCI_CHIP_RAGE128PX:
	case PCI_CHIP_RAGE128TS:
	case PCI_CHIP_RAGE128TT:
	case PCI_CHIP_RAGE128TU:
	case PCI_CHIP_RAGE128SE:
	case PCI_CHIP_RAGE128SF:
	case PCI_CHIP_RAGE128SG:
	case PCI_CHIP_RAGE128SH:
	case PCI_CHIP_RAGE128SK:
	case PCI_CHIP_RAGE128SL:
	case PCI_CHIP_RAGE128SN:
	default:                 info->IsPCI = FALSE; break;
	}
    }
#endif

    return TRUE;
}

static Bool R128PreInitDDC(ScrnInfoPtr pScrn, xf86Int10InfoPtr pInt10)
{
/* VBE and int10 are only available on x86. */
#if defined(__i386__) || defined(__x86_64__)
    R128InfoPtr   info = R128PTR(pScrn);
    vbeInfoPtr pVbe;
#endif

    if (!xf86LoadSubModule(pScrn, "ddc")) return FALSE;
    if (!xf86LoadSubModule(pScrn, "i2c")) return FALSE;

#if !defined(__i386__) && !defined(__x86_64__)
    return TRUE;
#else
    if (xf86LoadSubModule(pScrn, "vbe")) {
	pVbe = VBEInit(pInt10,info->pEnt->index);
	if (!pVbe) return FALSE;
        xf86SetDDCproperties(pScrn,xf86PrintEDID(vbeDoEDID(pVbe,NULL)));
	vbeFree(pVbe);
	return TRUE;
    } else
	return FALSE;
#endif
}

/* This is called by R128PreInit to initialize gamma correction. */
static Bool R128PreInitGamma(ScrnInfoPtr pScrn)
{
    Gamma zeros = { 0.0, 0.0, 0.0 };

    if (!xf86SetGamma(pScrn, zeros)) return FALSE;
    return TRUE;
}

/* This is called by R128PreInit to initialize the hardware cursor. */
static Bool R128PreInitCursor(ScrnInfoPtr pScrn)
{
    R128InfoPtr   info = R128PTR(pScrn);

    if (!info->swCursor) {
	if (!xf86LoadSubModule(pScrn, "ramdac")) return FALSE;
    }
    return TRUE;
}

static Bool R128PreInitInt10(ScrnInfoPtr pScrn, xf86Int10InfoPtr *ppInt10)
{
/* VBE and int10 are only available on x86. */
#if defined(__i386__) || defined(__x86_64__)
    R128InfoPtr   info = R128PTR(pScrn);
    if (xf86LoadSubModule(pScrn, "int10")) {
	xf86DrvMsg(pScrn->scrnIndex,X_INFO,"initializing int10\n");
	*ppInt10 = xf86InitInt10(info->pEnt->index);
    }
#endif
    return TRUE;
}

#ifdef R128DRI
static Bool R128PreInitDRI(ScrnInfoPtr pScrn)
{
    R128InfoPtr   info = R128PTR(pScrn);

    info->agpMode        = R128_DEFAULT_AGP_MODE;
    info->agpSize        = R128_DEFAULT_AGP_SIZE;
    info->ringSize       = R128_DEFAULT_RING_SIZE;
    info->bufSize        = R128_DEFAULT_BUFFER_SIZE;
    info->agpTexSize     = R128_DEFAULT_AGP_TEX_SIZE;

    info->CCEusecTimeout = R128_DEFAULT_CCE_TIMEOUT;

    if (!info->IsPCI) {
	if (xf86GetOptValInteger(info->Options,
				 OPTION_AGP_MODE, &(info->agpMode))) {
	    if (info->agpMode < 1 || info->agpMode > R128_AGP_MAX_MODE) {
		xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
			   "Illegal AGP Mode: %d\n", info->agpMode);
		return FALSE;
	    }
	    xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
		       "Using AGP %dx mode\n", info->agpMode);
	}

	if (xf86GetOptValInteger(info->Options,
				 OPTION_AGP_SIZE, (int *)&(info->agpSize))) {
	    switch (info->agpSize) {
	    case 4:
	    case 8:
	    case 16:
	    case 32:
	    case 64:
	    case 128:
	    case 256:
		break;
	    default:
		xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
			   "Illegal AGP size: %d MB\n", info->agpSize);
		return FALSE;
	    }
	}

	if (xf86GetOptValInteger(info->Options,
				 OPTION_RING_SIZE, &(info->ringSize))) {
	    if (info->ringSize < 1 || info->ringSize >= (int)info->agpSize) {
		xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
			   "Illegal ring buffer size: %d MB\n",
			   info->ringSize);
		return FALSE;
	    }
	}

	if (xf86GetOptValInteger(info->Options,
				 OPTION_BUFFER_SIZE, &(info->bufSize))) {
	    if (info->bufSize < 1 || info->bufSize >= (int)info->agpSize) {
		xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
			   "Illegal vertex/indirect buffers size: %d MB\n",
			   info->bufSize);
		return FALSE;
	    }
	    if (info->bufSize > 2) {
		xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
			   "Illegal vertex/indirect buffers size: %d MB\n",
			   info->bufSize);
		xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
			   "Clamping vertex/indirect buffers size to 2 MB\n");
		info->bufSize = 2;
	    }
	}

	if (info->ringSize + info->bufSize + info->agpTexSize >
	    (int)info->agpSize) {
	    xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
		       "Buffers are too big for requested AGP space\n");
	    return FALSE;
	}

	info->agpTexSize = info->agpSize - (info->ringSize + info->bufSize);
    }

    if (xf86GetOptValInteger(info->Options, OPTION_USEC_TIMEOUT,
			     &(info->CCEusecTimeout))) {
	/* This option checked by the R128 DRM kernel module */
    }

    if (!xf86LoadSubModule(pScrn, "shadowfb")) {
	info->allowPageFlip = 0;
	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
		   "Couldn't load shadowfb module:\n");
    } else {
	info->allowPageFlip = xf86ReturnOptValBool(info->Options,
						   OPTION_PAGE_FLIP,
						   FALSE);
    }

    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Page flipping %sabled\n",
	       info->allowPageFlip ? "en" : "dis");

    return TRUE;
}
#endif

static Bool R128PreInitControllers(ScrnInfoPtr pScrn, xf86Int10InfoPtr pInt10)
{
    xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
    int found = 0;
    int i;

    if (!R128GetBIOSParameters(pScrn, pInt10))
        return FALSE;

    if (!R128GetPLLParameters(pScrn))
        return FALSE;

    if (!R128AllocateControllers(pScrn))
        return FALSE;

    if (!R128SetupConnectors(pScrn))
        return FALSE;

    for (i = 0; i < config->num_output; i++) {
        xf86OutputPtr output = config->output[i];

        output->status = (*output->funcs->detect) (output);
        if (output->status == XF86OutputStatusConnected)
            found++;
    }
    return !!found;
}

static void
r128UMSOption(ScrnInfoPtr pScrn)
{
    R128InfoPtr      info = R128PTR(pScrn);

    info->dac6bits = xf86ReturnOptValBool(info->Options,
                                            OPTION_DAC_6BIT, FALSE);

#ifdef __powerpc__
    if (xf86ReturnOptValBool(info->Options, OPTION_FBDEV, TRUE))
#else
    if (xf86ReturnOptValBool(info->Options, OPTION_FBDEV, FALSE))
#endif
    {
        info->FBDev = TRUE;
        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
                    "Using framebuffer device.\n");
    }

    /* By default, don't access VGA IOs on PowerPC or SPARC. */
#if defined(__powerpc__) || defined(__sparc__) || !defined(WITH_VGAHW)
    info->VGAAccess = FALSE;
#else
    info->VGAAccess = TRUE;
#endif

#ifdef WITH_VGAHW
    xf86GetOptValBool(info->Options, OPTION_VGA_ACCESS,
                        &info->VGAAccess);
    if (info->VGAAccess) {
       if (!xf86LoadSubModule(pScrn, "vgahw"))
           info->VGAAccess = FALSE;
        else {
            if (!vgaHWGetHWRec(pScrn))
               info->VGAAccess = FALSE;
       }

       if (!info->VGAAccess) {
           xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
                       "Loading VGA module failed, trying to "
                       "run without it.\n");
       }
    } else
           xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                       "VGAAccess option set to FALSE, VGA "
                       "module load skipped.\n");
    if (info->VGAAccess) {
        vgaHWSetStdFuncs(VGAHWPTR(pScrn));
        vgaHWGetIOBase(VGAHWPTR(pScrn));
    }
#else
    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                "VGAHW support not compiled, VGA "
                "module load skipped.\n");
#endif

    if (xf86ReturnOptValBool(info->Options,
                                OPTION_SHOW_CACHE, FALSE)) {
        info->showCache = TRUE;
        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
                    "ShowCache enabled.\n");
    }

    if (xf86ReturnOptValBool(info->Options,
                                OPTION_SW_CURSOR, FALSE)) {
        info->swCursor = TRUE;
        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
                    "Software cursor requested.\n");
    }

    if(xf86GetOptValInteger(info->Options,
                            OPTION_VIDEO_KEY, &info->videoKey)) {
        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
                    "Video key set to 0x%x.\n", info->videoKey);
    } else {
        info->videoKey = 0x1E;
    }

#ifdef R128DRI
    /* DMA for Xv */
    info->DMAForXv = xf86ReturnOptValBool(info->Options,
                                            OPTION_XV_DMA, FALSE);
    if (info->DMAForXv) {
        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
                   "Will try to use DMA for Xv image transfers.\n");
    }

    /* Force PCI Mode */
    info->IsPCI = xf86ReturnOptValBool(info->Options,
                                        OPTION_IS_PCI, FALSE);
    if (info->IsPCI) {
        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
                    "Forced into PCI only mode.\n");
    }

    if (xf86ReturnOptValBool(info->Options, OPTION_CCE_PIO, FALSE)) {
        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
                    "Forcing CCE into PIO mode.\n");
        info->CCEMode = R128_DEFAULT_CCE_PIO_MODE;
    } else {
        info->CCEMode = R128_DEFAULT_CCE_BM_MODE;
    }

    if (xf86ReturnOptValBool(info->Options, OPTION_NO_SECURITY, FALSE)) {
        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
                    "WARNING!!! CCE Security checks disabled!!!\n");
        info->CCESecure = FALSE;
    } else {
        info->CCESecure = TRUE;
    }


#endif
}

static void
r128AcquireOption(ScrnInfoPtr pScrn)
{
    R128InfoPtr      info = R128PTR(pScrn);
#ifdef USE_EXA
    char *optstr;
#endif

    if (xf86ReturnOptValBool(info->Options, OPTION_NOACCEL, FALSE)) {
        info->noAccel = TRUE;
    }

#ifdef USE_EXA
    if (!info->noAccel) {
        optstr = (char *) xf86GetOptValString(info->Options,
                                                OPTION_ACCELMETHOD);
        if (optstr) {
            xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                        "AccelMethod option found.\n");
            if (xf86NameCmp(optstr, "EXA") == 0) {
                info->useEXA = TRUE;
                xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                            "AccelMethod is set to EXA, turning "
                            "EXA on.\n");
            }
        }

#ifdef RENDER
        info->RenderAccel = xf86ReturnOptValBool(info->Options,
                                                    OPTION_RENDERACCEL,
                                                    TRUE);
        if (info->RenderAccel)
            xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                        "Acceleration of RENDER operations will be "
                        "enabled upon successful loading of DRI and "
                        "EXA.\n");
#endif
    }
#endif

    r128UMSOption(pScrn);
}

static Bool R128CRTCResize(ScrnInfoPtr pScrn, int width, int height)
{
    pScrn->virtualX = width;
    pScrn->virtualY = height;
    return TRUE;
}

static const xf86CrtcConfigFuncsRec R128CRTCResizeFuncs = {
    R128CRTCResize
};

static Bool R128LegacyMS(ScrnInfoPtr pScrn)
{
    R128InfoPtr      info = R128PTR(pScrn);
    xf86Int10InfoPtr pInt10 = NULL;
    Bool ret = FALSE;

    if (info->FBDev) {
        /* check for linux framebuffer device */
        if (!xf86LoadSubModule(pScrn, "fbdevhw")) goto exit;
        if (!fbdevHWInit(pScrn, info->PciInfo, NULL)) goto exit;
        pScrn->SwitchMode    = fbdevHWSwitchModeWeak();
        pScrn->AdjustFrame   = fbdevHWAdjustFrameWeak();
        pScrn->ValidMode     = fbdevHWValidModeWeak();
    } else {
        if (!R128PreInitInt10(pScrn, &pInt10)) goto exit;
    }

    if (!R128PreInitConfig(pScrn)) goto freeInt10;

    xf86CrtcSetSizeRange(pScrn, 320, 200, 4096, 4096);

    if (!R128PreInitCursor(pScrn)) goto freeInt10;

    /* Don't fail on this one */
    info->DDC = R128PreInitDDC(pScrn, pInt10);

    if (!R128PreInitControllers(pScrn, pInt10)) goto freeInt10;

#ifdef R128DRI
    if (!R128PreInitDRI(pScrn)) goto freeInt10;
#endif

    ret = TRUE;
freeInt10:
    /* Free int10 info */
/* VBE and int10 are only available on x86. */
#if defined(__i386__) || defined(__x86_64__)
    if (pInt10) {
        xf86FreeInt10(pInt10);
    }
#endif

exit:
    return ret;
}

static void
R128PreInitAccel(ScrnInfoPtr pScrn)
{
    R128InfoPtr      info = R128PTR(pScrn);
#ifdef USE_EXA
    int errmaj, errmin;
#endif

    if (!info->noAccel) {
        if (info->useEXA) {
#ifdef USE_EXA
            info->exaReq.majorversion = EXA_VERSION_MAJOR;
            info->exaReq.minorversion = EXA_VERSION_MINOR;

            xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                        "Loading EXA module...\n");
            if (LoadSubModule(pScrn->module, "exa", NULL, NULL, NULL,
                                &info->exaReq, &errmaj, &errmin)) {
                xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                            "Loading EXA module.\n");
            } else {
                LoaderErrorMsg(NULL, "exa", errmaj, errmin);
            }
#endif
        }

        if ((!info->useEXA) ||
            ((info->useEXA) && (!info->accelOn))) {
        }
    }
}

/* R128PreInit is called once at server startup. */
Bool R128PreInit(ScrnInfoPtr pScrn, int flags)
{
    R128InfoPtr      info;
#ifdef WSDISPLAYIO_GET_BUSID
    struct wsdisplayio_bus_id bid;
#endif

    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                        "%s\n", __func__));

    if (flags & PROBE_DETECT) {
        return TRUE;
    }

    pScrn->monitor = pScrn->confScreen->monitor;

    if (!R128PreInitVisual(pScrn)) {
        return FALSE;
    }

    if (!R128PreInitGamma(pScrn)) {
        return FALSE;
    }

    if (pScrn->numEntities != 1) return FALSE;

    if (!R128GetRec(pScrn)) return FALSE;

    info                = R128PTR(pScrn);
    info->SwitchingMode = FALSE;
    info->MMIO          = NULL;

    info->pEnt          = xf86GetEntityInfo(pScrn->entityList[0]);
    if (info->pEnt->location.type != BUS_PCI) goto fail;

    info->PciInfo       = xf86GetPciInfoForEntity(info->pEnt->index);

    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
	       "PCI bus %d card %d func %d\n",
	       PCI_DEV_BUS(info->PciInfo),
	       PCI_DEV_DEV(info->PciInfo),
	       PCI_DEV_FUNC(info->PciInfo));

#ifdef WSDISPLAYIO_GET_BUSID
    /* now check if this is the console */
    info->HaveWSDisplay = FALSE;
    info->HaveBacklightControl = FALSE;
    if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_GET_BUSID, &bid) != -1) {
    	if ((bid.bus_type == WSDISPLAYIO_BUS_PCI) &&
    	    (bid.ubus.pci.bus == PCI_DEV_BUS(info->PciInfo)) &&
    	    (bid.ubus.pci.device == PCI_DEV_DEV(info->PciInfo)) &&
    	    (bid.ubus.pci.function == PCI_DEV_FUNC(info->PciInfo))) {
    	    	struct wsdisplay_param p;
    	    	info->HaveWSDisplay = TRUE;

#ifdef WSDISPLAYIO_PARAM_BACKLIGHT
    	    	/* now see if we have hacklight control */
    	    	p.param = WSDISPLAYIO_PARAM_BACKLIGHT;
		if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_GETPARAM, &p) != -1) {
		    info->HaveBacklightControl = TRUE;
		}
#endif
    	}
    }
#endif

    info->fifo_slots  = 0;
    info->pix24bpp    = xf86GetBppFromDepth(pScrn, pScrn->depth);
    info->CurrentLayout.bitsPerPixel = pScrn->bitsPerPixel;
    info->CurrentLayout.depth        = pScrn->depth;
    info->CurrentLayout.pixel_bytes  = pScrn->bitsPerPixel / 8;
    info->CurrentLayout.pixel_code   = (pScrn->bitsPerPixel != 16
                                       ? pScrn->bitsPerPixel
                                       : pScrn->depth);

    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
           "Pixel depth = %d bits stored in %d byte%s (%d bpp pixmaps)\n",
           pScrn->depth,
           info->CurrentLayout.pixel_bytes,
           info->CurrentLayout.pixel_bytes > 1 ? "s" : "",
           info->pix24bpp);

				/* We can't do this until we have a
				   pScrn->display. */
    xf86CollectOptions(pScrn, NULL);
    if (!(info->Options = malloc(sizeof(R128Options))))    goto fail;
    memcpy(info->Options, R128Options, sizeof(R128Options));
    xf86ProcessOptions(pScrn->scrnIndex, pScrn->options, info->Options);

    info->noAccel = FALSE;
    info->accelOn = FALSE;

    info->useEXA = FALSE;
#ifdef USE_EXA
    info->useEXA = TRUE;
#endif

    info->swCursor = FALSE;

    r128AcquireOption(pScrn);

    if (!R128PreInitWeight(pScrn))    goto fail;

    /* Allocate an xf86CrtcConfig */
    xf86CrtcConfigInit(pScrn, &R128CRTCResizeFuncs);

    R128LegacyMS(pScrn);

    if (!xf86InitialConfiguration(pScrn, TRUE)) {
        xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No valid modes.\n");
        goto fail;
    }
    pScrn->displayWidth = (pScrn->virtualX + 63) & ~63;

    /* Set display resolution */
    xf86SetDpi(pScrn, 0, 0);

    /* Get ScreenInit function */
    if (!xf86LoadSubModule(pScrn, "fb")) return FALSE;

    R128PreInitAccel(pScrn);

    info->CurrentLayout.displayWidth = pScrn->displayWidth;

    if (!xf86RandR12PreInit(pScrn)) {
        xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "RandR initialization failure\n");
        goto fail;
    }

    if (pScrn->modes == NULL) {
        xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No modes.\n");
        goto fail;
    }

				/* Free the video bios (if applicable) */
    if (info->VBIOS) {
	free(info->VBIOS);
	info->VBIOS = NULL;
    }

    if (info->MMIO) R128UnmapMMIO(pScrn);
    info->MMIO = NULL;

    return TRUE;

  fail:
				/* Pre-init failed. */

				/* Free the video bios (if applicable) */
    if (info->VBIOS) {
	free(info->VBIOS);
	info->VBIOS = NULL;
    }

#ifdef WITH_VGAHW
    if (info->VGAAccess)
           vgaHWFreeHWRec(pScrn);
#endif

    if (info->MMIO) R128UnmapMMIO(pScrn);
    info->MMIO = NULL;

    R128FreeRec(pScrn);
    return FALSE;
}

/* Load a palette. */
static void R128LoadPalette(ScrnInfoPtr pScrn, int numColors,
			    int *indices, LOCO *colors, VisualPtr pVisual)
{
    R128InfoPtr   info      = R128PTR(pScrn);
    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
    int i, j;
    int c, index;
    uint16_t lut_r[256], lut_g[256], lut_b[256];

    for (c = 0; c < xf86_config->num_crtc; c++) {
        xf86CrtcPtr crtc = xf86_config->crtc[c];
        R128CrtcPrivatePtr r128_crtc = crtc->driver_private;

        for (i = 0 ; i < 256; i++) {
            lut_r[i] = r128_crtc->lut_r[i] << 8;
            lut_g[i] = r128_crtc->lut_g[i] << 8;
            lut_b[i] = r128_crtc->lut_b[i] << 8;
        }

        switch (info->CurrentLayout.depth) {
        case 15:
            for (i = 0; i < numColors; i++) {
                index = indices[i];
                for (j = 0; j < 8; j++) {
                    lut_r[index * 8 + j] = colors[index].red << 8;
                    lut_g[index * 8 + j] = colors[index].green << 8;
                    lut_b[index * 8 + j] = colors[index].blue << 8;
                }
            }
        case 16:
            for (i = 0; i < numColors; i++) {
                index = indices[i];

                /* XXX: The old version of R128LoadPalette did not do this and
                 * the old version of RADEONLoadPalette has a comment asking why.
                 */
                if (i <= 31) {
                    for (j = 0; j < 8; j++) {
                        lut_r[index * 8 + j] = colors[index].red << 8;
                        lut_b[index * 8 + j] = colors[index].blue << 8;
                    }
                }

                for (j = 0; j < 4; j++) {
                    lut_g[index * 4 + j] = colors[index].green << 8;
                }
            }
        default:
            for (i = 0; i < numColors; i++) {
                index = indices[i];
                lut_r[index] = colors[index].red << 8;
                lut_g[index] = colors[index].green << 8;
                lut_b[index] = colors[index].blue << 8;
            }
            break;
        }

        /* Make the change through RandR */
        if (crtc->randr_crtc)
            RRCrtcGammaSet(crtc->randr_crtc, lut_r, lut_g, lut_b);
        else
            crtc->funcs->gamma_set(crtc, lut_r, lut_g, lut_b, 256);
    }
}

static void
R128BlockHandler(ScreenPtr pScreen, void *pTimeout)
{
    ScrnInfoPtr pScrn   = xf86ScreenToScrn(pScreen);
    R128InfoPtr info    = R128PTR(pScrn);

#ifdef R128DRI
    if (info->directRenderingEnabled)
        FLUSH_RING();
#endif

    pScreen->BlockHandler = info->BlockHandler;
    (*pScreen->BlockHandler) (pScreen, pTimeout);
    pScreen->BlockHandler = R128BlockHandler;

    if(info->VideoTimerCallback) {
        (*info->VideoTimerCallback)(pScrn, currentTime.milliseconds);
    }
}

/* Called at the start of each server generation. */
Bool R128ScreenInit(ScreenPtr pScreen, int argc, char **argv)
{
    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
    R128InfoPtr info   = R128PTR(pScrn);
    BoxRec      MemBox;
    int width_bytes = (pScrn->displayWidth *
                        info->CurrentLayout.pixel_bytes);
    int scanlines;
    int total = info->FbMapSize;
    FBAreaPtr fbarea = NULL;
#ifdef R128DRI
    int cpp = info->CurrentLayout.pixel_bytes;
    int x1 = 0, x2 = 0, y1 = 0, y2 = 0;
#ifdef USE_EXA
    ExaOffscreenArea*     osArea = NULL;
#endif /* USE_EXA */
#endif /* R128DRI */

    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                        "%s %lx %lx\n",
                        __func__,
                        pScrn->memPhysBase, pScrn->fbOffset));

#ifdef R128DRI
				/* Turn off the CCE for now. */
    info->CCEInUse     = FALSE;
    info->indirectBuffer = NULL;
#endif

    if (!R128MapMem(pScrn)) return FALSE;
    pScrn->fbOffset    = 0;
    //if(info->IsSecondary) pScrn->fbOffset = pScrn->videoRam * 1024;
#ifdef R128DRI
    info->fbX          = 0;
    info->fbY          = 0;
    info->frontOffset  = 0;
    info->frontPitch   = pScrn->displayWidth;
#endif

    info->PaletteSavedOnVT = FALSE;

    R128Save(pScrn);

				/* Visual setup */
    miClearVisualTypes();
    if (!miSetVisualTypes(pScrn->depth,
			  miGetDefaultVisualMask(pScrn->depth),
			  pScrn->rgbBits,
			  pScrn->defaultVisual)) return FALSE;
    miSetPixmapDepths ();

#ifdef R128DRI
				/* Setup DRI after visuals have been
				   established, but before fbScreenInit is
				   called. */
    {
	/* FIXME: When we move to dynamic allocation of back and depth
	   buffers, we will want to revisit the following check for 3
	   times the virtual size of the screen below. */
	int maxy        = info->FbMapSize / width_bytes;

        if (info->noAccel) {
	    xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
		       "Acceleration disabled, not initializing the DRI\n");
	    info->directRenderingEnabled = FALSE;
	} else if (maxy <= pScrn->virtualY * 3) {
	    xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
		       "Static buffer allocation failed -- "
		       "need at least %d kB video memory\n",
		       (pScrn->displayWidth * pScrn->virtualY *
			info->CurrentLayout.pixel_bytes * 3 + 1023) / 1024);
	    info->directRenderingEnabled = FALSE;
	} else {
            info->directRenderingEnabled = R128DRIScreenInit(pScreen);
	}
    }
#endif

    if (!fbScreenInit (pScreen, info->FB,
		       pScrn->virtualX, pScrn->virtualY,
		       pScrn->xDpi, pScrn->yDpi, pScrn->displayWidth,
		       pScrn->bitsPerPixel))
	return FALSE;

    xf86SetBlackWhitePixels(pScreen);

    if (pScrn->bitsPerPixel > 8) {
	VisualPtr visual;

	visual = pScreen->visuals + pScreen->numVisuals;
	while (--visual >= pScreen->visuals) {
	    if ((visual->class | DynamicClass) == DirectColor) {
		visual->offsetRed   = pScrn->offset.red;
		visual->offsetGreen = pScrn->offset.green;
		visual->offsetBlue  = pScrn->offset.blue;
		visual->redMask     = pScrn->mask.red;
		visual->greenMask   = pScrn->mask.green;
		visual->blueMask    = pScrn->mask.blue;
	    }
	}
    }

    /* must be after RGB order fixed */
    fbPictureInit (pScreen, 0, 0);

				/* Memory manager setup */
#ifdef R128DRI
    if (info->directRenderingEnabled) {
	int bufferSize = pScrn->virtualY * width_bytes;
	int l;

	switch (info->CCEMode) {
	case R128_DEFAULT_CCE_PIO_MODE:
	    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "CCE in PIO mode\n");
	    break;
	case R128_DEFAULT_CCE_BM_MODE:
	    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "CCE in BM mode\n");
	    break;
	default:
	    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "CCE in UNKNOWN mode\n");
	    break;
	}

	xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		   "Using %d MB AGP aperture\n", info->agpSize);
	xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		   "Using %d MB for the ring buffer\n", info->ringSize);
	xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		   "Using %d MB for vertex/indirect buffers\n", info->bufSize);
	xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		   "Using %d MB for AGP textures\n", info->agpTexSize);

	/* Try for front, back, depth, and two framebuffers worth of
	 * pixmap cache.  Should be enough for a fullscreen background
	 * image plus some leftovers.
	 */
	info->textureSize = info->FbMapSize - 5 * bufferSize;

	/* If that gives us less than half the available memory, let's
	 * be greedy and grab some more.  Sorry, I care more about 3D
	 * performance than playing nicely, and you'll get around a full
	 * framebuffer's worth of pixmap cache anyway.
	 */
	if (info->textureSize < (int)info->FbMapSize / 2) {
	    info->textureSize = info->FbMapSize - 4 * bufferSize;
	}

	if (info->textureSize > 0) {
	    l = R128MinBits((info->textureSize-1) / R128_NR_TEX_REGIONS);
	    if (l < R128_LOG_TEX_GRANULARITY) l = R128_LOG_TEX_GRANULARITY;

	    /* Round the texture size up to the nearest whole number of
	     * texture regions.  Again, be greedy about this, don't
	     * round down.
	     */
	    info->log2TexGran = l;
	    info->textureSize = (info->textureSize >> l) << l;
	} else {
	    info->textureSize = 0;
	}

	/* Set a minimum usable local texture heap size.  This will fit
	 * two 256x256x32bpp textures.
	 */
	if (info->textureSize < 512 * 1024) {
	    info->textureOffset = 0;
	    info->textureSize = 0;
	}

        total = info->FbMapSize - info->textureSize;
    }
#endif /* R128DRI */

    scanlines = total / width_bytes;
    if (scanlines > 8191) scanlines = 8191;

#ifdef R128DRI
    if (info->directRenderingEnabled)
        /*
         * Recalculate the texture offset and size to accommodate any
         * rounding to a whole number of scanlines.
         */
        info->textureOffset = scanlines * width_bytes;
#endif /* R128DRI */

    MemBox.x1 = 0;
    MemBox.y1 = 0;
    MemBox.x2 = pScrn->displayWidth;
    MemBox.y2 = scanlines;

    if (!info->useEXA) {
        if (!xf86InitFBManager(pScreen, &MemBox)) {
            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
                       "Memory manager initialization to (%d,%d) (%d,%d) failed\n",
                       MemBox.x1, MemBox.y1, MemBox.x2, MemBox.y2);
            return FALSE;
        } else {
            int width, height;

            xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                       "Memory manager initialized to (%d,%d) (%d,%d)\n",
                       MemBox.x1, MemBox.y1, MemBox.x2, MemBox.y2);
            if ((fbarea = xf86AllocateOffscreenArea(pScreen,
                                                    pScrn->displayWidth,
                                                    2, 0, NULL, NULL, NULL))) {
                xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                           "Reserved area from (%d,%d) to (%d,%d)\n",
                           fbarea->box.x1, fbarea->box.y1,
                           fbarea->box.x2, fbarea->box.y2);
            } else {
                xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Unable to reserve area\n");
            }
            if (xf86QueryLargestOffscreenArea(pScreen, &width,
                                              &height, 0, 0, 0)) {
                xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                           "Largest offscreen area available: %d x %d\n",
                           width, height);
            }

            if (!info->noAccel) {
                xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                           "Acceleration disabled.\n");
            }
        }
    }
#ifdef USE_EXA
    else {
        xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                    "Filling in EXA memory info\n");


        /*
         * Don't give EXA the true full memory size, because
         * the textureSize sized chunk on the end is handled
         * by DRI.
         */
        if (R128EXAInit(pScreen, total)) {
            info->accelOn = TRUE;
            xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                        "EXA Acceleration enabled.\n");
        } else {
            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
                        "EXA Acceleration initialization "
                        "failed.\n");
            xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                        "Acceleration disabled.\n");
        }
    }
#endif

#ifdef R128DRI
    if (info->directRenderingEnabled) {
				/* Allocate the shared back buffer */
	if(!info->useEXA) {
	    fbarea = xf86AllocateOffscreenArea(pScreen,
					       pScrn->virtualX,
					       pScrn->virtualY,
					       32, NULL, NULL, NULL);

	    if (fbarea) {
		x1 = fbarea->box.x1;
		x2 = fbarea->box.x2;
		y1 = fbarea->box.y1;
		y2 = fbarea->box.y2;
	    }
	}
#ifdef USE_EXA
	else {
	    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		       "Actually trying an EXA allocation...\n");
	    osArea = exaOffscreenAlloc(pScreen,
				       pScrn->virtualY * width_bytes,
				       32, TRUE, NULL, NULL);

	    if (osArea) {
		x1 = osArea->offset % width_bytes;
		x2 = (osArea->offset + osArea->size) % width_bytes;
		y1 = osArea->offset / width_bytes;
		y2 = (osArea->offset + osArea->size) / width_bytes;

		xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Went swimmingly...\n");
	    }
	}
#endif

	if ((!info->useEXA && fbarea) || (info->useEXA && osArea)) {
	    /* info->backOffset = y1 * width_bytes + x1 * cpp; */
	    info->backOffset = R128_ALIGN(y1 * width_bytes + x1 * cpp, 16);
	    info->backX = info->backOffset % width_bytes;
	    info->backY = info->backOffset / width_bytes;
	    info->backPitch = pScrn->displayWidth;

	    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		       "Reserved back buffer from (%d,%d) to (%d,%d) offset: %x\n",
		       x1, y1,
		       x2, y2, info->backOffset);
	} else {
	    xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Unable to reserve back buffer\n");
	    info->backX = -1;
	    info->backY = -1;
	    info->backOffset = -1;
	    info->backPitch = -1;
	}

				/* Allocate the shared depth buffer */
	if(!info->useEXA) {
	    fbarea = xf86AllocateOffscreenArea(pScreen,
					       pScrn->virtualX,
					       pScrn->virtualY + 1,
					       32, NULL, NULL, NULL);
	    if (fbarea) {
		x1 = fbarea->box.x1;
		x2 = fbarea->box.x2;
		y1 = fbarea->box.y1;
		y2 = fbarea->box.y2;
	    }
	}
#ifdef USE_EXA
	else {
	    osArea = exaOffscreenAlloc(pScreen,
				       (pScrn->virtualY + 1) * width_bytes,
				       32, TRUE, NULL, NULL);

	    if (osArea) {
		x1 = osArea->offset % width_bytes;
		x2 = (osArea->offset + osArea->size) % width_bytes;
		y1 = osArea->offset / width_bytes;
		y2 = (osArea->offset + osArea->size) / width_bytes;
	    }
	}
#endif

	if ((!info->useEXA && fbarea) || (info->useEXA && osArea)) {
	    /* info->depthOffset = y1 * width_bytes + x1 * cpp; */
	    info->depthOffset = R128_ALIGN(y1 * width_bytes + x1 * cpp, 16);
	    info->depthX = info->depthOffset % width_bytes;
	    info->depthY = info->depthOffset / width_bytes;
	    info->depthPitch = pScrn->displayWidth;
	    info->spanOffset = (y2 - 1) * width_bytes + x1 * cpp;

	    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		       "Reserved depth buffer from (%d,%d) to (%d,%d) offset: %x\n",
		       x1, y1,
		       x2, y2, info->depthOffset);

	    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		       "Reserved depth span from (%d,%d) offset 0x%x\n",
		       x1, y2 - 1, info->spanOffset);
	} else {
	    xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Unable to reserve depth buffer\n");
	    info->depthX = -1;
	    info->depthY = -1;
	    info->depthOffset = -1;
	    info->depthPitch = -1;
	    info->spanOffset = -1;
	}

	xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		   "Reserved %d kb for textures at offset 0x%x\n",
		   info->textureSize/1024, info->textureOffset);
    }
#endif /* R128DRI */

    pScrn->vtSema = TRUE;
    /* xf86CrtcRotate accesses pScrn->pScreen */
    pScrn->pScreen = pScreen;

    if (info->FBDev) {
	if (!fbdevHWModeInit(pScrn, pScrn->currentMode)) return FALSE;
    } else {
	if (!xf86SetDesiredModes(pScrn)) return FALSE;
    }

    R128SaveScreen(pScreen, SCREEN_SAVER_ON);
    //pScrn->AdjustFrame(pScrn, pScrn->frameX0, pScrn->frameY0);

				/* DGA setup */
#ifdef XFreeXDGA
    xf86DiDGAInit(pScreen, info->LinearAddr + pScrn->fbOffset);
#endif

				/* Backing store setup */
    xf86SetBackingStore(pScreen);

				/* Set Silken Mouse */
    xf86SetSilkenMouse(pScreen);

				/* Cursor setup */
    miDCInitialize(pScreen, xf86GetPointerScreenFuncs());

				/* Hardware cursor setup */
    if (!info->swCursor) {
	if (R128CursorInit(pScreen)) {
	    int width, height;

	    if (xf86QueryLargestOffscreenArea(pScreen, &width, &height,
					      0, 0, 0)) {
		xf86DrvMsg(pScrn->scrnIndex, X_INFO,
			   "Largest offscreen area available: %d x %d\n",
			   width, height);
	    }
	} else {
	    xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
		       "Hardware cursor initialization failed\n");
	    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Using software cursor\n");
	}
    } else {
	xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Using software cursor\n");
    }

    /* DPMS setup - FIXME: also for mirror mode in non-fbdev case? - Michel */
    if (info->FBDev)
	xf86DPMSInit(pScreen, fbdevHWDPMSSetWeak(), 0);
    else
        xf86DPMSInit(pScreen, xf86DPMSSet, 0);

    R128InitVideo(pScreen);

				/* Provide SaveScreen */
    pScreen->SaveScreen  = R128SaveScreen;

				/* Wrap CloseScreen */
    info->CloseScreen    = pScreen->CloseScreen;
    pScreen->CloseScreen = R128CloseScreen;

				/* Note unused options */
    if (serverGeneration == 1)
	xf86ShowUnusedOptions(pScrn->scrnIndex, pScrn->options);

#ifdef R128DRI
				/* DRI finalization */
    if (info->directRenderingEnabled) {
				/* Now that mi, fb, drm and others have
				   done their thing, complete the DRI
				   setup. */
	info->directRenderingEnabled = R128DRIFinishScreenInit(pScreen);
    }
    if (info->directRenderingEnabled) {
	xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Direct rendering enabled\n");
    } else {
	xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
		   "Direct rendering disabled\n");
    }
#endif

    info->BlockHandler = pScreen->BlockHandler;
    pScreen->BlockHandler = R128BlockHandler;

    if (!xf86CrtcScreenInit(pScreen)) return FALSE;

				/* Colormap setup */
    if (!miCreateDefColormap(pScreen)) return FALSE;
    if (!xf86HandleColormaps(pScreen, 256, info->dac6bits ? 6 : 8,
			     (info->FBDev ? fbdevHWLoadPaletteWeak() :
			     R128LoadPalette), NULL,
			     CMAP_PALETTED_TRUECOLOR
			     | CMAP_RELOAD_ON_MODE_SWITCH
#if 0 /* This option messes up text mode! (eich@suse.de) */
			     | CMAP_LOAD_EVEN_IF_OFFSCREEN
#endif
			     )) return FALSE;

    return TRUE;
}

/* Write common registers (initialized to 0). */
void R128RestoreCommonRegisters(ScrnInfoPtr pScrn, R128SavePtr restore)
{
    R128InfoPtr   info      = R128PTR(pScrn);
    unsigned char *R128MMIO = info->MMIO;

    OUTREG(R128_FP_GEN_CNTL, restore->fp_gen_cntl | R128_FP_BLANK_DIS);

    OUTREG(R128_OVR_CLR,              restore->ovr_clr);
    OUTREG(R128_OVR_WID_LEFT_RIGHT,   restore->ovr_wid_left_right);
    OUTREG(R128_OVR_WID_TOP_BOTTOM,   restore->ovr_wid_top_bottom);
    OUTREG(R128_OV0_SCALE_CNTL,       restore->ov0_scale_cntl);
    OUTREG(R128_MPP_TB_CONFIG,        restore->mpp_tb_config );
    OUTREG(R128_MPP_GP_CONFIG,        restore->mpp_gp_config );
    OUTREG(R128_SUBPIC_CNTL,          restore->subpic_cntl);
    OUTREG(R128_VIPH_CONTROL,         restore->viph_control);
    OUTREG(R128_I2C_CNTL_1,           restore->i2c_cntl_1);
    OUTREG(R128_GEN_INT_CNTL,         restore->gen_int_cntl);
    OUTREG(R128_CAP0_TRIG_CNTL,       restore->cap0_trig_cntl);
    OUTREG(R128_CAP1_TRIG_CNTL,       restore->cap1_trig_cntl);
    OUTREG(R128_BUS_CNTL,             restore->bus_cntl);
    OUTREG(R128_CONFIG_CNTL,          restore->config_cntl);
}

/* Write RMX registers */
void R128RestoreRMXRegisters(ScrnInfoPtr pScrn, R128SavePtr restore)
{
    R128InfoPtr   info      = R128PTR(pScrn);
    unsigned char *R128MMIO = info->MMIO;

    OUTREG(R128_FP_HORZ_STRETCH,      restore->fp_horz_stretch);
    OUTREG(R128_FP_VERT_STRETCH,      restore->fp_vert_stretch);
    OUTREG(R128_FP_CRTC_H_TOTAL_DISP, restore->fp_crtc_h_total_disp);
    OUTREG(R128_FP_CRTC_V_TOTAL_DISP, restore->fp_crtc_v_total_disp);
    OUTREG(R128_FP_H_SYNC_STRT_WID,   restore->fp_h_sync_strt_wid);
    OUTREG(R128_FP_V_SYNC_STRT_WID,   restore->fp_v_sync_strt_wid);
}

/* Write flat panel registers */
void R128RestoreFPRegisters(ScrnInfoPtr pScrn, R128SavePtr restore)
{
    R128InfoPtr   info      = R128PTR(pScrn);
    unsigned char *R128MMIO = info->MMIO;

    OUTREG(R128_TMDS_CRC,              restore->tmds_crc);
    OUTREG(R128_TMDS_TRANSMITTER_CNTL, restore->tmds_transmitter_cntl);
    OUTREG(R128_FP_PANEL_CNTL,         restore->fp_panel_cntl);
    OUTREG(R128_FP_GEN_CNTL, restore->fp_gen_cntl & ~(uint32_t)R128_FP_BLANK_DIS);
}

/* Write LVDS registers */
void R128RestoreLVDSRegisters(ScrnInfoPtr pScrn, R128SavePtr restore)
{
    R128InfoPtr   info      = R128PTR(pScrn);
    R128EntPtr    pR128Ent  = R128EntPriv(pScrn);
    unsigned char *R128MMIO = info->MMIO;
    uint32_t      tmp;

    xf86OutputPtr output = R128FirstOutput(pR128Ent->pCrtc[0]);
    R128OutputPrivatePtr r128_output = output->driver_private;

    tmp = INREG(R128_LVDS_GEN_CNTL);
    if ((tmp & (R128_LVDS_ON | R128_LVDS_BLON)) ==
	(restore->lvds_gen_cntl & (R128_LVDS_ON | R128_LVDS_BLON))) {
	OUTREG(R128_LVDS_GEN_CNTL, restore->lvds_gen_cntl);
    } else {
	if (restore->lvds_gen_cntl & (R128_LVDS_ON | R128_LVDS_BLON)) {
	    OUTREG(R128_LVDS_GEN_CNTL,
		   restore->lvds_gen_cntl & (uint32_t)~R128_LVDS_BLON);
	    usleep(r128_output->PanelPwrDly * 1000);
	    OUTREG(R128_LVDS_GEN_CNTL, restore->lvds_gen_cntl);
	} else {
	    OUTREG(R128_LVDS_GEN_CNTL, restore->lvds_gen_cntl | R128_LVDS_BLON);
	    usleep(r128_output->PanelPwrDly * 1000);
	    OUTREG(R128_LVDS_GEN_CNTL, restore->lvds_gen_cntl);
	}
    }
}

/* Write DDA registers. */
void R128RestoreDDARegisters(ScrnInfoPtr pScrn, R128SavePtr restore)
{
    R128InfoPtr   info      = R128PTR(pScrn);
    unsigned char *R128MMIO = info->MMIO;

    OUTREG(R128_DDA_CONFIG, restore->dda_config);
    OUTREG(R128_DDA_ON_OFF, restore->dda_on_off);
}

/* Write DDA registers. */
void R128RestoreDDA2Registers(ScrnInfoPtr pScrn, R128SavePtr restore)
{
    R128InfoPtr   info      = R128PTR(pScrn);
    unsigned char *R128MMIO = info->MMIO;

    OUTREG(R128_DDA2_CONFIG, restore->dda2_config);
    OUTREG(R128_DDA2_ON_OFF, restore->dda2_on_off);
}

/* Read common registers. */
static void R128SaveCommonRegisters(ScrnInfoPtr pScrn, R128SavePtr save)
{
    R128InfoPtr   info      = R128PTR(pScrn);
    unsigned char *R128MMIO = info->MMIO;

    save->ovr_clr            = INREG(R128_OVR_CLR);
    save->ovr_wid_left_right = INREG(R128_OVR_WID_LEFT_RIGHT);
    save->ovr_wid_top_bottom = INREG(R128_OVR_WID_TOP_BOTTOM);
    save->ov0_scale_cntl     = INREG(R128_OV0_SCALE_CNTL);
    save->mpp_tb_config      = INREG(R128_MPP_TB_CONFIG);
    save->mpp_gp_config      = INREG(R128_MPP_GP_CONFIG);
    save->subpic_cntl        = INREG(R128_SUBPIC_CNTL);
    save->viph_control       = INREG(R128_VIPH_CONTROL);
    save->i2c_cntl_1         = INREG(R128_I2C_CNTL_1);
    save->gen_int_cntl       = INREG(R128_GEN_INT_CNTL);
    save->cap0_trig_cntl     = INREG(R128_CAP0_TRIG_CNTL);
    save->cap1_trig_cntl     = INREG(R128_CAP1_TRIG_CNTL);
    save->bus_cntl           = INREG(R128_BUS_CNTL);
    save->config_cntl        = INREG(R128_CONFIG_CNTL);
}

/* Read CRTC registers. */
static void R128SaveCrtcRegisters(ScrnInfoPtr pScrn, R128SavePtr save)
{
    R128InfoPtr   info      = R128PTR(pScrn);
    unsigned char *R128MMIO = info->MMIO;

    save->crtc_gen_cntl        = INREG(R128_CRTC_GEN_CNTL);
    save->crtc_ext_cntl        = INREG(R128_CRTC_EXT_CNTL);
    save->dac_cntl             = INREG(R128_DAC_CNTL);
    save->crtc_h_total_disp    = INREG(R128_CRTC_H_TOTAL_DISP);
    save->crtc_h_sync_strt_wid = INREG(R128_CRTC_H_SYNC_STRT_WID);
    save->crtc_v_total_disp    = INREG(R128_CRTC_V_TOTAL_DISP);
    save->crtc_v_sync_strt_wid = INREG(R128_CRTC_V_SYNC_STRT_WID);
    save->crtc_offset          = INREG(R128_CRTC_OFFSET);
    save->crtc_offset_cntl     = INREG(R128_CRTC_OFFSET_CNTL);
    save->crtc_pitch           = INREG(R128_CRTC_PITCH);
}

/* Read flat panel registers */
static void R128SaveFPRegisters(ScrnInfoPtr pScrn, R128SavePtr save)
{
    R128InfoPtr   info      = R128PTR(pScrn);
    unsigned char *R128MMIO = info->MMIO;

    save->fp_crtc_h_total_disp = INREG(R128_FP_CRTC_H_TOTAL_DISP);
    save->fp_crtc_v_total_disp = INREG(R128_FP_CRTC_V_TOTAL_DISP);
    save->fp_gen_cntl          = INREG(R128_FP_GEN_CNTL);
    save->fp_h_sync_strt_wid   = INREG(R128_FP_H_SYNC_STRT_WID);
    save->fp_horz_stretch      = INREG(R128_FP_HORZ_STRETCH);
    save->fp_panel_cntl        = INREG(R128_FP_PANEL_CNTL);
    save->fp_v_sync_strt_wid   = INREG(R128_FP_V_SYNC_STRT_WID);
    save->fp_vert_stretch      = INREG(R128_FP_VERT_STRETCH);
    save->lvds_gen_cntl        = INREG(R128_LVDS_GEN_CNTL);
    save->tmds_crc             = INREG(R128_TMDS_CRC);
    save->tmds_transmitter_cntl = INREG(R128_TMDS_TRANSMITTER_CNTL);
}

/* Read CRTC2 registers. */
static void R128SaveCrtc2Registers(ScrnInfoPtr pScrn, R128SavePtr save)
{
    R128InfoPtr info        = R128PTR(pScrn);
    unsigned char *R128MMIO = info->MMIO;

    save->crtc2_gen_cntl        = INREG(R128_CRTC2_GEN_CNTL);
    save->crtc2_h_total_disp    = INREG(R128_CRTC2_H_TOTAL_DISP);
    save->crtc2_h_sync_strt_wid = INREG(R128_CRTC2_H_SYNC_STRT_WID);
    save->crtc2_v_total_disp    = INREG(R128_CRTC2_V_TOTAL_DISP);
    save->crtc2_v_sync_strt_wid = INREG(R128_CRTC2_V_SYNC_STRT_WID);
    save->crtc2_offset          = INREG(R128_CRTC2_OFFSET);
    save->crtc2_offset_cntl     = INREG(R128_CRTC2_OFFSET_CNTL);
    save->crtc2_pitch           = INREG(R128_CRTC2_PITCH);
}

/* Read PLL registers. */
static void R128SavePLLRegisters(ScrnInfoPtr pScrn, R128SavePtr save)
{
    save->ppll_ref_div         = INPLL(pScrn, R128_PPLL_REF_DIV);
    save->ppll_div_3           = INPLL(pScrn, R128_PPLL_DIV_3);
    save->ppll_div_0           = INPLL(pScrn, R128_PPLL_DIV_0);
    save->htotal_cntl          = INPLL(pScrn, R128_HTOTAL_CNTL);

    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                        "Read: 0x%08x 0x%08x 0x%08x\n",
                        save->ppll_ref_div,
                        save->ppll_div_3,
                        save->htotal_cntl));
    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                        "Read: rd=%d, fd=%d, pd=%d\n",
                        save->ppll_ref_div & R128_PPLL_REF_DIV_MASK,
                        save->ppll_div_3 & R128_PPLL_FB3_DIV_MASK,
                        (save->ppll_div_3 &
                                R128_PPLL_POST3_DIV_MASK) >> 16));
}

/* Read PLL2 registers. */
static void R128SavePLL2Registers(ScrnInfoPtr pScrn, R128SavePtr save)
{
    save->p2pll_ref_div        = INPLL(pScrn, R128_P2PLL_REF_DIV);
    save->p2pll_div_0          = INPLL(pScrn, R128_P2PLL_DIV_0);
    save->htotal_cntl2         = INPLL(pScrn, R128_HTOTAL2_CNTL);

    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                        "Read: 0x%08x 0x%08x 0x%08x\n",
                        save->p2pll_ref_div,
                        save->p2pll_div_0,
                        save->htotal_cntl2));
    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                        "Read: rd=%d, fd=%d, pd=%d\n",
                        save->p2pll_ref_div & R128_P2PLL_REF_DIV_MASK,
                        save->p2pll_div_0 & R128_P2PLL_FB0_DIV_MASK,
                        (save->p2pll_div_0 &
                                R128_P2PLL_POST0_DIV_MASK) >> 16));
}

/* Read DDA registers. */
static void R128SaveDDARegisters(ScrnInfoPtr pScrn, R128SavePtr save)
{
    R128InfoPtr   info      = R128PTR(pScrn);
    unsigned char *R128MMIO = info->MMIO;

    save->dda_config           = INREG(R128_DDA_CONFIG);
    save->dda_on_off           = INREG(R128_DDA_ON_OFF);
}

/* Read DDA2 registers. */
static void R128SaveDDA2Registers(ScrnInfoPtr pScrn, R128SavePtr save)
{
    R128InfoPtr   info      = R128PTR(pScrn);
    unsigned char *R128MMIO = info->MMIO;

    save->dda2_config           = INREG(R128_DDA2_CONFIG);
    save->dda2_on_off           = INREG(R128_DDA2_ON_OFF);
}

/* Read palette data. */
static void R128SavePalette(ScrnInfoPtr pScrn, R128SavePtr save)
{
    R128InfoPtr   info      = R128PTR(pScrn);
    unsigned char *R128MMIO = info->MMIO;
    int           i;

    PAL_SELECT(1);
    INPAL_START(0);
    for (i = 0; i < 256; i++) save->palette2[i] = INPAL_NEXT();
    PAL_SELECT(0);
    INPAL_START(0);
    for (i = 0; i < 256; i++) save->palette[i] = INPAL_NEXT();
    save->palette_valid = TRUE;
}

/* Save state that defines current video mode. */
static void R128SaveMode(ScrnInfoPtr pScrn, R128SavePtr save)
{
    R128InfoPtr   info      = R128PTR(pScrn);
    R128EntPtr    pR128Ent  = R128EntPriv(pScrn);

    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                        "%s(%p)\n", __func__, save));

    R128SaveCommonRegisters(pScrn, save);
    R128SaveCrtcRegisters(pScrn, save);
    R128SavePLLRegisters(pScrn, save);
    R128SaveDDARegisters(pScrn, save);
    if (pR128Ent->HasCRTC2) {
        R128SaveCrtc2Registers(pScrn, save);
        R128SavePLL2Registers(pScrn, save);
        R128SaveDDA2Registers(pScrn, save);
    }
    if (info->HasPanelRegs) {
        R128SaveFPRegisters(pScrn, save);
    }
    R128SavePalette(pScrn, save);

    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                        "%s returns %p\n", __func__, save));
}

/* Save everything needed to restore the original VC state. */
static void R128Save(ScrnInfoPtr pScrn)
{
    R128InfoPtr   info      = R128PTR(pScrn);
    unsigned char *R128MMIO = info->MMIO;
    R128SavePtr   save      = &info->SavedReg;

    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                        "%s\n", __func__));
    if (info->FBDev) {
	fbdevHWSave(pScrn);
	return;
    }

#ifdef WITH_VGAHW
    if (info->VGAAccess) {
        vgaHWPtr hwp = VGAHWPTR(pScrn);

        vgaHWUnlock(hwp);
# if defined(__powerpc__)
        /* temporary hack to prevent crashing on PowerMacs when trying to
         * read VGA fonts and colormap, will find a better solution
         * in the future. TODO: Check if there's actually some VGA stuff
         * setup in the card at all !!
         */
        vgaHWSave(pScrn, &hwp->SavedReg, VGA_SR_MODE); /* Save mode only */
# else
        /* Save mode * & fonts & cmap */
        vgaHWSave(pScrn, &hwp->SavedReg, VGA_SR_MODE | VGA_SR_FONTS);
# endif
        vgaHWLock(hwp);
    }
#endif

    save->dp_datatype      = INREG(R128_DP_DATATYPE);
    save->gen_reset_cntl   = INREG(R128_GEN_RESET_CNTL);
    save->clock_cntl_index = INREG(R128_CLOCK_CNTL_INDEX);
    save->amcgpio_en_reg   = INREG(R128_AMCGPIO_EN_REG);
    save->amcgpio_mask     = INREG(R128_AMCGPIO_MASK);

    R128SaveMode(pScrn, save);
}

/* Restore the original (text) mode. */
static void R128Restore(ScrnInfoPtr pScrn)
{
    R128InfoPtr   info      = R128PTR(pScrn);
    R128EntPtr    pR128Ent  = R128EntPriv(pScrn);
    unsigned char *R128MMIO = info->MMIO;
    R128SavePtr   restore   = &info->SavedReg;

    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                        "%s\n", __func__));
    if (info->FBDev) {
	fbdevHWRestore(pScrn);
	return;
    }

    R128Blank(pScrn);

    OUTREG(R128_AMCGPIO_MASK,     restore->amcgpio_mask);
    OUTREG(R128_AMCGPIO_EN_REG,   restore->amcgpio_en_reg);
    OUTREG(R128_CLOCK_CNTL_INDEX, restore->clock_cntl_index);
    OUTREG(R128_GEN_RESET_CNTL,   restore->gen_reset_cntl);
    OUTREG(R128_DP_DATATYPE,      restore->dp_datatype);

    R128RestoreCommonRegisters(pScrn, restore);
    if (pR128Ent->HasCRTC2) {
        R128RestoreDDA2Registers(pScrn, restore);
        R128RestoreCrtc2Registers(pScrn, restore);
        R128RestorePLL2Registers(pScrn, restore);
    }
    R128RestoreDDARegisters(pScrn, restore);
    R128RestoreCrtcRegisters(pScrn, restore);
    R128RestorePLLRegisters(pScrn, restore);
    R128RestoreDACRegisters(pScrn, restore);
    R128RestoreRMXRegisters(pScrn, restore);
    R128RestoreFPRegisters(pScrn, restore);
    R128RestoreLVDSRegisters(pScrn, restore);

#ifdef WITH_VGAHW
    if (info->VGAAccess) {
        vgaHWPtr hwp = VGAHWPTR(pScrn);
        vgaHWUnlock(hwp);
# if defined(__powerpc__)
        /* Temporary hack to prevent crashing on PowerMacs when trying to
         * write VGA fonts, will find a better solution in the future
         */
        vgaHWRestore(pScrn, &hwp->SavedReg, VGA_SR_MODE );
# else
        vgaHWRestore(pScrn, &hwp->SavedReg, VGA_SR_MODE | VGA_SR_FONTS );
# endif
        vgaHWLock(hwp);
    }
#endif

    R128WaitForVerticalSync(pScrn);
    R128Unblank(pScrn);
}

/* Define common registers for requested video mode. */
void R128InitCommonRegisters(R128SavePtr save, R128InfoPtr info)
{
    save->ovr_clr            = 0;
    save->ovr_wid_left_right = 0;
    save->ovr_wid_top_bottom = 0;
    save->ov0_scale_cntl     = 0;
    save->mpp_tb_config      = 0;
    save->mpp_gp_config      = 0;
    save->subpic_cntl        = 0;
    save->viph_control       = 0;
    save->i2c_cntl_1         = 0;
#ifdef R128DRI
    save->gen_int_cntl       = info->gen_int_cntl;
#else
    save->gen_int_cntl       = 0;
#endif
    save->cap0_trig_cntl     = 0;
    save->cap1_trig_cntl     = 0;
    save->bus_cntl           = info->BusCntl;
    /*
     * If bursts are enabled, turn on discards and aborts
     */
    if (save->bus_cntl & (R128_BUS_WRT_BURST|R128_BUS_READ_BURST))
	save->bus_cntl |= R128_BUS_RD_DISCARD_EN | R128_BUS_RD_ABORT_EN;
}

/* Define RMX registers for the requested video mode. */
void R128InitRMXRegisters(R128SavePtr orig, R128SavePtr save,
                          xf86OutputPtr output, DisplayModePtr mode)
{
    R128OutputPrivatePtr r128_output = output->driver_private;

    int   xres = mode->CrtcHDisplay;
    int   yres = mode->CrtcVDisplay;
    float Hratio, Vratio;

    save->fp_crtc_h_total_disp = save->crtc_h_total_disp;
    save->fp_crtc_v_total_disp = save->crtc_v_total_disp;
    save->fp_h_sync_strt_wid   = save->crtc_h_sync_strt_wid;
    save->fp_v_sync_strt_wid   = save->crtc_v_sync_strt_wid;

    if (r128_output->MonType != MT_DFP && r128_output->MonType != MT_LCD)
        return;

    if (r128_output->PanelXRes == 0 || r128_output->PanelYRes == 0) {
        xres = r128_output->PanelXRes;
        yres = r128_output->PanelYRes;

        Hratio = 1.0;
        Vratio = 1.0;
    } else {
        if (xres > r128_output->PanelXRes) xres = r128_output->PanelXRes;
        if (yres > r128_output->PanelYRes) yres = r128_output->PanelYRes;

        Hratio = (float)xres/(float)r128_output->PanelXRes;
        Vratio = (float)yres/(float)r128_output->PanelYRes;
    }

    save->fp_horz_stretch =
	(((((int)(Hratio * R128_HORZ_STRETCH_RATIO_MAX + 0.5))
	   & R128_HORZ_STRETCH_RATIO_MASK) << R128_HORZ_STRETCH_RATIO_SHIFT) |
       (orig->fp_horz_stretch & (R128_HORZ_PANEL_SIZE |
                                 R128_HORZ_FP_LOOP_STRETCH |
                                 R128_HORZ_STRETCH_RESERVED)));
    save->fp_horz_stretch &= ~R128_HORZ_AUTO_RATIO_FIX_EN;
    save->fp_horz_stretch &= ~R128_AUTO_HORZ_RATIO;
    if (xres == r128_output->PanelXRes)
         save->fp_horz_stretch &= ~(R128_HORZ_STRETCH_BLEND | R128_HORZ_STRETCH_ENABLE);
    else
         save->fp_horz_stretch |=  (R128_HORZ_STRETCH_BLEND | R128_HORZ_STRETCH_ENABLE);

    save->fp_vert_stretch =
	(((((int)(Vratio * R128_VERT_STRETCH_RATIO_MAX + 0.5))
	   & R128_VERT_STRETCH_RATIO_MASK) << R128_VERT_STRETCH_RATIO_SHIFT) |
	 (orig->fp_vert_stretch & (R128_VERT_PANEL_SIZE |
				   R128_VERT_STRETCH_RESERVED)));
    save->fp_vert_stretch &= ~R128_VERT_AUTO_RATIO_EN;
    if (yres == r128_output->PanelYRes)
        save->fp_vert_stretch &= ~(R128_VERT_STRETCH_ENABLE | R128_VERT_STRETCH_BLEND);
    else
        save->fp_vert_stretch |=  (R128_VERT_STRETCH_ENABLE | R128_VERT_STRETCH_BLEND);
}

/* Define flat panel registers for the requested video mode. */
void R128InitFPRegisters(R128SavePtr orig, R128SavePtr save, xf86OutputPtr output)
{
    xf86CrtcPtr crtc = output->crtc;
    R128CrtcPrivatePtr r128_crtc = crtc->driver_private;

    /* WARNING: Be careful about turning on the flat panel */
    save->fp_gen_cntl            = orig->fp_gen_cntl;
    save->fp_panel_cntl          = orig->fp_panel_cntl;
    save->tmds_transmitter_cntl  = orig->tmds_transmitter_cntl;
    save->tmds_crc               = orig->tmds_crc;

    if (r128_crtc->crtc_id)
        save->fp_gen_cntl       |=   R128_FP_SEL_CRTC2;
    else
        save->fp_gen_cntl       &=  ~R128_FP_SEL_CRTC2;

    save->fp_gen_cntl           &= ~(R128_FP_CRTC_USE_SHADOW_VEND |
                                     R128_FP_CRTC_USE_SHADOW_ROWCUR |
                                     R128_FP_CRTC_HORZ_DIV2_EN |
                                     R128_FP_CRTC_HOR_CRT_DIV2_DIS |
                                     R128_FP_CRT_SYNC_SEL |
                                     R128_FP_USE_SHADOW_EN);

    save->fp_gen_cntl           |=  (R128_FP_CRTC_DONT_SHADOW_VPAR |
                                     R128_FP_CRTC_DONT_SHADOW_HEND);

    save->fp_panel_cntl         |=  (R128_FP_DIGON | R128_FP_BLON);
    save->tmds_transmitter_cntl &=  ~R128_TMDS_PLLRST;
    save->tmds_transmitter_cntl |=   R128_TMDS_PLLEN;
}

/* Define LVDS registers for the requested video mode. */
void R128InitLVDSRegisters(R128SavePtr orig, R128SavePtr save, xf86OutputPtr output)
{
    xf86CrtcPtr crtc = output->crtc;
    R128CrtcPrivatePtr r128_crtc = crtc->driver_private;

    save->lvds_gen_cntl      =  orig->lvds_gen_cntl;

    if (r128_crtc->crtc_id)
        save->lvds_gen_cntl |=  R128_LVDS_SEL_CRTC2;
    else
        save->lvds_gen_cntl &= ~R128_LVDS_SEL_CRTC2;
}

#if 0
/* Define initial palette for requested video mode.  This doesn't do
   anything for XFree86 4.0. */
static void R128InitPalette(R128SavePtr save)
{
    save->palette_valid = FALSE;
}
#endif

static Bool R128SaveScreen(ScreenPtr pScreen, int mode)
{
    ScrnInfoPtr   pScrn = xf86ScreenToScrn(pScreen);
    Bool unblank;

    unblank = xf86IsUnblank(mode);
    if (unblank)
	SetTimeSinceLastInputEvent();

    if ((pScrn != NULL) && pScrn->vtSema) {
	if (unblank)
		R128Unblank(pScrn);
	else
		R128Blank(pScrn);
    }
    return TRUE;
}

/*
 * SwitchMode() doesn't work right on crtc2 on some laptops.
 * The workaround is to switch the mode, then switch to another VT, then
 * switch back. --AGD
 */
Bool R128SwitchMode(ScrnInfoPtr pScrn, DisplayModePtr mode)
{
    R128InfoPtr info        = R128PTR(pScrn);
    Bool ret;

    info->SwitchingMode = TRUE;
    ret = xf86SetSingleMode(pScrn, mode, RR_Rotate_0);
    info->SwitchingMode = FALSE;
    return ret;
}

ModeStatus R128DoValidMode(xf86OutputPtr output, DisplayModePtr mode, int flags)
{
    ScrnInfoPtr pScrn = output->scrn;
    R128InfoPtr info  = R128PTR(pScrn);
    R128OutputPrivatePtr r128_output = output->driver_private;
    int i, j;

    if (r128_output->MonType == MT_CRT)
        return MODE_OK;

    if (r128_output->MonType == MT_DFP || r128_output->MonType == MT_LCD) {
	if (mode->Flags & V_INTERLACE) return MODE_NO_INTERLACE;
	if (mode->Flags & V_DBLSCAN)   return MODE_NO_DBLESCAN;
    }

    if (r128_output->MonType == MT_LCD && info->VBIOS) {
	for (i = info->FPBIOSstart + 64; R128_BIOS16(i) != 0; i += 2) {
	    j = R128_BIOS16(i);

	    if (mode->CrtcHDisplay == R128_BIOS16(j) &&
		mode->CrtcVDisplay == R128_BIOS16(j + 2)) {
		if ((flags & MODECHECK_FINAL) == MODECHECK_FINAL) {
		    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
			       "Modifying mode according to VBIOS: %ix%i [pclk %.1f MHz] for FP to: ",
			       mode->CrtcHDisplay, mode->CrtcVDisplay,
			       (float)mode->Clock / 1000);

		    /* Assume we are using expanded mode */
		    if (R128_BIOS16(j + 5)) j  = R128_BIOS16(j + 5);
		    else                    j += 9;

		    mode->Clock = (uint32_t)R128_BIOS16(j) * 10;

		    mode->HDisplay   = mode->CrtcHDisplay   =
			((R128_BIOS16(j + 10) & 0x01ff) + 1) * 8;
		    mode->HSyncStart = mode->CrtcHSyncStart =
			((R128_BIOS16(j + 12) & 0x01ff) + 1) * 8;
		    mode->HSyncEnd   = mode->CrtcHSyncEnd   =
			mode->CrtcHSyncStart + (R128_BIOS8(j + 14) & 0x1f);
		    mode->HTotal     = mode->CrtcHTotal     =
			((R128_BIOS16(j + 8)  & 0x01ff) + 1) * 8;

		    mode->VDisplay   = mode->CrtcVDisplay   =
			(R128_BIOS16(j + 17) & 0x07ff) + 1;
		    mode->VSyncStart = mode->CrtcVSyncStart =
			(R128_BIOS16(j + 19) & 0x07ff) + 1;
		    mode->VSyncEnd   = mode->CrtcVSyncEnd   =
			mode->CrtcVSyncStart + ((R128_BIOS16(j + 19) >> 11) & 0x1f);
		    mode->VTotal     = mode->CrtcVTotal     =
			(R128_BIOS16(j + 15) & 0x07ff) + 1;
		    xf86ErrorF("%ix%i [pclk %.1f MHz]\n",
			       mode->CrtcHDisplay,mode->CrtcVDisplay,
			       (float)mode->Clock/ 1000);
		}
		return MODE_OK;
	    }
	}
	xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 5,
		       "Mode rejected for FP %ix%i [pclk: %.1f] "
		       "(not listed in VBIOS)\n",
		       mode->CrtcHDisplay, mode->CrtcVDisplay,
		       (float)mode->Clock / 1000);
	return MODE_NOMODE;
    }

    return MODE_OK;
}

/* Used to disallow modes that are not supported by the hardware. */
ModeStatus R128ValidMode(ScrnInfoPtr pScrn, DisplayModePtr mode,
                                   Bool verbose, int flags)
{
    R128EntPtr  pR128Ent = R128EntPriv(pScrn);
    xf86OutputPtr output = R128FirstOutput(pR128Ent->pCrtc[0]);

    return R128DoValidMode(output, mode, flags);
}

/* Adjust viewport into virtual desktop such that (0,0) in viewport space
   is (x,y) in virtual space. */
void R128AdjustFrame(ScrnInfoPtr pScrn, int x, int y)
{
    R128InfoPtr   info      = R128PTR(pScrn);
    unsigned char *R128MMIO = info->MMIO;
    int           Base;

    if(info->showCache && y && pScrn->vtSema)
        y += pScrn->virtualY - 1;

    Base = y * info->CurrentLayout.displayWidth + x;

    switch (info->CurrentLayout.pixel_code) {
    case 15:
    case 16: Base *= 2; break;
    case 24: Base *= 3; break;
    case 32: Base *= 4; break;
    }

    Base &= ~7;                 /* 3 lower bits are always 0 */

    if (info->CurrentLayout.pixel_code == 24)
	Base += 8 * (Base % 3); /* Must be multiple of 8 and 3 */

    OUTREG(R128_CRTC_OFFSET, Base);
}

/* Called when VT switching back to the X server.  Reinitialize the video
   mode. */
Bool R128EnterVT(ScrnInfoPtr pScrn)
{
    R128InfoPtr info  = R128PTR(pScrn);

    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                        "%s\n", __func__));

    pScrn->vtSema = TRUE;
    if (info->FBDev) {
        if (!fbdevHWEnterVT(pScrn)) return FALSE;
    } else {
        if (!xf86SetDesiredModes(pScrn)) return FALSE;
    }

    //if (!R128ModeInit(pScrn, pScrn->currentMode)) return FALSE;

    if (info->accelOn)
	R128EngineInit(pScrn);

#ifdef R128DRI
    if (info->directRenderingEnabled) {
	if (info->irq) {
	    /* Need to make sure interrupts are enabled */
	    unsigned char *R128MMIO = info->MMIO;
	    OUTREG(R128_GEN_INT_CNTL, info->gen_int_cntl);
	}
	R128CCE_START(pScrn, info);
	DRIUnlock(pScrn->pScreen);
    }
#endif

    info->PaletteSavedOnVT = FALSE;
    //pScrn->AdjustFrame(pScrn, pScrn->frameX0, pScrn->frameY0);

    return TRUE;
}

/* Called when VT switching away from the X server.  Restore the original
   text mode. */
void R128LeaveVT(ScrnInfoPtr pScrn)
{
    R128InfoPtr info  = R128PTR(pScrn);
    R128SavePtr save  = &info->ModeReg;

    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                        "%s\n", __func__));
#ifdef R128DRI
    if (info->directRenderingEnabled) {
	DRILock(pScrn->pScreen, 0);
	R128CCE_STOP(pScrn, info);
    }
#ifdef USE_EXA
    if (info->useEXA)
        info->state_2d.composite_setup = FALSE;
#endif
#endif
    R128SavePalette(pScrn, save);
    info->PaletteSavedOnVT = TRUE;
    if (info->FBDev)
        fbdevHWLeaveVT(pScrn);
    else
        R128Restore(pScrn);
}


/* Called at the end of each server generation.  Restore the original text
   mode, unmap video memory, and unwrap and call the saved CloseScreen
   function.  */
static Bool R128CloseScreen(ScreenPtr pScreen)
{
    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
    R128InfoPtr info  = R128PTR(pScrn);

    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                        "%s\n", __func__));

#ifdef R128DRI
				/* Disable direct rendering */
    if (info->directRenderingEnabled) {
	R128DRICloseScreen(pScreen);
	info->directRenderingEnabled = FALSE;
    }
#endif

    if (pScrn->vtSema) {
	R128Restore(pScrn);
	R128UnmapMem(pScrn);
    }

#ifdef USE_EXA
    if (info->useEXA) {
	exaDriverFini(pScreen);
	free(info->ExaDriver);
    }
#endif

    if (info->scratch_save)      free(info->scratch_save);
    info->scratch_save           = NULL;

    if (info->adaptor) {
        free(info->adaptor->pPortPrivates[0].ptr);
	xf86XVFreeVideoAdaptorRec(info->adaptor);
	info->adaptor = NULL;
    }

    pScrn->vtSema = FALSE;

    pScreen->BlockHandler = info->BlockHandler;
    pScreen->CloseScreen = info->CloseScreen;
    return (*pScreen->CloseScreen)(pScreen);
}

void R128FreeScreen(ScrnInfoPtr pScrn)
{
    R128InfoPtr   info      = R128PTR(pScrn);

    DEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                        "%s\n", __func__));
    if (info == NULL)
	return;
#ifdef WITH_VGAHW
    if (info->VGAAccess && xf86LoaderCheckSymbol("vgaHWFreeHWRec"))
	vgaHWFreeHWRec(pScrn);
#endif
    R128FreeRec(pScrn);
}
