xref: /plan9/sys/src/cmd/aux/vga/clgd542x.c (revision 76f6a3b860b80e94688a646002aae821e49bdb30)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 
5 #include "pci.h"
6 #include "vga.h"
7 
8 /*
9  * Cirrus Logic True Color VGA Family - CL-GD542X.
10  * Also works for Alpine VGA Family - CL-GD543X.
11  * Just the basics. BUGS:
12  *   the added capabilities of the 543X aren't used.
13  */
14 
15 typedef struct {
16 	uchar	id;			/* Id */
17 	ulong	vclk;			/* Maximum dot clock */
18 } Gd542x;
19 
20 static Gd542x family[] = {
21 	{ 0x88,  75000000, },		/* CL-GD5420 */
22 	{ 0x8C,  80000000, },		/* CL-GD5422 */
23 	{ 0x94,  80000000, },		/* CL-GD5424 */
24 	{ 0x90,  80000000, },		/* CL-GD5426 */
25 	{ 0x98,  80000000, },		/* CL-GD5428 */
26 	{ 0x9C,  86000000, },		/* CL-GD5429 */
27 	{ 0xA0,  86000000, },		/* CL-GD5430 */
28 	{ 0xA8,  86000000, },		/* CL-GD5434 */
29 
30 	{ 0xAC, 135000000, },		/* CL-GD5436 */
31 	{ 0xB8, 135000000, },		/* CL-GD5446 */
32 	{ 0xBC, 135000000, },		/* CL-GD5480 */
33 
34 	{ 0x30,  80000000, },		/* CL-GD7543 */
35 
36 	{ 0x00, },
37 };
38 
39 static Gd542x*
identify(Vga * vga,Ctlr * ctlr)40 identify(Vga* vga, Ctlr* ctlr)
41 {
42 	Gd542x *gd542x;
43 	uchar id;
44 
45 	id = vga->crt[0x27] & ~0x03;
46 	for(gd542x = &family[0]; gd542x->id; gd542x++){
47 		if(gd542x->id == id)
48 			return gd542x;
49 	}
50 
51 	error("%s: unknown chip id - 0x%2.2X\n", ctlr->name, vga->crt[0x27]);
52 	return 0;
53 }
54 
55 static void
snarf(Vga * vga,Ctlr * ctlr)56 snarf(Vga* vga, Ctlr* ctlr)
57 {
58 	int i;
59 	Gd542x *gd542x;
60 
61 	/*
62 	 * Unlock extended registers.
63 	 */
64 	vgaxo(Seqx, 0x06, 0x12);
65 
66 	/*
67 	 * Save all the registers, even though we'll only
68 	 * change a handful.
69 	 */
70 	for(i = 0x06; i < 0x20; i++)
71 		vga->sequencer[i] = vgaxi(Seqx, i);
72 
73 	for(i = 0x09; i < 0x3A; i++)
74 		vga->graphics[i] = vgaxi(Grx, i);
75 
76 	for(i = 0x19; i < 0x1E; i++)
77 		vga->crt[i] = vgaxi(Crtx, i);
78 
79 	vga->crt[0x27] = vgaxi(Crtx, 0x27);
80 
81 	/*
82 	 * Hack for Hidden DAC Register. Do 4 dummy reads
83 	 * of Pixmask first.
84 	 */
85 	for(i = 0; i < 4; i++)
86 		vgai(Pixmask);
87 	vga->crt[0x28] = vgai(Pixmask);
88 
89 	i = 0;
90 	switch(vga->crt[0x27] & ~0x03){
91 
92 	case 0x88:				/* CL-GD5420 */
93 	case 0x8C:				/* CL-GD5422 */
94 	case 0x94:				/* CL-GD5424 */
95 	case 0x80:				/* CL-GD5425 */
96 	case 0x90:				/* CL-GD5426 */
97 	case 0x98:				/* CL-GD5427 */
98 	case 0x9C:				/* CL-GD5429 */
99 		/*
100 		 * The BIOS leaves the memory size in Seq0A, bits 4 and 3.
101 		 * See Technical Reference Manual Appendix E1, Section 1.3.2.
102 		 *
103 		 * The storage area for the 64x64 cursors is the last 16Kb of
104 		 * display memory.
105 		 */
106 		i = (vga->sequencer[0x0A]>>3) & 0x03;
107 		break;
108 
109 	case 0xA0:				/* CL-GD5430 */
110 	case 0xA8:				/* CL-GD5434 */
111 	case 0xAC:				/* CL-GD5436 */
112 	case 0xB8:				/* CL-GD5446 */
113 	case 0x30:				/* CL-GD7543 */
114 		/*
115 		 * Attempt to intuit the memory size from the DRAM control
116 		 * register. Minimum is 512KB.
117 		 * If DRAM bank switching is on then there's double.
118 		 */
119 		i = (vga->sequencer[0x0F]>>3) & 0x03;
120 		if(vga->sequencer[0x0F] & 0x80)
121 			i++;
122 
123 		/*
124 		 * If it's a PCI card, can do linear.
125 		 * Most of the Cirrus chips can do linear addressing with
126 		 * all the different buses, but it can get messy. It's easy
127 		 * to cut PCI on the CLGD543x chips out of the pack.
128 		 */
129 		if(((vga->sequencer[0x17]>>3) & 0x07) == 0x04)
130 			ctlr->flag |= Hlinear;
131 		break;
132 	case 0xBC:				/* CL-GD5480 */
133 		i = 2;				/* 1024 = 256<<2 */
134 		if((vga->sequencer[0x0F] & 0x18) == 0x18){
135 			i <<= 1;		/* 2048 = 256<<3 */
136 			if(vga->sequencer[0x0F] & 0x80)
137 				i <<= 2;	/* 2048 = 256<<4 */
138 		}
139 		if(vga->sequencer[0x17] & 0x80)
140 			i <<= 1;
141 		ctlr->flag |= Hlinear;
142 		break;
143 
144 	default:				/* uh, ah dunno */
145 		break;
146 	}
147 
148 	if(vga->linear && (ctlr->flag & Hlinear)){
149 		vga->vmz = 16*1024*1024;
150 		vga->vma = 16*1024*1024;
151 		ctlr->flag |= Ulinear;
152 	}
153 	else
154 		vga->vmz = (256<<i)*1024;
155 
156 	gd542x = identify(vga, ctlr);
157 	if(vga->f[1] == 0 || vga->f[1] > gd542x->vclk)
158 		vga->f[1] = gd542x->vclk;
159 	ctlr->flag |= Fsnarf;
160 }
161 
162 void
clgd54xxclock(Vga * vga,Ctlr * ctlr)163 clgd54xxclock(Vga* vga, Ctlr* ctlr)
164 {
165 	int f;
166 	ulong d, dmin, fmin, n, nmin, p;
167 
168 	trace("%s->init->clgd54xxclock\n", ctlr->name);
169 
170 	/*
171 	 * Athough the Technical Reference Manual says only a handful
172 	 * of frequencies are tested, it also gives the following formula
173 	 * which can be used to generate any frequency within spec.,
174 	 * including the tested ones.
175 	 *
176 	 * Numerator is 7-bits, denominator 5-bits.
177 	 * Guess from the Technical Reference Manual that
178 	 * The post divisor is 1 for vclk<40MHz.
179 	 *
180 	 * Look for values of n and d and p that give
181 	 * the least error for
182 	 *	vclk = (RefFreq*n)/(d*(1+p));
183 	 *
184 	 * There's nothing like brute force and ignorance.
185 	 */
186 	fmin = vga->f[0];
187 	nmin = 69;
188 	dmin = 24;
189 	if(vga->f[0] >= 40000000)
190 		p = 0;
191 	else
192 		p = 1;
193 	for(n = 1; n < 128; n++){
194 		for(d = 1; d < 32; d++){
195 			f = vga->f[0] - (RefFreq*n)/(d*(1+p));
196 			if(f < 0)
197 				f = -f;
198 			if(f <= fmin){
199 				fmin = f;
200 				nmin = n;
201 				dmin = d;
202 			}
203 		}
204 	}
205 
206 	vga->f[0] = (RefFreq*nmin)/(dmin*(1+p));
207 	vga->d[0] = dmin;
208 	vga->n[0] = nmin;
209 	vga->p[0] = p;
210 }
211 
212 void
init(Vga * vga,Ctlr * ctlr)213 init(Vga* vga, Ctlr* ctlr)
214 {
215 	Mode *mode;
216 	Gd542x *gd542x;
217 	ushort x;
218 
219 	mode = vga->mode;
220 	gd542x = identify(vga, ctlr);
221 
222 	if(vga->f[0] == 0)
223 		vga->f[0] = vga->mode->frequency;
224 	if(vga->f[0] > gd542x->vclk)
225 		error("%s: pclk %lud too high (> %lud)\n",
226 			ctlr->name, vga->f[0], gd542x->vclk);
227 
228 	if(mode->z > 8)
229 		error("%s: depth %d not supported\n", ctlr->name, mode->z);
230 
231 	/*
232 	 * VCLK3
233 	 */
234 	clgd54xxclock(vga, ctlr);
235 	vga->misc |= 0x0C;
236 	vga->sequencer[0x0E] = vga->n[0];
237 	vga->sequencer[0x1E] = (vga->d[0]<<1)|vga->p[0];
238 
239 	vga->sequencer[0x07] = 0x00;
240 	if(mode->z == 8)
241 		vga->sequencer[0x07] |= 0x01;
242 
243 	if(vga->f[0] >= 42000000)
244 		vga->sequencer[0x0F] |= 0x20;
245 	else
246 		vga->sequencer[0x0F] &= ~0x20;
247 
248 	vga->sequencer[0x16] = (vga->sequencer[0x16] & 0xF0)|0x08;
249 
250 	/*
251 	 * Overflow bits.
252 	 */
253 	vga->crt[0x1A] = 0x00;
254 	x = mode->ehb>>3;
255 	if(x & 0x40)
256 		vga->crt[0x1A] |= 0x10;
257 	if(x & 0x80)
258 		vga->crt[0x1A] |= 0x20;
259 	if(vga->crt[0x16] & 0x100)
260 		vga->crt[0x1A] |= 0x40;
261 	if(vga->crt[0x16] & 0x200)
262 		vga->crt[0x1A] |= 0x80;
263 	vga->crt[0x1B] = 0x22;
264 	if(vga->crt[0x13] & 0x100)
265 		vga->crt[0x1B] |= 0x10;
266 
267 	vga->graphics[0x0B] = 0x00;
268 	if(vga->vmz > 1024*1024)
269 		vga->graphics[0x0B] |= 0x20;
270 
271 	if(mode->interlace == 'v'){
272 		vga->crt[0x19] = vga->crt[0x00]/2;
273 		vga->crt[0x1A] |= 0x01;
274 	}
275 }
276 
277 static void
load(Vga * vga,Ctlr * ctlr)278 load(Vga* vga, Ctlr* ctlr)
279 {
280 	vgaxo(Seqx, 0x0E, vga->sequencer[0x0E]);
281 	vgaxo(Seqx, 0x1E, vga->sequencer[0x1E]);
282 	if(ctlr->flag & Ulinear)
283 		vga->sequencer[0x07] |= 0xE0;
284 	vgaxo(Seqx, 0x07, vga->sequencer[0x07]);
285 	vgaxo(Seqx, 0x0F, vga->sequencer[0x0F]);
286 	vgaxo(Seqx, 0x16, vga->sequencer[0x16]);
287 
288 	if(vga->mode->interlace == 'v')
289 		vgaxo(Crtx, 0x19, vga->crt[0x19]);
290 	vgaxo(Crtx, 0x1A, vga->crt[0x1A]);
291 	vgaxo(Crtx, 0x1B, vga->crt[0x1B]);
292 
293 	vgaxo(Grx, 0x0B, vga->graphics[0x0B]);
294 }
295 
296 static void
dump(Vga * vga,Ctlr * ctlr)297 dump(Vga* vga, Ctlr* ctlr)
298 {
299 	int i;
300 	char *name;
301 
302 	name = ctlr->name;
303 
304 	printitem(name, "Seq06");
305 	for(i = 0x06; i < 0x20; i++)
306 		printreg(vga->sequencer[i]);
307 
308 	printitem(name, "Crt19");
309 	for(i = 0x19; i < 0x1E; i++)
310 		printreg(vga->crt[i]);
311 
312 	printitem(name, "Gr09");
313 	for(i = 0x09; i < 0x3A; i++)
314 		printreg(vga->graphics[i]);
315 
316 	printitem(name, "Id Hdr");
317 	printreg(vga->crt[0x27]);
318 	printreg(vga->crt[0x28]);
319 }
320 
321 Ctlr clgd542x = {
322 	"clgd542x",			/* name */
323 	snarf,				/* snarf */
324 	0,				/* options */
325 	init,				/* init */
326 	load,				/* load */
327 	dump,				/* dump */
328 };
329 
330 Ctlr clgd542xhwgc = {
331 	"clgd542xhwgc",			/* name */
332 	0,				/* snarf */
333 	0,				/* options */
334 	0,				/* init */
335 	0,				/* load */
336 	0,				/* dump */
337 };
338