/*
 * DANG_BEGIN_MODULE
 *
 * Description:
 *   Trident driver for DOSEMU.  Code taken from various sources.
 * 
 * DANG_END_MODULE
 *
 * $Date: $
 * $Source: $
 * $Revision: $
 * $State: $
 *
 * $Log: $
 */

#include "dosemu.h"
#include "base/port.h"
#include "env/libvga.h"

static int trident_memory;	/* amount of video memory in K */
static int reg_0c;

static void trident_getperm(void)
{
}

static void trident_releaseperm(void)
{
}

static Bit8u trident_inb(Bit32u port)
{
  return 0xff;
}

static void trident_outb(Bit32u port, Bit8u value)
{
}

static void trident_setpage( int page )
{
	port_out(0x0b, SEQ_I);
	port_out(port_in(SEQ_D), SEQ_D);
	port_in(SEQ_D);			/* select new mode regs */

	port_out(0x0e, SEQ_I);
	port_out(page ^ 0x02, SEQ_D);	/* select the page */
}

static void trident_saveregs(Bit16u *regs)
{
    int i;

    /* save extended CRT registers */
    for (i = 0; i < 8; i++) {
	 port_out(0x18 + i, CRT_I); 
	 regs[i] = port_in(CRT_D); 
    }

    /* now do the sequencer mode regs */
    port_out(0x0b, SEQ_I);		/* force old mode regs */
    port_out(0x00, SEQ_D);	/* by writing */

	/* outw(0x3C4, 0x820E); */		/* unlock conf. reg */
	/* port_out(0x0c, SEQ_I); */		/* save conf. reg */
	/* regs[11] = port_in(SEQ_D); */

    port_out(0x0d, SEQ_I);		/* old reg 13 */
    regs[8] = port_in(SEQ_D);
    port_out(0x0e, SEQ_I);		/* old reg 14 */
    regs[9] = port_in(SEQ_D);

    port_out(0x0b, SEQ_I);		/* now use new regs */
    port_out(0x00, SEQ_D);	
    port_in(SEQ_D);
    port_out(0x0d, SEQ_I);		/* new reg 13 */
    regs[10] = port_in(SEQ_D);
    port_out(0x0e, SEQ_I);		/* new reg 14 */
    regs[11] = port_in(SEQ_D) ^ 0x02;
    port_out(0x0f, SEQ_I);		/* new reg 15 */
    regs[12] = port_in(SEQ_D);
    port_out(0x0f, GRA_I);
    regs[13] = port_in(GRA_D);

      /* we do the ^ 0x02 so that when the regs are restored */
      /* later we don't have a special case; see trident.doc */
}


/* Set chipset-specific registers */

static void trident_setregs(const Bit16u *regs)
{
    int i;
    int crtc31 = 0;
    
    /* 7 extended CRT registers */
    /* 4 extended Sequencer registers (old and new) */
    /* CRTC reg 0x1f is apparently dependent */
    /* on the amount of memory installed. */

	switch (trident_memory >> 8) {
 		case 1 : crtc31 = 0x94; reg_0c = 0xad; break;	/* 256K */
  		case 2 :
 		case 3 : crtc31 = 0x98; reg_0c = 0xcd; break;	/* 512/768K */
  		case 4 : 					/* 1024K */
			 crtc31 = 0x18;
 			 reg_0c = 0xcd;
#if 0 /* XXX */
			 if (mode == G1024x768x256) {
 				reg_0c = 0xed;
				crtc31 = 0x98;
 			 }
 			 else 
                         if (mode == G640x480x256 || mode == G800x600x256)
 				reg_0c = 0xed;
#endif
			 break;
	}

#if 0 /* XXX */
	if (mode == TEXT) {
		reg_0c = regs[11];
		crtc31 = regs[12];
	}
#endif

    /* write extended CRT registers */
    for (i = 0; i < 8; i++) {
	 port_out(0x18 + i, CRT_I); 
	 port_out(regs[i], CRT_D); 
    }

    /* update sequencer mode regs */
    port_out(0x0b, SEQ_I);		/* select old regs */
    port_out(0x00, SEQ_D);
    port_out(0x0d, SEQ_I);		/* old reg 13 */
    port_out(regs[8], SEQ_D);
    port_out(0x0e, SEQ_I);		/* old reg 14 */
#if 0
    port_out(regs[9], SEQ_D);
#endif
    port_out( ((port_in(SEQ_D) & 0x08) | (regs[9] & 0xf7)), SEQ_D);
    

    port_out(0x0b, SEQ_I);
    port_out(0x00, SEQ_D);
    port_in(SEQ_D);			/* select new regs */

    	if (trident_memory > 512) {
	    port_out(0x0e, SEQ_I);		/* set bit 7 of reg 14  */
	    port_out(0x80, SEQ_D);		/* to enable writing to */
	    port_out(0x0c, SEQ_I);		/* reg 12               */
	    port_out(reg_0c, SEQ_D);
	}

/*	outw(0x3c4, 0x820e); */		/* unlock conf. reg */
/* 	port_out(0x0c, SEQ_I); */	/* reg 12 */

    port_out(0x0d, SEQ_I);		/* new reg 13 */
    port_out(regs[10], SEQ_D);
    port_out(0x0e, SEQ_I);		/* new reg 14 */
    port_out(regs[11], SEQ_D);
    port_out(0x0f, SEQ_I);		/* new reg 15 */
    port_out(regs[12], SEQ_D);

    /* update CRTC reg 1f */
    port_out(0x1f, CRT_I);
    port_out((port_in(CRT_D) & 0x03) | crtc31, CRT_D);
    port_out(0x0f, GRA_I);
    port_out(regs[13], GRA_D);
}


static Boolean trident_init(void)
{
  port_out(0x1f, CRT_I);
  trident_memory = (port_in(CRT_D) & 0x03) * 256 + 256;

  v_printf("Using Trident 8900/9000 driver (%dK)\n", trident_memory);

  return TRUE;
}

static Boolean trident_test(void)
{
	int origVal, newVal;
	int save0b;
	/* 
       	 * Check first that we have a Trident card.
       	 */
       	outb(0x3c4, 0x0b);
       	save0b = inb(0x3c5);
	outw(0x3C4, 0x000B);	/* Switch to Old Mode */
	inb(0x3C5);		/* Now to New Mode */
      	outb(0x3C4, 0x0E);
      	origVal = inb(0x3C5);
      	outb(0x3C5, 0x00);
      	newVal = inb(0x3C5) & 0x0F;
      	outb(0x3C5, (origVal ^ 0x02));

      	if (newVal != 2) {
      		outb(0x3c5, origVal);
      		outb(0x3c4, 0x0b);
      		outb(0x3c5, save0b);
      		return FALSE;
      	}

	/* check version */
	outw(0x3c4, 0x000b);
	switch (inb(0x3c5)) {
	case 0x02 :	/* 8800cs */
	case 0x03 :	/* 8900b */
	case 0x04 :	/* 8900c */
	case 0x13 :
	case 0x33 :	/* 8900cl */
	case 0x23 :	/* 9000 */
		break;
	default :
		return FALSE;
	}

	return trident_init();
}

DriverSpecs trident_driverspecs = {
	"Trident",
	trident_saveregs,
	trident_setregs,
	trident_test,
	trident_init,
	trident_setpage,
	trident_getperm,
	trident_releaseperm,
	trident_inb,
	trident_outb
};

