xref: /plan9-contrib/sys/src/cmd/aux/vga/main.c (revision ec59a3ddbfceee0efe34584c2c9981a5e5ff1ec4)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 
5 #include "pci.h"
6 #include "vga.h"
7 
8 Biobuf stdout;
9 
10 static int iflag, lflag, pflag, rflag;
11 static char *usage =
12 	"usage: aux/vga [ -BcdilpvV ] [ -b bios-id ] [ -m monitor ] [ -x db ] [ mode [ virtualsize ] ]\n";
13 
14 static char *dbname = "/lib/vgadb";
15 static char monitordb[128];
16 
17 static void
18 dump(Vga* vga)
19 {
20 	Ctlr *ctlr;
21 	Attr *attr;
22 
23 	if(vga->mode)
24 		dbdumpmode(vga->mode);
25 
26 	for(attr = vga->attr; attr; attr = attr->next)
27 		Bprint(&stdout, "vga->attr: %s=%s\n", attr->attr, attr->val);
28 
29 	for(ctlr = vga->link; ctlr; ctlr = ctlr->link){
30 		if(ctlr->dump == 0)
31 			continue;
32 
33 		trace("%s->dump\n", ctlr->name);
34 		if(ctlr->flag && ctlr->flag != Fsnarf){
35 			printitem(ctlr->name, "flag");
36 			printflag(ctlr->flag);
37 			Bprint(&stdout, "\n");
38 		}
39 		(*ctlr->dump)(vga, ctlr);
40 		ctlr->flag |= Fdump;
41 	}
42 	Bprint(&stdout, "\n");
43 }
44 
45 void
46 resyncinit(Vga* vga, Ctlr* ctlr, ulong on, ulong off)
47 {
48 	Ctlr *link;
49 
50 	trace("%s->resyncinit on 0x%8.8luX off 0x%8.8luX\n",
51 		ctlr->name, on, off);
52 
53 	for(link = vga->link; link; link = link->link){
54 		link->flag |= on;
55 		link->flag &= ~off;
56 		if(link == ctlr)
57 			continue;
58 
59 		if(link->init == 0 || (link->flag & Finit) == 0)
60 			continue;
61 		link->flag &= ~Finit;
62 		trace("%s->init 0x%8.8luX\n", link->name, link->flag);
63 		(*link->init)(vga, link);
64 	}
65 }
66 
67 void
68 sequencer(Vga* vga, int on)
69 {
70 	static uchar seq01;
71 	static int state = 1;
72 	char *s;
73 
74 	if(on)
75 		s = "on";
76 	else
77 		s = "off";
78 	trace("sequencer->enter %s\n", s);
79 	if(on){
80 		if(vga)
81 			seq01 = vga->sequencer[0x01];
82 		if(state == 0){
83 			seq01 |= 0x01;
84 			vgaxo(Seqx, 0x01, seq01);
85 			vgaxo(Seqx, 0x00, 0x03);
86 		}
87 	}
88 	else{
89 		vgaxo(Seqx, 0x00, 0x01);
90 		seq01 = vgaxi(Seqx, 0x01);
91 		vgaxo(Seqx, 0x01, seq01|0x20);
92 	}
93 	state = on;
94 	trace("sequencer->leave %s\n", s);
95 }
96 
97 static void
98 linear(Vga* vga)
99 {
100 	char buf[256];
101 	char *p;
102 
103 	/*
104 	 * Set up for linear addressing: try to allocate the
105 	 * kernel memory map then read the base-address back.
106 	 * vga->linear is a compatibility hack.
107 	 */
108 	if(vga->linear == 0){
109 		vga->ctlr->flag &= ~Ulinear;
110 		return;
111 	}
112 	if(vga->ctlr->flag & Ulinear){
113 		/*
114 		 * If there's already an aperture don't bother trying
115 		 * to set up a new one.
116 		 */
117 		vgactlr("addr", buf);
118 		if(atoi(buf)==0 && (buf[0]!='p' || buf[1]!=' ' || atoi(buf+2)==0)){
119 			sprint(buf, "0x%lux 0x%lux", vga->apz ? vga->apz : vga->vmz, vga->vma);
120 			vgactlw("linear", buf);
121 			vgactlr("addr", buf);
122 		}
123 		trace("linear->addr %s\n", buf);
124 		/*
125 		 * old: addr 0x12345678
126 		 * new: addr p 0x12345678 v 0x82345678 size 0x123
127 		 */
128 		if(buf[0]=='p' && buf[1]==' '){
129 			vga->vmb = strtoul(buf+2, 0, 0);
130 			p = strstr(buf, "size");
131 			if(p)
132 				vga->apz = strtoul(p+4, 0, 0);
133 		}else
134 			vga->vmb = strtoul(buf, 0, 0);
135 	}
136 	else
137 		vgactlw("linear", "0");
138 }
139 
140 char*
141 chanstr[32+1] = {
142 [1]	"k1",
143 [2]	"k2",
144 [4]	"k4",
145 [8]	"m8",
146 [16]	"r5g6b5",
147 [24]	"r8g8b8",
148 [32]	"x8r8g8b8",
149 };
150 
151 void
152 main(int argc, char** argv)
153 {
154 	char *bios, buf[256], sizeb[256], *p, *vsize, *psize;
155 	char *type, *vtype;
156 	int fd, virtual, len;
157 	Ctlr *ctlr;
158 	Vga *vga;
159 
160 	fmtinstall('H', encodefmt);
161 	Binit(&stdout, 1, OWRITE);
162 
163 	bios = getenv("vgactlr");
164 	if((type = getenv("monitor")) == 0)
165 		type = "vga";
166 	psize = vsize = "640x480x8";
167 
168 	ARGBEGIN{
169 
170 	case 'b':
171 		bios = ARGF();
172 		break;
173 
174 	case 'B':
175 		dumpbios(0x10000);
176 		exits(0);
177 
178 	case 'c':
179 		cflag = 1;
180 		break;
181 
182 	case 'd':
183 		dflag = 1;
184 		break;
185 
186 	case 'i':
187 		iflag = 1;
188 		break;
189 
190 	case 'l':
191 		lflag = 1;
192 		break;
193 
194 	case 'm':
195 		type = ARGF();
196 		break;
197 
198 	case 'p':
199 		pflag = 1;
200 		break;
201 
202 	case 'r':
203 		/*
204 		 * rflag > 1 means "leave me alone, I know what I'm doing."
205 		 */
206 		rflag++;
207 		break;
208 
209 	case 'v':
210 		vflag = 1;
211 		break;
212 
213 	case 'V':
214 		vflag = 1;
215 		Vflag = 1;
216 		break;
217 
218 	case 'x':
219 		dbname = ARGF();
220 		break;
221 
222 	default:
223 		error(usage, argv0);
224 
225 	}ARGEND
226 
227 	virtual = 0;
228 	switch(argc){
229 	case 1:
230 		vsize = psize = argv[0];
231 		break;
232 	case 2:
233 		psize = argv[0];
234 		vsize = argv[1];
235 		virtual = 1;
236 		break;
237 	case 0:
238 		break;
239 	default:
240 		error(usage, argv0);
241 	}
242 
243 	if(lflag && strcmp(vsize, "text") == 0){
244 		vesatextmode();
245 		vgactlw("textmode", "");
246 		exits(0);
247 	}
248 
249 	vga = alloc(sizeof(Vga));
250 	if(bios){
251 		if((vga->offset = strtol(bios, &p, 0)) == 0 || *p++ != '=')
252 			error("main: bad BIOS string format - %s\n", bios);
253 		len = strlen(p);
254 		vga->bios = alloc(len+1);
255 		strncpy(vga->bios, p, len);
256 		trace("main->BIOS %s\n", bios);
257 	}
258 
259 	/*
260 	 * Try to identify the VGA card and grab
261 	 * registers. Print them out if requested.
262 	 */
263 	if(strcmp(type, "vesa") == 0
264 		|| dbctlr(dbname, vga) == 0 || vga->ctlr == 0)
265 	if(dbvesa(vga) == 0 || vga->ctlr == 0){
266 		Bprint(&stdout, "%s: controller not in %s, not vesa\n", argv0, dbname);
267 		dumpbios(256);
268 		type = "vga";
269 		vsize = psize = "640x480x1";
270 		virtual = 0;
271 		vga->ctlr = &generic;
272 		vga->link = &generic;
273 	}
274 
275 	trace("main->snarf\n");
276 	for(ctlr = vga->link; ctlr; ctlr = ctlr->link){
277 		if(ctlr->snarf == 0)
278 			continue;
279 		trace("%s->snarf\n", ctlr->name);
280 		(*ctlr->snarf)(vga, ctlr);
281 	}
282 
283 	if(pflag)
284 		dump(vga);
285 
286 	for(ctlr = vga->link; ctlr; ctlr = ctlr->link)
287 		if(ctlr->flag & Ferror)
288 			error("%r");
289 
290 	if(iflag || lflag){
291 		if(getenv(type))
292 			snprint(monitordb, sizeof monitordb, "/env/%s", type);
293 		else
294 			strecpy(monitordb, monitordb+sizeof monitordb, dbname);
295 
296 		if(vga->vesa){
297 			strcpy(monitordb, "vesa bios");
298 			vga->mode = dbvesamode(psize);
299 		}else
300 			vga->mode = dbmode(monitordb, type, psize);
301 		if(vga->mode == 0)
302 			error("main: %s@%s not in %s\n", type, psize, monitordb);
303 
304 		if(virtual){
305 			if((p = strchr(vsize, 'x')) == nil)
306 				error("bad virtual size %s\n", vsize);
307 			vga->virtx = atoi(vsize);
308 			vga->virty = atoi(p+1);
309 			if(vga->virtx < vga->mode->x || vga->virty < vga->mode->y)
310 				error("virtual size smaller than physical size\n");
311 			vga->panning = 1;
312 		}
313 		else{
314 			vga->virtx = vga->mode->x;
315 			vga->virty = vga->mode->y;
316 			vga->panning = 0;
317 		}
318 
319 		trace("vmf %d vmdf %d vf1 %lud vbw %lud\n",
320 			vga->mode->frequency, vga->mode->deffrequency,
321 			vga->f[1], vga->mode->videobw);
322 		if(vga->mode->frequency == 0 && vga->mode->videobw != 0 && vga->f[1] != 0){
323 			/*
324 			 * boost clock as much as possible subject
325 			 * to video and memory bandwidth constraints
326 			 */
327 			ulong bytes, freq, membw;
328 			double rr;
329 
330 			freq = vga->mode->videobw;
331 			if(freq > vga->f[1])
332 				freq = vga->f[1];
333 
334 			rr = (double)freq/(vga->mode->ht*vga->mode->vt);
335 			if(rr > 85.0)		/* >85Hz is ridiculous */
336 				rr = 85.0;
337 
338 			bytes = (vga->mode->x*vga->mode->y*vga->mode->z)/8;
339 			membw = rr*bytes;
340 			if(vga->membw != 0 && membw > vga->membw){
341 				membw = vga->membw;
342 				rr = (double)membw/bytes;
343 			}
344 
345 			freq = rr*(vga->mode->ht*vga->mode->vt);
346 			vga->mode->frequency = freq;
347 
348 			trace("using frequency %lud rr %.2f membw %lud\n",
349 				freq, rr, membw);
350 		}
351 		else if(vga->mode->frequency == 0)
352 			vga->mode->frequency = vga->mode->deffrequency;
353 
354 		for(ctlr = vga->link; ctlr; ctlr = ctlr->link){
355 			if(ctlr->options == 0)
356 				continue;
357 			trace("%s->options\n", ctlr->name);
358 			(*ctlr->options)(vga, ctlr);
359 		}
360 
361 		/*
362 		 * skip init for vesa - vesa will do the registers for us
363 		 */
364 		if(!vga->vesa)
365 		for(ctlr = vga->link; ctlr; ctlr = ctlr->link){
366 			if(ctlr->init == 0)
367 				continue;
368 			trace("%s->init\n", ctlr->name);
369 			(*ctlr->init)(vga, ctlr);
370 		}
371 
372 		if(strcmp(vga->mode->chan, "") == 0){
373 			if(vga->mode->z < nelem(chanstr) && chanstr[vga->mode->z])
374 				strcpy(vga->mode->chan, chanstr[vga->mode->z]);
375 			else
376 				error("%s: unknown channel type to use for depth %d", vga->ctlr->name, vga->mode->z);
377 		}
378 
379 		if(iflag || pflag)
380 			dump(vga);
381 
382 		if(lflag){
383 			trace("main->load\n");
384 			if(vga->vmz && (vga->virtx*vga->virty*vga->mode->z)/8 > vga->vmz)
385 				error("%s: not enough video memory - %lud\n",
386 					vga->ctlr->name, vga->vmz);
387 
388 			if(vga->ctlr->type)
389 				vtype = vga->ctlr->type;
390 			else if(p = strchr(vga->ctlr->name, '-')){
391 				strncpy(buf, vga->ctlr->name, p - vga->ctlr->name);
392 				buf[p - vga->ctlr->name] = 0;
393 				vtype = buf;
394 			}
395 			else
396 				vtype = vga->ctlr->name;
397 			vgactlw("type", vtype);
398 
399 			/*
400 			 * VESA must be set up before linear.
401 			 * Set type to vesa for linear.
402 			 */
403 			if(vga->vesa){
404 				vesa.load(vga, vga->vesa);
405 				if(vga->vesa->flag&Ferror)
406 					error("vesa load error\n");
407 				vgactlw("type", vesa.name);
408 			}
409 
410 			/*
411 			 * The new draw device needs linear mode set
412 			 * before size.
413 			 */
414 			linear(vga);
415 
416 			/*
417 			 * Linear is over so switch to other driver for
418 			 * acceleration.
419 			 */
420 			if(vga->vesa)
421 				vgactlw("type", vtype);
422 
423 			sprint(buf, "%ludx%ludx%d %s",
424 				vga->virtx, vga->virty,
425 				vga->mode->z, vga->mode->chan);
426 			if(rflag){
427 				vgactlr("size", sizeb);
428 				if(rflag < 2 && strcmp(buf, sizeb) != 0)
429 					error("bad refresh: %s != %s\n",
430 						buf, sizeb);
431 			}
432 			else
433 				vgactlw("size", buf);
434 
435 			/*
436 			 * No fiddling with registers if VESA set
437 			 * things up already.  Sorry.
438 			 */
439 			if(!vga->vesa){
440 				/*
441 				 * Turn off the display during the load.
442 				 */
443 				sequencer(vga, 0);
444 
445 				for(ctlr = vga->link; ctlr; ctlr = ctlr->link){
446 					if(ctlr->load == 0 || ctlr == &vesa)
447 						continue;
448 					trace("%s->load\n", ctlr->name);
449 					(*ctlr->load)(vga, ctlr);
450 				}
451 
452 				sequencer(vga, 1);
453 			}
454 
455 			vgactlw("drawinit", "");
456 
457 			if(vga->hwgc == 0 || cflag)
458 				vgactlw("hwgc", "soft");
459 			else
460 				vgactlw("hwgc", vga->hwgc->name);
461 
462 			/* might as well initialize the cursor */
463 			if((fd = open("/dev/cursor", OWRITE)) >= 0){
464 				write(fd, buf, 0);
465 				close(fd);
466 			}
467 
468 			if(vga->virtx != vga->mode->x || vga->virty != vga->mode->y){
469 				sprint(buf, "%dx%d", vga->mode->x, vga->mode->y);
470 				vgactlw("actualsize", buf);
471 				if(vga->panning)
472 					vgactlw("panning", "on");
473 			}
474 
475 			if(pflag)
476 				dump(vga);
477 		}
478 	}
479 
480 	trace("main->exits\n");
481 	exits(0);
482 }
483