1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4
5 #include "pci.h"
6 #include "vga.h"
7
8 /*
9 * ATI Mach64 family.
10 */
11 enum {
12 HTotalDisp,
13 HSyncStrtWid,
14 VTotalDisp,
15 VSyncStrtWid,
16 VlineCrntVline,
17 OffPitch,
18 IntCntl,
19 CrtcGenCntl,
20
21 OvrClr,
22 OvrWidLR,
23 OvrWidTB,
24
25 CurClr0,
26 CurClr1,
27 CurOffset,
28 CurHVposn,
29 CurHVoff,
30
31 ScratchReg0,
32 ScratchReg1, /* Scratch Register (BIOS info) */
33 ClockCntl,
34 BusCntl,
35 MemCntl,
36 ExtMemCntl,
37 MemVgaWpSel,
38 MemVgaRpSel,
39 DacRegs,
40 DacCntl,
41 GenTestCntl,
42 ConfigCntl, /* Configuration control */
43 ConfigChipId,
44 ConfigStat0, /* Configuration status 0 */
45 ConfigStat1, /* Configuration status 1 */
46 ConfigStat2,
47 DspConfig, /* Rage */
48 DspOnOff, /* Rage */
49
50 DpBkgdClr,
51 DpChainMsk,
52 DpFrgdClr,
53 DpMix,
54 DpPixWidth,
55 DpSrc,
56 DpWriteMsk,
57
58 LcdIndex,
59 LcdData,
60
61 Nreg,
62
63 TvIndex = 0x1D,
64 TvData = 0x27,
65
66 LCD_ConfigPanel = 0,
67 LCD_GenCtrl,
68 LCD_DstnCntl,
69 LCD_HfbPitchAddr,
70 LCD_HorzStretch,
71 LCD_VertStretch,
72 LCD_ExtVertStretch,
73 LCD_LtGio,
74 LCD_PowerMngmnt,
75 LCD_ZvgPio,
76 Nlcd,
77 };
78
79 static char* iorname[Nreg] = {
80 "HTotalDisp",
81 "HSyncStrtWid",
82 "VTotalDisp",
83 "VSyncStrtWid",
84 "VlineCrntVline",
85 "OffPitch",
86 "IntCntl",
87 "CrtcGenCntl",
88
89 "OvrClr",
90 "OvrWidLR",
91 "OvrWidTB",
92
93 "CurClr0",
94 "CurClr1",
95 "CurOffset",
96 "CurHVposn",
97 "CurHVoff",
98
99 "ScratchReg0",
100 "ScratchReg1",
101 "ClockCntl",
102 "BusCntl",
103 "MemCntl",
104 "ExtMemCntl",
105 "MemVgaWpSel",
106 "MemVgaRpSel",
107 "DacRegs",
108 "DacCntl",
109 "GenTestCntl",
110 "ConfigCntl",
111 "ConfigChipId",
112 "ConfigStat0",
113 "ConfigStat1",
114 "ConfigStat2",
115 "DspConfig",
116 "DspOnOff",
117
118 "DpBkgdClr",
119 "DpChainMsk",
120 "DpFrgdClr",
121 "DpMix",
122 "DpPixWidth",
123 "DpSrc",
124 "DpWriteMsk",
125
126 "LcdIndex",
127 "LcdData",
128 };
129
130 static char* lcdname[Nlcd] = {
131 "LCD ConfigPanel",
132 "LCD GenCntl",
133 "LCD DstnCntl",
134 "LCD HfbPitchAddr",
135 "LCD HorzStretch",
136 "LCD VertStretch",
137 "LCD ExtVertStretch",
138 "LCD LtGio",
139 "LCD PowerMngmnt",
140 "LCD ZvgPio"
141 };
142
143 /*
144 * Crummy hack: all io register offsets
145 * here get IOREG or'ed in, so that we can
146 * tell the difference between an uninitialized
147 * array entry and HTotalDisp.
148 */
149 enum {
150 IOREG = 0x10000,
151 };
152 static ushort ioregs[Nreg] = {
153 [HTotalDisp] IOREG|0x0000,
154 [HSyncStrtWid] IOREG|0x0100,
155 [VTotalDisp] IOREG|0x0200,
156 [VSyncStrtWid] IOREG|0x0300,
157 [VlineCrntVline] IOREG|0x0400,
158 [OffPitch] IOREG|0x0500,
159 [IntCntl] IOREG|0x0600,
160 [CrtcGenCntl] IOREG|0x0700,
161 [OvrClr] IOREG|0x0800,
162 [OvrWidLR] IOREG|0x0900,
163 [OvrWidTB] IOREG|0x0A00,
164 [CurClr0] IOREG|0x0B00,
165 [CurClr1] IOREG|0x0C00,
166 [CurOffset] IOREG|0x0D00,
167 [CurHVposn] IOREG|0x0E00,
168 [CurHVoff] IOREG|0x0F00,
169 [ScratchReg0] IOREG|0x1000,
170 [ScratchReg1] IOREG|0x1100,
171 [ClockCntl] IOREG|0x1200,
172 [BusCntl] IOREG|0x1300,
173 [MemCntl] IOREG|0x1400,
174 [MemVgaWpSel] IOREG|0x1500,
175 [MemVgaRpSel] IOREG|0x1600,
176 [DacRegs] IOREG|0x1700,
177 [DacCntl] IOREG|0x1800,
178 [GenTestCntl] IOREG|0x1900,
179 [ConfigCntl] IOREG|0x1A00,
180 [ConfigChipId] IOREG|0x1B00,
181 [ConfigStat0] IOREG|0x1C00,
182 [ConfigStat1] IOREG|0x1D00,
183 /* [GpIo] IOREG|0x1E00, */
184 /* [HTotalDisp] IOREG|0x1F00, duplicate, says XFree86 */
185 };
186
187 static ushort pciregs[Nreg] = {
188 [HTotalDisp] 0x00,
189 [HSyncStrtWid] 0x01,
190 [VTotalDisp] 0x02,
191 [VSyncStrtWid] 0x03,
192 [VlineCrntVline] 0x04,
193 [OffPitch] 0x05,
194 [IntCntl] 0x06,
195 [CrtcGenCntl] 0x07,
196 [DspConfig] 0x08,
197 [DspOnOff] 0x09,
198 [OvrClr] 0x10,
199 [OvrWidLR] 0x11,
200 [OvrWidTB] 0x12,
201 [CurClr0] 0x18,
202 [CurClr1] 0x19,
203 [CurOffset] 0x1A,
204 [CurHVposn] 0x1B,
205 [CurHVoff] 0x1C,
206 [ScratchReg0] 0x20,
207 [ScratchReg1] 0x21,
208 [ClockCntl] 0x24,
209 [BusCntl] 0x28,
210 [LcdIndex] 0x29,
211 [LcdData] 0x2A,
212 [ExtMemCntl] 0x2B,
213 [MemCntl] 0x2C,
214 [MemVgaWpSel] 0x2D,
215 [MemVgaRpSel] 0x2E,
216 [DacRegs] 0x30,
217 [DacCntl] 0x31,
218 [GenTestCntl] 0x34,
219 [ConfigCntl] 0x37,
220 [ConfigChipId] 0x38,
221 [ConfigStat0] 0x39,
222 [ConfigStat1] 0x25, /* rsc: was 0x3A, but that's not what the LT manual says */
223 [ConfigStat2] 0x26,
224 [DpBkgdClr] 0xB0,
225 [DpChainMsk] 0xB3,
226 [DpFrgdClr] 0xB1,
227 [DpMix] 0xB5,
228 [DpPixWidth] 0xB4,
229 [DpSrc] 0xB6,
230 [DpWriteMsk] 0xB2,
231 };
232
233 enum {
234 PLLm = 0x02,
235 PLLp = 0x06,
236 PLLn0 = 0x07,
237 PLLn1 = 0x08,
238 PLLn2 = 0x09,
239 PLLn3 = 0x0A,
240 PLLx = 0x0B, /* external divisor (Rage) */
241
242 Npll = 32,
243 Ntv = 1, /* actually 256, but not used */
244 };
245
246 typedef struct Mach64xx Mach64xx;
247 struct Mach64xx {
248 ulong io;
249 Pcidev* pci;
250 int bigmem;
251 int lcdon;
252 int lcdpanelid;
253
254 ulong reg[Nreg];
255 ulong lcd[Nlcd];
256 ulong tv[Ntv];
257 uchar pll[Npll];
258
259 ulong (*ior32)(Mach64xx*, int);
260 void (*iow32)(Mach64xx*, int, ulong);
261 };
262
263 static ulong
portior32(Mach64xx * mp,int r)264 portior32(Mach64xx* mp, int r)
265 {
266 if((ioregs[r] & IOREG) == 0)
267 return ~0;
268
269 return inportl(((ioregs[r] & ~IOREG)<<2)+mp->io);
270 }
271
272 static void
portiow32(Mach64xx * mp,int r,ulong l)273 portiow32(Mach64xx* mp, int r, ulong l)
274 {
275 if((ioregs[r] & IOREG) == 0)
276 return;
277
278 outportl(((ioregs[r] & ~IOREG)<<2)+mp->io, l);
279 }
280
281 static ulong
pciior32(Mach64xx * mp,int r)282 pciior32(Mach64xx* mp, int r)
283 {
284 return inportl((pciregs[r]<<2)+mp->io);
285 }
286
287 static void
pciiow32(Mach64xx * mp,int r,ulong l)288 pciiow32(Mach64xx* mp, int r, ulong l)
289 {
290 outportl((pciregs[r]<<2)+mp->io, l);
291 }
292
293 static uchar
pllr(Mach64xx * mp,int r)294 pllr(Mach64xx* mp, int r)
295 {
296 int io;
297
298 if(mp->ior32 == portior32)
299 io = ((ioregs[ClockCntl]&~IOREG)<<2)+mp->io;
300 else
301 io = (pciregs[ClockCntl]<<2)+mp->io;
302
303 outportb(io+1, r<<2);
304 return inportb(io+2);
305 }
306
307 static void
pllw(Mach64xx * mp,int r,uchar b)308 pllw(Mach64xx* mp, int r, uchar b)
309 {
310 int io;
311
312 if(mp->ior32 == portior32)
313 io = ((ioregs[ClockCntl]&~IOREG)<<2)+mp->io;
314 else
315 io = (pciregs[ClockCntl]<<2)+mp->io;
316
317 outportb(io+1, (r<<2)|0x02);
318 outportb(io+2, b);
319 }
320
321 static ulong
lcdr32(Mach64xx * mp,ulong r)322 lcdr32(Mach64xx *mp, ulong r)
323 {
324 ulong or;
325
326 or = mp->ior32(mp, LcdIndex);
327 mp->iow32(mp, LcdIndex, (or&~0x0F) | (r&0x0F));
328 return mp->ior32(mp, LcdData);
329 }
330
331 static void
lcdw32(Mach64xx * mp,ulong r,ulong v)332 lcdw32(Mach64xx *mp, ulong r, ulong v)
333 {
334 ulong or;
335
336 or = mp->ior32(mp, LcdIndex);
337 mp->iow32(mp, LcdIndex, (or&~0x0F) | (r&0x0F));
338 mp->iow32(mp, LcdData, v);
339 }
340
341 static ulong
tvr32(Mach64xx * mp,ulong r)342 tvr32(Mach64xx *mp, ulong r)
343 {
344 outportb(mp->io+(TvIndex<<2), r&0x0F);
345 return inportl(mp->io+(TvData<<2));
346 }
347
348 static void
tvw32(Mach64xx * mp,ulong r,ulong v)349 tvw32(Mach64xx *mp, ulong r, ulong v)
350 {
351 outportb(mp->io+(TvIndex<<2), r&0x0F);
352 outportl(mp->io+(TvData<<2), v);
353 }
354
355 static int smallmem[] = {
356 512*1024, 1024*1024, 2*1024*1024, 4*1024*1024,
357 6*1024*1024, 8*1024*1024, 12*1024*1024, 16*1024*1024,
358 };
359
360 static int bigmem[] = {
361 512*1024, 2*512*1024, 3*512*1024, 4*512*1024,
362 5*512*1024, 6*512*1024, 7*512*1024, 8*512*1024,
363 5*1024*1024, 6*1024*1024, 7*1024*1024, 8*1024*1024,
364 10*1024*1024, 12*1024*1024, 14*1024*1024, 16*1024*1024,
365 };
366
367 static void
snarf(Vga * vga,Ctlr * ctlr)368 snarf(Vga* vga, Ctlr* ctlr)
369 {
370 Mach64xx *mp;
371 int i;
372 ulong v;
373
374 if(vga->private == nil){
375 vga->private = alloc(sizeof(Mach64xx));
376 mp = vga->private;
377 mp->io = 0x2EC;
378 mp->ior32 = portior32;
379 mp->iow32 = portiow32;
380 mp->pci = pcimatch(0, 0x1002, 0);
381 if (mp->pci) {
382 if(v = mp->pci->mem[1].bar & ~0x3) {
383 mp->io = v;
384 mp->ior32 = pciior32;
385 mp->iow32 = pciiow32;
386 }
387 }
388 }
389
390 mp = vga->private;
391 for(i = 0; i < Nreg; i++)
392 mp->reg[i] = mp->ior32(mp, i);
393
394 for(i = 0; i < Npll; i++)
395 mp->pll[i] = pllr(mp, i);
396
397 switch(mp->reg[ConfigChipId] & 0xFFFF){
398 default:
399 mp->lcdpanelid = 0;
400 break;
401 case ('L'<<8)|'B': /* 4C42: Rage LTPro AGP */
402 case ('L'<<8)|'I': /* 4C49: Rage 3D LTPro */
403 case ('L'<<8)|'M': /* 4C4D: Rage Mobility */
404 case ('L'<<8)|'P': /* 4C50: Rage 3D LTPro */
405 for(i = 0; i < Nlcd; i++)
406 mp->lcd[i] = lcdr32(mp, i);
407 if(mp->lcd[LCD_GenCtrl] & 0x02)
408 mp->lcdon = 1;
409 mp->lcdpanelid = ((mp->reg[ConfigStat2]>>14) & 0x1F);
410 break;
411 }
412
413 /*
414 * Check which memory size map we are using.
415 */
416 mp->bigmem = 0;
417 switch(mp->reg[ConfigChipId] & 0xFFFF){
418 case ('G'<<8)|'B': /* 4742: 264GT PRO */
419 case ('G'<<8)|'D': /* 4744: 264GT PRO */
420 case ('G'<<8)|'I': /* 4749: 264GT PRO */
421 case ('G'<<8)|'M': /* 474D: Rage XL */
422 case ('G'<<8)|'P': /* 4750: 264GT PRO */
423 case ('G'<<8)|'Q': /* 4751: 264GT PRO */
424 case ('G'<<8)|'R': /* 4752: */
425 case ('G'<<8)|'U': /* 4755: 264GT DVD */
426 case ('G'<<8)|'V': /* 4756: Rage2C */
427 case ('G'<<8)|'Z': /* 475A: Rage2C */
428 case ('V'<<8)|'U': /* 5655: 264VT3 */
429 case ('V'<<8)|'V': /* 5656: 264VT4 */
430 case ('L'<<8)|'B': /* 4C42: Rage LTPro AGP */
431 case ('L'<<8)|'I': /* 4C49: Rage 3D LTPro */
432 case ('L'<<8)|'M': /* 4C4D: Rage Mobility */
433 case ('L'<<8)|'P': /* 4C50: Rage 3D LTPro */
434 mp->bigmem = 1;
435 break;
436 case ('G'<<8)|'T': /* 4754: 264GT[B] */
437 case ('V'<<8)|'T': /* 5654: 264VT/GT/VTB */
438 /*
439 * Only the VTB and GTB use the new memory encoding,
440 * and they are identified by a nonzero ChipVersion,
441 * apparently.
442 */
443 if((mp->reg[ConfigChipId] >> 24) & 0x7)
444 mp->bigmem = 1;
445 break;
446 }
447
448 /*
449 * Memory size and aperture. It's recommended
450 * to use an 8Mb aperture on a 16Mb boundary.
451 */
452 if(mp->bigmem)
453 vga->vmz = bigmem[mp->reg[MemCntl] & 0x0F];
454 else
455 vga->vmz = smallmem[mp->reg[MemCntl] & 0x07];
456 vga->vma = 16*1024*1024;
457
458 switch(mp->reg[ConfigCntl]&0x3){
459 case 0:
460 vga->apz = 16*1024*1024; /* empirical -rsc */
461 break;
462 case 1:
463 vga->apz = 4*1024*1024;
464 break;
465 case 2:
466 vga->apz = 8*1024*1024;
467 break;
468 case 3:
469 vga->apz = 2*1024*1024; /* empirical: mach64GX -rsc */
470 break;
471 }
472
473 ctlr->flag |= Fsnarf;
474 }
475
476 static void
options(Vga *,Ctlr * ctlr)477 options(Vga*, Ctlr* ctlr)
478 {
479 ctlr->flag |= Hlinear|Foptions;
480 }
481
482 static void
clock(Vga * vga,Ctlr * ctlr)483 clock(Vga* vga, Ctlr* ctlr)
484 {
485 int clk, m, n, p;
486 double f, q;
487 Mach64xx *mp;
488
489 mp = vga->private;
490
491 /*
492 * Don't compute clock timings for LCD panels.
493 * Just use what's already there. We can't just use
494 * the frequency in the vgadb for this because
495 * the frequency being programmed into the PLLs
496 * is not the frequency being used to compute the DSP
497 * settings. The DSP-relevant frequency is the one
498 * we keep in /lib/vgadb.
499 */
500 if(mp->lcdon){
501 clk = mp->reg[ClockCntl] & 0x03;
502 n = mp->pll[7+clk];
503 p = (mp->pll[6]>>(clk*2)) & 0x03;
504 p |= (mp->pll[11]>>(2+clk)) & 0x04;
505 switch(p){
506 case 0:
507 case 1:
508 case 2:
509 case 3:
510 p = 1<<p;
511 break;
512 case 4+0:
513 p = 3;
514 break;
515 case 4+2:
516 p = 6;
517 break;
518 case 4+3:
519 p = 12;
520 break;
521
522 default:
523 case 4+1:
524 p = -1;
525 break;
526 }
527 m = mp->pll[PLLm];
528 f = (2.0*RefFreq*n)/(m*p) + 0.5;
529
530 vga->m[0] = m;
531 vga->p[0] = p;
532 vga->n[0] = n;
533 vga->f[0] = f;
534 return;
535 }
536
537 if(vga->f[0] == 0)
538 vga->f[0] = vga->mode->frequency;
539 f = vga->f[0];
540
541 /*
542 * To generate a specific output frequency, the reference (m),
543 * feedback (n), and post dividers (p) must be loaded with the
544 * appropriate divide-down ratios. In the following r is the
545 * XTALIN frequency (usually RefFreq) and t is the target frequency
546 * (vga->f).
547 *
548 * Use the maximum reference divider left by the BIOS for now,
549 * otherwise MCLK might be a concern. It can be calculated as
550 * follows:
551 * Upper Limit of PLL Lock Range
552 * Minimum PLLREFCLK = -----------------------------
553 * (2*255)
554 *
555 * XTALIN
556 * m = Floor[-----------------]
557 * Minimum PLLREFCLK
558 *
559 * For an upper limit of 135MHz and XTALIN of 14.318MHz m
560 * would be 54.
561 */
562 m = mp->pll[PLLm];
563 vga->m[0] = m;
564
565 /*
566 * The post divider may be 1, 2, 4 or 8 and is determined by
567 * calculating
568 * t*m
569 * q = -----
570 * (2*r)
571 * and using the result to look-up p.
572 */
573 q = (f*m)/(2*RefFreq);
574 if(ctlr->flag&Uenhanced){
575 if(q > 255 || q < 10.6666666667)
576 error("%s: vclk %lud out of range\n", ctlr->name, vga->f[0]);
577 if(q > 127.5)
578 p = 1;
579 else if(q > 85)
580 p = 2;
581 else if(q > 63.75)
582 p = 3;
583 else if(q > 42.5)
584 p = 4;
585 else if(q > 31.875)
586 p = 6;
587 else if(q > 21.25)
588 p = 8;
589 else
590 p = 12;
591 }else{
592 if(q > 255 || q < 16)
593 error("%s: vclk %lud out of range\n", ctlr->name, vga->f[0]);
594 if(q >= 127.5)
595 p = 1;
596 else if(q >= 63.5)
597 p = 2;
598 else if(q >= 31.5)
599 p = 4;
600 else
601 p = 8;
602 }
603 vga->p[0] = p;
604
605 /*
606 * The feedback divider should be kept in the range 0x80 to 0xFF
607 * and is found from
608 * n = q*p
609 * rounded to the nearest whole number.
610 */
611 vga->n[0] = (q*p)+0.5;
612 }
613
614 typedef struct Meminfo Meminfo;
615 struct Meminfo {
616 int latency;
617 int latch;
618 int trp; /* filled in from card */
619 int trcd; /* filled in from card */
620 int tcrd; /* filled in from card */
621 int tras; /* filled in from card */
622 };
623
624 enum {
625 Mdram,
626 Medo,
627 Msdram,
628 Mwram,
629 };
630
631 /*
632 * The manuals and documentation are silent on which settings
633 * to use for Mwdram, or how to tell which to use.
634 */
635 static Meminfo meminfo[] = {
636 [Mdram] { 1, 0 },
637 [Medo] { 1, 2 },
638 [Msdram] { 3, 1 },
639 [Mwram] { 1, 3 }, /* non TYPE_A */
640 };
641
642 static ushort looplatencytab[2][2] = {
643 { 8, 6 }, /* DRAM: ≤1M, > 1M */
644 { 9, 8 }, /* SDRAM: ≤1M, > 1M */
645 };
646
647 static ushort cyclesperqwordtab[2][2] = {
648 { 3, 2 }, /* DRAM: ≤1M, > 1M */
649 { 2, 1 }, /* SDRAM: ≤1M, > 1M */
650 };
651
652 static int memtype[] = {
653 -1, /* disable memory access */
654 Mdram, /* basic DRAM */
655 Medo, /* EDO */
656 Medo, /* hyper page DRAM or EDO */
657 Msdram, /* SDRAM */
658 Msdram, /* SGRAM */
659 Mwram,
660 Mwram
661 };
662
663 /*
664 * Calculate various memory parameters so that the card
665 * fetches the right bytes at the right time. I don't claim to
666 * understand the actual calculations very well.
667 *
668 * This is remarkably useful on laptops, since knowledge of
669 * x lets us find the frequency that the screen is really running
670 * at, which is not necessarily in the VCLKs.
671 */
672 static void
setdsp(Vga * vga,Ctlr *)673 setdsp(Vga* vga, Ctlr*)
674 {
675 Mach64xx *mp;
676 Meminfo *mem;
677 ushort table, memclk, memtyp;
678 int i, prec, xprec, fprec;
679 ulong t;
680 double pw, x, fifosz, fifoon, fifooff;
681 ushort dspon, dspoff;
682 int afifosz, lat, ncycle, pfc, rcc;
683
684 mp = vga->private;
685
686 /*
687 * Get video ram configuration from BIOS and chip
688 */
689 table = *(ushort*)readbios(sizeof table, 0xc0048);
690 trace("rom table offset %uX\n", table);
691 table = *(ushort*)readbios(sizeof table, 0xc0000+table+16);
692 trace("freq table offset %uX\n", table);
693 memclk = *(ushort*)readbios(sizeof memclk, 0xc0000+table+18);
694 trace("memclk %ud\n", memclk);
695 memtyp = memtype[mp->reg[ConfigStat0]&07];
696 mem = &meminfo[memtyp];
697
698 /*
699 * First we need to calculate x, the number of
700 * XCLKs that one QWORD occupies in the display FIFO.
701 *
702 * For some reason, x gets stretched out if LCD stretching
703 * is turned on.
704 */
705
706 x = ((double)memclk*640000.0) /
707 ((double)vga->mode->frequency * (double)vga->mode->z);
708 if(mp->lcd[LCD_HorzStretch] & (1<<31))
709 x *= 4096.0 / (double)(mp->lcd[LCD_HorzStretch] & 0xFFFF);
710
711 trace("memclk %d... x %f...", memclk, x);
712 /*
713 * We have 14 bits to specify x in. Decide where to
714 * put the decimal (err, binary) point by counting how
715 * many significant bits are in the integer portion of x.
716 */
717 t = x;
718 for(i=31; i>=0; i--)
719 if(t & (1<<i))
720 break;
721 xprec = i+1;
722 trace("t %lud... xprec %d...", t, xprec);
723
724 /*
725 * The maximum FIFO size is the number of XCLKs per QWORD
726 * multiplied by 32, for some reason. We have 11 bits to
727 * specify fifosz.
728 */
729 fifosz = x * 32.0;
730 trace("fifosz %f...", fifosz);
731 t = fifosz;
732 for(i=31; i>=0; i--)
733 if(t & (1<<i))
734 break;
735 fprec = i+1;
736 trace("fprec %d...", fprec);
737
738 /*
739 * Precision is specified as 3 less than the number of bits
740 * in the integer part of x, and 5 less than the number of bits
741 * in the integer part of fifosz.
742 *
743 * It is bounded by zero and seven.
744 */
745 prec = (xprec-3 > fprec-5) ? xprec-3 : fprec-5;
746 if(prec < 0)
747 prec = 0;
748 if(prec > 7)
749 prec = 7;
750
751 xprec = prec+3;
752 fprec = prec+5;
753 trace("prec %d...", prec);
754
755 /*
756 * Actual fifo size
757 */
758 afifosz = (1<<fprec) / x;
759 if(afifosz > 32)
760 afifosz = 32;
761
762 fifooff = ceil(x*(afifosz-1));
763
764 /*
765 * I am suspicious of this table, lifted from ATI docs,
766 * because it doesn't agree with the Windows drivers.
767 * We always get 0x0A for lat+2 while Windows uses 0x08.
768 */
769 lat = looplatencytab[memtyp > 1][vga->vmz > 1*1024*1024];
770 trace("afifosz %d...fifooff %f...", afifosz, fifooff);
771
772 /*
773 * Page fault clock
774 */
775 t = mp->reg[MemCntl];
776 mem->trp = (t>>8)&3; /* RAS precharge time */
777 mem->trcd = (t>>10)&3; /* RAS to CAS delay */
778 mem->tcrd = (t>>12)&1; /* CAS to RAS delay */
779 mem->tras = (t>>16)&7; /* RAS low minimum pulse width */
780 pfc = mem->trp + 1 + mem->trcd + 1 + mem->tcrd;
781 trace("pfc %d...", pfc);
782
783 /*
784 * Maximum random access cycle clock.
785 */
786 ncycle = cyclesperqwordtab[memtyp > 1][vga->vmz > 1*1024*1024];
787 rcc = mem->trp + 1 + mem->tras + 1;
788 if(rcc < pfc+ncycle)
789 rcc = pfc+ncycle;
790 trace("rcc %d...", rcc);
791
792 fifoon = (rcc > floor(x)) ? rcc : floor(x);
793 fifoon += (3.0 * rcc) - 1 + pfc + ncycle;
794 trace("fifoon %f...\n", fifoon);
795 /*
796 * Now finally put the bits together.
797 * x is stored in a 14 bit field with xprec bits of integer.
798 */
799 pw = x * (1<<(14-xprec));
800 mp->reg[DspConfig] = (ulong)pw | (((lat+2)&0xF)<<16) | ((prec&7)<<20);
801
802 /*
803 * These are stored in an 11 bit field with fprec bits of integer.
804 */
805 dspon = (ushort)fifoon << (11-fprec);
806 dspoff = (ushort)fifooff << (11-fprec);
807 mp->reg[DspOnOff] = ((dspon&0x7ff) << 16) | (dspoff&0x7ff);
808 }
809
810 static void
init(Vga * vga,Ctlr * ctlr)811 init(Vga* vga, Ctlr* ctlr)
812 {
813 Mode *mode;
814 Mach64xx *mp;
815 int p, x, y;
816
817 mode = vga->mode;
818 if((mode->x > 640 || mode->y > 480) && mode->z == 1)
819 error("%s: no support for 1-bit mode other than 640x480x1\n",
820 ctlr->name);
821
822 mp = vga->private;
823 if(mode->z > 8 && mp->pci == nil)
824 error("%s: no support for >8-bit color without PCI\n",
825 ctlr->name);
826
827 /*
828 * Check for Rage chip
829 */
830 switch (mp->reg[ConfigChipId]&0xffff) {
831 case ('G'<<8)|'B': /* 4742: 264GT PRO */
832 case ('G'<<8)|'D': /* 4744: 264GT PRO */
833 case ('G'<<8)|'I': /* 4749: 264GT PRO */
834 case ('G'<<8)|'M': /* 474D: Rage XL */
835 case ('G'<<8)|'P': /* 4750: 264GT PRO */
836 case ('G'<<8)|'Q': /* 4751: 264GT PRO */
837 case ('G'<<8)|'R': /* 4752: */
838 case ('G'<<8)|'U': /* 4755: 264GT DVD */
839 case ('G'<<8)|'V': /* 4756: Rage2C */
840 case ('G'<<8)|'Z': /* 475A: Rage2C */
841 case ('V'<<8)|'U': /* 5655: 264VT3 */
842 case ('V'<<8)|'V': /* 5656: 264VT4 */
843 case ('G'<<8)|'T': /* 4754: 264GT[B] */
844 case ('V'<<8)|'T': /* 5654: 264VT/GT/VTB */
845 case ('L'<<8)|'B': /* 4C42: Rage LTPro AGP */
846 case ('L'<<8)|'I': /* 4C49: 264LT PRO */
847 case ('L'<<8)|'M': /* 4C4D: Rage Mobility */
848 case ('L'<<8)|'P': /* 4C50: 264LT PRO */
849 ctlr->flag |= Uenhanced;
850 break;
851 }
852
853 /*
854 * Always use VCLK2.
855 */
856 clock(vga, ctlr);
857 mp->pll[PLLn2] = vga->n[0];
858 mp->pll[PLLp] &= ~(0x03<<(2*2));
859 switch(vga->p[0]){
860 case 1:
861 case 3:
862 p = 0;
863 break;
864
865 case 2:
866 p = 1;
867 break;
868
869 case 4:
870 case 6:
871 p = 2;
872 break;
873
874 case 8:
875 case 12:
876 p = 3;
877 break;
878
879 default:
880 p = 3;
881 break;
882 }
883 mp->pll[PLLp] |= p<<(2*2);
884 if ((1<<p) != vga->p[0])
885 mp->pll[PLLx] |= 1<<(4+2);
886 else
887 mp->pll[PLLx] &= ~(1<<(4+2));
888 mp->reg[ClockCntl] = 2;
889
890 mp->reg[ConfigCntl] = 0;
891
892 mp->reg[CrtcGenCntl] = 0x02000000|(mp->reg[CrtcGenCntl] & ~0x01400700);
893 switch(mode->z){
894 default:
895 case 1:
896 mp->reg[CrtcGenCntl] |= 0x00000100;
897 mp->reg[DpPixWidth] = 0x00000000;
898 break;
899 case 8:
900 mp->reg[CrtcGenCntl] |= 0x01000200;
901 mp->reg[DpPixWidth] = 0x00020202;
902 break;
903 case 15:
904 mp->reg[CrtcGenCntl] |= 0x01000300;
905 mp->reg[DpPixWidth] = 0x00030303;
906 break;
907 case 16:
908 mp->reg[CrtcGenCntl] |= 0x01000400;
909 mp->reg[DpPixWidth] = 0x00040404;
910 break;
911 case 24:
912 mp->reg[CrtcGenCntl] |= 0x01000500;
913 mp->reg[DpPixWidth] = 0x00050505;
914 break;
915 case 32:
916 mp->reg[CrtcGenCntl] |= 0x01000600;
917 mp->reg[DpPixWidth] = 0x00060606;
918 break;
919 }
920
921 mp->reg[HTotalDisp] = (((mode->x>>3)-1)<<16)|((mode->ht>>3)-1);
922 mp->reg[HSyncStrtWid] = (((mode->ehs - mode->shs)>>3)<<16)
923 |((mode->shs>>3)-1);
924 if(mode->hsync == '-')
925 mp->reg[HSyncStrtWid] |= 0x00200000;
926 mp->reg[VTotalDisp] = ((mode->y-1)<<16)|(mode->vt-1);
927 mp->reg[VSyncStrtWid] = ((mode->vre - mode->vrs)<<16)|(mode->vrs-1);
928 if(mode->vsync == '-')
929 mp->reg[VSyncStrtWid] |= 0x00200000;
930 mp->reg[IntCntl] = 0;
931
932 /*
933 * This used to set it to (mode->x/(8*2))<<22 for depths < 8,
934 * but from the manual that seems wrong to me. -rsc
935 */
936 mp->reg[OffPitch] = (vga->virtx/8)<<22;
937
938 mp->reg[OvrClr] = Pblack;
939
940 if(vga->linear && mode->z != 1)
941 ctlr->flag |= Ulinear;
942
943 /*
944 * Heuristic fiddling on LT PRO.
945 * Do this before setdsp so the stretching is right.
946 */
947 if(mp->lcdon){
948 /* use non-shadowed registers */
949 mp->lcd[LCD_GenCtrl] &= ~0x00000404;
950 mp->lcd[LCD_ConfigPanel] |= 0x00004000;
951
952 mp->lcd[LCD_VertStretch] = 0;
953 y = ((mp->lcd[LCD_ExtVertStretch]>>11) & 0x7FF)+1;
954 if(mode->y < y){
955 x = (mode->y*1024)/y;
956 mp->lcd[LCD_VertStretch] = 0xC0000000|x;
957 }
958 mp->lcd[LCD_ExtVertStretch] &= ~0x00400400;
959
960 /*
961 * The x value doesn't seem to be available on all
962 * chips so intuit it from the y value which seems to
963 * be reliable.
964 */
965 mp->lcd[LCD_HorzStretch] &= ~0xC00000FF;
966 x = (mp->lcd[LCD_HorzStretch]>>20) & 0xFF;
967 if(x == 0){
968 switch(y){
969 default:
970 break;
971 case 480:
972 x = 640;
973 break;
974 case 600:
975 x = 800;
976 break;
977 case 768:
978 x = 1024;
979 break;
980 case 1024:
981 x = 1280;
982 break;
983 }
984 }
985 else
986 x = (x+1)*8;
987 if(mode->x < x){
988 x = (mode->x*4096)/x;
989 mp->lcd[LCD_HorzStretch] |= 0xC0000000|x;
990 }
991 }
992
993 if(ctlr->flag&Uenhanced)
994 setdsp(vga, ctlr);
995
996 ctlr->flag |= Finit;
997 }
998
999 static void
load(Vga * vga,Ctlr * ctlr)1000 load(Vga* vga, Ctlr* ctlr)
1001 {
1002 Mach64xx *mp;
1003 int i;
1004
1005 mp = vga->private;
1006
1007 /*
1008 * Unlock the CRTC and LCD registers.
1009 */
1010 mp->iow32(mp, CrtcGenCntl, mp->ior32(mp, CrtcGenCntl)&~0x00400000);
1011 if(mp->lcdon)
1012 lcdw32(mp, LCD_GenCtrl, mp->lcd[LCD_GenCtrl]|0x80000000);
1013
1014 /*
1015 * Always use an aperture on a 16Mb boundary.
1016 */
1017 if(ctlr->flag & Ulinear)
1018 mp->reg[ConfigCntl] = ((vga->vmb/(4*1024*1024))<<4)|0x02;
1019
1020 mp->iow32(mp, ConfigCntl, mp->reg[ConfigCntl]);
1021
1022 mp->iow32(mp, GenTestCntl, 0);
1023 mp->iow32(mp, GenTestCntl, 0x100);
1024
1025 if((ctlr->flag&Uenhanced) == 0)
1026 mp->iow32(mp, MemCntl, mp->reg[MemCntl] & ~0x70000);
1027 mp->iow32(mp, BusCntl, mp->reg[BusCntl]);
1028 mp->iow32(mp, HTotalDisp, mp->reg[HTotalDisp]);
1029 mp->iow32(mp, HSyncStrtWid, mp->reg[HSyncStrtWid]);
1030 mp->iow32(mp, VTotalDisp, mp->reg[VTotalDisp]);
1031 mp->iow32(mp, VSyncStrtWid, mp->reg[VSyncStrtWid]);
1032 mp->iow32(mp, IntCntl, mp->reg[IntCntl]);
1033 mp->iow32(mp, OffPitch, mp->reg[OffPitch]);
1034 if(mp->lcdon){
1035 for(i=0; i<Nlcd; i++)
1036 lcdw32(mp, i, mp->lcd[i]);
1037 }
1038
1039 mp->iow32(mp, GenTestCntl, mp->reg[GenTestCntl]);
1040 mp->iow32(mp, ConfigCntl, mp->reg[ConfigCntl]);
1041 mp->iow32(mp, CrtcGenCntl, mp->reg[CrtcGenCntl]);
1042 mp->iow32(mp, OvrClr, mp->reg[OvrClr]);
1043 mp->iow32(mp, OvrWidLR, mp->reg[OvrWidLR]);
1044 mp->iow32(mp, OvrWidTB, mp->reg[OvrWidTB]);
1045 if(ctlr->flag&Uenhanced){
1046 mp->iow32(mp, DacRegs, mp->reg[DacRegs]);
1047 mp->iow32(mp, DacCntl, mp->reg[DacCntl]);
1048 mp->iow32(mp, CrtcGenCntl, mp->reg[CrtcGenCntl]&~0x02000000);
1049 mp->iow32(mp, DspOnOff, mp->reg[DspOnOff]);
1050 mp->iow32(mp, DspConfig, mp->reg[DspConfig]);
1051 mp->iow32(mp, CrtcGenCntl, mp->reg[CrtcGenCntl]);
1052 pllw(mp, PLLx, mp->pll[PLLx]);
1053 }
1054 pllw(mp, PLLn2, mp->pll[PLLn2]);
1055 pllw(mp, PLLp, mp->pll[PLLp]);
1056 pllw(mp, PLLn3, mp->pll[PLLn3]);
1057
1058 mp->iow32(mp, ClockCntl, mp->reg[ClockCntl]);
1059 mp->iow32(mp, ClockCntl, 0x40|mp->reg[ClockCntl]);
1060
1061 mp->iow32(mp, DpPixWidth, mp->reg[DpPixWidth]);
1062
1063 if(vga->mode->z > 8){
1064 int sh, i;
1065 /*
1066 * We need to initialize the palette, since the DACs use it
1067 * in true color modes. First see if the card supports an
1068 * 8-bit DAC.
1069 */
1070 mp->iow32(mp, DacCntl, mp->reg[DacCntl] | 0x100);
1071 if(mp->ior32(mp, DacCntl)&0x100){
1072 /* card appears to support it */
1073 vgactlw("palettedepth", "8");
1074 mp->reg[DacCntl] |= 0x100;
1075 }
1076
1077 if(mp->reg[DacCntl] & 0x100)
1078 sh = 0; /* 8-bit DAC */
1079 else
1080 sh = 2; /* 6-bit DAC */
1081
1082 for(i=0; i<256; i++)
1083 setpalette(i, i>>sh, i>>sh, i>>sh);
1084 }
1085
1086 ctlr->flag |= Fload;
1087 }
1088
1089 static void
pixelclock(Vga * vga,Ctlr * ctlr)1090 pixelclock(Vga* vga, Ctlr* ctlr)
1091 {
1092 Mach64xx *mp;
1093 ushort table, s;
1094 int memclk, ref_freq, ref_divider, min_freq, max_freq;
1095 int feedback, nmult, pd, post, value;
1096 int clock;
1097
1098 /*
1099 * Find the pixel clock from the BIOS and current
1100 * settings. Lifted from the ATI-supplied example code.
1101 * The clocks stored in the BIOS table are in kHz/10.
1102 *
1103 * This is the clock LCDs use in vgadb to set the DSP
1104 * values.
1105 */
1106 mp = vga->private;
1107
1108 /*
1109 * GetPLLInfo()
1110 */
1111 table = *(ushort*)readbios(sizeof table, 0xc0048);
1112 trace("rom table offset %uX\n", table);
1113 table = *(ushort*)readbios(sizeof table, 0xc0000+table+16);
1114 trace("freq table offset %uX\n", table);
1115 s = *(ushort*)readbios(sizeof s, 0xc0000+table+18);
1116 memclk = s*10000;
1117 trace("memclk %ud\n", memclk);
1118 s = *(ushort*)readbios(sizeof s, 0xc0000+table+8);
1119 ref_freq = s*10000;
1120 trace("ref_freq %ud\n", ref_freq);
1121 s = *(ushort*)readbios(sizeof s, 0xc0000+table+10);
1122 ref_divider = s;
1123 trace("ref_divider %ud\n", ref_divider);
1124 s = *(ushort*)readbios(sizeof s, 0xc0000+table+2);
1125 min_freq = s*10000;
1126 trace("min_freq %ud\n", min_freq);
1127 s = *(ushort*)readbios(sizeof s, 0xc0000+table+4);
1128 max_freq = s*10000;
1129 trace("max_freq %ud\n", max_freq);
1130
1131 /*
1132 * GetDivider()
1133 */
1134 pd = mp->pll[PLLp] & 0x03;
1135 value = (mp->pll[PLLx] & 0x10)>>2;
1136 trace("pd %uX value %uX (|%d)\n", pd, value, value|pd);
1137 value |= pd;
1138 post = 0;
1139 switch(value){
1140 case 0:
1141 post = 1;
1142 break;
1143 case 1:
1144 post = 2;
1145 break;
1146 case 2:
1147 post = 4;
1148 break;
1149 case 3:
1150 post = 8;
1151 break;
1152 case 4:
1153 post = 3;
1154 break;
1155 case 5:
1156 post = 0;
1157 break;
1158 case 6:
1159 post = 6;
1160 break;
1161 case 7:
1162 post = 12;
1163 break;
1164 }
1165 trace("post = %d\n", post);
1166
1167 feedback = mp->pll[PLLn0];
1168 if(mp->pll[PLLx] & 0x08)
1169 nmult = 4;
1170 else
1171 nmult = 2;
1172
1173 clock = (ref_freq/10000)*nmult*feedback;
1174 clock /= ref_divider*post;
1175 clock *= 10000;
1176
1177 Bprint(&stdout, "%s pixel clock = %ud\n", ctlr->name, clock);
1178 }
1179
1180 static void dumpmach64bios(Mach64xx*);
1181
1182 static void
dump(Vga * vga,Ctlr * ctlr)1183 dump(Vga* vga, Ctlr* ctlr)
1184 {
1185 Mach64xx *mp;
1186 int i, m, n, p;
1187 double f;
1188 static int first = 1;
1189
1190 if((mp = vga->private) == 0)
1191 return;
1192
1193 Bprint(&stdout, "%s pci %p io %lux %s\n", ctlr->name,
1194 mp->pci, mp->io, mp->ior32 == pciior32 ? "pciregs" : "ioregs");
1195 if(mp->pci)
1196 Bprint(&stdout, "%s ccru %ux\n", ctlr->name, mp->pci->ccru);
1197 for(i = 0; i < Nreg; i++)
1198 Bprint(&stdout, "%s %-*s%.8luX\n",
1199 ctlr->name, 20, iorname[i], mp->reg[i]);
1200
1201 printitem(ctlr->name, "PLL");
1202 for(i = 0; i < Npll; i++)
1203 printreg(mp->pll[i]);
1204 Bprint(&stdout, "\n");
1205
1206 switch(mp->reg[ConfigChipId] & 0xFFFF){
1207 default:
1208 break;
1209 case ('L'<<8)|'B': /* 4C42: Rage LTPro AGP */
1210 case ('L'<<8)|'I': /* 4C49: Rage 3D LTPro */
1211 case ('L'<<8)|'M': /* 4C4D: Rage Mobility */
1212 case ('L'<<8)|'P': /* 4C50: Rage 3D LTPro */
1213 for(i = 0; i < Nlcd; i++)
1214 Bprint(&stdout, "%s %-*s%.8luX\n",
1215 ctlr->name, 20, lcdname[i], mp->lcd[i]);
1216 break;
1217 }
1218
1219 /*
1220 * (2*r*n)
1221 * f = -------
1222 * (m*p)
1223 */
1224 m = mp->pll[2];
1225 for(i = 0; i < 4; i++){
1226 n = mp->pll[7+i];
1227 p = (mp->pll[6]>>(i*2)) & 0x03;
1228 p |= (mp->pll[11]>>(2+i)) & 0x04;
1229 switch(p){
1230 case 0:
1231 case 1:
1232 case 2:
1233 case 3:
1234 p = 1<<p;
1235 break;
1236 case 4+0:
1237 p = 3;
1238 break;
1239 case 4+2:
1240 p = 6;
1241 break;
1242 case 4+3:
1243 p = 12;
1244 break;
1245
1246 default:
1247 case 4+1:
1248 p = -1;
1249 break;
1250 }
1251 if(m*p == 0)
1252 Bprint(&stdout, "unknown VCLK%d\n", i);
1253 else {
1254 f = (2.0*RefFreq*n)/(m*p) + 0.5;
1255 Bprint(&stdout, "%s VCLK%d\t%ud\n", ctlr->name, i, (int)f);
1256 }
1257 }
1258
1259 pixelclock(vga, ctlr);
1260
1261 if(first) {
1262 first = 0;
1263 dumpmach64bios(mp);
1264 }
1265 }
1266
1267 enum {
1268 ClockFixed=0,
1269 ClockIcs2595,
1270 ClockStg1703,
1271 ClockCh8398,
1272 ClockInternal,
1273 ClockAtt20c408,
1274 ClockIbmrgb514
1275 };
1276
1277 /*
1278 * mostly derived from the xfree86 probe routines.
1279 */
1280 static void
dumpmach64bios(Mach64xx * mp)1281 dumpmach64bios(Mach64xx *mp)
1282 {
1283 int i, romtable, clocktable, freqtable, lcdtable, lcdpanel;
1284 uchar bios[0x10000];
1285
1286 memmove(bios, readbios(sizeof bios, 0xC0000), sizeof bios);
1287
1288 /* find magic string */
1289 for(i=0; i<1024; i++)
1290 if(strncmp((char*)bios+i, " 761295520", 10) == 0)
1291 break;
1292
1293 if(i==1024) {
1294 Bprint(&stdout, "no ATI bios found\n");
1295 return;
1296 }
1297
1298 /* this is horribly endian dependent. sorry. */
1299 romtable = *(ushort*)(bios+0x48);
1300 if(romtable+0x12 > sizeof(bios)) {
1301 Bprint(&stdout, "couldn't find ATI rom table\n");
1302 return;
1303 }
1304
1305 clocktable = *(ushort*)(bios+romtable+0x10);
1306 if(clocktable+0x0C > sizeof(bios)) {
1307 Bprint(&stdout, "couldn't find ATI clock table\n");
1308 return;
1309 }
1310
1311 freqtable = *(ushort*)(bios+clocktable-2);
1312 if(freqtable+0x20 > sizeof(bios)) {
1313 Bprint(&stdout, "couldn't find ATI frequency table\n");
1314 return;
1315 }
1316
1317 Bprint(&stdout, "ATI BIOS rom 0x%x freq 0x%x clock 0x%x\n", romtable, freqtable, clocktable);
1318 Bprint(&stdout, "clocks:");
1319 for(i=0; i<16; i++)
1320 Bprint(&stdout, " %d", *(ushort*)(bios+freqtable+2*i));
1321 Bprint(&stdout, "\n");
1322
1323 Bprint(&stdout, "programmable clock: %d\n", bios[clocktable]);
1324 Bprint(&stdout, "clock to program: %d\n", bios[clocktable+6]);
1325
1326 if(*(ushort*)(bios+clocktable+8) != 1430) {
1327 Bprint(&stdout, "reference numerator: %d\n", *(ushort*)(bios+clocktable+8)*10);
1328 Bprint(&stdout, "reference denominator: 1\n");
1329 } else {
1330 Bprint(&stdout, "default reference numerator: 157500\n");
1331 Bprint(&stdout, "default reference denominator: 11\n");
1332 }
1333
1334 switch(bios[clocktable]) {
1335 case ClockIcs2595:
1336 Bprint(&stdout, "ics2595\n");
1337 Bprint(&stdout, "reference divider: %d\n", *(ushort*)(bios+clocktable+0x0A));
1338 break;
1339 case ClockStg1703:
1340 Bprint(&stdout, "stg1703\n");
1341 break;
1342 case ClockCh8398:
1343 Bprint(&stdout, "ch8398\n");
1344 break;
1345 case ClockInternal:
1346 Bprint(&stdout, "internal clock\n");
1347 Bprint(&stdout, "reference divider in plls\n");
1348 break;
1349 case ClockAtt20c408:
1350 Bprint(&stdout, "att 20c408\n");
1351 break;
1352 case ClockIbmrgb514:
1353 Bprint(&stdout, "ibm rgb514\n");
1354 Bprint(&stdout, "clock to program = 7\n");
1355 break;
1356 default:
1357 Bprint(&stdout, "unknown clock\n");
1358 break;
1359 }
1360
1361 USED(mp);
1362 if(1 || mp->lcdpanelid) {
1363 lcdtable = *(ushort*)(bios+0x78);
1364 if(lcdtable+5 > sizeof bios || lcdtable+bios[lcdtable+5] > sizeof bios) {
1365 Bprint(&stdout, "can't find lcd bios table\n");
1366 goto NoLcd;
1367 }
1368
1369 lcdpanel = *(ushort*)(bios+lcdtable+0x0A);
1370 if(lcdpanel+0x1D > sizeof bios /*|| bios[lcdpanel] != mp->lcdpanelid*/) {
1371 Bprint(&stdout, "can't find lcd bios table0\n");
1372 goto NoLcd;
1373 }
1374
1375 Bprint(&stdout, "panelid %d x %d y %d\n", bios[lcdpanel], *(ushort*)(bios+lcdpanel+0x19), *(ushort*)(bios+lcdpanel+0x1B));
1376 }
1377 NoLcd:;
1378 }
1379
1380 Ctlr mach64xx = {
1381 "mach64xx", /* name */
1382 snarf, /* snarf */
1383 0, /* options */
1384 init, /* init */
1385 load, /* load */
1386 dump, /* dump */
1387 };
1388
1389 Ctlr mach64xxhwgc = {
1390 "mach64xxhwgc", /* name */
1391 0, /* snarf */
1392 0, /* options */
1393 0, /* init */
1394 0, /* load */
1395 0, /* dump */
1396 };
1397
1398
1399