1 /*
2 * vga driver using just vesa bios to set up.
3 *
4 * note that setting hwaccel to zero will cause cursor ghosts to be
5 * left behind. hwaccel set non-zero repairs this.
6 */
7 #include "u.h"
8 #include "../port/lib.h"
9 #include "mem.h"
10 #include "dat.h"
11 #include "fns.h"
12 #include "io.h"
13 #include "../port/error.h"
14 #include "ureg.h"
15
16 #define Image IMAGE
17 #include <draw.h>
18 #include <memdraw.h>
19 #include <cursor.h>
20 #include "screen.h"
21
22 enum {
23 Usesoftscreen = 1,
24 };
25
26 static void *hardscreen;
27 static uchar modebuf[0x1000];
28
29 #define WORD(p) ((p)[0] | ((p)[1]<<8))
30 #define LONG(p) ((p)[0] | ((p)[1]<<8) | ((p)[2]<<16) | ((p)[3]<<24))
31 #define PWORD(p, v) (p)[0] = (v); (p)[1] = (v)>>8
32 #define PLONG(p, v) (p)[0] = (v); (p)[1] = (v)>>8; (p)[2] = (v)>>16; (p)[3] = (v)>>24
33
34 static uchar*
vbesetup(Ureg * u,int ax)35 vbesetup(Ureg *u, int ax)
36 {
37 ulong pa;
38
39 pa = PADDR(RMBUF);
40 memset(modebuf, 0, sizeof modebuf);
41 memset(u, 0, sizeof *u);
42 u->ax = ax;
43 u->es = (pa>>4)&0xF000;
44 u->di = pa&0xFFFF;
45 return modebuf;
46 }
47
48 static void
vbecall(Ureg * u)49 vbecall(Ureg *u)
50 {
51 Chan *creg, *cmem;
52 ulong pa;
53
54 cmem = namec("/dev/realmodemem", Aopen, ORDWR, 0);
55 if(waserror()){
56 cclose(cmem);
57 nexterror();
58 }
59 creg = namec("/dev/realmode", Aopen, ORDWR, 0);
60 if(waserror()){
61 cclose(creg);
62 nexterror();
63 }
64 pa = PADDR(RMBUF);
65 devtab[cmem->type]->write(cmem, modebuf, sizeof modebuf, pa);
66 u->trap = 0x10;
67 devtab[creg->type]->write(creg, u, sizeof *u, 0);
68
69 devtab[creg->type]->read(creg, u, sizeof *u, 0);
70 if((u->ax&0xFFFF) != 0x004F)
71 error("vesa bios error");
72 devtab[cmem->type]->read(cmem, modebuf, sizeof modebuf, pa);
73
74 poperror();
75 cclose(creg);
76 poperror();
77 cclose(cmem);
78 }
79
80 static void
vbecheck(void)81 vbecheck(void)
82 {
83 Ureg u;
84 uchar *p;
85
86 p = vbesetup(&u, 0x4F00);
87 strcpy((char*)p, "VBE2");
88 vbecall(&u);
89 if(memcmp((char*)p, "VESA", 4) != 0)
90 error("bad vesa signature");
91 if(p[5] < 2)
92 error("bad vesa version");
93 }
94
95 static int
vbegetmode(void)96 vbegetmode(void)
97 {
98 Ureg u;
99
100 vbesetup(&u, 0x4F03);
101 vbecall(&u);
102 return u.bx;
103 }
104
105 static uchar*
vbemodeinfo(int mode)106 vbemodeinfo(int mode)
107 {
108 uchar *p;
109 Ureg u;
110
111 p = vbesetup(&u, 0x4F01);
112 u.cx = mode;
113 vbecall(&u);
114 return p;
115 }
116
117 static void
vesalinear(VGAscr * scr,int,int)118 vesalinear(VGAscr *scr, int, int)
119 {
120 int i, mode, size, havesize;
121 uchar *p;
122 ulong paddr;
123 Pcidev *pci;
124
125 if(hardscreen) {
126 scr->vaddr = 0;
127 scr->paddr = scr->apsize = 0;
128 return;
129 }
130
131 vbecheck();
132 mode = vbegetmode();
133 /*
134 * bochs loses the top bits - cannot use this
135 if((mode&(1<<14)) == 0)
136 error("not in linear graphics mode");
137 */
138 mode &= 0x3FFF;
139 p = vbemodeinfo(mode);
140 if(!(WORD(p+0) & (1<<4)))
141 error("not in VESA graphics mode");
142 if(!(WORD(p+0) & (1<<7)))
143 error("not in linear graphics mode");
144
145 paddr = LONG(p+40);
146 size = WORD(p+20)*WORD(p+16);
147 size = PGROUND(size);
148
149 /*
150 * figure out max size of memory so that we have
151 * enough if the screen is resized.
152 */
153 pci = nil;
154 havesize = 0;
155 while(!havesize && (pci = pcimatch(pci, 0, 0)) != nil){
156 if(pci->ccrb != Pcibcdisp)
157 continue;
158 for(i=0; i<nelem(pci->mem); i++)
159 if(paddr == (pci->mem[i].bar&~0x0F)){
160 if(pci->mem[i].size > size)
161 size = pci->mem[i].size;
162 havesize = 1;
163 break;
164 }
165 }
166
167 /* no pci - heuristic guess */
168 if (!havesize)
169 if(size < 4*1024*1024)
170 size = 4*1024*1024;
171 else
172 size = ROUND(size, 1024*1024);
173 if(size > 16*1024*1024) /* arbitrary */
174 size = 16*1024*1024;
175
176 vgalinearaddr(scr, paddr, size);
177 if(scr->apsize)
178 addvgaseg("vesascreen", scr->paddr, scr->apsize);
179
180 if(Usesoftscreen){
181 hardscreen = scr->vaddr;
182 scr->vaddr = 0;
183 scr->paddr = scr->apsize = 0;
184 }
185 }
186
187 static void
vesaflush(VGAscr * scr,Rectangle r)188 vesaflush(VGAscr *scr, Rectangle r)
189 {
190 int t, w, wid, off;
191 ulong *hp, *sp, *esp;
192
193 if(hardscreen == nil)
194 return;
195 if(rectclip(&r, scr->gscreen->r) == 0)
196 return;
197 sp = (ulong*)(scr->gscreendata->bdata + scr->gscreen->zero);
198 t = (r.max.x * scr->gscreen->depth + 2*BI2WD-1) / BI2WD;
199 w = (r.min.x * scr->gscreen->depth) / BI2WD;
200 w = (t - w) * BY2WD;
201 wid = scr->gscreen->width;
202 off = r.min.y * wid + (r.min.x * scr->gscreen->depth) / BI2WD;
203
204 hp = hardscreen;
205 hp += off;
206 sp += off;
207 esp = sp + Dy(r) * wid;
208 while(sp < esp){
209 memmove(hp, sp, w);
210 hp += wid;
211 sp += wid;
212 }
213 }
214
215 VGAdev vgavesadev = {
216 "vesa",
217 0,
218 0,
219 0,
220 vesalinear,
221 0,
222 0,
223 0,
224 0,
225 vesaflush,
226 };
227