jehanne/sys/src/cmd/hmi/vga/main.c

513 lines
10 KiB
C

/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <lib9.h>
#include <envvars.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;
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;
}
Bprint(&stdout, "\n");
}
void
resyncinit(Vga* vga, Ctlr* ctlr, uint32_t on, uint32_t 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 uint8_t 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(atoi(buf)==0 && (buf[0]!='p' || buf[1]!=' ' || atoi(buf+2)==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");
}
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");
}
static void
wiremach(void)
{
char file[128];
int32_t f, pid;
pid = getpid();
sprint(file, "#p/%d/ctl", pid);
f = open(file, OWRITE);
if(f < 0){
sprint(file, "/proc/%d/ctl", pid);
f = open(file, OWRITE);
}
if(f < 0)
fprint(2, "wiremach: cannot open neither #p/%d/ctl nor /proc/%d/ctl", pid, pid);
if(write(f, "wired 0", 7) < 7){
fprint(2, "%s: cannot wire to mach 0: %r", argv0);
exits("wire");
}
close(f);
}
void
main(int argc, char** argv)
{
char *bios, buf[256], sizeb[256], *p, *vsize, *psize;
char *type, *vtype;
int fd, 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;
}
wiremach();
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(psize);
}else
vga->mode = dbmode(monitordb, type, psize);
if(vga->mode == 0)
error("main: %s@%s not in %s\n", type, psize, monitordb);
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
*/
uint32_t 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;
vgactlw("type", vtype);
/*
* 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);
}
/*
* 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)
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);
/* might as well initialize the cursor */
if((fd = open("/dev/cursor", OWRITE)) >= 0){
write(fd, buf, 0);
close(fd);
}
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);
}