xref: /plan9/sys/src/cmd/aux/vga/mach32.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
1 #include <u.h>
2 #include <libc.h>
3 
4 #include "vga.h"
5 
6 /*
7  * ATI Mach32. Some hope.
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 	Advfunc		= 0x4AE8,	/* Advanced Function Control Register */
18 	Clocksel	= 0x4AEE,	/* Clock Select Register */
19 	Misc		= 0x36EE,	/* Miscellaneous Register */
20 	Membndry	= 0x42EE,	/* Memory Boundary Register */
21 	Memcfg		= 0x5EEE,	/* Memory Control Register */
22 };
23 
24 typedef struct {
25 	ushort	advfunc;
26 	ushort	clocksel;
27 	ushort	misc;
28 	ushort	membndry;
29 	ushort	memcfg;
30 } Mach32;
31 
32 /*
33  * There are a number of possible clock generator chips for these
34  * boards, and I don't know how to find out which is installed, other
35  * than by looking at the board. So, pick a subset that will work for
36  * all.
37  */
38 typedef struct {
39 	ulong	frequency;
40 	uchar	b8;			/* <6> - divide by 2 */
41 	uchar	b9;			/* <1> - bit <3> of frequency index */
42 	uchar	be;			/* <4> - bit <2> of frequency index */
43 	uchar	misc;			/* <3:2> - bits <1:0> of frequency index */
44 } Clock;
45 
46 static Clock clocks[] = {
47 	{  VgaFreq0,	0x40, 0x02, 0x00, 0x00, },
48 	{  32000000,	0x00, 0x00, 0x10, 0x04, },
49 	{  40000000,	0x00, 0x02, 0x10, 0x00, },
50 	{  44900000,	0x00, 0x02, 0x00, 0x0C, },
51 	{  65000000,	0x00, 0x02, 0x10, 0x0C, },
52 	{  75000000,	0x00, 0x02, 0x10, 0x08, },
53 	{	  0, },
54 };
55 
56 static ulong atix;
57 static uchar bios[256];
58 
59 static uchar
60 atixi(uchar index)
61 {
62 	outportb(atix, index);
63 	return inportb(atix+1);
64 }
65 
66 static void
67 atixo(uchar index, uchar data)
68 {
69 	outportw(atix, (data<<8)|index);
70 }
71 
72 static void
73 atixinit(Vga *vga, Ctlr *ctlr)
74 {
75 	uchar b;
76 	Mach32 *mach32;
77 
78 	/*
79 	 * Read in a part of the BIOS for configuration purposes,
80 	 * and try to determine the extended register address.
81 	 * Unfortunately, we can't determine the offset value, so
82 	 * we'll just have to assume it's 2.
83 	 */
84 	if(bios[0x00] == 0)
85 		 readbios((char*)bios, sizeof(bios), 0xC0000);
86 	if(atix == 0 && (atix = ((bios[0x11]<<8)|bios[0x10])) == 0)
87 		atix = 0x1CE;
88 	verbose("%s: atix=0x%lux\n", ctlr->name, atix);
89 
90 	/*
91 	 * Unlock the ATI Extended Registers.
92 	 * We leave them unlocked from now on.
93 	 * Why does this chip have so many
94 	 * lock bits?
95 	 */
96 	if((b = atixi(0xB8)) & 0x3F)
97 		atixo(0xB8, b & 0xC0);
98 	b = atixi(0xAB);
99 	atixo(0xAB, b & ~0x18);
100 	atixo(0xB4, 0x00);
101 	b = atixi(0xB9);
102 	atixo(0xB9, b & ~0x80);
103 	b = atixi(0xBE);
104 	atixo(0xBE, b|0x09);
105 
106 	if(vga->private == 0)
107 		vga->private = alloc(sizeof(mach32));
108 }
109 
110 static void
111 snarf(Vga *vga, Ctlr *ctlr)
112 {
113 	int i;
114 	Mach32 *mach32;
115 
116 	verbose("%s->snarf\n", ctlr->name);
117 
118 	atixinit(vga, ctlr);
119 	for(i = 0xA0; i < 0xC0; i++)
120 		vga->crt[i] = atixi(i);
121 
122 	mach32 = vga->private;
123 	mach32->advfunc = inportw(Advfunc);
124 	mach32->clocksel = inportw(Clocksel);
125 	mach32->misc = inportw(Misc);
126 	mach32->membndry = inportw(Membndry);
127 	mach32->memcfg = inportw(Memcfg);
128 
129 	/*
130 	 * Memory size.
131 	 */
132 	switch((mach32->misc>>2) & 0x03){
133 
134 	case 0:
135 		vga->vmb = 512*1024;
136 		break;
137 
138 	case 1:
139 		vga->vmb = 1024*1024;
140 		break;
141 
142 	case 2:
143 		vga->vmb = 2*1024*1024;
144 		break;
145 
146 	case 3:
147 		vga->vmb = 4*1024*1024;
148 		break;
149 	}
150 
151 	ctlr->flag |= Fsnarf;
152 }
153 
154 static void
155 options(Vga *vga, Ctlr *ctlr)
156 {
157 	USED(vga);
158 	verbose("%s->options\n", ctlr->name);
159 	ctlr->flag |= Foptions;
160 }
161 
162 static void
163 init(Vga *vga, Ctlr *ctlr)
164 {
165 	Clock *clockp;
166 	Mode *mode;
167 
168 	verbose("%s->init\n", ctlr->name);
169 	mode = vga->mode;
170 
171 	if(vga->f == 0)
172 		vga->f = vga->mode->frequency;
173 	for(clockp = clocks; clockp->frequency; clockp++){
174 		if(clockp->frequency > vga->f+100000)
175 			continue;
176 		if(clockp->frequency > vga->f-100000)
177 			break;
178 	}
179 	if(clockp->frequency == 0)
180 		error("%s: no suitable clock for %d\n", ctlr->name, vga->f);
181 
182 	vga->crt[0xB0] &= 0xDA;
183 	vga->crt[0xB1] &= 0x87;
184 	vga->crt[0xB5] &= 0x7E;
185 	vga->crt[0xB6] &= 0xE2;
186 	vga->crt[0xB3] &= 0xAF;
187 	vga->crt[0xA6] &= 0xFE;
188 	vga->crt[0xA7] &= 0xF4;
189 
190 	/*
191 	 * 256-colour linear addressing.
192 	 */
193 	if(mode->z == 8){
194 		vga->graphics[0x05] = 0x00;
195 		vga->attribute[0x10] &= ~0x40;
196 		vga->crt[0x13] = (mode->x/8)/2;
197 		vga->crt[0x14] = 0x00;
198 		vga->crt[0x17] = 0xE3;
199 
200 		vga->crt[0xB0] |= 0x20;
201 		vga->crt[0xB6] |= 0x04;
202 	}
203 	vga->attribute[0x11] = 0x00;
204 	vga->crt[0xB6] |= 0x01;
205 	vga->crt[0xBE] &= ~0x04;
206 
207 	/*
208 	 * Do the clock index bits.
209 	 */
210 	vga->crt[0xB9] &= 0xFD;
211 	vga->crt[0xB8] &= 0x3F;
212 	vga->crt[0xBE] &= 0xE5;
213 
214 	vga->crt[0xB8] |= clockp->b8;
215 	vga->crt[0xB9] |= clockp->b9;
216 	vga->crt[0xBE] |= clockp->be;
217 	vga->misc |= clockp->misc;
218 
219 	if(vga->mode->interlace == 'v')
220 		vga->crt[0xBE] |= 0x02;
221 
222 	/*
223 	 * Turn off 128Kb CPU address bit so
224 	 * we only have a 64Kb aperture at 0xA0000.
225 	 */
226 	vga->crt[0xBD] &= ~0x04;
227 
228 	ctlr->flag |= Finit;
229 }
230 
231 static void
232 load(Vga *vga, Ctlr *ctlr)
233 {
234 	ushort x;
235 
236 	verbose("%s->load\n", ctlr->name);
237 	/*
238 	 * Make sure we are in VGA mode,
239 	 * and that we have access to all the video memory through
240 	 * the 64Kb VGA aperture by disabling and linear aperture
241 	 * and memory boundary.
242 	 */
243 	outportw(Clocksel, 0x0000);
244 	x = inportw(Memcfg) & ~0x0003;
245 	outportw(Memcfg, x);
246 	outportw(Membndry, 0x0000);
247 
248 	atixo(0xB0, vga->crt[0xB0]);
249 	atixo(0xB1, vga->crt[0xB1]);
250 	atixo(0xB5, vga->crt[0xB5]);
251 	atixo(0xB6, vga->crt[0xB6]);
252 	atixo(0xB3, vga->crt[0xB3]);
253 	atixo(0xA6, vga->crt[0xA6]);
254 	atixo(0xA7, vga->crt[0xA7]);
255 	atixo(0xB8, vga->crt[0xB8]);
256 	atixo(0xB9, vga->crt[0xB9]);
257 	atixo(0xBE, vga->crt[0xBE]);
258 	vgao(MiscW, vga->misc);
259 
260 	ctlr->flag |= Fload;
261 }
262 
263 static void
264 dump(Vga *vga, Ctlr *ctlr)
265 {
266 	int i;
267 	Mach32 *mach32;
268 
269 	printitem(ctlr->name, "ATIX");
270 	for(i = 0xA0; i < 0xC0; i++)
271 		printreg(vga->crt[i]);
272 
273 	if((mach32 = vga->private) == 0)
274 		return;
275 
276 	printitem(ctlr->name, "ADVFUNC");
277 	print("%.4ux\n", mach32->advfunc);
278 	printitem(ctlr->name, "CLOCKSEL");
279 	print("%.4ux\n", mach32->clocksel);
280 	printitem(ctlr->name, "MISC");
281 	print("%.4ux\n", mach32->misc);
282 	printitem(ctlr->name, "MEMBNDRY");
283 	print("%.4ux\n", mach32->membndry);
284 	printitem(ctlr->name, "MEMCFG");
285 	print("%.4ux\n", mach32->memcfg);
286 }
287 
288 Ctlr mach32 = {
289 	"mach32",			/* name */
290 	snarf,				/* snarf */
291 	options,			/* options */
292 	init,				/* init */
293 	load,				/* load */
294 	dump,				/* dump */
295 };
296