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