plan9fox/sys/src/9/pc/vgavesa.c

272 lines
4.9 KiB
C

/*
* vga driver using just vesa bios to set up.
*/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"
#define Ureg Ureg386
#include "/386/include/ureg.h"
typedef struct Ureg386 Ureg386;
#define Image IMAGE
#include <draw.h>
#include <memdraw.h>
#include <cursor.h>
#include "screen.h"
enum {
RealModeBuf = 0x9000,
};
static uchar modebuf[0x1000];
static Chan *creg, *cmem;
#define WORD(p) ((p)[0] | ((p)[1]<<8))
#define LONG(p) ((p)[0] | ((p)[1]<<8) | ((p)[2]<<16) | ((p)[3]<<24))
#define PWORD(p, v) (p)[0] = (v); (p)[1] = (v)>>8
#define PLONG(p, v) (p)[0] = (v); (p)[1] = (v)>>8; (p)[2] = (v)>>16; (p)[3] = (v)>>24
typedef struct Vmode Vmode;
struct Vmode
{
char chan[32];
int attr; /* flags */
int bpl;
int dx, dy;
int depth;
ulong paddr;
};
static uchar*
vbesetup(Ureg386 *u, int ax)
{
memset(modebuf, 0, sizeof modebuf);
memset(u, 0, sizeof *u);
u->ax = ax;
u->es = (RealModeBuf>>4)&0xF000;
u->di = RealModeBuf&0xFFFF;
return modebuf;
}
static void
vbecall(Ureg386 *u)
{
if(devtab[cmem->type]->write(cmem, modebuf, sizeof(modebuf), RealModeBuf) != sizeof(modebuf))
error("write modebuf");
u->trap = 0x10;
if(devtab[creg->type]->write(creg, u, sizeof(*u), 0) != sizeof(*u))
error("write ureg");
if(devtab[creg->type]->read(creg, u, sizeof(*u), 0) != sizeof(*u))
error("read ureg");
if((u->ax&0xFFFF) != 0x004F)
error("vesa bios error");
if(devtab[cmem->type]->read(cmem, modebuf, sizeof(modebuf), RealModeBuf) != sizeof(modebuf))
error("read modebuf");
}
static void
vbecheck(void)
{
Ureg386 u;
uchar *p;
p = vbesetup(&u, 0x4F00);
strcpy((char*)p, "VBE2");
vbecall(&u);
if(memcmp((char*)p, "VESA", 4) != 0)
error("bad vesa signature");
if(p[5] < 2)
error("bad vesa version");
}
static int
vbegetmode(void)
{
Ureg386 u;
vbesetup(&u, 0x4F03);
vbecall(&u);
return u.bx;
}
static uchar*
vbemodeinfo(int mode)
{
uchar *p;
Ureg386 u;
p = vbesetup(&u, 0x4F01);
u.cx = mode;
vbecall(&u);
return p;
}
static char*
vmode(Vmode *m, uchar *p)
{
m->attr = WORD(p);
if(!(m->attr & (1<<4)))
return "not in VESA graphics mode";
if(!(m->attr & (1<<7)))
return "not in linear graphics mode";
m->bpl = WORD(p+16);
m->dx = WORD(p+18);
m->dy = WORD(p+20);
m->depth = p[25];
m->paddr = LONG(p+40);
if(m->depth <= 8)
snprint(m->chan, sizeof m->chan, "%c%d",
(m->attr & (1<<3)) ? 'm' : 'k', m->depth);
else
rgbmask2chan(m->chan, m->depth,
(1UL<<p[31])-1 << p[32],
(1UL<<p[33])-1 << p[34],
(1UL<<p[35])-1 << p[36]);
return nil;
}
static void
vesalinear(VGAscr *scr, int, int)
{
int i, mode, size, havesize;
Pcidev *pci;
char *err;
Vmode m;
vbecheck();
mode = vbegetmode();
/*
* bochs loses the top bits - cannot use this
if((mode&(1<<14)) == 0)
error("not in linear graphics mode");
*/
mode &= 0x3FFF;
if((err = vmode(&m, vbemodeinfo(mode))) != nil)
error(err);
size = m.dy * m.bpl;
/*
* figure out max size of memory so that we have
* enough if the screen is resized.
*/
pci = nil;
havesize = 0;
while(!havesize && (pci = pcimatch(pci, 0, 0)) != nil){
if(pci->ccrb != Pcibcdisp)
continue;
for(i=0; i<nelem(pci->mem); i++){
ulong a, e;
if(pci->mem[i].bar&1) /* not memory */
continue;
a = pci->mem[i].bar & ~0xF;
e = a + pci->mem[i].size;
if(m.paddr >= a && (m.paddr+size) <= e){
size = e - m.paddr;
havesize = 1;
break;
}
}
}
/* no pci - heuristic guess */
if(!havesize)
if(size < 4*1024*1024)
size = 4*1024*1024;
else
size = ROUND(size, 1024*1024);
vgalinearaddr(scr, m.paddr, size);
if(scr->apsize)
addvgaseg("vesascreen", scr->paddr, scr->apsize);
scr->softscreen = 1;
}
static void
vesaenable(VGAscr *)
{
cmem = namec("/dev/realmodemem", Aopen, ORDWR, 0);
if(waserror()){
cclose(cmem);
cmem = nil;
nexterror();
}
creg = namec("/dev/realmode", Aopen, ORDWR, 0);
poperror();
}
static void
vesadisable(VGAscr *)
{
if(cmem != nil)
cclose(cmem);
if(creg != nil)
cclose(creg);
cmem = creg = nil;
}
static void
vesablank(VGAscr *, int blank)
{
Ureg386 u;
vbesetup(&u, 0x4f10);
u.bx = blank ? 0x0101 : 0x0001;
/*
* dont wait forever when called from mouse kproc.
* some BIOS get stuck in i/o poll loop after
* blank/unblank for some reason. (Thinkpad A22p)
*/
if(up->kp)
procalarm(10000);
if(!waserror()){
vbecall(&u);
poperror();
}
if(up->kp){
procalarm(0);
up->notepending = 0;
}
}
static void
vesadrawinit(VGAscr *scr)
{
scr->blank = vesablank;
}
VGAdev vgavesadev = {
"vesa",
vesaenable,
vesadisable,
0,
vesalinear,
vesadrawinit,
};
/*
* called from multibootargs() to convert
* vbe mode info (passed from bootloader)
* to *bootscreen= parameter
*/
char*
vesabootscreenconf(char *s, char *e, uchar *p)
{
Vmode m;
if(vmode(&m, p) != nil)
return s;
return seprint(s, e, "*bootscreen=%dx%dx%d %s %#lux\n",
m.bpl * 8 / m.depth, m.dy, m.depth, m.chan, m.paddr);
}