/*
 * Written by Jake Richter
 * Copyright (c) 1989, 1990 Panacea Inc., Londonderry, NH - All Rights Reserved
 *
 * This code may be freely incorporated in any program without royalty, as
 * long as the copyright notice stays intact.
 *
 * Additions by Kevin E. Martin (martin@cs.unc.edu)
 *
 * KEVIN E. MARTIN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL KEVIN E. MARTIN BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include "X.h"
#include "Xmd.h"
#include "input.h"
#include "servermd.h"
#include "scrnintstr.h"
#include "site.h"

#include "compiler.h"

#include "x386Procs.h"
#include "x386OSD.h"

#include "reg8514.h"
#include <sys/types.h>
#include <sys/mman.h>
#include "vga.h"

typedef struct {
  vgaHWRec std;               /* good old IBM VGA */
  unsigned char s3reg[10];  /* Video Atribute */  
  unsigned char s3sysreg[36];  /* Video Atribute */
} s3HWRec, *s3HWPtr;

int vgaIOBase = 0x3d0;
extern x386InfoRec x386Info;
s3HWRec oldS3;

pointer vgaNewVideoState = NULL;
static short numPlanes = -1;
static short resolution = -1;
static short old_DAC_MASK = -1;
static LUTENTRY oldlut[256];
static short LUTInited = -1;
static short s3Initialised = 0;
pointer vgaBase;
pointer foobase;
int  old_clock;

short chip_id = 0;

void CleanUp8514(void)

{
    int i;


   vgaBase = foobase;

    WaitQueue(8);
     outp(0x3d4, 0x35);
     i = inp(0x3d5);
     outp(0x3d5, (i & 0xf0));

    outpw(ADVFUNC_CNTL, 0);
    vgaHWRestore(&oldS3);



/*  (ClockSelect)(restore->std.NoClock); */
      /* restore s3 special bits */
    if (chip_id == S3_801) {
      /* restore 801 specific registers */

      for (i = 32; i < 35 ; i++)
	{
	  outp(0x3d4, 0x40 + i);
	  outp(0x3d5, oldS3.s3sysreg[i]);

	}     
    }

    for (i = 0; i < 5; i++)
      {
	outp(0x3d4, 0x30 + i);
	outp(0x3d5, oldS3.s3reg[i]);
	outp(0x3d4, 0x38 + i);
	outp(0x3d5, oldS3.s3reg[5 + i]);	
      }


    for (i = 0; i < 16; i++)
      {
	outp(0x3d4, 0x40 + i);
	outp(0x3d5, oldS3.s3sysreg[i]);
      }     



    outp(0x3c2, old_clock);
    outw(0x3C4, 0x0300); /* now reenable the timing sequencer */
  }





void Init8514(   DisplayModePtr mode)
{

  short i;
  char	Graphics[9];
vgaHWRec new;

/*  s3HWRec	 new; */



  if (!s3Initialised)
  {
 
#ifdef __386BSD__
     vgaBase = (pointer) mmap(0, 0x20000, PROT_READ|PROT_WRITE, MAP_FILE,
 			     x386Info.consoleFd, 0);
     foobase = vgaBase;

     if (vgaBase == -1)
       FatalError("Could not memory map /dev/vga (the video memory)\n");
#endif
#ifdef __linux__
     vgaBase= (pointer) malloc(64 * 1024);
     vgaBase=mmap(vgaBase, 64 * 1024, PROT_WRITE | PROT_READ,
		     MAP_SHARED | MAP_FIXED,
		     open("/dev/mem", O_RDWR), 0xA0000);
     foobase = vgaBase;		     
     if (vgaBase == (pointer) -1)
       FatalError("Could not memory map /dev/mem (the video memory)\n");
#endif


     /* blanket save of state */

     /* unlock */
     outp(0x3d4,0x38);
     outp(0x3d5, 0x48);
     outp(0x3d4,0x39);
     outp(0x3d5, 0xa5);
      old_clock = inb(0x3CC);


     outp(0x3d4, 0x35); /* select segment 0 */
     i = inp(0x3d5);
     outp(0x3d5, i & 0xf0);

     vgaHWSave(&oldS3, sizeof(oldS3));


     
     for (i = 0; i < 5; i++)
     {
	outp(0x3d4, 0x30 + i);
	oldS3.s3reg[i] = inp(0x3d5);
	outp(0x3d4, 0x38 + i);
	oldS3.s3reg[5 + i] = inp(0x3d5);	
     }
   chip_id = oldS3.s3reg[0];

   if (chip_id == S3_801) {
     ErrorF("S3 chipset is a 801 \n");
   } else if (chip_id == S3_911) {
     ErrorF("S3 chipset is a 911 \n");
   } else if (chip_id == S3_924) {
     ErrorF("S3 chipset is a 924\n");
     ErrorF("Some poeple are having problems with the 924 chip\n");
     ErrorF("If your machine locks up, try with turbo disabled on the card\n");
   } else ErrorF("Unknown S3 chipset: %x\n", chip_id);

   
   outp(0x3d4, 0x11); /* allow writting? */
   outp(0x3d5, 0x00);
     for (i = 0; i < 16 ; i++)
     {
	outp(0x3d4, 0x40 + i);
	oldS3.s3sysreg[i] = inp(0x3d5);
     }     

     for (i = 32; i < 35 ; i++)
     {
	outp(0x3d4, 0x40 + i);
	oldS3.s3sysreg[i] = inp(0x3d5);
     }     
   
     s3Initialised = 1;     


     outp(DAC_MASK,0);   

  }
	 

		



    outp(0x3c4,1);
    outp(0x3c5,0x20);
  /*
   * Get NoClock
   */
  new.NoClock   = 0;

  /*
   * compute correct Hsync & Vsync polarity
   */
  if ((mode->Flags & (V_PHSYNC | V_NHSYNC))
      && (mode->Flags & (V_PVSYNC | V_NVSYNC)))
      {
	new.MiscOutReg = 0x23;
	if (mode->Flags & V_NHSYNC) new.MiscOutReg |= 0x40;
	if (mode->Flags & V_NVSYNC) new.MiscOutReg |= 0x80;
      }
      else
      {
	if      (mode->VDisplay < 400) new.MiscOutReg = 0xA3;
	else if (mode->VDisplay < 480) new.MiscOutReg = 0x63;
	else if (mode->VDisplay < 768) new.MiscOutReg = 0xcf;
	else                           new.MiscOutReg = 0x3f;
      }
   if ((mode->Flags & V_INTERLACE) != 0 ) {

     mode->Clock |= 0x20;

     mode->VDisplay =  mode->VDisplay /2;
     mode->VSyncStart=  mode->VSyncStart/2;
     mode->VSyncEnd=  mode->VSyncEnd /2;
     mode->VTotal=  mode->VTotal / 2;
   }
    /*
   * Time Sequencer
   */
  new.Sequencer[0] = 0x03;
  new.Sequencer[1] = 0x01; 
  new.Sequencer[2] = 0x0F;
  new.Sequencer[3] = 0x00;                             /* Font select */
  new.Sequencer[4] = 0x0E;                             /* Misc */

  /*
   * CRTC Controller
   */ 


  new.CRTC[0]  = (mode->HTotal >> 3) -  5;
  new.CRTC[1]  = (mode->HDisplay >> 3) - 1;
  new.CRTC[2]  = (mode->HSyncStart >> 3) -1;

  new.CRTC[3]  = ((mode->HSyncEnd >> 3) & 0x1F) | 0x80; 

  new.CRTC[4]  = (mode->HSyncStart >> 3);
  new.CRTC[5]  = (((mode->HSyncEnd >> 3) & 0x20 ) << 2 )
    | (((mode->HSyncEnd >> 3)) & 0x1F);
  new.CRTC[6]  = (mode->VTotal - 2) & 0xFF;
  new.CRTC[7]  = (((mode->VTotal -2) & 0x100) >> 8 )
    | (((mode->VDisplay -1) & 0x100) >> 7 )
      | ((mode->VSyncStart & 0x100) >> 6 )
	| (((mode->VSyncStart) & 0x100) >> 5 )
	  | 0x00
	    | (((mode->VTotal -2) & 0x200)   >> 4 )
	      | (((mode->VDisplay -1) & 0x200) >> 3 )
		| ((mode->VSyncStart & 0x200) >> 2 );
  new.CRTC[8]  = 0x00;
  new.CRTC[9]  = ((mode->VSyncStart & 0x200) >>4) | 0x40;

  new.CRTC[10] = 0x40;
  new.CRTC[10] = 0x00; /* vgaHW */
  new.CRTC[11] = 0x00;
  new.CRTC[12] = 0x00;
  new.CRTC[13] = 0x00;

  new.CRTC[15] = 0x80;
  new.CRTC[15] = 0; /* vgaHW */

  new.CRTC[16] = mode->VSyncStart & 0xFF;
  new.CRTC[17] = (mode->VSyncEnd & 0x0F) | 0x20;
  new.CRTC[18] = (mode->VDisplay -1) & 0xFF; 
  new.CRTC[19] = 0x80; /* 640 >> 3; /*  just a guess */



  new.CRTC[20] = 0x00; /* 1024 */


  new.CRTC[21] = mode->VSyncStart & 0xFF;
  new.CRTC[22] = (mode->VSyncStart +1) & 0xFF;  


  new.CRTC[23] = 0xAB; /* 640 */
  new.CRTC[23] = 0xE3; /* 1024 */
/*  new.CRTC[23] = 0xc3;  vgahw */

  new.CRTC[24] = 0xFF;

  /*
   * Graphics Display Controller
   */
  new.Graphics[0] = 0x00;
  new.Graphics[1] = 0x00;
  new.Graphics[2] = 0x00;
  new.Graphics[3] = 0x00;
  new.Graphics[4] = 0x00;
  new.Graphics[5] = 0xc0;
  new.Graphics[5] = 0x40; /* vgaHW */
  new.Graphics[6] = 0x05;   /* only map 64k VGA memory !!!! */
  new.Graphics[7] = 0x0F;
  new.Graphics[8] = 0xFF;

  new.Attribute[0]  = 0x00; /* standart colormap translation */
  new.Attribute[1]  = 0x01;
  new.Attribute[2]  = 0x02;
  new.Attribute[3]  = 0x03;
  new.Attribute[4]  = 0x04;
  new.Attribute[5]  = 0x05;
  new.Attribute[6]  = 0x06;
  new.Attribute[7]  = 0x07;
  new.Attribute[8]  = 0x10;
  new.Attribute[9]  = 0x11;
  new.Attribute[10] = 0x12;
  new.Attribute[11] = 0x13;
  new.Attribute[12] = 0x14;
  new.Attribute[13] = 0x15;
  new.Attribute[14] = 0x16;
  new.Attribute[15] = 0x17;
  new.Attribute[16] = 0x51; /* wrong for the ET4000 */
  new.Attribute[17] = 0x00; /* 1024 */
  new.Attribute[17] = 0xff;
  new.Attribute[18] = 0x0F;
  new.Attribute[19] = 0x10;
  new.Attribute[20] = 0x10;



  outpw(0x3C4, ((new.Sequencer[0] & 0xFD) << 8) | 0x00);

  if (vgaIOBase == 0x3B0)
    new.MiscOutReg &= 0xFE;
  else
    new.MiscOutReg |= 0x01;

  outp(0x3C2, new.MiscOutReg);

  /*
   * This here is a workaround a bug in the kd-driver. We MUST explicitely
   * restore the font we got, when we entered graphics mode.
   * The bug was seen on ESIX, and ISC 2.0.2 when using a monochrome
   * monitor.
   *
   * BTW, also GIO_FONT seems to have a bug, so we cannot use it, to get
   * a font.
   */
/*  new.FontInfo = 0; */



  i = inp(vgaIOBase + 0x0A); /* reset flip-flop */
  for (i=0; i<16; i++) { outp(0x3C0,i); outp(0x3C0, new.Attribute[i]); }

  for (i=16; i<21;i++) { outp(0x3C0,i | 0x20); 
			 outp(0x3C0, new.Attribute[i]); } 
  for (i=0; i<24; i++) outpw(vgaIOBase + 4,(new.CRTC[i] << 8) | i);



outp(0x3d4,0x31);
outp(0x3d5, 0x8d); 
outp(0x3d4,0x32);
outp(0x3d5, 0x00);

outp(0x3d4,0x33);
outp(0x3d5, 0x20); 

outp(0x3d4,0x34);
outp(0x3d5, 0x10);  /* 1024 */


outp(0x3d4,0x35);
if (mode->HDisplay > 800 )
 outp(0x3d5, 0x00); /* 1024 */
else outp(0x3d5, 0x13);

outp(0x3d4,0x3a);
outp(0x3d5, 0xb5); /* was 95 */

outp(0x3d4,0x3b);

 outp(0x3d5, (new.CRTC[0] + new.CRTC[4] + 1)/ 2 ); 



outp(0x3d4,0x3c);
outp(0x3d5, 0x44); /* 0xff 640 */

  outp(0x3d4,0x40);
  if (chip_id == S3_801) {
    outp(0x3d5,0x83); /* 801 */
  } else {
    outp(0x3d5, 0x09);
  }

outp(0x3d4,0x41);
outp(0x3d5, 0x14); /* 1024  */


outp(0x3d4, 0x42);
outp(0x3d5, mode->Clock);


outp(0x3d4, 0x43);
outp(0x3d5, 0x00);

outp(0x3d4, 0x44);
outp(0x3d5, 0x00);

outp(0x3d4, 0x45);
outp(0x3d5, 0x00);

if (chip_id == S3_801) {

/* S3 801 specific initialization */

  outp(0x3d4, 0x58);
  outp(0x3d5, 0x00);
  outp(0x3d4,0x59);
  outp(0x3d5,0x0a);  
  outp(0x3d4,0x54);
  outp(0x3d5,0xa0); 
  outp(0x3d4,0x60);
  outp(0x3d5,0x2f); 
  outp(0x3d4, 0x61);
  outp(0x3d5, 0x81);
  outp(0x3d4, 0x62);
  outp(0x3d5, 0);

}
   for (i=0; i<9;  i++) {

	outpw(0x3CE, (new.Graphics[i] << 8) | i);
		outp(0x3ce, i); Graphics[i] = inp(0x3cf); 
	} 

  outp(0x3C6,0xFF);

  outpw(0x3d4, 0x0035) ;

  for (i=0; i<5;  i++) outpw(0x3C4, (new.Sequencer[i] << 8) | i);



  if (mode->HDisplay < 800)  {
    outpw(0x4ae8,0x0003);
  }  else outpw(0x4ae8,0x0007);

/*  WaitQueue(8);
  outpw(MULTIFUNC_CNTL, 0xe000 |  0x40); */



    /* Blank the screen temporarily to display color 0 by turning the
     * display of all planes off
     */
    old_DAC_MASK = inp(DAC_MASK);
    outp(DAC_MASK, 0x00);


    /* Reset the 8514/A, and disable all interrupts. */
    outpw(SUBSYS_CNTL, GPCTRL_RESET | CHPTEST_NORMAL);
    outpw(SUBSYS_CNTL, GPCTRL_ENAB | CHPTEST_NORMAL);

    /* Check to see if an 8514/A is actually installed by writing to
     * the ERR_TERM register, and reading back.  The 0x5a5a value is
     * entirely arbitrary.
     */

    /* Get board status information */

    i = inpw(SUBSYS_STAT);

    /* Check if monitor capable of 1024x768 is attached.
     * If not, print error message and abort.
     * In this case, we want an IBM 8514 compatible monitor.
     */

/*    if ((i & MONITORID_MASK) != MONITORID_8514) {
	CleanUp8514();
	FatalError("Attached monitor not capable of displaying 8514!\n");
    }
hack */
    /* Determine the pixel depth of the board.  Then, based on depth,
     * set the memory control register use the appropriate CAS (VRAM
     * Column Address Select) configuration.
     */
/*    if (i & _8PLANE) { */
	numPlanes = 8;
	outpw(MULTIFUNC_CNTL, MEM_CNTL | VRTCFG_4 | HORCFG_8); 
/*    } else {
	numPlanes = 4;
	CleanUp8514();
	FatalError("Video card only has 512Kb (1Mb required)!\n");
    } hack */

    /* Now initialize the display controller part of the 8514/A.
     * We have set up the initialization values in two sets of
     * tables:  4 bpp and 8 bpp.  The "numPlanes" comparison below
     * determines which set of values to use (dual indices).
     */
/*    for (i = 0; initRegs[i] != 0; i++)
	outpw(initRegs[i], modeList[res + (numPlanes == 4 ? 0:2)][i]);
*/
    /* Now reenable the screen, but only the planes that actually exist.
     * Otherwise, you end up with bus noise on the display.
     */

    if (numPlanes == 8)
	outpw(DAC_MASK, 0xff);
    else
	outpw(DAC_MASK, 0x0f);

/*    resolution = res; */

}

/*	InitLUT()*/
/*
	Loads the Look-Up Table with all black.
	Assumes 8-bit board is in use.  If 4 bit board, the only the first
	16 entries in LUT will be used.
*/
void InitLUT(void)
{
    short i, j;

    outp(DAC_R_INDEX, 0);
    for (i = 0; i < 256; i++) {
	oldlut[i].r = inp(DAC_DATA);
	oldlut[i].g = inp(DAC_DATA);
	oldlut[i].b = inp(DAC_DATA);
    }
    LUTInited = 1;

    outp(DAC_W_INDEX, 0);

    /* Load the first 16 LUT entries */
    for (i = 0; i < 16; i++) {
	outp(DAC_DATA, 0);
	outp(DAC_DATA, 0);
	outp(DAC_DATA, 0);
    }

    if (numPlanes == 8) {
	/* Load the remaining 240 LUT entries */
	for (i = 1; i < 16; i++) {
	    for (j = 0; j < 16; j++) {
		outp(DAC_DATA, 0);
		outp(DAC_DATA, 0);
		outp(DAC_DATA, 0);
	    }
	}
    }
}

/*	InitEnvironment()

	Initializes the 8514/A's drawing environment and clears the display.
*/
void InitEnvironment(void)
{
    /* Current mixes, src, foreground active */

    outpw(FRGD_MIX, FSS_FRGDCOL | MIX_SRC);
    outpw(BKGD_MIX, BSS_BKGDCOL | MIX_SRC);

    /* Clipping rectangle to full drawable space */
    outpw(MULTIFUNC_CNTL, SCISSORS_T | 0x000);
    outpw(MULTIFUNC_CNTL, SCISSORS_L | 0x000);
    if (resolution == MODE_640) {
	outpw(MULTIFUNC_CNTL, SCISSORS_R | 0x3ff); /* Should these be */
	outpw(MULTIFUNC_CNTL, SCISSORS_B | 0x3ff); /* different???    */
    } else {
	outpw(MULTIFUNC_CNTL, SCISSORS_R | 0x7ff);
	outpw(MULTIFUNC_CNTL, SCISSORS_B | 0x3ff);
    }

    /* Enable writes to all planes and reset color compare */
    outpw(WRT_MASK, 0xffff);
    outpw(MULTIFUNC_CNTL, PIX_CNTL | 0x0000);

    /* Clear the display.  Need to set the color, origin, and size.
     * Then draw.
     */
    WaitQueue(6);
    outpw(FRGD_COLOR, 1);
    outpw(CUR_X, 0);
    outpw(CUR_Y, 0);
    outpw(MAJ_AXIS_PCNT, 1023);
    outpw(MULTIFUNC_CNTL, MIN_AXIS_PCNT | 1023);
    outpw(CMD, CMD_RECT | INC_Y | INC_X | DRAW | PLANAR | WRTDATA);

    WaitQueue(4);

    /* Reset current draw position */
    outpw(CUR_X, 0);
    outpw(CUR_Y, 0);

    /* Reset current colors, foreground is all on, background is 0. */
    outpw(FRGD_COLOR, 0xffff);
    outpw(BKGD_COLOR, 0x0000);

    /* Load the LUT */
    InitLUT();
}



