2 * Copyright 2005-2009, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
7 #include "vesa_private.h"
12 #include <drivers/bios.h>
14 #include <boot_item.h>
15 #include <frame_buffer_console.h>
16 #include <util/kernel_cpp.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.
31 vbe_call_prepare(bios_state
** state
)
35 status
= get_module(B_BIOS_MODULE_NAME
, (module_info
**)&sBIOSModule
);
37 dprintf(DEVICE_NAME
": failed to get BIOS module: %s\n",
42 status
= sBIOSModule
->prepare(state
);
44 dprintf(DEVICE_NAME
": failed to prepare BIOS state: %s\n",
46 put_module(B_BIOS_MODULE_NAME
);
54 vbe_call_finish(bios_state
* state
)
56 sBIOSModule
->finish(state
);
57 put_module(B_BIOS_MODULE_NAME
);
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
67 if (get_module(B_PCI_MODULE_NAME
, (module_info
**)&pci
) != B_OK
)
71 for (int32 index
= 0; pci
->get_nth_pci_info(index
, &info
) == B_OK
; index
++) {
72 if (info
.class_base
!= PCI_display
)
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
]
81 base
= info
.u
.h0
.base_registers
[i
];
82 size
= info
.u
.h0
.base_register_sizes
[i
];
84 put_module(B_PCI_MODULE_NAME
);
90 put_module(B_PCI_MODULE_NAME
);
91 return B_ENTRY_NOT_FOUND
;
96 get_color_space_for_depth(uint32 depth
)
103 // the app_server is smart enough to translate this to VGA mode
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
)
127 memset(vbeModeInfo
, 0, sizeof(vbe_mode_info
));
129 uint32 physicalAddress
= sBIOSModule
->physical_address(state
, vbeModeInfo
);
133 regs
.es
= physicalAddress
>> 4;
134 regs
.edi
= physicalAddress
- (regs
.es
<< 4);
136 status_t status
= sBIOSModule
->interrupt(state
, 0x10, ®s
);
137 if (status
!= B_OK
) {
138 dprintf(DEVICE_NAME
": vbe_get_mode_info(%u): BIOS failed: %s\n", mode
,
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
));
155 vbe_set_mode(bios_state
* state
, uint16 mode
)
159 regs
.ebx
= (mode
& SET_MODE_MASK
) | SET_MODE_LINEAR_BUFFER
;
161 status_t status
= sBIOSModule
->interrupt(state
, 0x10, ®s
);
162 if (status
!= B_OK
) {
163 dprintf(DEVICE_NAME
": vbe_set_mode(%u): BIOS failed: %s\n", mode
,
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);
179 vbe_to_system_dpms(uint8 vbeMode
)
182 if ((vbeMode
& (DPMS_OFF
| DPMS_REDUCED_ON
)) != 0)
184 if ((vbeMode
& DPMS_STANDBY
) != 0)
185 mode
|= B_DPMS_STAND_BY
;
186 if ((vbeMode
& DPMS_SUSPEND
) != 0)
187 mode
|= B_DPMS_SUSPEND
;
194 vbe_get_dpms_capabilities(uint32
& vbeMode
, uint32
& mode
)
196 // we always return a valid mode
200 // Prepare BIOS environment
202 status_t status
= vbe_call_prepare(&state
);
212 status
= sBIOSModule
->interrupt(state
, 0x10, ®s
);
213 if (status
!= B_OK
) {
214 dprintf(DEVICE_NAME
": vbe_get_dpms_capabilities(): BIOS failed: %s\n",
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);
226 vbeMode
= regs
.ebx
>> 8;
227 mode
= vbe_to_system_dpms(vbeMode
);
230 vbe_call_finish(state
);
236 vbe_set_bits_per_gun(bios_state
* state
, vesa_info
& info
, uint8 bits
)
238 info
.bits_per_gun
= 6;
242 regs
.ebx
= (bits
<< 8) | 1;
244 status_t status
= sBIOSModule
->interrupt(state
, 0x10, ®s
);
245 if (status
!= B_OK
) {
246 dprintf(DEVICE_NAME
": vbe_set_bits_per_gun(): BIOS failed: %s\n",
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);
257 info
.bits_per_gun
= regs
.ebx
>> 8;
263 vbe_set_bits_per_gun(vesa_info
& info
, uint8 bits
)
265 info
.bits_per_gun
= 6;
268 status_t status
= vbe_call_prepare(&state
);
272 status
= vbe_set_bits_per_gun(state
, info
, bits
);
274 vbe_call_finish(state
);
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.
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
;
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
);
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
,
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
;
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
)
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
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
,
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
));
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
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(
398 edid1_info
* edidInfo
= (edid1_info
*)get_boot_item(VESA_EDID_BOOT_INFO
,
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");
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
);
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
433 status_t status
= vbe_call_prepare(&state
);
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");
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");
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
,
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
);
469 vbe_call_finish(state
);
475 vesa_get_dpms_mode(vesa_info
& info
, uint32
& mode
)
478 // we always return a valid mode
480 // Prepare BIOS environment
482 status_t status
= vbe_call_prepare(&state
);
492 status
= sBIOSModule
->interrupt(state
, 0x10, ®s
);
493 if (status
!= B_OK
) {
494 dprintf(DEVICE_NAME
": vesa_get_dpms_mode(): BIOS failed: %s\n",
499 if ((regs
.eax
& 0xffff) != 0x4f) {
500 dprintf(DEVICE_NAME
": vesa_get_dpms_mode(): BIOS returned "
501 "0x%" B_PRIx32
"\n", regs
.eax
& 0xffff);
506 mode
= vbe_to_system_dpms(regs
.ebx
>> 8);
509 vbe_call_finish(state
);
515 vesa_set_dpms_mode(vesa_info
& info
, uint32 mode
)
517 // Only let supported modes through
518 mode
&= info
.shared_info
->dpms_capabilities
;
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
532 status_t status
= vbe_call_prepare(&state
);
538 regs
.ebx
= (vbeMode
<< 8) | 1;
542 status
= sBIOSModule
->interrupt(state
, 0x10, ®s
);
543 if (status
!= B_OK
) {
544 dprintf(DEVICE_NAME
": vesa_set_dpms_mode(): BIOS failed: %s\n",
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);
557 vbe_call_finish(state
);
563 vesa_set_indexed_colors(vesa_info
& info
, uint8 first
, uint8
* colors
,
567 uint32 shift
, physicalAddress
;
569 if (first
+ count
> 256)
572 // Prepare BIOS environment
574 status_t status
= vbe_call_prepare(&state
);
578 uint8
* palette
= (uint8
*)sBIOSModule
->allocate_mem(state
, 256 * 4);
579 if (palette
== NULL
) {
580 status
= B_NO_MEMORY
;
584 shift
= 8 - info
.bits_per_gun
;
586 // convert colors to VESA palette
587 for (int32 i
= first
; i
< count
; i
++) {
589 if (user_memcpy(color
, &colors
[i
* 3], 3) < B_OK
) {
590 status
= B_BAD_ADDRESS
;
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;
602 physicalAddress
= sBIOSModule
->physical_address(state
, palette
);
607 regs
.es
= physicalAddress
>> 4;
608 regs
.edi
= physicalAddress
- (regs
.es
<< 4);
610 status
= sBIOSModule
->interrupt(state
, 0x10, ®s
);
611 if (status
!= B_OK
) {
612 dprintf(DEVICE_NAME
": vesa_set_indexed_colors(): BIOS failed: %s\n",
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);
624 vbe_call_finish(state
);