/* This library is free software; you can redistribute it and/or   */
/* modify it without any restrictions. This library is distributed */
/* in the hope that it will be useful, but without any warranty.   */

/* ARK driver written by Harm Hanemaayer. */

/*
 * Jan 1995:
 * Initial ARK driver. Should at least provide 25.175 and 28.322 MHz
 * dot clocks.
 */


#include <stdlib.h>
#include <stdio.h>
#include "vga.h"
#include "libvga.h"
#include "driver.h"

#include "timing.h"
#include "ramdac.h"
#include "vgaregs.h"
#include "interface.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_MEMORY_CONTROL		ARK_SR10
#define ARK_VIDEO_CLOCK_SELECT		ARK_SR11
#define ARK_VLBUS_CONTROL		ARK_SR12	/* Unused */
#define ARK_PAGE_ADDRESS_LOW		ARK_SR13	/* Unused */
#define ARK_PAGE_ADDRESS_HIGH		ARK_SR14	/* Unused */
#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 + 18
#define ARK_TOTAL_REGS	VGA_TOTAL_REGS + 18 + 10


#define ARK1000PV	1
#define ARK2000PV	2

static int ark_chip;
static int ark_memory;
static CardSpecs *cardspecs;
static DacMethods *dac_used;

static int ark_init(int, int, int);


static void nothing() { }


/* Unlock. */

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

/* Fill in chipset specific mode information */

static int ark_getmodeinfo( int mode, vga_modeinfo *modeinfo ) {
        switch (modeinfo->colors) {
                case 16 :       /* 4-plane 16 color mode */
                        modeinfo->maxpixels = 65536 * 8;
                        break;
                default :
                        modeinfo->maxpixels = ark_memory * 1024 /
                                        modeinfo->bytesperpixel;
                        break;
        }
	modeinfo->maxlogicalwidth = 4088;
	modeinfo->startaddressrange = 0x1fffff;
	modeinfo->haveblit = 0;
	modeinfo->flags &= ~HAVE_RWPAGE;
	return 0;
}


/* Return non-zero if mode is available */

static int ark_modeavailable( int mode ) {
	struct info *info;
	ModeInfo *modeinfo;
	ModeTiming *modetiming;

	if ((mode < G640x480x256 || mode == G720x348x2)
	&& mode != G320x200x256)
		return vga_driverspecs.modeavailable(mode);

	/* Enough memory? */
	info = &__svgalib_infotable[mode];
	if (ark_memory * 1024 < info->ydim * info->xbytes)
		return 0;

	modeinfo = createModeInfoStructureForSvgalibMode(mode);

	modetiming = malloc(sizeof(ModeTiming));
	if (getmodetiming(modetiming, modeinfo, cardspecs)) {
		free(modetiming);
		free(modeinfo);
		return 0;
	}
	free(modetiming);
	free(modeinfo);

	return SVGADRV;
}


static int ark_saveregs( unsigned char regs[] ) {
	ark_unlock();

 	/* Save extended registers. */
 	regs[ARK_SR10] = inSR(0x10);
 	regs[ARK_SR11] = inSR(0x11);
 	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);
 
	dac_used->saveState(regs + ARK_DAC_OFFSET);
	return ARK_DAC_OFFSET - VGA_TOTAL_REGS + dac_used->stateSize;
}


/* Set chipset-specific registers */

static int ark_setregs( const unsigned char regs[], int mode )
{
	ark_unlock();

 	/* Write extended registers. */
	outSR(0x10, regs[ARK_SR10]);
	outSR(0x11, regs[ARK_SR11]);
	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]);
	outCR(0x46, regs[ARK_CR46]);

	if (dac_used->id == S3_SDAC) {
		unsigned char CR1;
		printf("DAC clock state: 0x%02X 0x%02X\n",
			regs[ARK_DAC_OFFSET + 3],
			regs[ARK_DAC_OFFSET + 4]);
		/* Blank the screen.*/
		CR1 = inCR(0x01);
		outCR(0x01, CR1 | 0x20);
		dac_used->restoreState(regs + ARK_DAC_OFFSET);
		outCR(0x01, CR1);	/* Unblank screen. */
	}
	return 0;
}


/*
 * Initialize register state for a mode.
 */

static void ark_initializemode( unsigned char *moderegs,
ModeTiming *modetiming, ModeInfo *modeinfo) {

	/* Get current values. */
	ark_saveregs(moderegs);

	/* Set up the standard VGA registers for a generic SVGA. */
	setup_VGA_registers(moderegs, modetiming, modeinfo);

	/* Set up the extended register values, including modifications */
	/* of standard VGA registers. */

	moderegs[VGA_CR13] = modeinfo->lineWidth >> 3;

	moderegs[ARK_MEMORY_CONTROL] &= ~0x0F;
	/* VESA Super VGA memory organisation, no COP. */
	moderegs[ARK_MEMORY_CONTROL] |= 0x03;

	moderegs[ARK_VIDEO_CLOCK_SELECT] &= ~0x0F;
	/* Set COP and Giant Shift pixel depth. */
	if (modeinfo->bytesPerPixel == 1)
		moderegs[ARK_VIDEO_CLOCK_SELECT] |= 0x5;
	if (modeinfo->bytesPerPixel == 2)
		moderegs[ARK_VIDEO_CLOCK_SELECT] |= 0xA;
	if (modeinfo->bytesPerPixel >= 3)
		moderegs[ARK_VIDEO_CLOCK_SELECT] |= 0xF;

	moderegs[ARK_APERTURE_WRITE_INDEX] = 0;
	moderegs[ARK_APERTURE_READ_INDEX] = 0;

/* Display FIFO. */
	{
		int threshold;
		unsigned char val;
		threshold = 4;		/* A guess. */
		val = moderegs[ARK_DISPLAY_FIFO_CONTROL];
		if (ark_chip == ARK1000PV) {
			val |= 0x08;	/* Enable full FIFO. */
			val &= ~0x07;
			val |= threshold;
		}
		if (ark_chip == ARK2000PV) {
			val &= 0x40;
			val |= 0x10;	/* 32-deep FIFO. */
			val |= (threshold & 0x0E) >> 1;
			if (threshold & 0x01)
				val |= 0x80;
			if (threshold & 0x10)
				val |= 0x20;
		}
		moderegs[ARK_DISPLAY_FIFO_CONTROL] = val;
	}

/* CRTC timing. */
	{
		unsigned char val;
		/* Vertical Overflow. */
		val = 0;
		if ((modetiming->CrtcVTotal - 2) & 0x400)
			val |= 0x80;
		if ((modetiming->VDisplay - 1) & 0x400)
			val |= 0x40;
		/* VBlankStart is equal to VSyncStart + 1. */
		if (modetiming->CrtcVSyncStart & 0x400)
			val |= 0x20;
		/* VRetraceStart is equal to VSyncStart + 1. */
		if (modetiming->CrtcVSyncStart & 0x400)
			val |= 0x10;
		moderegs[ARK_CRTC_VERTICAL_OVERFLOW] = val;

		/* Horizontal Overflow. */
		val = moderegs[ARK_CRTC_VERTICAL_OVERFLOW];
		val &= 0x07;
		if ((modetiming->CrtcHTotal / 8 - 5) & 0x100)
			val |= 0x80;
		if ((modetiming->CrtcHDisplay / 8 - 1) & 0x100)
			val |= 0x40;
		/* HBlankStart is equal to HSyncStart - 1. */
		if ((modetiming->CrtcHSyncStart / 8 - 1) & 0x100)
			val |= 0x20;
		/* HRetraceStart is equal to HSyncStart. */
		if ((modetiming->CrtcHSyncStart / 8) & 0x100)
			val |= 0x10;
		if ((modeinfo->lineWidth / 8) & 0x100)
			val |= 0x08;
		moderegs[ARK_CRTC_VERTICAL_OVERFLOW] = val;
	}
	moderegs[ARK_VGA_ENHANCEMENT] &= ~0x04;		/* No interlace. */
	if (modetiming->flags & INTERLACED) {
		/* Set mid-scan vertical retrace start. */
		moderegs[ARK_INTERLACE_RETRACE] =
			(modetiming->CrtcHTotal / 8 - 5) / 2;
		moderegs[ARK_VGA_ENHANCEMENT] |= 0x04;	/* Interlaced. */
	}

/* Clocking. */
	/* Select 8 or 16-bit video output to RAMDAC on 2000PV. */
	if (ark_chip == ARK2000PV) {
		int dac16;
 		moderegs[ARK_PIXEL_CLOCK_CONTROL] &= ~0x04;	/* 8-bit */
		dac16 = 0;
		if (modeinfo->bitsPerPixel == 8 &&
		cardspecs->mapClock(8, modetiming->pixelClock)
		== modetiming->pixelClock / 2)
			/* Typically high resolution 8bpp (> 110 MHz). */
			dac16 = 1;
		if (modeinfo->bitsPerPixel == 16 &&
		cardspecs->mapClock(16, modetiming->pixelClock)
		== modetiming->pixelClock)
			/* 16bpp at pixel rate. */
			dac16 = 1;
		/* Note: with an 8-bit DAC, 16bpp is Clock * 2. */
		/* 24bpp is always Clock * 3. */
		if (modeinfo->bitsPerPixel == 32 &&
		cardspecs->mapClock(32, modetiming->pixelClock)
		== modetiming->pixelClock * 2)
			/* 32bpp at Clock * 2. */
			dac16 = 1;
		/* Note: with an 8-bit dac, 32bpp is Clock * 4. */
		if (dac16)
			moderegs[ARK_PIXEL_CLOCK_CONTROL] |= 0x04; /* 16-bit */
	}

	if (modetiming->flags & USEPROGRCLOCK)
		moderegs[VGA_MISCOUTPUT] |= 0x0C; /* External clock select. */
	else {
		/* Program clock select. */
		moderegs[VGA_MISCOUTPUT] &= ~0x0C;
		moderegs[VGA_MISCOUTPUT] |=
			(modetiming->selectedClockNo & 3) << 2;
		moderegs[ARK_VIDEO_CLOCK_SELECT] &= ~0xC0;
		moderegs[ARK_VIDEO_CLOCK_SELECT] |=
			(modetiming->selectedClockNo & 0xC) << 4;
	}

	if (dac_used->id != NORMAL_DAC) {
		dac_used->initializeState(&moderegs[ARK_DAC_OFFSET],
			modeinfo->bitsPerPixel,
			colorbits_to_colormode(modeinfo->colorBits),
			modetiming->pixelClock);
		printf("DAC clock state: 0x%02X 0x%02X\n",
			moderegs[ARK_DAC_OFFSET + 3],
			moderegs[ARK_DAC_OFFSET + 4]);
	}
}


/* Set a mode */

static int ark_setmode( int mode, int prv_mode ) {
	ModeInfo *modeinfo;
	ModeTiming *modetiming;
	unsigned char *moderegs;

	if ((mode < G640x480x256 || mode == G720x348x2)
	&& mode != G320x200x256)
		/* Let the standard VGA driver set standard VGA modes. */
		return vga_driverspecs.setmode(mode, prv_mode);
	if (!ark_modeavailable(mode))
		return 1;

	modeinfo = createModeInfoStructureForSvgalibMode(mode);

	modetiming = malloc(sizeof(ModeTiming));
	if (getmodetiming(modetiming, modeinfo, cardspecs)) {
		free(modetiming);
		free(modeinfo);
		return 1;
	}

	moderegs = malloc(ARK_TOTAL_REGS);

	ark_initializemode(moderegs, modetiming, modeinfo);
	free(modeinfo);
	free(modetiming);

	__vga_setregs(moderegs);	/* Set standard regs. */
	ark_setregs(moderegs, mode);	/* Set extended regs. */
	free(moderegs);
	return 0;
}


/* Indentify chipset; return non-zero if detected */

static int ark_test() {
	if (ark_init(0, 0, 0))
		return 0;
	return 1;
}


/* 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. */
}


/* Set display start address (not for 16 color modes) */

static int ark_setdisplaystart( int address ) {
	outw(0x3d4, 0x0d + ((address >> 2) & 0x00ff) * 256);	/* sa2-sa9 */
	outw(0x3d4, 0x0c + ((address >> 2) & 0xff00));		/* sa10-sa17 */
	inb(0x3da);			/* set ATC to addressing mode */
	outb(0x3c0, 0x13 + 0x20);	/* select ATC reg 0x13 */
	outb(0x3c0, (inb(0x3c1) & 0xf0) | ((address & 3) << 1));
		/* write sa0-1 to bits 1-2 */
	outb(CRT_I, 0x40);
	outb(CRT_D, (address & 0x1C0000) >> 18);	/* sa18-sa20 */
}


/* Set logical scanline length (usually multiple of 8) */
/* Multiples of 8 to 2040 */

static int ark_setlogicalwidth( int width ) { 
	outw(0x3d4, 0x13 + (width >> 3) * 256);	/* lw3-lw11 */
	outb(CRT_I, 0x41);
	outb(CRT_D, (inb(CRT_D) & ~0x08) | ((width >> 3) & 0x100));
	return 0;
}


/* Function table (exported) */

DriverSpecs ark_driverspecs = {
	ark_saveregs,		/* saveregs */
	ark_setregs,		/* setregs */
	(int (*)()) nothing,	/* unlock */
	(int (*)()) nothing,	/* lock */
	ark_test,
	ark_init,
	(int (*)()) ark_setpage,
	(int (*)()) nothing,
	(int (*)()) nothing,
	ark_setmode,
	ark_modeavailable,
	ark_setdisplaystart,
	ark_setlogicalwidth,
	ark_getmodeinfo,
	0,	/* bitblt */
	0,	/* imageblt */
	0,	/* fillblt */
	0,	/* hlinelistblt */
	0,	/* bltwait */
	0,	/* extset */
	0,
	0,	/* linear */
	NULL	/* accelspecs */
};


/* Initialize driver (called after detection) */

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

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

static int ark_init( int force, int par1, int par2) {
	ark_unlock();

	if (force) {
		ark_chip = par1;	/* we already know the type */
		ark_memory = par2;
	}
	else {
		unsigned char id, val;
		id = inCR(0x50) >> 3;
		if (id == 0x12)
			ark_chip = ARK1000PV;
		else
		if (id == 0x13)
			ark_chip = ARK2000PV;
		else {
			printf("svgalib: ark: Unknown chiptype %d.\n",
				ark_chip);
			return -1;
		}
		val = inSR(0x10);
		if ((val & 0xC0) == 0)
			ark_memory = 1024;
		if ((val & 0xC0) == 0x40)
			ark_memory = 2048;
		else
			ark_memory = 4096;
	}

	/* Detect DAC. */

 #if 1	/* Use 0 to force SDAC. */
	dac_used = probeDacs(dacs_to_probe);
#else
	dac_used = &S3_SDAC_methods;
	dac_used->initialize();
#endif

/* begin: Initialize cardspecs. */
	cardspecs = malloc(sizeof(CardSpecs));
	cardspecs->videoMemory = ark_memory;
	cardspecs->maxHorizontalCrtc = 4088;
	cardspecs->nClocks = 0;
	cardspecs->flags = 0;

	dac_used->qualifyCardSpecs(cardspecs);

	/* Initialize standard clocks for unknown DAC. */
	if (!(dac_used->flags & CLOCK_PROGRAMMABLE)
	&& cardspecs->nClocks == 0) {
		cardspecs->nClocks = 2;
		cardspecs->clocks = malloc(sizeof(int) * 2);
		cardspecs->clocks[0] = 25175;
		cardspecs->clocks[1] = 28322;
	}

	/* Limit pixel clocks according to chip specifications. */
	if (ark_chip == ARK1000PV) {
		/* Limit max clocks according to 120 MHz DCLK spec. */
		/* 8-bit DAC. */
		LIMIT(cardspecs->maxPixelClock4bpp, 120000);
		LIMIT(cardspecs->maxPixelClock8bpp, 120000);
		LIMIT(cardspecs->maxPixelClock16bpp, 120000 / 2);
		LIMIT(cardspecs->maxPixelClock24bpp, 120000 / 3);
		LIMIT(cardspecs->maxPixelClock32bpp, 120000 / 4);
	}
	if (ark_chip == ARK2000PV) {
		/* Limit max clocks according to 120 MHz DCLK spec. */
		/* Assume 16-bit DAC. */
		LIMIT(cardspecs->maxPixelClock4bpp, 120000);
		LIMIT(cardspecs->maxPixelClock8bpp, 120000 * 2);
		LIMIT(cardspecs->maxPixelClock16bpp, 120000);
		LIMIT(cardspecs->maxPixelClock24bpp, 120000 / 3);
		LIMIT(cardspecs->maxPixelClock32bpp, 120000 / 2);
	}
/* end: Initialize cardspecs. */

	if (__svgalib_driver_report) {
		printf("svgalib: Using ARK driver (%s, %dK).\n",
			ark_chipname[ark_chip], ark_memory);
	}
	driverspecs = &ark_driverspecs;

	return 0;
}
