/*
 * DANG_BEGIN_MODULE
 *
 * Description:
 *   ARK driver for DOSEMU.  Code taken from SVGALIB. 
 * 
 *   Original driver written by Harm Hanemaayer.
 *
 * DANG_END_MODULE
 *
 * $Date: $
 * $Source: $
 * $Revision: $
 * $State: $
 *
 * $Log: $
 */

#include "dosemu.h"
#include "base/port.h"
#include "env/libvga.h"
#include "vga-ramdac.h"
#include "vgaregs.h"

#include <sys/mman.h>

/* Extended registers. */

#define ARK_SR10	VGA_TOTAL_REGS + 0
#define ARK_SR11	VGA_TOTAL_REGS + 1
#define ARK_SR12	VGA_TOTAL_REGS + 2
#define ARK_SR13	VGA_TOTAL_REGS + 3
#define ARK_SR14	VGA_TOTAL_REGS + 4
#define ARK_SR15	VGA_TOTAL_REGS + 5
#define ARK_SR16	VGA_TOTAL_REGS + 6
#define ARK_SR17	VGA_TOTAL_REGS + 7
#define ARK_SR18	VGA_TOTAL_REGS + 8
#define ARK_SR19	VGA_TOTAL_REGS + 9
#define ARK_SR1C	VGA_TOTAL_REGS + 10
#define ARK_SR1D	VGA_TOTAL_REGS + 11
#define ARK_CR40	VGA_TOTAL_REGS + 12
#define ARK_CR41	VGA_TOTAL_REGS + 13
#define ARK_CR42	VGA_TOTAL_REGS + 14
#define ARK_CR44	VGA_TOTAL_REGS + 15
#define ARK_CR46	VGA_TOTAL_REGS + 16
#define ARK_CR50	VGA_TOTAL_REGS + 17
#define ARK_PELMASK	VGA_TOTAL_REGS + 18

#define ARK_MEMORY_CONTROL		ARK_SR10
#define ARK_VIDEO_CLOCK_SELECT		ARK_SR11
#define ARK_VLBUS_CONTROL		ARK_SR12
#define ARK_PAGE_ADDRESS_LOW		ARK_SR13
#define ARK_PAGE_ADDRESS_HIGH		ARK_SR14
#define ARK_APERTURE_WRITE_INDEX	ARK_SR15
#define ARK_APERTURE_READ_INDEX		ARK_SR16
#define ARK_COP_FRAMEBUFFER_PITCH	ARK_SR17	/* Unused */
#define ARK_DISPLAY_FIFO_CONTROL	ARK_SR18
#define ARK_HOST_INTERFACE_TYPE		ARK_SR19	/* Read */
#define ARK_DPMS_CONTROL		ARK_SR1C
#define ARK_LOCK_REGISTER		ARK_SR1D
#define ARK_CRTC_VERTICAL_OVERFLOW	ARK_CR40
#define ARK_CRTC_HORIZONTAL_OVERFLOW	ARK_CR41
#define ARK_INTERLACE_RETRACE		ARK_CR42
#define ARK_VGA_ENHANCEMENT		ARK_CR44
#define ARK_PIXEL_CLOCK_CONTROL		ARK_CR46
#define ARK_CHIP_ID			ARK_CR50	/* Read */

#define ARK_DAC_OFFSET	VGA_TOTAL_REGS + 19
#define ARK_TOTAL_REGS	VGA_TOTAL_REGS + 19 + 10

#define ARK1000PV	0
#define ARK2000PV	1

#define VESA	0
#define PCI	1

static const char *ark_chipname[] = { "ARK1000PV", "ARK2000PV" };

static DacMethods *dacs_to_probe[] = { &S3_SDAC_methods, &normal_dac_methods,
	NULL };

static int ark_chip;
static int ark_memory;
static DacMethods *dac_used = NULL;

static void ark_unlock(void)
{
  /* Set bit 0 of SR1D. */
  outSR(0x1D, inSR(0x1D) | 0x01);
}

static void ark_saveregs(Bit16u *regs)
{
  ark_unlock();

  /* Save extended registers. */
  regs[ARK_SR10] = inSR(0x10);
  regs[ARK_SR11] = inSR(0x11);
  regs[ARK_SR12] = inSR(0x12);
  regs[ARK_SR13] = inSR(0x13);
  regs[ARK_SR14] = inSR(0x14);
  regs[ARK_SR15] = inSR(0x15);
  regs[ARK_SR16] = inSR(0x16);
  regs[ARK_SR18] = inSR(0x18);
  regs[ARK_SR19] = inSR(0x19);
  regs[ARK_SR1C] = inSR(0x1C);
  regs[ARK_SR1D] = inSR(0x1D);

  regs[ARK_CR40] = inCR(0x40);
  regs[ARK_CR41] = inCR(0x41);
  regs[ARK_CR42] = inCR(0x42);
  regs[ARK_CR44] = inCR(0x44);
  regs[ARK_CR46] = inCR(0x46);
  regs[ARK_CR50] = inCR(0x50);
  
  port_in(0x3C8);
  regs[ARK_PELMASK] = port_in(0x3C6);
 
#if 0  /* XXX */
  dac_used->saveState(regs + ARK_DAC_OFFSET);
  return ARK_DAC_OFFSET - VGA_TOTAL_REGS + dac_used->stateSize;
#endif
}


/* Set chipset-specific registers */

static void ark_setregs(const Bit16u *regs)
{
  ark_unlock();

  /* Write extended registers. */
  outCR(0x46, inCR(0x46) | 0x20);	/* Disable Clock Select Latch. */

  outSR(0x10, regs[ARK_SR10]);
  outSR(0x11, regs[ARK_SR11]);
  outSR(0x12, regs[ARK_SR12]);
  outSR(0x13, regs[ARK_SR13]);
  outSR(0x14, regs[ARK_SR14]);
  outSR(0x15, regs[ARK_SR15]);
  outSR(0x16, regs[ARK_SR16]);
  outSR(0x18, regs[ARK_SR18]);
  outSR(0x1C, regs[ARK_SR1C]);
  outSR(0x1D, regs[ARK_SR1D]);

  outCR(0x40, regs[ARK_CR40]);
  outCR(0x41, regs[ARK_CR41]);
  outCR(0x42, regs[ARK_CR42]);
  outCR(0x44, regs[ARK_CR44]);
  
  port_in(0x3C8);
  port_out(0x3C6, regs[ARK_PELMASK]);
#if 0  /* XXX */
  dac_used->restoreState(regs + ARK_DAC_OFFSET);
#endif

  outCR(0x46, regs[ARK_CR46]);
}

/* Bank switching function - set 64K bank number */

static void ark_setpage( int page ) {
	outw(0x3c4, 0x15 + (page << 8));	/* Aperture Write index. */
	outw(0x3c4, 0x16 + (page << 8));	/* Aperture Read index. */
}

static Boolean ark_init(void)
{
  Bit8u id, val;
  ark_unlock();

  id = inCR(0x50) >> 3;
  if (id == 0x12)
    ark_chip = ARK1000PV;
  else
    if (id == 0x13)
      ark_chip = ARK2000PV;
    else {
      v_printf("VGA: ark: Unknown chiptype %d.\n", id);
      return FALSE;
    }
  val = inSR(0x10);
  if (ark_chip == ARK1000PV)
    if ((val & 0x40) == 0)
      ark_memory = 1024;
    else
      ark_memory = 2048;
  else
    if ((val & 0xC0) == 0)
      ark_memory = 1024;
    else
      if ((val & 0xC0) == 0x40)
	ark_memory = 2048;
      else
	ark_memory = 4096;

  v_printf("VGA: ark: detected a %s ark card\n", ark_chipname[ark_chip]);

  if (dac_used == NULL) {
    /* Not supported. */
    v_printf("VGA: ark: Assuming normal VGA DAC.\n");
    dac_used = &normal_dac_methods;
  }

  return TRUE;
}

static void nothing(void) { }

DriverSpecs ark_driverspecs = {
	"ARK",
	ark_saveregs,		/* saveregs */
	ark_setregs,		/* setregs */
	NULL,                   /* test -- returns false positives */
	ark_init,
	ark_setpage,
	nothing,                /* get_perm -- no additional regs */
	nothing,                /* release_perm */
	NULL,
	NULL
};

