
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "apm.h"
#include "xf86cmap.h"
#include "shadowfb.h"
#if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) < 6
#include "xf86Resources.h"
#include "xf86RAC.h"
#endif
#include "xf86int10.h"
#include "vbe.h"

#include "opaque.h"
#ifdef HAVE_XEXTPROTO_71
#include <X11/extensions/dpmsconst.h>
#else
#define DPMS_SERVER
#include <X11/extensions/dpms.h>
#endif


#define APM_VERSION		4000
#define APM_NAME		"APM"
#define APM_DRIVER_NAME		"apm"
#define APM_MAJOR_VERSION       PACKAGE_VERSION_MAJOR
#define APM_MINOR_VERSION       PACKAGE_VERSION_MINOR
#define APM_PATCHLEVEL          PACKAGE_VERSION_PATCHLEVEL

#define PCI_VENDOR_ALLIANCE     0x1142
#define PCI_CHIP_AP6422         0x6422
#define PCI_CHIP_AT24           0x6424
#define PCI_CHIP_AT3D	        0x643D

/* 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(SCREEN_INIT_ARGS_DECL);
static Bool     ApmEnterVT(VT_FUNC_ARGS_DECL);
static void     ApmLeaveVT(VT_FUNC_ARGS_DECL);
static Bool     ApmCloseScreen(CLOSE_SCREEN_ARGS_DECL);
static void     ApmFreeScreen(FREE_SCREEN_ARGS_DECL);
static ModeStatus ApmValidMode(SCRN_ARG_TYPE arg, 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);

#ifdef XF86RUSH
int ApmPixmapIndex = -1;
static unsigned long ApmGeneration = 0;
#endif

_X_EXPORT DriverRec APM = {
	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 }
};

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

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

	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)
{
    free(pScrn->driverPrivate);
    pScrn->driverPrivate = NULL;
}


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

/* lock Alliance registers */
static void
ApmLock(ApmPtr pApm)
{
    if (pApm->Chipset >= AT3D)
	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 void
ApmAssignFPtr(ScrnInfoPtr pScrn)
{
    pScrn->driverVersion	= APM_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.
     */

#ifndef XSERVER_LIBPCIACCESS
    if (xf86GetPciVideoInfo() == NULL) {
	return FALSE;
    }
#endif
    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;
	    }
	}
    }


    free(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;
    const char		*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);
#ifndef XSERVER_LIBPCIACCESS
	pApm->PciTag	= pciTag(pApm->PciInfo->bus, pApm->PciInfo->device,
				 pApm->PciInfo->func);
#endif
    }
    else {
	pApm->PciInfo	= NULL;
#ifndef XSERVER_LIBPCIACCESS
	pApm->PciTag	= 0;
#endif
    }

    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;

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

    hwp = VGAHWPTR(pScrn);
    vgaHWSetStdFuncs(hwp);
    vgaHWGetIOBase(hwp);
#if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) < 12
#define PIOOFFSET hwp->PIOOffset
#else
/* FIXME reintroduce domain support */
#define PIOOFFSET 0
#endif
    pApm->iobase = PIOOFFSET;
    pApm->xport = 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 = malloc(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;
    }
    from = X_DEFAULT;
    pApm->hwCursor = FALSE;
    if (xf86GetOptValBool(pApm->Options, OPTION_HW_CURSOR, &pApm->hwCursor))
	from = X_CONFIG;
    if (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 = PCI_DEV_DEVICE_ID(pApm->PciInfo);
	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 = PCI_DEV_REVISION(pApm->PciInfo);
    }

    /*
     * 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 = PCI_REGION_BASE(pApm->PciInfo, 0, REGION_MEM) & 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")) {
	if (xf86LoadSubModule(pScrn, "i2c")) {
	    pApm->I2C = TRUE;
	}
    }

    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;

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

#ifndef XSERVER_LIBPCIACCESS
    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;
#endif
    if (pEnt->device->videoRam != 0) {
	pScrn->videoRam = pEnt->device->videoRam;
	from = X_CONFIG;
    } else if (pApm->Chipset >= AT3D) {
	unsigned char		d9, db, uc;
	/*unsigned long		save;*/
	volatile unsigned char	*LinMap;

#ifndef XSERVER_LIBPCIACCESS
	LinMap = xf86MapPciMem(pScrn->scrnIndex, VIDMEM_MMIO,
				     pApm->PciTag, pApm->LinAddress,
				     pApm->LinMapSize);
#else
	{
	    void** result = (void**)&LinMap;
	    int err = pci_device_map_range(pApm->PciInfo,
					   pApm->LinAddress,
					   pApm->LinMapSize,
					   PCI_DEV_MAP_FLAG_WRITABLE,
					   result);
	    
	    if (err)
		return FALSE;
	}
#endif

	/*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);*/
#ifndef XSERVER_LIBPCIACCESS
	xf86UnMapVidMem(pScrn->scrnIndex, (pointer)LinMap, pApm->LinMapSize);
#else
	pci_device_unmap_range(pApm->PciInfo, (pointer)LinMap, pApm->LinMapSize);
#endif
	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 GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) < 12
    if (!xf86IsPc98())
#endif
    {
	hwp->MapSize = 0x10000;
	vgaHWMapMem(pScrn);
	if (pApm->I2C) {
	    if (!ApmI2CInit(pScrn)) {
		xf86DrvMsg(pScrn->scrnIndex,X_ERROR,"I2C initialization failed\n");
	    }
	    else {
		MonInfo = xf86DoEDID_DDC2(XF86_SCRN_ARG(pScrn),pApm->I2CPtr);
	    }
	}
	if (0 && !MonInfo)
	    MonInfo = xf86DoEDID_DDC1(XF86_SCRN_ARG(pScrn),vgaHWddc1SetSpeed,ddc1Read);
	if (MonInfo) {
	    xf86PrintEDID(MonInfo);
	    xf86SetDDCproperties(pScrn, 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 8:
    case 16:
    case 24:
    case 32:
	mod = "fb";
	break;
    }

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

    /* Load XAA if needed */
    if (!pApm->NoAccel) {
#ifdef HAVE_XAA_H
	if (!xf86LoadSubModule(pScrn, "xaa")) {
	    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Falling back to shadowfb\n");
	    pApm->NoAccel = TRUE;
	    pApm->ShadowFB = TRUE;
	}
#else
	pApm->NoAccel = TRUE;
	pApm->ShadowFB = TRUE;
#endif
    }

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

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

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

#ifndef XSERVER_LIBPCIACCESS
    pApm->LinMap = xf86MapPciMem(pScrn->scrnIndex, VIDMEM_FRAMEBUFFER,
				 pApm->PciTag,
				 (unsigned long)pApm->LinAddress,
				 pApm->LinMapSize);
#else
    {
	void** result = (void**)&pApm->LinMap;
	int err = pci_device_map_range(pApm->PciInfo,
				       pApm->LinAddress,
				       pApm->LinMapSize,
				       PCI_DEV_MAP_FLAG_WRITABLE |
				       PCI_DEV_MAP_FLAG_WRITE_COMBINE,
				       result);
	
	if (err) 
	    return FALSE;
    }
#endif


    if (pApm->LinMap == NULL)
	return FALSE;

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

    /*
     * 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) {
	    WRXB(0xD9, pApm->d9);
	    WRXB(0xDB, pApm->db);
	}
	WRXB(0xC9, pApm->c9);
#ifndef XSERVER_LIBPCIACCESS
	xf86UnMapVidMem(pScrn->scrnIndex, (pointer)pApm->LinMap, pApm->LinMapSize);
#else
	pci_device_unmap_range(pApm->PciInfo, (pointer)pApm->LinMap, pApm->LinMapSize);
#endif
	pApm->LinMap = NULL;
    }
    else if (pApm->FbBase)
#ifndef XSERVER_LIBPCIACCESS
	xf86UnMapVidMem(pScrn->scrnIndex, (pointer)pApm->LinMap, 0x10000);
#else
	pci_device_unmap_range(pApm->PciInfo, (pointer)pApm->LinMap, 0x10000);
#endif

    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 = malloc(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. */
	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);

	/* 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 GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) < 12
    if (xf86IsPc98())
       outb(0xFAC, 0xFF);
#endif

    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
	ApmReg->EX[XRE8] = RDXL(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. */
	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. */
	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. */
	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]);

	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(SCREEN_INIT_ARGS_DECL)
{
    ScrnInfoPtr		pScrn = xf86ScreenToScrn(pScreen);
    APMDECL(pScrn);
    int			ret;
    unsigned char	*FbBase;

    pApm->pScreen = pScreen;

    /* Map the chip memory and MMIO areas */
    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(ADJUST_FRAME_ARGS(pScrn, pScrn->frameX0, pScrn->frameY0));

    /*
     * 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 = malloc(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 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(pScrn->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.
     */
#ifdef HAVE_XAA_H
    if (!pApm->NoAccel) {
	ApmAccelInit(pScreen);		/* set up XAA interface */
    }
#endif

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

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

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

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

    /* 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(SWITCH_MODE_ARGS_DECL)
{
    SCRN_INFO_PTR(arg);
    return ApmModeInit(pScrn, mode);
}

/*
 * This function is used to initialize the Start Address - the first
 * displayed location in the video memory.
 */
/* Usually mandatory */
void
ApmAdjustFrame(ADJUST_FRAME_ARGS_DECL)
{
    SCRN_INFO_PTR(arg);
    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(VT_FUNC_ARGS_DECL)
{
    SCRN_INFO_PTR(arg);
    APMDECL(pScrn);
    vgaHWPtr	hwp = VGAHWPTR(pScrn);

    if (pApm->Chipset >= AT3D) {
	/* 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);
    }
    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(ADJUST_FRAME_ARGS(pScrn, pScrn->frameX0, pScrn->frameY0));

    return TRUE;
}

/* Mandatory */
static void
ApmLeaveVT(VT_FUNC_ARGS_DECL)
{
    SCRN_INFO_PTR(arg);
    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) {
	WRXB(0xD9, pApm->d9);
	WRXB(0xDB, pApm->db);
    }
    WRXB(0xC9, pApm->c9);

#if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) < 12
    if (xf86IsPc98())
	outb(0xFAC, 0xFE);
#endif
}

/*
 * 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(CLOSE_SCREEN_ARGS_DECL)
{
    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
    vgaHWPtr	hwp = VGAHWPTR(pScrn);
    APMDECL(pScrn);

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

    pScrn->vtSema = FALSE;

#if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) < 12
    if (xf86IsPc98())
	outb(0xFAC, 0xFE);
#endif

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

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

/* Optional */
static void
ApmFreeScreen(FREE_SCREEN_ARGS_DECL)
{
    SCRN_INFO_PTR(arg);
    vgaHWFreeHWRec(pScrn);
    ApmFreeRec(pScrn);
}

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

/* Optional */
static ModeStatus
ApmValidMode(SCRN_ARG_TYPE arg, 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;
    }
    tmp = RDXB(0xD0);
    WRXB(0xD0, (tmp & 0xFC) | dpmsreg);
}

static Bool
ApmSaveScreen(ScreenPtr pScreen, int mode)
{
    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
    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
