/* $XFree86: xc/programs/Xserver/hw/xfree86/vga256/drivers/al2101/al_driver.c,v 3.6 1995/01/10 10:30:06 dawes Exp $ */
/*
 * Copyright 1994 by Paolo Severini, Italy.
 *
 * 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 Paolo Severini not be used in
 * advertising or publicity pertaining to distribution of the software without
 * specific, written prior permission.  Paolo Severini makes no representations
 * about the suitability of this software for any purpose.  It is provided
 * "as is" without express or implied warranty.
 *
 * PAOLO SEVERINI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL PAOLO SEVERINI 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:  Paolo Severini, lendl@dist.dist.unige.it
 *
 */


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

#include "compiler.h"

#include "xf86.h"
#include "xf86Priv.h"
#include "xf86_OSlib.h"
#include "xf86_HWlib.h"
#include "vga.h"

typedef struct {
  vgaHWRec std;               /* good old IBM VGA */
  unsigned char ExtFuncReg1;
  unsigned char ExtCRTCreg1;
  unsigned char AbilReadBank;
  unsigned char ReadBank;         
  unsigned char WriteBank;
  } vgaAL2101Rec, *vgaAL2101Ptr;

static Bool     AL2101Probe();
static char *   AL2101Ident();
static Bool     AL2101ClockSelect();
static void     AL2101EnterLeave();
static Bool     AL2101Init();
static Bool     AL2101ValidMode();
static void *   AL2101Save();
static void     AL2101Restore();
static void     AL2101Adjust();
extern void     AL2101SetRead();
extern void     AL2101SetWrite();
extern void     AL2101SetReadWrite();

vgaVideoChipRec AL2101 = {
  AL2101Probe,
  AL2101Ident,
  AL2101EnterLeave,
  AL2101Init,
  AL2101ValidMode,
  AL2101Save,
  AL2101Restore,
  AL2101Adjust,
  (void (*)())NoopDDA,
  (void (*)())NoopDDA,
  (void (*)())NoopDDA,
  AL2101SetRead,
  AL2101SetWrite,
  AL2101SetReadWrite,
  0x10000,
  0x10000,
  16,
  0xFFFF,
  0x00000, 0x10000,
  0x00000, 0x10000,
  TRUE,                               /* Uses 2 banks */
  VGA_NO_DIVIDE_VERT,
  {0,},
  8,
  FALSE,
  0,
  0,
  FALSE,
  FALSE,
  NULL,
  1,
};


#define new ((vgaAL2101Ptr)vgaNewVideoState)

static unsigned AL2101_IOPorts[] = {
	0x3D6, 0x3D7
};

/*
 * AL2101ClockSelect --
 *      select one of the possible clocks ...
 */

static Bool
AL2101ClockSelect(no)
     int no;
{
  static unsigned char save1, save2;
  unsigned char temp;

  switch(no)
  {
    case CLK_REG_SAVE:
      save1 = inb(0x3CC);
      outb(0x3CE, 0x0C); save2 = inb(0x3CF);
      break;
    case CLK_REG_RESTORE:
      outb(0x3C2, save1);
      outw(0x3CE, 0x0C | (save2 << 8));
      break;
    default:
      temp = inb(0x3CC);
      outb(0x3C2, ( temp & 0xf3) | ((no << 2) & 0x0C));
      outb(0x3CE, 0x0C); temp = inb(0x3CF);
      outw(0x3CE, 0x0C | (( (temp & 0xDF) | ((no & 0x04)<<3)) <<8) );
  }
  return(TRUE);
}


/*
 * AL2101Ident --
 */

static char *
AL2101Ident(n)
     int n;
{
  static char *chipsets[] = {"al2101"};

  if (n + 1 > sizeof(chipsets) / sizeof(char *))
    return(NULL);
  else
    return(chipsets[n]);
}


/*
 * AL2101Probe --
 *      check up whether a Al2101 based board is installed
 */

static Bool
AL2101Probe()
{
  unsigned char tmp, tmp2, tmp3;

  /*
   * Set up I/O ports to be used by this card
   */  
  xf86ClearIOPortList(vga256InfoRec.scrnIndex);
  xf86AddIOPorts(vga256InfoRec.scrnIndex, Num_VGA_IOPorts, VGA_IOPorts);
  xf86AddIOPorts(vga256InfoRec.scrnIndex, sizeof(AL2101_IOPorts) /
      sizeof(AL2101_IOPorts[0]), AL2101_IOPorts);

  if (vga256InfoRec.chipset)
    {
      if (StrCaseCmp(vga256InfoRec.chipset, AL2101Ident(0)))
	return (FALSE);
      else
	AL2101EnterLeave(ENTER);
    }
  else
    {
      AL2101EnterLeave(ENTER);
      outb(vgaIOBase + 4, 0x1A);
      tmp= inb(vgaIOBase + 5);
      outb(vgaIOBase + 5, tmp & 0xE0); /* disable extensions */
      outb(vgaIOBase + 4, 0x19);
      tmp2=inb(vgaIOBase + 5);
      outb(vgaIOBase + 5, tmp2^0xFF);
      tmp3=inb(vgaIOBase + 5);
      if (tmp3 != (tmp2 ^ 0xFF))
	{
	  outw(vgaIOBase + 4, ((tmp|0x10)<<8) | 0x1A);
          outb(vgaIOBase + 4, 0x19);
          tmp2=inb(vgaIOBase + 5);
          outb(vgaIOBase + 5, tmp2^0xFF);
          tmp3=inb(vgaIOBase + 5);
	  outb(vgaIOBase + 5, tmp2);
          if (tmp3 == (tmp2 ^ 0xFF))
	    {
              outb(vgaIOBase + 4, 0x1A);
              tmp2=inb(vgaIOBase + 5);
              outb(vgaIOBase + 5, tmp2^0x3F);
              tmp3=inb(vgaIOBase + 5);
	      outb(vgaIOBase + 5, tmp2);
              if (tmp3 == (tmp2 ^ 0x3F))
		{
 		  if (!vga256InfoRec.videoRam) vga256InfoRec.videoRam = 1024;
 		  if (!vga256InfoRec.clocks) vgaGetClocks(8, AL2101ClockSelect);

 		  vga256InfoRec.chipset = AL2101Ident(0);
  		  vga256InfoRec.bankedMono = FALSE;
 		  return(TRUE);
		}
	    }
	}
      outw(vgaIOBase + 4, (tmp<<8) | 0x1A);
      AL2101EnterLeave(LEAVE);	
      return(FALSE);
    }   
  if (!vga256InfoRec.videoRam) vga256InfoRec.videoRam = 1024;
  if (!vga256InfoRec.clocks) vgaGetClocks(8, AL2101ClockSelect);

  vga256InfoRec.chipset = AL2101Ident(0);
  vga256InfoRec.bankedMono = FALSE;
  return(TRUE);
}



/*
 * AL2101EnterLeave --
 *      enable/disable io-mapping
 */

static void 
AL2101EnterLeave(enter)
     Bool enter;
{
  unsigned char temp;

  if (enter)
    {
      xf86EnableIOPorts(vga256InfoRec.scrnIndex);

      vgaIOBase = (inb(0x3CC) & 0x01) ? 0x3D0 : 0x3B0;
      outb(vgaIOBase + 4, 0x1A); temp = inb(vgaIOBase + 5);
      outb(vgaIOBase + 5, temp | 0x10);
    }
  else
    {
      xf86DisableIOPorts(vga256InfoRec.scrnIndex);
    }
}


/*
 * AL2101Restore --
 *      restore a video mode
 */

static void 
AL2101Restore(restore)
  vgaAL2101Ptr restore;
{
  outb(0x3D6,0x00);outb(0x3D7,0x00);

  outw(0x3CE, (restore->AbilReadBank  << 8) | 0x0F);
  outb(0x3D6, restore->ReadBank);
  outb(0x3D7, restore->WriteBank);
  outw(0x3CE, (restore->ExtFuncReg1 << 8) | 0x0C);
  outw(vgaIOBase + 4, (restore->ExtCRTCreg1 << 8) | 0x19);

  vgaHWRestore((vgaHWPtr)restore);

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



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

static void *
AL2101Save(save)
     vgaAL2101Ptr save;
{
  unsigned char    tmp1, tmp2, tmp3;

  tmp1 = inb(0x3D6);
  tmp2 = inb(0x3D7);
  outb(0x3CE,0x0F);  tmp3=inb(0x3CF);

  save = (vgaAL2101Ptr)vgaHWSave((vgaHWPtr)save, sizeof(vgaAL2101Rec));

  save->ReadBank     = tmp1;
  save->WriteBank    = tmp2;
  save->AbilReadBank = tmp3;
  outb(0x3D6,0x00);outb(0x3D7,0x00); /*segment select*/

  outb(vgaIOBase + 4, 0x19); save->ExtCRTCreg1  = inb(vgaIOBase + 5);
  outb(0x3CE,0x0C);          save->ExtFuncReg1  = inb(0x3CF);

  return ((void *) save);
}



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

static Bool
AL2101Init(mode)
     DisplayModePtr mode;
{
  if (!vgaHWInit(mode,sizeof(vgaAL2101Rec)))
    return(FALSE);
  new->ExtFuncReg1   = 0x10; 
  new->ExtCRTCreg1   = 0x12; 
  new->AbilReadBank  = 0x04;
  new->ReadBank      = 0x00;
  new->WriteBank     = 0x00;
  new->std.CRTC[5] |= 0x60;	/* Hsync skew */
  new->std.CRTC[19]  = vga256InfoRec.virtualX >> 4;

  return(TRUE);
}


/*
 * AL2101Adjust --
 *      adjust the current video frame to display the mousecursor
 */

static void 
AL2101Adjust(x, y)
     int x, y;
{
  int Base = (y * vga256InfoRec.displayWidth + x) >> 3;

  outw(vgaIOBase + 4, (Base & 0x00FF00)        | 0x0C);
  outw(vgaIOBase + 4, ((Base & 0x00FF) << 8)   | 0x0D);
  outw(vgaIOBase + 4, ((Base & 0x070000) >> 8) | 0x20);
}

/*
 * AL2101ValidMode --
 *
 */
static Bool
AL2101ValidMode(mode)
DisplayModePtr mode;
{
return TRUE;
}
