xref: /plan9-contrib/sys/src/cmd/aux/vga/ct65540.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <bio.h>
47dd7cddfSDavid du Colombier 
5*9a747e4fSDavid du Colombier #include "pci.h"
67dd7cddfSDavid du Colombier #include "vga.h"
77dd7cddfSDavid du Colombier 
87dd7cddfSDavid du Colombier enum
97dd7cddfSDavid du Colombier {
107dd7cddfSDavid du Colombier 	X=	0x3D6,	/* index reg */
117dd7cddfSDavid du Colombier 	D=	0x3D7,	/* data reg */
127dd7cddfSDavid du Colombier };
137dd7cddfSDavid du Colombier 
147dd7cddfSDavid du Colombier static int misc[] = {	0x0,  0x1,  0x2,  0x3,  0x4,  0x5,  0x6,  0xE, 0x28, 0x29,
157dd7cddfSDavid du Colombier 			0x70, 0x72, 0x73, 0x7D, 0x7F, -1};
167dd7cddfSDavid du Colombier static int map[] = {	0x7,  0x8,  0xB,  0xC,  0x10, 0x11, -1};
177dd7cddfSDavid du Colombier static int flags[] = {	0xF,  0x2B, 0x44, 0x45, -1};
187dd7cddfSDavid du Colombier static int compat[] = {	0x14, 0x15, 0x1F, 0x7E, -1};
197dd7cddfSDavid du Colombier static int clock[] = {	0x30, 0x31, 0x32, 0x33, -1};
207dd7cddfSDavid du Colombier static int mm[] = {	0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, -1};
217dd7cddfSDavid du Colombier static int alt[] = {	0x0D, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E,
227dd7cddfSDavid du Colombier 			0x24, 0x25, 0x26, 0x64, 0x65, 0x66, 0x67, -1};
237dd7cddfSDavid du Colombier static int flat[] = {	0x2C, 0x2D, 0x2E, 0x2F, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54,
247dd7cddfSDavid du Colombier 			0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E,
257dd7cddfSDavid du Colombier 			0x5F, 0x60, 0x61, 0x62, 0x63, 0x68, 0x6C, 0x6E, 0x6F, -1};
267dd7cddfSDavid du Colombier 
277dd7cddfSDavid du Colombier typedef struct Group Group;
287dd7cddfSDavid du Colombier struct Group {
297dd7cddfSDavid du Colombier 	char *name;
307dd7cddfSDavid du Colombier 	int *x;
317dd7cddfSDavid du Colombier };
327dd7cddfSDavid du Colombier static Group group[] =
337dd7cddfSDavid du Colombier {
347dd7cddfSDavid du Colombier 	{ "misc", misc, },
357dd7cddfSDavid du Colombier 	{ "map", map, },
367dd7cddfSDavid du Colombier 	{ "compatability", compat, },
377dd7cddfSDavid du Colombier 	{ "clock", clock, },
387dd7cddfSDavid du Colombier 	{ "multimedia", mm, },
397dd7cddfSDavid du Colombier 	{ "alternate", alt, },
407dd7cddfSDavid du Colombier 	{ "flat-panel", flat, },
417dd7cddfSDavid du Colombier 	{ 0 },
427dd7cddfSDavid du Colombier };
437dd7cddfSDavid du Colombier 
447dd7cddfSDavid du Colombier static uchar greg[256];
457dd7cddfSDavid du Colombier 
467dd7cddfSDavid du Colombier static uchar
ctxi(uchar index)477dd7cddfSDavid du Colombier ctxi(uchar index)
487dd7cddfSDavid du Colombier {
497dd7cddfSDavid du Colombier 	outportb(X, index);
507dd7cddfSDavid du Colombier 	return inportb(D);
517dd7cddfSDavid du Colombier }
527dd7cddfSDavid du Colombier 
537dd7cddfSDavid du Colombier static void
ctxo(uchar index,uchar data)547dd7cddfSDavid du Colombier ctxo(uchar index, uchar data)
557dd7cddfSDavid du Colombier {
567dd7cddfSDavid du Colombier 	outportb(X, index);
577dd7cddfSDavid du Colombier 	outportb(D, data);
587dd7cddfSDavid du Colombier }
597dd7cddfSDavid du Colombier 
607dd7cddfSDavid du Colombier /*
617dd7cddfSDavid du Colombier  * ct65540.
627dd7cddfSDavid du Colombier  */
637dd7cddfSDavid du Colombier static void
snarf(Vga *,Ctlr * ctlr)647dd7cddfSDavid du Colombier snarf(Vga*, Ctlr* ctlr)
657dd7cddfSDavid du Colombier {
667dd7cddfSDavid du Colombier 	Group *g;
677dd7cddfSDavid du Colombier 	int *xp;
687dd7cddfSDavid du Colombier 
697dd7cddfSDavid du Colombier 	ctlr->flag |= Fsnarf;
707dd7cddfSDavid du Colombier 
717dd7cddfSDavid du Colombier 	for(g = group; g->name; g++)
727dd7cddfSDavid du Colombier 		for(xp = g->x; *xp >= 0; xp++)
737dd7cddfSDavid du Colombier 			greg[*xp] = ctxi(*xp);
747dd7cddfSDavid du Colombier }
757dd7cddfSDavid du Colombier 
767dd7cddfSDavid du Colombier static void
options(Vga *,Ctlr * ctlr)777dd7cddfSDavid du Colombier options(Vga*, Ctlr* ctlr)
787dd7cddfSDavid du Colombier {
797dd7cddfSDavid du Colombier 	ctlr->flag |= Hlinear|Foptions;
807dd7cddfSDavid du Colombier }
817dd7cddfSDavid du Colombier 
827dd7cddfSDavid du Colombier /*
837dd7cddfSDavid du Colombier  *  brute force and ignorance
847dd7cddfSDavid du Colombier  */
857dd7cddfSDavid du Colombier static int
setclock(Vga * vga)867dd7cddfSDavid du Colombier setclock(Vga* vga)
877dd7cddfSDavid du Colombier {
887dd7cddfSDavid du Colombier 	ulong fvco, t;
897dd7cddfSDavid du Colombier 	ulong m, n;
907dd7cddfSDavid du Colombier 	ulong bestm, bestn, diff, bestdiff, lastdiff;
917dd7cddfSDavid du Colombier 	ulong p;
927dd7cddfSDavid du Colombier 
937dd7cddfSDavid du Colombier 	if(vga->mode->frequency > 220000000)
947dd7cddfSDavid du Colombier 		return -1;
957dd7cddfSDavid du Colombier 
967dd7cddfSDavid du Colombier 	vga->misc &= ~(3<<2);
977dd7cddfSDavid du Colombier 	vga->feature &= ~3;
987dd7cddfSDavid du Colombier 	greg[0x33] &= ~0x20;		/* set VCLK not MCLK */
997dd7cddfSDavid du Colombier 	greg[0x33] &= ~0x80;		/* clk0 & clk1 are 25.175 & 28.322 MHz */
1007dd7cddfSDavid du Colombier 
1017dd7cddfSDavid du Colombier 	vga->misc |= (2<<2);
1027dd7cddfSDavid du Colombier 	vga->feature |= 2;
1037dd7cddfSDavid du Colombier 
1047dd7cddfSDavid du Colombier 	fvco = vga->mode->frequency;
1057dd7cddfSDavid du Colombier 	if(fvco == 0)
1067dd7cddfSDavid du Colombier 		return -1;
1077dd7cddfSDavid du Colombier 	p = 0;
1087dd7cddfSDavid du Colombier 	while(fvco < 100000000){
1097dd7cddfSDavid du Colombier 		fvco *= 2;
1107dd7cddfSDavid du Colombier 		p++;
1117dd7cddfSDavid du Colombier 	}
1127dd7cddfSDavid du Colombier 
1137dd7cddfSDavid du Colombier 	m = (1<<31)/(4*RefFreq);
1147dd7cddfSDavid du Colombier 	if(m > 127)
1157dd7cddfSDavid du Colombier 		m = 127;
1167dd7cddfSDavid du Colombier 	bestdiff = 1<<31;
1177dd7cddfSDavid du Colombier 	bestm = 3;
1187dd7cddfSDavid du Colombier 	bestn = 3;
1197dd7cddfSDavid du Colombier 	for(; m > 2; m--){
1207dd7cddfSDavid du Colombier 		lastdiff = 1<<31;
1217dd7cddfSDavid du Colombier 		for(n = 3; n < 128; n++){
1227dd7cddfSDavid du Colombier 			t = (RefFreq*4*m)/n;
1237dd7cddfSDavid du Colombier 			diff = abs(fvco-t);
1247dd7cddfSDavid du Colombier 			if(diff < bestdiff){
1257dd7cddfSDavid du Colombier 				bestdiff = diff;
1267dd7cddfSDavid du Colombier 				bestm = m;
1277dd7cddfSDavid du Colombier 				bestn = n;
1287dd7cddfSDavid du Colombier 			} else {
1297dd7cddfSDavid du Colombier 				if(diff > lastdiff)
1307dd7cddfSDavid du Colombier 					break;
1317dd7cddfSDavid du Colombier 			}
1327dd7cddfSDavid du Colombier 			lastdiff = diff;
1337dd7cddfSDavid du Colombier 		}
1347dd7cddfSDavid du Colombier 	}
1357dd7cddfSDavid du Colombier 
1367dd7cddfSDavid du Colombier 	greg[0x31] = bestm - 2;
1377dd7cddfSDavid du Colombier 	greg[0x32] = bestn - 2;
1387dd7cddfSDavid du Colombier 	greg[0x30] = (p<<1) | 1;
1397dd7cddfSDavid du Colombier 	return 0;
1407dd7cddfSDavid du Colombier }
1417dd7cddfSDavid du Colombier 
1427dd7cddfSDavid du Colombier static void
init(Vga * vga,Ctlr * ctlr)1437dd7cddfSDavid du Colombier init(Vga* vga, Ctlr* ctlr)
1447dd7cddfSDavid du Colombier {
1457dd7cddfSDavid du Colombier 	int x;
1467dd7cddfSDavid du Colombier 
1477dd7cddfSDavid du Colombier 	greg[0x15] = 0;			/* allow writes to all registers */
1487dd7cddfSDavid du Colombier 
1497dd7cddfSDavid du Colombier 	if(vga->mode->z > 8)
1507dd7cddfSDavid du Colombier 		error("depth %d not supported\n", vga->mode->z);
1517dd7cddfSDavid du Colombier 
1527dd7cddfSDavid du Colombier 	if(vga->mode->z == 8){
1537dd7cddfSDavid du Colombier 		if(vga->linear && (ctlr->flag & Hlinear))
1547dd7cddfSDavid du Colombier 			ctlr->flag |= Ulinear;
1557dd7cddfSDavid du Colombier 		vga->vmz = 1024*1024;
1567dd7cddfSDavid du Colombier 		vga->vmb = 1024*1024;
1577dd7cddfSDavid du Colombier 
1587dd7cddfSDavid du Colombier 		/* linear mapping - extension regs*/
1597dd7cddfSDavid du Colombier 		greg[0x04] = (1<<2);	/* enable CRTC bits 16 & 17, 32 bit mode */
1607dd7cddfSDavid du Colombier 		greg[0x0b] = 0x15;	/* linear addressing, > 256k, sequential addr */
1617dd7cddfSDavid du Colombier 		greg[0x28] = 0x90;	/* non-interlaced, 256 colors */
1627dd7cddfSDavid du Colombier 
1637dd7cddfSDavid du Colombier 		/* normal regs */
1647dd7cddfSDavid du Colombier 		vga->sequencer[0x04] = 0x0A;	/* sequential memory access */
1657dd7cddfSDavid du Colombier 		vga->graphics[0x05] = 0x00;	/* sequential access, shift out 8 bits at */
1667dd7cddfSDavid du Colombier 						/* a time */
1677dd7cddfSDavid du Colombier 		vga->attribute[0x10] &= ~(1<<6); /* 1 dot clock per pixel */
1687dd7cddfSDavid du Colombier 		vga->crt[0x14] = 0x00;
1697dd7cddfSDavid du Colombier 		vga->crt[0x17] = 0xe3;		/* always byte mode */
1707dd7cddfSDavid du Colombier 	} else {
1717dd7cddfSDavid du Colombier 		/* mapped to 0xa0000 - extension regs*/
1727dd7cddfSDavid du Colombier 		greg[0x04] = (1<<2);	/* enable CRTC bits 16 & 17, 32 bit mode */
1737dd7cddfSDavid du Colombier 		greg[0x0b] = 0x01;	/* 0xA0000 - 0xAFFFF, planar addressing */
1747dd7cddfSDavid du Colombier 		greg[0x28] = 0x80;	/* non-interlaced, 16 colors */
1757dd7cddfSDavid du Colombier 	}
1767dd7cddfSDavid du Colombier 
1777dd7cddfSDavid du Colombier 	/* the extension registers have even more overflow bits */
1787dd7cddfSDavid du Colombier 	x = 0;
1797dd7cddfSDavid du Colombier 	if(vga->mode->vt & (1<<10))
1807dd7cddfSDavid du Colombier 		x |= (1<<0);
1817dd7cddfSDavid du Colombier 	if(vga->mode->vrs & (1<<10))
1827dd7cddfSDavid du Colombier 		x |= (1<<2);
1837dd7cddfSDavid du Colombier 	greg[0x16] = x;
1847dd7cddfSDavid du Colombier 	x = 0;
1857dd7cddfSDavid du Colombier 	if(vga->mode->ht & (1<<(8+3)))
1867dd7cddfSDavid du Colombier 		x |= (1<<0);
1877dd7cddfSDavid du Colombier 	if(vga->mode->shb & (1<<(8+3)))
1887dd7cddfSDavid du Colombier 		x |= (1<<4);
1897dd7cddfSDavid du Colombier 	if(vga->mode->ehb & (1<<(6+3)))
1907dd7cddfSDavid du Colombier 		x |= (1<<5);
1917dd7cddfSDavid du Colombier 	greg[0x17] = x;
1927dd7cddfSDavid du Colombier 
1937dd7cddfSDavid du Colombier 	if(vga->mode->y > 480)
1947dd7cddfSDavid du Colombier 		vga->misc &= 0x3F;
1957dd7cddfSDavid du Colombier 
1967dd7cddfSDavid du Colombier 	setclock(vga);
1977dd7cddfSDavid du Colombier 
1987dd7cddfSDavid du Colombier 	ctlr->flag |= Finit;
1997dd7cddfSDavid du Colombier }
2007dd7cddfSDavid du Colombier 
2017dd7cddfSDavid du Colombier static void
load(Vga * vga,Ctlr * ctlr)2027dd7cddfSDavid du Colombier load(Vga* vga, Ctlr* ctlr)
2037dd7cddfSDavid du Colombier {
2047dd7cddfSDavid du Colombier 	Group *g;
2057dd7cddfSDavid du Colombier 	int *xp;
2067dd7cddfSDavid du Colombier 
2077dd7cddfSDavid du Colombier 	/* must be first */
2087dd7cddfSDavid du Colombier 	ctxo(0x15, greg[0x15]);	/* write protect */
2097dd7cddfSDavid du Colombier 	ctxo(0x33, greg[0x33]);	/* select clock */
2107dd7cddfSDavid du Colombier 
2117dd7cddfSDavid du Colombier 	if(ctlr->flag & Ulinear){
2127dd7cddfSDavid du Colombier 		greg[0x8] = vga->vmb>>20;
2137dd7cddfSDavid du Colombier 		ctxo(0x08, greg[0x08]);
2147dd7cddfSDavid du Colombier 	}
2157dd7cddfSDavid du Colombier 
2167dd7cddfSDavid du Colombier 	/* only write what changed */
2177dd7cddfSDavid du Colombier 	for(g = group; g->name; g++)
2187dd7cddfSDavid du Colombier 		for(xp = g->x; *xp >= 0; xp++)
2197dd7cddfSDavid du Colombier 			ctxo(*xp, greg[*xp]);
2207dd7cddfSDavid du Colombier 
2217dd7cddfSDavid du Colombier 	ctlr->flag |= Fload;
2227dd7cddfSDavid du Colombier }
2237dd7cddfSDavid du Colombier 
2247dd7cddfSDavid du Colombier static void
dump(Vga *,Ctlr * ctlr)2257dd7cddfSDavid du Colombier dump(Vga*, Ctlr* ctlr)
2267dd7cddfSDavid du Colombier {
2277dd7cddfSDavid du Colombier 	Group *g;
2287dd7cddfSDavid du Colombier 	int *xp;
2297dd7cddfSDavid du Colombier 	char *name;
2307dd7cddfSDavid du Colombier 	int lastx;
2317dd7cddfSDavid du Colombier 	char item[32];
2327dd7cddfSDavid du Colombier 
2337dd7cddfSDavid du Colombier 	name = ctlr->name;
2347dd7cddfSDavid du Colombier 
2357dd7cddfSDavid du Colombier 	for(g = group; g->name; g++){
2367dd7cddfSDavid du Colombier 		lastx = -2;
2377dd7cddfSDavid du Colombier 		for(xp = g->x; *xp >= 0; xp++){
2387dd7cddfSDavid du Colombier 			if(*xp != lastx+1){
2397dd7cddfSDavid du Colombier 				sprint(item, "%s %2.2ux:", g->name, *xp);
2407dd7cddfSDavid du Colombier 				printitem(name, item);
2417dd7cddfSDavid du Colombier 			}
2427dd7cddfSDavid du Colombier 			lastx = *xp;
2437dd7cddfSDavid du Colombier 			printreg(greg[*xp]);
2447dd7cddfSDavid du Colombier 		}
2457dd7cddfSDavid du Colombier 	}
2467dd7cddfSDavid du Colombier }
2477dd7cddfSDavid du Colombier 
2487dd7cddfSDavid du Colombier Ctlr ct65540 = {
2497dd7cddfSDavid du Colombier 	"ct65540",			/* name */
2507dd7cddfSDavid du Colombier 	snarf,				/* snarf */
2517dd7cddfSDavid du Colombier 	options,			/* options */
2527dd7cddfSDavid du Colombier 	init,				/* init */
2537dd7cddfSDavid du Colombier 	load,				/* load */
2547dd7cddfSDavid du Colombier 	dump,				/* dump */
2557dd7cddfSDavid du Colombier };
2567dd7cddfSDavid du Colombier 
2577dd7cddfSDavid du Colombier Ctlr ct65545 = {
2587dd7cddfSDavid du Colombier 	"ct65545",			/* name */
2597dd7cddfSDavid du Colombier 	snarf,				/* snarf */
2607dd7cddfSDavid du Colombier 	options,			/* options */
2617dd7cddfSDavid du Colombier 	init,				/* init */
2627dd7cddfSDavid du Colombier 	load,				/* load */
2637dd7cddfSDavid du Colombier 	dump,				/* dump */
2647dd7cddfSDavid du Colombier };
2657dd7cddfSDavid du Colombier 
2667dd7cddfSDavid du Colombier Ctlr ct65545hwgc = {
2677dd7cddfSDavid du Colombier 	"ct65545hwgc",			/* name */
2687dd7cddfSDavid du Colombier 	0,				/* snarf */
2697dd7cddfSDavid du Colombier 	0,				/* options */
2707dd7cddfSDavid du Colombier 	0,				/* init */
2717dd7cddfSDavid du Colombier 	0,				/* load */
2727dd7cddfSDavid du Colombier 	0,				/* dump */
2737dd7cddfSDavid du Colombier };
274