~kris/9p

9hist

150ca26a9045f63dd57e8c7f1ac4d2b66b18ee2d — David du Colombier 24 years ago d816200
Plan 9 from Bell Labs 2002-01-25
8 files changed, 1843 insertions(+), 5 deletions(-)

A mtx/devrtc.c
M mtx/l.s
M mtx/mem.h
A pc/devtv.c
A pc/vgamga4xx.c
M port/devbridge.c
M port/devloopback.c
M port/devsdp.c
A mtx/devrtc.c => mtx/devrtc.c +421 -0
@@ 0,0 1,421 @@
/*
 *	M48T59/559 Timekeeper
 */
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"../port/error.h"

#include	"io.h"

enum{
	STB0 = 0x74,
	STB1 = 0x75,
	Data = 0x77,

	NVOFF=	0,
	NVLEN=	0x1ff0,		/* length in bytes of NV RAM */

	/*
	 *  register offsets into time of day clock
	 */
	NVflags=	0x1ff0,
	NVctl=	0x1ff8,
	NVsec,
	NVmin,
	NVhour,	
	NVday,		/* (1 = Sun) */
	NVmday,		/* (1-31) */
	NVmon,		/* (1-12) */
	NVyear,		/* (0-99) */

	/* NVctl */
	RTwrite = (1<<7),
	RTread = (1<<6),
	RTsign = (1<<5),
	RTcal = 0x1f,

	Qdir = 0,
	Qrtc,
	Qnvram,
};

/*
 *  broken down time
 */
typedef struct
{
	int	sec;
	int	min;
	int	hour;
	int	mday;
	int	mon;
	int	year;
} Rtc;

QLock	rtclock;		/* mutex on nvram operations */

static Dirtab rtcdir[]={
	".",		{Qdir, 0, QTDIR},	0,	DMDIR|0555,
	"rtc",		{Qrtc, 0},	0,	0644,
	"nvram",	{Qnvram, 0},	0,	0600,
};

static ulong	rtc2sec(Rtc*);
static void	sec2rtc(ulong, Rtc*);
static void	setrtc(Rtc*);
static void	nvcksum(void);
static void	nvput(int, uchar);
static uchar	nvget(int);

static Chan*
rtcattach(char *spec)
{
	return devattach('r', spec);
}

static Walkqid*
rtcwalk(Chan *c, Chan *nc, char **name, int nname)
{
	return devwalk(c, nc, name, nname, rtcdir, nelem(rtcdir), devgen);
}

static int	 
rtcstat(Chan *c, uchar *dp, int n)
{
	return devstat(c, dp, n, rtcdir, nelem(rtcdir), devgen);
}

static Chan*
rtcopen(Chan *c, int omode)
{
	omode = openmode(omode);
	switch((ulong)c->qid.path){
	case Qrtc:
		if(strcmp(up->user, eve)!=0 && omode!=OREAD)
			error(Eperm);
		break;
	case Qnvram:
		if(strcmp(up->user, eve)!=0 || !cpuserver)
			error(Eperm);
	}
	return devopen(c, omode, rtcdir, nelem(rtcdir), devgen);
}

static void	 
rtcclose(Chan*)
{
}

static long	 
rtcread(Chan *c, void *buf, long n, vlong off)
{
	char *p;
	ulong t;
	int i;
	ulong offset = off;

	if(c->qid.type & QTDIR)
		return devdirread(c, buf, n, rtcdir, nelem(rtcdir), devgen);

	switch((ulong)c->qid.path){
	case Qrtc:
		qlock(&rtclock);
		t = rtctime();
		qunlock(&rtclock);
		n = readnum(offset, buf, n, t, 12);
		return n;
	case Qnvram:
		offset += NVOFF;
		if(offset > NVLEN)
			return 0;
		if(n > NVLEN - offset)
			n = NVLEN - offset;
		p = buf;
		qlock(&rtclock);
		for(i = 0; i < n; i++)
			p[i] = nvget(i+offset);
		qunlock(&rtclock);
		return n;
	}
	error(Egreg);
	return -1;		/* never reached */
}

static long	 
rtcwrite(Chan *c, void *buf, long n, vlong off)
{
	Rtc rtc;
	ulong secs;
	char *cp, *ep;
	int i;
	ulong offset = off;

	switch((ulong)c->qid.path){
	case Qrtc:
		if(offset!=0)
			error(Ebadarg);
		/*
		 *  read the time
		 */
		cp = ep = buf;
		ep += n;
		while(cp < ep){
			if(*cp>='0' && *cp<='9')
				break;
			cp++;
		}
		secs = strtoul(cp, 0, 0);
		/*
		 *  convert to bcd
		 */
		sec2rtc(secs, &rtc);
		/*
		 * write it
		 */
		qlock(&rtclock);
		setrtc(&rtc);
		qunlock(&rtclock);
		return n;
	case Qnvram:
		offset += NVOFF;
		if(offset > NVLEN)
			return 0;
		if(n > NVLEN - offset)
			n = NVLEN - offset;
		qlock(&rtclock);
		for(i = 0; i < n; i++)
			nvput(i+offset, ((uchar*)buf)[i]);
		nvcksum();
		qunlock(&rtclock);
		return n;
	}
	error(Egreg);
	return -1;		/* never reached */
}

long
rtcbwrite(Chan *c, Block *bp, ulong offset)
{
	return devbwrite(c, bp, offset);
}

Dev rtcdevtab = {
	'r',
	"rtc",

	devreset,
	devinit,
	devshutdown,
	rtcattach,
	rtcwalk,
	rtcstat,
	rtcopen,
	devcreate,
	rtcclose,
	rtcread,
	devbread,
	rtcwrite,
	devbwrite,
	devremove,
	devwstat,
};

static void
nvput(int offset, uchar val)
{
	outb(STB0, offset);
	outb(STB1, offset>>8);
	outb(Data, val);
}

static uchar
nvget(int offset)
{
	outb(STB0, offset);
	outb(STB1, offset>>8);
	return inb(Data);
}

static void
nvcksum(void)
{
}

static int
getbcd(int bcd)
{
	return (bcd&0x0f) + 10 * (bcd>>4);
}

static int
putbcd(int val)
{
	return (val % 10) | (((val/10) % 10) << 4);
}

long	 
rtctime(void)
{
	int ctl;
	Rtc rtc;

	/*
	 *  convert from BCD
	 */
	ctl = nvget(NVctl);
	ctl &= RTsign|RTcal;
	nvput(NVctl, ctl|RTread);

	rtc.sec = getbcd(nvget(NVsec) & 0x7f);
	rtc.min = getbcd(nvget(NVmin));
	rtc.hour = getbcd(nvget(NVhour));
	rtc.mday = getbcd(nvget(NVmday));
	rtc.mon = getbcd(nvget(NVmon));
	rtc.year = getbcd(nvget(NVyear));
	if(rtc.year < 70)
		rtc.year += 2000;
	else
		rtc.year += 1900;

	nvput(NVctl, ctl);

	return rtc2sec(&rtc);
}

static void
setrtc(Rtc *rtc)
{
	int ctl;

	ctl = nvget(NVctl);
	ctl &= RTsign|RTcal;
	nvput(NVctl, ctl|RTwrite);

	nvput(NVsec, putbcd(rtc->sec));
	nvput(NVmin, putbcd(rtc->min));
	nvput(NVhour, putbcd(rtc->hour));
	nvput(NVmday, putbcd(rtc->mday));
	nvput(NVmon, putbcd(rtc->mon));
	nvput(NVyear, putbcd(rtc->year % 100));

	nvput(NVctl, ctl);
}

#define SEC2MIN 60L
#define SEC2HOUR (60L*SEC2MIN)
#define SEC2DAY (24L*SEC2HOUR)

/*
 *  days per month plus days/year
 */
static	int	dmsize[] =
{
	365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
static	int	ldmsize[] =
{
	366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};

/*
 *  return the days/month for the given year
 */
static int *
yrsize(int y)
{

	if((y%4) == 0 && ((y%100) != 0 || (y%400) == 0))
		return ldmsize;
	else
		return dmsize;
}

/*
 *  compute seconds since Jan 1 1970
 */
static ulong
rtc2sec(Rtc *rtc)
{
	ulong secs;
	int i;
	int *d2m;

	secs = 0;

	/*
	 *  seconds per year
	 */
	for(i = 1970; i < rtc->year; i++){
		d2m = yrsize(i);
		secs += d2m[0] * SEC2DAY;
	}

	/*
	 *  seconds per month
	 */
	d2m = yrsize(rtc->year);
	for(i = 1; i < rtc->mon; i++)
		secs += d2m[i] * SEC2DAY;

	secs += (rtc->mday-1) * SEC2DAY;
	secs += rtc->hour * SEC2HOUR;
	secs += rtc->min * SEC2MIN;
	secs += rtc->sec;

	return secs;
}

/*
 *  compute rtc from seconds since Jan 1 1970
 */
static void
sec2rtc(ulong secs, Rtc *rtc)
{
	int d;
	long hms, day;
	int *d2m;

	/*
	 * break initial number into days
	 */
	hms = secs % SEC2DAY;
	day = secs / SEC2DAY;
	if(hms < 0) {
		hms += SEC2DAY;
		day -= 1;
	}

	/*
	 * generate hours:minutes:seconds
	 */
	rtc->sec = hms % 60;
	d = hms / 60;
	rtc->min = d % 60;
	d /= 60;
	rtc->hour = d;

	/*
	 * year number
	 */
	if(day >= 0)
		for(d = 1970; day >= *yrsize(d); d++)
			day -= *yrsize(d);
	else
		for (d = 1970; day < 0; d--)
			day += *yrsize(d-1);
	rtc->year = d;

	/*
	 * generate month
	 */
	d2m = yrsize(rtc->year);
	for(d = 1; day >= d2m[d]; d++)
		day -= d2m[d];
	rtc->mday = day + 1;
	rtc->mon = d;

	return;
}

M mtx/l.s => mtx/l.s +3 -0
@@ 288,6 288,9 @@ tas0:
TEXT	firmware(SB), $0
	MOVW	initsprg3(SB), R3
	MOVW	R3, SPR(SPRG3)
//	MOVW	$0x4000, R3
//	MOVW	R3, SPR(SPRG3)
	MOVW	R0, SPR(HID0)
	MOVW	$MSR_IP, R4
	MOVW	R4, SPR(SRR1)
	MOVW	$0xfff00100, R5

M mtx/mem.h => mtx/mem.h +1 -0
@@ 99,6 99,7 @@
#define MSR_IP		BIT(25)		/* Exception prefix 0x000/0xFFF */
#define MSR_IR		BIT(26)		/* Instruction MMU enable */
#define MSR_DR		BIT(27)		/* Data MMU enable */
#define MSR_PM		BIT(29)		/* Performance Monitor marked mode (604e specific) */
#define MSR_RI		BIT(30)		/* Recoverable Exception */
#define MSR_LE		BIT(31)		/* Little-Endian enable */


A pc/devtv.c => pc/devtv.c +854 -0
@@ 0,0 1,854 @@
/*
  * Driver for Bt848 TV tuner.  WARNING, I used the Linux driver as
  * documentation.
  *
  */
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"../port/error.h"
#include "io.h"

enum {
	Qdir = 0,
	Qdata,
	Qctl,
	Qregs,
	Qdbuf,

	Brooktree_vid = 0x109e,
	Brooktree_848_did = 0x0350,
	Intel_vid = 0x8086,
	Intel_82437_did = 0x122d,

	K = 1024,
	M = K * K,

	ntsc_rawpixels = 910,
	ntsc_sqpixels = 780,					// Including blanking & inactive
	ntsc_hactive = 640,
	ntsc_vactive = 480,
	ntsc_clkx1delay = 135,				// Clock ticks.
	ntsc_clkx1hactive = 754,
	ntsc_vdelay = 26,					// # of scan lines.
	ntsc_vscale = 0,

	i2c_timing = 7 << 4,
	i2c_bt848w3b = 1 << 2,
	i2c_bt848scl = 1 << 1,
	i2c_bt848sda = 1 << 0,

	i2c_miroproee = 0x80,				// MIRO PRO EEPROM
	i2c_tea6300 = i2c_miroproee,
	i2c_tda8425 = 0x82,
	i2c_tda9840 = 0x84,
	i2c_tda9850 = 0xb6,
	i2c_haupee = 0xa0,					// Hauppage EEPROM
	i2c_stbee = 0xae,					// STB EEPROM

	Bt848_miropro = 0,
	Bt848_miro,

	// Bit fields.
	iform_muxsel1 = 3 << 5,				// 004
	iform_muxsel0 = 2 << 5,	
	iform_xtselmask = 3 << 3,
	iform_xtauto = 3 << 3,
	iform_formatmask = 7 << 0,
	iform_ntsc = 1 << 0,

	control_ldec = 1 << 5,				// 02C
	contrast_100percent = 0xd8,			// 030

	vscale_interlaced = 1 << 5,			// 04C

	adelay_ntsc = 104,					// 060
	bdelay_ntsc = 93,					// 064
	adc_crush = 1 << 0,					// 068

	colorfmt_rgb16 = (2 << 4) | (2 << 0),	// 0D4
	colorctl_gamma = 1 << 4,			// 0D8
	capctl_fullframe = 1 << 4,				// 0DC
	capctl_captureodd = 1 << 1,
	capctl_captureeven = 1 << 0,
	vbipacksize = 0x190,				// 0E0

	intstat_i2crack = 1 << 25,				// 100
	intstat_scerr = 1 << 19,
	intstat_ocerr = 1 << 18,
	intstat_fbus = 1 << 12,
	intstat_risci = 1 << 11,
	intstat_i2cdone = 1 << 8,	
	intstat_vpress = 1 << 5,
	intstat_hlock = 1 << 4,
	intstat_vsync = 1 << 1,
	intstat_fmtchg = 1 << 0,
	intmask_etbf = 1 << 23,				// 104

	gpiodmactl_pltp23_16 = 2 << 6,		// 10C
	gpiodmactl_pltp23_0 = 0 << 6,	
	gpiodmactl_pltp1_16 = 2 << 4,
	gpiodmactl_pltp1_0 = 0 << 4,
	gpiodmactl_pktp_32 = 3 << 2,		
	gpiodmactl_pktp_0 = 0 << 2,		
	gpiodmactl_riscenable = 1 << 1,
	gpiodmactl_fifoenable = 1 << 0,	

	// RISC instructions and parameters.
	fifo_vre = 0x4,
	fifo_vro = 0xC,
	fifo_fm1 = 0x6,

	riscirq = 1 << 24,
	riscwrite = 0x1 << 28,
		riscwrite_sol = 1 << 27,
		riscwrite_eol = 1 << 26,
	riscskip = 0x2 << 28,
	riscjmp = 0x7 << 28,
	riscsync = 0x8 << 28,
		riscsync_resync = 1 << 15,
		riscsync_vre = fifo_vre << 0,
		riscsync_vro = fifo_vro << 0,
		riscsync_fm1 = fifo_fm1 << 0,
};

typedef struct {
	ushort		vid;
	ushort		did;
	char			*name;
} Variant;

typedef struct {
	ulong	devstat;		// 000
	ulong	iform;		// 004
	ulong	tdec;			// 008
	ulong	ecrop;		// 00C
	ulong	evdelaylo;		// 010
	ulong	evactivelo;	// 014
	ulong	ehdelaylo;		// 018
	ulong	ehactivelo;	// 01C
	ulong	ehscalehi;		// 020
	ulong	ehscalelo;		// 024
	ulong	bright;		// 028
	ulong	econtrol;		// 02C
	ulong	contrastlo;	// 030
	ulong	satulo;		// 034
	ulong	satvlo;		// 038
	ulong	hue;			// 03C
	ulong	escloop;		// 040
	ulong	pad0;		// 044
	ulong	oform;		// 048
	ulong	evscalehi;		// 04C
	ulong	evscalelo;		// 050
	ulong	test;			// 054
	ulong	pad1[2];		// 058-05C
	ulong	adelay;		// 060
	ulong	bdelay;		// 064
	ulong	adc;			// 068
	ulong	evtc;			// 06C
	ulong	pad2[3];		// 070-078
	ulong	sreset;		// 07C
	ulong	tglb;			// 080
	ulong	tgctrl;		// 084
	ulong	pad3;		// 088
	ulong	ocrop;		// 08C
	ulong	ovdelaylo;		// 090
	ulong	ovactivelo;	// 094
	ulong	ohdelaylo;	// 098
	ulong	ohactivelo;	// 09C
	ulong	ohscalehi;		// 0A0
	ulong	ohscalelo;		// 0A4
	ulong	pad4;		// 0A8
	ulong	ocontrol;		// 0AC
	ulong	pad5[4];		// 0B0-0BC
	ulong	oscloop;		// 0C0
	ulong	pad6[2];		// 0C4-0C8
	ulong	ovscalehi;		// 0CC
	ulong	ovscalelo;		// 0D0
	ulong	colorfmt;		// 0D4
	ulong	colorctl;		// 0D8
	ulong	capctl;		// 0DC
	ulong	vbipacksize;	// 0E0
	ulong	vbipackdel;	// 0E4
	ulong	fcap;			// 0E8
	ulong	ovtc;			// 0EC
	ulong	pllflo;		// 0F0
	ulong	pllfhi;		// 0F4
	ulong	pllxci;		// 0F8
	ulong	dvsif;		// 0FC
	ulong	intstat;		// 100
	ulong	intmask;		// 104
	ulong	pad7;		// 108
	ulong	gpiodmactl;	// 10C
	ulong	i2c;			// 110
	ulong	riscstrtadd;	// 114
	ulong	gpioouten;	// 118
	ulong	gpioreginp;	// 11C
	ulong	risccount;		// 120
	ulong	pad8[55];		// 124-1FC	
	ulong	gpiodata[64];	// 200-2FC
} Bt848;

typedef struct {
	char		*name;
  	ushort	freq_vhfh;	// Start frequency
	ushort	freq_uhf;  
	uchar	VHF_L;
	uchar	VHF_H;
	uchar	UHF;
	uchar	cfg; 
	ushort	offs;
} Tuner;

typedef struct {
	Lock;
	Bt848	*bt848;
	Variant	*variant;
	Tuner	*tuner;
	Pcidev	*pci;
	ulong	*program;	// Current DMA program
	uchar	i2ctuneraddr;
	uchar	i2ccmd;		// I2C command
	int		board;		// What board is this?

	uchar	*dbuf;
} Tv;

// Tuner related.
enum {
	TemicPAL = 0,
	PhilipsPAL,
	PhilipsNTSC,
	PhilipsSECAM,
	Notuner,
	PhilipsPALI,
	TemicNTSC,
	TemicPALI,
	Temic4036,
	AlpsTSBH1,
	AlpsTSBE1,

	Freqmultiplier = 16,
};

static Tuner tuners[] = {
        {"Temic PAL", Freqmultiplier * 140.25, Freqmultiplier * 463.25, 
		0x02, 0x04, 0x01, 0x8e, 623 },
	{"Philips PAL_I", Freqmultiplier * 140.25, Freqmultiplier * 463.25, 
		0xa0, 0x90, 0x30, 0x8e, 623 },
	{"Philips NTSC",  Freqmultiplier * 157.25, Freqmultiplier * 451.25, 
		0xA0, 0x90, 0x30, 0x8e, 732 },
	{"Philips SECAM", Freqmultiplier * 168.25, Freqmultiplier * 447.25, 
		0xA7, 0x97, 0x37, 0x8e, 623 },
	{"NoTuner", 0, 0, 
		0x00, 0x00, 0x00, 0x00, 0 },
	{"Philips PAL", Freqmultiplier * 168.25, Freqmultiplier * 447.25, 
		0xA0, 0x90, 0x30, 0x8e, 623 },
	{"Temic NTSC", Freqmultiplier * 157.25, Freqmultiplier * 463.25, 
		0x02, 0x04, 0x01, 0x8e, 732 },
	{"TEMIC PAL_I", Freqmultiplier * 170.00, Freqmultiplier * 450.00, 
		0x02, 0x04, 0x01, 0x8e, 623 },
	{"Temic 4036 FY5 NTSC", Freqmultiplier * 157.25, Freqmultiplier * 463.25, 
		0xa0, 0x90, 0x30, 0x8e, 732 },
	{"Alps TSBH1", Freqmultiplier * 137.25, Freqmultiplier * 385.25, 
		0x01, 0x02, 0x08, 0x8e, 732 },
	{"Alps TSBE1", Freqmultiplier * 137.25, Freqmultiplier * 385.25, 
		0x01, 0x02, 0x08, 0x8e, 732 },
};

enum {
	CMvstart,
	CMvstop,
	CMchannel,
};

static Cmdtab tvctlmsg[] = {
	CMvstart,			"vstart",			3,
	CMvstop,			"vstop",			1,
	CMchannel,		"channel",			3,
};

static Dirtab tvtab[]={
	".",		{ Qdir, 0, QTDIR },	0,	DMDIR|0555,
	"tv",		{ Qdata, 0 },		0,	0600,
	"tvctl",	{ Qctl, 0 },			0,	0600,
	"tvregs",	{ Qregs, 0 },		0,	0400,
	"tvdbuf",	{ Qdbuf, 0 },		0,	0400,
};

static Variant variant[] = {
{	Brooktree_vid,	Brooktree_848_did,	"Brooktree 848 TV tuner",	},
};

static char *boards[] = {
	"MIRO PRO",
	"MIRO",
};

static Tv *tv;

static int i2cread(Tv *, uchar, uchar *);
static int i2cwrite(Tv *, uchar, uchar, uchar, int);
static void tvinterrupt(Ureg *, Tv *);
static void vstart(Tv *, ulong, int);
static void vstop(Tv *);
static void frequency(Tv *, int, int);

static void
tvinit(void)
{
	Pcidev *pci;
	ulong intmask;

	if (!getconf("tv0"))
		return;

	// Test for a triton memory controller.
	intmask = 0;
	if (pcimatch(nil, Intel_vid, Intel_82437_did))
		intmask = intmask_etbf;

	pci = nil;
	while ((pci = pcimatch(pci, 0, 0)) != nil) {
		int i, t;
		Bt848 *bt848;
		ushort hscale, hdelay;
		uchar v;

		for (i = 0; i != nelem(variant); i++)
			if (pci->vid == variant[i].vid && pci->did == variant[i].did)
				break;
		if (i == nelem(variant))
			continue;

		if (tv) {
			print("#V: Currently there is only support for one TV, ignoring %s\n",
				variant[i].name);
			continue;
		}

		tv = (Tv *)malloc(sizeof(Tv));
		assert(tv);

		tv->variant = &variant[i];
		tv->pci = pci;
		tv->bt848 = (Bt848 *)upamalloc(pci->mem[0].bar & ~0x0F, 4 * K, K);
		if (tv->bt848 == nil)
			panic("#V: Cannot allocate memory for Bt848\n");
		bt848 = tv->bt848;

		// i2c stuff.
		if (pci->did >= 878)
			tv->i2ccmd = 0x83;
		else 
			tv->i2ccmd = i2c_timing | i2c_bt848scl | i2c_bt848sda;

		{
			bt848->gpioouten = 0;

			bt848->gpioouten |= 1 << 5;
			bt848->gpiodata[0] |= 1 << 5;
			microdelay(2500);
			bt848->gpiodata[0] |= 1 << 5;
			microdelay(2500);
		}
		
		if (i2cread(tv, i2c_haupee, &v)) 
			panic("#V: Cannot deal with hauppauge boards\n");
		if (i2cread(tv, i2c_stbee, &v))
			panic("#V: Cannot deal with STB cards\n");
		if (i2cread(tv, i2c_miroproee, &v)) {
			tv->board = Bt848_miropro;
			t = ((bt848->gpiodata[0] >> 10) - 1) & 7;
		}
		else {
			tv->board = Bt848_miro;
			t = ((bt848->gpiodata[0] >> 10) - 1) & 7;
		}

		if (t >= nelem(tuners))
			t = 4;
		tv->tuner = &tuners[t];
		tv->i2ctuneraddr = 
			i2cread(tv, 0xc1, &v)? 0xc0:
			i2cread(tv, 0xc3, &v)? 0xc2:
			i2cread(tv, 0xc5, &v)? 0xc4:
			i2cread(tv, 0xc7, &v)? 0xc6: -1;

		bt848->capctl = capctl_fullframe;
		bt848->adelay = adelay_ntsc;
		bt848->bdelay = bdelay_ntsc;
		bt848->iform = iform_muxsel0|iform_xtauto|iform_ntsc;
		bt848->vbipacksize = vbipacksize & 0xff;
		bt848->vbipackdel = (vbipacksize >> 8) & 1;

		// setpll(bt848);

		bt848->colorfmt = colorfmt_rgb16;

		hscale = (ntsc_rawpixels * 4096) / ntsc_sqpixels - 4096;
		hdelay = (ntsc_clkx1delay * ntsc_hactive) / ntsc_clkx1hactive;

		bt848->ovtc = bt848->evtc = 0;
		bt848->ehscalehi = bt848->ohscalehi = (hscale >> 8) & 0xff;
		bt848->ehscalelo = bt848->ohscalelo = hscale & 0xff;
		bt848->evscalehi &= ~0x1f;
		bt848->ovscalehi &= ~0x1f;
		bt848->evscalehi |= vscale_interlaced | ((ntsc_vscale >> 8) & 0x1f);
		bt848->ovscalehi |= vscale_interlaced | (ntsc_vscale >> 8) & 0x1f;
		bt848->evscalelo = bt848->ovscalelo = ntsc_vscale & 0xff;
		bt848->ehactivelo = bt848->ohactivelo = ntsc_hactive & 0xff;
		bt848->ehdelaylo = bt848->ohdelaylo = hdelay & 0xff;
		bt848->evactivelo = bt848->ovactivelo = ntsc_vactive & 0xff;
		bt848->evdelaylo = bt848->ovdelaylo = ntsc_vdelay & 0xff;
		bt848->ecrop = bt848->ocrop = 
			((ntsc_hactive >> 8) & 0x03) |
			((hdelay >> 6) & 0x0C) |
	        		((ntsc_vactive >> 4) & 0x30) |
			((ntsc_vdelay >> 2) & 0xC0);	

		bt848->colorctl = colorctl_gamma;
		bt848->capctl = 0;
		bt848->gpiodmactl = gpiodmactl_pltp23_16 |
			gpiodmactl_pltp1_16 | gpiodmactl_pktp_32;
		bt848->gpioreginp = 0;
		bt848->contrastlo = contrast_100percent;
		bt848->bright = 16;
		bt848->adc = (2 << 6) | adc_crush;
		bt848->econtrol = bt848->ocontrol = control_ldec;
		bt848->escloop = bt848->oscloop = 0;
		bt848->intstat = (ulong)-1;
		bt848->intmask = intmask | 
			intstat_vsync | intstat_scerr | intstat_risci | intstat_ocerr | 
			intstat_vpress | intstat_fmtchg | intstat_hlock;

		bt848->gpioouten &= ~0xF;
		bt848->gpioouten |= 0xF;
		bt848->gpiodata[0] &= ~0xF;
		bt848->gpiodata[0] |= 2;	// Enable audio on the MIRO card.

		print("#V: %s (rev %d) (%s/%s) %.8ulX, intl %d\n", 
				tv->variant->name, pci->rid, boards[tv->board],
				tv->tuner->name, pci->mem[0].bar & ~0x0F, pci->intl);

		intrenable(pci->intl, (void (*)(Ureg *, void *))tvinterrupt, 
				tv, pci->tbdf, "tv");	

		tv->dbuf = (uchar *)malloc(ntsc_hactive * ntsc_vactive * sizeof(ushort));
		memset(tv->dbuf, 0, ntsc_hactive * ntsc_vactive * sizeof(ushort));
	}
}

static Chan*
tvattach(char *spec)
{
	return devattach('V', spec);
}

static Walkqid *
tvwalk(Chan *c, Chan *nc, char **name, int nname)
{
	return devwalk(c, nc, name, nname, tvtab, nelem(tvtab), devgen);
}

static int
tvstat(Chan *c, uchar *db, int n)
{
	return devstat(c, db, n, tvtab, nelem(tvtab), devgen);
}

static Chan*
tvopen(Chan *c, int omode)
{
	return devopen(c, omode, tvtab, nelem(tvtab), devgen);
}

static void
tvclose(Chan *)
{}

static long
tvread(Chan *c, void *a, long n, vlong offset)
{
	static char regs[10 * K];
	static int regslen;
	char *e, *p;
	int nbytes;

	USED(offset);

	switch((int)c->qid.path) {
	case Qdir:
		return devdirread(c, a, n, tvtab, nelem(tvtab), devgen);
	case Qdata:
		error(Eio);
	case Qregs:
		if (offset == 0) {
			Bt848 *bt848 = tv->bt848;
			int i;

			e = regs + sizeof(regs);
			p = regs;
			for (i = 0; i < 0x300 >> 2; i++)
				p = seprint(p, e, "%.3X %.8ulX\n", i << 2, ((ulong *)bt848)[i]);
			regslen = p - regs;
		}

		if (offset >= regslen) 
			return 0;
		if (offset + n > regslen)
			n = regslen - offset;

		return readstr(offset, a, n, &regs[offset]);

	case Qdbuf:
		if (offset > ntsc_hactive * ntsc_vactive * sizeof(ushort)) {
			memset(tv->dbuf, 0xaa, 
					ntsc_hactive * ntsc_vactive * sizeof(ushort));
			return 0;
		}

		if (offset + n > ntsc_hactive * ntsc_vactive * sizeof(ushort))
			nbytes = ntsc_hactive * ntsc_vactive * sizeof(ushort) - offset;
		else
			nbytes = n;
		memmove(a, tv->dbuf + offset, nbytes);
		return nbytes;

	default:
		n=0;
		break;
	}
	return n;
}

static long
tvwrite(Chan *c, void *a, long n, vlong)
{
	Cmdbuf *cb;
	Cmdtab *ct;

	switch((int)c->qid.path) {
	case Qctl:
		cb = parsecmd(a, n);
		if(waserror()){
			free(cb);
			nexterror();
		}
		ct = lookupcmd(cb, tvctlmsg, nelem(tvctlmsg));
		switch (ct->index) {
		case CMvstart:
			vstart(tv, strtoul(cb->f[1], (char **)nil, 0),
					(int)strtoul(cb->f[2], (char **)nil, 0));
			break;
		case CMvstop:
			vstop(tv);
			break;
		case CMchannel:
			frequency(tv, (int)strtol(cb->f[1], (char **)nil, 0), 
				(int)strtol(cb->f[2], (char **)nil, 0));
			break;
		}
		poperror();
		break;

	default:
		error(Eio);
	}
	return n;
}

Dev tvdevtab = {
	'V',
	"tv",

	devreset,
	tvinit,
	devshutdown,
	tvattach,
	tvwalk,
	tvstat,
	tvopen,
	devcreate,
	tvclose,
	tvread,
	devbread,
	tvwrite,
	devbwrite,
	devremove,
	devwstat,
};

static void
tvinterrupt(Ureg *, Tv *tv)
{
	Bt848 *bt848 = tv->bt848;
	ulong status;

	while ((status = bt848->intstat & bt848->intmask) != 0) {
		bt848->intstat = status;

		if ((status & intstat_fmtchg) == intstat_fmtchg) {
			iprint("int: fmtchg\n");
			status &= ~intstat_fmtchg;
		}

		if ((status & intstat_vpress) == intstat_vpress) {
			iprint("int: vpress\n");
			status &= ~intstat_vpress;
		}
		
		if ((status & intstat_vsync) == intstat_vsync)
			status &= ~intstat_vsync;

		if ((status & intstat_scerr) == intstat_scerr) {
			iprint("int: scerr\n");
			bt848->gpiodmactl &= 
				~(gpiodmactl_riscenable|gpiodmactl_fifoenable);
			bt848->gpiodmactl |= gpiodmactl_fifoenable;
			bt848->gpiodmactl |= gpiodmactl_riscenable;
			status &= ~intstat_scerr;
		}

		if ((status & intstat_risci) == intstat_risci) {
			iprint("int: risci\n");
			status &= ~intstat_risci;
		}
			
		if ((status & intstat_ocerr) == intstat_ocerr) {
			iprint("int: ocerr\n");
			status &= ~intstat_ocerr;
		}

		if ((status & intstat_fbus) == intstat_fbus) {
			iprint("int: fbus\n");
			status &= ~intstat_fbus;
		}

		if (status)
			iprint("int: ignored interrupts %.8ulX\n", status);
	}
}

static int
i2cread(Tv *tv, uchar off, uchar *v)
{	
	Bt848 *bt848 = tv->bt848;
	ulong intstat;
	int i;

	bt848->intstat	= intstat_i2cdone;
	bt848->i2c = (off << 24) | tv->i2ccmd;

	intstat = -1;
	for (i = 0; i != 1000; i++) {
		if ((intstat = bt848->intstat) & intstat_i2cdone)
			break;
		microdelay(1000);
	}

	if (i == 1000) {
		print("i2cread: timeout\n");
		return 0;
	}

	if ((intstat & intstat_i2crack) == 0)
		return 0;

	*v = bt848->i2c >> 8;
	return 1;
}

static int
i2cwrite(Tv *tv, uchar off, uchar d1, uchar d2, int both)
{
	Bt848 *bt848 = tv->bt848;
	ulong intstat, data;
	int i;

	bt848->intstat	= intstat_i2cdone;
	data = (off << 24) | (d1 << 16) | tv->i2ccmd;
	if (both)
		data |= (d2 << 8) | i2c_bt848w3b;
	bt848->i2c = data;

	intstat = 0;
	for (i = 0; i != 1000; i++) {
		if ((intstat = bt848->intstat) & intstat_i2cdone)
			break;
		microdelay(1000);
	}

	if (i == 1000) {
		print("i2cread: timeout\n");
		return 0;
	}

	if ((intstat & intstat_i2crack) == 0)
		return 0;

	return 1;
}


static ulong *
riscprogram(ulong paddr, int stride, int bpp)
{
	ulong *p, *pbase;
	int i;

	pbase = p = (ulong *)malloc((ntsc_vactive + 5) * 2 * sizeof(ulong));
	assert(p);

	assert(ntsc_hactive * bpp <= 0x7FF);

	*p++ = riscsync | riscsync_resync | riscsync_vre;
	*p++ = 0;

	*p++ = riscsync | riscsync_fm1;
	*p++ = 0;

	for (i = 0; i != ntsc_vactive / 2; i++) {
		*p++ = riscwrite | ntsc_hactive * bpp | riscwrite_sol | riscwrite_eol;
		*p++ = paddr + i * 2 * stride * bpp;
	}

	*p++ = riscsync | riscsync_resync | riscsync_vro;
	*p++ = 0;

	*p++ = riscsync | riscsync_fm1;
	*p++ = 0;

	for (i = 0; i != ntsc_vactive / 2; i++) {
		*p++ = riscwrite | ntsc_hactive * bpp | riscwrite_sol | riscwrite_eol;
		*p++ = paddr + (i * 2 + 1) * stride * bpp;
	}

	*p++ = riscjmp;
	*p++ = PADDR(pbase);
	USED(p);

	return pbase;
}

static void
vstart(Tv *tv, ulong paddr, int stride)
{
	Bt848 *bt848 = tv->bt848;
	ulong *program, cfmt;
	int bpp;

	cfmt = bt848->colorfmt;
	bpp = -1;
	switch (cfmt) {
	case colorfmt_rgb16:
		bpp = 2;
		break;
	default:
		panic("render: Unsupport color format\n");
	}

	program = riscprogram(paddr, stride, bpp);
//	program = riscprogram(PADDR(tv->dbuf), ntsc_hactive * bpp, bpp);

	ilock(tv);
	if (waserror()) {
		iunlock(tv);
		free(program);
		nexterror();
	}

	if (tv->program)
		error(Einuse);

	if (cfmt != bt848->colorfmt)
		error(Eio);

	tv->program = program;
	bt848->riscstrtadd = PADDR(tv->program);
	bt848->capctl |= capctl_captureodd|capctl_captureeven;
	bt848->gpiodmactl |= gpiodmactl_fifoenable;
	bt848->gpiodmactl |= gpiodmactl_riscenable;
	
	poperror();
	iunlock(tv);
}

static void
vstop(Tv *tv)
{
	Bt848 *bt848 = tv->bt848;

	ilock(tv);
	if (waserror()) {

		if (tv->program)
			free(tv->program);
		iunlock(tv);
		nexterror();
	}

	if (tv->program) {
		bt848->gpiodmactl &= ~gpiodmactl_riscenable;
		bt848->gpiodmactl &= ~gpiodmactl_fifoenable;
		bt848->capctl &= ~(capctl_captureodd|capctl_captureeven);

		free(tv->program);
		tv->program = nil;
	}
	
	poperror();
	iunlock(tv);
}

static long
hrcfreq[] = {	/* HRC CATV frequencies */
	    0,  7200,  5400,  6000,  6600,  7800,  8400, 17400,
	18000, 18600, 19200, 19800, 20400, 21000, 12000, 12600,
	13200, 13800, 14400, 15000, 15600, 16200, 16800, 21600,
	22200, 22800, 23400, 24000, 24600, 25200, 25800, 26400,
	27000, 27600, 28200, 28800, 29400, 30000, 30600, 31200,
	31800, 32400, 33000, 33600, 34200, 34800, 35400, 36000,
	36600, 37200, 37800, 38400, 39000, 39600, 40200, 40800,
	41400, 42000, 42600, 43200, 43800, 44400, 45000, 45600,
	46200, 46800, 47400, 48000, 48600, 49200, 49800, 50400,
	51000, 51600, 52200, 52800, 53400, 54000, 54600, 55200,
	55800, 56400, 57000, 57600, 58200, 58800, 59400, 60000,
	60600, 61200, 61800, 62400, 63000, 63600, 64200,  9000,
	 9600, 10200, 10800, 11400, 64800, 65400, 66000, 66600,
	67200, 67800, 68400, 69000, 69600, 70200, 70800, 71400,
	72000, 72600, 73200, 73800, 74400, 75000, 75600, 76200,
	76800, 77400, 78000, 78600, 79200, 79800,
};

static void
frequency(Tv *tv, int channel, int finetune)
{
	Tuner *tuner = tv->tuner;
	long freq;
	ushort div;
	uchar cfg;

	if (channel < 0 || channel > nelem(hrcfreq))
		error(Ebadarg);

	freq = (hrcfreq[channel] * Freqmultiplier) / 100;

	if (freq < tuner->freq_vhfh)
		cfg = tuner->VHF_L;
	else if (freq < tuner->freq_uhf)
		cfg =  tuner->VHF_H;
	else
		cfg = tuner->UHF;

	div = (freq + tuner->offs + finetune) & 0x7fff;
	
	if (!i2cwrite(tv, 0xc0, (div >> 8) & 0x7f, div, 1))
		error(Eio);
	
	if (!i2cwrite(tv, 0xc0, tuner->cfg, cfg, 1))
		error(Eio);
}


A pc/vgamga4xx.c => pc/vgamga4xx.c +559 -0
@@ 0,0 1,559 @@

/*
 * Matrox G200, G400 and G450.
 * see /sys/src/cmd/aux/vga/mga4xx.c
 */

#include 	"u.h"
#include 	"../port/lib.h"
#include 	"mem.h"
#include 	"dat.h"
#include 	"fns.h"
#include 	"io.h"
#include 	"../port/error.h"

#define	Image	IMAGE
#include 	<draw.h>
#include 	<memdraw.h>
#include 	<cursor.h>
#include	"screen.h"

enum {
	MATROX			= 0x102B,
	MGA4xx			= 0x0525,
	MGA200			= 0x0521,

	Kilo				= 1024,
	Meg				= 1024*1024,

	FCOL			= 0x1c24,
	FXRIGHT			= 0x1cac,	
	FXLEFT			= 0x1ca8,
	YDST			= 0x1c90,
	YLEN			= 0x1c5c,
 	DWGCTL			= 0x1c00,
 		DWG_TRAP		= 0x04,
 		DWG_BITBLT		= 0x08,
 		DWG_ILOAD		= 0x09,
 		DWG_LINEAR		= 0x0080,
 		DWG_SOLID		= 0x0800,
 		DWG_ARZERO		= 0x1000,
 		DWG_SGNZERO	= 0x2000,
 		DWG_SHIFTZERO	= 0x4000,
		DWG_REPLACE		= 0x000C0000,
 		DWG_REPLACE2	= (DWG_REPLACE | 0x40),
 		DWG_XOR		= 0x00060010,
 		DWG_BFCOL		= 0x04000000,
 		DWG_BMONOWF	= 0x08000000,
		DWG_TRANSC		= 0x40000000,
 	SRCORG			= 0x2cb4,
	PITCH			= 0x1c8c,
	DSTORG			= 0x2cb8,
	PLNWRT			= 0x1c1c,
	ZORG			= 0x1c0c,
	MACCESS			= 0x1c04,
	STATUS			= 0x1e14,
	FXBNDRY			= 0x1C84,
 	CXBNDRY			= 0x1C80,
	YTOP			= 0x1C98,
	YBOT			= 0x1C9C,
	YDSTLEN			= 0x1C88,
	AR0				= 0x1C60,
	AR1				= 0x1C64,
	AR2				= 0x1C68,
	AR3				= 0x1C6C,
	AR4				= 0x1C70,
	AR5				= 0x1C74,
	SGN				= 0x1C58,
		SGN_SCANLEFT = 		1,
		SGN_SCANRIGHT = 		0,
		SGN_SDY_POSITIVE = 	0,
		SGN_SDY_NEGATIVE = 	4,

	GO				= 0x0100,
 	FIFOSTATUS		= 0x1E10,
	CACHEFLUSH		= 0x1FFF,

	CRTCEXTIDX		= 0x1FDE,		/* CRTC Extension Index */
	CRTCEXTDATA		= 0x1FDF,		/* CRTC Extension Data */

	FILL_OPERAND		= 0x800c7804,
};

static Pcidev*
mgapcimatch(void)
{
	Pcidev*	p;
	
	p = pcimatch(nil, MATROX, MGA4xx);
	if (p == nil)
		p = pcimatch(nil, MATROX, MGA200);
	return p;
}

static ulong
mga4xxlinear(VGAscr* scr, int* size, int* align)
{
	ulong 	aperture, oaperture;
	int 		oapsize, wasupamem;
	Pcidev *	p;

	oaperture = scr->aperture;
	oapsize = scr->apsize;
	wasupamem = scr->isupamem;

	if(p = mgapcimatch()){
		aperture = p->mem[0].bar & ~0x0F;
		if(p->did == MGA4xx)
			*size = 32*Meg;
		else
			*size = 8*Meg;
	}
	else
		aperture = 0;

	if(wasupamem) {
		if(oaperture == aperture)
			return oaperture;
		upafree(oaperture, oapsize);
	}
	scr->isupamem = 0;

	aperture = upamalloc(aperture, *size, *align);
	if(aperture == 0){
		if(wasupamem && upamalloc(oaperture, oapsize, 0)) {
			aperture = oaperture;
			scr->isupamem = 1;
		}
		else
			scr->isupamem = 0;
	}
	else
		scr->isupamem = 1;

	return aperture;
}

static void
mgawrite8(VGAscr* scr, int index, uchar val)
{
	((uchar*)scr->io)[index] = val;
}

static uchar
mgaread8(VGAscr* scr, int index)
{
	return ((uchar*)scr->io)[index];
}

static uchar
crtcextset(VGAscr* scr, int index, uchar set, uchar clr)
{
	uchar	tmp;

	mgawrite8(scr, CRTCEXTIDX, index);
	tmp = mgaread8(scr, CRTCEXTDATA);
	mgawrite8(scr, CRTCEXTIDX, index);
	mgawrite8(scr, CRTCEXTDATA, (tmp & ~clr) | set);

	return tmp;
}

static void
mga4xxenable(VGAscr* scr)
{
	Pcidev *	pci;
	int 		size, align;
	ulong 	aperture;
	int 		i, n, k;
	uchar *	p;
	uchar	x[16];
	uchar	crtcext3;

	/*
	 * Only once, can't be disabled for now.
	 * scr->io holds the virtual address of
	 * the MMIO registers.
	 */
	if(scr->io)
		return;

	pci = mgapcimatch();
	if(pci == nil)
		return;

	scr->io = upamalloc(pci->mem[1].bar & ~0x0F, 16*1024, 0);
	if(scr->io == 0)
		return;

	addvgaseg("mga4xxmmio", scr->io, pci->mem[1].size);

	scr->io = (ulong)KADDR(scr->io);

	/* need to map frame buffer here too, so vga can find memory size */
	size = 8*Meg;
	align = 0;
	aperture = mga4xxlinear(scr, &size, &align);
	if(aperture) {
		scr->aperture = aperture;
		addvgaseg("mga4xxscreen", aperture, size);

		/* Find out how much memory is here, some multiple of 2 Meg */

		/* First Set MGA Mode ... */
		crtcext3 = crtcextset(scr, 3, 0x80, 0x00);

		p = (uchar*)aperture;
		n = (size / Meg) / 2;
		for (i = 0; i < n; i++) {
			k = (2*i+1)*Meg;
			p[k] = 0;
			p[k] = i+1;
			*((uchar*)(scr->io + CACHEFLUSH)) = 0;
			x[i] = p[k];
 		}
		for(i = 1; i < n; i++)
			if(x[i] != i+1)
				break;
  		scr->apsize = 2*i*Meg;

		crtcextset(scr, 3, crtcext3, 0xff);
	}
}

enum {
	Index		= 0x00,		/* Index */
	Data			= 0x0A,		/* Data */

	Cxlsb		= 0x0C,		/* Cursor X LSB */
	Cxmsb		= 0x0D,		/* Cursor X MSB */
	Cylsb		= 0x0E,		/* Cursor Y LSB */
	Cymsb		= 0x0F,		/* Cursor Y MSB */

	Icuradrl		= 0x04,		/* Cursor Base Address Low */	
	Icuradrh		= 0x05,		/* Cursor Base Address High */
	Icctl			= 0x06,		/* Indirect Cursor Control */
};

static void
dac4xxdisable(VGAscr* scr)
{
	uchar * 	dac4xx;
	
	if(scr->io == 0)
		return;

	dac4xx = KADDR(scr->io+0x3C00);
	
	*(dac4xx+Index) = Icctl;
	*(dac4xx+Data) = 0x00;
}

static void
dac4xxload(VGAscr* scr, Cursor* curs)
{
	int 		y;
	uchar *	p;
	uchar * 	dac4xx;

	if(scr->io == 0)
		return;

	dac4xx = KADDR(scr->io+0x3C00);
	
	dac4xxdisable(scr);

	p = KADDR(scr->storage);
	for(y = 0; y < 64; y++){
		*p++ = 0; *p++ = 0; *p++ = 0;
		*p++ = 0; *p++ = 0; *p++ = 0;
		if(y <16){
			*p++ = curs->set[1+y*2]|curs->clr[1+2*y];
			*p++ = curs->set[y*2]|curs->clr[2*y];
		} else{
			*p++ = 0; *p++ = 0;
		}

		*p++ = 0; *p++ = 0; *p++ = 0;
		*p++ = 0; *p++ = 0; *p++ = 0;
		if(y <16){
			*p++ = curs->set[1+y*2];
			*p++ = curs->set[y*2];
		} else{
			*p++ = 0; *p++ = 0;
		}
	}
	scr->offset.x = 64 + curs->offset.x;
	scr->offset.y = 64 + curs->offset.y;

	*(dac4xx+Index) = Icctl;
	*(dac4xx+Data) = 0x03;
}

static int
dac4xxmove(VGAscr* scr, Point p)
{
	int 		x, y;
	uchar *	dac4xx;

	if(scr->io == 0)
		return 1;

	dac4xx = KADDR(scr->io + 0x3C00);

	x = p.x + scr->offset.x;
	y = p.y + scr->offset.y;

	*(dac4xx+Cxlsb) = x & 0xFF;
	*(dac4xx+Cxmsb) = (x>>8) & 0x0F;

	*(dac4xx+Cylsb) = y & 0xFF;
	*(dac4xx+Cymsb) = (y>>8) & 0x0F;

	return 0;
}

static void
dac4xxenable(VGAscr* scr)
{
	uchar *	dac4xx;
	ulong	storage;
	
	if(scr->io == 0)
		return;
	dac4xx = KADDR(scr->io+0x3C00);

	dac4xxdisable(scr);

	storage = (scr->apsize - 4096) & ~0x3ff;

	*(dac4xx+Index) = Icuradrl;
	*(dac4xx+Data) = 0xff & (storage >> 10);
	*(dac4xx+Index) = Icuradrh;
	*(dac4xx+Data) = 0xff & (storage >> 18);		

	scr->storage = (ulong) KADDR((ulong)scr->aperture + (ulong)storage);

	/* Show X11-Like Cursor */
	*(dac4xx+Index) = Icctl;
	*(dac4xx+Data) = 0x03;

	/* Cursor Color 0 : White */
	*(dac4xx+Index) = 0x08;
	*(dac4xx+Data)  = 0xff;
	*(dac4xx+Index) = 0x09;
	*(dac4xx+Data)  = 0xff;
	*(dac4xx+Index) = 0x0a;
	*(dac4xx+Data)  = 0xff;

	/* Cursor Color 1 : Black */
	*(dac4xx+Index) = 0x0c;
	*(dac4xx+Data)  = 0x00;
	*(dac4xx+Index) = 0x0d;
	*(dac4xx+Data)  = 0x00;
	*(dac4xx+Index) = 0x0e;
	*(dac4xx+Data)  = 0x00;

	/* Cursor Color 2 : Red */
	*(dac4xx+Index) = 0x10;
	*(dac4xx+Data)  = 0xff;
	*(dac4xx+Index) = 0x11;
	*(dac4xx+Data)  = 0x00;
	*(dac4xx+Index) = 0x12;
	*(dac4xx+Data)  = 0x00;

	/*
	 * Load, locate and enable the
	 * 64x64 cursor in X11 mode.
	 */
	dac4xxload(scr, &arrow);
	dac4xxmove(scr, ZP);
}

static void
mga4xxblank(VGAscr* scr, int blank)
{
	char * 	cp;
	uchar * 	mga;
	uchar 	seq1, crtcext1;
	
	/* blank = 0 -> turn screen on */
	/* blank = 1 -> turn screen off */

	if(scr->io == 0)
		return;
	mga = KADDR(scr->io);	

	if (blank == 0) {
		seq1 = 0x00;
		crtcext1 = 0x00;
	} else {
		seq1 = 0x20;
		crtcext1 = 0x10;			/* Default value ... : standby */
		cp = getconf("*dpms");
		if (cp) {
			if (cistrcmp(cp, "standby") == 0) {
				crtcext1 = 0x10;
			} else if (cistrcmp(cp, "suspend") == 0) {
				crtcext1 = 0x20;
			} else if (cistrcmp(cp, "off") == 0) {
				crtcext1 = 0x30;
			}
		}
	}

	*(mga + 0x1fc4) = 1;
	seq1 |= *(mga + 0x1fc5) & ~0x20;
	*(mga + 0x1fc5) = seq1;

	*(mga + 0x1fde) = 1;
	crtcext1 |= *(mga + 0x1fdf) & ~0x30;
	*(mga + 0x1fdf) = crtcext1;
}

static void
mgawrite32(uchar * mga, ulong reg, ulong val)
{
	ulong *	l;

	l = (ulong *)(&mga[reg]);
	l[0] = val;
}

static ulong
mgaread32(uchar * mga, ulong reg)
{
	return *((ulong *)(&mga[reg]));
}

static int
mga4xxfill(VGAscr* scr, Rectangle r, ulong color)
{
	uchar * 		mga;
 
	/* Constant Shaded Trapezoids / Rectangle Fills */
	if(scr->io == 0)
		return 0;
	mga = KADDR(scr->io);

	mgawrite32(mga, DWGCTL, 0);
	mgawrite32(mga, FCOL, color);
	mgawrite32(mga, FXRIGHT, r.max.x);
	mgawrite32(mga, FXLEFT, r.min.x);
	mgawrite32(mga, YDST, r.min.y);
	mgawrite32(mga, YLEN, Dy(r));
	mgawrite32(mga, DWGCTL + GO, FILL_OPERAND);

	while (mgaread32(mga, STATUS) & 0x00010000)
		;
	return 1;
}

#define mga_fifo(n)	do {} while ((mgaread32(mga, FIFOSTATUS) & 0xFF) < (n))

static int
mga4xxscroll(VGAscr* scr, Rectangle r_dst, Rectangle r_src)
{
	uchar * 	mga;
	ulong	pitch;
	ulong	w, h, start, end, y;
 
	/* Two-operand Bitblts */
	if(scr->io == 0)
		return 0;
	mga = KADDR(scr->io);

	pitch = Dx(scr->gscreen->r);

	mgawrite32(mga, DWGCTL, 0);
	h = Dy(r_src);
	w = Dx(r_src);
	y = r_dst.min.y;

	if ((r_dst.min.y < r_src.min.y) || 
	    ((r_dst.min.y == r_src.min.y) && (r_dst.min.x <= r_src.min.y))) {
		mga_fifo(2);
		mgawrite32(mga, DWGCTL, DWG_BITBLT | DWG_SHIFTZERO | 
						DWG_SGNZERO | DWG_BFCOL | DWG_REPLACE);
		mgawrite32(mga, AR5, pitch);
		w--;
		start = r_src.min.y*pitch+r_src.min.x;
		end = start+w;
	} else {
		mga_fifo(3);
		mgawrite32(mga, DWGCTL, DWG_BITBLT | DWG_SHIFTZERO | 
						DWG_BFCOL | DWG_REPLACE);
		mgawrite32(mga, SGN, 5);
		mgawrite32(mga, AR5, (-pitch) & 0x3ffff);
		w--;
		end = (r_src.min.y+h-1)*pitch+r_src.min.x;
		start = end+w;
		y += h-1;
	}
	mga_fifo(4);
	mgawrite32(mga, AR0, end);
	mgawrite32(mga, AR3, start);
	mgawrite32(mga, FXBNDRY, ((r_dst.min.x+w)<<16) | r_dst.min.x);
	mgawrite32(mga, YDST, y);
	mgawrite32(mga, YLEN + GO, h);

	while (mgaread32(mga, STATUS) & 0x00010000)
		;
	return 1;
}

static void
mga4xxdrawinit(VGAscr* scr)
{
	uchar * 	mga;
 	Pcidev*	p;

	p = pcimatch(nil, MATROX, MGA4xx);
	if (p == nil)
		return ;

	if(scr->io == 0)
		return;
	mga = KADDR(scr->io);

	mgawrite32(mga, SRCORG, 0);
	mgawrite32(mga, DSTORG, 0);
	mgawrite32(mga, ZORG, 0);
	mgawrite32(mga, PLNWRT, ~0);
	mgawrite32(mga, FCOL, 0xffff0000);
	mgawrite32(mga, CXBNDRY, 0xFFFF0000);
	mgawrite32(mga, YTOP, 0);
	mgawrite32(mga, YBOT, 0x01FFFFFF);
	mgawrite32(mga, PITCH, Dx(scr->gscreen->r) & ((1 << 13) - 1));
	switch(scr->gscreen->depth) {
	case 8:
		mgawrite32(mga, MACCESS, 0);
		break;
	case 32:
		mgawrite32(mga, MACCESS, 2);
		break;
	default:
		return;		/* depth not supported ! */
	}
	scr->fill = mga4xxfill;
	scr->scroll = mga4xxscroll;
	scr->blank = mga4xxblank;
}

VGAdev vgamga4xxdev = {
	"mga4xx",

	mga4xxenable,		/* enable */
	0,					/* disable */
	0,					/* page */
	mga4xxlinear,			/* linear */
	mga4xxdrawinit,
};

VGAcur vgamga4xxcur = {
	"mga4xxhwgc",
	dac4xxenable,
	dac4xxdisable,
	dac4xxload,
	dac4xxmove,
};

M port/devbridge.c => port/devbridge.c +2 -2
@@ 437,7 437,7 @@ bridgegen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
			devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
			break;
		default:
			panic("bridgewalk %lux", c->qid.path);
			panic("bridgewalk %llux", c->qid.path);
		}
		return 1;
	}


@@ 451,7 451,7 @@ bridgegen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
			return -1;
		dt = dirtab[TYPE(c->qid)];
		if(dt == nil)
			panic("bridgegen: unknown type: %d", TYPE(c->qid));
			panic("bridgegen: unknown type: %lud", TYPE(c->qid));
		devdir(c, c->qid, dt->name, dt->length, eve, dt->perm, dp);
		return 1;
	case Qtopdir:

M port/devloopback.c => port/devloopback.c +1 -1
@@ 208,7 208,7 @@ loopbackgen(Chan *c, char*, Dirtab*, int, int i, Dir *dp)
			devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
			break;
		default:
			panic("loopbackgen %lux", c->qid.path);
			panic("loopbackgen %llux", c->qid.path);
		}
		return 1;
	}

M port/devsdp.c => port/devsdp.c +2 -2
@@ 723,7 723,7 @@ sdpgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp)
			devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
			break;
		default:
			panic("sdpwalk %lux", c->qid.path);
			panic("sdpwalk %llux", c->qid.path);
		}
		return 1;
	}


@@ 737,7 737,7 @@ sdpgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp)
			return -1;
		dt = dirtab[TYPE(c->qid)];
		if(dt == nil)
			panic("sdpgen: unknown type: %d", TYPE(c->qid));
			panic("sdpgen: unknown type: %lud", TYPE(c->qid));
		devdir(c, c->qid, dt->name, dt->length, eve, dt->perm, dp);
		return 1;
	case Qtopdir: