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