xref: /plan9/sys/src/cmd/aux/vga/s3clock.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
1219b2ee8SDavid du Colombier #include <u.h>
2219b2ee8SDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <bio.h>
4219b2ee8SDavid du Colombier 
5*9a747e4fSDavid du Colombier #include "pci.h"
6219b2ee8SDavid du Colombier #include "vga.h"
7219b2ee8SDavid du Colombier 
87dd7cddfSDavid du Colombier /*
97dd7cddfSDavid du Colombier  * Clocks which require fiddling with the S3 registers
107dd7cddfSDavid du Colombier  * in order to be loaded.
117dd7cddfSDavid du Colombier  */
12219b2ee8SDavid du Colombier static void
setcrt42(Vga * vga,Ctlr * ctlr,uchar index)13219b2ee8SDavid du Colombier setcrt42(Vga* vga, Ctlr* ctlr, uchar index)
14219b2ee8SDavid du Colombier {
157dd7cddfSDavid du Colombier 	trace("%s->clock->setcrt42\n", ctlr->name);
16219b2ee8SDavid du Colombier 
17219b2ee8SDavid du Colombier 	vgao(MiscW, vga->misc & ~0x0C);
18219b2ee8SDavid du Colombier 	outportb(Crtx+1, 0x00);
19219b2ee8SDavid du Colombier 
20219b2ee8SDavid du Colombier 	vga->crt[0x42] = (vga->crt[0x42] & 0xF0)|index;
21219b2ee8SDavid du Colombier 	vgao(MiscW, vga->misc);
22219b2ee8SDavid du Colombier 	vgaxo(Crtx, 0x42, vga->crt[0x42]);
23219b2ee8SDavid du Colombier }
24219b2ee8SDavid du Colombier 
25219b2ee8SDavid du Colombier static void
icd2061aload(Vga * vga,Ctlr * ctlr)26219b2ee8SDavid du Colombier icd2061aload(Vga* vga, Ctlr* ctlr)
27219b2ee8SDavid du Colombier {
28219b2ee8SDavid du Colombier 	ulong sdata;
29219b2ee8SDavid du Colombier 	int i;
30219b2ee8SDavid du Colombier 	uchar crt42;
31219b2ee8SDavid du Colombier 
327dd7cddfSDavid du Colombier 	trace("%s->clock->icd2061aload\n", ctlr->name);
33219b2ee8SDavid du Colombier 	/*
34219b2ee8SDavid du Colombier 	 * The serial word to be loaded into the icd2061a is
35219b2ee8SDavid du Colombier 	 *	(2<<21)|(vga->i<<17)|((vga->n)<<10)|(vga->p<<7)|vga->d
36219b2ee8SDavid du Colombier 	 * Always select ICD2061A REG2.
37219b2ee8SDavid du Colombier 	 */
387dd7cddfSDavid du Colombier 	sdata = (2<<21)|(vga->i[0]<<17)|((vga->n[0])<<10)|(vga->p[0]<<7)|vga->d[0];
39219b2ee8SDavid du Colombier 
40219b2ee8SDavid du Colombier 	/*
41219b2ee8SDavid du Colombier 	 * The display should be already off to enable  us to clock the
42219b2ee8SDavid du Colombier 	 * serial data word into either MiscW or Crt42.
43219b2ee8SDavid du Colombier 	 *
44219b2ee8SDavid du Colombier 	 * Set the Misc register to make clock-select-out the contents of
45219b2ee8SDavid du Colombier 	 * register Crt42. Must turn the sequencer off when changing bits
46219b2ee8SDavid du Colombier 	 * <3:2> of Misc. Also, must set Crt42 to 0 before setting <3:2>
47219b2ee8SDavid du Colombier 	 * of Misc due to a hardware glitch.
48219b2ee8SDavid du Colombier 	 */
49219b2ee8SDavid du Colombier 	vgao(MiscW, vga->misc & ~0x0C);
50219b2ee8SDavid du Colombier 	crt42 = vgaxi(Crtx, 0x42) & 0xF0;
51219b2ee8SDavid du Colombier 	outportb(Crtx+1, 0x00);
52219b2ee8SDavid du Colombier 
53219b2ee8SDavid du Colombier 	vgaxo(Seqx, 0x00, 0x00);
54219b2ee8SDavid du Colombier 	vgao(MiscW, vga->misc|0x0C);
55219b2ee8SDavid du Colombier 	vgaxo(Seqx, 0x00, 0x03);
56219b2ee8SDavid du Colombier 
57219b2ee8SDavid du Colombier 	/*
58219b2ee8SDavid du Colombier 	 * Unlock the ICD2061A. The unlock sequence is at least 5 low-to-high
59219b2ee8SDavid du Colombier 	 * transitions of CLK with DATA high, followed by a single low-to-high
60219b2ee8SDavid du Colombier 	 * transition of CLK with DATA low.
61219b2ee8SDavid du Colombier 	 * Using Crt42, CLK is bit0, DATA is bit 1. If we used MiscW, they'd
62219b2ee8SDavid du Colombier 	 * be bits 2 and 3 respectively.
63219b2ee8SDavid du Colombier 	 */
64219b2ee8SDavid du Colombier 	outportb(Crtx+1, crt42|0x00);			/* -DATA|-CLK */
65219b2ee8SDavid du Colombier 	outportb(Crtx+1, crt42|0x02);			/* +DATA|-CLK */
66219b2ee8SDavid du Colombier 	for(i = 0; i < 5; i++){
67219b2ee8SDavid du Colombier 		outportb(Crtx+1, crt42|0x03);		/* +DATA|+CLK */
68219b2ee8SDavid du Colombier 		outportb(Crtx+1, crt42|0x02);		/* +DATA|-CLK */
69219b2ee8SDavid du Colombier 	}
70219b2ee8SDavid du Colombier 	outportb(Crtx+1, crt42|0x00);			/* -DATA|-CLK */
71219b2ee8SDavid du Colombier 	outportb(Crtx+1, crt42|0x01);			/* -DATA|+CLK */
72219b2ee8SDavid du Colombier 
73219b2ee8SDavid du Colombier 	/*
74219b2ee8SDavid du Colombier 	 * Now write the serial data word, framed by a start-bit and a stop-bit.
75219b2ee8SDavid du Colombier 	 * The data is written using a modified Manchester encoding such that a
76219b2ee8SDavid du Colombier 	 * data-bit is sampled on the rising edge of CLK and the complement of
77219b2ee8SDavid du Colombier 	 * the data-bit is sampled on the previous falling edge of CLK.
78219b2ee8SDavid du Colombier 	 */
79219b2ee8SDavid du Colombier 	outportb(Crtx+1, crt42|0x00);			/* -DATA|-CLK (start-bit) */
80219b2ee8SDavid du Colombier 	outportb(Crtx+1, crt42|0x01);			/* -DATA|+CLK */
81219b2ee8SDavid du Colombier 
82219b2ee8SDavid du Colombier 	for(i = 0; i < 24; i++){			/* serial data word */
83219b2ee8SDavid du Colombier 		if(sdata & 0x01){
84219b2ee8SDavid du Colombier 			outportb(Crtx+1, crt42|0x01);	/* -DATA|+CLK */
85219b2ee8SDavid du Colombier 			outportb(Crtx+1, crt42|0x00);	/* -DATA|-CLK (falling edge) */
86219b2ee8SDavid du Colombier 			outportb(Crtx+1, crt42|0x02);	/* +DATA|-CLK */
87219b2ee8SDavid du Colombier 			outportb(Crtx+1, crt42|0x03);	/* +DATA|+CLK (rising edge) */
88219b2ee8SDavid du Colombier 		}
89219b2ee8SDavid du Colombier 		else {
90219b2ee8SDavid du Colombier 			outportb(Crtx+1, crt42|0x03);	/* +DATA|+CLK */
91219b2ee8SDavid du Colombier 			outportb(Crtx+1, crt42|0x02);	/* +DATA|-CLK (falling edge) */
92219b2ee8SDavid du Colombier 			outportb(Crtx+1, crt42|0x00);	/* -DATA|-CLK */
93219b2ee8SDavid du Colombier 			outportb(Crtx+1, crt42|0x01);	/* -DATA|+CLK (rising edge) */
94219b2ee8SDavid du Colombier 		}
95219b2ee8SDavid du Colombier 		sdata >>= 1;
96219b2ee8SDavid du Colombier 	}
97219b2ee8SDavid du Colombier 
98219b2ee8SDavid du Colombier 	outportb(Crtx+1, crt42|0x03);			/* +DATA|+CLK (stop-bit) */
99219b2ee8SDavid du Colombier 	outportb(Crtx+1, crt42|0x02);			/* +DATA|-CLK */
100219b2ee8SDavid du Colombier 	outportb(Crtx+1, crt42|0x03);			/* +DATA|+CLK */
101219b2ee8SDavid du Colombier 
102219b2ee8SDavid du Colombier 	/*
103219b2ee8SDavid du Colombier 	 * We always use REG2 in the ICD2061A.
104219b2ee8SDavid du Colombier 	 */
105219b2ee8SDavid du Colombier 	setcrt42(vga, ctlr, 0x02);
106219b2ee8SDavid du Colombier }
107219b2ee8SDavid du Colombier 
108219b2ee8SDavid du Colombier static void
ch9294load(Vga * vga,Ctlr * ctlr)109219b2ee8SDavid du Colombier ch9294load(Vga* vga, Ctlr* ctlr)
110219b2ee8SDavid du Colombier {
1117dd7cddfSDavid du Colombier 	trace("%s->clock->ch9294load\n", ctlr->name);
112219b2ee8SDavid du Colombier 
1137dd7cddfSDavid du Colombier 	setcrt42(vga, ctlr, vga->i[0]);
1147dd7cddfSDavid du Colombier }
1157dd7cddfSDavid du Colombier 
1167dd7cddfSDavid du Colombier static void
tvp3025load(Vga * vga,Ctlr * ctlr)1177dd7cddfSDavid du Colombier tvp3025load(Vga* vga, Ctlr* ctlr)
1187dd7cddfSDavid du Colombier {
1197dd7cddfSDavid du Colombier 	uchar crt5c, x;
1207dd7cddfSDavid du Colombier 
1217dd7cddfSDavid du Colombier 	trace("%s->clock->tvp3025load\n", ctlr->name);
1227dd7cddfSDavid du Colombier 
1237dd7cddfSDavid du Colombier 	/*
1247dd7cddfSDavid du Colombier 	 * Crt5C bit 5 is RS4.
1257dd7cddfSDavid du Colombier 	 * Clear it to select TVP3025 registers for
1267dd7cddfSDavid du Colombier 	 * the calls to tvp302xo().
1277dd7cddfSDavid du Colombier 	 */
1287dd7cddfSDavid du Colombier 	crt5c = vgaxi(Crtx, 0x5C);
1297dd7cddfSDavid du Colombier 	vgaxo(Crtx, 0x5C, crt5c & ~0x20);
1307dd7cddfSDavid du Colombier 
1317dd7cddfSDavid du Colombier 	tvp3020xo(0x2C, 0x00);
1327dd7cddfSDavid du Colombier 	tvp3020xo(0x2D, vga->d[0]);
1337dd7cddfSDavid du Colombier 	tvp3020xo(0x2D, vga->n[0]);
1347dd7cddfSDavid du Colombier 	tvp3020xo(0x2D, 0x08|vga->p[0]);
1357dd7cddfSDavid du Colombier 
1367dd7cddfSDavid du Colombier 	tvp3020xo(0x2F, 0x01);
1377dd7cddfSDavid du Colombier 	tvp3020xo(0x2F, 0x01);
1387dd7cddfSDavid du Colombier 	tvp3020xo(0x2F, vga->p[0]);
1397dd7cddfSDavid du Colombier 	x = 0x54;
1407dd7cddfSDavid du Colombier 	if(vga->ctlr && (vga->ctlr->flag & Uenhanced))
1417dd7cddfSDavid du Colombier 		x = 0xC4;
1427dd7cddfSDavid du Colombier 	tvp3020xo(0x1E, x);
1437dd7cddfSDavid du Colombier 
1447dd7cddfSDavid du Colombier 	vgaxo(Crtx, 0x5C, crt5c);
1457dd7cddfSDavid du Colombier 	vgao(MiscW, vga->misc);
1467dd7cddfSDavid du Colombier 
1477dd7cddfSDavid du Colombier 	ctlr->flag |= Fload;
1487dd7cddfSDavid du Colombier }
1497dd7cddfSDavid du Colombier 
1507dd7cddfSDavid du Colombier static void
tvp3026load(Vga * vga,Ctlr * ctlr)1517dd7cddfSDavid du Colombier tvp3026load(Vga* vga, Ctlr* ctlr)
1527dd7cddfSDavid du Colombier {
1537dd7cddfSDavid du Colombier 	trace("%s->clock->tvp3026load\n", ctlr->name);
1547dd7cddfSDavid du Colombier 
1557dd7cddfSDavid du Colombier 	if((vga->misc & 0x0C) != 0x0C && vga->mode->z == 1){
1567dd7cddfSDavid du Colombier 		tvp3026xo(0x1A, 0x07);
1577dd7cddfSDavid du Colombier 		tvp3026xo(0x18, 0x80);
1587dd7cddfSDavid du Colombier 		tvp3026xo(0x19, 0x98);
1597dd7cddfSDavid du Colombier 		tvp3026xo(0x2C, 0x2A);
1607dd7cddfSDavid du Colombier 		tvp3026xo(0x2F, 0x00);
1617dd7cddfSDavid du Colombier 		tvp3026xo(0x2D, 0x00);
1627dd7cddfSDavid du Colombier 		tvp3026xo(0x39, 0x18);
1637dd7cddfSDavid du Colombier 		setcrt42(vga, ctlr, 0);
1647dd7cddfSDavid du Colombier 	}
1657dd7cddfSDavid du Colombier 	else if(vga->mode->z == 8){
1667dd7cddfSDavid du Colombier 		tvp3026xo(0x1A, 0x05);
1677dd7cddfSDavid du Colombier 		tvp3026xo(0x18, 0x80);
1687dd7cddfSDavid du Colombier 		tvp3026xo(0x19, 0x4C);
1697dd7cddfSDavid du Colombier 		tvp3026xo(0x2C, 0x2A);
1707dd7cddfSDavid du Colombier 		tvp3026xo(0x2F, 0x00);
1717dd7cddfSDavid du Colombier 		tvp3026xo(0x2D, 0x00);
1727dd7cddfSDavid du Colombier 
1737dd7cddfSDavid du Colombier 		tvp3026xo(0x2C, 0x00);
1747dd7cddfSDavid du Colombier 		tvp3026xo(0x2D, 0xC0|vga->n[0]);
1757dd7cddfSDavid du Colombier 		tvp3026xo(0x2D, vga->m[0] & 0x3F);
1767dd7cddfSDavid du Colombier 		tvp3026xo(0x2D, 0xB0|vga->p[0]);
1777dd7cddfSDavid du Colombier 		while(!(tvp3026xi(0x2D) & 0x40))
1787dd7cddfSDavid du Colombier 			;
1797dd7cddfSDavid du Colombier 
1807dd7cddfSDavid du Colombier 		tvp3026xo(0x39, 0x38|vga->q[1]);
1817dd7cddfSDavid du Colombier 		tvp3026xo(0x2C, 0x00);
1827dd7cddfSDavid du Colombier 		tvp3026xo(0x2F, 0xC0|vga->n[1]);
1837dd7cddfSDavid du Colombier 		tvp3026xo(0x2F, vga->m[1]);
1847dd7cddfSDavid du Colombier 		tvp3026xo(0x2F, 0xF0|vga->p[1]);
1857dd7cddfSDavid du Colombier 		while(!(tvp3026xi(0x2F) & 0x40))
1867dd7cddfSDavid du Colombier 			;
1877dd7cddfSDavid du Colombier 
1887dd7cddfSDavid du Colombier 		setcrt42(vga, ctlr, 3);
1897dd7cddfSDavid du Colombier 	}
1907dd7cddfSDavid du Colombier 
1917dd7cddfSDavid du Colombier 	ctlr->flag |= Fload;
192219b2ee8SDavid du Colombier }
193219b2ee8SDavid du Colombier 
194219b2ee8SDavid du Colombier static struct {
195219b2ee8SDavid du Colombier 	char*	name;
196219b2ee8SDavid du Colombier 	void	(*load)(Vga*, Ctlr*);
197219b2ee8SDavid du Colombier } clocks[] = {
198219b2ee8SDavid du Colombier 	{ "icd2061a",		icd2061aload, },
199219b2ee8SDavid du Colombier 	{ "ch9294",		ch9294load, },
2007dd7cddfSDavid du Colombier 	{ "tvp3025clock",	tvp3025load, },
2017dd7cddfSDavid du Colombier 	{ "tvp3026clock",	tvp3026load, },
202219b2ee8SDavid du Colombier 	{ 0 },
203219b2ee8SDavid du Colombier };
204219b2ee8SDavid du Colombier 
205219b2ee8SDavid du Colombier static void
init(Vga * vga,Ctlr * ctlr)206219b2ee8SDavid du Colombier init(Vga* vga, Ctlr* ctlr)
207219b2ee8SDavid du Colombier {
208*9a747e4fSDavid du Colombier 	char name[Namelen+1], *p;
209219b2ee8SDavid du Colombier 	int i;
210219b2ee8SDavid du Colombier 
211219b2ee8SDavid du Colombier 	if(vga->clock == 0)
212219b2ee8SDavid du Colombier 		return;
213219b2ee8SDavid du Colombier 
214219b2ee8SDavid du Colombier 	/*
215219b2ee8SDavid du Colombier 	 * Check we know about it.
216219b2ee8SDavid du Colombier 	 */
217*9a747e4fSDavid du Colombier 	strncpy(name, vga->clock->name, Namelen);
218*9a747e4fSDavid du Colombier 	name[Namelen] = 0;
219219b2ee8SDavid du Colombier 	if(p = strchr(name, '-'))
220219b2ee8SDavid du Colombier 		*p = 0;
221219b2ee8SDavid du Colombier 	for(i = 0; clocks[i].name; i++){
222219b2ee8SDavid du Colombier 		if(strcmp(clocks[i].name, name) == 0)
223219b2ee8SDavid du Colombier 			break;
224219b2ee8SDavid du Colombier 	}
225219b2ee8SDavid du Colombier 	if(clocks[i].name == 0)
2267dd7cddfSDavid du Colombier 		error("%s: unknown clock \"%s\"\n", ctlr->name, vga->clock->name);
227219b2ee8SDavid du Colombier 
2287dd7cddfSDavid du Colombier 	if(vga->clock->init && (vga->clock->flag & Finit) == 0)
229219b2ee8SDavid du Colombier 		(*vga->clock->init)(vga, vga->clock);
230219b2ee8SDavid du Colombier 
231219b2ee8SDavid du Colombier 	/*
232219b2ee8SDavid du Colombier 	 * If we don't already have a desired pclk,
233219b2ee8SDavid du Colombier 	 * take it from the mode.
234219b2ee8SDavid du Colombier 	 */
2357dd7cddfSDavid du Colombier 	if(vga->f[0] == 0)
2367dd7cddfSDavid du Colombier 		vga->f[0] = vga->mode->frequency;
2377dd7cddfSDavid du Colombier 	if(vga->f[0] != VgaFreq0 && vga->f[0] != VgaFreq1)
238219b2ee8SDavid du Colombier 		vga->misc |= 0x0C;
239219b2ee8SDavid du Colombier 
240219b2ee8SDavid du Colombier 	ctlr->flag |= Finit;
241219b2ee8SDavid du Colombier }
242219b2ee8SDavid du Colombier 
243219b2ee8SDavid du Colombier static void
load(Vga * vga,Ctlr * ctlr)244219b2ee8SDavid du Colombier load(Vga* vga, Ctlr* ctlr)
245219b2ee8SDavid du Colombier {
246*9a747e4fSDavid du Colombier 	char name[Namelen+1], *p;
247219b2ee8SDavid du Colombier 	int i;
248219b2ee8SDavid du Colombier 
249219b2ee8SDavid du Colombier 	if(vga->clock == 0 || (vga->clock->flag & Fload))
250219b2ee8SDavid du Colombier 		return;
251219b2ee8SDavid du Colombier 
252*9a747e4fSDavid du Colombier 	strncpy(name, vga->clock->name, Namelen);
253*9a747e4fSDavid du Colombier 	name[Namelen] = 0;
254219b2ee8SDavid du Colombier 	if(p = strchr(name, '-'))
255219b2ee8SDavid du Colombier 		*p = 0;
256219b2ee8SDavid du Colombier 
257219b2ee8SDavid du Colombier 	for(i = 0; clocks[i].name; i++){
258219b2ee8SDavid du Colombier 		if(strcmp(clocks[i].name, name))
259219b2ee8SDavid du Colombier 			continue;
2607dd7cddfSDavid du Colombier 		clocks[i].load(vga, ctlr);
2617dd7cddfSDavid du Colombier 		if(strcmp(clocks[i].name, "icd2061a") == 0){
2627dd7cddfSDavid du Colombier 			clocks[i].load(vga, ctlr);
2637dd7cddfSDavid du Colombier 			clocks[i].load(vga, ctlr);
2647dd7cddfSDavid du Colombier 		}
265219b2ee8SDavid du Colombier 
266219b2ee8SDavid du Colombier 		ctlr->flag |= Fload;
267219b2ee8SDavid du Colombier 		return;
268219b2ee8SDavid du Colombier 	}
269219b2ee8SDavid du Colombier }
270219b2ee8SDavid du Colombier 
271219b2ee8SDavid du Colombier Ctlr s3clock = {
272219b2ee8SDavid du Colombier 	"s3clock",			/* name */
273219b2ee8SDavid du Colombier 	0,				/* snarf */
274219b2ee8SDavid du Colombier 	0,				/* options */
275219b2ee8SDavid du Colombier 	init,				/* init */
276219b2ee8SDavid du Colombier 	load,				/* load */
277219b2ee8SDavid du Colombier 	0,				/* dump */
278219b2ee8SDavid du Colombier };
279