/* $XFree86: xc/programs/Xserver/hw/xfree98/accel/s3nec/s3pc98.c,v 3.8.2.1 1998/02/15 16:09:44 hohndel Exp $ */




/* $XConsortium: s3pc98.c /main/7 1996/10/23 13:22:34 kaleb $ */

#include "misc.h"
#include "cfb.h"
#include "pixmapstr.h"
#include "fontstruct.h"
#include "s3.h"
#include "regs3.h"
#include "xf86_HWlib.h"
#include "xf86_Config.h"
#include "s3linear.h"
#include "s3Bt485.h"

extern int xf86bpp;

#include "s3pc98.h"

#define MasterClock		52000000l	/* 52MHz */	 
#undef chipID
#undef _port_tbl
short		chipID = S3_UNKNOWN;
unsigned short	_port_tbl[0x3e0];
unsigned int	necIndex,necData;
pointer		mmioBase = NULL;
int		pc98BoardType;
static pointer	PWLBctrlPort = NULL;

/* MCLK = 62.56 MHz   m = 0x7f, n = 0x2d */
static unsigned char sdac_data[ 30 ] = {
	0x28, 0x61, 0x3d, 0x62, 0x28, 0x22, 0x77, 0x4a,	0x79, 0x49,
	0x62, 0x46, 0x6b, 0x2a, 0x52, 0x26, 0x52, 0x26, 0x52, 0x26,
	0x7f, 0x2d, 0x7f, 0x2d,	0x7f, 0x2d, 0x7f, 0x2d, 0x22, 0x22
};

/* MCLK = 65.63 MHz   m = 0x35, n = 0x41 */
static unsigned char sdac_data805[ 30 ] = {
	0x28, 0x61, 0x3d, 0x62, 0x2a, 0x43, 0x77, 0x4a,	0x79, 0x49,
	0x62, 0x46, 0x6b, 0x2a, 0x50, 0x42, 0x50, 0x42, 0x50, 0x42,
	0x35, 0x41, 0x35, 0x41,	0x35, 0x41, 0x35, 0x41, 0x27, 0x27
};

static unsigned char seqreg_data[ 0x05 ] = {
	0x03, 0x01, 0x0f, 0x00, 0x0e
};

static unsigned char grctrl_data[ 0x09 ] = {
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff
};

static unsigned char vgareg_data[ 0x70 ] = {
	0xa1, 0x7f, 0x80, 0x84, 0x84, 0x95, 0x24, 0xe5, /* 00 - 07 */
	0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, /* 08 - 0F */
	0x02, 0x88, 0xff, 0x80, 0x60, 0xff, 0x24, 0xe3, /* 10 - 17 */
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 18 - 1F */
	0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 20 - 27 */
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 28 - 2F */
	0xc0, 0x8d, 0x00, 0x00, 0x10, 0x30, 0x8d, 0xfd, /* 30 - 37 */
	0x79, 0xbd, 0x95, 0x9a, 0x00, 0x00, 0x00, 0x00, /* 38 - 3F */
	0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0xff, /* 40 - 47 */
	0xfc, 0xff, 0xff, 0x00, 0x1f, 0xff, 0xc0, 0xc0, /* 48 - 4F */
	0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, /* 50 - 57 */
	0x0a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, /* 58 - 5F */
	0xff, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, /* 60 - 67 */
	0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  /* 68 - 6F */
};

static unsigned char vgareg_data805[ 0x70 ] = {
	0x98, 0x7F, 0x7F, 0x99, 0x80, 0x19, 0x9D, 0x1F, /* 00 - 07 */
	0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, /* 08 - 0F */
	0x80, 0x2B, 0x7F, 0x80, 0x00, 0x80, 0x81, 0xE3, /* 10 - 17 */
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 18 - 1F */
	0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 20 - 27 */
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 28 - 2F */
	0xA8, 0x8D, 0x00, 0x20, 0x10, 0x0C, 0x83, 0x0F, /* 30 - 37 */
	0x4B, 0xAD, 0xB5, 0x8C, 0x4C, 0x4C, 0x4C, 0x4C, /* 38 - 3F */
	0x01, 0xF9, 0x27, 0x00, 0x00, 0x01, 0x01, 0x83, /* 40 - 47 */
	0x81, 0x8E, 0x00, 0xC0, 0xC3, 0x00, 0x00, 0x00, /* 48 - 4F */
	0x00, 0x00, 0x00, 0x20, 0xF8, 0x40, 0x00, 0x00, /* 50 - 57 */
	0x00, 0x00, 0x0A, 0x00, 0x07, 0x00, 0x40, 0x00, /* 58 - 5F */
	0xFF, 0x81, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, /* 60 - 67 */
	0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F  /* 68 - 6F */
};
	
void crtswitch(short);
int  pwboardinit(void);
int  pw805iboardinit(void);
void skbboardinit(void);
void skb4boardinit(void);
void hkbboardinit(void);
void ga968boardinit(void);
Bool necboardinit(void);
Bool BoardInit();
int  pwlbboardinit(void);
void PWLBctrl(unsigned char);
static void Wait0(void);
static void unlocks3regs(void);
static void _pwportconvert(void);
static void _hkbportconvert(void);
static void _necportconvert(void);
static void _skbportconvert(void);
static void _skb4portconvert(void);
static int  PWBoardCheck(unsigned char PW_Switch);
static int  PWLBBoardCheck(unsigned char PW_Switch);
static void SetSDACClock( unsigned char *sdac );
static void GetSDACClock( void );
static void SetRegisters( unsigned char *seqreg,unsigned char *grctrl,unsigned char *vgareg);
static void GetRegisters( void );

static void Wait0(void)
{
	inb(vgaCRIndex);
	inb(vgaCRIndex);
	inb(vgaCRIndex);
	inb(vgaCRIndex);
}

static void unlocks3regs(void)
{
	wrinx(vgaCRIndex, 0x38, 0x48);
	wrinx(vgaCRIndex, 0x39, 0xa5);
/*	wrinx(vgaCRIndex, 0x39, 0xa0); */
}

void crtswitch(short crtmod)
{
	unsigned char	tmp;
	UNLOCK_SYS_REGS;

	switch(pc98BoardType){
	case PW:
	case PW805I:
	case PW968:
		if(crtmod)
			tmp = rdinx(vgaCRIndex, 0x41) | 1;
		else
			tmp = rdinx(vgaCRIndex, 0x41) & 0xfe;
		wrinx(vgaCRIndex, 0x41, tmp);
		_outb(0x0dc|PW_PORT, tmp);
		break;
	case PWLB:
		if(crtmod)
			tmp = rdinx(vgaCRIndex, 0x41) | 1;
		else
			tmp = rdinx(vgaCRIndex, 0x41) & 0xfe;
		wrinx(vgaCRIndex, 0x41, tmp);
		PWLBctrl(tmp);
		break;
	case PCSKB:
	case PCHKB:
	case PCSKB4:
		if(crtmod == 0)
			outb(0x6e,0);
		else
			outb(0x6e,0x3b);
		break;
	case NECWAB928_E_C:
	case NECWAB928_E_L:
	case NECWAB928_I:
		if(crtmod == 0){
			_outb(necIndex,3);
			_outb(necData,1);
		}else{
			_outb(necIndex,3);
			_outb(necData,3);
		}	
		break;
	case NECWAB864_I:
		if(crtmod == 0){
			_outb(0x6a, 0x07);
			_outb(0x6a, 0x8e);
			_outb(0x6a, 0x06);
			_outb(0x7c, 0x00); /* Is this needed? */
			_outb(0x68, 0x0f);
			_outb(necIndex, 3);
			_outb(necData , 0x01);
			_outb(necIndex, 6);
			_outb(necData , 0xff);
			_outb(necIndex, 7);
			_outb(necData , 0x00);
		}else{
			_outb(0x68, 0x0e);
			_outb(0x7c, 0x80); /* Is this needed? */
			_outb(0x6a, 0x07);
			_outb(0x6a, 0x8f);
			_outb(0x6a, 0x06);
			_outb(necIndex, 3);
			_outb(necData , 0x03);
			_outb(necIndex, 6);
			_outb(necData , 0x00);
			_outb(necIndex, 7);
			_outb(necData , 0xff);
		}
		break;
	case GA968:
		if (crtmod) {
			_outb(0xfac, 1);
		} else {
			_outb(0xfac, 0);
		}	
		break;
	default:
		ErrorF("PC98: Unknown board type.\n");
	}	
}


Bool BoardInit()
{
	switch(pc98BoardType & 0xf0){
	case PW:
		if(pwboardinit()) {
      			ErrorF("PW: chipset unknown\n");
      			xf86DisableIOPorts(s3InfoRec.scrnIndex);
      			return(FALSE);
   		}
		break;

	case PW805I:
		ErrorF("   PC98: Changing PW805i mode\n");
		if(pw805iboardinit()) {
      			ErrorF("PW805i: chipset unknown\n");
      			xf86DisableIOPorts(s3InfoRec.scrnIndex);
      			return(FALSE);
   		}
		pw805iboardinit(); /* Initializing PW805i 2 times */
		break;

	case PW968:
		if(pw968boardinit()) {
      			ErrorF("PW968: chipset unknown\n");
      			xf86DisableIOPorts(s3InfoRec.scrnIndex);
      			return(FALSE);
   		}
		break;

	case PWLB:
		ErrorF("   PC98: Changing PW localbus mode\n");
		if(pwlbboardinit()) {
      			ErrorF("PWLB: chipset unknown\n");
      			xf86DisableIOPorts(s3InfoRec.scrnIndex);
      			return(FALSE);
   		}
		break;
	case PCHKB:		
		ErrorF("   PC98: Changing PCHKB mode\n");
		hkbboardinit();
		break;
	case PCSKB:
		ErrorF("   PC98: Changing PCSKB mode\n");
		skbboardinit();
		break;
	case PCSKB4:
		ErrorF("   PC98: Changing PCSKB4 mode\n");
		skb4boardinit();
		break;
	case NECWAB:
		ErrorF("   PC98: Changing NEC-WAB mode\n");
		necboardinit();
		break;
	case GA968:
		ga968boardinit();
		break;
	default:
		ErrorF("   PC98: Unknown Board Type. \n");
		return(FALSE);
	}
	return(TRUE);
}
	

Bool s3EnterLeaveMachdep(int dir)
{
	Bool error = TRUE;

 	if (dir == S3PC98SERVER_ENTER)
	{
#ifdef PC98_GA968
		s3ConnectPCI(PCI_S3_VENDOR_ID, PCI_968);
#endif
#ifdef PC98_NEC
		if (mmioBase != NULL && pc98BoardType == NECWAB864_I)
			error = BoardInit();
#endif
	}
	else
	{
#ifdef PC98_GA968
		   s3DisconnectPCI(PCI_S3_VENDOR_ID, PCI_968);
#endif
	}

	return error;
}

static int PWBoardCheck(unsigned char PW_Switch)
{
	unsigned int tmp;

	_outb(0x0dc|PW_PORT, PW_Switch);
	Wait0();
	outb(ROM_PAGE_SEL, 0x1e);
	Wait0();
	_outb(0x0d2|PW_PORT, 1);
	Wait0();
	outb(ROM_PAGE_SEL, 0x0e);
	Wait0();
	outw(ADVFUNC_CNTL, 0000);
	Wait0();
	outb(0x3c2, 0xa3);
	Wait0();
	unlocks3regs();
	tmp = rdinx(vgaCRIndex, 0x30);
	switch(tmp & 0xf0) {
		case 0x80:
			chipID = S3_924;
			return -1;	
		case 0x90:
			chipID = S3_928;
			_outb(0x0da|PW_PORT, 3);
			if(_inb(0x0de|PW_PORT) == 1) {
				_port_tbl[0x3c7] = 0x0d6|PW_PORT;
				_port_tbl[0x3c9] = 0x0d8|PW_PORT;
			} else {
				_port_tbl[0x3c7] = 0x10d7|PW_PORT;
				_port_tbl[0x3c9] = 0x10d9|PW_PORT;
			}
			break;
		case 0xa0:
			chipID = S3_801;
			_port_tbl[0x3c7] = 0x0d6|PW_PORT;
			_port_tbl[0x3c9] = 0x0d8|PW_PORT;
			if(tmp >= 0xa8){
				chipID = S3_805;
			}
			break;
		case 0xe0:
		case 0xf0:
			outb(vgaCRIndex, 0x2e);
			tmp |= (inb(vgaCRReg) << 8);
			ErrorF("PC98 : chipID = %X \n",tmp);
			if((tmp & 0xfff0) == 0xf0e0 ) {
			  chipID = S3_968;
			  _port_tbl[0x3c7] = 0x0d6|PW_PORT;
			  _port_tbl[0x3c9] = 0x0d8|PW_PORT;
			}else{
			  chipID = S3_UNKNOWN;
			  return -1;
			}
			break;
		default:
			chipID = S3_UNKNOWN;
			return -1;
	}
	return 0;
}

int pwboardinit()
{
	unsigned char comm;
	unsigned char PW_Switch = 0xf0; /* 0xf0 | Quicksaver=4 */
	vgaCRIndex = 0x3d4;
	if(chipID == S3_UNKNOWN) {
		_pwportconvert();
		_outb(0x0dc|PW_PORT, 0);
		AltICD2061SetClock(MasterClock, 1);
		usleep(150000);
		if(PWBoardCheck(PW_Switch)) {
			crtswitch(0);
			return -1;
		}
	}
	if(OFLG_ISSET(OPTION_EPSON_MEM_WIN, &s3InfoRec.options)) {
		unsigned char tmp = PW_WinAdd;
		if(tmp >= 0xf0)
			tmp = _inb(0x43b);
			_outb(0x43b, tmp & 0xfd);
		}
 	if(!OFLG_ISSET(OPTION_NO_MEM_ACCESS, &s3InfoRec.options)) {
		_outb(0x0da|PW_PORT, 3);
		if(_inb(0x0de|PW_PORT) == 1) {
			_outb(0x0da|PW_PORT, 4);
			_outb(0x0de|PW_PORT, PW_WinAdd | 1);		    
		} else
		    if(chipID == S3_928) {
		    	_outb(0xfa2, 1);
		      	_outb(0xfa3, PW_WinAdd);		      
		    }
		PW_Switch |= 8;
	} 
	wrinx(vgaCRIndex, 0x41, PW_Switch);
	_outb(0x0dc|PW_PORT, PW_Switch);
	usleep(150000);

      switch (xf86bpp) { /* Following part was got from s3init.c. Why need? */
      case 8: 
         comm = 0;  /* repack mode 0, color mode 0 */
         break;
      case 15:
         comm = 0x80;  /* repack mode 1a using both clock edges */
	 break;
      case 16:
         comm = 0xc0;  /* repack mode 1a using both clock edges */
         break;
      case 32:
         comm = 0x40;  /* repack mode 3a using both clock edges */
         break;
      default:
         comm = 0;  /* repack mode 0, color mode 0 */
      }

      comm |= 0x08;  /* enable LUT for gamma correction */

	inb(0x3c7);
	inb(0x3c6);
	inb(0x3c6);
	inb(0x3c6);
 	inb(0x3c6); 
  	outb(0x3c6, comm);
	inb(0x3c7);
	return 0;
}

int pw805iboardinit()
{
	unsigned char PW_Switch = 0xf0; /* 0xf0 | Quicksaver=4 */
	vgaCRIndex = 0x3d4;
		_pwportconvert();
		_port_tbl[0x3c7] = 0x0d6|PW_PORT;
		_port_tbl[0x3c9] = 0x0d8|PW_PORT;
		SetSDACClock(sdac_data805);
/*		GetRegisters(); */
	        SetRegisters(seqreg_data,grctrl_data,vgareg_data805);
		usleep(150000);
		if(PWBoardCheck(PW_Switch)) {
			crtswitch(0);
			return -1;
		}
	if(OFLG_ISSET(OPTION_EPSON_MEM_WIN, &s3InfoRec.options)) {
		unsigned char tmp = PW_WinAdd;
		if(tmp >= 0xf0)
			tmp = _inb(0x43b);
			_outb(0x43b, tmp & 0xfd);
		}
 	if(!OFLG_ISSET(OPTION_NO_MEM_ACCESS, &s3InfoRec.options)) {
			_outb(0x0da|PW_PORT, 4);
			_outb(0x0de|PW_PORT, PW_WinAdd | 1);		    
		PW_Switch |= 0x88;
	}
	wrinx(vgaCRIndex, 0x41, PW_Switch);
	_outb(0x0dc|PW_PORT, PW_Switch);
	usleep(150000);
}



static int PWLBBoardCheck(unsigned char PW_Switch)
{
	unsigned char tmp;

	PWLBctrl(PW_Switch);
	usleep(1000);
	outb(ROM_PAGE_SEL, 0x1e);
	usleep(100);
	outb(0x102, 1);
	usleep(100);
	outb(ROM_PAGE_SEL, 0x0e);
	usleep(100);
	outw(ADVFUNC_CNTL, 0000);
	usleep(100);
	outb(0x3c2, 0xa3);
	usleep(100);
	unlocks3regs();
	wrinx(vgaCRIndex, 0x40, 0x95);
	tmp = rdinx(vgaCRIndex, 0x30);
	switch(tmp & 0xf0) {
		case 0x90:
			chipID = S3_928;
			break;
		case 0xD0:
			chipID = S3_964;
			break;
		default:
			chipID = S3_UNKNOWN;
			return -1;
	}
	return 0;
}

int pwlbboardinit()
{
	unsigned char comm;
	unsigned char PW_Switch = 0xf0; /* 0xf0 | Quicksaver=4 */

	mmioBase = xf86MapVidMem(0, LINEAR_REGION,
			(pointer)((PWLB_WinAdd << 16) + 0x800000), 65536);
	PWLBctrlPort = xf86MapVidMem(0, LINEAR_REGION,
			(pointer)((PWLB_WinAdd << 16) + 0xC00000), 1);
	vgaCRIndex = 0x3d4;
	if(chipID == S3_UNKNOWN) {
		PWLBctrl(0);
		AltICD2061SetClock(MasterClock, 1);
		usleep(150000);
		if(PWLBBoardCheck(PW_Switch)) {
			crtswitch(0);
			return -1;
		}
	}
 	if(!OFLG_ISSET(OPTION_NO_MEM_ACCESS, &s3InfoRec.options)) {
		PW_Switch |= 8;
	} 
	wrinx(vgaCRIndex, 0x41, PW_Switch);
	PWLBctrl(PW_Switch);
	usleep(150000);

	switch (xf86bpp) { /* Following part was got from s3init.c. Why need? */
	case 8: 
		comm = 0;  /* repack mode 0, color mode 0 */
		break;
	case 15:
		comm = 0x80;  /* repack mode 1a using both clock edges */
		break;
	case 16:
		comm = 0xc0;  /* repack mode 1a using both clock edges */
		break;
	case 32:
		comm = 0x40;  /* repack mode 3a using both clock edges */
		break;
	default:
		comm = 0;  /* repack mode 0, color mode 0 */
	}

	comm |= 0x08;  /* enable LUT for gamma correction */

	inb(0x3c7);
	inb(0x3c6);
	inb(0x3c6);
	inb(0x3c6);
	inb(0x3c6); 
	outb(0x3c6, comm);
	inb(0x3c7);
	return 0;
}

void PWLBctrl(unsigned char a)
{
	*(volatile unsigned char *)PWLBctrlPort = a;
}

int pw968boardinit()
{
	unsigned char PW_Switch = 0xf0; /* 0xf0 | Quicksaver=4 */
	vgaCRIndex = 0x3d4;
	if(chipID == S3_UNKNOWN) {
		_pwportconvert();
		_outb(0x0dc|PW_PORT, 0);

		usleep(150000);
		if(PWBoardCheck(PW_Switch)) {
			crtswitch(0);
			return -1;
		}
	}
	if(OFLG_ISSET(OPTION_EPSON_MEM_WIN, &s3InfoRec.options)) {
		unsigned char tmp = PW_WinAdd;
		if(tmp >= 0xf0)
			tmp = _inb(0x43b);
			_outb(0x43b, tmp & 0xfd);
		}
 	if(!OFLG_ISSET(OPTION_NO_MEM_ACCESS, &s3InfoRec.options)) {
		_outb(0x0da|PW_PORT, 4);
		_outb(0x0de|PW_PORT, PW_WinAdd | 1);		    
		PW_Switch |= 8;
	} 
	wrinx(vgaCRIndex, 0x41, PW_Switch);
	_outb(0x0dc|PW_PORT, PW_Switch);
	usleep(150000);

	return 0;
}


void skbboardinit()
{
  int i,WAIT;
  WAIT = 20000;
   _skbportconvert();		
   outb(0x6E,0x39);
   for(i=1;i< (WAIT * 3);i++) inb(0x03DA);
   outb(0x46,0x10);
   for(i=1;i< WAIT;i++) inb(0x03DA);
   outb(0x102,0x01);
   for(i=1;i< WAIT;i++) inb(0x03DA);
   outb(0x46,0x08);
   for(i=1;i< WAIT;i++) inb(0x03DA);
   outb(0x03C2,0xED);
   for(i=1;i< WAIT;i++) inb(0x03DA);
   outw(0x03D4,0xA039);
   for(i=1;i< WAIT;i++) inb(0x03DA);
   outb(0x03D4,0x40);
   for(i=1;i< WAIT;i++) inb(0x03DA);
   outb(0x03D5,0x41);
   for(i=1;i< WAIT;i++) inb(0x03DA);
   outw(0x4A,0x0007);
   for(i=1;i< WAIT;i++) inb(0x03DA);
   outb(0x6E,0x3B);
   for(i=1;i< (WAIT * 3);i++) inb(0x03DA);
/* outw(0x03D4,0x0742);
   outw(0x03D4,0x9C3B);
   outw(0x03D4,0xFF3C); */
/* for(i=1;i< 50000;i++) inb(0x03DA); */
}

void hkbboardinit()
{
  int i,WAIT;
  WAIT = 20000;
   _hkbportconvert();		
   outb(0x6E,0x39);
   for(i=1;i< (WAIT * 3);i++) inb(0x03DA);
   outb(0x46,0x10);
   for(i=1;i< WAIT;i++) inb(0x03DA);
   outb(0x102,0x01);
   for(i=1;i< WAIT;i++) inb(0x03DA);
   outb(0x46,0x08);
   for(i=1;i< WAIT;i++) inb(0x03DA);
   outb(0x03C2,0xED);
   for(i=1;i< WAIT;i++) inb(0x03DA);
   outw(0x03D4,0xA039);
   for(i=1;i< WAIT;i++) inb(0x03DA);
   outb(0x03D4,0x40);
   for(i=1;i< WAIT;i++) inb(0x03DA);
   outb(0x03D5,0x41);
   for(i=1;i< WAIT;i++) inb(0x03DA);
   outw(0x4A,0x0007);
   for(i=1;i< WAIT;i++) inb(0x03DA);
   outb(0x6E,0x3B);
   for(i=1;i< (WAIT * 3);i++) inb(0x03DA);
/* outw(0x03D4,0x0742);
   outw(0x03D4,0x9C3B);
   outw(0x03D4,0xFF3C); */
/* for(i=1;i< 50000;i++) inb(0x03DA); */
}

void skb4boardinit()
{
  int i,WAIT;
  WAIT = 20000;
   _skb4portconvert();		
   outb(0x3C2,0x0);
   _outb(0x8c70,0x4);
   outb(0x6E,0x39);
   for(i=1;i< (WAIT * 3);i++) inb(0x03DA);
   outb(0x46,0x10);
   for(i=1;i< WAIT;i++) inb(0x03DA);
   outb(0x102,0x01);
   for(i=1;i< WAIT;i++) inb(0x03DA);
   outb(0x46,0x08);
/*    for(i=1;i< WAIT;i++) inb(0x03DA);
   outb(0x03C2,0x2D); */
   for(i=1;i< WAIT;i++) inb(0x03DA);
   outb(0x03C2,0xED);
   for(i=1;i< WAIT;i++) inb(0x03DA);
   outw(0x03D4,0xA039);
   for(i=1;i< WAIT;i++) inb(0x03DA);
   outb(0x03D4,0x40);
   for(i=1;i< WAIT;i++) inb(0x03DA);
   outb(0x03D5,0xA1);
   for(i=1;i< WAIT;i++) inb(0x03DA);
   outw(0x4A,0x0006);
   for(i=1;i< WAIT;i++) inb(0x03DA);
    outb(0x6E,0x3B);
   for(i=1;i< (WAIT * 3);i++) inb(0x03DA);
/* outw(0x03D4,0x0742);
   outw(0x03D4,0x9C3B);
   outw(0x03D4,0xFF3C); */
/*    outw(0x03D4,0x8A31);
   outw(0x03D4,0x4032);
   outw(0x03D4,0x853A); 
   outb(0x03C0,0x30);  */

/*   _outb(0x8C72,0x0); 
   _outb(0x8C73,0x02); 
   _outb(0x8C7b,0x04); 
   _outb(0x8C7D,0x00); 
   _outb(0x8C7E,0x00); 
   _outb(0x8C70,0xC4);  */
}


Bool necboardinit()
{
	unsigned char tmp;
	int	i;

	vgaCRIndex = 0x3d4;
	vgaCRReg   = 0x3d5;
	if (mmioBase == NULL)
		mmioBase = xf86MapVidMem(0, VGA_REGION,(pointer)(0xdf000), 4096);
   	_necportconvert();		

 	_outb(PC98_NEC_INDX1, 0);
	_outb(PC98_NEC_DATA1, 0x0FF);
	tmp = _inb(PC98_NEC_DATA1);
	if(tmp != 0xff){
		necIndex = PC98_NEC_INDX1;
		necData  = PC98_NEC_DATA1;
	}else{
		necIndex = PC98_NEC_INDX2;
		necData  = PC98_NEC_DATA2;
	}

	_outb(necIndex, 0);
	_outb(necData, 0xff);
	tmp = _inb(necData);
	switch(tmp){
		case 0x10: /* WAB-A */
		case 0x11: /* FC-WAB-A */
			pc98BoardType = NECWAB928_E_L;
			break;

		case 0x12: /* Internel 928 */
			pc98BoardType = NECWAB928_I;
			break;

		case 0x18: /* Internel 864 */
			pc98BoardType = NECWAB864_I;
			_outb(0x68, 0x0e);
			_outb(0x7c, 0x80); /* Is this needed? */
			_outb(0x6a, 0x07);
			_outb(0x6a, 0x8f);
			_outb(0x6a, 0x06);
			break;

		case 0x20: /* WAB-B */
		case 0x21: /* FC-WAB-B */
			pc98BoardType = NECWAB928_E_C;
			break;

		default:
			ErrorF("   PC98:Unknown return code ... %02X\n",tmp);
			return(FALSE);
	}

	_outb(necIndex, 1);    
	_outb(necData , 0x5f);    /* VGA registers was mapped at 0xdf000 */
/*	_outb(necData , 0x80); */ /* VGA registers was mapped at 0xf80000 */

	_outb(necIndex, 2);
	_outb(necData , 0x00);
	_outb(necIndex, 3);
	if(pc98BoardType == NECWAB864_I){
		_outb(necData , 0x03); /* chip enable & relay on? */
	}else{
		_outb(necData , 0x01); /* chip enable */
	}

	outb(ROM_PAGE_SEL,0x1e);
	outb(0x102,0x01);
	outb(ROM_PAGE_SEL,0x0e);
	outw(ADVFUNC_CNTL, 0x0005);
	outb(0x3c2,0xcd);
	outb(0x3da,0x00);
	wrinx(vgaCRIndex, 0x38, 0x48);
	wrinx(vgaCRIndex, 0x39, 0xa0);
	wrinx(vgaCRIndex, 0x40, 0xa5);

/*	GetRegisters(); */
	if (OFLG_ISSET(CLOCK_OPTION_S3GENDAC, &s3InfoRec.clockOptions)){
		if(!OFLG_ISSET(OPTION_NOINIT, &s3InfoRec.options)){
/*			GetSDACClock(); */
			SetSDACClock(sdac_data);
/* 			GetSDACClock(); */
		        SetRegisters(seqreg_data,grctrl_data,vgareg_data);
/*			GetRegisters(); */
		} 
	}
	return (TRUE);	
}

void ga968boardinit(void)
{
	vgaCRIndex = 0x3d4;

	_outb(0x37, 8);
	outb(ROM_PAGE_SEL, 0x10);
	outb(0x102, 1);
	outb(ROM_PAGE_SEL, 0x08);
	outw(ADVFUNC_CNTL, 0000);
	outb(0x3c6, 0xFF);
	outb(0x3c2, 0x23);
	unlocks3regs();
	wrinx(vgaCRIndex, 0x40, 0xD1);

	outw(0x3D4, 0xD140);
	outw(0x3D4, 0x0055);
	outw(0x3D4, 0x705C);

	outw(0x3D4, 0x0F60);
	outw(0x3D4, 0x8061);
	outw(0x3D4, 0xA162);
	outw(0x3D4, 0x0063);
	outw(0x3D4, 0x0064);
	outw(0x3D4, 0x0065);
	outw(0x3D4, 0x0066);
	outw(0x3D4, 0x1067);
	outw(0x3D4, 0xEC68);
	outw(0x3D4, 0x0069);
	outw(0x3D4, 0x006A);
	outw(0x3D4, 0x806B);
	outw(0x3D4, 0x006C);
	outw(0x3D4, 0x006D);

	outw(0x3CE, 0x0000);
	outw(0x3CE, 0x0001);
	outw(0x3CE, 0x0002);
	outw(0x3CE, 0x0003);
	outw(0x3CE, 0x0004);
	outw(0x3CE, 0x4005);
	outw(0x3CE, 0x0506);
	outw(0x3CE, 0x0F07);
	outw(0x3CE, 0xFF08);

	outb(0x3C0, 0x30);
	outb(0x3C0, 0x41);
	outb(0x3C0, 0x31);
	outb(0x3C0, 0x00);
	outb(0x3C0, 0x32);
	outb(0x3C0, 0x0F);
	outb(0x3C0, 0x33);
	outb(0x3C0, 0x00);
	outb(0x3C0, 0x34);
	outb(0x3C0, 0x00);

	outw(0x3D4, 0x0155);

	outb(0x3C9, 0x00);	/* IBM RAMDAC index high-8bit */

	outb(0x3C8, 0x02); outb(0x3C6, 0x01);
	outb(0x3C8, 0x03); outb(0x3C6, 0x00);
	outb(0x3C8, 0x04); outb(0x3C6, 0x00);
	outb(0x3C8, 0x05); outb(0x3C6, 0x00);
	outb(0x3C8, 0x06); outb(0x3C6, 0x02);
	outb(0x3C8, 0x07); outb(0x3C6, 0x00);
	outb(0x3C8, 0x08); outb(0x3C6, 0x01);
	outb(0x3C8, 0x0A); outb(0x3C6, 0x03);
	outb(0x3C8, 0x0B); outb(0x3C6, 0x00);
	outb(0x3C8, 0x0C); outb(0x3C6, 0xC2);
	outb(0x3C8, 0x0D); outb(0x3C6, 0x00);
	outb(0x3C8, 0x0E); outb(0x3C6, 0x00);
	outb(0x3C8, 0x10); outb(0x3C6, 0x00);
	outb(0x3C8, 0x11); outb(0x3C6, 0x02);
	outb(0x3C8, 0x14); outb(0x3C6, 0x08);
	outb(0x3C8, 0x15); outb(0x3C6, 0x08);
	outb(0x3C8, 0x16); outb(0x3C6, 0x77);
	outb(0x3C8, 0x20); outb(0x3C6, 0x24);
	outb(0x3C8, 0x21); outb(0x3C6, 0x30);
	outb(0x3C8, 0x22); outb(0x3C6, 0x3D);
	outb(0x3C8, 0x30); outb(0x3C6, 0x00);
	outb(0x3C8, 0x70); outb(0x3C6, 0x01);
	outb(0x3C8, 0x71); outb(0x3C6, 0x43);
	outb(0x3C8, 0x72); outb(0x3C6, 0x00);

	outw(0x3D4, 0x0055);
	outb(0x3C8, 0x00);

	outb(0x3C6, 0xFF);
	outb(0x3C2, 0xEF);
}

static void SetSDACClock( unsigned char *sdac )
{
	int i;
	unsigned char saveCR55;

	outb(vgaCRIndex, 0x55);
	saveCR55 = inb(vgaCRReg);
	outb(vgaCRReg, saveCR55 | 1);

	if(pc98BoardType == PW805I)
	  outb(0x3C7, 0); /* Write MCLK & etc for PW805i. Can't use 0x3C8 */
	else
	  outb(0x3C8, 0); /* Write MCLK & etc */

	for( i=0; i<30; i++){
		outb( 0x3c9, sdac[ i ] );
	}
	return;
}

static void GetSDACClock( void )
{
	int m, n, n1, n2, mclk,i;
	unsigned char saveCR55;

	outb(vgaCRIndex, 0x55);
	saveCR55 = inb(vgaCRReg);
	outb(vgaCRReg, saveCR55 | 1);

	outb(0x3C7, 10); /* read MCLK */
	m = inb(0x3C9);
	n = inb(0x3C9);
	ErrorF( "MCLK is m=%d , n=%d", m, n );
	m &= 0x7f;
	n1 = n & 0x1f;
	n2 = (n>>5) & 0x03;
	mclk = ((1431818 * (m+2)) / (n1+2) / (1 << n2) + 50) / 100;
	ErrorF( "(%1.3f MHz)\n", mclk / 1000.0 );
	ErrorF( "3c6 = %02X \n",inb(0x3C6));
	ErrorF( "SDAC= " );
	outb(0x3C7, 0);
	for(i=0;i<=30;i++){
		ErrorF( "%02X ",inb(0x3C9));
	}
	ErrorF( "\n");
	return;
}

static void SetRegisters( unsigned char *seqreg,unsigned char *grctrl, unsigned char *vgareg)
{
	int i;

	/* Sequence Registers */
	for( i=0; i<=4; i++ ){
		outb( 0x3c4, i );
		outb( 0x3c5, seqreg[ i ] );
		_inb( 0x5f );
		_inb( 0x5f );
	}

	/* Set Graphic Control Data */
	for( i=0; i<=8; i++ ){
		outb( 0x3ce, i );
		outb( 0x3cf, grctrl[ i ] );
		_inb( 0x5f );
		_inb( 0x5f );
	}

	/* Set VGA Registers */
	for( i=0; i<=0x6f; i++ ){
		outb( vgaCRIndex, i );
		outb( vgaCRReg, vgareg[ i ] );
		_outb( 0x5f, 0 );
		_outb( 0x5f, 0 );
	} 
	return;
}

static void GetRegisters( void )
{
	int i, tmp;

	tmp = inb( 0x3cc );
	ErrorF( "[6.1] 3C2(Miscellaneous Output Registes) : 0x%02X\n", tmp );
	tmp = inb( 0x3da );
	ErrorF( "      3CA(Feature Control Registes) : 0x%02X\n", tmp );

	ErrorF( "      3C4/3C5(Sequencer Registers)\n" );
	for( i=0; i<=4; i++ ){
		outb( 0x3c4, i );
		tmp = inb( 0x3c5 );
		ErrorF( "        Index %02X : 0x%02X\n", i, tmp );
	}
	ErrorF( "[6.4] 3CE/3CF(Graphics Controller Registers)\n" );
	for( i=0; i<=8; i++ ){
		outb( 0x3ce, i );
		tmp = inb( 0x3cf );
		ErrorF( "        Index %02X : 0x%02X\n", i, tmp );
	}
/*
	ErrorF( "[6.5] 3C0/3C1(Attribute Controller Registers)\n" );
	for( i=0; i<=0x14; i++ ){
		outb( 0x3c0, i );
		tmp = inb( 0x3c1 );
		ErrorF( "        Index %02X : 0x%02X\n", i, tmp );
	}
*/
	ErrorF( "      3x4/3x5(VGA Registers)\n" );
	for( i=0; i<=0x6f; i++ ){
		outb( vgaCRIndex, i );
		tmp = inb( vgaCRReg );
		ErrorF( "        Index %02X : 0x%02X\n", i, tmp );
	}
	return;
}


static void _pwportconvert()
{
	_port_tbl[0x3c0] = 0x10d0|PW_PORT;
	_port_tbl[0x3c1] = 0x10d1|PW_PORT;
	_port_tbl[0x3c2] = 0x10d2|PW_PORT;
	_port_tbl[0x3c4] = 0x10d4|PW_PORT;
	_port_tbl[0x3c5] = 0x10d5|PW_PORT;
	_port_tbl[0x3c6] = 0x10d6|PW_PORT;
	_port_tbl[0x3c8] = 0x10d8|PW_PORT;
	_port_tbl[0x3cc] = 0x10dc|PW_PORT;
	_port_tbl[0x3ce] = 0x10de|PW_PORT;
	_port_tbl[0x3cf] = 0x10df|PW_PORT;
	_port_tbl[0x3d0] = 0x20d0|PW_PORT;
	_port_tbl[0x3d4] = 0x20d4|PW_PORT;
	_port_tbl[0x3d5] = 0x20d5|PW_PORT;
	_port_tbl[0x3da] = 0x20da|PW_PORT;
	_port_tbl[0x42] = 0x40d2|PW_PORT;
	_port_tbl[0x46] = 0x40d6|PW_PORT;
	_port_tbl[0x4a] = 0x40da|PW_PORT;
	_port_tbl[0x82] = 0x80d2|PW_PORT;
	_port_tbl[0x86] = 0x80d6|PW_PORT;
	_port_tbl[0x8a] = 0x80da|PW_PORT;
	_port_tbl[0x8e] = 0x80de|PW_PORT;
	_port_tbl[0x92] = 0x90d2|PW_PORT;
	_port_tbl[0x96] = 0x90d6|PW_PORT;
	_port_tbl[0x9a] = 0x90da|PW_PORT;
	_port_tbl[0x9e] = 0x90de|PW_PORT;
	_port_tbl[0xa2] = 0xa0d2|PW_PORT;
	_port_tbl[0xa6] = 0xa0d6|PW_PORT;
	_port_tbl[0xaa] = 0xa0da|PW_PORT;
	_port_tbl[0xae] = 0xa0de|PW_PORT;
	_port_tbl[0xb2] = 0xb0d2|PW_PORT;
	_port_tbl[0xb6] = 0xb0d6|PW_PORT;
	_port_tbl[0xba] = 0xb0da|PW_PORT;
	_port_tbl[0xbe] = 0xb0de|PW_PORT;
	_port_tbl[0xe2] = 0xe0d2|PW_PORT;
}

static void _hkbportconvert()
{
	_port_tbl[0x102] = 0xD02;
	_port_tbl[0x3c0] = 0xF40;
	_port_tbl[0x3c1] = 0xF41;
	_port_tbl[0x3c2] = 0xF42;
	_port_tbl[0x3c4] = 0xF44;
	_port_tbl[0x3c5] = 0xF45;
	_port_tbl[0x3c6] = 0xF46;
	_port_tbl[0x3c7] = 0xF47;
	_port_tbl[0x3c8] = 0xF48;
	_port_tbl[0x3c9] = 0xF49;
	_port_tbl[0x3cc] = 0xF4C;
	_port_tbl[0x3ce] = 0xF4E;
	_port_tbl[0x3cf] = 0xF4F;
	_port_tbl[0x3d0] = 0xF50;
	_port_tbl[0x3d4] = 0xF54;
	_port_tbl[0x3d5] = 0xF55;
	_port_tbl[0x3da] = 0xF5A;
	_port_tbl[0x42] = 0x4e68;
	_port_tbl[0x46] = 0x4a68;
	_port_tbl[0x4a] = 0x4c68;
	_port_tbl[0x82] = 0x8e68;
	_port_tbl[0x86] = 0x8a68;
	_port_tbl[0x8a] = 0x8c68;
	_port_tbl[0x8e] = 0x8868;
	_port_tbl[0x92] = 0x9e68;
	_port_tbl[0x96] = 0x9a68;
	_port_tbl[0x9a] = 0x9c68;
	_port_tbl[0x9e] = 0x9868;
	_port_tbl[0xa2] = 0xae68;
	_port_tbl[0xa6] = 0xaa68;
	_port_tbl[0xaa] = 0xac68;
	_port_tbl[0xae] = 0xa868;
	_port_tbl[0xb2] = 0xbe68;
	_port_tbl[0xb6] = 0xba68;
	_port_tbl[0xba] = 0xbc68;
	_port_tbl[0xbe] = 0xb868;
	_port_tbl[0xe2] = 0xee68;
	_port_tbl[0x6e] = 0x6e68;
}



static void _skbportconvert()
{
	_port_tbl[0x102] = 0xD02;
	_port_tbl[0x3c0] = 0xFC0;
	_port_tbl[0x3c1] = 0xFC1;
	_port_tbl[0x3c2] = 0xFC2;
	_port_tbl[0x3c4] = 0xFC4;
	_port_tbl[0x3c5] = 0xFC5;
	_port_tbl[0x3c6] = 0xFC6;
	_port_tbl[0x3c7] = 0xFC7;
	_port_tbl[0x3c8] = 0xFC8;
	_port_tbl[0x3c9] = 0xFC9;
	_port_tbl[0x3cc] = 0xFCC;
	_port_tbl[0x3ce] = 0xFCE;
	_port_tbl[0x3cf] = 0xFCF;
	_port_tbl[0x3d0] = 0xFD0;
	_port_tbl[0x3d4] = 0xFD4;
	_port_tbl[0x3d5] = 0xFD5;
	_port_tbl[0x3da] = 0xFDA;
	_port_tbl[0x42] = 0x4ee8;
	_port_tbl[0x46] = 0x4ae8;
	_port_tbl[0x4a] = 0x46e8;
	_port_tbl[0x82] = 0x8ee8;
	_port_tbl[0x86] = 0x8ae8;
	_port_tbl[0x8a] = 0x86e8;
	_port_tbl[0x8e] = 0x82e8;
	_port_tbl[0x92] = 0x9ee8;
	_port_tbl[0x96] = 0x9ae8;
	_port_tbl[0x9a] = 0x96e8;
	_port_tbl[0x9e] = 0x92e8;
	_port_tbl[0xa2] = 0xaee8;
	_port_tbl[0xa6] = 0xaae8;
	_port_tbl[0xaa] = 0xa6e8;
	_port_tbl[0xae] = 0xa2e8;
	_port_tbl[0xb2] = 0xbee8;
	_port_tbl[0xb6] = 0xbae8;
	_port_tbl[0xba] = 0xb6e8;
	_port_tbl[0xbe] = 0xb2e8;
	_port_tbl[0xe2] = 0xeee8;
	_port_tbl[0x6e] = 0x6ee8;
}

static void _skb4portconvert()
{
	_port_tbl[0x102] = 0xD02;
	_port_tbl[0x3c0] = 0xF40;
	_port_tbl[0x3c1] = 0xF41;
	_port_tbl[0x3c2] = 0xF42;
	_port_tbl[0x3c4] = 0xF44;
	_port_tbl[0x3c5] = 0xF45;
	_port_tbl[0x3c6] = 0xF46;
	_port_tbl[0x3c7] = 0xF47;
	_port_tbl[0x3c8] = 0xF48;
	_port_tbl[0x3c9] = 0xF49;
	_port_tbl[0x3cc] = 0xF4C;
	_port_tbl[0x3ce] = 0xF4E;
	_port_tbl[0x3cf] = 0xF4F;
	_port_tbl[0x3d0] = 0xF50;
	_port_tbl[0x3d4] = 0xF54;
	_port_tbl[0x3d5] = 0xF55;
	_port_tbl[0x3da] = 0xF5A;
	_port_tbl[0x42] = 0x4e68;
	_port_tbl[0x46] = 0x4a68;
	_port_tbl[0x4a] = 0x4c68;
	_port_tbl[0x82] = 0x8e68;
	_port_tbl[0x86] = 0x8a68;
	_port_tbl[0x8a] = 0x8c68;
	_port_tbl[0x8e] = 0x8868;
	_port_tbl[0x92] = 0x9e68;
	_port_tbl[0x96] = 0x9a68;
	_port_tbl[0x9a] = 0x9c68;
	_port_tbl[0x9e] = 0x9868;
	_port_tbl[0xa2] = 0xae68;
	_port_tbl[0xa6] = 0xaa68;
	_port_tbl[0xaa] = 0xac68;
	_port_tbl[0xae] = 0xa868;
	_port_tbl[0xb2] = 0xbe68;
	_port_tbl[0xb6] = 0xba68;
	_port_tbl[0xba] = 0xbc68;
	_port_tbl[0xbe] = 0xb868;
	_port_tbl[0xe2] = 0xee68;
	_port_tbl[0x6e] = 0x6e68;
}

void _necportconvert()
{
	int i;
	for(i=0x3b0; i<0x3e0; i++){
		_port_tbl[i] = i - 0x200;
	}
	_port_tbl[0x102] = 0x002;
	_port_tbl[0x42] =  0x240;
	_port_tbl[0x46] =  0x244;
	_port_tbl[0x4a] =  0x248;
	_port_tbl[0x82] =  0x280;
	_port_tbl[0x86] =  0x284;
	_port_tbl[0x8a] =  0x288;
	_port_tbl[0x8e] =  0x28c;
	_port_tbl[0x92] =  0x290;
	_port_tbl[0x96] =  0x294;
	_port_tbl[0x9a] =  0x298;
	_port_tbl[0x9e] =  0x29c;
	_port_tbl[0xa2] =  0x2a0;
	_port_tbl[0xa6] =  0x2a4;
	_port_tbl[0xaa] =  0x2a8;
	_port_tbl[0xae] =  0x2ac;
	_port_tbl[0xb2] =  0x2b0;
	_port_tbl[0xb6] =  0x2b4;
	_port_tbl[0xba] =  0x2b8;
	_port_tbl[0xbe] =  0x2bc;
	_port_tbl[0xe2] =  0x2e0;
}
