]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/aux/vga/mach64xx.c
abaco: cleanup, handle image/x-icon, don't use backspace as a hotkey, and remove...
[plan9front.git] / sys / src / cmd / aux / vga / mach64xx.c
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
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
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
282 pciior32(Mach64xx* mp, int r)
283 {
284         return inportl((pciregs[r]<<2)+mp->io);
285 }
286
287 static void
288 pciiow32(Mach64xx* mp, int r, ulong l)
289 {
290         outportl((pciregs[r]<<2)+mp->io, l);
291 }
292
293 static uchar
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
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
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
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
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
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
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
477 options(Vga*, Ctlr* ctlr)
478 {
479         ctlr->flag |= Hlinear|Foptions;
480 }
481
482 static void
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
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
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
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
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
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 
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