/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/apm/apm_driver.c,v 1.65 2003/10/30 17:36:57 tsi Exp $ */

#include "apm.h"
#include "xf86cmap.h"
#include "shadowfb.h"
#include "xf86Resources.h"
#include "xf86int10.h"
#include "xf86RAC.h"
#include "vbe.h"

#include "opaque.h"
#define DPMS_SERVER
#include "extensions/dpms.h"

#define VERSION			4000
#define APM_NAME		"APM"
#define APM_DRIVER_NAME		"apm"
#define APM_MAJOR_VERSION       1
#define APM_MINOR_VERSION       0
#define APM_PATCHLEVEL          0
#ifndef PCI_CHIP_AT3D
#define PCI_CHIP_AT3D	0x643D
#endif

/* bytes to save for text/font data */
#define TEXT_AMOUNT 32768

/* Mandatory functions */
static const OptionInfoRec *	ApmAvailableOptions(int chipid, int busid);
static void     ApmIdentify(int flags);
static Bool     ApmProbe(DriverPtr drv, int flags);
static Bool     ApmPreInit(ScrnInfoPtr pScrn, int flags);
static Bool     ApmScreenInit(int Index, ScreenPtr pScreen, int argc,
                                  char **argv);
static Bool     ApmEnterVT(int scrnIndex, int flags);
static void     ApmLeaveVT(int scrnIndex, int flags);
static Bool     ApmCloseScreen(int scrnIndex, ScreenPtr pScreen);
static void     ApmFreeScreen(int scrnIndex, int flags);
static ModeStatus ApmValidMode(int scrnIndex, DisplayModePtr mode,
                               Bool verbose, int flags);
static Bool	ApmSaveScreen(ScreenPtr pScreen, int mode);
static void	ApmUnlock(ApmPtr pApm);
static void	ApmLock(ApmPtr pApm);
static void	ApmRestore(ScrnInfoPtr pScrn, vgaRegPtr vgaReg,
			    ApmRegPtr ApmReg);
static void	ApmLoadPalette(ScrnInfoPtr pScrn, int numColors, int *indices,
				LOCO *colors, VisualPtr pVisual);
static void	ApmDisplayPowerManagementSet(ScrnInfoPtr pScrn,
					     int PowerManagementMode,
					     int flags);
static void	ApmProbeDDC(ScrnInfoPtr pScrn, int index);


int ApmPixmapIndex = -1;
static unsigned long ApmGeneration = 0;

DriverRec APM = {
	VERSION,
	APM_DRIVER_NAME,
	ApmIdentify,
	ApmProbe,
	ApmAvailableOptions,
	NULL,
	0
};

static SymTabRec ApmChipsets[] = {
    { AP6422,	"AP6422"	},
    { AT24,	"AT24"		},
    { AT3D,	"AT3D"		},
    { -1,	NULL		}
};

static PciChipsets ApmPciChipsets[] = {
    { PCI_CHIP_AP6422,	PCI_CHIP_AP6422,	RES_SHARED_VGA },
    { PCI_CHIP_AT24,	PCI_CHIP_AT24,		RES_SHARED_VGA },
    { PCI_CHIP_AT3D,	PCI_CHIP_AT3D,		RES_SHARED_VGA },
    { -1,			-1,		RES_UNDEFINED }
};

static IsaChipsets ApmIsaChipsets[] = {
    { PCI_CHIP_AP6422,	RES_EXCLUSIVE_VGA},
    {-1,		RES_UNDEFINED}
};

typedef enum {
    OPTION_SET_MCLK,
    OPTION_SW_CURSOR,
    OPTION_HW_CURSOR,
    OPTION_NOLINEAR,
    OPTION_NOACCEL,
    OPTION_SHADOW_FB,
    OPTION_PCI_BURST,
    OPTION_REMAP_DPMS_ON,
    OPTION_REMAP_DPMS_STANDBY,
    OPTION_REMAP_DPMS_SUSPEND,
    OPTION_REMAP_DPMS_OFF,
    OPTION_PCI_RETRY
} ApmOpts;

static const OptionInfoRec ApmOptions[] =
{
    {OPTION_SET_MCLK, "SetMclk", OPTV_FREQ,
	{0}, FALSE},
    {OPTION_SW_CURSOR, "SWcursor", OPTV_BOOLEAN,
	{0}, FALSE},
    {OPTION_HW_CURSOR, "HWcursor", OPTV_BOOLEAN,
	{0}, TRUE},
    {OPTION_NOLINEAR, "NoLinear", OPTV_BOOLEAN,
	{0}, FALSE},
    {OPTION_NOACCEL, "NoAccel", OPTV_BOOLEAN,
	{0}, FALSE},
    {OPTION_SHADOW_FB, "ShadowFB", OPTV_BOOLEAN,
	{0}, FALSE},
    {OPTION_PCI_BURST, "pci_burst", OPTV_BOOLEAN,
	{0}, FALSE},
    {OPTION_REMAP_DPMS_ON, "Remap_DPMS_On", OPTV_ANYSTR,
	{0}, FALSE},
    {OPTION_REMAP_DPMS_STANDBY, "Remap_DPMS_Standby", OPTV_ANYSTR,
	{0}, FALSE},
    {OPTION_REMAP_DPMS_SUSPEND, "Remap_DPMS_Suspend", OPTV_ANYSTR,
	{0}, FALSE},
    {OPTION_REMAP_DPMS_OFF, "Remap_DPMS_Off", OPTV_ANYSTR,
	{0}, FALSE},
    {OPTION_PCI_RETRY, "PciRetry", OPTV_BOOLEAN,
	{0}, FALSE},
    {-1, NULL, OPTV_NONE,
	{0}, FALSE}
};

/*
 * List of symbols from other modules that this module references.  This
 * list is used to tell the loader that it is OK for symbols here to be
 * unresolved providing that it hasn't been told that they haven't been
 * told that they are essential via a call to xf86LoaderReqSymbols() or
 * xf86LoaderReqSymLists().  The purpose is this is to avoid warnings about
 * unresolved symbols that are not required.
 */

static const char *vgahwSymbols[] = {
    "vgaHWBlankScreen",
    "vgaHWCursor",
    "vgaHWFreeHWRec",
    "vgaHWGetHWRec",
    "vgaHWGetIOBase",
    "vgaHWInit",
    "vgaHWLock",
    "vgaHWMapMem",
    "vgaHWProtect",
    "vgaHWRestore",
    "vgaHWSave",
    "vgaHWSetMmioFuncs",
    "vgaHWUnlock",
    NULL
};

static const char *xaaSymbols[] = {
    "XAACreateInfoRec",
    "XAACursorInfoRec",
    "XAACursorInit",
    "XAADestroyInfoRec",
    "XAAGlyphScanlineFuncLSBFirst",
    "XAAInit",
    "XAAReverseBitOrder",
    "XAAStippleScanlineFuncMSBFirst",
    NULL
};

static const char *ramdacSymbols[] = {
    "xf86CreateCursorInfoRec",
    "xf86DestroyCursorInfoRec",
    "xf86InitCursor",
    NULL
};

#ifdef XFree86LOADER
static const char *vbeSymbols[] = {
    "VBEInit",
    "vbeDoEDID",
    "vbeFree",
    NULL
};
#endif

static const char *ddcSymbols[] = {
    "xf86DoEDID_DDC1",
    "xf86DoEDID_DDC2",
    "xf86PrintEDID",
    NULL
};

static const char *i2cSymbols[] = {
    "xf86CreateI2CBusRec",
    "xf86I2CBusInit",
    NULL
};

static const char *shadowSymbols[] = {
    "ShadowFBInit",
    NULL
};

#ifdef XFree86LOADER
static const char *miscfbSymbols[] = {
    "xf1bppScreenInit",
    "xf4bppScreenInit",
    NULL
};
#endif

static const char *fbSymbols[] = {
    "fbPictureInit",
    "fbScreenInit",
    NULL
};

static const char *int10Symbols[] = {
    "xf86Free10",
    "xf86InitInt10",
    NULL
};

#ifdef XFree86LOADER

static XF86ModuleVersionInfo apmVersRec = {
    "apm",
    MODULEVENDORSTRING,
    MODINFOSTRING1,
    MODINFOSTRING2,
    XORG_VERSION_CURRENT,
    APM_MAJOR_VERSION, APM_MINOR_VERSION, APM_PATCHLEVEL,
    ABI_CLASS_VIDEODRV,			/* This is a video driver */
    ABI_VIDEODRV_VERSION,
    MOD_CLASS_VIDEODRV,
    {0,0,0,0}
};

static MODULESETUPPROTO(apmSetup);

/*
 * This is the module init data.
 * Its name has to be the driver name followed by ModuleData.
 */
XF86ModuleData apmModuleData = { &apmVersRec, apmSetup, NULL };

static pointer
apmSetup(pointer module, pointer opts, int *errmaj, int *errmain)
{
    static Bool setupDone = FALSE;

    if (!setupDone) {
	setupDone = TRUE;
	xf86AddDriver(&APM, module, 0);

	LoaderRefSymLists(vgahwSymbols, fbSymbols, xaaSymbols, 
			  miscfbSymbols, ramdacSymbols, vbeSymbols,
			  ddcSymbols, i2cSymbols, shadowSymbols, 
			  int10Symbols, NULL);

	return (pointer)1;
    }
    else {
	if (errmaj) *errmaj = LDR_ONCEONLY;
	return NULL;
    }
}
#endif

static Bool
ApmGetRec(ScrnInfoPtr pScrn)
{
    if (pScrn->driverPrivate)
	return TRUE;
    pScrn->driverPrivate = xnfcalloc(sizeof(ApmRec), 1);
    /* pScrn->driverPrivate != NULL at this point */

    return TRUE;
}

static void
ApmFreeRec(ScrnInfoPtr pScrn)
{
    if (pScrn->driverPrivate) {
	xfree(pScrn->driverPrivate);
	pScrn->driverPrivate = NULL;
    }
}


/* unlock Alliance registers */
static void
ApmUnlock(ApmPtr pApm)
{
    if (pApm->Chipset >= AT3D && !pApm->noLinear)
	ApmWriteSeq(0x10, 0x12);
    else
	wrinx(pApm->xport, 0x10, 0x12);
}

/* lock Alliance registers */
static void
ApmLock(ApmPtr pApm)
{
    if (pApm->Chipset >= AT3D && !pApm->noLinear)
	ApmWriteSeq(0x10, pApm->savedSR10 ? 0 : 0x12);
    else
	wrinx(pApm->xport, 0x10, pApm->savedSR10 ? 0 : 0x12);
}

static void
ApmIdentify(int flags)
{
    xf86PrintChipsets(APM_NAME, "driver for the Alliance chipsets",
		      ApmChipsets);
}

static const OptionInfoRec *
ApmAvailableOptions(int chipid, int busid)
{
    return ApmOptions;
}

static int
ApmFindIsaDevice(GDevPtr dev)
{
    char	save = rdinx(0x3C4, 0x10);
    int		i;
    int		apmChip = -1;

    /*
     * Start by probing the VGA chipset.
     */
    outw(0x3C4, 0x1210);
    if (rdinx(0x3C4, 0x11) == 'P' && rdinx(0x3C4, 0x12) == 'r' &&
	rdinx(0x3C4, 0x13) == 'o') {
	char	id_ap6420[] = "6420";
	char	id_ap6422[] = "6422";
	char	id_at24[]   = "6424";
	char	id_at3d[]   = "AT3D";
	char	idstring[]  = "    ";

	/*
	 * Must be an Alliance !!!
	 */
	for (i = 0; i < 4; i++)
	    idstring[i] = rdinx(0x3C4, 0x14 + i);
	if (!memcmp(id_ap6420, idstring, 4) ||
	    !memcmp(id_ap6422, idstring, 4))
	    apmChip = AP6422;
	else if (!memcmp(id_at24, idstring, 4))
	    apmChip = AT24;
	else if (!memcmp(id_at3d, idstring, 4))
	    apmChip = AT3D;
	if (apmChip >= 0) {
	    int	apm_xbase;

	    apm_xbase = (rdinx(0x3C4, 0x1F) << 8) | rdinx(0x3C4, 0x1E);

	    if (!(wrinx(0x3C4, 0x1D, 0xCA >> 2), inb(apm_xbase + 2))) {
		/*
		 * TODO Not PCI
		 */
	    }

	}
    }
    wrinx(0x3C4, 0x10, save);

    return apmChip;
}

static void
ApmAssignFPtr(ScrnInfoPtr pScrn)
{
    pScrn->driverVersion	= VERSION;
    pScrn->driverName		= APM_DRIVER_NAME;
    pScrn->name			= APM_NAME;
    pScrn->Probe		= ApmProbe;
    pScrn->PreInit		= ApmPreInit;
    pScrn->ScreenInit		= ApmScreenInit;
    pScrn->SwitchMode		= ApmSwitchMode;
    pScrn->AdjustFrame		= ApmAdjustFrame;
    pScrn->EnterVT		= ApmEnterVT;
    pScrn->LeaveVT		= ApmLeaveVT;
    pScrn->FreeScreen		= ApmFreeScreen;
    pScrn->ValidMode		= ApmValidMode;
}

static Bool
ApmProbe(DriverPtr drv, int flags)
{
    int			numDevSections, numUsed, i;
    GDevPtr		*DevSections;
    int			*usedChips;
    int			foundScreen = FALSE;

    /*
     * Check if there is a chipset override in the config file
     */
    if ((numDevSections = xf86MatchDevice(APM_DRIVER_NAME,
					   &DevSections)) <= 0)
	return FALSE;

    /*
     * We need to probe the hardware first. We then need to see how this
     * fits in with what is given in the config file, and allow the config
     * file info to override any contradictions.
     */

    if (xf86GetPciVideoInfo() == NULL) {
	return FALSE;
    }
    numUsed = xf86MatchPciInstances(APM_NAME, PCI_VENDOR_ALLIANCE,
		    ApmChipsets, ApmPciChipsets, DevSections, numDevSections,
		    drv, &usedChips);

    if (numUsed > 0) {
	if (flags & PROBE_DETECT)
	    foundScreen = TRUE;
	else for (i = 0; i < numUsed; i++) {
	    ScrnInfoPtr	pScrn;
	    
	    /*
	     * Allocate a ScrnInfoRec and claim the slot
	     */
	    pScrn = NULL;
	    if ((pScrn = xf86ConfigPciEntity(pScrn, 0, usedChips[i],
						   ApmPciChipsets, NULL,
						   NULL,NULL,NULL,NULL))){
		
		/*
		 * Fill in what we can of the ScrnInfoRec
		 */
		ApmAssignFPtr(pScrn);
		foundScreen = TRUE;
	    }
	}
    }

    /* Check for non-PCI cards */
    numUsed = xf86MatchIsaInstances(APM_NAME, ApmChipsets,
			ApmIsaChipsets, drv, ApmFindIsaDevice, DevSections,
			numDevSections, &usedChips);
    if (numUsed > 0) {
	if (flags & PROBE_DETECT)
	    foundScreen = TRUE;
	else for (i = 0; i < numUsed; i++) {
	    ScrnInfoPtr pScrn = NULL;
	    if ((pScrn = xf86ConfigIsaEntity(pScrn, 0, usedChips[i],
					     ApmIsaChipsets, NULL, NULL, NULL,
					     NULL, NULL))) {
	    /*
	     * Fill in what we can of the ScrnInfoRec
	     */
	    ApmAssignFPtr(pScrn);
	    foundScreen = TRUE;
	    }
	}
    }
    xfree(DevSections);
    return foundScreen;
}

/*
 * GetAccelPitchValues -
 *
 * This function returns a list of display width (pitch) values that can
 * be used in accelerated mode.
 */
static int *
GetAccelPitchValues(ScrnInfoPtr pScrn)
{
    int *linePitches = NULL;
    int linep[] = {640, 800, 1024, 1152, 1280, 0};

    if (sizeof linep > 0) {
	linePitches = (int *)xnfalloc(sizeof linep);
	memcpy(linePitches, linep, sizeof linep);
    }

    return linePitches;
}

static unsigned int
ddc1Read(ScrnInfoPtr pScrn)
{
    APMDECL(pScrn);
    unsigned char	tmp;

    tmp = RDXB_IOP(0xD0);
    WRXB_IOP(0xD0, tmp & 0x07);
    while (STATUS_IOP() & 0x800);             
    while (!(STATUS_IOP() & 0x800));             
    return (STATUS_IOP() & STATUS_SDA) != 0;
}

static void
ApmProbeDDC(ScrnInfoPtr pScrn, int index)
{
    vbeInfoPtr pVbe;

    if (xf86LoadSubModule(pScrn, "vbe")) {
        pVbe = VBEInit(NULL, index);
        ConfiguredMonitor = vbeDoEDID(pVbe, NULL);
	vbeFree(pVbe);
    }
}

static Bool
ApmPreInit(ScrnInfoPtr pScrn, int flags)
{
    APMDECL(pScrn);
    EntityInfoPtr	pEnt;
    vgaHWPtr		hwp;
    MessageType		from;
    char		*mod = NULL, *req = NULL, *s;
    ClockRangePtr	clockRanges;
    int			i;
    xf86MonPtr		MonInfo = NULL;
    double		real;

    /*
     * Note: This function is only called once at server startup, and
     * not at the start of each server generation.  This means that
     * only things that are persistent across server generations can
     * be initialised here.  xf86Screens[] is (pScrn is a pointer to one
     * of these).  Privates allocated using xf86AllocateScrnInfoPrivateIndex()
     * are too, and should be used for data that must persist across
     * server generations.
     *
     * Per-generation data should be allocated with
     * AllocateScreenPrivateIndex() from the ScreenInit() function.
     */

    /* Check the number of entities, and fail if it isn't one. */
    if (pScrn->numEntities != 1)
	return FALSE;

    /* Allocate the ApmRec driverPrivate */
    if (!ApmGetRec(pScrn)) {
	return FALSE;
    }
    pApm = APMPTR(pScrn);

    /* Get the entity */
    pEnt = pApm->pEnt	= xf86GetEntityInfo(pScrn->entityList[0]);
    if (pEnt->location.type == BUS_PCI) {
	pApm->PciInfo	= xf86GetPciInfoForEntity(pEnt->index);
	pApm->PciTag	= pciTag(pApm->PciInfo->bus, pApm->PciInfo->device,
				 pApm->PciInfo->func);
    }
    else {
	pApm->PciInfo	= NULL;
	pApm->PciTag	= 0;
    }

    if (flags & PROBE_DETECT) {
        ApmProbeDDC(pScrn, pEnt->index);
        return TRUE;
    }

    /* The vgahw module should be allocated here when needed */
    if (!xf86LoadSubModule(pScrn, "vgahw"))
	return FALSE;

    xf86LoaderReqSymLists(vgahwSymbols, NULL);

    /*
     * Allocate a vgaHWRec
     */
    if (!vgaHWGetHWRec(pScrn))
	return FALSE;

    hwp = VGAHWPTR(pScrn);
    vgaHWGetIOBase(hwp);
    pApm->iobase = hwp->PIOOffset;
    pApm->xport = hwp->PIOOffset + 0x3C4;

    /* Set pScrn->monitor */
    pScrn->monitor = pScrn->confScreen->monitor;

    /* XXX: Access funcs */
    /*
     * The first thing we should figure out is the depth, bpp, etc.
     */
    if (!xf86SetDepthBpp(pScrn, 0, 0, 0, Support24bppFb | Support32bppFb)) {
	return FALSE;
    } else {
	/* Check that the returned depth is one we support */
	switch (pScrn->depth) {
	case 4:
	case 8:
	case 15:
	case 16:
	case 24:
	    /* OK */
	    break;
	default:
	    xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
		       "Given depth (%d) is not supported by this driver\n",
		       pScrn->depth);
	    return FALSE;
	}
    }
    xf86PrintDepthBpp(pScrn);

    /*
     * This must happen after pScrn->display has been set because
     * xf86SetWeight references it.
     */
    if (pScrn->depth > 8) {
	/* The defaults are OK for us */
	rgb zeros = {0, 0, 0};

	if (!xf86SetWeight(pScrn, zeros, zeros)) {
	    return FALSE;
	} else {
	    /* XXX check that weight returned is supported */
            ;
        }
    }

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

    /* We use a programmable clock */
    pScrn->progClock = TRUE;

    /* Collect all of the relevant option flags (fill in pScrn->options) */
    xf86CollectOptions(pScrn, NULL);

    /* Process the options */
    if (!(pApm->Options = xalloc(sizeof(ApmOptions))))
	return FALSE;
    memcpy(pApm->Options, ApmOptions, sizeof(ApmOptions));
    xf86ProcessOptions(pScrn->scrnIndex, pScrn->options, pApm->Options);

    pApm->scrnIndex = pScrn->scrnIndex;
    /* Set the bits per RGB for 8bpp mode */
    if (pScrn->depth > 1 && pScrn->depth <= 8) {
	/* Default to 8 */
	pScrn->rgbBits = 8;
    }
    if (xf86ReturnOptValBool(pApm->Options, OPTION_NOLINEAR, FALSE)) {
	pApm->noLinear = TRUE;
	xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "No linear framebuffer\n");
    }
    from = X_DEFAULT;
    pApm->hwCursor = FALSE;
    if (xf86GetOptValBool(pApm->Options, OPTION_HW_CURSOR, &pApm->hwCursor))
	from = X_CONFIG;
    if (pApm->noLinear ||
	xf86ReturnOptValBool(pApm->Options, OPTION_SW_CURSOR, FALSE)) {
	from = X_CONFIG;
	pApm->hwCursor = FALSE;
    }
    xf86DrvMsg(pScrn->scrnIndex, from, "Using %s cursor\n",
		pApm->hwCursor ? "HW" : "SW");
    from = X_DEFAULT;
    if (pScrn->bitsPerPixel < 8)
	pApm->NoAccel = TRUE;
    if (xf86ReturnOptValBool(pApm->Options, OPTION_NOACCEL, FALSE)) {
	from = X_CONFIG;
	pApm->NoAccel = TRUE;
    }
    if (pApm->NoAccel)
	xf86DrvMsg(pScrn->scrnIndex, from, "Acceleration disabled\n");
    if (xf86GetOptValFreq(pApm->Options, OPTION_SET_MCLK, OPTUNITS_MHZ, &real)) {
	xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "MCLK used is %.1f MHz\n", real);
	pApm->MemClk = (int)(real * 1000.0);
    }
    if (xf86ReturnOptValBool(pApm->Options, OPTION_SHADOW_FB, FALSE)) {
	pApm->ShadowFB = TRUE;
	pApm->NoAccel = TRUE;
	xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
		"Using \"Shadow Framebuffer\" - acceleration disabled\n");
    }
    if (xf86ReturnOptValBool(pApm->Options, OPTION_PCI_RETRY, FALSE)) {
	if (xf86ReturnOptValBool(pApm->Options, OPTION_PCI_BURST, FALSE)) {
	  pApm->UsePCIRetry = TRUE;
	  xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "PCI retry enabled\n");
	}
	else
	  xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "\"pci_retry\" option requires pci_burst \"on\".\n");
    }
    pApm->DPMSMask[DPMSModeOn]		= DPMSModeOn;
    pApm->DPMSMask[DPMSModeStandby]	= DPMSModeStandby;
    pApm->DPMSMask[DPMSModeSuspend]	= DPMSModeSuspend;
    pApm->DPMSMask[DPMSModeOff]		= DPMSModeOff;
    if ((s = xf86GetOptValString(pApm->Options, OPTION_REMAP_DPMS_ON))) {
	if (!strcmp(s, "on"))
	    pApm->DPMSMask[DPMSModeOn] = DPMSModeOn;
	else if (!strcmp(s, "standby"))
	    pApm->DPMSMask[DPMSModeOn] = DPMSModeStandby;
	else if (!strcmp(s, "suspend"))
	    pApm->DPMSMask[DPMSModeOn] = DPMSModeSuspend;
	else if (!strcmp(s, "off"))
	    pApm->DPMSMask[DPMSModeOn] = DPMSModeOff;
	else if (s[0] >= '0' && s[0] <= '9') {
	    pApm->DPMSMask[DPMSModeOn] = strtol(s, NULL, 0);
	    if (pApm->DPMSMask[DPMSModeOn] > (sizeof pApm->DPMSMask)-1)
		pApm->DPMSMask[DPMSModeOn] = (sizeof pApm->DPMSMask) - 1;
	}
    }
    if ((s = xf86GetOptValString(pApm->Options, OPTION_REMAP_DPMS_STANDBY))) {
	if (!strcmp(s, "on"))
	    pApm->DPMSMask[DPMSModeStandby] = DPMSModeOn;
	else if (!strcmp(s, "standby"))
	    pApm->DPMSMask[DPMSModeStandby] = DPMSModeStandby;
	else if (!strcmp(s, "suspend"))
	    pApm->DPMSMask[DPMSModeStandby] = DPMSModeSuspend;
	else if (!strcmp(s, "off"))
	    pApm->DPMSMask[DPMSModeStandby] = DPMSModeOff;
	else if (s[0] >= '0' && s[0] <= '9') {
	    pApm->DPMSMask[DPMSModeStandby] = strtol(s, NULL, 0);
	    if (pApm->DPMSMask[DPMSModeStandby] > (sizeof pApm->DPMSMask)-1)
		pApm->DPMSMask[DPMSModeStandby] = (sizeof pApm->DPMSMask) - 1;
	}
    }
    if ((s = xf86GetOptValString(pApm->Options, OPTION_REMAP_DPMS_SUSPEND))) {
	if (!strcmp(s, "on"))
	    pApm->DPMSMask[DPMSModeSuspend] = DPMSModeOn;
	else if (!strcmp(s, "standby"))
	    pApm->DPMSMask[DPMSModeSuspend] = DPMSModeStandby;
	else if (!strcmp(s, "suspend"))
	    pApm->DPMSMask[DPMSModeSuspend] = DPMSModeSuspend;
	else if (!strcmp(s, "off"))
	    pApm->DPMSMask[DPMSModeSuspend] = DPMSModeOff;
	else if (s[0] >= '0' && s[0] <= '9') {
	    pApm->DPMSMask[DPMSModeSuspend] = strtol(s, NULL, 0);
	    if (pApm->DPMSMask[DPMSModeSuspend] > (sizeof pApm->DPMSMask)-1)
		pApm->DPMSMask[DPMSModeSuspend] = (sizeof pApm->DPMSMask) - 1;
	}
    }
    if ((s = xf86GetOptValString(pApm->Options, OPTION_REMAP_DPMS_OFF))) {
	if (!strcmp(s, "on"))
	    pApm->DPMSMask[DPMSModeOff] = DPMSModeOn;
	else if (!strcmp(s, "standby"))
	    pApm->DPMSMask[DPMSModeOff] = DPMSModeStandby;
	else if (!strcmp(s, "suspend"))
	    pApm->DPMSMask[DPMSModeOff] = DPMSModeSuspend;
	else if (!strcmp(s, "off"))
	    pApm->DPMSMask[DPMSModeOff] = DPMSModeOff;
	else if (s[0] >= '0' && s[0] <= '9') {
	    pApm->DPMSMask[DPMSModeOff] = strtol(s, NULL, 0);
	    if (pApm->DPMSMask[DPMSModeOff] > (sizeof pApm->DPMSMask)-1)
		pApm->DPMSMask[DPMSModeOff] = (sizeof pApm->DPMSMask) - 1;
	}
    }

    /*
     * Set the Chipset and ChipRev, allowing config file entries to
     * override.
     */
    if (pEnt->device->chipset && *pEnt->device->chipset) {
	pScrn->chipset = pEnt->device->chipset;
        pApm->Chipset = xf86StringToToken(ApmChipsets, pScrn->chipset);
        from = X_CONFIG;
    } else if (pEnt->device->chipID >= 0) {
	pApm->Chipset = pEnt->device->chipID;
	pScrn->chipset = (char *)xf86TokenToString(ApmChipsets, pApm->Chipset);

	from = X_CONFIG;
	xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "ChipID override: 0x%04X\n",
		   pApm->Chipset);
    } else {
	from = X_PROBED;
	if (pApm->PciInfo)
	    pApm->Chipset = pApm->PciInfo->chipType;
	else
	    pApm->Chipset = pEnt->chipset;
	pScrn->chipset = (char *)xf86TokenToString(ApmChipsets, pApm->Chipset);
    }
    if (pScrn->bitsPerPixel == 24 && pApm->Chipset < AT24) {
	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
		   "Given depth (%d) is not supported by this driver\n",
		   pScrn->depth);
	return FALSE;
    }
    if (pEnt->device->chipRev >= 0) {
	pApm->ChipRev = pEnt->device->chipRev;
	xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "ChipRev override: %d\n",
		   pApm->ChipRev);
    } else if (pApm->PciInfo) {
	pApm->ChipRev = pApm->PciInfo->chipRev;
    }

    /*
     * This shouldn't happen because such problems should be caught in
     * ApmProbe(), but check it just in case.
     */
    if (pScrn->chipset == NULL) {
	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
		   "ChipID 0x%04X is not recognised\n", pApm->Chipset);
	return FALSE;
    }
    if (pApm->Chipset < 0) {
	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
		   "Chipset \"%s\" is not recognised\n", pScrn->chipset);
	return FALSE;
    }

    xf86DrvMsg(pScrn->scrnIndex, from, "Chipset: \"%s\"\n", pScrn->chipset);

    if (pEnt->device->MemBase != 0) {
	pApm->LinAddress = pEnt->device->MemBase;
	from = X_CONFIG;
    } else if (pApm->PciInfo) {
	pApm->LinAddress = pApm->PciInfo->memBase[0] & 0xFF800000;
	from = X_PROBED;
    } else {
	/*
	 * VESA local bus.
	 * Pray that 2048MB works.
	 */
	pApm->LinAddress = 0x80000000;
    }

    xf86DrvMsg(pScrn->scrnIndex, from, "Linear framebuffer at 0x%lX\n",
	       (unsigned long)pApm->LinAddress);

    if (xf86LoadSubModule(pScrn, "ddc")) {
	xf86LoaderReqSymLists(ddcSymbols, NULL);
	if (xf86LoadSubModule(pScrn, "i2c")) {
	    xf86LoaderReqSymLists(i2cSymbols, NULL);
	    pApm->I2C = TRUE;
	}
    }

    if (pApm->noLinear) {
	/*
	 * TODO not AT3D.
	 * XXX ICI XXX
	 */
	pApm->LinMapSize      =  4 * 1024 * 1024 /* 0x10000 */;
	pApm->FbMapSize       =  4 * 1024 * 1024 /* 0x10000 */;
	if (pApm->Chipset >= AT3D)
	    pApm->LinAddress +=  8 * 1024 * 1024 /* 0xA0000 */;
    }
    else {
	if (pApm->Chipset >= AT3D)
	    pApm->LinMapSize  = 16 * 1024 * 1024;
	else
	    pApm->LinMapSize  =  6 * 1024 * 1024;
	pApm->FbMapSize   =  4 * 1024 * 1024;
    }

    if (xf86LoadSubModule(pScrn, "int10")) {
	void	*ptr;

	xf86LoaderReqSymLists(int10Symbols, NULL);
	xf86DrvMsg(pScrn->scrnIndex,X_INFO,"initializing int10\n");
	ptr = xf86InitInt10(pEnt->index);
	if (ptr)
	    xf86FreeInt10(ptr);
    }

    xf86RegisterResources(pEnt->index, NULL, ResNone);
    xf86SetOperatingState(resVga, pEnt->index, ResDisableOpr);
    pScrn->racMemFlags = 0;	/* For noLinear, access to 0xA0000 */
    if (pApm->VGAMap)
	pScrn->racIoFlags = 0;
    else
	pScrn->racIoFlags = RAC_COLORMAP | RAC_VIEWPORT;

    if (pEnt->device->videoRam != 0) {
	pScrn->videoRam = pEnt->device->videoRam;
	from = X_CONFIG;
    } else if (!pApm->noLinear && pApm->Chipset >= AT3D) {
	unsigned char		d9, db, uc;
	/*unsigned long		save;*/
	volatile unsigned char	*LinMap;

	LinMap = xf86MapPciMem(pScrn->scrnIndex, VIDMEM_MMIO,
				     pApm->PciTag, pApm->LinAddress,
				     pApm->LinMapSize);
	/*save = pciReadLong(pApm->PciTag, PCI_CMD_STAT_REG);
	pciWriteLong(pApm->PciTag, PCI_CMD_STAT_REG, save | PCI_CMD_MEM_ENABLE);*/
	d9 = LinMap[0xFFECD9];
	db = LinMap[0xFFECDB];
	LinMap[0xFFECDB] = (db & 0xF4) | 0x0A;
	LinMap[0xFFECD9] = (d9 & 0xCF) | 0x20;
	LinMap[0xFFF3C4] = 0x1C;
	uc = LinMap[0xFFF3C5];
	LinMap[0xFFF3C5] = 0x3F;
	LinMap[0xFFF3C4] = 0x20;
	pScrn->videoRam = LinMap[0xFFF3C5] * 64;
	LinMap[0xFFF3C4] = 0x10;
	pApm->savedSR10 = LinMap[0xFFF3C5];
	LinMap[0xFFF3C4] = 0x1E;
	pApm->xbase  = LinMap[0xFFF3C5];
	LinMap[0xFFF3C4] = 0x1F;
	pApm->xbase |= LinMap[0xFFF3C5] << 8;
	LinMap[0xFFF3C4] = 0x1C;
	LinMap[0xFFF3C5] = uc;
	LinMap[0xFFECDB] = db;
	LinMap[0xFFECD9] = d9;
	/*pciWriteLong(pApm->PciTag, PCI_CMD_STAT_REG, save);*/
	xf86UnMapVidMem(pScrn->scrnIndex, (pointer)LinMap, pApm->LinMapSize);
	from = X_PROBED;
    }
    else {
	/*unsigned long		save;

	save = pciReadLong(pApm->PciTag, PCI_CMD_STAT_REG);
	pciWriteLong(pApm->PciTag, PCI_CMD_STAT_REG, save | PCI_CMD_IO_ENABLE);*/
	pApm->savedSR10 = rdinx(pApm->xport, 0x10);
	wrinx(pApm->xport, 0x10, 0x12);
	pScrn->videoRam = rdinx(pApm->xport, 0x20) * 64;
	pApm->xbase = rdinx(pApm->xport, 0x1F) << 8;
	pApm->xbase |= rdinx(pApm->xport, 0x1E);
	pApm->xbase += pApm->iobase;
	wrinx(pApm->xport, 0x10, pApm->savedSR10 ? 0 : 0x12);
	/*pciWriteLong(pApm->PciTag, PCI_CMD_STAT_REG, save);*/
	from = X_PROBED;
    }
    if (pApm->Chipset < AT3D && pScrn->videoRam >= 4096)
	pScrn->videoRam -= 32;

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

    if (!xf86IsPc98()) {
	hwp->MapSize = 0x10000;
	vgaHWMapMem(pScrn);
	if (pApm->I2C) {
	    if (!ApmI2CInit(pScrn)) {
		xf86DrvMsg(pScrn->scrnIndex,X_ERROR,"I2C initialization failed\n");
	    }
	    else {
		MonInfo = xf86DoEDID_DDC2(pScrn->scrnIndex,pApm->I2CPtr);
	    }
	}
	if (0 && !MonInfo)
	    MonInfo = xf86DoEDID_DDC1(pScrn->scrnIndex,vgaHWddc1SetSpeed,ddc1Read);
	if (MonInfo)
	    xf86PrintEDID(MonInfo);
	pScrn->monitor->DDC = MonInfo;
    }

    /* The gamma fields must be initialised when using the new cmap code */
    if (pScrn->depth > 1) {
	Gamma zeros = {0.0, 0.0, 0.0};

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

    pApm->MinClock = 23125;
    xf86DrvMsg(pScrn->scrnIndex, X_DEFAULT, "Min pixel clock set to %d MHz\n",
	       pApm->MinClock / 1000);

    /*
     * If the user has specified ramdac speed in the XF86Config
     * file, we respect that setting.
     */
    from = X_DEFAULT;
    if (pEnt->device->dacSpeeds[0]) {
	int speed = 0;

	switch (pScrn->bitsPerPixel) {
	case 4:
	case 8:
	   speed = pEnt->device->dacSpeeds[DAC_BPP8];
	   break;
	case 16:
	   speed = pEnt->device->dacSpeeds[DAC_BPP16];
	   break;
	case 24:
	   speed = pEnt->device->dacSpeeds[DAC_BPP24];
	   break;
	case 32:
	   speed = pEnt->device->dacSpeeds[DAC_BPP32];
	   break;
	}
	if (speed == 0)
	    pApm->MaxClock = pEnt->device->dacSpeeds[0];
	else
	    pApm->MaxClock = speed;
	from = X_CONFIG;
    } else {
	switch(pApm->Chipset)
	{
	  /* These values come from the Manual for AT24 and AT3D 
	     in the overview of various modes. I've taken the largest
	     number for the different modes. Alliance wouldn't 
	     tell me what the maximum frequency was, so...
	   */
	  case AT24:
	       switch(pScrn->bitsPerPixel)
	       {
		 case 4:
		 case 8:
		      pApm->MaxClock = 160000;
		      break;
		 case 16:
		      pApm->MaxClock = 144000;
		      break;
		 case 24:
		      pApm->MaxClock = 75000; /* Hmm. */
		      break;
		 case 32:
		      pApm->MaxClock = 94500;
		      break;
		 default:
		      return FALSE;
	       }
	       break;
	  case AT3D:
	       switch(pScrn->bitsPerPixel)
	       {
		 case 4:
		 case 8:
		      pApm->MaxClock = 175500;
		      break;
		 case 16:
		      pApm->MaxClock = 144000;
		      break;
		 case 24:
		      pApm->MaxClock = 94000; /* Changed from 75000 by Grenié */
		      break;
		 case 32:
		      pApm->MaxClock = 94500;
		      break;
		 default:
		      return FALSE;
	       }
	       break;
	  case AP6422:
	       switch(pScrn->bitsPerPixel)
	       {
		 case 4:
		 case 8:
		      pApm->MaxClock = 135000;
		      break;
		 case 16:
		      pApm->MaxClock = 75000;
		      break;
		 case 32:
		      pApm->MaxClock = 60000;
		      break;
		 default:
		      return FALSE;
	       }
	       break;
	  default:
	       pApm->MaxClock = 135000;
	       break;
	}
    }
    xf86DrvMsg(pScrn->scrnIndex, from, "Max pixel clock is %d MHz\n",
	       pApm->MaxClock / 1000);

    /*
     * Setup the ClockRanges, which describe what clock ranges are available,
     * and what sort of modes they can be used for.
     */
    clockRanges = (ClockRangePtr)xnfcalloc(sizeof(ClockRange), 1);
    clockRanges->next = NULL;
    clockRanges->minClock = pApm->MinClock;
    clockRanges->maxClock = pApm->MaxClock;
    clockRanges->clockIndex = -1;		/* programmable */
    clockRanges->interlaceAllowed = FALSE;	/* XXX change this */
    clockRanges->doubleScanAllowed = FALSE;	/* XXX check this */

    /* Select valid modes from those available */
    if (pApm->NoAccel) {
	/*
	 * XXX Assuming min pitch 256, max 2048
	 * XXX Assuming min height 128, max 1024 (changed EE)
	 */
	i = xf86ValidateModes(pScrn, pScrn->monitor->Modes,
			      pScrn->display->modes, clockRanges,
			      NULL, 256, 2048,
			      pScrn->bitsPerPixel, 128, 1024,
			      pScrn->display->virtualX,
			      pScrn->display->virtualY,
			      pApm->FbMapSize,
			      LOOKUP_BEST_REFRESH);
    } else {
	/*
	 * XXX Assuming min height 128, max 2048
	 */
	i = xf86ValidateModes(pScrn, pScrn->monitor->Modes,
			      pScrn->display->modes, clockRanges,
			      GetAccelPitchValues(pScrn), 0, 0,
			      pScrn->bitsPerPixel, 128, 1024,
			      pScrn->display->virtualX,
			      pScrn->display->virtualY,
			      pApm->FbMapSize,
			      LOOKUP_BEST_REFRESH);
    }

    if (i == -1) {
	ApmFreeRec(pScrn);
	return FALSE;
    }

    /* Prune the modes marked as invalid */
    xf86PruneDriverModes(pScrn);

    if (i == 0 || pScrn->modes == NULL) {
	xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No valid modes found\n");
	ApmFreeRec(pScrn);
	return FALSE;
    }

    xf86SetCrtcForModes(pScrn, INTERLACE_HALVE_V);

    /* Set the current mode to the first in the list */
    pScrn->currentMode = pScrn->modes;

    /* Print the list of modes being used */
    xf86PrintModes(pScrn);

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

    /* Load bpp-specific modules */
    switch (pScrn->bitsPerPixel) {
    case 1:
	mod = "xf1bpp";
	req = "xf1bppScreenInit";
	break;
    case 4:
	mod = "xf4bpp";
	req = "xf4bppScreenInit";
	break;
    case 8:
    case 16:
    case 24:
    case 32:
	mod = "fb";
	break;
    }

    if (mod && xf86LoadSubModule(pScrn, mod) == NULL) {
	ApmFreeRec(pScrn);
	return FALSE;
    }

    if (mod) {
	if (req) {
	    xf86LoaderReqSymbols(req, NULL);
	} else {
	    xf86LoaderReqSymLists(fbSymbols, NULL);
	}
    }

    /* Load XAA if needed */
    if (!pApm->NoAccel) {
	if (!xf86LoadSubModule(pScrn, "xaa")) {
	    ApmFreeRec(pScrn);
	    return FALSE;
	}
	xf86LoaderReqSymLists(xaaSymbols, NULL);
    }

    /* Load ramdac if needed */
    if (pApm->hwCursor) {
	if (!xf86LoadSubModule(pScrn, "ramdac")) {
	    ApmFreeRec(pScrn);
	    return FALSE;
	}
	xf86LoaderReqSymLists(ramdacSymbols, NULL);
    }

    /* Load shadowfb if needed */
    if (pApm->ShadowFB) {
	if (!xf86LoadSubModule(pScrn, "shadowfb")) {
	    ApmFreeRec(pScrn);
	    return FALSE;
	}
	xf86LoaderReqSymLists(shadowSymbols, NULL);
    }

    pApm->CurrentLayout.displayWidth	= pScrn->virtualX;
    pApm->CurrentLayout.displayHeight	= pScrn->virtualY;
    pApm->CurrentLayout.bitsPerPixel	= pScrn->bitsPerPixel;
    pApm->CurrentLayout.bytesPerScanline= (pApm->CurrentLayout.displayWidth * pApm->CurrentLayout.bitsPerPixel) >> 3;
    pApm->CurrentLayout.depth		= pScrn->depth;
    pApm->CurrentLayout.Scanlines	= 2 * (pScrn->videoRam << 10) / pApm->CurrentLayout.bytesPerScanline;
    if (pScrn->bitsPerPixel == 24)
	pApm->CurrentLayout.mask32	= 3;
    else
	pApm->CurrentLayout.mask32	= 32 / pScrn->bitsPerPixel - 1;

    return TRUE;
}

/*
 * Map the framebuffer and MMIO memory.
 */

static Bool
ApmMapMem(ScrnInfoPtr pScrn)
{
    APMDECL(pScrn);
    vgaHWPtr	hwp = VGAHWPTR(pScrn);

    pApm->LinMap = xf86MapPciMem(pScrn->scrnIndex, VIDMEM_FRAMEBUFFER,
				 pApm->PciTag,
				 (unsigned long)pApm->LinAddress,
				 pApm->LinMapSize);
    if (pApm->LinMap == NULL)
	return FALSE;

    if (!pApm->noLinear) {
	if (pApm->Chipset >= AT3D) {
	    pApm->FbBase = (void *)(((char *)pApm->LinMap) + 0x800000);
	    pApm->VGAMap = ((char *)pApm->LinMap) + 0xFFF000;
	    pApm->MemMap = ((char *)pApm->LinMap) + 0xFFEC00;
	    pApm->BltMap = (void *)(((char *)pApm->LinMap) + 0x3F8000);
	}
	else {
	    pApm->FbBase = (void *)pApm->LinMap;
	    pApm->VGAMap = NULL;
	    if (pScrn->videoRam == 6 * 1024 - 32) {
		pApm->MemMap = ((char *)pApm->LinMap) + 0x5FF800;
		pApm->BltMap = (void *)(((char *)pApm->LinMap) + 0x5F8000);
	    }
	    else {
		pApm->MemMap = ((char *)pApm->LinMap) + 0x3FF800;
		pApm->BltMap = (void *)(((char *)pApm->LinMap) + 0x3F8000);
	    }
	}

	/*
	 * Initialize chipset
	 */
	pApm->c9 = RDXB(0xC9);
	if (pApm->Chipset >= AT3D) {
	    pApm->d9 = RDXB(0xD9);
	    pApm->db = RDXB(0xDB);

	    /* If you change these two, change them also in apm_funcs.c */
	    WRXB(0xDB, (pApm->db & 0xF4) | 0x0A);
	    WRXB(0xD9, (pApm->d9 & 0xCF) | 0x20);

	    vgaHWSetMmioFuncs(hwp, (CARD8 *)pApm->LinMap, 0xFFF000);
	}
	if (pApm->Chipset >= AP6422)
	    WRXB(0xC9, pApm->c9 | 0x10);
    }
    else {
	pApm->FbBase = pApm->LinMap;

	/*
	 * Initialize chipset
	 */
	if (pApm->Chipset >= AT3D) {
	    pApm->d9 = RDXB_IOP(0xD9);
	    pApm->db = RDXB_IOP(0xDB);
	    WRXB_IOP(0xDB, pApm->db & 0xF4);
	}
    }
    /*
     * Save color mode
     */
    pApm->MiscOut = hwp->readMiscOut(hwp);

    return TRUE;
}

/*
 * Unmap the framebuffer and MMIO memory
 */

static Bool
ApmUnmapMem(ScrnInfoPtr pScrn)
{
    APMDECL(pScrn);
    vgaHWPtr	hwp = VGAHWPTR(pScrn);

    /*
     * Reset color mode
     */
    hwp->writeMiscOut(hwp, pApm->MiscOut);
    if (pApm->LinMap) {
	if (pApm->Chipset >= AT3D) {
	    if (!pApm->noLinear) {
		WRXB(0xD9, pApm->d9);
		WRXB(0xDB, pApm->db);
	    }
	    else {
		WRXB_IOP(0xD9, pApm->d9);
		WRXB_IOP(0xDB, pApm->db);
	    }
	}
	WRXB(0xC9, pApm->c9);
	xf86UnMapVidMem(pScrn->scrnIndex, (pointer)pApm->LinMap, pApm->LinMapSize);
	pApm->LinMap = NULL;
    }
    else if (pApm->FbBase)
	xf86UnMapVidMem(pScrn->scrnIndex, (pointer)pApm->LinMap, 0x10000);

    return TRUE;
}

/*
 * This function saves the video state.
 */
static void
ApmSave(ScrnInfoPtr pScrn)
{
    APMDECL(pScrn);
    ApmRegPtr	ApmReg = &pApm->SavedReg;
    vgaHWPtr	hwp    = VGAHWPTR(pScrn);

    if (pApm->VGAMap) {
	ApmReg->SEQ[0x1B] = ApmReadSeq(0x1B);
	ApmReg->SEQ[0x1C] = ApmReadSeq(0x1C);

	/*
	 * Save fonts
	 */
	if (!(hwp->SavedReg.Attribute[0x10] & 1)) {
	    if (pApm->FontInfo || (pApm->FontInfo = (pointer)xalloc(TEXT_AMOUNT))) {
		int locked;

		locked = ApmReadSeq(0x10);
		if (locked)
		    ApmWriteSeq(0x10, 0x12);
		ApmWriteSeq(0x1C, 0x3F);
		memcpy(pApm->FontInfo, pApm->FbBase, TEXT_AMOUNT);
		ApmWriteSeq(0x1C, ApmReg->SEQ[0x1C]);
		if (locked)
		    ApmWriteSeq(0x10, 0);
	    }
	}
	/*
	 * This function will handle creating the data structure and filling
	 * in the generic VGA portion.
	 */
	vgaHWSave(pScrn, &hwp->SavedReg, VGA_SR_MODE | VGA_SR_CMAP);

	/* Hardware cursor registers. */
	ApmReg->EX[XR140] = RDXL(0x140);
	ApmReg->EX[XR144] = RDXW(0x144);
	ApmReg->EX[XR148] = RDXL(0x148);
	ApmReg->EX[XR14C] = RDXW(0x14C);

	ApmReg->CRT[0x19] = ApmReadCrtc(0x19);
	ApmReg->CRT[0x1A] = ApmReadCrtc(0x1A);
	ApmReg->CRT[0x1B] = ApmReadCrtc(0x1B);
	ApmReg->CRT[0x1C] = ApmReadCrtc(0x1C);
	ApmReg->CRT[0x1D] = ApmReadCrtc(0x1D);
	ApmReg->CRT[0x1E] = ApmReadCrtc(0x1E);

	/* RAMDAC registers. */
	ApmReg->EX[XRE8] = RDXL(0xE8);
	ApmReg->EX[XREC] = RDXL(0xEC);

	/* Color correction */
	ApmReg->EX[XRE0] = RDXL(0xE0);

	ApmReg->EX[XR80] = RDXB(0x80);
    }
    else {
	/*
	 * This function will handle creating the data structure and filling
	 * in the generic VGA portion.
	 */
	vgaHWSave(pScrn, &hwp->SavedReg, VGA_SR_ALL);

	ApmReg->SEQ[0x1B] = rdinx(pApm->xport, 0x1B);
	ApmReg->SEQ[0x1C] = rdinx(pApm->xport, 0x1C);

	/* Hardware cursor registers. */
	if (pApm->noLinear) {
	    ApmReg->EX[XR140] = RDXL_IOP(0x140);
	    ApmReg->EX[XR144] = RDXW_IOP(0x144);
	    ApmReg->EX[XR148] = RDXL_IOP(0x148);
	    ApmReg->EX[XR14C] = RDXW_IOP(0x14C);
	}
	else {
	    ApmReg->EX[XR140] = RDXL(0x140);
	    ApmReg->EX[XR144] = RDXW(0x144);
	    ApmReg->EX[XR148] = RDXL(0x148);
	    ApmReg->EX[XR14C] = RDXW(0x14C);
	}

	ApmReg->CRT[0x19] = rdinx(pApm->iobase + 0x3D4, 0x19);
	ApmReg->CRT[0x1A] = rdinx(pApm->iobase + 0x3D4, 0x1A);
	ApmReg->CRT[0x1B] = rdinx(pApm->iobase + 0x3D4, 0x1B);
	ApmReg->CRT[0x1C] = rdinx(pApm->iobase + 0x3D4, 0x1C);
	ApmReg->CRT[0x1D] = rdinx(pApm->iobase + 0x3D4, 0x1D);
	ApmReg->CRT[0x1E] = rdinx(pApm->iobase + 0x3D4, 0x1E);

	if (pApm->noLinear) {
	    /* RAMDAC registers. */
	    ApmReg->EX[XRE8] = RDXL_IOP(0xE8);
	    ApmReg->EX[XREC] = RDXL_IOP(0xEC);

	    /* Color correction */
	    ApmReg->EX[XRE0] = RDXL_IOP(0xE0);

	    ApmReg->EX[XR80] = RDXB_IOP(0x80);
	}
	else {
	    /* RAMDAC registers. */
	    ApmReg->EX[XRE8] = RDXL(0xE8);
	    ApmReg->EX[XREC] = RDXL(0xEC);

	    /* Color correction */
	    ApmReg->EX[XRE0] = RDXL(0xE0);

	    ApmReg->EX[XR80] = RDXB(0x80);
	}
    }
}

#define WITHIN(v,c1,c2) (((v) >= (c1)) && ((v) <= (c2)))

static unsigned
comp_lmn(ApmPtr pApm, long clock)
{
  int     n, m, l, f;
  double  fvco;
  double  fout;
  double  fmax, fmin;
  double  fref;
  double  fvco_goal;
  double  k, c;
  double fout_best = 0;
  unsigned int best = 0;
  
  if (pApm->Chipset >= AT3D)
    fmax = 370000.0;
  else
    fmax = 250000.0;

  fref = 14318.0;
  fmin = fmax / 2.0;

  for (m = 1; m <= 5; m++)
  {
    for (l = 3; l >= 0; l--)
    {
      for (n = 8; n <= 127; n++)
      {
        fout = ((double)(n + 1) * fref)/((double)(m + 1) * (1 << l));
        fvco_goal = (double)clock * (double)(1 << l);
        fvco = fout * (double)(1 << l);
        if (!WITHIN(fvco, 0.99*fvco_goal, 1.01*fvco_goal))
          continue;
        if (!WITHIN(fvco, fmin, fmax))
          continue;
        if (!WITHIN(fvco / (double)(n+1), 300.0, 300000.0))
          continue;
        if (!WITHIN(fref / (double)(m+1), 300.0, 300000.0))
          continue;

	if (fout_best != 0) {
	    double diff_new = clock - fout;
	    double diff_old = clock - best;
	    diff_new = diff_new < 0 ? -diff_new : diff_new;
	    diff_old = diff_old < 0 ? -diff_old : diff_old;
	    if (diff_new > diff_old)
		continue;
	}
	fout_best = fout;
	
        /* The following formula was empirically derived by
           matching a number of fvco values with acceptable
           values of f.

           (fvco can be 185MHz - 370MHz on AT3D)
           (fvco can be 125MHz - 250MHz on AT24/AP6422)

           The table that was measured up follows:

           AT3D

           fvco       f
           (125)     (x-7) guess
           200       5-7
           219       4-7
           253       3-6
           289       2-5
           320       0-4
           (400)     (0-x) guess

           AT24

           fvco       f
           126       7
           200       5-7
           211       4-7

           AP6422

           fvco       f
           126       7
           169       5-7
           200       4-5
           211       4-5

           From this, a function "f = k * fvco + c" was derived.

           For AT3D, this table was measured with MCLK == 50MHz.
           The driver has since been set to use MCLK == 57.3MHz for,
           but I don't think that makes a difference here.
         */

        if (pApm->Chipset >= AT24)
        {
          k = 7.0 / (175.0 - 380.0);
          c = -k * 380.0;
          f = (int)(k * fvco/1000.0 + c + 0.5);
          if (f > 7) f = 7;
          if (f < 0) f = 0;
        } else { /* i.e AP6422 */
          c = (211.0*6.0-169.0*4.5)/(211.0-169.0);
          k = (4.5-c)/211.0;
          f = (int)(k * fvco/1000.0 + c + 0.5);
          if (f > 7) f = 7;
          if (f < 0) f = 0;
        }
	
        best =  (n << 16) | (m << 8) | (l << 2) | (f << 4);
      }
    }
  }
  
  if (fout_best != 0)
      return best;
  
  xf86DrvMsg(pApm->scrnIndex, X_PROBED,
		"Cannot find register values for clock %6.2f MHz. "
		"Please use a (slightly) different clock.\n",
		 (double)clock / 1000.0);
  return 0;
}

/*
 * Initialise a new mode.  This is currently still using the old
 * "initialise struct, restore/write struct to HW" model.  That could
 * be changed.
 */

static Bool
ApmModeInit(ScrnInfoPtr pScrn, DisplayModePtr mode)
{
    APMDECL(pScrn);
    ApmRegPtr	ApmReg = &pApm->ModeReg;
    vgaHWPtr	hwp;


    /* set clockIndex to "2" for programmable clocks */
    if (pScrn->progClock)
	mode->ClockIndex = 2;

    /* prepare standard VGA register contents */
    if (!vgaHWInit(pScrn, mode))
	return FALSE;
    pScrn->vtSema = TRUE;
    hwp = VGAHWPTR(pScrn);

    hwp->writeMiscOut(hwp, pApm->MiscOut | 0x0F);

    if (xf86IsPc98())
       outb(0xFAC, 0xFF);

    memcpy(ApmReg, &pApm->SavedReg, sizeof pApm->SavedReg);

    /*
     * The APM chips have a scale factor of 8 for the
     * scanline offset. There are four extended bit in addition
     * to the 8 VGA bits.
     */
    {
	int offset;

	offset = (pApm->CurrentLayout.displayWidth *
		  pApm->CurrentLayout.bitsPerPixel / 8)	>> 3;
	hwp->ModeReg.CRTC[0x13] = offset;
	/* Bit 8 resides at CR1C bits 7:4. */
	ApmReg->CRT[0x1C] = (offset & 0xF00) >> 4;
    }

    /* Set pixel depth. */
    switch(pApm->CurrentLayout.bitsPerPixel)
    {
    case 4:
	 ApmReg->EX[XR80] = 0x01;
	 break;
    case 8:
	 ApmReg->EX[XR80] = 0x02;
	 break;
    case 16:
	 if (pApm->CurrentLayout.depth == 15)
	     ApmReg->EX[XR80] = 0x0C;
	 else
	     ApmReg->EX[XR80] = 0x0D;
	 break;
    case 24:
	 ApmReg->EX[XR80] = 0x0E;
	 break;
    case 32:
	 ApmReg->EX[XR80] = 0x0F;
	 break;
    default:
	 FatalError("Unsupported bit depth %d\n", pApm->CurrentLayout.depth);
	 break;
    }

    /* Set banking register to zero. */
    ApmReg->EX[XRC0] = 0;

    /* Handle the CRTC overflow bits. */
    {
	unsigned char val;
	/* Vertical Overflow. */
	val = 0;
	if ((mode->CrtcVTotal - 2) & 0x400)
	    val |= 0x01;
	if ((mode->CrtcVDisplay - 1) & 0x400)
	    val |= 0x02;
	/* VBlankStart is equal to VSyncStart + 1. */
	if (mode->CrtcVSyncStart & 0x400)
	    val |= 0x04;
	/* VRetraceStart is equal to VSyncStart + 1. */
	if (mode->CrtcVSyncStart & 0x400)
	    val |= 0x08;
	ApmReg->CRT[0x1A] = val;

	/* Horizontal Overflow. */
	val = 0;
	if ((mode->CrtcHTotal / 8 - 5) & 0x100)
	    val |= 1;
	if ((mode->CrtcHDisplay / 8 - 1) & 0x100)
	    val |= 2;
	/* HBlankStart is equal to HSyncStart - 1. */
	if ((mode->CrtcHSyncStart / 8 - 1) & 0x100)
	    val |= 4;
	/* HRetraceStart is equal to HSyncStart. */
	if ((mode->CrtcHSyncStart / 8) & 0x100)
	    val |= 8;
	ApmReg->CRT[0x1B] = val;

	/* Assume the CRTC is not KGA (see vgaHWInit) */
	hwp->ModeReg.CRTC[3] = (hwp->ModeReg.CRTC[3] & 0xE0) |
				(((mode->CrtcHBlankEnd >> 3) - 1) & 0x1F);
	hwp->ModeReg.CRTC[5]  = ((((mode->CrtcHBlankEnd >> 3) - 1) & 0x20) << 2)
				| (hwp->ModeReg.CRTC[5] & 0x7F);
	hwp->ModeReg.CRTC[22] = (mode->CrtcVBlankEnd - 1) & 0xFF;
    }
    ApmReg->CRT[0x1E] = 1;          /* disable autoreset feature */

    /* Program clock select. */
    ApmReg->EX[XREC] = comp_lmn(pApm, mode->Clock);
    if (!ApmReg->EX[XREC])
      return FALSE;
    hwp->ModeReg.MiscOutReg |= 0x0C;

    /* Set up the RAMDAC registers. */

    if (pApm->CurrentLayout.bitsPerPixel > 8)
	/* Get rid of white border. */
	hwp->ModeReg.Attribute[0x11] = 0x00;
    else
	hwp->ModeReg.Attribute[0x11] = 0xFF;
    if (pApm->MemClk)
	ApmReg->EX[XRE8] = comp_lmn(pApm, pApm->MemClk);
    else if (pApm->Chipset >= AT3D)
	ApmReg->EX[XRE8] = 0x071F01E8; /* Enable 58MHz MCLK (actually 57.3 MHz)
				       This is what is used in the Windows
				       drivers. The BIOS sets it to 50MHz. */
    else if (!pApm->noLinear)
	ApmReg->EX[XRE8] = RDXL(0xE8); /* No change */
    else
	ApmReg->EX[XRE8] = RDXL_IOP(0xE8); /* No change */

    ApmReg->EX[XRE0] = 0x10;

    /* If you change it, change in apm_funcs.c as well */
    if (pApm->Chipset >= AT3D) {
	ApmReg->SEQ[0x1B] = 0x20;
	ApmReg->SEQ[0x1C] = 0x2F;
    }
    else {
	ApmReg->SEQ[0x1B] = 0x24;
	if (pScrn->videoRam >= 6 * 1024)
	    ApmReg->SEQ[0x1C] = 0x2F;
	else
	    ApmReg->SEQ[0x1C] = 0x2D;
    }

    /* ICICICICI */
    ApmRestore(pScrn, &hwp->ModeReg, ApmReg);

    return TRUE;
}

/*
 * Restore the initial mode.
 */
static void
ApmRestore(ScrnInfoPtr pScrn, vgaRegPtr vgaReg, ApmRegPtr ApmReg)
{
    APMDECL(pScrn);

    vgaHWProtect(pScrn, TRUE);
    ApmUnlock(pApm);

    if (pApm->VGAMap) {
	/*
	 * Restore fonts
	 */
	if (!(vgaReg->Attribute[0x10] & 1) && pApm->FontInfo) {
	    ApmWriteSeq(0x1C, 0x3F);
	    memcpy(pApm->FbBase, pApm->FontInfo, TEXT_AMOUNT);
	}

	/* Set aperture index to 0. */
	WRXW(0xC0, 0);

	/*
	 * Write the extended registers first
	 */
	ApmWriteSeq(0x1B, ApmReg->SEQ[0x1B]);
	ApmWriteSeq(0x1C, ApmReg->SEQ[0x1C]);

	/* Hardware cursor registers. */
	WRXL(0x140, ApmReg->EX[XR140]);
	WRXW(0x144, ApmReg->EX[XR144]);
	WRXL(0x148, ApmReg->EX[XR148]);
	WRXW(0x14C, ApmReg->EX[XR14C]);

	ApmWriteCrtc(0x19, ApmReg->CRT[0x19]);
	ApmWriteCrtc(0x1A, ApmReg->CRT[0x1A]);
	ApmWriteCrtc(0x1B, ApmReg->CRT[0x1B]);
	ApmWriteCrtc(0x1D, ApmReg->CRT[0x1D]);
	ApmWriteCrtc(0x1E, ApmReg->CRT[0x1E]);

	/* RAMDAC registers. */
	WRXL(0xE8, ApmReg->EX[XRE8]);

	WRXL(0xEC, ApmReg->EX[XREC] & ~(1 << 7));
	WRXL(0xEC, ApmReg->EX[XREC] | (1 << 7)); /* Do a PLL resync */

	/* Color correction */
	WRXL(0xE0, ApmReg->EX[XRE0]);

	/*
	 * This function handles restoring the generic VGA registers.
	 */
	vgaHWRestore(pScrn, vgaReg, VGA_SR_MODE | VGA_SR_CMAP);

	/* set these after setting the default VGA registers */
	ApmWriteCrtc(0x1C, ApmReg->CRT[0x1C]);
	WRXB(0x80, ApmReg->EX[XR80]);
    }
    else {
	/* Set aperture index to 0. */
	if (pApm->noLinear)
	    WRXW_IOP(0xC0, 0);
	else
	    WRXW(0xC0, 0);

	/*
	 * Write the extended registers first
	 */
	wrinx(pApm->xport, 0x1B, ApmReg->SEQ[0x1B]);
	wrinx(pApm->xport, 0x1C, ApmReg->SEQ[0x1C]);

	/* Hardware cursor registers. */
	if (pApm->noLinear) {
	    WRXL_IOP(0x140, ApmReg->EX[XR140]);
	    WRXW_IOP(0x144, ApmReg->EX[XR144]);
	    WRXL_IOP(0x148, ApmReg->EX[XR148]);
	    WRXW_IOP(0x14C, ApmReg->EX[XR14C]);
	}
	else {
	    WRXL(0x140, ApmReg->EX[XR140]);
	    WRXW(0x144, ApmReg->EX[XR144]);
	    WRXL(0x148, ApmReg->EX[XR148]);
	    WRXW(0x14C, ApmReg->EX[XR14C]);
	}

	wrinx(pApm->iobase + 0x3D4, 0x19, ApmReg->CRT[0x19]);
	wrinx(pApm->iobase + 0x3D4, 0x1A, ApmReg->CRT[0x1A]);
	wrinx(pApm->iobase + 0x3D4, 0x1B, ApmReg->CRT[0x1B]);
	wrinx(pApm->iobase + 0x3D4, 0x1C, ApmReg->CRT[0x1C]);
	wrinx(pApm->iobase + 0x3D4, 0x1D, ApmReg->CRT[0x1D]);
	wrinx(pApm->iobase + 0x3D4, 0x1E, ApmReg->CRT[0x1E]);

	/* RAMDAC registers. */
	if (pApm->noLinear) {
	    WRXL_IOP(0xE8, ApmReg->EX[XRE8]);
	    WRXL_IOP(0xEC, ApmReg->EX[XREC] & ~(1 << 7));
	    WRXL_IOP(0xEC, ApmReg->EX[XREC] | (1 << 7)); /* Do a PLL resync */
	}
	else {
	    WRXL(0xE8, ApmReg->EX[XRE8]);
	    WRXL(0xEC, ApmReg->EX[XREC] & ~(1 << 7));
	    WRXL(0xEC, ApmReg->EX[XREC] | (1 << 7)); /* Do a PLL resync */
	}

	/* Color correction */
	if (pApm->noLinear)
	    WRXL_IOP(0xE0, ApmReg->EX[XRE0]);
	else
	    WRXL(0xE0, ApmReg->EX[XRE0]);

	if (pApm->noLinear)
	    WRXB_IOP(0x80, ApmReg->EX[XR80]);
	else
	    WRXB(0x80, ApmReg->EX[XR80]);

	/*
	 * This function handles restoring the generic VGA registers.
	 */
	vgaHWRestore(pScrn, vgaReg, VGA_SR_ALL);
    }

    vgaHWProtect(pScrn, FALSE);
}


/* Refresh a region of the shadow framebuffer to the screen */
static void
ApmRefreshArea(ScrnInfoPtr pScrn, int num, BoxPtr pbox)
{
    APMDECL(pScrn);
    int width, height, Bpp, FBPitch;
    unsigned char *src, *dst;

    Bpp = pApm->CurrentLayout.bitsPerPixel >> 3;
    FBPitch = pApm->CurrentLayout.bytesPerScanline;

    while(num--) {
	width = (pbox->x2 - pbox->x1) * Bpp;
	height = pbox->y2 - pbox->y1;
	src = pApm->ShadowPtr + (pbox->y1 * pApm->ShadowPitch) +
						(pbox->x1 * Bpp);
	dst = (unsigned char *)pApm->FbBase + (pbox->y1 * FBPitch) + (pbox->x1 * Bpp);

	while(height--) {
	    memcpy(dst, src, width);
	    dst += FBPitch;
	    src += pApm->ShadowPitch;
	}

	pbox++;
    }
}


/* Mandatory */

/* This gets called at the start of each server generation */

static Bool
ApmScreenInit(int scrnIndex, ScreenPtr pScreen, int argc, char **argv)
{
    ScrnInfoPtr		pScrn = xf86Screens[pScreen->myNum];
    APMDECL(pScrn);
    int			ret;
    unsigned char	*FbBase;

    pApm->pScreen = pScreen;

    /* Map the chip memory and MMIO areas */
    if (pApm->noLinear) {
	pApm->saveCmd = pciReadLong(pApm->PciTag, PCI_CMD_STAT_REG);
	pciWriteLong(pApm->PciTag, PCI_CMD_STAT_REG, pApm->saveCmd | (PCI_CMD_IO_ENABLE|PCI_CMD_MEM_ENABLE));
	pApm->FbBase = xf86MapPciMem(pScrn->scrnIndex, VIDMEM_FRAMEBUFFER,
				 pApm->PciTag, 0xA0000, 0x10000);
    }
    else
	if (!ApmMapMem(pScrn))
	    return FALSE;

    /* No memory reserved yet */
    pApm->OffscreenReserved = 0;

    /* Save the current state */
    ApmSave(pScrn);

    /* Initialise the first mode */
    ApmModeInit(pScrn, pScrn->currentMode);
    pApm->CurrentLayout.pMode = pScrn->currentMode;

    /* Darken the screen for aesthetic reasons and set the viewport */
    ApmSaveScreen(pScreen, SCREEN_SAVER_ON);
    ApmAdjustFrame(scrnIndex, pScrn->frameX0, pScrn->frameY0, 0);

    /*
     * Reset fb's visual list.
     */
    miClearVisualTypes();

    /* Setup the visuals we support. */

    /*
     * For bpp > 8, the default visuals are not acceptable because we only
     * support TrueColor and not DirectColor.  To deal with this, call
     * miSetVisualTypes for each visual supported.
     */

    if (pApm->CurrentLayout.bitsPerPixel > 8) {
	if (!miSetVisualTypes(pScrn->depth, TrueColorMask, pScrn->rgbBits,
				pScrn->defaultVisual))
	    return FALSE;
    } else {
	if (!miSetVisualTypes(pScrn->depth,
			      miGetDefaultVisualMask(pScrn->depth),
			      pScrn->rgbBits, pScrn->defaultVisual))
	    return FALSE;
    }

    /*
     * Call the framebuffer layer's ScreenInit function, and fill in other
     * pScreen fields.
     */

    if(pApm->ShadowFB) {
	pApm->ShadowPitch =
		((pScrn->virtualX * pScrn->bitsPerPixel >> 3) + 3) & ~3L;
	pApm->ShadowPtr = xalloc(pApm->ShadowPitch * pScrn->virtualY);
	FbBase = pApm->ShadowPtr;
    } else {
	pApm->ShadowPtr = NULL;
	FbBase = pApm->FbBase;
    }

    /* Reserve memory */
    ApmHWCursorReserveSpace(pApm);
    ApmAccelReserveSpace(pApm);

    miSetPixmapDepths();

    switch (pScrn->bitsPerPixel) {
    case 1:
	ret = xf1bppScreenInit(pScreen, FbBase,
			pScrn->virtualX, pScrn->virtualY,
			pScrn->xDpi, pScrn->yDpi,
			pScrn->displayWidth);
	break;
    case 4:
	ret = xf4bppScreenInit(pScreen, FbBase,
			pScrn->virtualX, pScrn->virtualY,
			pScrn->xDpi, pScrn->yDpi,
			pScrn->displayWidth);
	break;
    case 8:
    case 16:
    case 24:
    case 32:
	ret = fbScreenInit(pScreen, FbBase, pScrn->virtualX,
	    pScrn->virtualY, pScrn->xDpi, pScrn->yDpi,
	    pScrn->displayWidth, pScrn->bitsPerPixel);
	break;
    default:
	xf86DrvMsg(scrnIndex, X_ERROR,
	    "Internal error: invalid bpp (%d) in ApmScrnInit\n",
	    pScrn->bitsPerPixel);
	ret = FALSE;
	break;
    }
    if (!ret)
	return FALSE;

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

	/* Fixup RGB ordering */
	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 visual RGB order fixed */
    if (pScrn->bitsPerPixel > 4)
	fbPictureInit(pScreen, 0, 0);
    
    xf86SetBlackWhitePixels(pScreen);

    if (!pApm->ShadowFB) {       /* hardware cursor needs to wrap this layer */
	if(!ApmDGAInit(pScreen)) {
	    xf86DrvMsg(pScrn->scrnIndex,X_ERROR,"DGA initialization failed\n");
	}
    }

    /*
     * Initialize the acceleration interface.
     */
    if (!pApm->NoAccel) {
	ApmAccelInit(pScreen);		/* set up XAA interface */
    }

    miInitializeBackingStore(pScreen);
    xf86SetBackingStore(pScreen);
    xf86SetSilkenMouse(pScreen);

    /* Initialise cursor functions */
    miDCInitialize (pScreen, xf86GetPointerScreenFuncs());

    /* Initialize HW cursor layer (after DGA and SW cursor) */
    if (pApm->hwCursor) {
	if (!ApmHWCursorInit(pScreen))
            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
                "Hardware cursor initialization failed\n");
    }


    /* Initialise default colourmap */
    if (!miCreateDefColormap(pScreen))
	return FALSE;

    /*
     * Initialize colormap layer.
     * Must follow initialization of the default colormap.
     */
    if (!xf86HandleColormaps(pScreen, 256, 8, ApmLoadPalette, NULL,
				CMAP_RELOAD_ON_MODE_SWITCH)) {
	xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Colormap initialization failed\n");
	return FALSE;
    }

    if (pApm->ShadowFB)
	ShadowFBInit(pScreen, ApmRefreshArea);

    xf86DPMSInit(pScreen, ApmDisplayPowerManagementSet, 0);

    if (pApm->noLinear)
	ApmInitVideo_IOP(pScreen);
    else
	ApmInitVideo(pScreen);

    pScreen->SaveScreen  = ApmSaveScreen;

    pApm->CloseScreen = pScreen->CloseScreen;
    pScreen->CloseScreen = ApmCloseScreen;

    pScrn->memPhysBase = pApm->LinAddress;
    pScrn->fbOffset = (((char *)pApm->FbBase) - ((char *)pApm->LinMap));

    /* Report any unused options (only for the first generation) */
    if (serverGeneration == 1) {
	xf86ShowUnusedOptions(pScrn->scrnIndex, pScrn->options);
    }

    if (ApmGeneration != serverGeneration) {
	if ((ApmPixmapIndex = AllocatePixmapPrivateIndex()) < 0)
	    return FALSE;
	ApmGeneration = serverGeneration;
    }

    if (!AllocatePixmapPrivate(pScreen, ApmPixmapIndex, sizeof(ApmPixmapRec)))
	return FALSE;

    /* Done */
    return TRUE;
}

/* mandatory */
static void
ApmLoadPalette(ScrnInfoPtr pScrn, int numColors, int *indices, LOCO *colors,
	       VisualPtr pVisual)
{
    APMDECL(pScrn);
    int i, index, last = -1;

    if (pApm->VGAMap) {
	for (i = 0; i < numColors; i++) {
	    index = indices[i];
	    if (index != last)
		ApmWriteDacWriteAddr(index);
	    last = index + 1;
	    ApmWriteDacData(colors[index].red);
	    ApmWriteDacData(colors[index].green);
	    ApmWriteDacData(colors[index].blue);
	}
    }
    else {
	for (i = 0; i < numColors; i++) {
	    index = indices[i];
	    if (index != last) 
		outb(pApm->iobase + 0x3C8, index);
	    last = index + 1;
	    outb(pApm->iobase + 0x3C9, colors[index].red);
	    outb(pApm->iobase + 0x3C9, colors[index].green);
	    outb(pApm->iobase + 0x3C9, colors[index].blue);
	}
    }
}

/* Usually mandatory */
Bool
ApmSwitchMode(int scrnIndex, DisplayModePtr mode, int flags)
{
    return ApmModeInit(xf86Screens[scrnIndex], mode);
}

/*
 * This function is used to initialize the Start Address - the first
 * displayed location in the video memory.
 */
/* Usually mandatory */
void
ApmAdjustFrame(int scrnIndex, int x, int y, int flags)
{
    ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
    APMDECL(pScrn);
    int Base;

    if (pApm->CurrentLayout.bitsPerPixel == 24)
	x = (x + 3) & ~3;
    Base = ((y * pApm->CurrentLayout.displayWidth + x) * (pApm->CurrentLayout.bitsPerPixel / 8)) >> 2;
    /*
     * These are the generic starting address registers.
     */
    if (pApm->VGAMap) {
	ApmWriteCrtc(0x0C, Base >> 8);
	ApmWriteCrtc(0x0D, Base);

	/*
	 * Here the high-order bits are masked and shifted, and put into
	 * the appropriate extended registers.
	 */
	ApmWriteCrtc(0x1C, (ApmReadCrtc(0x1C) & 0xF0) | ((Base & 0x0F0000) >> 16));
    }
    else {
	outw(pApm->iobase + 0x3D4, (Base & 0x00FF00) | 0x0C);
	outw(pApm->iobase + 0x3D4, ((Base & 0x00FF) << 8) | 0x0D);

	/*
	 * Here the high-order bits are masked and shifted, and put into
	 * the appropriate extended registers.
	 */
	modinx(pApm->iobase + 0x3D4, 0x1C, 0x0F, (Base & 0x0F0000) >> 16);
    }
}

/*
 * This is called when VT switching back to the X server.  Its job is
 * to reinitialise the video mode.
 *
 * We may wish to unmap video/MMIO memory too.
 */

/* Mandatory */
static Bool
ApmEnterVT(int scrnIndex, int flags)
{
    ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
    APMDECL(pScrn);
    vgaHWPtr	hwp = VGAHWPTR(pScrn);

    if (pApm->Chipset >= AT3D) {
	if (!pApm->noLinear) {
	    /* If you change it, change it also in apm_funcs.c */
	    WRXB(0xDB, (pApm->db & 0xF4) | 0x0A | pApm->Rush);
	    WRXB(0xD9, (pApm->d9 & 0xCF) | 0x20);
	}
	else {
	    WRXB_IOP(0xDB, pApm->db & 0xF4);
	}
    }
    if (pApm->Chipset >= AP6422)
	WRXB(0xC9, pApm->c9 | 0x10);
    ApmUnlock(APMPTR(pScrn));
    vgaHWUnlock(hwp);
    /*
     * Set color mode
     */
    hwp->writeMiscOut(hwp, pApm->MiscOut | 0x0F);

    if (!ApmModeInit(pScrn, pScrn->currentMode))
	return FALSE;
    ApmAdjustFrame(scrnIndex, pScrn->frameX0, pScrn->frameY0, 0);

    return TRUE;
}

/* Mandatory */
static void
ApmLeaveVT(int scrnIndex, int flags)
{
    ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
    APMDECL(pScrn);
    vgaHWPtr	hwp = VGAHWPTR(pScrn);

    ApmRestore(pScrn, &hwp->SavedReg, &pApm->SavedReg);
    /*
     * Reset color mode
     */
    (*hwp->writeMiscOut)(hwp, pApm->MiscOut);
    vgaHWLock(hwp);
    ApmLock(pApm);
    if (pApm->Chipset >= AT3D) {
	if (!pApm->noLinear) {
	    WRXB(0xD9, pApm->d9);
	    WRXB(0xDB, pApm->db);
	}
	else {
	    WRXB_IOP(0xD9, pApm->d9);
	    WRXB_IOP(0xDB, pApm->db);
	}
    }
    WRXB(0xC9, pApm->c9);

    if (xf86IsPc98())
	outb(0xFAC, 0xFE);
}

/*
 * This is called at the end of each server generation.  It restores the
 * original (text) mode.  It should really also unmap the video memory too.
 */

/* Mandatory */
static Bool
ApmCloseScreen(int scrnIndex, ScreenPtr pScreen)
{
    ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
    vgaHWPtr	hwp = VGAHWPTR(pScrn);
    APMDECL(pScrn);

    if (pScrn->vtSema) {
	ApmRestore(pScrn, &hwp->SavedReg, &pApm->SavedReg);
	vgaHWLock(hwp);
	ApmUnmapMem(pScrn);
    }
    if(pApm->AccelInfoRec)
	XAADestroyInfoRec(pApm->AccelInfoRec);
    if(pApm->DGAXAAInfo)
	XAADestroyInfoRec(pApm->DGAXAAInfo);
    pApm->AccelInfoRec = NULL;
    if(pApm->CursorInfoRec)
	xf86DestroyCursorInfoRec(pApm->CursorInfoRec);
    pApm->CursorInfoRec = NULL;
    if (pApm->DGAModes)
	xfree(pApm->DGAModes);
    if (pApm->adaptor)
	xfree(pApm->adaptor);

    pScrn->vtSema = FALSE;

    if (xf86IsPc98())
	outb(0xFAC, 0xFE);

    pScreen->CloseScreen = pApm->CloseScreen;
    return (*pScreen->CloseScreen)(scrnIndex, pScreen);
}

/* Free up any per-generation data structures */

/* Optional */
static void
ApmFreeScreen(int scrnIndex, int flags)
{
    vgaHWFreeHWRec(xf86Screens[scrnIndex]);
    ApmFreeRec(xf86Screens[scrnIndex]);
}

/* Checks if a mode is suitable for the selected chipset. */

/* Optional */
static ModeStatus
ApmValidMode(int scrnIndex, DisplayModePtr mode, Bool verbose, int flags)
{
    if (mode->Flags & V_INTERLACE)
	return(MODE_BAD);

    return(MODE_OK);
}


/*
 * ApmDisplayPowerManagementSet --
 *
 * Sets VESA Display Power Management Signaling (DPMS) Mode.
 */
static void
ApmDisplayPowerManagementSet(ScrnInfoPtr pScrn, int PowerManagementMode,
			     int flags)
{
    APMDECL(pScrn);
    unsigned char dpmsreg, tmp;

    if (PowerManagementMode < sizeof pApm->DPMSMask &&
	    PowerManagementMode >= 0)
	PowerManagementMode = pApm->DPMSMask[PowerManagementMode];
    switch (PowerManagementMode)
    {
    case DPMSModeOn:
	/* Screen: On; HSync: On, VSync: On */
	dpmsreg = 0x00;
	break;
    case DPMSModeStandby:
	/* Screen: Off; HSync: Off, VSync: On */
	dpmsreg = 0x01;
	break;
    case DPMSModeSuspend:
	/* Screen: Off; HSync: On, VSync: Off */
	dpmsreg = 0x02;
	break;
    case DPMSModeOff:
	/* Screen: Off; HSync: Off, VSync: Off */
	dpmsreg = 0x03;
	break;
    default:
	dpmsreg = 0;
    }
    if (pApm->noLinear) {
	tmp = RDXB_IOP(0xD0);
	WRXB_IOP(0xD0, (tmp & 0xFC) | dpmsreg);
    } else {
	tmp = RDXB(0xD0);
	WRXB(0xD0, (tmp & 0xFC) | dpmsreg);
    }
}

static Bool
ApmSaveScreen(ScreenPtr pScreen, int mode)
{
    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
    Bool unblank;

    unblank = xf86IsUnblank(mode);

    if (unblank)
	SetTimeSinceLastInputEvent();

    if (pScrn->vtSema)
	vgaHWBlankScreen(pScrn, unblank);
    return TRUE;
}

#ifdef APM_DEBUG
unsigned char _L_ACR(unsigned char *x);
unsigned char _L_ACR(unsigned char *x)
{
    return *x;
}

unsigned short _L_ASR(unsigned short *x);
unsigned short _L_ASR(unsigned short *x)
{
    return *x;
}

unsigned int _L_AIR(unsigned int *x);
unsigned int _L_AIR(unsigned int *x)
{
    return *x;
}

void _L_ACW(char *x, char y);
void _L_ACW(char *x, char y)
{
    *x = y;
}

void _L_ASW(short *x, short y);
void _L_ASW(short *x, short y)
{
    *x = y;
}

void _L_AIW(int *x, int y);
void _L_AIW(int *x, int y)
{
    *x = y;
}
#endif
