/*
 * Driver for the SGI Indy's Newport graphics card
 * 
 * This driver is based on the newport.c & newport_con.c kernel code
 *
 * (c) 2000-2002 Guido Guenther <agx@sigxcpu.org>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is fur-
 * nished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FIT-
 * NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
 * XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CON-
 * NECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * Except as contained in this notice, the name of the XFree86 Project shall not
 * be used in advertising or otherwise to promote the sale, use or other deal-
 * ings in this Software without prior written authorization from the XFree86
 * Project.
 *
 */

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

#if defined(__NetBSD__)
#include <fcntl.h>
#include <dev/wscons/wsconsio.h>
#include <sys/ioctl.h>
#endif

/* function prototypes, common data structures & generic includes */
#include "newport.h"

/* Drivers using the mi SW cursor need: */
#include "mipointer.h"
/* Drivers using the mi colourmap code need: */
#include "micmap.h"

/* Drivers using fb need: */
#include "fb.h"

/* Drivers using the shadow frame buffer need: */
#include "shadowfb.h"

/* Xv Extension */
#include "xf86xv.h"
#include <X11/extensions/Xv.h>

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

#if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) > 6
#define xf86LoaderReqSymLists(...) do {} while (0)
#define LoaderRefSymLists(...) do {} while (0)
#endif

#define NEWPORT_VERSION		4000
#define NEWPORT_NAME		"NEWPORT"
#define NEWPORT_DRIVER_NAME	"newport"
#define NEWPORT_MAJOR_VERSION	PACKAGE_VERSION_MAJOR
#define NEWPORT_MINOR_VERSION	PACKAGE_VERSION_MINOR
#define NEWPORT_PATCHLEVEL	PACKAGE_VERSION_PATCHLEVEL


/* Prototypes ------------------------------------------------------- */
static void	NewportIdentify(int flags);
static const OptionInfoRec * NewportAvailableOptions(int chipid, int busid);
static Bool NewportProbe(DriverPtr drv, int flags);
static Bool NewportPreInit(ScrnInfoPtr pScrn, int flags);
static Bool NewportScreenInit(int Index, ScreenPtr pScreen, int argc, char **argv);
static Bool NewportEnterVT(int scrnIndex, int flags);
static void NewportLeaveVT(int scrnIndex, int flags);
static Bool NewportCloseScreen(int scrnIndex, ScreenPtr pScreen);
static Bool NewportSaveScreen(ScreenPtr pScreen, int mode);
static unsigned NewportHWProbe(unsigned probedIDs[]);	/* return number of found boards */
static Bool NewportModeInit(ScrnInfoPtr pScrn, DisplayModePtr mode);
static void NewportRestore(ScrnInfoPtr pScrn, Bool Closing);
static Bool NewportGetRec(ScrnInfoPtr pScrn);
static Bool NewportFreeRec(ScrnInfoPtr pScrn);
static Bool NewportMapRegs(ScrnInfoPtr pScrn);
static void NewportUnmapRegs(ScrnInfoPtr pScrn);
static Bool NewportProbeCardInfo(ScrnInfoPtr pScrn);
/* ------------------------------------------------------------------ */

_X_EXPORT DriverRec NEWPORT = {
        NEWPORT_VERSION,
	NEWPORT_DRIVER_NAME,
        NewportIdentify,
        NewportProbe,
        NewportAvailableOptions,
        NULL,
	0
};

/* Supported "chipsets" */
#define CHIP_XL		0x1

static SymTabRec NewportChipsets[] = {
    { CHIP_XL, "XL" },
    {-1, NULL }
};

static MODULESETUPPROTO(newportSetup);

static XF86ModuleVersionInfo newportVersRec =
{
	"newport",
	MODULEVENDORSTRING,
	MODINFOSTRING1,
	MODINFOSTRING2,
	XORG_VERSION_CURRENT,
/*
	XF86_VERSION_CURRENT,
*/	
	NEWPORT_MAJOR_VERSION, NEWPORT_MINOR_VERSION, NEWPORT_PATCHLEVEL,
	ABI_CLASS_VIDEODRV,
	ABI_VIDEODRV_VERSION,
	MOD_CLASS_VIDEODRV,
	{0,0,0,0}
};

_X_EXPORT XF86ModuleData newportModuleData = {
	&newportVersRec,
	newportSetup,
	NULL
};

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

/* This module should be loaded only once, but check to be sure. */
	if (!setupDone) {
		/*
		 * Modules that this driver always requires may be loaded
		 * here  by calling LoadSubModule().
		 */
		setupDone = TRUE;
		xf86AddDriver(&NEWPORT, module, 0);

		/*
		 * The return value must be non-NULL on success even though
		 * there is no TearDownProc.
		 */
	return (pointer)1;
       } else {
	if (errmaj) *errmaj = LDR_ONCEONLY;
	return NULL;
       }
}

typedef enum {
	OPTION_BITPLANES,
	OPTION_BUS_ID,
	OPTION_HWCURSOR,
	OPTION_NOACCEL,
	OPTION_XMAP_TIMING,
} NewportOpts;

/* Supported options */
static const OptionInfoRec NewportOptions [] = {
	{ OPTION_BITPLANES, "bitplanes", OPTV_INTEGER, {0}, FALSE },
	{ OPTION_BUS_ID, "BusID", OPTV_INTEGER, {0}, FALSE },
	{ OPTION_HWCURSOR, "HWCursor", OPTV_BOOLEAN, {0}, FALSE },
	{ OPTION_NOACCEL, "NoAccel", OPTV_BOOLEAN, {0}, FALSE },
	{ OPTION_XMAP_TIMING, "XmapTiming", OPTV_INTEGER, {0}, FALSE },
	{ -1, NULL, OPTV_NONE, {0}, FALSE }
};

/* ------------------------------------------------------------------ */

static Bool
NewportGetRec(ScrnInfoPtr pScrn)
{
	NewportPtr pNewport;
	if (pScrn->driverPrivate != NULL)
		return TRUE;
	pScrn->driverPrivate = xnfcalloc(sizeof(NewportRec), 1);
	
	pNewport = NEWPORTPTR(pScrn);
	pNewport->pNewportRegs = NULL;

	return TRUE;
}

static Bool
NewportFreeRec(ScrnInfoPtr pScrn)
{
	free(pScrn->driverPrivate);
	pScrn->driverPrivate = NULL;
	return TRUE;
}

static void
NewportIdentify(int flags)
{
	xf86PrintChipsets( NEWPORT_NAME, "driver for Newport Graphics Card", 
	    NewportChipsets);
}

static Bool
NewportProbe(DriverPtr drv, int flags)
{
	int numDevSections, numUsed, i, j, busID;
	Bool foundScreen = FALSE;
	GDevPtr *devSections;
	GDevPtr dev = NULL;
#ifndef XSERVER_LIBPCIACCESS
	resRange range[] = { {ResExcMemBlock ,0,0}, _END };
#endif
	unsigned probedIDs[NEWPORT_MAX_BOARDS];
	memType base;
	if ((numDevSections = xf86MatchDevice(NEWPORT_DRIVER_NAME, &devSections)) <= 0) 
                return FALSE;

	numUsed = NewportHWProbe(probedIDs);
	if ( numUsed <= 0 ) 
		return FALSE;

	if ( xf86DoConfigure && xf86DoConfigurePass1 ) {
		GDevPtr pGDev;
		for (i = 0; i < numUsed; i++) {
			pGDev = xf86AddBusDeviceToConfigure(NEWPORT_DRIVER_NAME,
				BUS_NONE, NULL, 0);
			if (pGDev) {
				/*
				 * XF86Match???Instances() treat chipID and 
				 * chipRev as overrides, so clobber them here.
				 */
				pGDev->chipID = pGDev->chipRev = -1;
			}
	    	}
	}

	if(flags & PROBE_DETECT) {
		foundScreen = TRUE;
	} else {
		for (i = 0; i < numDevSections; i++) {
			dev = devSections[i];
			busID =  xf86SetIntOption(dev->options, "BusID", 0);

			for( j = 0; j < numUsed; j++) {
				if ( busID == probedIDs[j] ) {
					int entity;
					ScrnInfoPtr pScrn = NULL;

					entity = xf86ClaimNoSlot(drv, 0, dev, TRUE);
#ifndef XSERVER_LIBPCIACCESS
					base = (NEWPORT_BASE_ADDR0
						+ busID * NEWPORT_BASE_OFFSET);
					RANGE(range[0], base, base + sizeof(NewportRegs),\
							ResExcMemBlock);
#endif
					pScrn = xf86AllocateScreen(drv, 0);
					xf86AddEntityToScreen(pScrn, entity);
					pScrn->driverVersion = NEWPORT_VERSION;
					pScrn->driverName    = NEWPORT_DRIVER_NAME;
					pScrn->name          = NEWPORT_NAME;
					pScrn->Probe         = NewportProbe;
					pScrn->PreInit       = NewportPreInit;
					pScrn->ScreenInit    = NewportScreenInit;
					pScrn->EnterVT       = NewportEnterVT;
					pScrn->LeaveVT       = NewportLeaveVT;
					pScrn->driverPrivate = (void*)(long)busID;
					foundScreen = TRUE;
					break;
				}
			}
		}
	}
	free(devSections);
	return foundScreen;
}

/* most of this is from DESIGN.TXT s20.3.6 */
static Bool 
NewportPreInit(ScrnInfoPtr pScrn, int flags)
{
	int i, busID;
	NewportPtr pNewport;
	MessageType from;
	ClockRangePtr clockRanges;

	if (flags & PROBE_DETECT) return FALSE;

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

	busID = (long)(pScrn->driverPrivate);
	pScrn->driverPrivate = NULL;
	
	/* Fill in the monitor field */
	pScrn->monitor = pScrn->confScreen->monitor;

#ifdef NEWPORT_USE32BPP
	if (!xf86SetDepthBpp(pScrn, 24, 32, 32, Support32bppFb))
		return FALSE;
#else
	if (!xf86SetDepthBpp(pScrn, 24, 0, 0, 
			Support24bppFb | SupportConvert32to24 | 
				PreferConvert32to24 ))
		return FALSE;
#endif		
	switch( pScrn->depth ) {
			/* check if the returned depth is one we support */
		case 8:
		case 24:
			/* OK */
			break;
		default: 
			xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 
			"Given depth (%d) is not supported by Newport driver\n",
			pScrn->depth);
			return FALSE;
	}
	xf86PrintDepthBpp(pScrn);

	/* Set bits per RGB for 8bpp */
	if( pScrn->depth == 8)
		pScrn->rgbBits = 8;

	/* Set Default Weight */
	if( pScrn->depth > 8 ) {
#ifdef NEWPORT_USE32BPP
		rgb w = {8, 8, 8};
		rgb m = {0x0000FF, 0x00FF00, 0xFF0000};
		if (!xf86SetWeight(pScrn, w, m))
			return FALSE;
#else	
		rgb zeros = {0, 0, 0};
		if (!xf86SetWeight(pScrn, zeros, zeros))
			return FALSE;
#endif			
	}
	
	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;
        	}
	}

	{ /* Set default Gamma */
		Gamma zeros = {0.0, 0.0, 0.0};

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

	/* Allocate the NewportRec driverPrivate */
	if (!NewportGetRec(pScrn)) {
		return FALSE;
	} 
	pNewport = NEWPORTPTR(pScrn);
	pNewport->busID = busID;

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

	/* Fill in pScrn->options) */
	xf86CollectOptions(pScrn, NULL);
	if (!(pNewport->Options = malloc(sizeof(NewportOptions))))
		return FALSE;
	memcpy(pNewport->Options, NewportOptions, sizeof(NewportOptions));
	xf86ProcessOptions(pScrn->scrnIndex, pScrn->options, pNewport->Options);

	/* Set fields in ScreenInfoRec && NewportRec */
    	pScrn->videoRam = 1280 * (pScrn->bitsPerPixel >> 3);

	/* get revisions of REX3, etc. */
	if( ! NewportMapRegs(pScrn))
		return FALSE;
	NewportProbeCardInfo(pScrn);
	NewportUnmapRegs(pScrn);

	from=X_PROBED;
	xf86DrvMsg(pScrn->scrnIndex, from,
		"Newport Graphics Revisions: Board: %d, Rex3: %c, Cmap: %c, Xmap9: %c\n",
		pNewport->board_rev, pNewport->rex3_rev, 
		pNewport->cmap_rev, pNewport->xmap9_rev);

	if ( (xf86GetOptValInteger(pNewport->Options, OPTION_BITPLANES, 
	    &pNewport->bitplanes)))
	from = X_CONFIG;
	xf86DrvMsg(pScrn->scrnIndex, from, "Newport has %d bitplanes\n", 
	    pNewport->bitplanes);

	if ( pScrn->depth > pNewport->bitplanes ) {
		xf86DrvMsg(pScrn->scrnIndex, X_ERROR, \
			"Display depth(%d) > number of bitplanes on Newport board(%d)\n", \
			pScrn->depth, pNewport->bitplanes);
		return FALSE;
	}
	if ( ( pNewport->bitplanes != 8 ) && ( pNewport->bitplanes != 24 ) ) {
		xf86DrvMsg(pScrn->scrnIndex, X_ERROR, \
			"Number of bitplanes on newport must be either 8 or 24 not %d\n", \
			pNewport->bitplanes);
		return FALSE;
	}

	from=X_DEFAULT;
	pNewport->hwCursor = TRUE;
    	if (xf86GetOptValBool(pNewport->Options, OPTION_HWCURSOR, &pNewport->hwCursor))
        	from = X_CONFIG;
	xf86DrvMsg(pScrn->scrnIndex, from, "Using %s cursor\n",
        	pNewport->hwCursor ? "HW" : "SW");
	
	/* Set up clock ranges that are alway ok */
	/* XXX: Use information from VC2 here */
	clockRanges = xnfcalloc(sizeof(ClockRange),1);
	clockRanges->next = NULL;
	clockRanges->minClock = 10000;
	clockRanges->maxClock = 300000;
	clockRanges->clockIndex = -1;         /* programmable */
	clockRanges->interlaceAllowed = TRUE;
	clockRanges->doubleScanAllowed = TRUE;
	
	/* see above note */
	/* There is currently only an 1280x1024 mode */
	i = xf86ValidateModes(pScrn, pScrn->monitor->Modes,
			pScrn->display->modes, clockRanges, 
			NULL, 256, 2048,
			pScrn->bitsPerPixel, 128, 2048,
			pScrn->display->virtualX,
			pScrn->display->virtualY,
			pScrn->videoRam * 1024,
			LOOKUP_BEST_REFRESH);

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

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

	/* unnecessary, but do it to get a valid ScrnInfoRec */
	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);
	xf86SetDpi (pScrn, 0, 0);

	/* Load FB module */
	if (!xf86LoadSubModule (pScrn, "fb")) {
		NewportFreeRec(pScrn);
		return FALSE;
	}

	/* Load ramdac modules */
    	if (pNewport->hwCursor) {
        	if (!xf86LoadSubModule(pScrn, "ramdac")) {
			NewportFreeRec(pScrn);
            		return FALSE;
        	}
    	}

	/* Load ShadowFB module */
	if (!xf86LoadSubModule(pScrn, "shadowfb")) {
		NewportFreeRec(pScrn);
		return FALSE;
	}

	/* Load XAA module */
	if (!xf86LoadSubModule(pScrn, "xaa")) {
		NewportFreeRec(pScrn);
		return FALSE;
	}
	xf86LoaderReqSymLists(xaaSymbols, NULL);
	return TRUE;
}


Bool
NewportXAAScreenInit(ScreenPtr pScreen);

/*
 * Parse the xmap timing option if it exists.
 *
 * Return true if it's valid, false if it's invalid.
 * If it's not present then return true; this is to signal
 * if the caller should quit the driver setup path.
 */
static Bool
NewportOptionXmapConfig(ScrnInfoPtr pScrn, NewportPtr pNewport)
{
	int t;

	/* Initialise / parse the Xmap timing type */
	pNewport->XmapTiming = XmapTimingUnset;

	if (xf86GetOptValInteger(pNewport->Options, OPTION_XMAP_TIMING, &t)) {
		switch (t) {
		case XmapTimingFast:
			xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
			    "No XmapTiming set to FAST\n");
			pNewport->XmapTiming = t;
			break;
		case XmapTimingSlow:
			xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
			    "No XmapTiming set to SLOW\n");
			pNewport->XmapTiming = t;
			break;
		case XmapTimingVerySlow:
			xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
			    "No XmapTiming set to VERYSLOW\n");
			pNewport->XmapTiming = t;
			break;
		default:
			xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
			    "XmapTiming value (%d) is not valid!\n", t);
			return FALSE;
		}
	}

	/* Default to "fast" for now, preserve existing behaviour */
	if (pNewport->XmapTiming == XmapTimingUnset) {
		xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
		    "No XmapTiming provided; defaulting to FAST\n");
		pNewport->XmapTiming = XmapTimingFast;
	}

	return TRUE;
}


static Bool 
NewportScreenInit(int index, ScreenPtr pScreen, int argc, char **argv)
{
	ScrnInfoPtr pScrn;
	NewportPtr pNewport;
	VisualPtr visual;
	BOOL ret;
	int i;

	/* First get a pointer to our private info */
	pScrn = xf86Screens[pScreen->myNum];
	pNewport = NEWPORTPTR(pScrn);

	/* map the Newportregs until the server dies */
	if( ! NewportMapRegs(pScrn)) 
		return FALSE;

	/* Reset visual list. */
	miClearVisualTypes();

	if (!miSetVisualTypes(pScrn->depth, pScrn->depth != 8 ? TrueColorMask :
					miGetDefaultVisualMask(pScrn->depth),
				pScrn->rgbBits, pScrn->defaultVisual))
		return FALSE;
	
	miSetPixmapDepths ();

	pNewport->Bpp = pScrn->bitsPerPixel >> 3;
	/* Setup the stuff for the shadow framebuffer */
	pNewport->ShadowPitch = (( pScrn->virtualX * pNewport->Bpp ) + 3) & ~3L;
	pNewport->ShadowPtr = xnfalloc(pNewport->ShadowPitch * pScrn->virtualY);

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

	ret = fbScreenInit(pScreen, pNewport->ShadowPtr,
			   pScrn->virtualX, pScrn->virtualY,
			   pScrn->xDpi, pScrn->yDpi,
			   pScrn->displayWidth,
			   pScrn->bitsPerPixel);

	if(!ret)
		return FALSE;

	/* we need rgb ordering if bitsPerPixel > 8 */
	if (pScrn->bitsPerPixel > 8) {
		for (i = 0, visual = pScreen->visuals;
			i < pScreen->numVisuals; i++, visual++) {
			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;
				/*
				xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Bitplanes R:%d:%08X G:%d:%08X B:%d:%08X\n", 
				           visual->offsetRed, visual->redMask,
					   visual->offsetGreen, visual->greenMask,
					   visual->offsetBlue, visual->blueMask);
				*/
			}
		}
	}

	/* must be after RGB ordering fixed */
	if (!fbPictureInit(pScreen, NULL, 0))
		xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
			   "RENDER extension initialisation failed.\n");

	xf86SetBackingStore(pScreen);

	xf86SetBlackWhitePixels(pScreen);

	/* Parse the xmap config */
	if (! NewportOptionXmapConfig(pScrn, pNewport))
		return FALSE;

#ifdef NEWPORT_ACCEL
	pNewport->NoAccel = FALSE;
	if (xf86ReturnOptValBool(pNewport->Options, OPTION_NOACCEL, FALSE)) 
	{
	    pNewport->NoAccel = TRUE;
	    xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Acceleration disabled\n");
	}
	if (!pNewport->NoAccel) {
	    if (!xf86LoadSubModule(pScrn, "xaa"))
		pNewport->NoAccel = TRUE;
	}
#if 0    
	if (pScrn->bitsPerPixel < 24)
	{ /* not implemented yet */
	    pNewport->NoAccel = TRUE;
	}
#endif	
	pNewport->pXAAInfoRec = NULL;
	if (!pNewport->NoAccel)
	{
	    if (!NewportXAAScreenInit(pScreen))
		return FALSE;
	}
#endif
	/* Initialize software cursor */
	if(!miDCInitialize(pScreen, xf86GetPointerScreenFuncs()))
		return FALSE;

	/* Initialize hardware cursor */
	if(pNewport->hwCursor)
		if(!NewportHWCursorInit(pScreen)) {
			xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
               		"Hardware cursor initialization failed\n");
			return FALSE;
	}

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

	/* Install our LoadPalette funciton */
	if(!xf86HandleColormaps(pScreen, 256, 8, NewportLoadPalette, 0,
				CMAP_RELOAD_ON_MODE_SWITCH )) {
		xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
                   "Colormap initialization failed\n");
		return FALSE;
	}
#ifdef NEWPORT_ACCEL
	if (pNewport->NoAccel)
#endif
	/* Initialise shadow frame buffer */
	if(!ShadowFBInit(pScreen, (pNewport->Bpp == 1) ? &NewportRefreshArea8 :
				&NewportRefreshArea24)) {
		xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
                   "ShadowFB initialization failed\n");
		return FALSE;
	}

	{
		XF86VideoAdaptorPtr *ptr;
		int n;

		n = xf86XVListGenericAdaptors(pScrn,&ptr);
		if (n) {
			xf86XVScreenInit(pScreen, ptr, n);
	        }
        }

	pScreen->SaveScreen = NewportSaveScreen;
	/* Wrap the current CloseScreen function */
	pNewport->CloseScreen = pScreen->CloseScreen;
	pScreen->CloseScreen = NewportCloseScreen;

	if (serverGeneration == 1) {
		xf86ShowUnusedOptions(pScrn->scrnIndex, pScrn->options);
	}
	NewportModeInit(pScrn, pScrn->currentMode);
	return TRUE;
}

/* called when switching away from a VT */
static Bool
NewportEnterVT(int scrnIndex, int flags)
{
	ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
	return NewportModeInit(pScrn, pScrn->currentMode);
}

/* called when switching to a VT */
static void
NewportLeaveVT(int scrnIndex, int flags)
{
	ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
	NewportRestore(pScrn, FALSE);
}

/* called at the end of each server generation */
static Bool
NewportCloseScreen(int scrnIndex, ScreenPtr pScreen)
{
	ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
	NewportPtr pNewport = NEWPORTPTR(pScrn);
#ifdef NEWPORT_ACCEL
	if (pNewport->pXAAInfoRec && !pNewport->NoAccel)
	{
	    XAADestroyInfoRec(pNewport->pXAAInfoRec);
	    pNewport->pXAAInfoRec = NULL;
	}
#endif	

	NewportRestore(pScrn, TRUE);
	free(pNewport->ShadowPtr);

	/* unmap the Newport's registers from memory */
	NewportUnmapRegs(pScrn);
	pScrn->vtSema = FALSE;
 
	pScreen->CloseScreen = pNewport->CloseScreen;
	return (*pScreen->CloseScreen)(scrnIndex, pScreen);
}

/* Blank or unblank the screen */ 
static Bool
NewportSaveScreen(ScreenPtr pScreen, int mode)
{
	ScrnInfoPtr pScrn;
	NewportPtr pNewport;
	NewportRegsPtr pNewportRegs;
	Bool unblank;

	if (!pScreen)
		return TRUE;

	unblank = xf86IsUnblank(mode);
	pScrn = xf86Screens[pScreen->myNum];

	if (!pScrn->vtSema)
		return TRUE;

	pNewport = NEWPORTPTR(pScrn);
	pNewportRegs = NEWPORTPTR(pScrn)->pNewportRegs;
	
	if (unblank)
		pNewport->vc2ctrl |= VC2_CTRL_EDISP;            
	else
		pNewport->vc2ctrl &= ~VC2_CTRL_EDISP;           
	NewportVc2Set( pNewportRegs, VC2_IREG_CONTROL, pNewport->vc2ctrl);
	return TRUE;
}


static const OptionInfoRec *
NewportAvailableOptions(int chipid, int busid)
{
	return NewportOptions;
}

/*
 * Setup all 32 mode/DID entries in the xmap9 tables with the
 * same mode.
 */
static void
NewportSetXmapModeTable(NewportPtr pNewport, uint32_t mode)
{
	NewportRegsPtr pNewportRegs = pNewport->pNewportRegs;
	int i;

	for (i = 0; i < 32; i++) {
		NewportBfwait(pNewport->pNewportRegs);
		NewportXmap9SetModeRegister(pNewport, i, mode);
	}

	/* select the set up mode register */
	NewportBfwait(pNewport->pNewportRegs);
	NewportXmap9WriteRegister(pNewportRegs, DCB_XMAP_ALL,
	    XM9_CRS_MODE_REG_INDEX, 0);
}

/*
 * Setup the XMAP9 for the given requested operating mode.
 *
 * For now it's a no-op, but eventually it should explicitly configure
 * 8 or 24 bit mode depending upon the requested config and supported
 * hardware.
 */
static void
NewportHwSetupXmapMode(NewportPtr pNewport)
{
#if 0
		/* TODO: refactor this out; program them separately to not mess with odd/even dithering! */
		NewportBfwait(pNewport->pNewportRegs);

		pNewportRegs->set.dcbmode = (DCB_XMAP_ALL |
		    W_DCB_XMAP9_PROTOCOL | XM9_CRS_CONFIG | NPORT_DMODE_W1 );
		pNewportRegs->set.dcbdata0.bytes.b3 &=
		    ~(XM9_8_BITPLANES | XM9_PUPMODE);
#endif
}

/* This sets up the actual mode on the Newport */
static Bool 
NewportModeInit(ScrnInfoPtr pScrn, DisplayModePtr mode)
{
	int width, height;
	int i;
	NewportPtr pNewport = NEWPORTPTR(pScrn);
	NewportRegsPtr pNewportRegs = NEWPORTREGSPTR(pScrn);

	width = mode->HDisplay;
	height = mode->VDisplay;
	if ((width > 1280) || (width < 1024) ||
	    (height < 768) || (height > 1024)) {
		xf86DrvMsg(pScrn->scrnIndex, X_ERROR, \
		"Width = %d and height = %d is not supported by by this driver\n", width, height);
		return FALSE;
	}

	pScrn->vtSema = TRUE;
	/* first backup the necessary registers... */
	NewportBackupRex3(pScrn);
	if( pNewport->hwCursor )
		NewportBackupVc2Cursor( pScrn );
	NewportBackupVc2(pScrn);
	NewportBackupPalette(pScrn);
	NewportBackupXmap9s( pScrn );
	/* ...then  setup the hardware */
	pNewport->drawmode1 = DM1_RGBPLANES | 
				NPORT_DMODE1_CCLT | 
				NPORT_DMODE1_CCEQ | 
				NPORT_DMODE1_CCGT | 
				NPORT_DMODE1_LOSRC;

	/* Configure the XMAP mode */
	NewportHwSetupXmapMode(pNewport);

	if( pNewport->Bpp == 1) { /* 8bpp */
		/*
		 * Configure 8 bit draw depth, 8 bit host pixel packing.
		 * Note that RGB mode isn't enabled here because
		 * the CI mode is being used.
		 */
		pNewport->drawmode1 |=  NPORT_DMODE1_DD8 | 
					NPORT_DMODE1_HD8 | 
					NPORT_DMODE1_RWPCKD;

		/*
		 * Setup the mode table for 8 bit colour indexed table,
		 * not an RGB mode; it will use CMAP CI table 0.
		 */
		NewportSetXmapModeTable(pNewport,
		    XM9_MREG_PIX_SIZE_8BPP | XM9_MREG_PIX_MODE_CI |
		    XM9_MREG_GAMMA_BYPASS);

	} else { /* 24bpp */
		CARD32 mode = 0L;

		/* tell the xmap9s that we are using 24bpp */

		/*
		 * Setup the mode table for RGB 888 (24 bit), use the
		 * RGB2 CMAP table.
		 */
		NewportSetXmapModeTable(pNewport, XM9_MREG_PIX_SIZE_24BPP
		    | XM9_MREG_PIX_MODE_RGB2 | XM9_MREG_GAMMA_BYPASS);

		pNewport->drawmode1 |= 
					/* set drawdepth to 24 bit */
					NPORT_DMODE1_DD24 |
					/* turn on RGB mode */	
					NPORT_DMODE1_RGBMD | 
					/* turn on 8888 = RGBA pixel packing */
					NPORT_DMODE1_HD32 | NPORT_DMODE1_RWPCKD;
		/*
		 * After setting up XMAP9 we have to reinitialize the CMAP for
		 * whatever reason (the docs say nothing about it). 
		 */
		for (i = 0; i < 256; i++) {
			LOCO col;

			col.red = col.green = col.blue = i;
			NewportCmapSetRGB(NEWPORTREGSPTR(pScrn), i, col);
		}
	}

	/*
	 * Always setup an RGB2 ramp regardless of 8 or 24 bit operation.
	 *
	 * This table will be needed if eventually support is added for
	 * RGB8 operation (whether XL8 or XL24) rather than 8 bit indexed
	 * colour tables.
	 */
	for (i = 0; i < 256; i++) {
		LOCO col;

		col.red = col.green = col.blue = i;
		NewportCmapSetRGB(NEWPORTREGSPTR(pScrn), i + 0x1f00,
		    col);
	}

	/* blank the framebuffer */
	NewportWait(pNewportRegs);
	pNewportRegs->set.drawmode0 = (NPORT_DMODE0_DRAW |
				       NPORT_DMODE0_DOSETUP |
				       NPORT_DMODE0_STOPX | NPORT_DMODE0_STOPY |
				       NPORT_DMODE0_BLOCK);
	pNewportRegs->set.drawmode1 = pNewport->drawmode1 |
					NPORT_DMODE1_FCLR |
					NPORT_DMODE1_RGBMD;
	pNewportRegs->set.colorvram = 0;
	pNewportRegs->set.xystarti = 0;
	pNewportRegs->go.xyendi = ( (1279+64) << 16) | 1023;

	/* default drawmode */
	NewportWait(pNewportRegs);
	pNewportRegs->set.drawmode1 = pNewport->drawmode1;

	/* XXX: Lazy mode on: just use the textmode value */
	pNewport->vc2ctrl = pNewport->txt_vc2ctrl;

	return TRUE;
}


/* 
 * This will actually restore the saved state 
 * (either when switching back to a VT or when the server is going down)
 * Closing is true if the X server is really going down 
 */
static void
NewportRestore(ScrnInfoPtr pScrn, Bool Closing)
{
	NewportPtr pNewport = NEWPORTPTR(pScrn);

	/* Restore backed up registers */
	NewportRestoreRex3( pScrn );
	if( pNewport->hwCursor )
		NewportRestoreVc2Cursor( pScrn );
	NewportRestoreVc2( pScrn );
	NewportRestorePalette( pScrn );
	NewportRestoreXmap9s( pScrn );
}


/* Probe for the Newport card ;) */
/* XXX: we need a better probe here in order to support multihead! */
static unsigned
NewportHWProbe(unsigned probedIDs[])
{
	unsigned hasNewport = 0;

#if defined(__NetBSD__)
	int fd, type, i;

	probedIDs[0] = 0;

	fd = open("/dev/ttyE0", O_RDONLY, 0);
	i = ioctl(fd, WSDISPLAYIO_GTYPE, &type);
	close(fd);

	if ( (i == 0) && ( type == WSDISPLAY_TYPE_NEWPORT) )
		hasNewport = 1;
#else
	FILE* cpuinfo;
	char line[80];
	if ((cpuinfo = fopen("/proc/cpuinfo", "r"))) {
		while(fgets(line, 80, cpuinfo) != NULL) {
			if(strstr(line, "SGI Indy") != NULL) {
				hasNewport = 1;
				probedIDs[0] = 0;
				break;
			}
			if(strstr(line, "SGI Indigo2") != NULL) {
				hasNewport = 1;
				probedIDs[0] = 1;
				break;
			}
		}
		fclose(cpuinfo);
	}
#endif
	return hasNewport;
}

/* Probe for Chipset revisions */
static Bool NewportProbeCardInfo(ScrnInfoPtr pScrn)
{
	unsigned int tmp,cmap_rev;
	NewportPtr pNewport = NEWPORTPTR(pScrn);
	NewportRegsPtr pNewportRegs = pNewport->pNewportRegs;
	CARD8 val;

	NewportWait(pNewportRegs); 
	pNewportRegs->set.dcbmode = (DCB_CMAP0 | NCMAP_PROTOCOL |
			NCMAP_REGADDR_RREG | NPORT_DMODE_W1);
	tmp = pNewportRegs->set.dcbdata0.bytes.b3;
	pNewport->board_rev = (tmp >> 4) & 7;
	pNewport->bitplanes = ((pNewport->board_rev > 1) && (tmp & 0x80)) ? 8 : 24;
	cmap_rev = tmp & 7;
	pNewport->cmap_rev = (char)('A'+(cmap_rev ? (cmap_rev+1):0));
	pNewport->rex3_rev = (char)('A'+(pNewportRegs->cset.ustat & 7));
	val = NewportXmap9ReadRegister(pNewportRegs, DCB_XMAP0, XM9_CRS_REVISION);
	pNewport->xmap9_rev = (char)('A'+(val & 7));
	
	/* XXX: read possible modes from VC2 here */
	return TRUE;
}


/* map NewportRegs */
static Bool
NewportMapRegs(ScrnInfoPtr pScrn)
{
	NewportPtr pNewport = NEWPORTPTR(pScrn);

#if defined(__NetBSD__)
	pNewport->pNewportRegs = xf86MapVidMem(pScrn->scrnIndex,
			VIDMEM_MMIO, NEWPORT_REGISTERS, sizeof(NewportRegs));
#else
	pNewport->pNewportRegs = xf86MapVidMem(pScrn->scrnIndex, 
			VIDMEM_MMIO,
			NEWPORT_BASE_ADDR0 + pNewport->busID * 
			    NEWPORT_BASE_OFFSET, sizeof(NewportRegs));
#endif
	if ( ! pNewport->pNewportRegs ) {
		xf86Msg(X_ERROR, "can't map registers\n");
		return FALSE;
	}
	return TRUE;
}

/* unmap NewportRegs */
static void
NewportUnmapRegs(ScrnInfoPtr pScrn)
{
	NewportPtr pNewport = NEWPORTPTR(pScrn);

	xf86UnMapVidMem( pScrn->scrnIndex, pNewport->pNewportRegs,
			sizeof(NewportRegs));
	pNewport->pNewportRegs = NULL;
}
