/*
 * $XConsortium: vgaHW.c,v 1.3 91/08/26 15:40:56 gildea Exp $
 *
 * Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany.
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of Thomas Roell not be used in
 * advertising or publicity pertaining to distribution of the software without
 * specific, written prior permission.  Thomas Roell makes no representations
 * about the suitability of this software for any purpose.  It is provided
 * "as is" without express or implied warranty.
 *
 * THOMAS ROELL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL THOMAS ROELL 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.
 *
 * Author:  Thomas Roell, roell@informatik.tu-muenchen.de
 */

#ifndef linux
#define _NEED_SYSI86
#endif

#include "compiler.h"

#include "x386OSD.h"
#include "vga.h"
#include "scrnintstr.h"

#define new ((vgaHWPtr)vgaNewVideoState)


/*
 * vgaSaveScreen -- 
 *      Disable the video on the frame buffer to save the screen.
 */
Bool
vgaSaveScreen (pScreen, on)
     ScreenPtr     pScreen;
     Bool          on;
{
  unsigned char   state;

  if (x386VTSema) {
    outb(0x3C4,1);
    state = inb(0x3C5);
  
    if (on) state &= 0xDF;
    else    state |= 0x20;
    
    /*
     * turn off srceen if necessary
     */
    outw(0x3C4, 0x0100);              /* syncronous reset */
    outw(0x3C4, (state << 8) | 0x01); /* change mode */
    outw(0x3C4, 0x0300);              /* syncronous reset */

  } else {
    if (on)
      ((vgaHWPtr)vgaNewVideoState)->Sequencer[1] &= 0xDF;
    else
      ((vgaHWPtr)vgaNewVideoState)->Sequencer[1] |= 0x20;
  }

  return(TRUE);
}



/*
 * vgaHWRestore --
 *      restore a video mode
 */

void
vgaHWRestore(restore)
     vgaHWPtr restore;
{
  int i;

  outw(0x3C4, ((restore->Sequencer[0] & 0xFD) << 8) | 0x00);

  if (vgaIOBase == 0x3B0)
    restore->MiscOutReg &= 0xFE;
  else
    restore->MiscOutReg |= 0x01;

  outb(0x3C2, restore->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.
   */
  
  if(restore->FontInfo) {
    /*
     * here we switch temporary to 16 color-plane-mode, to simply
     * copy the font-info
     *
     * BUGALLERT: The vga's segment-select register MUST be set appropriate !
     */
    i = inb(vgaIOBase + 0x0A); /* reset flip-flop */
    outb(0x3C0,0x30); outb(0x3C0, 0x01); /* graphics mode */
    outw(0x3C4, 0x0402);    /* write to plane 2 */
    outw(0x3C4, 0x0604);    /* enable plane graphics */
    outw(0x3CE, 0x0204);    /* read plane 2 */
    outw(0x3CE, 0x0005);    /* write mode 0, read mode 0 */
    outw(0x3CE, 0x0506);    /* set graphics */
    bcopy(restore->FontInfo, vgaBase, 8192);

  }
  
  for (i=0; i<5;  i++) outw(0x3C4, (restore->Sequencer[i] << 8) | i);
  i = inb(vgaIOBase + 0x0A); /* reset flip-flop */
  for (i=0; i<16; i++) { outb(0x3C0,i); outb(0x3C0, restore->Attribute[i]); }
  for (i=16; i<21;i++) { outb(0x3C0,i | 0x20);
			 outb(0x3C0, restore->Attribute[i]); }
  for (i=0; i<24; i++) outw(vgaIOBase + 4,(restore->CRTC[i] << 8) | i);
  for (i=0; i<9;  i++) outw(0x3CE, (restore->Graphics[i] << 8) | i);
  
  outb(0x3C6,0xFF);
  outb(0x3C8,0x00);
  for (i=0; i<768; i++) outb(0x3C9, restore->DAC[i]);

}



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

void *
vgaHWSave(save, size)
     vgaHWPtr save;
     int          size;
{
  int           i;

  if (save == NULL) {
    save = (vgaHWPtr)Xcalloc(size);
    /*
     * Here we are, when we first save the videostate. This means we came here
     * to save the original Text mode. Because some drivers may depend
     * on NoClock we set it here to a resonable value.
     */
    save->NoClock = (inb(0x3CC) >> 2) & 3;
  }

  /*
   * now get the fuck'in register
   */
  save->MiscOutReg = inb(0x3CC);

  vgaIOBase = (save->MiscOutReg & 0x01) ? 0x3D0 : 0x3B0;
  for (i=0; i<24; i++) { outb(vgaIOBase + 4,i);
			 save->CRTC[i] = inb(vgaIOBase + 5); }
  for (i=0; i<5;  i++) { outb(0x3C4,i); save->Sequencer[i]   = inb(0x3C5); }
  for (i=0; i<9;  i++) { outb(0x3CE,i); save->Graphics[i]  = inb(0x3CF); }
  i = inb(vgaIOBase + 0x0A); /* reset flip-flop */
  for (i=0; i<16; i++) { outb(0x3C0,i); save->Attribute[i] = inb(0x3C1);
			 outb(0x3C0, save->Attribute[i]); }
  for (i=16; i<21; i++) { outb(0x3C0,i | 0x20); 
			  save->Attribute[i] = inb(0x3C1);
			  outb(0x3C0, save->Attribute[i]); }
  
#ifdef GOOD_ET4000
  /* Some recent (1991) ET4000 chips have a HW bug that prevents the reading
     of the color lookup table.  Mask rev 9042EAI is known to have this bug.

     X386 already keeps track of the contents of the color lookup table so
     reading the HW isn't needed.  Therefore, as a workaround for this HW
     bug, the following (correct) code has been #ifdef'ed out.  This is also
     a valid change for ET4000 chips that don't have the HW bug.  The code
     is really just being retained for reference.  MWS 22-Aug-91
  */

  /*			 
   * save the colorlookuptable 
   */
  outb(0x3C6,0xFF);
  outb(0x3C7,0x00);
  for (i=0; i<768; i++) save->DAC[i] = inb(0x3C9); 
#endif /* GOOD_ET4000 */

  /*
   * get the character set of the first non-graphics application
   */
  if (((save->Attribute[0x10] & 0x01) == 0) &&
      (save->FontInfo == NULL)) {
    /*
     * Here we switch temporary to 16 color-plane-mode, to simply
     * copy the font-info
     *
     * BUGALLERT: The vga's segment-select register MUST be set appropriate !
     */
    i = inb(vgaIOBase + 0x0A); /* reset flip-flop */
    outb(0x3C0,0x30); outb(0x3C0, 0x01); /* graphics mode */
    outw(0x3C4, 0x0402);    /* write to plane 2 */
    outw(0x3C4, 0x0604);    /* enable plane graphics */
    outw(0x3CE, 0x0204);    /* read plane 2 */
    outw(0x3CE, 0x0005);    /* write mode 0, read mode 0 */
    outw(0x3CE, 0x0506);    /* set graphics */

    save->FontInfo = (pointer)xalloc(8192);
    bcopy(vgaBase, save->FontInfo, 8192);
  }
  
  return ((void *) save);
}



/*
 * vgaHWInit --
 *      Handle the initialization, etc. of a screen.
 */

void
vgaHWInit(mode,size)
     int             size;
     DisplayModePtr      mode;
{
  int                i;

  if (vgaNewVideoState == NULL) {
    vgaNewVideoState = (void *)Xcalloc(size);

    /*
     * initialize default colormap for monochrome
     */
    for (i=0; i<3;   i++) new->DAC[i] = 0x00;
    for (i=3; i<768; i++) new->DAC[i] = 0x3F;

  }

  /*
   * Get NoClock
   */
  new->NoClock   = mode->Clock;

  /*
   * 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 = 0xE3;
	else                           new->MiscOutReg = 0x23;
      }
  new->MiscOutReg |= (new->NoClock & 0x03) << 2;
  
  /*
   * Time Sequencer
   */
  new->Sequencer[0] = 0x00;
  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 )
	  | 0x10
	    | (((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] = 0x00;
  new->CRTC[11] = 0x00;
  new->CRTC[12] = 0x00;
  new->CRTC[13] = 0x00;
  new->CRTC[14] = 0x00;
  new->CRTC[15] = 0x00;
  new->CRTC[16] = mode->VSyncStart & 0xFF;
  new->CRTC[17] = (mode->VSyncEnd & 0x0F) | 0x20;
  new->CRTC[18] = (mode->VDisplay -1) & 0xFF;
  new->CRTC[19] = vga256InfoRec.virtualX >> 4;  /* just a guess */
  new->CRTC[20] = 0x00;
  new->CRTC[21] = mode->VSyncStart & 0xFF; 
  new->CRTC[22] = (mode->VSyncStart +1) & 0xFF;
  new->CRTC[23] = 0xC3;
  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] = 0x40;
  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]  = 0x08;
  new->Attribute[9]  = 0x09;
  new->Attribute[10] = 0x0A;
  new->Attribute[11] = 0x0B;
  new->Attribute[12] = 0x0C;
  new->Attribute[13] = 0x0D;
  new->Attribute[14] = 0x0E;
  new->Attribute[15] = 0x0F;
  new->Attribute[16] = 0x41; /* wrong for the ET4000 */
  new->Attribute[17] = 0x01;
  new->Attribute[18] = 0x0F;
  new->Attribute[19] = 0x00;
  new->Attribute[20] = 0x00;
}


/*
 * vgaGetClocks --
 *      get the dot-clocks via a BIG BAD hack ...
 */

void
vgaGetClocks(num, ClockFunc)
     int num;
     void (*ClockFunc)();
{
  int          norm;
  register int status = vgaIOBase + 0x0A;
  unsigned long       i, j, cnt, rcnt, sync;

#ifndef linux
  sysi86(SI86V86, V86SC_IOPL, PS_IOPL);
#endif

  for (i = 1; i < num; i++) {
    
    (*ClockFunc)(i);

    cnt  = 0;
    sync = 200000;

    intr_disable();
    while ((inb(status) & 0x08) == 0x00) if (sync-- == 0) goto finish;
    while ((inb(status) & 0x08) == 0x08) if (sync-- == 0) goto finish;
    while ((inb(status) & 0x08) == 0x00) if (sync-- == 0) goto finish;
    
    for (rcnt = 0; rcnt < 5; rcnt++) {
      while (!(inb(status) & 0x08)) cnt++;
      while ((inb(status) & 0x08)) cnt++;
    }
    
  finish:
    intr_enable();

    vga256InfoRec.clock[i] = cnt ? cnt : 1000000;
  }

  for (i = 2; i < num; i++)
    vga256InfoRec.clock[i] = (int)(0.5 +
      (28.322 * vga256InfoRec.clock[1]) / (vga256InfoRec.clock[i]));

  vga256InfoRec.clock[0] = 25;
  vga256InfoRec.clock[1] = 28;

  for (i=0; i < num; i++)
    for (j=i+1; j < num; j++)
      if (vga256InfoRec.clock[i] == vga256InfoRec.clock[j]) 
	vga256InfoRec.clock[j] = 0;

  vga256InfoRec.clocks = num;
  (ClockFunc)(0);
}


