/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/s3virge/s3v_dga.c,v 1.7 2002/01/14 18:02:58 dawes Exp $ */

/*
Copyright (C) 1994-2000 The XFree86 Project, Inc.  All Rights Reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice 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 CONNECTION
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 dealings
in this Software without prior written authorization from the XFree86 Project.
*/

/*
Copyright (C) 1994-2000 The XFree86 Project, Inc.  All Rights Reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice 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 CONNECTION
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 dealings
in this Software without prior written authorization from the XFree86 Project.
*/

/*
 * file: s3v_dga.c
 * ported from mga
 *
 */

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

#include "xf86.h"
#include "xf86_OSproc.h"
#include "xf86Pci.h"
#include "xf86PciInfo.h"
#include "xaa.h"
#include "xaalocal.h"
#include "s3v.h"
#if 0
#include "mga_bios.h"
#include "mga.h"
#include "mga_reg.h"
#include "mga_macros.h"
#endif
#include "dgaproc.h"


static Bool S3V_OpenFramebuffer(ScrnInfoPtr, char **, unsigned char **, 
					int *, int *, int *);
static Bool S3V_SetMode(ScrnInfoPtr, DGAModePtr);
static int  S3V_GetViewport(ScrnInfoPtr);
static void S3V_SetViewport(ScrnInfoPtr, int, int, int);
static void S3V_FillRect(ScrnInfoPtr, int, int, int, int, unsigned long);
static void S3V_BlitRect(ScrnInfoPtr, int, int, int, int, int, int);
/* dummy... */
#if 0
static void MGA_BlitTransRect(ScrnInfoPtr, int, int, int, int, int, int, 
					unsigned long);
#endif

static
DGAFunctionRec S3V_DGAFuncs = {
   S3V_OpenFramebuffer,
   NULL,
   S3V_SetMode,
   S3V_SetViewport,
   S3V_GetViewport,
   S3VAccelSync,
   S3V_FillRect,
   S3V_BlitRect,
   NULL
   /* dummy... MGA_BlitTransRect */
};


Bool
S3VDGAInit(ScreenPtr pScreen)
{   
   ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
   S3VPtr ps3v = S3VPTR(pScrn);
   DGAModePtr modes = NULL, newmodes = NULL, currentMode;
   DisplayModePtr pMode, firstMode;
   int Bpp = pScrn->bitsPerPixel >> 3;
   int num = 0;
   Bool oneMore;

   PVERB5("	S3VDGAInit\n");
    
   pMode = firstMode = pScrn->modes;

   while(pMode) {
	/* The MGA driver wasn't designed with switching depths in
	   mind.  Subsequently, large chunks of it will probably need
	   to be rewritten to accommodate depth changes in DGA mode */

	if(0 /*pScrn->displayWidth != pMode->HDisplay*/) {
	    newmodes = xrealloc(modes, (num + 2) * sizeof(DGAModeRec));
	    oneMore = TRUE;
	} else {
	    newmodes = xrealloc(modes, (num + 1) * sizeof(DGAModeRec));
	    oneMore = FALSE;
	}

	if(!newmodes) {
	   xfree(modes);
	   return FALSE;
	}
	modes = newmodes;

SECOND_PASS:

	currentMode = modes + num;
	num++;

	currentMode->mode = pMode;
	currentMode->flags = DGA_CONCURRENT_ACCESS | DGA_PIXMAP_AVAILABLE;
	if(!ps3v->NoAccel)
	   currentMode->flags |= DGA_FILL_RECT | DGA_BLIT_RECT;
	if(pMode->Flags & V_DBLSCAN)
	   currentMode->flags |= DGA_DOUBLESCAN;
	if(pMode->Flags & V_INTERLACE)
	   currentMode->flags |= DGA_INTERLACED;
	currentMode->byteOrder = pScrn->imageByteOrder;
	currentMode->depth = pScrn->depth;
	currentMode->bitsPerPixel = pScrn->bitsPerPixel;
	currentMode->red_mask = pScrn->mask.red;
	currentMode->green_mask = pScrn->mask.green;
	currentMode->blue_mask = pScrn->mask.blue;
	currentMode->visualClass = (Bpp == 1) ? PseudoColor : TrueColor;
	currentMode->viewportWidth = pMode->HDisplay;
	currentMode->viewportHeight = pMode->VDisplay;
	/* currentMode->xViewportStep = (3 - ps3v->BppShift); */
				/* always 1 on ViRGE ? */
	currentMode->xViewportStep = 1;
	currentMode->yViewportStep = 1;
	currentMode->viewportFlags = DGA_FLIP_RETRACE;
	/* currentMode->offset = ps3v->YDstOrg * (pScrn->bitsPerPixel / 8);
	 * MGA, 0 for ViRGE */
	currentMode->offset = 0;
	/* currentMode->address = pMga->FbStart;   MGA */
	currentMode->address = ps3v->FBBase;
/*cep*/
  xf86ErrorFVerb(VERBLEV, 
	"	S3VDGAInit firstone vpWid=%d, vpHgt=%d, Bpp=%d, mdbitsPP=%d\n",
		currentMode->viewportWidth,
		currentMode->viewportHeight,
		Bpp,
		currentMode->bitsPerPixel
		 );
		

	if(oneMore) { /* first one is narrow width */
	    currentMode->bytesPerScanline = ((pMode->HDisplay * Bpp) + 3) & ~3L;
	    currentMode->imageWidth = pMode->HDisplay;
	    /* currentMode->imageHeight =  pMga->FbUsableSize /
					currentMode->bytesPerScanline; 
					MGA above */
	    currentMode->imageHeight =  pMode->VDisplay;
	    currentMode->pixmapWidth = currentMode->imageWidth;
	    currentMode->pixmapHeight = currentMode->imageHeight;
	    currentMode->maxViewportX = currentMode->imageWidth - 
					currentMode->viewportWidth;
	    /* this might need to get clamped to some maximum */
	    currentMode->maxViewportY = currentMode->imageHeight -
					currentMode->viewportHeight;
	    oneMore = FALSE;

/*cep*/
  xf86ErrorFVerb(VERBLEV, 
	"	S3VDGAInit imgHgt=%d, ram=%d, bytesPerScanl=%d\n",
		currentMode->imageHeight,
		ps3v->videoRambytes,
		currentMode->bytesPerScanline );
		
	    goto SECOND_PASS;
	} else {
	    currentMode->bytesPerScanline = 
			((pScrn->displayWidth * Bpp) + 3) & ~3L;
	    currentMode->imageWidth = pScrn->displayWidth;
	    /* currentMode->imageHeight =  pMga->FbUsableSize /
					currentMode->bytesPerScanline; 
					*/
	    currentMode->imageHeight =  ps3v->videoRambytes /
	    			currentMode->bytesPerScanline;
	    currentMode->pixmapWidth = currentMode->imageWidth;
	    currentMode->pixmapHeight = currentMode->imageHeight;
	    currentMode->maxViewportX = currentMode->imageWidth - 
					currentMode->viewportWidth;
	    /* this might need to get clamped to some maximum */
	    currentMode->maxViewportY = currentMode->imageHeight -
					currentMode->viewportHeight;
	}	    

	pMode = pMode->next;
	if(pMode == firstMode)
	   break;
   }

   ps3v->numDGAModes = num;
   ps3v->DGAModes = modes;

   return DGAInit(pScreen, &S3V_DGAFuncs, modes, num);  
}


static Bool
S3V_SetMode(
   ScrnInfoPtr pScrn,
   DGAModePtr pMode
){
   static int OldDisplayWidth[MAXSCREENS];
   int index = pScrn->pScreen->myNum;

   S3VPtr ps3v = S3VPTR(pScrn);

   if(!pMode) { /* restore the original mode */
	/* put the ScreenParameters back */
	
	pScrn->displayWidth = OldDisplayWidth[index];
	
        S3VSwitchMode(index, pScrn->currentMode, 0);
	ps3v->DGAactive = FALSE;
   } else {
	if(!ps3v->DGAactive) {  /* save the old parameters */
	    OldDisplayWidth[index] = pScrn->displayWidth;

	    ps3v->DGAactive = TRUE;
	}

	pScrn->displayWidth = pMode->bytesPerScanline / 
			      (pMode->bitsPerPixel >> 3);

        S3VSwitchMode(index, pMode->mode, 0);
   }
   
   return TRUE;
}



static int  
S3V_GetViewport(
  ScrnInfoPtr pScrn
){
    S3VPtr ps3v = S3VPTR(pScrn);

    return ps3v->DGAViewportStatus;
}

static void 
S3V_SetViewport(
   ScrnInfoPtr pScrn, 
   int x, int y, 
   int flags
){
   S3VPtr ps3v = S3VPTR(pScrn);

   S3VAdjustFrame(pScrn->pScreen->myNum, x, y, flags);
   ps3v->DGAViewportStatus = 0;  /* MGAAdjustFrame loops until finished */
}

static void 
S3V_FillRect (
   ScrnInfoPtr pScrn, 
   int x, int y, int w, int h, 
   unsigned long color
){
    S3VPtr ps3v = S3VPTR(pScrn);

    if(ps3v->AccelInfoRec) {
	(*ps3v->AccelInfoRec->SetupForSolidFill)(pScrn, color, GXcopy, ~0);
	(*ps3v->AccelInfoRec->SubsequentSolidFillRect)(pScrn, x, y, w, h);
	SET_SYNC_FLAG(ps3v->AccelInfoRec);
    }
}

static void 
S3V_BlitRect(
   ScrnInfoPtr pScrn, 
   int srcx, int srcy, 
   int w, int h, 
   int dstx, int dsty
){
    S3VPtr ps3v = S3VPTR(pScrn);

    if(ps3v->AccelInfoRec) {
	int xdir = ((srcx < dstx) && (srcy == dsty)) ? -1 : 1;
	int ydir = (srcy < dsty) ? -1 : 1;

	(*ps3v->AccelInfoRec->SetupForScreenToScreenCopy)(
		pScrn, xdir, ydir, GXcopy, ~0, -1);
	(*ps3v->AccelInfoRec->SubsequentScreenToScreenCopy)(
		pScrn, srcx, srcy, dstx, dsty, w, h);
	SET_SYNC_FLAG(ps3v->AccelInfoRec);
    }
}

#if 0
static void 
MGA_BlitTransRect(
   ScrnInfoPtr pScrn, 
   int srcx, int srcy, 
   int w, int h, 
   int dstx, int dsty,
   unsigned long color
){
  /* this one should be separate since the XAA function would
     prohibit usage of ~0 as the key */
}
#endif

static Bool 
S3V_OpenFramebuffer(
   ScrnInfoPtr pScrn, 
   char **name,
   unsigned char **mem,
   int *size,
   int *offset,
   int *flags
){
    S3VPtr ps3v = S3VPTR(pScrn);

    *name = NULL; 		/* no special device */
    *mem = (unsigned char*)ps3v->PciInfo->memBase[0];
    *size = ps3v->videoRambytes;
    *offset = 0;
    *flags = DGA_NEED_ROOT;

    return TRUE;
}