usb_ecm: Use the current configuration instead of a fixed one.
[haiku.git] / src / add-ons / kernel / drivers / graphics / vesa / vesa.cpp
blob0a3dd144558b8f992e40f13849f1be7b0a95818a
1 /*
2 * Copyright 2005-2009, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
7 #include "vesa_private.h"
8 #include "vesa.h"
10 #include <string.h>
12 #include <drivers/bios.h>
14 #include <boot_item.h>
15 #include <frame_buffer_console.h>
16 #include <util/kernel_cpp.h>
17 #include <vm/vm.h>
19 #include "driver.h"
20 #include "utility.h"
21 #include "vesa_info.h"
24 static bios_module_info* sBIOSModule;
27 /*! Loads the BIOS module and sets up a state for it. The BIOS module is only
28 loaded when we need it, as it is quite a large module.
30 static status_t
31 vbe_call_prepare(bios_state** state)
33 status_t status;
35 status = get_module(B_BIOS_MODULE_NAME, (module_info**)&sBIOSModule);
36 if (status != B_OK) {
37 dprintf(DEVICE_NAME ": failed to get BIOS module: %s\n",
38 strerror(status));
39 return status;
42 status = sBIOSModule->prepare(state);
43 if (status != B_OK) {
44 dprintf(DEVICE_NAME ": failed to prepare BIOS state: %s\n",
45 strerror(status));
46 put_module(B_BIOS_MODULE_NAME);
49 return status;
53 static void
54 vbe_call_finish(bios_state* state)
56 sBIOSModule->finish(state);
57 put_module(B_BIOS_MODULE_NAME);
61 static status_t
62 find_graphics_card(addr_t frameBuffer, addr_t& base, size_t& size)
64 // TODO: when we port this over to the new driver API, this mechanism can be
65 // used to find the right device_node
66 pci_module_info* pci;
67 if (get_module(B_PCI_MODULE_NAME, (module_info**)&pci) != B_OK)
68 return B_ERROR;
70 pci_info info;
71 for (int32 index = 0; pci->get_nth_pci_info(index, &info) == B_OK; index++) {
72 if (info.class_base != PCI_display)
73 continue;
75 // check PCI BARs
76 for (uint32 i = 0; i < 6; i++) {
77 if (info.u.h0.base_registers[i] <= frameBuffer
78 && info.u.h0.base_registers[i] + info.u.h0.base_register_sizes[i]
79 > frameBuffer) {
80 // found it!
81 base = info.u.h0.base_registers[i];
82 size = info.u.h0.base_register_sizes[i];
84 put_module(B_PCI_MODULE_NAME);
85 return B_OK;
90 put_module(B_PCI_MODULE_NAME);
91 return B_ENTRY_NOT_FOUND;
95 static uint32
96 get_color_space_for_depth(uint32 depth)
98 switch (depth) {
99 case 1:
100 return B_GRAY1;
101 case 4:
102 return B_GRAY8;
103 // the app_server is smart enough to translate this to VGA mode
104 case 8:
105 return B_CMAP8;
106 case 15:
107 return B_RGB15;
108 case 16:
109 return B_RGB16;
110 case 24:
111 return B_RGB24;
112 case 32:
113 return B_RGB32;
116 return 0;
120 static status_t
121 vbe_get_mode_info(bios_state* state, uint16 mode, struct vbe_mode_info* modeInfo)
123 void* vbeModeInfo = sBIOSModule->allocate_mem(state,
124 sizeof(struct vbe_mode_info));
125 if (vbeModeInfo == NULL)
126 return B_NO_MEMORY;
127 memset(vbeModeInfo, 0, sizeof(vbe_mode_info));
129 uint32 physicalAddress = sBIOSModule->physical_address(state, vbeModeInfo);
130 bios_regs regs = {};
131 regs.eax = 0x4f01;
132 regs.ecx = mode;
133 regs.es = physicalAddress >> 4;
134 regs.edi = physicalAddress - (regs.es << 4);
136 status_t status = sBIOSModule->interrupt(state, 0x10, &regs);
137 if (status != B_OK) {
138 dprintf(DEVICE_NAME ": vbe_get_mode_info(%u): BIOS failed: %s\n", mode,
139 strerror(status));
140 return status;
143 if ((regs.eax & 0xffff) != 0x4f) {
144 dprintf(DEVICE_NAME ": vbe_get_mode_info(%u): BIOS returned "
145 "0x%04" B_PRIx32 "\n", mode, regs.eax & 0xffff);
146 return B_ENTRY_NOT_FOUND;
149 memcpy(modeInfo, vbeModeInfo, sizeof(struct vbe_mode_info));
150 return B_OK;
154 static status_t
155 vbe_set_mode(bios_state* state, uint16 mode)
157 bios_regs regs = {};
158 regs.eax = 0x4f02;
159 regs.ebx = (mode & SET_MODE_MASK) | SET_MODE_LINEAR_BUFFER;
161 status_t status = sBIOSModule->interrupt(state, 0x10, &regs);
162 if (status != B_OK) {
163 dprintf(DEVICE_NAME ": vbe_set_mode(%u): BIOS failed: %s\n", mode,
164 strerror(status));
165 return status;
168 if ((regs.eax & 0xffff) != 0x4f) {
169 dprintf(DEVICE_NAME ": vbe_set_mode(%u): BIOS returned 0x%04" B_PRIx32
170 "\n", mode, regs.eax & 0xffff);
171 return B_ERROR;
174 return B_OK;
178 static uint32
179 vbe_to_system_dpms(uint8 vbeMode)
181 uint32 mode = 0;
182 if ((vbeMode & (DPMS_OFF | DPMS_REDUCED_ON)) != 0)
183 mode |= B_DPMS_OFF;
184 if ((vbeMode & DPMS_STANDBY) != 0)
185 mode |= B_DPMS_STAND_BY;
186 if ((vbeMode & DPMS_SUSPEND) != 0)
187 mode |= B_DPMS_SUSPEND;
189 return mode;
193 static status_t
194 vbe_get_dpms_capabilities(uint32& vbeMode, uint32& mode)
196 // we always return a valid mode
197 vbeMode = 0;
198 mode = B_DPMS_ON;
200 // Prepare BIOS environment
201 bios_state* state;
202 status_t status = vbe_call_prepare(&state);
203 if (status != B_OK)
204 return status;
206 bios_regs regs = {};
207 regs.eax = 0x4f10;
208 regs.ebx = 0;
209 regs.esi = 0;
210 regs.edi = 0;
212 status = sBIOSModule->interrupt(state, 0x10, &regs);
213 if (status != B_OK) {
214 dprintf(DEVICE_NAME ": vbe_get_dpms_capabilities(): BIOS failed: %s\n",
215 strerror(status));
216 goto out;
219 if ((regs.eax & 0xffff) != 0x4f) {
220 dprintf(DEVICE_NAME ": vbe_get_dpms_capabilities(): BIOS returned "
221 "0x%04" B_PRIx32 "\n", regs.eax & 0xffff);
222 status = B_ERROR;
223 goto out;
226 vbeMode = regs.ebx >> 8;
227 mode = vbe_to_system_dpms(vbeMode);
229 out:
230 vbe_call_finish(state);
231 return status;
235 static status_t
236 vbe_set_bits_per_gun(bios_state* state, vesa_info& info, uint8 bits)
238 info.bits_per_gun = 6;
240 bios_regs regs = {};
241 regs.eax = 0x4f08;
242 regs.ebx = (bits << 8) | 1;
244 status_t status = sBIOSModule->interrupt(state, 0x10, &regs);
245 if (status != B_OK) {
246 dprintf(DEVICE_NAME ": vbe_set_bits_per_gun(): BIOS failed: %s\n",
247 strerror(status));
248 return status;
251 if ((regs.eax & 0xffff) != 0x4f) {
252 dprintf(DEVICE_NAME ": vbe_set_bits_per_gun(): BIOS returned "
253 "0x%04" B_PRIx32 "\n", regs.eax & 0xffff);
254 return B_ERROR;
257 info.bits_per_gun = regs.ebx >> 8;
258 return B_OK;
262 static status_t
263 vbe_set_bits_per_gun(vesa_info& info, uint8 bits)
265 info.bits_per_gun = 6;
267 bios_state* state;
268 status_t status = vbe_call_prepare(&state);
269 if (status != B_OK)
270 return status;
272 status = vbe_set_bits_per_gun(state, info, bits);
274 vbe_call_finish(state);
275 return status;
279 /*! Remaps the frame buffer if necessary; if we've already mapped the complete
280 frame buffer, there is no need to map it again.
282 static status_t
283 remap_frame_buffer(vesa_info& info, addr_t physicalBase, uint32 width,
284 uint32 height, int8 depth, uint32 bytesPerRow, bool initializing)
286 vesa_shared_info& sharedInfo = *info.shared_info;
287 addr_t frameBuffer = info.frame_buffer;
289 if (!info.complete_frame_buffer_mapped) {
290 addr_t base = physicalBase;
291 size_t size = bytesPerRow * height;
292 bool remap = !initializing;
294 if (info.physical_frame_buffer_size != 0) {
295 // we can map the complete frame buffer
296 base = info.physical_frame_buffer;
297 size = info.physical_frame_buffer_size;
298 remap = true;
301 if (remap) {
302 area_id area = map_physical_memory("vesa frame buffer", base,
303 size, B_ANY_KERNEL_ADDRESS, B_READ_AREA | B_WRITE_AREA,
304 (void**)&frameBuffer);
305 if (area < 0)
306 return area;
308 if (initializing) {
309 // We need to manually update the kernel's frame buffer address,
310 // since this frame buffer remapping has not been issued by the
311 // app_server (which would otherwise take care of this)
312 frame_buffer_update(frameBuffer, width, height, depth,
313 bytesPerRow);
316 delete_area(info.shared_info->frame_buffer_area);
318 info.frame_buffer = frameBuffer;
319 sharedInfo.frame_buffer_area = area;
321 // Turn on write combining for the area
322 vm_set_area_memory_type(area, base, B_MTR_WC);
324 if (info.physical_frame_buffer_size != 0)
325 info.complete_frame_buffer_mapped = true;
329 if (info.complete_frame_buffer_mapped)
330 frameBuffer += physicalBase - info.physical_frame_buffer;
332 // Update shared frame buffer information
333 sharedInfo.frame_buffer = (uint8*)frameBuffer;
334 sharedInfo.physical_frame_buffer = (uint8*)physicalBase;
335 sharedInfo.bytes_per_row = bytesPerRow;
337 return B_OK;
341 // #pragma mark -
344 status_t
345 vesa_init(vesa_info& info)
347 frame_buffer_boot_info* bufferInfo
348 = (frame_buffer_boot_info*)get_boot_item(FRAME_BUFFER_BOOT_INFO, NULL);
349 if (bufferInfo == NULL)
350 return B_ERROR;
352 info.vbe_capabilities = bufferInfo->vesa_capabilities;
353 info.complete_frame_buffer_mapped = false;
355 // Find out which PCI device we belong to, so that we know its frame buffer
356 // size
357 find_graphics_card(bufferInfo->physical_frame_buffer,
358 info.physical_frame_buffer, info.physical_frame_buffer_size);
360 size_t modesSize = 0;
361 vesa_mode* modes = (vesa_mode*)get_boot_item(VESA_MODES_BOOT_INFO,
362 &modesSize);
363 info.modes = modes;
365 size_t sharedSize = (sizeof(vesa_shared_info) + 7) & ~7;
367 info.shared_area = create_area("vesa shared info",
368 (void**)&info.shared_info, B_ANY_KERNEL_ADDRESS,
369 ROUND_TO_PAGE_SIZE(sharedSize + modesSize), B_FULL_LOCK,
370 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_USER_CLONEABLE_AREA);
371 if (info.shared_area < 0)
372 return info.shared_area;
374 vesa_shared_info& sharedInfo = *info.shared_info;
376 memset(&sharedInfo, 0, sizeof(vesa_shared_info));
378 if (modes != NULL) {
379 sharedInfo.vesa_mode_offset = sharedSize;
380 sharedInfo.vesa_mode_count = modesSize / sizeof(vesa_mode);
382 memcpy((uint8*)&sharedInfo + sharedSize, modes, modesSize);
385 sharedInfo.frame_buffer_area = bufferInfo->area;
387 remap_frame_buffer(info, bufferInfo->physical_frame_buffer,
388 bufferInfo->width, bufferInfo->height, bufferInfo->depth,
389 bufferInfo->bytes_per_row, true);
390 // Does not matter if this fails - the frame buffer was already mapped
391 // before.
393 sharedInfo.current_mode.virtual_width = bufferInfo->width;
394 sharedInfo.current_mode.virtual_height = bufferInfo->height;
395 sharedInfo.current_mode.space = get_color_space_for_depth(
396 bufferInfo->depth);
398 edid1_info* edidInfo = (edid1_info*)get_boot_item(VESA_EDID_BOOT_INFO,
399 NULL);
400 if (edidInfo != NULL) {
401 sharedInfo.has_edid = true;
402 memcpy(&sharedInfo.edid_info, edidInfo, sizeof(edid1_info));
405 vbe_get_dpms_capabilities(info.vbe_dpms_capabilities,
406 sharedInfo.dpms_capabilities);
407 if (bufferInfo->depth <= 8)
408 vbe_set_bits_per_gun(info, 8);
410 dprintf(DEVICE_NAME ": vesa_init() completed successfully!\n");
411 return B_OK;
415 void
416 vesa_uninit(vesa_info& info)
418 dprintf(DEVICE_NAME": vesa_uninit()\n");
420 delete_area(info.shared_info->frame_buffer_area);
421 delete_area(info.shared_area);
425 status_t
426 vesa_set_display_mode(vesa_info& info, uint32 mode)
428 if (mode >= info.shared_info->vesa_mode_count)
429 return B_ENTRY_NOT_FOUND;
431 // Prepare BIOS environment
432 bios_state* state;
433 status_t status = vbe_call_prepare(&state);
434 if (status != B_OK)
435 return status;
437 // Get mode information
438 struct vbe_mode_info modeInfo;
439 status = vbe_get_mode_info(state, info.modes[mode].mode, &modeInfo);
440 if (status != B_OK) {
441 dprintf(DEVICE_NAME": vesa_set_display_mode(): cannot get mode info\n");
442 goto out;
445 // Set mode
446 status = vbe_set_mode(state, info.modes[mode].mode);
447 if (status != B_OK) {
448 dprintf(DEVICE_NAME": vesa_set_display_mode(): cannot set mode\n");
449 goto out;
452 if (info.modes[mode].bits_per_pixel <= 8)
453 vbe_set_bits_per_gun(state, info, 8);
455 // Map new frame buffer if necessary
457 status = remap_frame_buffer(info, modeInfo.physical_base, modeInfo.width,
458 modeInfo.height, modeInfo.bits_per_pixel, modeInfo.bytes_per_row,
459 false);
460 if (status == B_OK) {
461 // Update shared frame buffer information
462 info.shared_info->current_mode.virtual_width = modeInfo.width;
463 info.shared_info->current_mode.virtual_height = modeInfo.height;
464 info.shared_info->current_mode.space = get_color_space_for_depth(
465 modeInfo.bits_per_pixel);
468 out:
469 vbe_call_finish(state);
470 return status;
474 status_t
475 vesa_get_dpms_mode(vesa_info& info, uint32& mode)
477 mode = B_DPMS_ON;
478 // we always return a valid mode
480 // Prepare BIOS environment
481 bios_state* state;
482 status_t status = vbe_call_prepare(&state);
483 if (status != B_OK)
484 return status;
486 bios_regs regs = {};
487 regs.eax = 0x4f10;
488 regs.ebx = 2;
489 regs.esi = 0;
490 regs.edi = 0;
492 status = sBIOSModule->interrupt(state, 0x10, &regs);
493 if (status != B_OK) {
494 dprintf(DEVICE_NAME ": vesa_get_dpms_mode(): BIOS failed: %s\n",
495 strerror(status));
496 goto out;
499 if ((regs.eax & 0xffff) != 0x4f) {
500 dprintf(DEVICE_NAME ": vesa_get_dpms_mode(): BIOS returned "
501 "0x%" B_PRIx32 "\n", regs.eax & 0xffff);
502 status = B_ERROR;
503 goto out;
506 mode = vbe_to_system_dpms(regs.ebx >> 8);
508 out:
509 vbe_call_finish(state);
510 return status;
514 status_t
515 vesa_set_dpms_mode(vesa_info& info, uint32 mode)
517 // Only let supported modes through
518 mode &= info.shared_info->dpms_capabilities;
520 uint8 vbeMode = 0;
521 if ((mode & B_DPMS_OFF) != 0)
522 vbeMode |= DPMS_OFF | DPMS_REDUCED_ON;
523 if ((mode & B_DPMS_STAND_BY) != 0)
524 vbeMode |= DPMS_STANDBY;
525 if ((mode & B_DPMS_SUSPEND) != 0)
526 vbeMode |= DPMS_SUSPEND;
528 vbeMode &= info.vbe_dpms_capabilities;
530 // Prepare BIOS environment
531 bios_state* state;
532 status_t status = vbe_call_prepare(&state);
533 if (status != B_OK)
534 return status;
536 bios_regs regs = {};
537 regs.eax = 0x4f10;
538 regs.ebx = (vbeMode << 8) | 1;
539 regs.esi = 0;
540 regs.edi = 0;
542 status = sBIOSModule->interrupt(state, 0x10, &regs);
543 if (status != B_OK) {
544 dprintf(DEVICE_NAME ": vesa_set_dpms_mode(): BIOS failed: %s\n",
545 strerror(status));
546 goto out;
549 if ((regs.eax & 0xffff) != 0x4f) {
550 dprintf(DEVICE_NAME ": vesa_set_dpms_mode(): BIOS returned "
551 "0x%04" B_PRIx32 "\n", regs.eax & 0xffff);
552 status = B_ERROR;
553 goto out;
556 out:
557 vbe_call_finish(state);
558 return status;
562 status_t
563 vesa_set_indexed_colors(vesa_info& info, uint8 first, uint8* colors,
564 uint16 count)
566 bios_regs regs = {};
567 uint32 shift, physicalAddress;
569 if (first + count > 256)
570 count = 256 - first;
572 // Prepare BIOS environment
573 bios_state* state;
574 status_t status = vbe_call_prepare(&state);
575 if (status != B_OK)
576 return status;
578 uint8* palette = (uint8*)sBIOSModule->allocate_mem(state, 256 * 4);
579 if (palette == NULL) {
580 status = B_NO_MEMORY;
581 goto out;
584 shift = 8 - info.bits_per_gun;
586 // convert colors to VESA palette
587 for (int32 i = first; i < count; i++) {
588 uint8 color[3];
589 if (user_memcpy(color, &colors[i * 3], 3) < B_OK) {
590 status = B_BAD_ADDRESS;
591 goto out;
594 // order is BGR-
595 palette[i * 4 + 0] = color[2] >> shift;
596 palette[i * 4 + 1] = color[1] >> shift;
597 palette[i * 4 + 2] = color[0] >> shift;
598 palette[i * 4 + 3] = 0;
601 // set palette
602 physicalAddress = sBIOSModule->physical_address(state, palette);
603 regs.eax = 0x4f09;
604 regs.ebx = 0;
605 regs.ecx = count;
606 regs.edx = first;
607 regs.es = physicalAddress >> 4;
608 regs.edi = physicalAddress - (regs.es << 4);
610 status = sBIOSModule->interrupt(state, 0x10, &regs);
611 if (status != B_OK) {
612 dprintf(DEVICE_NAME ": vesa_set_indexed_colors(): BIOS failed: %s\n",
613 strerror(status));
614 goto out;
617 if ((regs.eax & 0xffff) != 0x4f) {
618 dprintf(DEVICE_NAME ": vesa_set_indexed_colors(): BIOS returned "
619 "0x%04" B_PRIx32 "\n", regs.eax & 0xffff);
620 status = B_ERROR;
623 out:
624 vbe_call_finish(state);
625 return status;