/*
 * BDM2: Banked dumb monochrome driver
 * Pascal Haible 9/93, haible@izfm.uni-stuttgart.de
 *
 * bdm2/driver/sigma/sigmadriv.c
 *
 * Parts derived from:
 * hga2/*
 * Author:  Davor Matic, dmatic@athena.mit.edu
 * and
 * vga256/*
 * Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany.
 *
 * see bdm2/COPYRIGHT for copyright and disclaimers.
 */

/* $XFree86: mit/server/ddx/x386/bdm2/drivers/sigma/sigmadriv.c,v 2.3 1993/12/01 12:37:29 dawes Exp $ */

#include "X.h"
#include "input.h"
#include "screenint.h"

#include "compiler.h"

#include "x386.h"
#include "x386Priv.h"
#include "xf86_OSlib.h"
#include "xf86_Config.h"
#include "bdm.h"
#include "sigmaHW.h"

int sigma_Current_mode = sigma_Textmode;
int sigmaMemBase = SIGMA_DEF_MEM_BASE;

/*
 * Define the SIGMA I/O Ports
 */
unsigned SIGMA_IOPorts[] = { SLV_EN1, SLV_W16, SLV_W17, SLV_W18,
			SLV_BLANK, SLV_ZOOM, SLV_GR0, SLV_GR1,
			SLV_BANK0, SLV_BANK1, SLV_BANK2, SLV_BANK3,
			SLV_HIRES /* = MONOEN */, SLV_BOLD /* = WOB */ };
int Num_SIGMA_IOPorts = (sizeof(SIGMA_IOPorts)/sizeof(SIGMA_IOPorts[0]));

volatile int delay_dummy=0;
#define SIGMA_DELAY	{ delay_dummy++; }

char *	SIGMAIdent();
Bool	SIGMAProbe();
void	SIGMAEnterLeave();
void *	SIGMAInit();
void *	SIGMASave();
void	SIGMARestore();
void	SIGMAAdjust();
Bool	SIGMASaveScreen();
void	SIGMAGetMode();

/* Assembler functions in sigmabank.s
 * - to be called by assembler functions only! */
extern void SIGMASetRead();
extern void SIGMASetWrite();
extern void SIGMASetReadWrite();

#if 0
/* From bdm.h -- see there, this here might not be up to date */
/*
 * structure for accessing the video chip`s functions
 *
 * We are doing a total encapsulation of the driver's functions.
 * Banking (bdmSetReadWrite(p) etc.) is done in bdmBank.c
 *   using the chip's function pointed to by
 *   bmpSetReadWriteFunc(bank) etc.
 */
typedef struct {
  char * (* ChipIdent)();
  Bool (* ChipProbe)();
  void (* ChipEnterLeave)();
  void * (* ChipInit)();
  void * (* ChipSave)();
  void (* ChipRestore)();
  void (* ChipAdjust)();
  Bool (* ChipSaveScreen)();
  void (* ChipGetMode)();
  /* These are the chip's banking functions:		*/
  /* they do the real switching to the desired bank	*/
  /* they 'become' bdmSetReadFunc() etc.		*/
  void (* ChipSetRead)();
  void (* ChipSetWrite)();
  void (* ChipSetReadWrite)();
  /* Bottom and top of the banking window (rel. to ChipMapBase)	*/
  /* Note: Top = highest accessable byte + 1			*/
  void *ChipReadBottom;
  void *ChipReadTop;
  void *ChipWriteBottom;
  void *ChipWriteTop;
  /* Memory to map      */
  int ChipMapBase;
  int ChipMapSize;      /* replaces MEMTOMAP */
  int ChipSegmentSize;
  int ChipSegmentShift;
  int ChipSegmentMask;
  Bool ChipUse2Banks;
  /* Display size is given by the driver */
  int ChipHDisplay;
  int ChipVDisplay;
  /* In case Scan Line in mfb is longer than HDisplay */
  int ChipScanLineWidth;
  OFlagSet ChipOptionFlags;
} bdmVideoChipRec, *bdmVideoChipPtr;
#endif /* 0 */

bdmVideoChipRec SIGMA = {
  /* Functions */
  SIGMAIdent,
  SIGMAProbe,
  SIGMAEnterLeave,
  SIGMAInit,
  SIGMASave,
  SIGMARestore,
  SIGMAAdjust,
  SIGMASaveScreen,
  NoopDDA,			/* SIGMAGetMode */
  SIGMASetRead,
  SIGMASetWrite,
  SIGMASetReadWrite,
  (void *)SIGMA_BANK1_BOTTOM,	/* ReadBottom */
  (void *)SIGMA_BANK1_TOP,	/* ReadTop */
  (void *)SIGMA_BANK2_BOTTOM,	/* WriteBottom */
  (void *)SIGMA_BANK2_TOP,	/* WriteTop */
  SIGMA_DEF_MAP_BASE,		/* MapBase */
  SIGMA_MAP_SIZE,		/* MapSize */
  SIGMA_SEGMENT_SIZE,		/* SegmentSize */
  SIGMA_SEGMENT_SHIFT,		/* SegmentShift */
  SIGMA_SEGMENT_MASK,		/* SegmentMask */
  TRUE,				/* Use2Banks */
  SIGMA_HDISPLAY,		/* HDisplay */
  SIGMA_VDISPLAY,		/* VDisplay */
  SIGMA_SCAN_LINE_WIDTH,	/* ScanLineWidth */
  {0,},
};

/*
 * SIGMAIdent
 */

char *
SIGMAIdent(n)
	int n;
{
/* "Sigma L-View (LVA-PC-00SP0)", "Sigma LaserView PLUS (LDA-1200)" */
/* But this must be the chipset name optionally preset (lowercase!?)*/
/* using only one as I don't know how to tell them apart */
static char *chipsets[] = {"sigmalview"};
if (n >= sizeof(chipsets) / sizeof(char *))
	return(NULL);
else return(chipsets[n]);
}

/*
 * SIGMAProbe --
 *      check whether an SIGMA based board is installed
 */

Bool
SIGMAProbe()
{
  /*
   * Set up I/O ports to be used by this card
   */
  xf86ClearIOPortList(bdm2InfoRec.scrnIndex);
  xf86AddIOPorts(bdm2InfoRec.scrnIndex, Num_SIGMA_IOPorts, SIGMA_IOPorts);

  if (bdm2InfoRec.chipset) {
	/* Chipset preset */
	if (strcmp(bdm2InfoRec.chipset, SIGMAIdent(0)))
		/* desired chipset != this one */
		return (FALSE);
	else {
		SIGMAEnterLeave(ENTER);
		/* go on with videoram etc. below */
	}
  }
  else {
	Bool found=FALSE;
	unsigned char zoom_save;

	/* Enable I/O Ports */
	SIGMAEnterLeave(ENTER);

	/*
	 * Check if there is a Sigma L-View or LaserView PLUS board
	 * in the system.
	 */

	/* Test if ZOOM bit (bit 7 on extended port 0x4649) is r/w */
	/* save it first */
	zoom_save = inb(SLV_ZOOM);
	SIGMA_DELAY;
	outb(SLV_ZOOM,0);
	SIGMA_DELAY;
	found=((inb(SLV_ZOOM)&0x80)==0);
	SIGMA_DELAY;
	outb(SLV_ZOOM,0x80);
	SIGMA_DELAY;
	found=found && ((inb(SLV_ZOOM)&0x80)==0x80);
	SIGMA_DELAY;
	/* write back */
	if (found)
		outb(SLV_ZOOM, (zoom_save & 0x80));
					/* write only 0x00 or 0x80 */

	/* There seems to be no easy way to tell if it is an PLUS or not
	 * (apart perhaps from writing to both planes) */
	if (found) {
		bdm2InfoRec.chipset = SIGMAIdent(0);
		ErrorF("%s %s: %s detected\n",
			XCONFIG_PROBED,
			bdm2InfoRec.name,
			bdm2InfoRec.chipset);
	} else {
	/* there is no Sigma L-View card */
		SIGMAEnterLeave(LEAVE);
		return(FALSE);
	}
  } /* else (bdm2InfoRec.chipset) -- bdm2InfoRec.chipset is already set */

/* The following is done for both probed and preset chipset */

  if (!bdm2InfoRec.videoRam) {
	/* videoram not given in Xconfig */
	bdm2InfoRec.videoRam=256;
  }
  if (bdm2InfoRec.MemBase!=0) {
	if ( (bdm2InfoRec.MemBase!=0xA0000L) &&
		(bdm2InfoRec.MemBase!=0xB0000L) &&
		(bdm2InfoRec.MemBase!=0xC0000L) &&
		(bdm2InfoRec.MemBase!=0xD0000L) &&
		(bdm2InfoRec.MemBase!=0xE0000L) ) {
	    /* Invalid MemBase */
	    ErrorF("%s %s: %s: Invalid MemBase 0x%x (must be 0x{ABCDE}0000),\n\t\tusing default\n",
		XCONFIG_GIVEN,
		bdm2InfoRec.name,
		bdm2InfoRec.chipset,
		sigmaMemBase);
	    sigmaMemBase=SIGMA_DEF_MEM_BASE;
	    ErrorF("%s %s: %s using mem base 0x%x\n",
		XCONFIG_PROBED,
		bdm2InfoRec.name,
		bdm2InfoRec.chipset,
		sigmaMemBase);
	} else {
	    /* Valid MemBase */
	    sigmaMemBase=bdm2InfoRec.MemBase;
	    SIGMA.ChipMapBase=sigmaMemBase;
	    ErrorF("%s %s: %s using mem base 0x%x\n",
		XCONFIG_GIVEN,
		bdm2InfoRec.name,
		bdm2InfoRec.chipset,
		sigmaMemBase);
	}
  } else {
	/* Default MemBase */
	sigmaMemBase=SIGMA_DEF_MEM_BASE;
	ErrorF("%s %s: %s using mem base 0x%x\n",
		XCONFIG_PROBED,
		bdm2InfoRec.name,
		bdm2InfoRec.chipset,
		sigmaMemBase);
  }
  /* We do 'virtual' handling here as it is highly chipset specific */
  /* Screen size (pixels) is fixed, virtual size can be larger up to
   * ChipMaxVirtualX and ChipMaxVirtualY */
  /* Real display size is given by SIGMA_HDISPLAY and SIGMA_VDISPLAY,
   * desired virtual size is bdm2InfoRec.virtualX and bdm2InfoRec.virtualY.
   * Think they can be -1 at this point.
   * Maximum virtual size as done by the driver is
   * SIGMA_MAX_VIRTUAL_X and ..._Y
   */
   if (!(bdm2InfoRec.virtualX < 0)) {
	/* virtual set in Xconfig */
	ErrorF("%s %s: %s: Virtual not allowed for this chipset\n",
		XCONFIG_PROBED, bdm2InfoRec.name, bdm2InfoRec.chipset);
   }
   /* Set virtual to real size */
   bdm2InfoRec.virtualX = SIGMA_HDISPLAY;
   bdm2InfoRec.virtualY = SIGMA_VDISPLAY;
   /* Must return real display size */
   /* hardcoded in SIGMA */
   return(TRUE);
}

/*
 * SIGMAEnterLeave --
 *      enable/disable io permissions
 */

void 
SIGMAEnterLeave(enter)
     Bool enter;
{
  if (enter)
	xf86EnableIOPorts(bdm2InfoRec.scrnIndex);
  else
	xf86DisableIOPorts(bdm2InfoRec.scrnIndex);
}

/*
 * SIGMAInit --
 *      Handle the initialization of the SIGMAs registers
 */

void *
SIGMAInit(mode)
     DisplayModePtr mode;
{
return((void *)sigma_Graphmode);
}

/*
 * SIGMASave --
 *      save the current video mode
 */

void *
SIGMASave(save)
     pointer save;
{
return((void *)sigma_Current_mode);
}

/*
 * SIGMARestore --
 *      restore a video mode
 */

void
SIGMARestore(restore)
     pointer restore;
{
    if ((int)restore==sigma_Textmode) {
	/* Blank the screen to black */
	outb(SLV_GR0,0);
	SIGMA_DELAY;
	outb(SLV_GR1,0);
	SIGMA_DELAY;
	outb(SLV_BLANK,0);
	SIGMA_DELAY;
	/* Disable adapter memory */
	outb(SLV_EN1,0);
	SIGMA_DELAY;

	/* deselect hires */
	outb(SLV_HIRES,0x0);
	SIGMA_DELAY;
	/* Unblank the screen */
	outb(SLV_GR0,0x80);
	SIGMA_DELAY;
	outb(SLV_GR1,0x80);
	SIGMA_DELAY;
	outb(SLV_BLANK,0x80);
	SIGMA_DELAY;

	sigma_Current_mode=sigma_Textmode;

    } else if ((int)restore==sigma_Graphmode) {
	/* Blank the screen to black */
	outb(SLV_GR0,0);
	SIGMA_DELAY;
	outb(SLV_GR1,0);
	SIGMA_DELAY;
	outb(SLV_BLANK,0);
	SIGMA_DELAY;
	/* Disable adapter memory */
	outb(SLV_EN1,0);
	SIGMA_DELAY;

	/* Set page frame */
	outb(SLV_W16,((sigmaMemBase & 0x10000L) ? 0 : 0x80));
	SIGMA_DELAY;
	outb(SLV_W17,((sigmaMemBase & 0x20000L) ? 0 : 0x80));
	SIGMA_DELAY;
	outb(SLV_W18,((sigmaMemBase & 0x40000L) ? 0 : 0x80));
	SIGMA_DELAY;
	outb(SLV_ZOOM,0x80);
	SIGMA_DELAY;

	/* */
	outb(SLV_HIRES,0x80);
	SIGMA_DELAY;
	/* Unblank the screen */
	outb(SLV_GR0,0x80);
	SIGMA_DELAY;
	outb(SLV_GR1,0x80);
	SIGMA_DELAY;
	outb(SLV_BLANK,0x80);
	SIGMA_DELAY;
	/* Set enable state */
	outb(SLV_EN1,0x80);

	sigma_Current_mode=sigma_Graphmode;

    } else ErrorF("Warning: SIGMARestore called with invalid arg.\n");
}

/*
 * SIGMASaveScreen();
 *	Disable the video on the frame buffer (screensaver)
 */

Bool
SIGMASaveScreen(pScreen,on)
	ScreenPtr pScreen;
	Bool      on;
{
if (on == SCREEN_SAVER_FORCER)
	SetTimeSinceLastInputEvent();
if (x386VTSema) {
	if (on) { /* Grrr! SaveScreen(on=TRUE) means turn ScreenSaver off */
		/* Unblank to 4 gray levels */
		outb(SLV_BLANK,0x80);
		SIGMA_DELAY;
		outb(SLV_GR0,0x80);
		SIGMA_DELAY;
		outb(SLV_GR1,0x80);
	} else {
		/* Blank to black */
		outb(SLV_BLANK,0);
		SIGMA_DELAY;
		outb(SLV_GR0,0);
		SIGMA_DELAY;
		outb(SLV_GR1,0);
	}
} /* if we are not on the active VT, don't do anything - the screen
   * will be visible as soon as we switch back anyway (?) */
return(TRUE);
}

/* SIGMAAdjust --
 *      adjust the current video frame to display the mousecursor
 *      (x,y) is the upper left corner to be displayed.
 *      The Sigma L-View / LaserView PLUS can't pan.
 */
void
SIGMAAdjust(x,y)
	int x, y;
{
}
