xref: /plan9-contrib/sys/src/9/pc/vgavesa.c (revision 7874c72cdf15e7bba44575d64a95358fcedce352)
160441a25SDavid du Colombier /*
260441a25SDavid du Colombier  * vga driver using just vesa bios to set up.
360441a25SDavid du Colombier  *
460441a25SDavid du Colombier  * note that setting hwaccel to zero will cause cursor ghosts to be
560441a25SDavid du Colombier  * left behind.  hwaccel set non-zero repairs this.
660441a25SDavid du Colombier  */
74de34a7eSDavid du Colombier #include "u.h"
84de34a7eSDavid du Colombier #include "../port/lib.h"
94de34a7eSDavid du Colombier #include "mem.h"
104de34a7eSDavid du Colombier #include "dat.h"
114de34a7eSDavid du Colombier #include "fns.h"
124de34a7eSDavid du Colombier #include "io.h"
134de34a7eSDavid du Colombier #include "../port/error.h"
144de34a7eSDavid du Colombier #include "ureg.h"
154de34a7eSDavid du Colombier 
164de34a7eSDavid du Colombier #define	Image	IMAGE
174de34a7eSDavid du Colombier #include <draw.h>
184de34a7eSDavid du Colombier #include <memdraw.h>
194de34a7eSDavid du Colombier #include <cursor.h>
204de34a7eSDavid du Colombier #include "screen.h"
214de34a7eSDavid du Colombier 
22*cb8c047aSDavid du Colombier enum {
23*cb8c047aSDavid du Colombier 	Usesoftscreen = 1,
24*cb8c047aSDavid du Colombier };
2526d1d1dfSDavid du Colombier 
2626d1d1dfSDavid du Colombier static void *hardscreen;
27*cb8c047aSDavid du Colombier static uchar modebuf[0x1000];
2826d1d1dfSDavid du Colombier 
294de34a7eSDavid du Colombier #define WORD(p) ((p)[0] | ((p)[1]<<8))
304de34a7eSDavid du Colombier #define LONG(p) ((p)[0] | ((p)[1]<<8) | ((p)[2]<<16) | ((p)[3]<<24))
314de34a7eSDavid du Colombier #define PWORD(p, v) (p)[0] = (v); (p)[1] = (v)>>8
324de34a7eSDavid du Colombier #define PLONG(p, v) (p)[0] = (v); (p)[1] = (v)>>8; (p)[2] = (v)>>16; (p)[3] = (v)>>24
334de34a7eSDavid du Colombier 
344de34a7eSDavid du Colombier static uchar*
vbesetup(Ureg * u,int ax)354de34a7eSDavid du Colombier vbesetup(Ureg *u, int ax)
364de34a7eSDavid du Colombier {
37e862f8a5SDavid du Colombier 	ulong pa;
38e862f8a5SDavid du Colombier 
39e862f8a5SDavid du Colombier 	pa = PADDR(RMBUF);
40*cb8c047aSDavid du Colombier 	memset(modebuf, 0, sizeof modebuf);
414de34a7eSDavid du Colombier 	memset(u, 0, sizeof *u);
424de34a7eSDavid du Colombier 	u->ax = ax;
43e862f8a5SDavid du Colombier 	u->es = (pa>>4)&0xF000;
44e862f8a5SDavid du Colombier 	u->di = pa&0xFFFF;
45*cb8c047aSDavid du Colombier 	return modebuf;
464de34a7eSDavid du Colombier }
474de34a7eSDavid du Colombier 
484de34a7eSDavid du Colombier static void
vbecall(Ureg * u)494de34a7eSDavid du Colombier vbecall(Ureg *u)
504de34a7eSDavid du Colombier {
51*cb8c047aSDavid du Colombier 	Chan *creg, *cmem;
52*cb8c047aSDavid du Colombier 	ulong pa;
53*cb8c047aSDavid du Colombier 
54*cb8c047aSDavid du Colombier 	cmem = namec("/dev/realmodemem", Aopen, ORDWR, 0);
55*cb8c047aSDavid du Colombier 	if(waserror()){
56*cb8c047aSDavid du Colombier 		cclose(cmem);
57*cb8c047aSDavid du Colombier 		nexterror();
58*cb8c047aSDavid du Colombier 	}
59*cb8c047aSDavid du Colombier 	creg = namec("/dev/realmode", Aopen, ORDWR, 0);
60*cb8c047aSDavid du Colombier 	if(waserror()){
61*cb8c047aSDavid du Colombier 		cclose(creg);
62*cb8c047aSDavid du Colombier 		nexterror();
63*cb8c047aSDavid du Colombier 	}
64*cb8c047aSDavid du Colombier 	pa = PADDR(RMBUF);
65*cb8c047aSDavid du Colombier 	devtab[cmem->type]->write(cmem, modebuf, sizeof modebuf, pa);
664de34a7eSDavid du Colombier 	u->trap = 0x10;
67*cb8c047aSDavid du Colombier 	devtab[creg->type]->write(creg, u, sizeof *u, 0);
68*cb8c047aSDavid du Colombier 
69*cb8c047aSDavid du Colombier 	devtab[creg->type]->read(creg, u, sizeof *u, 0);
704de34a7eSDavid du Colombier 	if((u->ax&0xFFFF) != 0x004F)
714de34a7eSDavid du Colombier 		error("vesa bios error");
72*cb8c047aSDavid du Colombier 	devtab[cmem->type]->read(cmem, modebuf, sizeof modebuf, pa);
73*cb8c047aSDavid du Colombier 
74*cb8c047aSDavid du Colombier 	poperror();
75*cb8c047aSDavid du Colombier 	cclose(creg);
76*cb8c047aSDavid du Colombier 	poperror();
77*cb8c047aSDavid du Colombier 	cclose(cmem);
784de34a7eSDavid du Colombier }
794de34a7eSDavid du Colombier 
804de34a7eSDavid du Colombier static void
vbecheck(void)814de34a7eSDavid du Colombier vbecheck(void)
824de34a7eSDavid du Colombier {
834de34a7eSDavid du Colombier 	Ureg u;
844de34a7eSDavid du Colombier 	uchar *p;
854de34a7eSDavid du Colombier 
864de34a7eSDavid du Colombier 	p = vbesetup(&u, 0x4F00);
874de34a7eSDavid du Colombier 	strcpy((char*)p, "VBE2");
884de34a7eSDavid du Colombier 	vbecall(&u);
894de34a7eSDavid du Colombier 	if(memcmp((char*)p, "VESA", 4) != 0)
904de34a7eSDavid du Colombier 		error("bad vesa signature");
914de34a7eSDavid du Colombier 	if(p[5] < 2)
924de34a7eSDavid du Colombier 		error("bad vesa version");
934de34a7eSDavid du Colombier }
944de34a7eSDavid du Colombier 
954de34a7eSDavid du Colombier static int
vbegetmode(void)964de34a7eSDavid du Colombier vbegetmode(void)
974de34a7eSDavid du Colombier {
984de34a7eSDavid du Colombier 	Ureg u;
994de34a7eSDavid du Colombier 
1004de34a7eSDavid du Colombier 	vbesetup(&u, 0x4F03);
1014de34a7eSDavid du Colombier 	vbecall(&u);
1024de34a7eSDavid du Colombier 	return u.bx;
1034de34a7eSDavid du Colombier }
1044de34a7eSDavid du Colombier 
1054de34a7eSDavid du Colombier static uchar*
vbemodeinfo(int mode)1064de34a7eSDavid du Colombier vbemodeinfo(int mode)
1074de34a7eSDavid du Colombier {
1084de34a7eSDavid du Colombier 	uchar *p;
1094de34a7eSDavid du Colombier 	Ureg u;
1104de34a7eSDavid du Colombier 
1114de34a7eSDavid du Colombier 	p = vbesetup(&u, 0x4F01);
1124de34a7eSDavid du Colombier 	u.cx = mode;
1134de34a7eSDavid du Colombier 	vbecall(&u);
1144de34a7eSDavid du Colombier 	return p;
1154de34a7eSDavid du Colombier }
1164de34a7eSDavid du Colombier 
1174de34a7eSDavid du Colombier static void
vesalinear(VGAscr * scr,int,int)118390ad7e1SDavid du Colombier vesalinear(VGAscr *scr, int, int)
1194de34a7eSDavid du Colombier {
120*cb8c047aSDavid du Colombier 	int i, mode, size, havesize;
1214de34a7eSDavid du Colombier 	uchar *p;
1224de34a7eSDavid du Colombier 	ulong paddr;
1234de34a7eSDavid du Colombier 	Pcidev *pci;
1244de34a7eSDavid du Colombier 
125*cb8c047aSDavid du Colombier 	if(hardscreen) {
126*cb8c047aSDavid du Colombier 		scr->vaddr = 0;
127*cb8c047aSDavid du Colombier 		scr->paddr = scr->apsize = 0;
128*cb8c047aSDavid du Colombier 		return;
129*cb8c047aSDavid du Colombier 	}
130*cb8c047aSDavid du Colombier 
1314de34a7eSDavid du Colombier 	vbecheck();
1324de34a7eSDavid du Colombier 	mode = vbegetmode();
133*cb8c047aSDavid du Colombier 	/*
134*cb8c047aSDavid du Colombier 	 * bochs loses the top bits - cannot use this
1354de34a7eSDavid du Colombier 	if((mode&(1<<14)) == 0)
1364de34a7eSDavid du Colombier 		error("not in linear graphics mode");
1374de34a7eSDavid du Colombier 	 */
1384de34a7eSDavid du Colombier 	mode &= 0x3FFF;
1394de34a7eSDavid du Colombier 	p = vbemodeinfo(mode);
1404de34a7eSDavid du Colombier 	if(!(WORD(p+0) & (1<<4)))
1414de34a7eSDavid du Colombier 		error("not in VESA graphics mode");
1424de34a7eSDavid du Colombier 	if(!(WORD(p+0) & (1<<7)))
1434de34a7eSDavid du Colombier 		error("not in linear graphics mode");
1444de34a7eSDavid du Colombier 
1454de34a7eSDavid du Colombier 	paddr = LONG(p+40);
1464de34a7eSDavid du Colombier 	size = WORD(p+20)*WORD(p+16);
1474de34a7eSDavid du Colombier 	size = PGROUND(size);
1484de34a7eSDavid du Colombier 
1494de34a7eSDavid du Colombier 	/*
1504de34a7eSDavid du Colombier 	 * figure out max size of memory so that we have
1514de34a7eSDavid du Colombier 	 * enough if the screen is resized.
1524de34a7eSDavid du Colombier 	 */
1534de34a7eSDavid du Colombier 	pci = nil;
154*cb8c047aSDavid du Colombier 	havesize = 0;
155*cb8c047aSDavid du Colombier 	while(!havesize && (pci = pcimatch(pci, 0, 0)) != nil){
15626d1d1dfSDavid du Colombier 		if(pci->ccrb != Pcibcdisp)
1574de34a7eSDavid du Colombier 			continue;
1584de34a7eSDavid du Colombier 		for(i=0; i<nelem(pci->mem); i++)
1594de34a7eSDavid du Colombier 			if(paddr == (pci->mem[i].bar&~0x0F)){
1604de34a7eSDavid du Colombier 				if(pci->mem[i].size > size)
1614de34a7eSDavid du Colombier 					size = pci->mem[i].size;
162*cb8c047aSDavid du Colombier 				havesize = 1;
163*cb8c047aSDavid du Colombier 				break;
1644de34a7eSDavid du Colombier 			}
1654de34a7eSDavid du Colombier 	}
1664de34a7eSDavid du Colombier 
1674de34a7eSDavid du Colombier 	/* no pci - heuristic guess */
168*cb8c047aSDavid du Colombier 	if (!havesize)
1694de34a7eSDavid du Colombier 		if(size < 4*1024*1024)
1704de34a7eSDavid du Colombier 			size = 4*1024*1024;
1714de34a7eSDavid du Colombier 		else
1724de34a7eSDavid du Colombier 			size = ROUND(size, 1024*1024);
173*cb8c047aSDavid du Colombier 	if(size > 16*1024*1024)		/* arbitrary */
17426d1d1dfSDavid du Colombier 		size = 16*1024*1024;
175*cb8c047aSDavid du Colombier 
176390ad7e1SDavid du Colombier 	vgalinearaddr(scr, paddr, size);
177*cb8c047aSDavid du Colombier 	if(scr->apsize)
178*cb8c047aSDavid du Colombier 		addvgaseg("vesascreen", scr->paddr, scr->apsize);
179*cb8c047aSDavid du Colombier 
180*cb8c047aSDavid du Colombier 	if(Usesoftscreen){
181390ad7e1SDavid du Colombier 		hardscreen = scr->vaddr;
182*cb8c047aSDavid du Colombier 		scr->vaddr = 0;
183*cb8c047aSDavid du Colombier 		scr->paddr = scr->apsize = 0;
184fe3a2384SDavid du Colombier 	}
18526d1d1dfSDavid du Colombier }
18626d1d1dfSDavid du Colombier 
18726d1d1dfSDavid du Colombier static void
vesaflush(VGAscr * scr,Rectangle r)18826d1d1dfSDavid du Colombier vesaflush(VGAscr *scr, Rectangle r)
18926d1d1dfSDavid du Colombier {
19026d1d1dfSDavid du Colombier 	int t, w, wid, off;
19126d1d1dfSDavid du Colombier 	ulong *hp, *sp, *esp;
19226d1d1dfSDavid du Colombier 
193*cb8c047aSDavid du Colombier 	if(hardscreen == nil)
194*cb8c047aSDavid du Colombier 		return;
19526d1d1dfSDavid du Colombier 	if(rectclip(&r, scr->gscreen->r) == 0)
19626d1d1dfSDavid du Colombier 		return;
19726d1d1dfSDavid du Colombier 	sp = (ulong*)(scr->gscreendata->bdata + scr->gscreen->zero);
19826d1d1dfSDavid du Colombier 	t = (r.max.x * scr->gscreen->depth + 2*BI2WD-1) / BI2WD;
19926d1d1dfSDavid du Colombier 	w = (r.min.x * scr->gscreen->depth) / BI2WD;
20026d1d1dfSDavid du Colombier 	w = (t - w) * BY2WD;
20126d1d1dfSDavid du Colombier 	wid = scr->gscreen->width;
20226d1d1dfSDavid du Colombier 	off = r.min.y * wid + (r.min.x * scr->gscreen->depth) / BI2WD;
20326d1d1dfSDavid du Colombier 
204*cb8c047aSDavid du Colombier 	hp = hardscreen;
20526d1d1dfSDavid du Colombier 	hp += off;
20626d1d1dfSDavid du Colombier 	sp += off;
20726d1d1dfSDavid du Colombier 	esp = sp + Dy(r) * wid;
20826d1d1dfSDavid du Colombier 	while(sp < esp){
20926d1d1dfSDavid du Colombier 		memmove(hp, sp, w);
21026d1d1dfSDavid du Colombier 		hp += wid;
21126d1d1dfSDavid du Colombier 		sp += wid;
21226d1d1dfSDavid du Colombier 	}
2134de34a7eSDavid du Colombier }
2144de34a7eSDavid du Colombier 
2154de34a7eSDavid du Colombier VGAdev vgavesadev = {
2164de34a7eSDavid du Colombier 	"vesa",
2174de34a7eSDavid du Colombier 	0,
2184de34a7eSDavid du Colombier 	0,
2194de34a7eSDavid du Colombier 	0,
2204de34a7eSDavid du Colombier 	vesalinear,
2214de34a7eSDavid du Colombier 	0,
2224de34a7eSDavid du Colombier 	0,
2234de34a7eSDavid du Colombier 	0,
2244de34a7eSDavid du Colombier 	0,
22526d1d1dfSDavid du Colombier 	vesaflush,
2264de34a7eSDavid du Colombier };
227