1/* $NetBSD: vbe.c,v 1.6 2010/06/25 15:35:08 tsutsui 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 39extern const uint8_t rasops_cmap[]; 40static uint8_t *vbe_edid = NULL; 41static int vbe_edid_valid = 0; 42 43static struct _vbestate { 44 int available; 45 int modenum; 46} vbestate; 47 48static int 49vbe_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 65static bool 66vbe_check(void) 67{ 68 if (!vbestate.available) { 69 printf("VBE not available\n"); 70 return false; 71 } 72 return true; 73} 74 75void 76vbe_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 91int 92vbe_available(void) 93{ 94 return vbestate.available; 95} 96 97int 98vbe_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 116int 117vbe_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 fb.physaddr = (uint64_t)mi.PhysBasePtr & 0xffffffff; 149 fb.width = mi.XResolution; 150 fb.height = mi.YResolution; 151 fb.stride = mi.BytesPerScanLine; 152 fb.depth = mi.BitsPerPixel; 153 fb.flags = 0; 154 fb.rnum = mi.RedMaskSize; 155 fb.rpos = mi.RedFieldPosition; 156 fb.gnum = mi.GreenMaskSize; 157 fb.gpos = mi.GreenFieldPosition; 158 fb.bnum = mi.BlueMaskSize; 159 fb.bpos = mi.BlueFieldPosition; 160 fb.vbemode = modenum; 161 162 framebuffer_configure(&fb); 163 164 return 0; 165} 166 167int 168vbe_commit(void) 169{ 170 int ret = 1; 171 172 if (vbestate.modenum > 0) { 173 ret = vbe_set_mode(vbestate.modenum); 174 if (ret) { 175 printf("WARNING: failed to set VBE mode 0x%x\n", 176 vbestate.modenum); 177 wait_sec(5); 178 } 179 } 180 return ret; 181} 182 183static void * 184vbe_farptr(uint32_t farptr) 185{ 186 return VBEPHYPTR((((farptr & 0xffff0000) >> 12) + (farptr & 0xffff))); 187} 188 189static int 190vbe_parse_mode_str(char *str, int *x, int *y, int *depth) 191{ 192 char *p; 193 194 p = str; 195 *x = strtoul(p, NULL, 0); 196 if (*x == 0) 197 return 0; 198 p = strchr(p, 'x'); 199 if (!p) 200 return 0; 201 ++p; 202 *y = strtoul(p, NULL, 0); 203 if (*y == 0) 204 return 0; 205 p = strchr(p, 'x'); 206 if (!p) 207 *depth = 8; 208 else { 209 ++p; 210 *depth = strtoul(p, NULL, 0); 211 if (*depth == 0) 212 return 0; 213 } 214 215 return 1; 216} 217 218static int 219vbe_find_mode_xyd(int x, int y, int depth) 220{ 221 struct vbeinfoblock vbe; 222 struct modeinfoblock mi; 223 uint32_t farptr; 224 uint16_t mode; 225 int safety = 0; 226 227 memset(&vbe, 0, sizeof(vbe)); 228 memcpy(vbe.VbeSignature, "VBE2", 4); 229 if (biosvbe_info(&vbe) != 0x004f) 230 return 0; 231 if (memcmp(vbe.VbeSignature, "VESA", 4) != 0) 232 return 0; 233 farptr = vbe.VideoModePtr; 234 if (farptr == 0) 235 return 0; 236 237 while ((mode = *(uint16_t *)vbe_farptr(farptr)) != 0xffff) { 238 safety++; 239 farptr += 2; 240 if (safety == 100) 241 return 0; 242 if (biosvbe_get_mode_info(mode, &mi) != 0x004f) 243 continue; 244 /* we only care about linear modes here */ 245 if (vbe_mode_is_supported(&mi) == 0) 246 continue; 247 safety = 0; 248 if (mi.XResolution == x && 249 mi.YResolution == y && 250 mi.BitsPerPixel == depth) 251 return mode; 252 } 253 254 return 0; 255} 256 257static int 258vbe_find_mode(char *str) 259{ 260 int x, y, depth; 261 262 if (!vbe_parse_mode_str(str, &x, &y, &depth)) 263 return 0; 264 265 return vbe_find_mode_xyd(x, y, depth); 266} 267 268static void 269vbe_dump_mode(int modenum, struct modeinfoblock *mi) 270{ 271 printf("0x%x=%dx%dx%d", modenum, 272 mi->XResolution, mi->YResolution, mi->BitsPerPixel); 273} 274 275static int 276vbe_get_edid(int *pwidth, int *pheight) 277{ 278 const uint8_t magic[] = EDID_MAGIC; 279 int ddc_caps, ret; 280 281 ddc_caps = biosvbe_ddc_caps(); 282 if (ddc_caps == 0) { 283 return 1; 284 } 285 286 if (vbe_edid == NULL) { 287 vbe_edid = alloc(128); 288 } 289 if (vbe_edid_valid == 0) { 290 ret = biosvbe_ddc_read_edid(0, vbe_edid); 291 if (ret != 0x004f) 292 return 1; 293 if (memcmp(vbe_edid, magic, sizeof(magic)) != 0) 294 return 1; 295 vbe_edid_valid = 1; 296 } 297 298 *pwidth = vbe_edid[EDID_DESC_BLOCK + 2] | 299 (((int)vbe_edid[EDID_DESC_BLOCK + 4] & 0xf0) << 4); 300 *pheight = vbe_edid[EDID_DESC_BLOCK + 5] | 301 (((int)vbe_edid[EDID_DESC_BLOCK + 7] & 0xf0) << 4); 302 303 return 0; 304} 305 306void 307vbe_modelist(void) 308{ 309 struct vbeinfoblock vbe; 310 struct modeinfoblock mi; 311 uint32_t farptr; 312 uint16_t mode; 313 int nmodes = 0, safety = 0; 314 int ddc_caps, edid_width, edid_height; 315 316 if (!vbe_check()) 317 return; 318 319 ddc_caps = biosvbe_ddc_caps(); 320 if (ddc_caps & 3) { 321 printf("DDC"); 322 if (ddc_caps & 1) 323 printf(" [DDC1]"); 324 if (ddc_caps & 2) 325 printf(" [DDC2]"); 326 327 if (vbe_get_edid(&edid_width, &edid_height) != 0) 328 printf(": no EDID information\n"); 329 else 330 printf(": EDID %dx%d\n", edid_width, edid_height); 331 } 332 333 printf("Modes: "); 334 memset(&vbe, 0, sizeof(vbe)); 335 memcpy(vbe.VbeSignature, "VBE2", 4); 336 if (biosvbe_info(&vbe) != 0x004f) 337 goto done; 338 if (memcmp(vbe.VbeSignature, "VESA", 4) != 0) 339 goto done; 340 farptr = vbe.VideoModePtr; 341 if (farptr == 0) 342 goto done; 343 344 while ((mode = *(uint16_t *)vbe_farptr(farptr)) != 0xffff) { 345 safety++; 346 farptr += 2; 347 if (safety == 100) { 348 printf("[?] "); 349 break; 350 } 351 if (biosvbe_get_mode_info(mode, &mi) != 0x004f) 352 continue; 353 /* we only care about linear modes here */ 354 if (vbe_mode_is_supported(&mi) == 0) 355 continue; 356 safety = 0; 357 if (nmodes % 4 == 0) 358 printf("\n"); 359 else 360 printf(" "); 361 vbe_dump_mode(mode, &mi); 362 nmodes++; 363 } 364 365done: 366 if (nmodes == 0) 367 printf("none found"); 368 printf("\n"); 369} 370 371void 372command_vesa(char *cmd) 373{ 374 char arg[20]; 375 int modenum, edid_width, edid_height; 376 377 if (!vbe_check()) 378 return; 379 380 strlcpy(arg, cmd, sizeof(arg)); 381 382 if (strcmp(arg, "list") == 0) { 383 vbe_modelist(); 384 return; 385 } 386 387 if (strcmp(arg, "disabled") == 0 || strcmp(arg, "off") == 0) { 388 vbestate.modenum = 0; 389 return; 390 } 391 392 if (strcmp(arg, "enabled") == 0 || strcmp(arg, "on") == 0) { 393 if (vbe_get_edid(&edid_width, &edid_height) != 0) { 394 modenum = VBE_DEFAULT_MODE; 395 } else { 396 modenum = vbe_find_mode_xyd(edid_width, edid_height, 8); 397 if (modenum == 0) 398 modenum = VBE_DEFAULT_MODE; 399 } 400 } else if (strncmp(arg, "0x", 2) == 0) { 401 modenum = strtoul(arg, NULL, 0); 402 } else if (strchr(arg, 'x') != NULL) { 403 modenum = vbe_find_mode(arg); 404 if (modenum == 0) { 405 printf("mode %s not supported by firmware\n", arg); 406 return; 407 } 408 } else { 409 modenum = 0; 410 } 411 412 if (modenum >= 0x100) { 413 vbestate.modenum = modenum; 414 return; 415 } 416 417 printf("invalid flag, must be 'on', 'off', " 418 "a display mode, or a VBE mode number\n"); 419} 420