xref: /plan9/sys/src/cmd/aux/vga/s3clock.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 
5 #include "pci.h"
6 #include "vga.h"
7 
8 /*
9  * Clocks which require fiddling with the S3 registers
10  * in order to be loaded.
11  */
12 static void
setcrt42(Vga * vga,Ctlr * ctlr,uchar index)13 setcrt42(Vga* vga, Ctlr* ctlr, uchar index)
14 {
15 	trace("%s->clock->setcrt42\n", ctlr->name);
16 
17 	vgao(MiscW, vga->misc & ~0x0C);
18 	outportb(Crtx+1, 0x00);
19 
20 	vga->crt[0x42] = (vga->crt[0x42] & 0xF0)|index;
21 	vgao(MiscW, vga->misc);
22 	vgaxo(Crtx, 0x42, vga->crt[0x42]);
23 }
24 
25 static void
icd2061aload(Vga * vga,Ctlr * ctlr)26 icd2061aload(Vga* vga, Ctlr* ctlr)
27 {
28 	ulong sdata;
29 	int i;
30 	uchar crt42;
31 
32 	trace("%s->clock->icd2061aload\n", ctlr->name);
33 	/*
34 	 * The serial word to be loaded into the icd2061a is
35 	 *	(2<<21)|(vga->i<<17)|((vga->n)<<10)|(vga->p<<7)|vga->d
36 	 * Always select ICD2061A REG2.
37 	 */
38 	sdata = (2<<21)|(vga->i[0]<<17)|((vga->n[0])<<10)|(vga->p[0]<<7)|vga->d[0];
39 
40 	/*
41 	 * The display should be already off to enable  us to clock the
42 	 * serial data word into either MiscW or Crt42.
43 	 *
44 	 * Set the Misc register to make clock-select-out the contents of
45 	 * register Crt42. Must turn the sequencer off when changing bits
46 	 * <3:2> of Misc. Also, must set Crt42 to 0 before setting <3:2>
47 	 * of Misc due to a hardware glitch.
48 	 */
49 	vgao(MiscW, vga->misc & ~0x0C);
50 	crt42 = vgaxi(Crtx, 0x42) & 0xF0;
51 	outportb(Crtx+1, 0x00);
52 
53 	vgaxo(Seqx, 0x00, 0x00);
54 	vgao(MiscW, vga->misc|0x0C);
55 	vgaxo(Seqx, 0x00, 0x03);
56 
57 	/*
58 	 * Unlock the ICD2061A. The unlock sequence is at least 5 low-to-high
59 	 * transitions of CLK with DATA high, followed by a single low-to-high
60 	 * transition of CLK with DATA low.
61 	 * Using Crt42, CLK is bit0, DATA is bit 1. If we used MiscW, they'd
62 	 * be bits 2 and 3 respectively.
63 	 */
64 	outportb(Crtx+1, crt42|0x00);			/* -DATA|-CLK */
65 	outportb(Crtx+1, crt42|0x02);			/* +DATA|-CLK */
66 	for(i = 0; i < 5; i++){
67 		outportb(Crtx+1, crt42|0x03);		/* +DATA|+CLK */
68 		outportb(Crtx+1, crt42|0x02);		/* +DATA|-CLK */
69 	}
70 	outportb(Crtx+1, crt42|0x00);			/* -DATA|-CLK */
71 	outportb(Crtx+1, crt42|0x01);			/* -DATA|+CLK */
72 
73 	/*
74 	 * Now write the serial data word, framed by a start-bit and a stop-bit.
75 	 * The data is written using a modified Manchester encoding such that a
76 	 * data-bit is sampled on the rising edge of CLK and the complement of
77 	 * the data-bit is sampled on the previous falling edge of CLK.
78 	 */
79 	outportb(Crtx+1, crt42|0x00);			/* -DATA|-CLK (start-bit) */
80 	outportb(Crtx+1, crt42|0x01);			/* -DATA|+CLK */
81 
82 	for(i = 0; i < 24; i++){			/* serial data word */
83 		if(sdata & 0x01){
84 			outportb(Crtx+1, crt42|0x01);	/* -DATA|+CLK */
85 			outportb(Crtx+1, crt42|0x00);	/* -DATA|-CLK (falling edge) */
86 			outportb(Crtx+1, crt42|0x02);	/* +DATA|-CLK */
87 			outportb(Crtx+1, crt42|0x03);	/* +DATA|+CLK (rising edge) */
88 		}
89 		else {
90 			outportb(Crtx+1, crt42|0x03);	/* +DATA|+CLK */
91 			outportb(Crtx+1, crt42|0x02);	/* +DATA|-CLK (falling edge) */
92 			outportb(Crtx+1, crt42|0x00);	/* -DATA|-CLK */
93 			outportb(Crtx+1, crt42|0x01);	/* -DATA|+CLK (rising edge) */
94 		}
95 		sdata >>= 1;
96 	}
97 
98 	outportb(Crtx+1, crt42|0x03);			/* +DATA|+CLK (stop-bit) */
99 	outportb(Crtx+1, crt42|0x02);			/* +DATA|-CLK */
100 	outportb(Crtx+1, crt42|0x03);			/* +DATA|+CLK */
101 
102 	/*
103 	 * We always use REG2 in the ICD2061A.
104 	 */
105 	setcrt42(vga, ctlr, 0x02);
106 }
107 
108 static void
ch9294load(Vga * vga,Ctlr * ctlr)109 ch9294load(Vga* vga, Ctlr* ctlr)
110 {
111 	trace("%s->clock->ch9294load\n", ctlr->name);
112 
113 	setcrt42(vga, ctlr, vga->i[0]);
114 }
115 
116 static void
tvp3025load(Vga * vga,Ctlr * ctlr)117 tvp3025load(Vga* vga, Ctlr* ctlr)
118 {
119 	uchar crt5c, x;
120 
121 	trace("%s->clock->tvp3025load\n", ctlr->name);
122 
123 	/*
124 	 * Crt5C bit 5 is RS4.
125 	 * Clear it to select TVP3025 registers for
126 	 * the calls to tvp302xo().
127 	 */
128 	crt5c = vgaxi(Crtx, 0x5C);
129 	vgaxo(Crtx, 0x5C, crt5c & ~0x20);
130 
131 	tvp3020xo(0x2C, 0x00);
132 	tvp3020xo(0x2D, vga->d[0]);
133 	tvp3020xo(0x2D, vga->n[0]);
134 	tvp3020xo(0x2D, 0x08|vga->p[0]);
135 
136 	tvp3020xo(0x2F, 0x01);
137 	tvp3020xo(0x2F, 0x01);
138 	tvp3020xo(0x2F, vga->p[0]);
139 	x = 0x54;
140 	if(vga->ctlr && (vga->ctlr->flag & Uenhanced))
141 		x = 0xC4;
142 	tvp3020xo(0x1E, x);
143 
144 	vgaxo(Crtx, 0x5C, crt5c);
145 	vgao(MiscW, vga->misc);
146 
147 	ctlr->flag |= Fload;
148 }
149 
150 static void
tvp3026load(Vga * vga,Ctlr * ctlr)151 tvp3026load(Vga* vga, Ctlr* ctlr)
152 {
153 	trace("%s->clock->tvp3026load\n", ctlr->name);
154 
155 	if((vga->misc & 0x0C) != 0x0C && vga->mode->z == 1){
156 		tvp3026xo(0x1A, 0x07);
157 		tvp3026xo(0x18, 0x80);
158 		tvp3026xo(0x19, 0x98);
159 		tvp3026xo(0x2C, 0x2A);
160 		tvp3026xo(0x2F, 0x00);
161 		tvp3026xo(0x2D, 0x00);
162 		tvp3026xo(0x39, 0x18);
163 		setcrt42(vga, ctlr, 0);
164 	}
165 	else if(vga->mode->z == 8){
166 		tvp3026xo(0x1A, 0x05);
167 		tvp3026xo(0x18, 0x80);
168 		tvp3026xo(0x19, 0x4C);
169 		tvp3026xo(0x2C, 0x2A);
170 		tvp3026xo(0x2F, 0x00);
171 		tvp3026xo(0x2D, 0x00);
172 
173 		tvp3026xo(0x2C, 0x00);
174 		tvp3026xo(0x2D, 0xC0|vga->n[0]);
175 		tvp3026xo(0x2D, vga->m[0] & 0x3F);
176 		tvp3026xo(0x2D, 0xB0|vga->p[0]);
177 		while(!(tvp3026xi(0x2D) & 0x40))
178 			;
179 
180 		tvp3026xo(0x39, 0x38|vga->q[1]);
181 		tvp3026xo(0x2C, 0x00);
182 		tvp3026xo(0x2F, 0xC0|vga->n[1]);
183 		tvp3026xo(0x2F, vga->m[1]);
184 		tvp3026xo(0x2F, 0xF0|vga->p[1]);
185 		while(!(tvp3026xi(0x2F) & 0x40))
186 			;
187 
188 		setcrt42(vga, ctlr, 3);
189 	}
190 
191 	ctlr->flag |= Fload;
192 }
193 
194 static struct {
195 	char*	name;
196 	void	(*load)(Vga*, Ctlr*);
197 } clocks[] = {
198 	{ "icd2061a",		icd2061aload, },
199 	{ "ch9294",		ch9294load, },
200 	{ "tvp3025clock",	tvp3025load, },
201 	{ "tvp3026clock",	tvp3026load, },
202 	{ 0 },
203 };
204 
205 static void
init(Vga * vga,Ctlr * ctlr)206 init(Vga* vga, Ctlr* ctlr)
207 {
208 	char name[Namelen+1], *p;
209 	int i;
210 
211 	if(vga->clock == 0)
212 		return;
213 
214 	/*
215 	 * Check we know about it.
216 	 */
217 	strncpy(name, vga->clock->name, Namelen);
218 	name[Namelen] = 0;
219 	if(p = strchr(name, '-'))
220 		*p = 0;
221 	for(i = 0; clocks[i].name; i++){
222 		if(strcmp(clocks[i].name, name) == 0)
223 			break;
224 	}
225 	if(clocks[i].name == 0)
226 		error("%s: unknown clock \"%s\"\n", ctlr->name, vga->clock->name);
227 
228 	if(vga->clock->init && (vga->clock->flag & Finit) == 0)
229 		(*vga->clock->init)(vga, vga->clock);
230 
231 	/*
232 	 * If we don't already have a desired pclk,
233 	 * take it from the mode.
234 	 */
235 	if(vga->f[0] == 0)
236 		vga->f[0] = vga->mode->frequency;
237 	if(vga->f[0] != VgaFreq0 && vga->f[0] != VgaFreq1)
238 		vga->misc |= 0x0C;
239 
240 	ctlr->flag |= Finit;
241 }
242 
243 static void
load(Vga * vga,Ctlr * ctlr)244 load(Vga* vga, Ctlr* ctlr)
245 {
246 	char name[Namelen+1], *p;
247 	int i;
248 
249 	if(vga->clock == 0 || (vga->clock->flag & Fload))
250 		return;
251 
252 	strncpy(name, vga->clock->name, Namelen);
253 	name[Namelen] = 0;
254 	if(p = strchr(name, '-'))
255 		*p = 0;
256 
257 	for(i = 0; clocks[i].name; i++){
258 		if(strcmp(clocks[i].name, name))
259 			continue;
260 		clocks[i].load(vga, ctlr);
261 		if(strcmp(clocks[i].name, "icd2061a") == 0){
262 			clocks[i].load(vga, ctlr);
263 			clocks[i].load(vga, ctlr);
264 		}
265 
266 		ctlr->flag |= Fload;
267 		return;
268 	}
269 }
270 
271 Ctlr s3clock = {
272 	"s3clock",			/* name */
273 	0,				/* snarf */
274 	0,				/* options */
275 	init,				/* init */
276 	load,				/* load */
277 	0,				/* dump */
278 };
279