]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/vgavesa.c
vga: dont confuse io bar with mem bar in vgas3 and vgavesa
[plan9front.git] / sys / src / 9 / pc / vgavesa.c
1 /*
2  * vga driver using just vesa bios to set up.
3  */
4 #include "u.h"
5 #include "../port/lib.h"
6 #include "mem.h"
7 #include "dat.h"
8 #include "fns.h"
9 #include "io.h"
10 #include "../port/error.h"
11 #include "ureg.h"
12
13 #define Image   IMAGE
14 #include <draw.h>
15 #include <memdraw.h>
16 #include <cursor.h>
17 #include "screen.h"
18
19 enum {
20         Cdisable = 0,
21         Cenable,
22         Cblank,
23
24         RealModeBuf = 0x9000,
25 };
26
27 static uchar modebuf[0x1000];
28 static Chan *creg, *cmem;
29 static QLock vesaq;
30 static Rendez vesar;
31 static int vesactl;
32
33 #define WORD(p) ((p)[0] | ((p)[1]<<8))
34 #define LONG(p) ((p)[0] | ((p)[1]<<8) | ((p)[2]<<16) | ((p)[3]<<24))
35 #define PWORD(p, v) (p)[0] = (v); (p)[1] = (v)>>8
36 #define PLONG(p, v) (p)[0] = (v); (p)[1] = (v)>>8; (p)[2] = (v)>>16; (p)[3] = (v)>>24
37
38 static uchar*
39 vbesetup(Ureg *u, int ax)
40 {
41         memset(modebuf, 0, sizeof modebuf);
42         memset(u, 0, sizeof *u);
43         u->ax = ax;
44         u->es = (RealModeBuf>>4)&0xF000;
45         u->di = RealModeBuf&0xFFFF;
46         return modebuf;
47 }
48
49 static void
50 vbecall(Ureg *u)
51 {
52         if(devtab[cmem->type]->write(cmem, modebuf, sizeof(modebuf), RealModeBuf) != sizeof(modebuf))
53                 error("write modebuf");
54         u->trap = 0x10;
55         if(devtab[creg->type]->write(creg, u, sizeof(*u), 0) != sizeof(*u))
56                 error("write ureg");
57         if(devtab[creg->type]->read(creg, u, sizeof(*u), 0) != sizeof(*u))
58                 error("read ureg");
59         if((u->ax&0xFFFF) != 0x004F)
60                 error("vesa bios error");
61         if(devtab[cmem->type]->read(cmem, modebuf, sizeof(modebuf), RealModeBuf) != sizeof(modebuf))
62                 error("read modebuf");
63 }
64
65 static void
66 vbecheck(void)
67 {
68         Ureg u;
69         uchar *p;
70
71         p = vbesetup(&u, 0x4F00);
72         strcpy((char*)p, "VBE2");
73         vbecall(&u);
74         if(memcmp((char*)p, "VESA", 4) != 0)
75                 error("bad vesa signature");
76         if(p[5] < 2)
77                 error("bad vesa version");
78 }
79
80 static int
81 vbegetmode(void)
82 {
83         Ureg u;
84
85         vbesetup(&u, 0x4F03);
86         vbecall(&u);
87         return u.bx;
88 }
89
90 static uchar*
91 vbemodeinfo(int mode)
92 {
93         uchar *p;
94         Ureg u;
95
96         p = vbesetup(&u, 0x4F01);
97         u.cx = mode;
98         vbecall(&u);
99         return p;
100 }
101
102 static void
103 vesalinear(VGAscr *scr, int, int)
104 {
105         int i, mode, size, havesize;
106         ulong paddr;
107         Pcidev *pci;
108         uchar *p;
109
110         vbecheck();
111         mode = vbegetmode();
112         /*
113          * bochs loses the top bits - cannot use this
114         if((mode&(1<<14)) == 0)
115                 error("not in linear graphics mode");
116          */
117         mode &= 0x3FFF;
118         p = vbemodeinfo(mode);
119         if(!(WORD(p+0) & (1<<4)))
120                 error("not in VESA graphics mode");
121         if(!(WORD(p+0) & (1<<7)))
122                 error("not in linear graphics mode");
123
124         paddr = LONG(p+40);
125         size = WORD(p+20)*WORD(p+16);
126
127         /*
128          * figure out max size of memory so that we have
129          * enough if the screen is resized.
130          */
131         pci = nil;
132         havesize = 0;
133         while(!havesize && (pci = pcimatch(pci, 0, 0)) != nil){
134                 if(pci->ccrb != Pcibcdisp)
135                         continue;
136                 for(i=0; i<nelem(pci->mem); i++){
137                         ulong a, e;
138
139                         if(pci->mem[i].bar&1)   /* not memory */
140                                 continue;
141                         a = pci->mem[i].bar & ~0xF;
142                         e = a + pci->mem[i].size;
143                         if(paddr >= a && (paddr+size) <= e){
144                                 size = e - paddr;
145                                 havesize = 1;
146                                 break;
147                         }
148                 }
149         }
150
151         /* no pci - heuristic guess */
152         if(!havesize)
153                 if(size < 4*1024*1024)
154                         size = 4*1024*1024;
155                 else
156                         size = ROUND(size, 1024*1024);
157
158         vgalinearaddr(scr, paddr, size);
159         if(scr->apsize)
160                 addvgaseg("vesascreen", scr->paddr, scr->apsize);
161
162         scr->softscreen = 1;
163 }
164
165 static int
166 vesadisabled(void *)
167 {
168         return vesactl == Cdisable;
169 }
170
171 static void
172 vesaproc(void*)
173 {
174         Ureg u;
175
176         while(vesactl != Cdisable){
177                 if(!waserror()){
178                         sleep(&vesar, vesadisabled, nil);
179
180                         vbesetup(&u, 0x4f10);
181                         if(vesactl == Cblank)
182                                 u.bx = 0x0101;
183                         else    
184                                 u.bx = 0x0001;
185
186                         /*
187                          * dont wait forever here. some BIOS get stuck
188                          * in i/o poll loop after blank/unblank for some
189                          * reason. (Thinkpad A22p)
190                          */
191                         procalarm(10000);
192                         vbecall(&u);
193
194                         poperror();
195                 }
196                 procalarm(0);
197                 up->notepending = 0;
198         }
199         cclose(cmem);
200         cclose(creg);
201         cmem = creg = nil;
202         qunlock(&vesaq);
203
204         pexit("", 1);
205 }
206
207 static void
208 vesaenable(VGAscr *)
209 {
210         eqlock(&vesaq);
211         if(waserror()){
212                 qunlock(&vesaq);
213                 nexterror();
214         }
215         cmem = namec("/dev/realmodemem", Aopen, ORDWR, 0);
216         if(waserror()){
217                 cclose(cmem);
218                 cmem = nil;
219                 nexterror();
220         }
221         creg = namec("/dev/realmode", Aopen, ORDWR, 0);
222         poperror();
223         poperror();
224
225         vesactl = Cenable;
226         kproc("vesa", vesaproc, nil);
227 }
228
229 static void
230 vesadisable(VGAscr *)
231 {
232         vesactl = Cdisable;
233         wakeup(&vesar);
234
235         /* wait for vesaproc to finish */
236         qlock(&vesaq);
237         qunlock(&vesaq);
238 }
239
240 static void
241 vesablank(VGAscr *, int blank)
242 {
243         if(vesactl != Cdisable){
244                 vesactl = blank ? Cblank : Cenable;
245                 wakeup(&vesar);
246         }
247 }
248
249 static void
250 vesadrawinit(VGAscr *scr)
251 {
252         scr->blank = vesablank;
253 }
254
255 VGAdev vgavesadev = {
256         "vesa",
257         vesaenable,
258         vesadisable,
259         0,
260         vesalinear,
261         vesadrawinit,
262 };