/*
 * This file is part of the UCB release of Plan 9. It is subject to the license
 * terms in the LICENSE file found in the top-level directory of this
 * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
 * part of the UCB release of Plan 9, including this file, may be copied,
 * modified, propagated, or distributed except according to the terms contained
 * in the LICENSE file.
 */

#include <u.h>
#include <lib9.h>
#include <bio.h>

#include "pci.h"
#include "vga.h"

/*
 * SGS-Thompson STG1702 Enhanced True Color Palette-DAC
 * with 16-bit Pixel Port.
 */
enum {
	Command		= 0x00,		/* Pixel Command Register */
	IndexLO		= 0x01,		/* LO-byte of 16-bit Index */
	IndexHI		= 0x02,		/* HI-byte of 16-bit Index */
	Index		= 0x03,		/* Indexed Register */

	CompanyID	= 0x00,		/* Company ID = 0x44 */
	DeviceID	= 0x01,		/* Device ID = 0x02 */
	Pmode		= 0x03,		/* Primary Pixel Mode Select */
	Smode		= 0x04,		/* Secondary Pixel Mode Select */
	Pipeline	= 0x05,		/* Pipeline Timing Control */
	Sreset		= 0x06,		/* Soft Reset */
	Power		= 0x07,		/* Power Management */

	Nindex		= 0x08,
};

static void
pixmask(void)
{
	inportb(PaddrW);
}

static void
commandrw(void)
{
	int i;

	pixmask();
	for(i = 0; i < 4; i++)
		inportb(Pixmask);
}

static uint8_t
commandr(void)
{
	uint8_t command;

	commandrw();
	command = inportb(Pixmask);
	pixmask();

	return command;
}

static void
commandw(uint8_t command)
{
	commandrw();
	outportb(Pixmask, command);
	pixmask();
}

static void
indexrw(uint8_t index)
{
	uint8_t command;

	command = commandr();
	commandw(command|0x10);

	commandrw();
	inportb(Pixmask);
	outportb(Pixmask, index & 0xFF);
	outportb(Pixmask, (index>>8) & 0xFF);
}

static void
options(Vga* vga, Ctlr* ctlr)
{
	ctlr->flag |= Hpclk2x8|Foptions;
}

static void
init(Vga* vga, Ctlr* ctlr)
{
	uint32_t pclk;

	/*
	 * Part comes in -135MHz speed-grade.
	 * In 8-bit mode the max. PCLK is 110MHz. In 2x8-bit mode
	 * the max. PCLK is the speed-grade, using the 2x doubler.
	 * We can use mode 2 (2x8-bit, internal clock doubler)
	 * if connected to a suitable graphics chip, e.g. the
	 * ET4000-w32p.
	 */
	if(vga->ctlr && ((vga->ctlr->flag & Hpclk2x8) && vga->mode->z == 8))
		pclk = 135000000;
	else
		pclk = 110000000;

	/*
	 * If we don't already have a desired pclk,
	 * take it from the mode.
	 * Check it's within range.
	 */
	if(vga->f[0] == 0)
		vga->f[0] = vga->mode->frequency;
	if(vga->f[0] < 16000000 || vga->f[0] > pclk)
		error("%s: invalid pclk - %ld\n", ctlr->name, vga->f[0]);

	/*
	 * Determine whether to use 2x8-bit mode or not.
	 * If yes and the clock has already been initialised,
	 * initialise it again.
	 */
	if(vga->ctlr && (vga->ctlr->flag & Hpclk2x8) && vga->mode->z == 8 && vga->f[0] >= 110000000){
		vga->f[0] /= 2;
		resyncinit(vga, ctlr, Upclk2x8, 0);
	}

	ctlr->flag |= Finit;
}

static void
load(Vga* vga, Ctlr* ctlr)
{
	uint8_t command, mode, pipeline;

	command = 0x00;
	mode = 0x00;
	pipeline = 0x02;
	if(ctlr->flag & Upclk2x8){
		command = 0x08;
		mode = 0x05;
		pipeline = 0x02;
		if(vga->f[0] < 16000000)
			pipeline = 0x00;
		else if(vga->f[0] < 32000000)
			pipeline = 0x01;
	}

	indexrw(Pmode);
	outportb(Pixmask, mode);
	outportb(Pixmask, mode);
	outportb(Pixmask, pipeline);
	sleep(1);
	commandw(command);

	ctlr->flag |= Fload;
}

static void
dump(Vga* vga, Ctlr* ctlr)
{
	int i;

	printitem(ctlr->name, "command");
	printreg(commandr());

	printitem(ctlr->name, "index");
	indexrw(CompanyID);
	for(i = 0; i < Nindex; i++)
		printreg(inportb(Pixmask));

	pixmask();
}

Ctlr stg1702 = {
	"stg1702",			/* name */
	0,				/* snarf */
	options,			/* options */
	init,				/* init */
	load,				/* load */
	dump,				/* dump */
};
