1 /* 2 * Copyright 2005-2009, Axel Dörfler, axeld@pinc-software.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "vesa_private.h" 8 #include "vesa.h" 9 10 #include <string.h> 11 12 #include <boot_item.h> 13 #include <frame_buffer_console.h> 14 #include <util/kernel_cpp.h> 15 #include <arch/x86/vm86.h> 16 #include <vm.h> 17 18 #include "driver.h" 19 #include "utility.h" 20 #include "vesa_info.h" 21 22 23 static uint32 24 get_color_space_for_depth(uint32 depth) 25 { 26 switch (depth) { 27 case 4: 28 return B_GRAY8; 29 // the app_server is smart enough to translate this to VGA mode 30 case 8: 31 return B_CMAP8; 32 case 15: 33 return B_RGB15; 34 case 16: 35 return B_RGB16; 36 case 24: 37 return B_RGB24; 38 case 32: 39 return B_RGB32; 40 } 41 42 return 0; 43 } 44 45 46 static status_t 47 vbe_get_mode_info(struct vm86_state& vmState, uint16 mode, 48 struct vbe_mode_info* modeInfo) 49 { 50 struct vbe_mode_info* vbeModeInfo = (struct vbe_mode_info*)0x1000; 51 52 memset(vbeModeInfo, 0, sizeof(vbe_mode_info)); 53 vmState.regs.eax = 0x4f01; 54 vmState.regs.ecx = mode; 55 vmState.regs.es = 0x1000 >> 4; 56 vmState.regs.edi = 0x0000; 57 58 status_t status = vm86_do_int(&vmState, 0x10); 59 if (status != B_OK) { 60 dprintf(DEVICE_NAME ": vbe_get_mode_info(%u): vm86 failed\n", mode); 61 return status; 62 } 63 64 if ((vmState.regs.eax & 0xffff) != 0x4f) { 65 dprintf(DEVICE_NAME ": vbe_get_mode_info(): BIOS returned 0x%04lx\n", 66 vmState.regs.eax & 0xffff); 67 return B_ENTRY_NOT_FOUND; 68 } 69 70 memcpy(modeInfo, vbeModeInfo, sizeof(struct vbe_mode_info)); 71 return B_OK; 72 } 73 74 75 static status_t 76 vbe_set_mode(struct vm86_state& vmState, uint16 mode) 77 { 78 vmState.regs.eax = 0x4f02; 79 vmState.regs.ebx = (mode & SET_MODE_MASK) | SET_MODE_LINEAR_BUFFER; 80 81 status_t status = vm86_do_int(&vmState, 0x10); 82 if (status != B_OK) { 83 dprintf(DEVICE_NAME ": vbe_set_mode(%u): vm86 failed\n", mode); 84 return status; 85 } 86 87 if ((vmState.regs.eax & 0xffff) != 0x4f) { 88 dprintf(DEVICE_NAME ": vbe_set_mode(): BIOS returned 0x%04lx\n", 89 vmState.regs.eax & 0xffff); 90 return B_ERROR; 91 } 92 93 return B_OK; 94 } 95 96 97 static uint32 98 vbe_to_system_dpms(uint8 vbeMode) 99 { 100 uint32 mode = 0; 101 if ((vbeMode & (DPMS_OFF | DPMS_REDUCED_ON)) != 0) 102 mode |= B_DPMS_OFF; 103 if ((vbeMode & DPMS_STANDBY) != 0) 104 mode |= B_DPMS_STAND_BY; 105 if ((vbeMode & DPMS_SUSPEND) != 0) 106 mode |= B_DPMS_SUSPEND; 107 108 return mode; 109 } 110 111 112 static status_t 113 vbe_get_dpms_capabilities(uint32& vbeMode, uint32& mode) 114 { 115 // we always return a valid mode 116 vbeMode = 0; 117 mode = B_DPMS_ON; 118 119 // Prepare vm86 mode environment 120 struct vm86_state vmState; 121 status_t status = vm86_prepare(&vmState, 0x20000); 122 if (status != B_OK) { 123 dprintf(DEVICE_NAME": vbe_get_dpms_capabilities(): vm86_prepare " 124 "failed: %s\n", strerror(status)); 125 return status; 126 } 127 128 vmState.regs.eax = 0x4f10; 129 vmState.regs.ebx = 0; 130 vmState.regs.esi = 0; 131 vmState.regs.edi = 0; 132 133 status = vm86_do_int(&vmState, 0x10); 134 if (status != B_OK) { 135 dprintf(DEVICE_NAME ": vbe_get_dpms_capabilities(): vm86 failed\n"); 136 goto out; 137 } 138 139 if ((vmState.regs.eax & 0xffff) != 0x4f) { 140 dprintf(DEVICE_NAME ": vbe_get_dpms_capabilities(): BIOS returned " 141 "0x%04lx\n", vmState.regs.eax & 0xffff); 142 status = B_ERROR; 143 goto out; 144 } 145 146 vbeMode = vmState.regs.ebx >> 8; 147 mode = vbe_to_system_dpms(vbeMode); 148 149 out: 150 vm86_cleanup(&vmState); 151 return status; 152 } 153 154 155 // #pragma mark - 156 157 158 status_t 159 vesa_init(vesa_info& info) 160 { 161 frame_buffer_boot_info* bufferInfo 162 = (frame_buffer_boot_info*)get_boot_item(FRAME_BUFFER_BOOT_INFO, NULL); 163 if (bufferInfo == NULL) 164 return B_ERROR; 165 166 size_t modesSize = 0; 167 vesa_mode* modes = (vesa_mode*)get_boot_item(VESA_MODES_BOOT_INFO, 168 &modesSize); 169 info.modes = modes; 170 171 size_t sharedSize = (sizeof(vesa_shared_info) + 7) & ~7; 172 173 info.shared_area = create_area("vesa shared info", 174 (void**)&info.shared_info, B_ANY_KERNEL_ADDRESS, 175 ROUND_TO_PAGE_SIZE(sharedSize + modesSize), B_FULL_LOCK, 176 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_USER_CLONEABLE_AREA); 177 if (info.shared_area < B_OK) 178 return info.shared_area; 179 180 vesa_shared_info &sharedInfo = *info.shared_info; 181 182 memset(&sharedInfo, 0, sizeof(vesa_shared_info)); 183 184 if (modes != NULL) { 185 sharedInfo.vesa_mode_offset = sharedSize; 186 sharedInfo.vesa_mode_count = modesSize / sizeof(vesa_mode); 187 188 memcpy((uint8*)&sharedInfo + sharedSize, modes, modesSize); 189 } 190 191 sharedInfo.frame_buffer_area = bufferInfo->area; 192 sharedInfo.frame_buffer = (uint8*)bufferInfo->frame_buffer; 193 194 sharedInfo.current_mode.virtual_width = bufferInfo->width; 195 sharedInfo.current_mode.virtual_height = bufferInfo->height; 196 sharedInfo.current_mode.space = get_color_space_for_depth( 197 bufferInfo->depth); 198 sharedInfo.bytes_per_row = bufferInfo->bytes_per_row; 199 200 // TODO: we might want to do this via vm86 instead 201 edid1_info* edidInfo = (edid1_info*)get_boot_item(VESA_EDID_BOOT_INFO, 202 NULL); 203 if (edidInfo != NULL) { 204 sharedInfo.has_edid = true; 205 memcpy(&sharedInfo.edid_info, edidInfo, sizeof(edid1_info)); 206 } 207 208 vbe_get_dpms_capabilities(info.vbe_dpms_capabilities, 209 sharedInfo.dpms_capabilities); 210 211 physical_entry mapping; 212 get_memory_map((void*)sharedInfo.frame_buffer, B_PAGE_SIZE, &mapping, 1); 213 sharedInfo.physical_frame_buffer = (uint8*)mapping.address; 214 215 dprintf(DEVICE_NAME ": vesa_init() completed successfully!\n"); 216 return B_OK; 217 } 218 219 220 void 221 vesa_uninit(vesa_info& info) 222 { 223 dprintf(DEVICE_NAME": vesa_uninit()\n"); 224 225 delete_area(info.shared_info->frame_buffer_area); 226 delete_area(info.shared_area); 227 } 228 229 230 status_t 231 vesa_set_display_mode(vesa_info& info, uint32 mode) 232 { 233 if (mode >= info.shared_info->vesa_mode_count) 234 return B_ENTRY_NOT_FOUND; 235 236 // Prepare vm86 mode environment 237 struct vm86_state vmState; 238 status_t status = vm86_prepare(&vmState, 0x20000); 239 if (status != B_OK) { 240 dprintf(DEVICE_NAME": vesa_set_display_mode(): vm86_prepare failed\n"); 241 return status; 242 } 243 244 area_id frameBufferArea; 245 frame_buffer_boot_info* bufferInfo; 246 struct vbe_mode_info modeInfo; 247 248 // Get mode information 249 status = vbe_get_mode_info(vmState, info.modes[mode].mode, &modeInfo); 250 if (status != B_OK) { 251 dprintf(DEVICE_NAME": vesa_set_display_mode(): cannot get mode info\n"); 252 goto out; 253 } 254 255 // Set mode 256 status = vbe_set_mode(vmState, info.modes[mode].mode); 257 if (status != B_OK) { 258 dprintf(DEVICE_NAME": vesa_set_display_mode(): cannot set mode\n"); 259 goto out; 260 } 261 262 // Map new frame buffer 263 void* frameBuffer; 264 frameBufferArea = map_physical_memory("vesa_fb", 265 (void*)modeInfo.physical_base, modeInfo.bytes_per_row * modeInfo.height, 266 B_ANY_KERNEL_ADDRESS, B_READ_AREA | B_WRITE_AREA, &frameBuffer); 267 if (frameBufferArea < B_OK) { 268 status = (status_t)frameBufferArea; 269 goto out; 270 } 271 delete_area(info.shared_info->frame_buffer_area); 272 273 // Turn on write combining for the area 274 vm_set_area_memory_type(frameBufferArea, modeInfo.physical_base, B_MTR_WC); 275 276 // Update shared frame buffer information 277 info.shared_info->frame_buffer_area = frameBufferArea; 278 info.shared_info->frame_buffer = (uint8*)frameBuffer; 279 info.shared_info->physical_frame_buffer = (uint8*)modeInfo.physical_base; 280 info.shared_info->bytes_per_row = modeInfo.bytes_per_row; 281 info.shared_info->current_mode.virtual_width = modeInfo.width; 282 info.shared_info->current_mode.virtual_height = modeInfo.height; 283 info.shared_info->current_mode.space = get_color_space_for_depth( 284 modeInfo.bits_per_pixel); 285 286 // Update boot item as it's used in vesa_init() 287 bufferInfo 288 = (frame_buffer_boot_info*)get_boot_item(FRAME_BUFFER_BOOT_INFO, NULL); 289 bufferInfo->area = frameBufferArea; 290 bufferInfo->frame_buffer = (addr_t)frameBuffer; 291 bufferInfo->width = modeInfo.width; 292 bufferInfo->height = modeInfo.height; 293 bufferInfo->depth = modeInfo.bits_per_pixel; 294 bufferInfo->bytes_per_row = modeInfo.bytes_per_row; 295 296 out: 297 vm86_cleanup(&vmState); 298 return status; 299 } 300 301 302 status_t 303 vesa_get_dpms_mode(vesa_info& info, uint32& mode) 304 { 305 mode = B_DPMS_ON; 306 // we always return a valid mode 307 308 // Prepare vm86 mode environment 309 struct vm86_state vmState; 310 status_t status = vm86_prepare(&vmState, 0x20000); 311 if (status != B_OK) { 312 dprintf(DEVICE_NAME": vesa_get_dpms_mode(): vm86_prepare failed: %s\n", 313 strerror(status)); 314 return status; 315 } 316 317 vmState.regs.eax = 0x4f10; 318 vmState.regs.ebx = 2; 319 vmState.regs.esi = 0; 320 vmState.regs.edi = 0; 321 322 status = vm86_do_int(&vmState, 0x10); 323 if (status != B_OK) { 324 dprintf(DEVICE_NAME ": vesa_get_dpms_mode(): vm86 failed: %s\n", 325 strerror(status)); 326 goto out; 327 } 328 329 if ((vmState.regs.eax & 0xffff) != 0x4f) { 330 dprintf(DEVICE_NAME ": vesa_get_dpms_mode(): BIOS returned 0x%04lx\n", 331 vmState.regs.eax & 0xffff); 332 status = B_ERROR; 333 goto out; 334 } 335 336 mode = vbe_to_system_dpms(vmState.regs.ebx >> 8); 337 338 out: 339 vm86_cleanup(&vmState); 340 return status; 341 } 342 343 344 status_t 345 vesa_set_dpms_mode(vesa_info& info, uint32 mode) 346 { 347 // Only let supported modes through 348 mode &= info.shared_info->dpms_capabilities; 349 350 uint8 vbeMode = 0; 351 if ((mode & B_DPMS_OFF) != 0) 352 vbeMode |= DPMS_OFF | DPMS_REDUCED_ON; 353 if ((mode & B_DPMS_STAND_BY) != 0) 354 vbeMode |= DPMS_STANDBY; 355 if ((mode & B_DPMS_SUSPEND) != 0) 356 vbeMode |= DPMS_SUSPEND; 357 358 vbeMode &= info.vbe_dpms_capabilities; 359 360 // Prepare vm86 mode environment 361 struct vm86_state vmState; 362 status_t status = vm86_prepare(&vmState, 0x20000); 363 if (status != B_OK) { 364 dprintf(DEVICE_NAME": vesa_set_dpms_mode(): vm86_prepare failed: %s\n", 365 strerror(status)); 366 return status; 367 } 368 369 vmState.regs.eax = 0x4f10; 370 vmState.regs.ebx = (vbeMode << 8) | 1; 371 vmState.regs.esi = 0; 372 vmState.regs.edi = 0; 373 374 status = vm86_do_int(&vmState, 0x10); 375 if (status != B_OK) { 376 dprintf(DEVICE_NAME ": vesa_set_dpms_mode(): vm86 failed: %s\n", 377 strerror(status)); 378 goto out; 379 } 380 381 if ((vmState.regs.eax & 0xffff) != 0x4f) { 382 dprintf(DEVICE_NAME ": vesa_set_dpms_mode(): BIOS returned 0x%04lx\n", 383 vmState.regs.eax & 0xffff); 384 status = B_ERROR; 385 goto out; 386 } 387 388 out: 389 vm86_cleanup(&vmState); 390 return status; 391 } 392