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