1 /* $NetBSD: vbe.c,v 1.9 2017/01/24 11:09:14 nonaka Exp $ */ 2 3 /*- 4 * Copyright (c) 2009 Jared D. McNeill <jmcneill@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