]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/vgavesa.c
pc, pc64: support for multiboot framebuffer, common bootargs and multiboot code
[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
12 #define Ureg Ureg386
13 #include "/386/include/ureg.h"
14 typedef struct Ureg386 Ureg386;
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         RealModeBuf = 0x9000,
24 };
25
26 static uchar modebuf[0x1000];
27 static Chan *creg, *cmem;
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 typedef struct Vmode Vmode;
35 struct Vmode
36 {
37         char    chan[32];
38         int     attr;   /* flags */
39         int     bpl;
40         int     dx, dy;
41         int     depth;
42         ulong   paddr;
43 };
44
45 static uchar*
46 vbesetup(Ureg386 *u, int ax)
47 {
48         memset(modebuf, 0, sizeof modebuf);
49         memset(u, 0, sizeof *u);
50         u->ax = ax;
51         u->es = (RealModeBuf>>4)&0xF000;
52         u->di = RealModeBuf&0xFFFF;
53         return modebuf;
54 }
55
56 static void
57 vbecall(Ureg386 *u)
58 {
59         if(devtab[cmem->type]->write(cmem, modebuf, sizeof(modebuf), RealModeBuf) != sizeof(modebuf))
60                 error("write modebuf");
61         u->trap = 0x10;
62         if(devtab[creg->type]->write(creg, u, sizeof(*u), 0) != sizeof(*u))
63                 error("write ureg");
64         if(devtab[creg->type]->read(creg, u, sizeof(*u), 0) != sizeof(*u))
65                 error("read ureg");
66         if((u->ax&0xFFFF) != 0x004F)
67                 error("vesa bios error");
68         if(devtab[cmem->type]->read(cmem, modebuf, sizeof(modebuf), RealModeBuf) != sizeof(modebuf))
69                 error("read modebuf");
70 }
71
72 static void
73 vbecheck(void)
74 {
75         Ureg386 u;
76         uchar *p;
77
78         p = vbesetup(&u, 0x4F00);
79         strcpy((char*)p, "VBE2");
80         vbecall(&u);
81         if(memcmp((char*)p, "VESA", 4) != 0)
82                 error("bad vesa signature");
83         if(p[5] < 2)
84                 error("bad vesa version");
85 }
86
87 static int
88 vbegetmode(void)
89 {
90         Ureg386 u;
91
92         vbesetup(&u, 0x4F03);
93         vbecall(&u);
94         return u.bx;
95 }
96
97 static uchar*
98 vbemodeinfo(int mode)
99 {
100         uchar *p;
101         Ureg386 u;
102
103         p = vbesetup(&u, 0x4F01);
104         u.cx = mode;
105         vbecall(&u);
106         return p;
107 }
108
109 static char*
110 vmode(Vmode *m, uchar *p)
111 {
112         m->attr = WORD(p);
113         if(!(m->attr & (1<<4)))
114                 return "not in VESA graphics mode";
115         if(!(m->attr & (1<<7)))
116                 return "not in linear graphics mode";
117         m->bpl = WORD(p+16);
118         m->dx = WORD(p+18);
119         m->dy = WORD(p+20);
120         m->depth = p[25];
121         m->paddr = LONG(p+40);
122         if(m->depth <= 8)
123                 snprint(m->chan, sizeof m->chan, "%c%d", 
124                         (m->attr & (1<<3)) ? 'm' : 'k', m->depth);
125         else
126                 rgbmask2chan(m->chan, m->depth,
127                         (1UL<<p[31])-1 << p[32],
128                         (1UL<<p[33])-1 << p[34],
129                         (1UL<<p[35])-1 << p[36]);
130         return nil;
131 }
132
133 static void
134 vesalinear(VGAscr *scr, int, int)
135 {
136         int i, mode, size, havesize;
137         Pcidev *pci;
138         char *err;
139         Vmode m;
140
141         vbecheck();
142         mode = vbegetmode();
143         /*
144          * bochs loses the top bits - cannot use this
145         if((mode&(1<<14)) == 0)
146                 error("not in linear graphics mode");
147          */
148         mode &= 0x3FFF;
149         if((err = vmode(&m, vbemodeinfo(mode))) != nil)
150                 error(err);
151
152         size = m.dy * m.bpl;
153
154         /*
155          * figure out max size of memory so that we have
156          * enough if the screen is resized.
157          */
158         pci = nil;
159         havesize = 0;
160         while(!havesize && (pci = pcimatch(pci, 0, 0)) != nil){
161                 if(pci->ccrb != Pcibcdisp)
162                         continue;
163                 for(i=0; i<nelem(pci->mem); i++){
164                         ulong a, e;
165
166                         if(pci->mem[i].bar&1)   /* not memory */
167                                 continue;
168                         a = pci->mem[i].bar & ~0xF;
169                         e = a + pci->mem[i].size;
170                         if(m.paddr >= a && (m.paddr+size) <= e){
171                                 size = e - m.paddr;
172                                 havesize = 1;
173                                 break;
174                         }
175                 }
176         }
177
178         /* no pci - heuristic guess */
179         if(!havesize)
180                 if(size < 4*1024*1024)
181                         size = 4*1024*1024;
182                 else
183                         size = ROUND(size, 1024*1024);
184
185         vgalinearaddr(scr, m.paddr, size);
186         if(scr->apsize)
187                 addvgaseg("vesascreen", scr->paddr, scr->apsize);
188
189         scr->softscreen = 1;
190 }
191
192 static void
193 vesaenable(VGAscr *)
194 {
195         cmem = namec("/dev/realmodemem", Aopen, ORDWR, 0);
196         if(waserror()){
197                 cclose(cmem);
198                 cmem = nil;
199                 nexterror();
200         }
201         creg = namec("/dev/realmode", Aopen, ORDWR, 0);
202         poperror();
203 }
204
205 static void
206 vesadisable(VGAscr *)
207 {
208         if(cmem != nil)
209                 cclose(cmem);
210         if(creg != nil)
211                 cclose(creg);
212         cmem = creg = nil;
213 }
214
215 static void
216 vesablank(VGAscr *, int blank)
217 {
218         Ureg386 u;
219
220         vbesetup(&u, 0x4f10);
221         u.bx = blank ? 0x0101 : 0x0001;
222
223         /*
224          * dont wait forever when called from mouse kproc.
225          * some BIOS get stuck in i/o poll loop after
226          * blank/unblank for some reason. (Thinkpad A22p)
227          */
228         if(up->kp)
229                 procalarm(10000);
230
231         if(!waserror()){
232                 vbecall(&u);
233                 poperror();
234         }
235
236         if(up->kp){
237                 procalarm(0);
238                 up->notepending = 0;
239         }
240 }
241
242 static void
243 vesadrawinit(VGAscr *scr)
244 {
245         scr->blank = vesablank;
246 }
247
248 VGAdev vgavesadev = {
249         "vesa",
250         vesaenable,
251         vesadisable,
252         0,
253         vesalinear,
254         vesadrawinit,
255 };
256
257 /*
258  * called from multibootargs() to convert
259  * vbe mode info (passed from bootloader)
260  * to *bootscreen= parameter
261  */
262 char*
263 vesabootscreenconf(char *s, char *e, uchar *p)
264 {
265         Vmode m;
266
267         if(vmode(&m, p) != nil)
268                 return s;
269         return seprint(s, e, "*bootscreen=%dx%dx%d %s %#lux\n",
270                 m.bpl * 8 / m.depth, m.dy, m.depth, m.chan, m.paddr);
271 }