xref: /plan9/sys/src/cmd/aux/vga/mach64.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
1 #include <u.h>
2 #include <libc.h>
3 
4 #include "vga.h"
5 
6 /*
7  * ATI Mach64. Some hope. Kind of like a Mach32.
8  * No support for accelerator so can only do up to 1024x768.
9  *
10  * All ATI Extended Registers are addressed using the modified index
11  *	index = (0x02<<6)|(index & 0x3F);
12  * so registers 0x00->0x3F map to 0x80->0xBF, but we will only ever
13  * look at a few in the range 0xA0->0xBF. In this way we can stash
14  * them in the vga->crt[] array.
15  */
16 enum {
17 	Configcntl	= 0x6AEC,	/* Configuration control */
18 	Configstat	= 0x72EC,	/* Configuration status */
19 	Memcntl		= 0x52EC,	/* Memory control */
20 	Scratch1	= 0x46EC,	/* Scratch Register (BIOS info) */
21 };
22 
23 typedef struct {
24 	ulong	configcntl;
25 	ulong	configstat;
26 	ulong	memcntl;
27 	ulong	scratch1;
28 } Mach64;
29 
30 /*
31  * There are a number of possible clock generator chips for these
32  * boards. We can divide any frequency by 2 (bit<6> of b8).
33  */
34 typedef struct {
35 	ulong	frequency;
36 	uchar	be;			/* <4> - bit<3> of frequency index */
37 	uchar	b9;			/* <1> - bit<2> of frequency index */
38 	uchar	genmo;			/* <3:2> - bits <1:0> of frequency index */
39 } Pclk;
40 
41 enum {
42 	Npclkx		= 16,		/* number of clock entries per table */
43 };
44 
45 /*
46  * ATI18811-0
47  */
48 Pclk ati188110[Npclkx] = {
49 	{  42950000, 0x00, 0x00, 0x00 },
50 	{  48770000, 0x00, 0x00, 0x04 },
51 	{  92400000, 0x00, 0x00, 0x08 },
52 	{  36000000, 0x00, 0x00, 0x0C },
53 	{  50350000, 0x00, 0x02, 0x00 },
54 	{  56640000, 0x00, 0x02, 0x04 },
55 	{ 	  0, 0x00, 0x02, 0x08 },
56 	{  44900000, 0x00, 0x02, 0x0C },
57 	{  30240000, 0x10, 0x00, 0x00 },
58 	{  32000000, 0x10, 0x00, 0x04 },
59 	{ 110000000, 0x10, 0x00, 0x08 },
60 	{  80000000, 0x10, 0x00, 0x0C },
61 	{  39910000, 0x10, 0x02, 0x00 },
62 	{  44900000, 0x10, 0x02, 0x04 },
63 	{  75000000, 0x10, 0x02, 0x08 },
64 	{  65000000, 0x10, 0x02, 0x0C },
65 };
66 
67 /*
68  * ATI18811-1, ATI18811-2
69  * PCLK_TABLE = 0 in Mach64 speak.
70  */
71 Pclk ati188111[Npclkx] = {
72 	{ 100000000, 0x00, 0x00, 0x00 },
73 	{ 126000000, 0x00, 0x00, 0x04 },
74 	{  92400000, 0x00, 0x00, 0x08 },
75 	{  36000000, 0x00, 0x00, 0x0C },
76 	{  50350000, 0x00, 0x02, 0x00 },
77 	{  56640000, 0x00, 0x02, 0x04 },
78 	{ 	  0, 0x00, 0x02, 0x08 },
79 	{  44900000, 0x00, 0x02, 0x0C },
80 	{ 135000000, 0x10, 0x00, 0x00 },
81 	{  32000000, 0x10, 0x00, 0x04 },
82 	{ 110000000, 0x10, 0x00, 0x08 },
83 	{  80000000, 0x10, 0x00, 0x0C },
84 	{  39910000, 0x10, 0x02, 0x00 },
85 	{  44900000, 0x10, 0x02, 0x04 },
86 	{  75000000, 0x10, 0x02, 0x08 },
87 	{  65000000, 0x10, 0x02, 0x0C },
88 };
89 
90 /*
91  * ATI18818
92  * The first four entries are programmable and the default
93  * settings are either those below or those below divided by 2
94  * (PCLK_TABLE = 1 and PCLK_TABLE = 2 respectively in Mach64
95  * speak).
96  */
97 Pclk ati18818[Npclkx] = {
98 	{  50350000, 0x00, 0x00, 0x00 },
99 	{  56640000, 0x00, 0x00, 0x04 },
100 	{  63000000, 0x00, 0x00, 0x08 },
101 	{  72000000, 0x00, 0x00, 0x0C },
102 	{  40000000, 0x00, 0x02, 0x00 },
103 	{  44900000, 0x00, 0x02, 0x04 },
104 	{  49500000, 0x00, 0x02, 0x08 },
105 	{  50000000, 0x00, 0x02, 0x0C },
106 	{ 	  0, 0x10, 0x00, 0x00 },
107 	{ 110000000, 0x10, 0x00, 0x04 },
108 	{ 126000000, 0x10, 0x00, 0x08 },
109 	{ 135000000, 0x10, 0x00, 0x0C },
110 	{ 	  0, 0x10, 0x02, 0x00 },
111 	{  80000000, 0x10, 0x02, 0x04 },
112 	{  75000000, 0x10, 0x02, 0x08 },
113 	{  65000000, 0x10, 0x02, 0x0C },
114 };
115 
116 static Pclk *pclkp;			/* which clock chip we are using */
117 static ulong atix;			/* index to extended regsiters */
118 
119 static uchar
120 atixi(uchar index)
121 {
122 	outportb(atix, index);
123 	return inportb(atix+1);
124 }
125 
126 static void
127 atixo(uchar index, uchar data)
128 {
129 	outportw(atix, (data<<8)|index);
130 }
131 
132 static void
133 atixinit(Vga *vga, Ctlr *ctlr)
134 {
135 	uchar b;
136 	Mach64 *mach64;
137 
138 	/*
139 	 * Set the I/O address and offset for the ATI
140 	 * extended registers to something we know about.
141 	 */
142 	if(atix == 0){
143 		outportw(Grx, (0xCE<<8)|0x50);
144 		outportw(Grx, (0x81<<8)|0x51);
145 		atix = 0x1CE;
146 	}
147 	verbose("%s: atix=0x%lux\n", ctlr->name, atix);
148 
149 	/*
150 	 * Unlock the ATI Extended Registers.
151 	 * We leave them unlocked from now on.
152 	 * Why does this chip have so many
153 	 * lock bits?
154 	 */
155 	if((b = atixi(0xB8)) & 0x3F)
156 		atixo(0xB8, b & 0xC0);
157 	b = atixi(0xAB);
158 	atixo(0xAB, b & ~0x18);
159 	atixo(0xB4, 0x00);
160 	b = atixi(0xB9);
161 	atixo(0xB9, b & ~0x80);
162 	b = atixi(0xBE);
163 	atixo(0xBE, b|0x09);
164 
165 	if(vga->private == 0)
166 		vga->private = alloc(sizeof(mach64));
167 }
168 
169 static void
170 snarf(Vga *vga, Ctlr *ctlr)
171 {
172 	int i;
173 	Mach64 *mach64;
174 
175 	verbose("%s->snarf\n", ctlr->name);
176 
177 	atixinit(vga, ctlr);
178 	for(i = 0xA0; i < 0xC0; i++)
179 		vga->crt[i] = atixi(i);
180 
181 	mach64 = vga->private;
182 	mach64->configcntl = inportl(Configcntl);
183 	mach64->configstat = inportl(Configstat);
184 	mach64->memcntl = inportl(Memcntl);
185 	mach64->scratch1 = inportl(Scratch1);
186 
187 	/*
188 	 * Memory size.
189 	 */
190 	switch(mach64->memcntl & 0x07){
191 
192 	case 0:
193 		vga->vmb = 512*1024;
194 		break;
195 
196 	case 1:
197 		vga->vmb = 1024*1024;
198 		break;
199 
200 	case 2:
201 		vga->vmb = 2*1024*1024;
202 		break;
203 
204 	case 3:
205 		vga->vmb = 4*1024*1024;
206 		break;
207 
208 	case 4:
209 		vga->vmb = 6*1024*1024;
210 		break;
211 
212 	case 5:
213 		vga->vmb = 8*1024*1024;
214 		break;
215 	}
216 
217 	ctlr->flag |= Fsnarf;
218 }
219 
220 static void
221 init(Vga *vga, Ctlr *ctlr)
222 {
223 	Mode *mode;
224 	int f, divisor, index;
225 
226 	verbose("%s->init\n", ctlr->name);
227 	mode = vga->mode;
228 
229 	/*
230 	 * Must somehow determine which clock chip to use here.
231 	 * For now, punt and assume ATI18818.
232 	 */
233 	pclkp = ati18818;
234 	if(pclkp == 0)
235 		error("%s: can't determine clock chip\n", ctlr->name);
236 
237 	if(vga->f == 0)
238 		vga->f = vga->mode->frequency;
239 
240 	/*
241 	 * Find a clock frequency close to what we want.
242 	 * 'Close' is within 1MHz.
243 	 */
244 	for(divisor = 0, index = 0; index < Npclkx; index++, divisor = 0){
245 		divisor = 1;
246 		f = pclkp[index].frequency/divisor;
247 		if(f < vga->f+1000000 && f >= vga->f-1000000)
248 			break;
249 
250 		divisor = 2;
251 		f /= divisor;
252 		if(f < vga->f+1000000 && f >= vga->f-1000000)
253 			break;
254 	}
255 	if(divisor == 0)
256 		error("%s: no suitable clock for %d\n", ctlr->name, vga->f);
257 
258 	vga->d = divisor;
259 	vga->i = index;
260 
261 	vga->crt[0xB0] &= 0xDA;
262 	vga->crt[0xB1] &= 0x87;
263 	vga->crt[0xB5] &= 0x7E;
264 	vga->crt[0xB6] &= 0xE2;
265 	vga->crt[0xB3] &= 0xAF;
266 	vga->crt[0xA6] &= 0xFE;
267 	vga->crt[0xA7] &= 0xF4;
268 
269 	/*
270 	 * 256-colour linear addressing.
271 	 */
272 	if(mode->z == 8){
273 		vga->graphics[0x05] = 0x00;
274 		vga->attribute[0x10] &= ~0x40;
275 		vga->crt[0x13] = (mode->x/8)/2;
276 		vga->crt[0x14] = 0x00;
277 		vga->crt[0x17] = 0xE3;
278 
279 		vga->crt[0xB0] |= 0x20;
280 		vga->crt[0xB6] |= 0x04;
281 	}
282 	vga->attribute[0x11] = 0x00;
283 	vga->crt[0xB6] |= 0x01;
284 	vga->crt[0xBE] &= ~0x04;
285 
286 	/*
287 	 * Do the clock index bits.
288 	 */
289 	vga->crt[0xB8] &= 0x3F;
290 	vga->crt[0xB9] &= 0xFD;
291 	vga->crt[0xBE] &= 0xE5;
292 
293 	if(vga->d == 2)
294 		vga->crt[0xB8] |= 0x40;
295 	vga->crt[0xB9] |= pclkp[vga->i].b9;
296 	vga->crt[0xBE] |= pclkp[vga->i].be;
297 	vga->misc |= pclkp[vga->i].genmo;
298 
299 	if(vga->mode->interlace == 'v')
300 		vga->crt[0xBE] |= 0x02;
301 
302 	/*
303 	 * Turn off 128Kb CPU address bit so
304 	 * we only have a 64Kb aperture at 0xA0000.
305 	 */
306 	vga->crt[0xBD] &= ~0x04;
307 
308 	ctlr->type = mach32.name;
309 
310 	/*
311 	 * The Mach64 can only address 1Mb in VGA mode
312 	 */
313 	vga->vmb = 1*1024*1024;
314 
315 	ctlr->flag |= Finit;
316 }
317 
318 static void
319 load(Vga *vga, Ctlr *ctlr)
320 {
321 	verbose("%s->load\n", ctlr->name);
322 
323 	/*
324 	 * We should probably do something here to make sure we that we
325 	 * have access to all the video memory through the 64Kb VGA aperture
326 	 * by disabling and linear aperture and memory boundary and then
327 	 * enabling the VGA controller.
328 	 * But for now, let's just assume it's ok, the Mach64 documentation
329 	 * is just as clear as the Mach32 documentation.
330 	 */
331 	atixo(0xB0, vga->crt[0xB0]);
332 	atixo(0xB1, vga->crt[0xB1]);
333 	atixo(0xB5, vga->crt[0xB5]);
334 	atixo(0xB6, vga->crt[0xB6]);
335 	atixo(0xB3, vga->crt[0xB3]);
336 	atixo(0xA6, vga->crt[0xA6]);
337 	atixo(0xA7, vga->crt[0xA7]);
338 	atixo(0xB8, vga->crt[0xB8]);
339 	atixo(0xB9, vga->crt[0xB9]);
340 	atixo(0xBE, vga->crt[0xBE]);
341 	vgao(MiscW, vga->misc);
342 
343 	ctlr->flag |= Fload;
344 }
345 
346 static void
347 dump(Vga *vga, Ctlr *ctlr)
348 {
349 	int i;
350 	Mach64 *mach64;
351 
352 	printitem(ctlr->name, "ATIX");
353 	for(i = 0xA0; i < 0xC0; i++)
354 		printreg(vga->crt[i]);
355 
356 	if((mach64 = vga->private) == 0)
357 		return;
358 
359 	printitem(ctlr->name, "CONFIGCNTL");
360 	print("%.8lux\n", mach64->configcntl);
361 	printitem(ctlr->name, "CONFIGSTAT");
362 	print("%.8lux\n", mach64->configstat);
363 	printitem(ctlr->name, "MEMCNTL");
364 	print("%.8lux\n", mach64->memcntl);
365 	printitem(ctlr->name, "SCRATCH1");
366 	print("%.8lux\n", mach64->scratch1);
367 }
368 
369 Ctlr mach64 = {
370 	"mach64",			/* name */
371 	snarf,				/* snarf */
372 	0,				/* options */
373 	init,				/* init */
374 	load,				/* load */
375 	dump,				/* dump */
376 };
377