xref: /plan9/sys/src/cmd/aux/vga/s3clock.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
1*219b2ee8SDavid du Colombier #include <u.h>
2*219b2ee8SDavid du Colombier #include <libc.h>
3*219b2ee8SDavid du Colombier 
4*219b2ee8SDavid du Colombier #include "vga.h"
5*219b2ee8SDavid du Colombier 
6*219b2ee8SDavid du Colombier static void
7*219b2ee8SDavid du Colombier setcrt42(Vga *vga, Ctlr *ctlr, uchar index)
8*219b2ee8SDavid du Colombier {
9*219b2ee8SDavid du Colombier 	verbose("%s->clock->setcrt42\n", ctlr->name);
10*219b2ee8SDavid du Colombier 
11*219b2ee8SDavid du Colombier 	vgao(MiscW, vga->misc & ~0x0C);
12*219b2ee8SDavid du Colombier 	outportb(Crtx+1, 0x00);
13*219b2ee8SDavid du Colombier 
14*219b2ee8SDavid du Colombier 	vga->crt[0x42] = (vga->crt[0x42] & 0xF0)|index;
15*219b2ee8SDavid du Colombier 	vgao(MiscW, vga->misc);
16*219b2ee8SDavid du Colombier 	vgaxo(Crtx, 0x42, vga->crt[0x42]);
17*219b2ee8SDavid du Colombier }
18*219b2ee8SDavid du Colombier 
19*219b2ee8SDavid du Colombier static void
20*219b2ee8SDavid du Colombier icd2061aload(Vga *vga, Ctlr *ctlr)
21*219b2ee8SDavid du Colombier {
22*219b2ee8SDavid du Colombier 	ulong sdata;
23*219b2ee8SDavid du Colombier 	int i;
24*219b2ee8SDavid du Colombier 	uchar crt42;
25*219b2ee8SDavid du Colombier 
26*219b2ee8SDavid du Colombier 	verbose("%s->clock->icd2061aload\n", ctlr->name);
27*219b2ee8SDavid du Colombier 	/*
28*219b2ee8SDavid du Colombier 	 * The serial word to be loaded into the icd2061a is
29*219b2ee8SDavid du Colombier 	 *	(2<<21)|(vga->i<<17)|((vga->n)<<10)|(vga->p<<7)|vga->d
30*219b2ee8SDavid du Colombier 	 * Always select ICD2061A REG2.
31*219b2ee8SDavid du Colombier 	 */
32*219b2ee8SDavid du Colombier 	sdata = (2<<21)|(vga->i<<17)|((vga->n)<<10)|(vga->p<<7)|vga->d;
33*219b2ee8SDavid du Colombier 
34*219b2ee8SDavid du Colombier 	/*
35*219b2ee8SDavid du Colombier 	 * The display should be already off to enable  us to clock the
36*219b2ee8SDavid du Colombier 	 * serial data word into either MiscW or Crt42.
37*219b2ee8SDavid du Colombier 	 *
38*219b2ee8SDavid du Colombier 	 * Set the Misc register to make clock-select-out the contents of
39*219b2ee8SDavid du Colombier 	 * register Crt42. Must turn the sequencer off when changing bits
40*219b2ee8SDavid du Colombier 	 * <3:2> of Misc. Also, must set Crt42 to 0 before setting <3:2>
41*219b2ee8SDavid du Colombier 	 * of Misc due to a hardware glitch.
42*219b2ee8SDavid du Colombier 	 */
43*219b2ee8SDavid du Colombier 	vgao(MiscW, vga->misc & ~0x0C);
44*219b2ee8SDavid du Colombier 	crt42 = vgaxi(Crtx, 0x42) & 0xF0;
45*219b2ee8SDavid du Colombier 	outportb(Crtx+1, 0x00);
46*219b2ee8SDavid du Colombier 
47*219b2ee8SDavid du Colombier 	vgaxo(Seqx, 0x00, 0x00);
48*219b2ee8SDavid du Colombier 	vgao(MiscW, vga->misc|0x0C);
49*219b2ee8SDavid du Colombier 	vgaxo(Seqx, 0x00, 0x03);
50*219b2ee8SDavid du Colombier 
51*219b2ee8SDavid du Colombier 	/*
52*219b2ee8SDavid du Colombier 	 * Unlock the ICD2061A. The unlock sequence is at least 5 low-to-high
53*219b2ee8SDavid du Colombier 	 * transitions of CLK with DATA high, followed by a single low-to-high
54*219b2ee8SDavid du Colombier 	 * transition of CLK with DATA low.
55*219b2ee8SDavid du Colombier 	 * Using Crt42, CLK is bit0, DATA is bit 1. If we used MiscW, they'd
56*219b2ee8SDavid du Colombier 	 * be bits 2 and 3 respectively.
57*219b2ee8SDavid du Colombier 	 */
58*219b2ee8SDavid du Colombier 	outportb(Crtx+1, crt42|0x00);			/* -DATA|-CLK */
59*219b2ee8SDavid du Colombier 	outportb(Crtx+1, crt42|0x02);			/* +DATA|-CLK */
60*219b2ee8SDavid du Colombier 	for(i = 0; i < 5; i++){
61*219b2ee8SDavid du Colombier 		outportb(Crtx+1, crt42|0x03);		/* +DATA|+CLK */
62*219b2ee8SDavid du Colombier 		outportb(Crtx+1, crt42|0x02);		/* +DATA|-CLK */
63*219b2ee8SDavid du Colombier 	}
64*219b2ee8SDavid du Colombier 	outportb(Crtx+1, crt42|0x00);			/* -DATA|-CLK */
65*219b2ee8SDavid du Colombier 	outportb(Crtx+1, crt42|0x01);			/* -DATA|+CLK */
66*219b2ee8SDavid du Colombier 
67*219b2ee8SDavid du Colombier 	/*
68*219b2ee8SDavid du Colombier 	 * Now write the serial data word, framed by a start-bit and a stop-bit.
69*219b2ee8SDavid du Colombier 	 * The data is written using a modified Manchester encoding such that a
70*219b2ee8SDavid du Colombier 	 * data-bit is sampled on the rising edge of CLK and the complement of
71*219b2ee8SDavid du Colombier 	 * the data-bit is sampled on the previous falling edge of CLK.
72*219b2ee8SDavid du Colombier 	 */
73*219b2ee8SDavid du Colombier 	outportb(Crtx+1, crt42|0x00);			/* -DATA|-CLK (start-bit) */
74*219b2ee8SDavid du Colombier 	outportb(Crtx+1, crt42|0x01);			/* -DATA|+CLK */
75*219b2ee8SDavid du Colombier 
76*219b2ee8SDavid du Colombier 	for(i = 0; i < 24; i++){			/* serial data word */
77*219b2ee8SDavid du Colombier 		if(sdata & 0x01){
78*219b2ee8SDavid du Colombier 			outportb(Crtx+1, crt42|0x01);	/* -DATA|+CLK */
79*219b2ee8SDavid du Colombier 			outportb(Crtx+1, crt42|0x00);	/* -DATA|-CLK (falling edge) */
80*219b2ee8SDavid du Colombier 			outportb(Crtx+1, crt42|0x02);	/* +DATA|-CLK */
81*219b2ee8SDavid du Colombier 			outportb(Crtx+1, crt42|0x03);	/* +DATA|+CLK (rising edge) */
82*219b2ee8SDavid du Colombier 		}
83*219b2ee8SDavid du Colombier 		else {
84*219b2ee8SDavid du Colombier 			outportb(Crtx+1, crt42|0x03);	/* +DATA|+CLK */
85*219b2ee8SDavid du Colombier 			outportb(Crtx+1, crt42|0x02);	/* +DATA|-CLK (falling edge) */
86*219b2ee8SDavid du Colombier 			outportb(Crtx+1, crt42|0x00);	/* -DATA|-CLK */
87*219b2ee8SDavid du Colombier 			outportb(Crtx+1, crt42|0x01);	/* -DATA|+CLK (rising edge) */
88*219b2ee8SDavid du Colombier 		}
89*219b2ee8SDavid du Colombier 		sdata >>= 1;
90*219b2ee8SDavid du Colombier 	}
91*219b2ee8SDavid du Colombier 
92*219b2ee8SDavid du Colombier 	outportb(Crtx+1, crt42|0x03);			/* +DATA|+CLK (stop-bit) */
93*219b2ee8SDavid du Colombier 	outportb(Crtx+1, crt42|0x02);			/* +DATA|-CLK */
94*219b2ee8SDavid du Colombier 	outportb(Crtx+1, crt42|0x03);			/* +DATA|+CLK */
95*219b2ee8SDavid du Colombier 
96*219b2ee8SDavid du Colombier 	/*
97*219b2ee8SDavid du Colombier 	 * We always use REG2 in the ICD2061A.
98*219b2ee8SDavid du Colombier 	 */
99*219b2ee8SDavid du Colombier 	setcrt42(vga, ctlr, 0x02);
100*219b2ee8SDavid du Colombier }
101*219b2ee8SDavid du Colombier 
102*219b2ee8SDavid du Colombier static void
103*219b2ee8SDavid du Colombier ch9294load(Vga *vga, Ctlr *ctlr)
104*219b2ee8SDavid du Colombier {
105*219b2ee8SDavid du Colombier 	verbose("%s->clock->ch9294load\n", ctlr->name);
106*219b2ee8SDavid du Colombier 
107*219b2ee8SDavid du Colombier 	setcrt42(vga, ctlr, vga->i);
108*219b2ee8SDavid du Colombier }
109*219b2ee8SDavid du Colombier 
110*219b2ee8SDavid du Colombier static struct {
111*219b2ee8SDavid du Colombier 	char*	name;
112*219b2ee8SDavid du Colombier 	void	(*load)(Vga*, Ctlr*);
113*219b2ee8SDavid du Colombier } clocks[] = {
114*219b2ee8SDavid du Colombier 	{ "icd2061a",	icd2061aload, },
115*219b2ee8SDavid du Colombier 	{ "ch9294",	ch9294load, },
116*219b2ee8SDavid du Colombier 	{ 0 },
117*219b2ee8SDavid du Colombier };
118*219b2ee8SDavid du Colombier 
119*219b2ee8SDavid du Colombier static void
120*219b2ee8SDavid du Colombier init(Vga *vga, Ctlr *ctlr)
121*219b2ee8SDavid du Colombier {
122*219b2ee8SDavid du Colombier 	char name[NAMELEN+1], *p;
123*219b2ee8SDavid du Colombier 	int i;
124*219b2ee8SDavid du Colombier 
125*219b2ee8SDavid du Colombier 	verbose("%s->init\n", ctlr->name);
126*219b2ee8SDavid du Colombier 
127*219b2ee8SDavid du Colombier 	if(vga->clock == 0)
128*219b2ee8SDavid du Colombier 		return;
129*219b2ee8SDavid du Colombier 
130*219b2ee8SDavid du Colombier 	/*
131*219b2ee8SDavid du Colombier 	 * Check we know about it.
132*219b2ee8SDavid du Colombier 	 */
133*219b2ee8SDavid du Colombier 	strncpy(name, vga->clock->name, NAMELEN);
134*219b2ee8SDavid du Colombier 	name[NAMELEN] = 0;
135*219b2ee8SDavid du Colombier 	if(p = strchr(name, '-'))
136*219b2ee8SDavid du Colombier 		*p = 0;
137*219b2ee8SDavid du Colombier 	for(i = 0; clocks[i].name; i++){
138*219b2ee8SDavid du Colombier 		if(strcmp(clocks[i].name, name) == 0)
139*219b2ee8SDavid du Colombier 			break;
140*219b2ee8SDavid du Colombier 	}
141*219b2ee8SDavid du Colombier 	if(clocks[i].name == 0)
142*219b2ee8SDavid du Colombier 		error("don't recognise s3clock \"%s\"\n", vga->clock->name);
143*219b2ee8SDavid du Colombier 
144*219b2ee8SDavid du Colombier 	if(vga->clock->init && (vga->clock->flag & Finit) == 0){
145*219b2ee8SDavid du Colombier 		(*vga->clock->init)(vga, vga->clock);
146*219b2ee8SDavid du Colombier 		(*vga->clock->init)(vga, vga->clock);
147*219b2ee8SDavid du Colombier 		(*vga->clock->init)(vga, vga->clock);
148*219b2ee8SDavid du Colombier 	}
149*219b2ee8SDavid du Colombier 
150*219b2ee8SDavid du Colombier 	/*
151*219b2ee8SDavid du Colombier 	 * If we don't already have a desired pclk,
152*219b2ee8SDavid du Colombier 	 * take it from the mode.
153*219b2ee8SDavid du Colombier 	 */
154*219b2ee8SDavid du Colombier 	if(vga->f == 0)
155*219b2ee8SDavid du Colombier 		vga->f = vga->mode->frequency;
156*219b2ee8SDavid du Colombier 	if(vga->f != VgaFreq0 && vga->f != VgaFreq1)
157*219b2ee8SDavid du Colombier 		vga->misc |= 0x0C;
158*219b2ee8SDavid du Colombier 
159*219b2ee8SDavid du Colombier 	ctlr->flag |= Finit;
160*219b2ee8SDavid du Colombier }
161*219b2ee8SDavid du Colombier 
162*219b2ee8SDavid du Colombier static void
163*219b2ee8SDavid du Colombier load(Vga *vga, Ctlr *ctlr)
164*219b2ee8SDavid du Colombier {
165*219b2ee8SDavid du Colombier 	char name[NAMELEN+1], *p;
166*219b2ee8SDavid du Colombier 	int i;
167*219b2ee8SDavid du Colombier 
168*219b2ee8SDavid du Colombier 	verbose("%s->load\n", ctlr->name);
169*219b2ee8SDavid du Colombier 
170*219b2ee8SDavid du Colombier 	if(vga->clock == 0 || (vga->clock->flag & Fload))
171*219b2ee8SDavid du Colombier 		return;
172*219b2ee8SDavid du Colombier 
173*219b2ee8SDavid du Colombier 	strncpy(name, vga->clock->name, NAMELEN);
174*219b2ee8SDavid du Colombier 	name[NAMELEN] = 0;
175*219b2ee8SDavid du Colombier 	if(p = strchr(name, '-'))
176*219b2ee8SDavid du Colombier 		*p = 0;
177*219b2ee8SDavid du Colombier 
178*219b2ee8SDavid du Colombier 	for(i = 0; clocks[i].name; i++){
179*219b2ee8SDavid du Colombier 		if(strcmp(clocks[i].name, name))
180*219b2ee8SDavid du Colombier 			continue;
181*219b2ee8SDavid du Colombier 		(*clocks[i].load)(vga, ctlr);
182*219b2ee8SDavid du Colombier 
183*219b2ee8SDavid du Colombier 		ctlr->flag |= Fload;
184*219b2ee8SDavid du Colombier 		return;
185*219b2ee8SDavid du Colombier 	}
186*219b2ee8SDavid du Colombier }
187*219b2ee8SDavid du Colombier 
188*219b2ee8SDavid du Colombier Ctlr s3clock = {
189*219b2ee8SDavid du Colombier 	"s3clock",			/* name */
190*219b2ee8SDavid du Colombier 	0,				/* snarf */
191*219b2ee8SDavid du Colombier 	0,				/* options */
192*219b2ee8SDavid du Colombier 	init,				/* init */
193*219b2ee8SDavid du Colombier 	load,				/* load */
194*219b2ee8SDavid du Colombier 	0,				/* dump */
195*219b2ee8SDavid du Colombier };
196