512 lines
10 KiB
C
512 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 <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;
|
||
|
|
||
|
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);
|
||
|
}
|