ref: b5a6dc7849cbd9f1fd23183ba46f0d5deb24e81d
dir: /sys/src/cmd/aux/vga/main.c/
#include <u.h> #include <libc.h> #include <bio.h> #include "pci.h" #include "vga.h" Biobuf stdout; static int iflag, lflag, pflag, rflag; static char *dbname = "/lib/vgadb"; static char monitordb[128]; static void dump(Vga* vga) { Ctlr *ctlr; Attr *attr; int i; if(vga->mode) dbdumpmode(vga->mode); for(attr = vga->attr; attr; attr = attr->next) Bprint(&stdout, "vga->attr: %s=%s\n", attr->attr, attr->val); for(ctlr = vga->link; ctlr; ctlr = ctlr->link){ if(ctlr->dump == 0) continue; trace("%s->dump\n", ctlr->name); if(ctlr->flag && ctlr->flag != Fsnarf){ printitem(ctlr->name, "flag"); printflag(ctlr->flag); Bprint(&stdout, "\n"); } (*ctlr->dump)(vga, ctlr); ctlr->flag |= Fdump; } for(i=0; i < nelem(vga->edid); i++){ if(vga->edid[i]) printedid(vga->edid[i]); } Bprint(&stdout, "\n"); } void resyncinit(Vga* vga, Ctlr* ctlr, ulong on, ulong off) { Ctlr *link; trace("%s->resyncinit on 0x%8.8luX off 0x%8.8luX\n", ctlr->name, on, off); for(link = vga->link; link; link = link->link){ link->flag |= on; link->flag &= ~off; if(link == ctlr) continue; if(link->init == 0 || (link->flag & Finit) == 0) continue; link->flag &= ~Finit; trace("%s->init 0x%8.8luX\n", link->name, link->flag); (*link->init)(vga, link); } } void sequencer(Vga* vga, int on) { static uchar seq01; static int state = 1; char *s; if(on) s = "on"; else s = "off"; trace("sequencer->enter %s\n", s); if(on){ if(vga) seq01 = vga->sequencer[0x01]; if(state == 0){ seq01 |= 0x01; vgaxo(Seqx, 0x01, seq01); vgaxo(Seqx, 0x00, 0x03); } } else{ vgaxo(Seqx, 0x00, 0x01); seq01 = vgaxi(Seqx, 0x01); vgaxo(Seqx, 0x01, seq01|0x20); } state = on; trace("sequencer->leave %s\n", s); } static void linear(Vga* vga) { char buf[256]; char *p; /* * Set up for linear addressing: try to allocate the * kernel memory map then read the base-address back. * vga->linear is a compatibility hack. */ if(vga->linear == 0){ vga->ctlr->flag &= ~Ulinear; return; } if(vga->ctlr->flag & Ulinear){ /* * If there's already an aperture don't bother trying * to set up a new one. */ vgactlr("addr", buf); if(strtoul(buf, 0, 0)==0 && (buf[0]!='p' || buf[1]!=' ' || strtoul(buf+2, 0, 0)==0)){ sprint(buf, "0x%lux 0x%lux", vga->apz ? vga->apz : vga->vmz, vga->vma); vgactlw("linear", buf); vgactlr("addr", buf); } trace("linear->addr %s\n", buf); /* * old: addr 0x12345678 * new: addr p 0x12345678 v 0x82345678 size 0x123 */ if(buf[0]=='p' && buf[1]==' '){ vga->vmb = strtoul(buf+2, 0, 0); p = strstr(buf, "size"); if(p) vga->apz = strtoul(p+4, 0, 0); }else vga->vmb = strtoul(buf, 0, 0); } else vgactlw("linear", "0"); } static Mode* dbedidmode(Vga *vga, char *type, char *size) { char buf[32], *p; int i, x, y, z; Modelist *l; Mode *m; z = 32; x = y = 0; snprint(buf, sizeof(buf), "%s", size); if((p = strchr(buf, 'x')) != nil){ *p++ = 0; x = atoi(buf); y = atoi(p); if((p = strchr(p, 'x')) != nil){ *p++ = 0; z = atoi(p); } } for(i=0; i<nelem(vga->edid); i++){ if(vga->edid[i] == nil) continue; for(l = vga->edid[i]->modelist; l != nil; l = l->next) if(strcmp(l->name, type) == 0) goto found; } for(i=0; i<nelem(vga->edid); i++){ if(vga->edid[i] == nil) continue; for(l = vga->edid[i]->modelist; l != nil; l = l->next) if((x == 0 || l->x == x) && (y == 0 || l->y == y)) goto found; } return nil; found: m = alloc(sizeof(Mode)); *m = *((Mode*)l); m->z = z; x = m->x; y = m->y; snprint(m->type, sizeof(m->type), "%s", type); snprint(m->size, sizeof(m->size), "%dx%dx%d", x, y, z); return m; } char* chanstr[32+1] = { [1] "k1", [2] "k2", [4] "k4", [8] "m8", [16] "r5g6b5", [24] "r8g8b8", [32] "x8r8g8b8", }; static void usage(void) { fprint(2, "usage: aux/vga [ -BcdilpvV ] [ -b bios-id ] [ -m monitor ] [ -x db ] [ mode [ virtualsize ] ]\n"); exits("usage"); } void main(int argc, char** argv) { char *bios, buf[256], sizeb[256], *p, *vsize, *psize; char *type, *vtype; int virtual, len; Ctlr *ctlr; Vga *vga; fmtinstall('H', encodefmt); Binit(&stdout, 1, OWRITE); bios = getenv("vgactlr"); if((type = getenv("monitor")) == 0) type = "vga"; psize = vsize = "640x480x8"; ARGBEGIN{ default: usage(); break; case 'b': bios = EARGF(usage()); break; case 'B': dumpbios(0x10000); exits(0); case 'c': cflag = 1; break; case 'd': dflag = 1; break; case 'i': iflag = 1; break; case 'l': lflag = 1; break; case 'm': type = EARGF(usage()); break; case 'p': pflag = 1; break; case 'r': /* * rflag > 1 means "leave me alone, I know what I'm doing." */ rflag++; break; case 'v': vflag = 1; break; case 'V': vflag = 1; Vflag = 1; break; case 'x': dbname = EARGF(usage()); break; }ARGEND virtual = 0; switch(argc){ default: usage(); break; case 1: vsize = psize = argv[0]; break; case 2: psize = argv[0]; vsize = argv[1]; virtual = 1; break; case 0: break; } if(lflag && strcmp(vsize, "text") == 0){ vesatextmode(); vgactlw("textmode", ""); exits(0); } vga = alloc(sizeof(Vga)); if(bios){ if((vga->offset = strtol(bios, &p, 0)) == 0 || *p++ != '=') error("main: bad BIOS string format - %s\n", bios); len = strlen(p); vga->bios = alloc(len+1); strncpy(vga->bios, p, len); trace("main->BIOS %s\n", bios); } /* * Try to identify the VGA card and grab * registers. Print them out if requested. * If monitor=vesa or our vga controller can't be found * in vgadb, try vesa modes; failing that, try vga. */ if(strcmp(type, "vesa") == 0 || dbctlr(dbname, vga) == 0 || vga->ctlr == 0) if(dbvesa(vga) == 0 || vga->ctlr == 0){ Bprint(&stdout, "%s: controller not in %s, not vesa\n", argv0, dbname); dumpbios(256); type = "vga"; vsize = psize = "640x480x1"; virtual = 0; vga->ctlr = &generic; vga->link = &generic; } trace("main->snarf\n"); for(ctlr = vga->link; ctlr; ctlr = ctlr->link){ if(ctlr->snarf == 0) continue; trace("%s->snarf\n", ctlr->name); (*ctlr->snarf)(vga, ctlr); } if(pflag) dump(vga); for(ctlr = vga->link; ctlr; ctlr = ctlr->link) if(ctlr->flag & Ferror) error("%r\n"); if(iflag || lflag){ if(getenv(type)) snprint(monitordb, sizeof monitordb, "/env/%s", type); else strecpy(monitordb, monitordb+sizeof monitordb, dbname); if(vga->vesa){ strcpy(monitordb, "vesa bios"); vga->mode = dbvesamode(vga, psize); }else { vga->mode = dbmode(monitordb, type, psize); if(vga->mode == nil) vga->mode = dbedidmode(vga, type, psize); } if(vga->mode == nil) error("main: %s@%s not in %s\n", type, psize, monitordb); /* * because vga programs shb/ehb (Crt02, Crt03) the same as * shs/ehs (Crt04, Crt05), only shb/ehb is specified in vgadb * meaning the horizontal sync pulse start and end position. */ if(vga->mode->shs == 0) vga->mode->shs = vga->mode->shb; if(vga->mode->ehs == 0) vga->mode->ehs = vga->mode->ehb; if(virtual){ if((p = strchr(vsize, 'x')) == nil) error("bad virtual size %s\n", vsize); vga->virtx = atoi(vsize); vga->virty = atoi(p+1); if(vga->virtx < vga->mode->x || vga->virty < vga->mode->y) error("virtual size smaller than physical size\n"); vga->panning = 1; } else{ vga->virtx = vga->mode->x; vga->virty = vga->mode->y; vga->panning = 0; } trace("vmf %d vmdf %d vf1 %lud vbw %lud\n", vga->mode->frequency, vga->mode->deffrequency, vga->f[1], vga->mode->videobw); if(vga->mode->frequency == 0 && vga->mode->videobw != 0 && vga->f[1] != 0){ /* * boost clock as much as possible subject * to video and memory bandwidth constraints */ ulong bytes, freq, membw; double rr; freq = vga->mode->videobw; if(freq > vga->f[1]) freq = vga->f[1]; rr = (double)freq/(vga->mode->ht*vga->mode->vt); if(rr > 85.0) /* >85Hz is ridiculous */ rr = 85.0; bytes = (vga->mode->x*vga->mode->y*vga->mode->z)/8; membw = rr*bytes; if(vga->membw != 0 && membw > vga->membw){ membw = vga->membw; rr = (double)membw/bytes; } freq = rr*(vga->mode->ht*vga->mode->vt); vga->mode->frequency = freq; trace("using frequency %lud rr %.2f membw %lud\n", freq, rr, membw); } else if(vga->mode->frequency == 0) vga->mode->frequency = vga->mode->deffrequency; for(ctlr = vga->link; ctlr; ctlr = ctlr->link){ if(ctlr->options == 0) continue; trace("%s->options\n", ctlr->name); (*ctlr->options)(vga, ctlr); } /* * skip init for vesa - vesa will do the registers for us */ if(!vga->vesa) for(ctlr = vga->link; ctlr; ctlr = ctlr->link){ if(ctlr->init == 0) continue; trace("%s->init\n", ctlr->name); (*ctlr->init)(vga, ctlr); } if(strcmp(vga->mode->chan, "") == 0){ if(vga->mode->z < nelem(chanstr) && chanstr[vga->mode->z]) strcpy(vga->mode->chan, chanstr[vga->mode->z]); else error("%s: unknown channel type to use for depth %d\n", vga->ctlr->name, vga->mode->z); } if(iflag || pflag) dump(vga); if(lflag){ trace("main->load\n"); if(vga->vmz && (vga->virtx*vga->virty*vga->mode->z)/8 > vga->vmz) error("%s: not enough video memory - %lud\n", vga->ctlr->name, vga->vmz); if(vga->ctlr->type) vtype = vga->ctlr->type; else if(p = strchr(vga->ctlr->name, '-')){ strncpy(buf, vga->ctlr->name, p - vga->ctlr->name); buf[p - vga->ctlr->name] = 0; vtype = buf; } else vtype = vga->ctlr->name; /* * VESA must be set up before linear. * Set type to vesa for linear. */ if(vga->vesa){ vesa.load(vga, vga->vesa); if(vga->vesa->flag&Ferror) error("vesa load error\n"); vgactlw("type", vesa.name); } else vgactlw("type", vtype); /* * The new draw device needs linear mode set * before size. */ linear(vga); /* * Linear is over so switch to other driver for * acceleration. */ if(vga->vesa && strcmp(vesa.name, vtype)) vgactlw("type", vtype); sprint(buf, "%ludx%ludx%d %s", vga->virtx, vga->virty, vga->mode->z, vga->mode->chan); if(rflag){ vgactlr("size", sizeb); if(rflag < 2 && strcmp(buf, sizeb) != 0) error("bad refresh: %s != %s\n", buf, sizeb); } else vgactlw("size", buf); /* * No fiddling with registers if VESA set * things up already. Sorry. */ if(!vga->vesa){ /* * Turn off the display during the load. */ sequencer(vga, 0); for(ctlr = vga->link; ctlr; ctlr = ctlr->link){ if(ctlr->load == 0 || ctlr == &vesa) continue; trace("%s->load\n", ctlr->name); (*ctlr->load)(vga, ctlr); } sequencer(vga, 1); } vgactlw("drawinit", ""); if(vga->hwgc == 0 || cflag) vgactlw("hwgc", "soft"); else vgactlw("hwgc", vga->hwgc->name); if(vga->virtx != vga->mode->x || vga->virty != vga->mode->y){ sprint(buf, "%dx%d", vga->mode->x, vga->mode->y); vgactlw("actualsize", buf); if(vga->panning) vgactlw("panning", "on"); } if(pflag) dump(vga); } } trace("main->exits\n"); exits(0); }