xref: /plan9-contrib/sys/src/9/pc/vgavesa.c (revision 7874c72cdf15e7bba44575d64a95358fcedce352)
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