xref: /plan9/sys/src/cmd/aux/vga/mga2164w.c (revision 74f16c8187aab477889167f2422d0597b1b7d0ff)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 
5 #include "pci.h"
6 #include "vga.h"
7 
8 /*
9  * Matrox Millenium and Matrox Millenium II.
10  * Matrox MGA-2064W, MGA-2164W 3D graphics accelerators
11  * Texas Instruments Tvp3026 RAMDAC.
12  */
13 enum {
14 	Meg		= 1024*1024,
15 	/* pci chip manufacturer */
16 	MATROX		= 0x102B,
17 
18 	/* pci chip device ids */
19 	MGA2064		= 0x0519,
20 	MGA2164		= 0x051B,
21 	MGA2164AGP	= 0x051F,
22 
23 	/* i/o ports */
24 	Crtcext		= 0x03DE,	/* CRTC Extensions */
25 
26 	/* config space offsets */
27 	Devctrl		= 0x04,		/* Device Control */
28 	Option		= 0x40,		/* Option */
29 
30 	/* control aperture offsets */
31 	RAMDAC		= 0x3C00,	/* RAMDAC registers */
32 	CACHEFLUSH	= 0x1FFF,
33 };
34 
35 typedef struct {
36 	Pcidev*	pci;
37 	int	devid;
38 	uchar*	membase1;
39 	uchar*	membase2;
40 	ulong	devctrl;
41 	ulong	option;
42 	uchar	crtcext[6];
43 	uchar	tvp[64];
44 	uchar	pclk[3];
45 	uchar	mclk[3];
46 	uchar	lclk[3];
47 } Mga;
48 
49 static uchar
_tvp3026i(Vga * vga,Ctlr * ctlr,uchar reg)50 _tvp3026i(Vga* vga, Ctlr* ctlr, uchar reg)
51 {
52 	Mga *mga;
53 
54 	if(reg >= 0x10)
55 		error("%s: tvp3026io: direct reg 0x%uX out of range\n", ctlr->name, reg);
56 
57 	if(vga->private == nil)
58 		error("%s: tvp3026io: no *mga\n", ctlr->name);
59 	mga = vga->private;
60 
61 	return *(mga->membase1+RAMDAC+reg);
62 }
63 
64 static void
_tvp3026o(Vga * vga,Ctlr * ctlr,uchar reg,uchar data)65 _tvp3026o(Vga* vga, Ctlr* ctlr, uchar reg, uchar data)
66 {
67 	Mga *mga;
68 
69 	if(reg >= 0x10)
70 		error("%s: tvp3026io: direct reg 0x%uX out of range\n", ctlr->name, reg);
71 
72 	if(vga->private == nil)
73 		error("%s: tvp3026io: no *mga\n", ctlr->name);
74 	mga = vga->private;
75 
76 	*(mga->membase1+RAMDAC+reg) = data;
77 }
78 
79 static uchar
_tvp3026xi(Vga * vga,Ctlr * ctlr,uchar index)80 _tvp3026xi(Vga* vga, Ctlr* ctlr, uchar index)
81 {
82 	if(index >= 0x40)
83 		error("%s: tvp3026xi: reg 0x%uX out of range\n", ctlr->name, index);
84 
85 	_tvp3026o(vga, ctlr, 0x00, index);
86 
87 	return _tvp3026i(vga, ctlr, 0x0A);
88 }
89 
90 void
_tvp3026xo(Vga * vga,Ctlr * ctlr,uchar index,uchar data)91 _tvp3026xo(Vga* vga, Ctlr* ctlr, uchar index, uchar data)
92 {
93 	if(index >= 0x40)
94 		error("%s: tvp3026xo: reg 0x%uX out of range\n", ctlr->name, index);
95 
96 	_tvp3026o(vga, ctlr, 0x00, index);
97 	_tvp3026o(vga, ctlr, 0x0A, data);
98 }
99 
100 static uchar
crtcexti(uchar index)101 crtcexti(uchar index)
102 {
103 	uchar data;
104 
105 	outportb(Crtcext, index);
106 	data = inportb(Crtcext+1);
107 
108 	return data;
109 }
110 
111 static void
crtcexto(uchar index,uchar data)112 crtcexto(uchar index, uchar data)
113 {
114 	outportb(Crtcext, index);
115 	outportb(Crtcext+1, data);
116 }
117 
118 static void
mapmga(Vga * vga,Ctlr * ctlr)119 mapmga(Vga* vga, Ctlr* ctlr)
120 {
121 	int f;
122 	uchar *m;
123 	Mga *mga;
124 
125 	if(vga->private == nil)
126 		error("%s: tvp3026io: no *mga\n", ctlr->name);
127 	mga = vga->private;
128 
129 	f = open("#v/vgactl", OWRITE);
130 	if(f < 0)
131 		error("%s: can't open vgactl\n", ctlr->name);
132 	if(write(f, "type mga2164w", 13) != 13)
133 		error("%s: can't set mga type\n", ctlr->name);
134 
135 	m = segattach(0, "mga2164wmmio", 0, 16*1024);
136 	if(m == (void*)-1)
137 		error("%s: can't attach mga2164wmmio segment\n", ctlr->name);
138 	mga->membase1 = m;
139 
140 	m = segattach(0, "mga2164wscreen", 0, (mga->devid==MGA2064? 8 : 16)*Meg);
141 	if(m ==(void*)-1)
142 		error("%s: can't attach mga2164wscreen segment\n", ctlr->name);
143 
144 	mga->membase2 = m;
145 }
146 
147 static void
clockcalc(Vga * vga,Ctlr * ctlr,int bpp)148 clockcalc(Vga* vga, Ctlr* ctlr, int bpp)
149 {
150 	ulong m, n, p, q;
151 	double z, fdiff, fmindiff, fvco, fgoal;
152 	Mga *mga;
153 
154 	USED(ctlr);
155 
156 	mga = vga->private;
157 
158 	/*
159 	 * Look for values of n, d and p that give
160 	 * the least error for
161 	 *	Fvco = 8*RefFreq*(65-m)/(65-n)
162 	 *	Fpll = Fvco/2**p
163 	 * N and m are 6 bits, p is 2 bits. Constraints:
164 	 *	110MHz <= Fvco <= 250MHz
165 	 *	40 <= n <= 62
166 	 *	 1 <= m <= 62
167 	 *	 0 <= p <=  3
168 	 */
169 	fgoal = (double)vga->f[0];
170 	fmindiff = fgoal;
171 	vga->m[0] = 0x15;
172 	vga->n[0] = 0x18;
173 	vga->p[0] = 3;
174 	for(m = 62; m > 0; m--){
175 		for(n = 62; n >= 40; n--){
176 			fvco = 8.*((double)RefFreq)*(65.-m)/(65.-n);
177 			if(fvco < 110e6 || fvco > 250e6)
178 				continue;
179 			for(p = 0; p < 4; p++){
180 				fdiff = fgoal - fvco/(1<<p);
181 				if(fdiff < 0)
182 					fdiff = -fdiff;
183 				if(fdiff < fmindiff){
184 					fmindiff = fdiff;
185 					vga->m[0] = m;
186 					vga->n[0] = n;
187 					vga->p[0] = p;
188 				}
189 			}
190 		}
191 	}
192 	mga->pclk[0] = 0xC0 | (vga->n[0] & 0x3f);
193 	mga->pclk[1] = (vga->m[0] & 0x3f);
194 	mga->pclk[2] = 0xB0 | (vga->p[0] & 0x03);
195 
196 	/*
197 	 * Now the loop clock:
198 	 * 	m is fixed;
199 	 *	calculate n;
200 	 *	set z to the lower bound (110MHz) and calculate p and q.
201 	 */
202 	vga->m[1] = 61;
203 	n = 65 - 4*(64/(vga->vmz==2*Meg? bpp*2 : bpp));
204 	fvco = (8*RefFreq*(65-vga->m[0]))/(65-vga->n[0]);
205 	vga->f[1] = fvco/(1<<vga->p[0]);
206 	z = 110e6*(65.-n)/(4.*vga->f[1]);
207 	if(z <= 16){
208 		for(p = 0; p < 4; p++){
209 			if(1<<(p+1) > z)
210 				break;
211 		}
212 		q = 0;
213 	}
214 	else{
215 		p = 3;
216 		q = (z - 16)/16 + 1;
217 	}
218 	vga->n[1] = n;
219 	vga->p[1] = p;
220 	vga->q[1] = q;
221 	mga->lclk[0] = 0xC0 | (vga->n[1] & 0x3f);
222 	mga->lclk[1] = (vga->m[1] & 0x3f);
223 	mga->lclk[2] = 0xF0 | (vga->p[1] & 0x03);
224 	mga->tvp[0x39] = 0x38 | q;
225 }
226 
227 static void
snarf(Vga * vga,Ctlr * ctlr)228 snarf(Vga* vga, Ctlr* ctlr)
229 {
230 	int i, k, n;
231 	uchar *p, x[8];
232 	Pcidev *pci;
233 	Mga *mga;
234 
235 	if(vga->private == nil) {
236 		pci = pcimatch(nil, MATROX, MGA2164AGP);
237 		if(pci == nil)
238 			pci = pcimatch(nil, MATROX, MGA2164);
239 		if(pci == nil)
240 			pci = pcimatch(nil, MATROX, MGA2064);
241 		if(pci == nil)
242 			error("%s: no Pcidev with Vid=0x102B, Did=0x051[9BF] or 0x521\n", ctlr->name);
243 
244 		vga->private = alloc(sizeof(Mga));
245 		mga = (Mga*)vga->private;
246 		mga->devid = pci->did;
247 		mga->pci = pci;
248 		mapmga(vga, ctlr);
249 	}
250 	else {
251 		mga = (Mga*)vga->private;
252 		pci = mga->pci;
253 	}
254 
255 	for(i = 0; i < 0x06; i++)
256 		mga->crtcext[i] = crtcexti(i);
257 
258 	mga->devctrl = pcicfgr32(pci, Devctrl);
259 	mga->option = pcicfgr32(pci, Option);
260 
261 	for(i = 0; i < 64; i++)
262 		mga->tvp[i] = _tvp3026xi(vga, ctlr, i);
263 
264 	for(i = 0; i < 3; i++){
265 		_tvp3026xo(vga, ctlr, 0x2C, (i<<4)|(i<<2)|i);
266 		mga->pclk[i] = _tvp3026xi(vga, ctlr, 0x2D);
267 	}
268 
269 	for(i = 0; i < 3; i++){
270 		_tvp3026xo(vga, ctlr, 0x2C, (i<<4)|(i<<2)|i);
271 		mga->mclk[i] = _tvp3026xi(vga, ctlr, 0x2E);
272 	}
273 
274 	for(i = 0; i < 3; i++){
275 		_tvp3026xo(vga, ctlr, 0x2C, (i<<4)|(i<<2)|i);
276 		mga->lclk[i] = _tvp3026xi(vga, ctlr, 0x2F);
277 	}
278 
279 	/* find out how much memory is here, some multiple of 2Meg */
280 	crtcexto(3, mga->crtcext[3] | 0x80);	/* mga mode */
281 	p = mga->membase2;
282 	n = (mga->devid==MGA2064? 4 : 8);
283 	for(i = 0; i < n; i++) {
284 		k = (2*i+1)*Meg;
285 		p[k] = 0;
286 		p[k] = i+1;
287 		*(mga->membase1+CACHEFLUSH) = 0;
288 		x[i] = p[k];
289 		trace("x[%d]=%d\n", i, x[i]);
290 	}
291 	for(i = 1; i < n; i++)
292 		if(x[i] != i+1)
293 			break;
294 	vga->vmz = 2*i*Meg;
295 	trace("probe found %d megabytes\n", 2*i);
296 	crtcexto(3, mga->crtcext[3]);	/* restore mga mode */
297 
298 
299 	ctlr->flag |= Fsnarf;
300 }
301 
302 static void
options(Vga * vga,Ctlr * ctlr)303 options(Vga* vga, Ctlr* ctlr)
304 {
305 	if(vga->virtx & 127)
306 		vga->virtx = (vga->virtx+127)&~127;
307 	ctlr->flag |= Foptions;
308 }
309 
310 static void
init(Vga * vga,Ctlr * ctlr)311 init(Vga* vga, Ctlr* ctlr)
312 {
313 	Mode *mode;
314 	Ctlr *c;
315 	Mga *mga;
316 	int scale, offset, pixbuswidth;
317 
318 	mga = vga->private;
319 	mode = vga->mode;
320 
321 	ctlr->flag |= Ulinear;
322 
323 	pixbuswidth = (vga->vmz == 2*Meg) ? 32 : 64;
324 	trace("pixbuswidth=%d\n", pixbuswidth);
325 
326 	if(vga->mode->z > 8)
327 		error("depth %d not supported\n", vga->mode->z);
328 
329 	if(vga->f[0] == 0)
330 		vga->f[0] = vga->mode->frequency;
331 
332 	/* supposed to let tvp reg 1D control sync polarity */
333 	vga->misc |= 0xC8;
334 
335 	/*
336 	 * In power graphics mode, supposed to use
337 	 *	hblkend = htotal+4
338 	 *	hblkstr = hdispend
339 	 */
340 	if((vga->crt[0x00] & 0x0F) == 0x0F) {
341 		vga->crt[0x00]++;
342 		mode->ht += 8;
343 	}
344 	vga->crt[0x02] = vga->crt[0x01];
345 	vga->crt[0x03] = (((mode->ht>>3)-1) & 0x1F) | 0x80;
346 	vga->crt[0x05] = ((((mode->ht>>3)-1) & 0x20) << 2)
347 		| ((mode->ehs>>3) & 0x1F);
348 
349 	offset = (vga->virtx*mode->z) >> ((pixbuswidth==32)? 6 : 7);
350 	vga->crt[0x13] = offset;
351 	vga->crt[0x14] = 0;
352 	vga->crt[0x17] = 0xE3;
353 
354 	mga->crtcext[0] = (offset & 0x300) >> 4;
355 	mga->crtcext[1] = ((((mode->ht>>3)-5) & 0x100) >> 8)
356 		| ((((mode->x>>3)-1) & 0x100) >> 7)
357 		| (((mode->shs>>3) & 0x100) >> 6)  /* why not (shs>>3)-1 ? */
358 		| (((mode->ht>>3)-1) & 0x40);
359 	mga->crtcext[2] = (((mode->vt-2) & 0xC00) >> 10)
360 		| (((mode->y-1) & 0x400) >> 8)
361 		| ((mode->vrs & 0xC00) >> 7)
362 		| ((mode->vrs & 0xC00) >> 5);
363 	scale = mode->z == 8 ? 1 : (mode->z == 16? 2 : 4);
364 	if(pixbuswidth == 32)
365 		scale *= 2;
366 	scale--;
367 	mga->crtcext[3] = scale | 0x80;
368 	if(vga->vmz >= 8*Meg)
369 		mga->crtcext[3] |= 0x10;
370 	else if(vga->vmz == 2*Meg)
371 		mga->crtcext[3] |= 0x08;
372 	mga->crtcext[4] = 0;
373 	mga->crtcext[5] = 0;
374 
375 	mga->tvp[0x0F] = (mode->z == 8) ? 0x06 : 0x07;
376 	mga->tvp[0x18] = (mode->z == 8) ? 0x80 :
377 		(mode->z == 16) ? 0x05 : 0x06;
378 	mga->tvp[0x19] = (mode->z == 8) ? 0x4C :
379 		(mode->z == 16) ? 0x54 : 0x5C;
380 	if(pixbuswidth == 32)
381 		mga->tvp[0x19]--;
382 	mga->tvp[0x1A] = (mode->z == 8) ? 0x25 :
383 		(mode->z == 16) ? 0x15 : 0x05;
384 	mga->tvp[0x1C] = 0;
385 	mga->tvp[0x1D] = (mode->y < 768) ? 0x00 : 0x03;
386 	mga->tvp[0x1E] = (mode->z == 8) ? 0x04 : 0x24; /* six bit mode */
387 	mga->tvp[0x2A] = 0;
388 	mga->tvp[0x2B] = 0x1E;
389 	mga->tvp[0x30] = 0xFF;
390 	mga->tvp[0x31] = 0xFF;
391 	mga->tvp[0x32] = 0xFF;
392 	mga->tvp[0x33] = 0xFF;
393 	mga->tvp[0x34] = 0xFF;
394 	mga->tvp[0x35] = 0xFF;
395 	mga->tvp[0x36] = 0xFF;
396 	mga->tvp[0x37] = 0xFF;
397 	mga->tvp[0x38] = 0;
398 	mga->tvp[0x39] = 0;
399 	mga->tvp[0x3A] = 0;
400 	mga->tvp[0x06] = 0;
401 
402 	// From: "Bruce G. Stewart" <bruce.g.stewart@worldnet.att.net>
403 	// 05-Jul-00 - Fix vertical blanking setup
404 	// This should probably be corrected in vga.c too.
405 	{
406 		// Start vertical blanking after the last displayed line
407 		// End vertical blanking after the total line count
408 		int svb = mode->y;
409 		int evb = mode->vt;
410 
411 		// A field is 1/2 of the lines in interlaced mode
412 		if(mode->interlace == 'v'){
413 			svb /= 2;
414 			evb /= 2;
415 		}
416 		--svb;
417 		--evb;			// line counter counts from 0
418 
419 		vga->crt[0x15] = svb;
420 		vga->crt[0x07] = (vga->crt[0x07] & ~0x08) | ((svb & 0x100)>>5);
421 		vga->crt[0x09] = (vga->crt[0x09] & ~0x20) | ((svb & 0x200)>>4);
422 		// MGA specific: bits 10 and 11
423 		mga->crtcext[0x02] &=  ~0x18;
424 		mga->crtcext[0x02] |= ((svb & 0xC00)>>7);
425 
426 		vga->crt[0x16]     = evb;
427 	}
428 
429 	clockcalc(vga, ctlr, mode->z);
430 
431 
432 	mga->option = mga->option & ~0x3000;
433 	if(vga->vmz > 2*Meg)
434 		mga->option |= 0x1000;
435 
436 	/* disable vga load (want to do fields in different order) */
437 	for(c = vga->link; c; c = c->link)
438 		if(c->name == "vga")
439 			c->load = nil;
440 
441 	ctlr->flag |= Finit;
442 }
443 
444 static void
load(Vga * vga,Ctlr * ctlr)445 load(Vga* vga, Ctlr* ctlr)
446 {
447 	int i;
448 	Mga *mga;
449 	Pcidev* pci;
450 
451 	mga = vga->private;
452 	pci = mga->pci;
453 
454 	trace("loading crtcext0-5\n");
455 	/* turn on mga mode, set other crt high bits */
456 	for(i = 0; i < 6; i++)
457 		crtcexto(i, mga->crtcext[i]);
458 
459 	trace("setting memory mode\n");
460 	/* set memory mode */
461 	pcicfgw32(pci, Option, mga->option);
462 
463 	/* clocksource: pixel clock PLL */
464 	_tvp3026xo(vga, ctlr, 0x1A, mga->tvp[0x1A]);
465 
466 	/* disable pclk and lclk */
467 	_tvp3026xo(vga, ctlr, 0x2C, 0x2A);
468 	_tvp3026xo(vga, ctlr, 0x2F, 0);
469 	_tvp3026xo(vga, ctlr, 0x2D, 0);
470 
471 	trace("loading vga registers\n");
472 	/* vga misc, seq, and registers */
473 	vgao(MiscW, vga->misc);
474 
475 	for(i = 2; i < 0x05; i++)
476 		vgaxo(Seqx, i, vga->sequencer[i]);
477 
478 	vgaxo(Crtx, 0x11, vga->crt[0x11] & ~0x80);
479 	for(i = 0; i < 0x19; i++)
480 		vgaxo(Crtx, i, vga->crt[i]);
481 
482 	/* apparently AGP needs this to restore start address */
483 	crtcexto(0, mga->crtcext[0]);
484 
485 	trace("programming pclk\n");
486 	/* set up pclk */
487 	_tvp3026xo(vga, ctlr, 0x2C, 0);
488 	for(i = 0; i < 3; i++)
489 		_tvp3026xo(vga, ctlr, 0x2D, mga->pclk[i]);
490 	while(!(_tvp3026xi(vga, ctlr, 0x2D) & 0x40))
491 		;
492 
493 	trace("programming lclk\n");
494 	/* set up lclk */
495 	_tvp3026xo(vga, ctlr, 0x39, mga->tvp[0x39]);
496 	_tvp3026xo(vga, ctlr, 0x2C, 0);
497 	for(i = 0; i < 3; i++)
498 		_tvp3026xo(vga, ctlr, 0x2F, mga->lclk[i]);
499 	while(!(_tvp3026xi(vga, ctlr, 0x2F) & 0x40))
500 		;
501 
502 	trace("loading tvp registers\n");
503 	/* tvp registers, order matters */
504 	_tvp3026xo(vga, ctlr, 0x0F, mga->tvp[0x0F]);
505 	_tvp3026xo(vga, ctlr, 0x18, mga->tvp[0x18]);
506 	_tvp3026xo(vga, ctlr, 0x19, mga->tvp[0x19]);
507 	_tvp3026xo(vga, ctlr, 0x1A, mga->tvp[0x1A]);
508 	_tvp3026xo(vga, ctlr, 0x1C, mga->tvp[0x1C]);
509 	_tvp3026xo(vga, ctlr, 0x1D, mga->tvp[0x1D]);
510 	_tvp3026xo(vga, ctlr, 0x1E, mga->tvp[0x1E]);
511 	_tvp3026xo(vga, ctlr, 0x2A, mga->tvp[0x2A]);
512 	_tvp3026xo(vga, ctlr, 0x2B, mga->tvp[0x2B]);
513 	_tvp3026xo(vga, ctlr, 0x30, mga->tvp[0x30]);
514 	_tvp3026xo(vga, ctlr, 0x31, mga->tvp[0x31]);
515 	_tvp3026xo(vga, ctlr, 0x32, mga->tvp[0x32]);
516 	_tvp3026xo(vga, ctlr, 0x33, mga->tvp[0x33]);
517 	_tvp3026xo(vga, ctlr, 0x34, mga->tvp[0x34]);
518 	_tvp3026xo(vga, ctlr, 0x35, mga->tvp[0x35]);
519 	_tvp3026xo(vga, ctlr, 0x36, mga->tvp[0x36]);
520 	_tvp3026xo(vga, ctlr, 0x37, mga->tvp[0x37]);
521 	_tvp3026xo(vga, ctlr, 0x38, mga->tvp[0x38]);
522 	_tvp3026xo(vga, ctlr, 0x39, mga->tvp[0x39]);
523 	_tvp3026xo(vga, ctlr, 0x3A, mga->tvp[0x3A]);
524 	_tvp3026xo(vga, ctlr, 0x06, mga->tvp[0x06]);
525 
526 	trace("done mga load\n");
527 	ctlr->flag |= Fload;
528 }
529 
530 static void
dump(Vga * vga,Ctlr * ctlr)531 dump(Vga* vga, Ctlr* ctlr)
532 {
533 	int i;
534 	char *name;
535 	Mga *mga;
536 
537 	name = ctlr->name;
538 	mga = vga->private;
539 
540 	printitem(name, "Devctrl");
541 	printreg(mga->devctrl);
542 
543 	printitem(name, "Option");
544 	printreg(mga->option);
545 
546 	printitem(name, "Crtcext");
547 	for(i = 0; i < 0x06; i++)
548 		printreg(mga->crtcext[i]);
549 
550 	printitem(name, "TVP");
551 	for(i = 0; i < 64; i++)
552 		printreg(mga->tvp[i]);
553 
554 	printitem(name, "PCLK");
555 	for(i = 0; i < 3; i++)
556 		printreg(mga->pclk[i]);
557 
558 	printitem(name, "MCLK");
559 	for(i = 0; i < 3; i++)
560 		printreg(mga->mclk[i]);
561 
562 	printitem(name, "LCLK");
563 	for(i = 0; i < 3; i++)
564 		printreg(mga->lclk[i]);
565 }
566 
567 Ctlr mga2164w = {
568 	"mga2164w",			/* name */
569 	snarf,				/* snarf */
570 	options,			/* options */
571 	init,				/* init */
572 	load,				/* load */
573 	dump,				/* dump */
574 };
575 
576 Ctlr mga2164whwgc = {
577 	"mga2164whwgc",			/* name */
578 	0,				/* snarf */
579 	0,				/* options */
580 	0,				/* init */
581 	0,				/* load */
582 	dump,				/* dump */
583 };
584