vbe.c revision 1.8.10.1 1 /* $NetBSD: vbe.c,v 1.8.10.1 2017/02/05 13:40:13 skrll Exp $ */
2
3 /*-
4 * Copyright (c) 2009 Jared D. McNeill <jmcneill (at) invisible.ca>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /*
30 * VESA BIOS Extensions routines
31 */
32
33 #include <lib/libsa/stand.h>
34 #include <lib/libkern/libkern.h>
35 #include <machine/bootinfo.h>
36 #include "libi386.h"
37 #include "vbe.h"
38
39 extern const uint8_t rasops_cmap[];
40 static uint8_t *vbe_edid = NULL;
41 static int vbe_edid_valid = 0;
42
43 static struct _vbestate {
44 int available;
45 int modenum;
46 } vbestate;
47
48 static int
49 vbe_mode_is_supported(struct modeinfoblock *mi)
50 {
51 if ((mi->ModeAttributes & 0x01) == 0)
52 return 0; /* mode not supported by hardware */
53 if ((mi->ModeAttributes & 0x08) == 0)
54 return 0; /* linear fb not available */
55 if ((mi->ModeAttributes & 0x10) == 0)
56 return 0; /* text mode */
57 if (mi->NumberOfPlanes != 1)
58 return 0; /* planar mode not supported */
59 if (mi->MemoryModel != 0x04 /* Packed pixel */ &&
60 mi->MemoryModel != 0x06 /* Direct Color */)
61 return 0; /* unsupported pixel format */
62 return 1;
63 }
64
65 static bool
66 vbe_check(void)
67 {
68 if (!vbestate.available) {
69 printf("VBE not available\n");
70 return false;
71 }
72 return true;
73 }
74
75 void
76 vbe_init(void)
77 {
78 struct vbeinfoblock vbe;
79
80 memset(&vbe, 0, sizeof(vbe));
81 memcpy(vbe.VbeSignature, "VBE2", 4);
82 if (biosvbe_info(&vbe) != 0x004f)
83 return;
84 if (memcmp(vbe.VbeSignature, "VESA", 4) != 0)
85 return;
86
87 vbestate.available = 1;
88 vbestate.modenum = 0;
89 }
90
91 int
92 vbe_available(void)
93 {
94 return vbestate.available;
95 }
96
97 int
98 vbe_set_palette(const uint8_t *cmap, int slot)
99 {
100 struct paletteentry pe;
101 int ret;
102
103 if (!vbe_check())
104 return 1;
105
106 pe.Blue = cmap[2] >> 2;
107 pe.Green = cmap[1] >> 2;
108 pe.Red = cmap[0] >> 2;
109 pe.Alignment = 0;
110
111 ret = biosvbe_palette_data(0x0600, slot, &pe);
112
113 return ret == 0x004f ? 0 : 1;
114 }
115
116 int
117 vbe_set_mode(int modenum)
118 {
119 struct modeinfoblock mi;
120 struct btinfo_framebuffer fb;
121 int ret, i;
122
123 if (!vbe_check())
124 return 1;
125
126 ret = biosvbe_get_mode_info(modenum, &mi);
127 if (ret != 0x004f) {
128 printf("mode 0x%x invalid\n", modenum);
129 return 1;
130 }
131
132 if (!vbe_mode_is_supported(&mi)) {
133 printf("mode 0x%x not supported\n", modenum);
134 return 1;
135 }
136
137 ret = biosvbe_set_mode(modenum);
138 if (ret != 0x004f) {
139 printf("mode 0x%x could not be set\n", modenum);
140 return 1;
141 }
142
143 /* Setup palette for packed pixel mode */
144 if (mi.MemoryModel == 0x04)
145 for (i = 0; i < 256; i++)
146 vbe_set_palette(&rasops_cmap[i * 3], i);
147
148 memset(&fb, 0, sizeof(fb));
149 fb.physaddr = (uint64_t)mi.PhysBasePtr & 0xffffffff;
150 fb.width = mi.XResolution;
151 fb.height = mi.YResolution;
152 fb.stride = mi.BytesPerScanLine;
153 fb.depth = mi.BitsPerPixel;
154 fb.flags = 0;
155 fb.rnum = mi.RedMaskSize;
156 fb.rpos = mi.RedFieldPosition;
157 fb.gnum = mi.GreenMaskSize;
158 fb.gpos = mi.GreenFieldPosition;
159 fb.bnum = mi.BlueMaskSize;
160 fb.bpos = mi.BlueFieldPosition;
161 fb.vbemode = modenum;
162
163 framebuffer_configure(&fb);
164
165 return 0;
166 }
167
168 int
169 vbe_commit(void)
170 {
171 int ret = 1;
172
173 if (vbestate.modenum > 0) {
174 ret = vbe_set_mode(vbestate.modenum);
175 if (ret) {
176 printf("WARNING: failed to set VBE mode 0x%x\n",
177 vbestate.modenum);
178 wait_sec(5);
179 }
180 }
181 return ret;
182 }
183
184 static void *
185 vbe_farptr(uint32_t farptr)
186 {
187 return VBEPHYPTR((((farptr & 0xffff0000) >> 12) + (farptr & 0xffff)));
188 }
189
190 static int
191 vbe_parse_mode_str(char *str, int *x, int *y, int *depth)
192 {
193 char *p;
194
195 p = str;
196 *x = strtoul(p, NULL, 0);
197 if (*x == 0)
198 return 0;
199 p = strchr(p, 'x');
200 if (!p)
201 return 0;
202 ++p;
203 *y = strtoul(p, NULL, 0);
204 if (*y == 0)
205 return 0;
206 p = strchr(p, 'x');
207 if (!p)
208 *depth = 8;
209 else {
210 ++p;
211 *depth = strtoul(p, NULL, 0);
212 if (*depth == 0)
213 return 0;
214 }
215
216 return 1;
217 }
218
219 static int
220 vbe_find_mode_xyd(int x, int y, int depth)
221 {
222 struct vbeinfoblock vbe;
223 struct modeinfoblock mi;
224 uint32_t farptr;
225 uint16_t mode;
226 int safety = 0;
227
228 memset(&vbe, 0, sizeof(vbe));
229 memcpy(vbe.VbeSignature, "VBE2", 4);
230 if (biosvbe_info(&vbe) != 0x004f)
231 return 0;
232 if (memcmp(vbe.VbeSignature, "VESA", 4) != 0)
233 return 0;
234 farptr = vbe.VideoModePtr;
235 if (farptr == 0)
236 return 0;
237
238 while ((mode = *(uint16_t *)vbe_farptr(farptr)) != 0xffff) {
239 safety++;
240 farptr += 2;
241 if (safety == 100)
242 return 0;
243 if (biosvbe_get_mode_info(mode, &mi) != 0x004f)
244 continue;
245 /* we only care about linear modes here */
246 if (vbe_mode_is_supported(&mi) == 0)
247 continue;
248 safety = 0;
249 if (mi.XResolution == x &&
250 mi.YResolution == y &&
251 mi.BitsPerPixel == depth)
252 return mode;
253 }
254
255 return 0;
256 }
257
258 static int
259 vbe_find_mode(char *str)
260 {
261 int x, y, depth;
262
263 if (!vbe_parse_mode_str(str, &x, &y, &depth))
264 return 0;
265
266 return vbe_find_mode_xyd(x, y, depth);
267 }
268
269 static void
270 vbe_dump_mode(int modenum, struct modeinfoblock *mi)
271 {
272 printf("0x%x=%dx%dx%d", modenum,
273 mi->XResolution, mi->YResolution, mi->BitsPerPixel);
274 }
275
276 static int
277 vbe_get_edid(int *pwidth, int *pheight)
278 {
279 const uint8_t magic[] = EDID_MAGIC;
280 int ddc_caps, ret;
281
282 ddc_caps = biosvbe_ddc_caps();
283 if (ddc_caps == 0) {
284 return 1;
285 }
286
287 if (vbe_edid == NULL) {
288 vbe_edid = alloc(128);
289 }
290 if (vbe_edid_valid == 0) {
291 ret = biosvbe_ddc_read_edid(0, vbe_edid);
292 if (ret != 0x004f)
293 return 1;
294 if (memcmp(vbe_edid, magic, sizeof(magic)) != 0)
295 return 1;
296 vbe_edid_valid = 1;
297 }
298
299 *pwidth = vbe_edid[EDID_DESC_BLOCK + 2] |
300 (((int)vbe_edid[EDID_DESC_BLOCK + 4] & 0xf0) << 4);
301 *pheight = vbe_edid[EDID_DESC_BLOCK + 5] |
302 (((int)vbe_edid[EDID_DESC_BLOCK + 7] & 0xf0) << 4);
303
304 return 0;
305 }
306
307 void
308 vbe_modelist(void)
309 {
310 struct vbeinfoblock vbe;
311 struct modeinfoblock mi;
312 uint32_t farptr;
313 uint16_t mode;
314 int nmodes = 0, safety = 0;
315 int ddc_caps, edid_width, edid_height;
316
317 if (!vbe_check())
318 return;
319
320 ddc_caps = biosvbe_ddc_caps();
321 if (ddc_caps & 3) {
322 printf("DDC");
323 if (ddc_caps & 1)
324 printf(" [DDC1]");
325 if (ddc_caps & 2)
326 printf(" [DDC2]");
327
328 if (vbe_get_edid(&edid_width, &edid_height) != 0)
329 printf(": no EDID information\n");
330 else
331 printf(": EDID %dx%d\n", edid_width, edid_height);
332 }
333
334 printf("Modes: ");
335 memset(&vbe, 0, sizeof(vbe));
336 memcpy(vbe.VbeSignature, "VBE2", 4);
337 if (biosvbe_info(&vbe) != 0x004f)
338 goto done;
339 if (memcmp(vbe.VbeSignature, "VESA", 4) != 0)
340 goto done;
341 farptr = vbe.VideoModePtr;
342 if (farptr == 0)
343 goto done;
344
345 while ((mode = *(uint16_t *)vbe_farptr(farptr)) != 0xffff) {
346 safety++;
347 farptr += 2;
348 if (safety == 100) {
349 printf("[?] ");
350 break;
351 }
352 if (biosvbe_get_mode_info(mode, &mi) != 0x004f)
353 continue;
354 /* we only care about linear modes here */
355 if (vbe_mode_is_supported(&mi) == 0)
356 continue;
357 safety = 0;
358 if (nmodes % 4 == 0)
359 printf("\n");
360 else
361 printf(" ");
362 vbe_dump_mode(mode, &mi);
363 nmodes++;
364 }
365
366 done:
367 if (nmodes == 0)
368 printf("none found");
369 printf("\n");
370 }
371
372 void
373 command_vesa(char *cmd)
374 {
375 char arg[20];
376 int modenum, edid_width, edid_height;
377
378 if (!vbe_check())
379 return;
380
381 strlcpy(arg, cmd, sizeof(arg));
382
383 if (strcmp(arg, "list") == 0) {
384 vbe_modelist();
385 return;
386 }
387
388 if (strcmp(arg, "disabled") == 0 || strcmp(arg, "off") == 0) {
389 vbestate.modenum = 0;
390 return;
391 }
392
393 if (strcmp(arg, "enabled") == 0 || strcmp(arg, "on") == 0) {
394 if (vbe_get_edid(&edid_width, &edid_height) != 0) {
395 modenum = VBE_DEFAULT_MODE;
396 } else {
397 modenum = vbe_find_mode_xyd(edid_width, edid_height, 8);
398 if (modenum == 0)
399 modenum = VBE_DEFAULT_MODE;
400 }
401 } else if (strncmp(arg, "0x", 2) == 0) {
402 modenum = strtoul(arg, NULL, 0);
403 } else if (strchr(arg, 'x') != NULL) {
404 modenum = vbe_find_mode(arg);
405 if (modenum == 0) {
406 printf("mode %s not supported by firmware\n", arg);
407 return;
408 }
409 } else {
410 modenum = 0;
411 }
412
413 if (modenum >= 0x100) {
414 vbestate.modenum = modenum;
415 return;
416 }
417
418 printf("invalid flag, must be 'on', 'off', 'list', "
419 "a display mode, or a VBE mode number\n");
420 }
421