1219b2ee8SDavid du Colombier #include <u.h>
2219b2ee8SDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <bio.h>
4219b2ee8SDavid du Colombier
59a747e4fSDavid du Colombier #include "pci.h"
6219b2ee8SDavid du Colombier #include "vga.h"
7219b2ee8SDavid du Colombier
8219b2ee8SDavid du Colombier /*
9219b2ee8SDavid du Colombier * Cirrus Logic True Color VGA Family - CL-GD542X.
10219b2ee8SDavid du Colombier * Also works for Alpine VGA Family - CL-GD543X.
11219b2ee8SDavid du Colombier * Just the basics. BUGS:
12219b2ee8SDavid du Colombier * the added capabilities of the 543X aren't used.
13219b2ee8SDavid du Colombier */
14219b2ee8SDavid du Colombier
15219b2ee8SDavid du Colombier typedef struct {
16219b2ee8SDavid du Colombier uchar id; /* Id */
17219b2ee8SDavid du Colombier ulong vclk; /* Maximum dot clock */
18219b2ee8SDavid du Colombier } Gd542x;
19219b2ee8SDavid du Colombier
20219b2ee8SDavid du Colombier static Gd542x family[] = {
21219b2ee8SDavid du Colombier { 0x88, 75000000, }, /* CL-GD5420 */
22219b2ee8SDavid du Colombier { 0x8C, 80000000, }, /* CL-GD5422 */
23219b2ee8SDavid du Colombier { 0x94, 80000000, }, /* CL-GD5424 */
24219b2ee8SDavid du Colombier { 0x90, 80000000, }, /* CL-GD5426 */
25219b2ee8SDavid du Colombier { 0x98, 80000000, }, /* CL-GD5428 */
26219b2ee8SDavid du Colombier { 0x9C, 86000000, }, /* CL-GD5429 */
27219b2ee8SDavid du Colombier { 0xA0, 86000000, }, /* CL-GD5430 */
2859cc4ca5SDavid du Colombier { 0xA8, 86000000, }, /* CL-GD5434 */
297dd7cddfSDavid du Colombier
307dd7cddfSDavid du Colombier { 0xAC, 135000000, }, /* CL-GD5436 */
317dd7cddfSDavid du Colombier { 0xB8, 135000000, }, /* CL-GD5446 */
32*76f6a3b8SDavid du Colombier { 0xBC, 135000000, }, /* CL-GD5480 */
3359cc4ca5SDavid du Colombier
3459cc4ca5SDavid du Colombier { 0x30, 80000000, }, /* CL-GD7543 */
3559cc4ca5SDavid du Colombier
36219b2ee8SDavid du Colombier { 0x00, },
37219b2ee8SDavid du Colombier };
38219b2ee8SDavid du Colombier
39219b2ee8SDavid du Colombier static Gd542x*
identify(Vga * vga,Ctlr * ctlr)407dd7cddfSDavid du Colombier identify(Vga* vga, Ctlr* ctlr)
41219b2ee8SDavid du Colombier {
42219b2ee8SDavid du Colombier Gd542x *gd542x;
43219b2ee8SDavid du Colombier uchar id;
44219b2ee8SDavid du Colombier
45219b2ee8SDavid du Colombier id = vga->crt[0x27] & ~0x03;
46219b2ee8SDavid du Colombier for(gd542x = &family[0]; gd542x->id; gd542x++){
47219b2ee8SDavid du Colombier if(gd542x->id == id)
48219b2ee8SDavid du Colombier return gd542x;
49219b2ee8SDavid du Colombier }
50219b2ee8SDavid du Colombier
517dd7cddfSDavid du Colombier error("%s: unknown chip id - 0x%2.2X\n", ctlr->name, vga->crt[0x27]);
52219b2ee8SDavid du Colombier return 0;
53219b2ee8SDavid du Colombier }
54219b2ee8SDavid du Colombier
55219b2ee8SDavid du Colombier static void
snarf(Vga * vga,Ctlr * ctlr)56219b2ee8SDavid du Colombier snarf(Vga* vga, Ctlr* ctlr)
57219b2ee8SDavid du Colombier {
58219b2ee8SDavid du Colombier int i;
597dd7cddfSDavid du Colombier Gd542x *gd542x;
60219b2ee8SDavid du Colombier
617dd7cddfSDavid du Colombier /*
627dd7cddfSDavid du Colombier * Unlock extended registers.
637dd7cddfSDavid du Colombier */
647dd7cddfSDavid du Colombier vgaxo(Seqx, 0x06, 0x12);
65219b2ee8SDavid du Colombier
66219b2ee8SDavid du Colombier /*
67219b2ee8SDavid du Colombier * Save all the registers, even though we'll only
68219b2ee8SDavid du Colombier * change a handful.
69219b2ee8SDavid du Colombier */
70219b2ee8SDavid du Colombier for(i = 0x06; i < 0x20; i++)
71219b2ee8SDavid du Colombier vga->sequencer[i] = vgaxi(Seqx, i);
72219b2ee8SDavid du Colombier
73219b2ee8SDavid du Colombier for(i = 0x09; i < 0x3A; i++)
74219b2ee8SDavid du Colombier vga->graphics[i] = vgaxi(Grx, i);
75219b2ee8SDavid du Colombier
76219b2ee8SDavid du Colombier for(i = 0x19; i < 0x1E; i++)
77219b2ee8SDavid du Colombier vga->crt[i] = vgaxi(Crtx, i);
78219b2ee8SDavid du Colombier
79219b2ee8SDavid du Colombier vga->crt[0x27] = vgaxi(Crtx, 0x27);
80219b2ee8SDavid du Colombier
81219b2ee8SDavid du Colombier /*
82219b2ee8SDavid du Colombier * Hack for Hidden DAC Register. Do 4 dummy reads
83219b2ee8SDavid du Colombier * of Pixmask first.
84219b2ee8SDavid du Colombier */
85219b2ee8SDavid du Colombier for(i = 0; i < 4; i++)
86219b2ee8SDavid du Colombier vgai(Pixmask);
87219b2ee8SDavid du Colombier vga->crt[0x28] = vgai(Pixmask);
88219b2ee8SDavid du Colombier
897dd7cddfSDavid du Colombier i = 0;
907dd7cddfSDavid du Colombier switch(vga->crt[0x27] & ~0x03){
917dd7cddfSDavid du Colombier
927dd7cddfSDavid du Colombier case 0x88: /* CL-GD5420 */
937dd7cddfSDavid du Colombier case 0x8C: /* CL-GD5422 */
947dd7cddfSDavid du Colombier case 0x94: /* CL-GD5424 */
957dd7cddfSDavid du Colombier case 0x80: /* CL-GD5425 */
967dd7cddfSDavid du Colombier case 0x90: /* CL-GD5426 */
977dd7cddfSDavid du Colombier case 0x98: /* CL-GD5427 */
987dd7cddfSDavid du Colombier case 0x9C: /* CL-GD5429 */
99219b2ee8SDavid du Colombier /*
1007dd7cddfSDavid du Colombier * The BIOS leaves the memory size in Seq0A, bits 4 and 3.
101219b2ee8SDavid du Colombier * See Technical Reference Manual Appendix E1, Section 1.3.2.
1027dd7cddfSDavid du Colombier *
1037dd7cddfSDavid du Colombier * The storage area for the 64x64 cursors is the last 16Kb of
1047dd7cddfSDavid du Colombier * display memory.
105219b2ee8SDavid du Colombier */
1067dd7cddfSDavid du Colombier i = (vga->sequencer[0x0A]>>3) & 0x03;
107219b2ee8SDavid du Colombier break;
108219b2ee8SDavid du Colombier
1097dd7cddfSDavid du Colombier case 0xA0: /* CL-GD5430 */
1107dd7cddfSDavid du Colombier case 0xA8: /* CL-GD5434 */
1117dd7cddfSDavid du Colombier case 0xAC: /* CL-GD5436 */
1127dd7cddfSDavid du Colombier case 0xB8: /* CL-GD5446 */
11359cc4ca5SDavid du Colombier case 0x30: /* CL-GD7543 */
1147dd7cddfSDavid du Colombier /*
1157dd7cddfSDavid du Colombier * Attempt to intuit the memory size from the DRAM control
1167dd7cddfSDavid du Colombier * register. Minimum is 512KB.
1177dd7cddfSDavid du Colombier * If DRAM bank switching is on then there's double.
1187dd7cddfSDavid du Colombier */
1197dd7cddfSDavid du Colombier i = (vga->sequencer[0x0F]>>3) & 0x03;
1207dd7cddfSDavid du Colombier if(vga->sequencer[0x0F] & 0x80)
1217dd7cddfSDavid du Colombier i++;
1227dd7cddfSDavid du Colombier
1237dd7cddfSDavid du Colombier /*
1247dd7cddfSDavid du Colombier * If it's a PCI card, can do linear.
1257dd7cddfSDavid du Colombier * Most of the Cirrus chips can do linear addressing with
1267dd7cddfSDavid du Colombier * all the different buses, but it can get messy. It's easy
1277dd7cddfSDavid du Colombier * to cut PCI on the CLGD543x chips out of the pack.
1287dd7cddfSDavid du Colombier */
1297dd7cddfSDavid du Colombier if(((vga->sequencer[0x17]>>3) & 0x07) == 0x04)
1307dd7cddfSDavid du Colombier ctlr->flag |= Hlinear;
131219b2ee8SDavid du Colombier break;
132*76f6a3b8SDavid du Colombier case 0xBC: /* CL-GD5480 */
133*76f6a3b8SDavid du Colombier i = 2; /* 1024 = 256<<2 */
134*76f6a3b8SDavid du Colombier if((vga->sequencer[0x0F] & 0x18) == 0x18){
135*76f6a3b8SDavid du Colombier i <<= 1; /* 2048 = 256<<3 */
136*76f6a3b8SDavid du Colombier if(vga->sequencer[0x0F] & 0x80)
137*76f6a3b8SDavid du Colombier i <<= 2; /* 2048 = 256<<4 */
138*76f6a3b8SDavid du Colombier }
139*76f6a3b8SDavid du Colombier if(vga->sequencer[0x17] & 0x80)
140*76f6a3b8SDavid du Colombier i <<= 1;
141*76f6a3b8SDavid du Colombier ctlr->flag |= Hlinear;
142*76f6a3b8SDavid du Colombier break;
143219b2ee8SDavid du Colombier
1447dd7cddfSDavid du Colombier default: /* uh, ah dunno */
145219b2ee8SDavid du Colombier break;
146219b2ee8SDavid du Colombier }
147219b2ee8SDavid du Colombier
1487dd7cddfSDavid du Colombier if(vga->linear && (ctlr->flag & Hlinear)){
1497dd7cddfSDavid du Colombier vga->vmz = 16*1024*1024;
1507dd7cddfSDavid du Colombier vga->vma = 16*1024*1024;
1517dd7cddfSDavid du Colombier ctlr->flag |= Ulinear;
1527dd7cddfSDavid du Colombier }
1537dd7cddfSDavid du Colombier else
1547dd7cddfSDavid du Colombier vga->vmz = (256<<i)*1024;
1557dd7cddfSDavid du Colombier
1567dd7cddfSDavid du Colombier gd542x = identify(vga, ctlr);
1577dd7cddfSDavid du Colombier if(vga->f[1] == 0 || vga->f[1] > gd542x->vclk)
1587dd7cddfSDavid du Colombier vga->f[1] = gd542x->vclk;
159219b2ee8SDavid du Colombier ctlr->flag |= Fsnarf;
160219b2ee8SDavid du Colombier }
161219b2ee8SDavid du Colombier
1627dd7cddfSDavid du Colombier void
clgd54xxclock(Vga * vga,Ctlr * ctlr)1637dd7cddfSDavid du Colombier clgd54xxclock(Vga* vga, Ctlr* ctlr)
164219b2ee8SDavid du Colombier {
165219b2ee8SDavid du Colombier int f;
166219b2ee8SDavid du Colombier ulong d, dmin, fmin, n, nmin, p;
167219b2ee8SDavid du Colombier
1687dd7cddfSDavid du Colombier trace("%s->init->clgd54xxclock\n", ctlr->name);
169219b2ee8SDavid du Colombier
170219b2ee8SDavid du Colombier /*
171219b2ee8SDavid du Colombier * Athough the Technical Reference Manual says only a handful
172219b2ee8SDavid du Colombier * of frequencies are tested, it also gives the following formula
173219b2ee8SDavid du Colombier * which can be used to generate any frequency within spec.,
174219b2ee8SDavid du Colombier * including the tested ones.
175219b2ee8SDavid du Colombier *
176219b2ee8SDavid du Colombier * Numerator is 7-bits, denominator 5-bits.
177219b2ee8SDavid du Colombier * Guess from the Technical Reference Manual that
178219b2ee8SDavid du Colombier * The post divisor is 1 for vclk<40MHz.
179219b2ee8SDavid du Colombier *
180219b2ee8SDavid du Colombier * Look for values of n and d and p that give
181219b2ee8SDavid du Colombier * the least error for
1827dd7cddfSDavid du Colombier * vclk = (RefFreq*n)/(d*(1+p));
183219b2ee8SDavid du Colombier *
184219b2ee8SDavid du Colombier * There's nothing like brute force and ignorance.
185219b2ee8SDavid du Colombier */
1867dd7cddfSDavid du Colombier fmin = vga->f[0];
187219b2ee8SDavid du Colombier nmin = 69;
188219b2ee8SDavid du Colombier dmin = 24;
1897dd7cddfSDavid du Colombier if(vga->f[0] >= 40000000)
190219b2ee8SDavid du Colombier p = 0;
191219b2ee8SDavid du Colombier else
192219b2ee8SDavid du Colombier p = 1;
193219b2ee8SDavid du Colombier for(n = 1; n < 128; n++){
194219b2ee8SDavid du Colombier for(d = 1; d < 32; d++){
1957dd7cddfSDavid du Colombier f = vga->f[0] - (RefFreq*n)/(d*(1+p));
196219b2ee8SDavid du Colombier if(f < 0)
197219b2ee8SDavid du Colombier f = -f;
198219b2ee8SDavid du Colombier if(f <= fmin){
199219b2ee8SDavid du Colombier fmin = f;
200219b2ee8SDavid du Colombier nmin = n;
201219b2ee8SDavid du Colombier dmin = d;
202219b2ee8SDavid du Colombier }
203219b2ee8SDavid du Colombier }
204219b2ee8SDavid du Colombier }
205219b2ee8SDavid du Colombier
2067dd7cddfSDavid du Colombier vga->f[0] = (RefFreq*nmin)/(dmin*(1+p));
2077dd7cddfSDavid du Colombier vga->d[0] = dmin;
2087dd7cddfSDavid du Colombier vga->n[0] = nmin;
2097dd7cddfSDavid du Colombier vga->p[0] = p;
210219b2ee8SDavid du Colombier }
211219b2ee8SDavid du Colombier
212219b2ee8SDavid du Colombier void
init(Vga * vga,Ctlr * ctlr)213219b2ee8SDavid du Colombier init(Vga* vga, Ctlr* ctlr)
214219b2ee8SDavid du Colombier {
215219b2ee8SDavid du Colombier Mode *mode;
216219b2ee8SDavid du Colombier Gd542x *gd542x;
217219b2ee8SDavid du Colombier ushort x;
218219b2ee8SDavid du Colombier
219219b2ee8SDavid du Colombier mode = vga->mode;
2207dd7cddfSDavid du Colombier gd542x = identify(vga, ctlr);
221219b2ee8SDavid du Colombier
2227dd7cddfSDavid du Colombier if(vga->f[0] == 0)
2237dd7cddfSDavid du Colombier vga->f[0] = vga->mode->frequency;
2247dd7cddfSDavid du Colombier if(vga->f[0] > gd542x->vclk)
2257dd7cddfSDavid du Colombier error("%s: pclk %lud too high (> %lud)\n",
2267dd7cddfSDavid du Colombier ctlr->name, vga->f[0], gd542x->vclk);
2277dd7cddfSDavid du Colombier
2287dd7cddfSDavid du Colombier if(mode->z > 8)
2297dd7cddfSDavid du Colombier error("%s: depth %d not supported\n", ctlr->name, mode->z);
230219b2ee8SDavid du Colombier
231219b2ee8SDavid du Colombier /*
232219b2ee8SDavid du Colombier * VCLK3
233219b2ee8SDavid du Colombier */
2347dd7cddfSDavid du Colombier clgd54xxclock(vga, ctlr);
235219b2ee8SDavid du Colombier vga->misc |= 0x0C;
2367dd7cddfSDavid du Colombier vga->sequencer[0x0E] = vga->n[0];
2377dd7cddfSDavid du Colombier vga->sequencer[0x1E] = (vga->d[0]<<1)|vga->p[0];
238219b2ee8SDavid du Colombier
239219b2ee8SDavid du Colombier vga->sequencer[0x07] = 0x00;
240219b2ee8SDavid du Colombier if(mode->z == 8)
241219b2ee8SDavid du Colombier vga->sequencer[0x07] |= 0x01;
242219b2ee8SDavid du Colombier
2437dd7cddfSDavid du Colombier if(vga->f[0] >= 42000000)
244219b2ee8SDavid du Colombier vga->sequencer[0x0F] |= 0x20;
245219b2ee8SDavid du Colombier else
246219b2ee8SDavid du Colombier vga->sequencer[0x0F] &= ~0x20;
247219b2ee8SDavid du Colombier
248219b2ee8SDavid du Colombier vga->sequencer[0x16] = (vga->sequencer[0x16] & 0xF0)|0x08;
249219b2ee8SDavid du Colombier
250219b2ee8SDavid du Colombier /*
251219b2ee8SDavid du Colombier * Overflow bits.
252219b2ee8SDavid du Colombier */
253219b2ee8SDavid du Colombier vga->crt[0x1A] = 0x00;
254219b2ee8SDavid du Colombier x = mode->ehb>>3;
255219b2ee8SDavid du Colombier if(x & 0x40)
256219b2ee8SDavid du Colombier vga->crt[0x1A] |= 0x10;
257219b2ee8SDavid du Colombier if(x & 0x80)
258219b2ee8SDavid du Colombier vga->crt[0x1A] |= 0x20;
259219b2ee8SDavid du Colombier if(vga->crt[0x16] & 0x100)
260219b2ee8SDavid du Colombier vga->crt[0x1A] |= 0x40;
261219b2ee8SDavid du Colombier if(vga->crt[0x16] & 0x200)
262219b2ee8SDavid du Colombier vga->crt[0x1A] |= 0x80;
263219b2ee8SDavid du Colombier vga->crt[0x1B] = 0x22;
264219b2ee8SDavid du Colombier if(vga->crt[0x13] & 0x100)
265219b2ee8SDavid du Colombier vga->crt[0x1B] |= 0x10;
266219b2ee8SDavid du Colombier
267219b2ee8SDavid du Colombier vga->graphics[0x0B] = 0x00;
2687dd7cddfSDavid du Colombier if(vga->vmz > 1024*1024)
2697dd7cddfSDavid du Colombier vga->graphics[0x0B] |= 0x20;
270219b2ee8SDavid du Colombier
271219b2ee8SDavid du Colombier if(mode->interlace == 'v'){
272219b2ee8SDavid du Colombier vga->crt[0x19] = vga->crt[0x00]/2;
273219b2ee8SDavid du Colombier vga->crt[0x1A] |= 0x01;
274219b2ee8SDavid du Colombier }
275219b2ee8SDavid du Colombier }
276219b2ee8SDavid du Colombier
277219b2ee8SDavid du Colombier static void
load(Vga * vga,Ctlr * ctlr)278219b2ee8SDavid du Colombier load(Vga* vga, Ctlr* ctlr)
279219b2ee8SDavid du Colombier {
280219b2ee8SDavid du Colombier vgaxo(Seqx, 0x0E, vga->sequencer[0x0E]);
281219b2ee8SDavid du Colombier vgaxo(Seqx, 0x1E, vga->sequencer[0x1E]);
2827dd7cddfSDavid du Colombier if(ctlr->flag & Ulinear)
2837dd7cddfSDavid du Colombier vga->sequencer[0x07] |= 0xE0;
284219b2ee8SDavid du Colombier vgaxo(Seqx, 0x07, vga->sequencer[0x07]);
285219b2ee8SDavid du Colombier vgaxo(Seqx, 0x0F, vga->sequencer[0x0F]);
286219b2ee8SDavid du Colombier vgaxo(Seqx, 0x16, vga->sequencer[0x16]);
287219b2ee8SDavid du Colombier
288219b2ee8SDavid du Colombier if(vga->mode->interlace == 'v')
289219b2ee8SDavid du Colombier vgaxo(Crtx, 0x19, vga->crt[0x19]);
290219b2ee8SDavid du Colombier vgaxo(Crtx, 0x1A, vga->crt[0x1A]);
291219b2ee8SDavid du Colombier vgaxo(Crtx, 0x1B, vga->crt[0x1B]);
292219b2ee8SDavid du Colombier
293219b2ee8SDavid du Colombier vgaxo(Grx, 0x0B, vga->graphics[0x0B]);
294219b2ee8SDavid du Colombier }
295219b2ee8SDavid du Colombier
296219b2ee8SDavid du Colombier static void
dump(Vga * vga,Ctlr * ctlr)297219b2ee8SDavid du Colombier dump(Vga* vga, Ctlr* ctlr)
298219b2ee8SDavid du Colombier {
299219b2ee8SDavid du Colombier int i;
300219b2ee8SDavid du Colombier char *name;
301219b2ee8SDavid du Colombier
302219b2ee8SDavid du Colombier name = ctlr->name;
303219b2ee8SDavid du Colombier
304219b2ee8SDavid du Colombier printitem(name, "Seq06");
305219b2ee8SDavid du Colombier for(i = 0x06; i < 0x20; i++)
306219b2ee8SDavid du Colombier printreg(vga->sequencer[i]);
307219b2ee8SDavid du Colombier
308219b2ee8SDavid du Colombier printitem(name, "Crt19");
309219b2ee8SDavid du Colombier for(i = 0x19; i < 0x1E; i++)
310219b2ee8SDavid du Colombier printreg(vga->crt[i]);
311219b2ee8SDavid du Colombier
312219b2ee8SDavid du Colombier printitem(name, "Gr09");
313219b2ee8SDavid du Colombier for(i = 0x09; i < 0x3A; i++)
314219b2ee8SDavid du Colombier printreg(vga->graphics[i]);
315219b2ee8SDavid du Colombier
316219b2ee8SDavid du Colombier printitem(name, "Id Hdr");
317219b2ee8SDavid du Colombier printreg(vga->crt[0x27]);
318219b2ee8SDavid du Colombier printreg(vga->crt[0x28]);
319219b2ee8SDavid du Colombier }
320219b2ee8SDavid du Colombier
321219b2ee8SDavid du Colombier Ctlr clgd542x = {
322219b2ee8SDavid du Colombier "clgd542x", /* name */
323219b2ee8SDavid du Colombier snarf, /* snarf */
324219b2ee8SDavid du Colombier 0, /* options */
325219b2ee8SDavid du Colombier init, /* init */
326219b2ee8SDavid du Colombier load, /* load */
327219b2ee8SDavid du Colombier dump, /* dump */
328219b2ee8SDavid du Colombier };
3297dd7cddfSDavid du Colombier
3307dd7cddfSDavid du Colombier Ctlr clgd542xhwgc = {
3317dd7cddfSDavid du Colombier "clgd542xhwgc", /* name */
3327dd7cddfSDavid du Colombier 0, /* snarf */
3337dd7cddfSDavid du Colombier 0, /* options */
3347dd7cddfSDavid du Colombier 0, /* init */
3357dd7cddfSDavid du Colombier 0, /* load */
3367dd7cddfSDavid du Colombier 0, /* dump */
3377dd7cddfSDavid du Colombier };
338