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