vesa: live mode patching, nvidia version.

Some improvements to allow setting 8, 15, 16 and 32bit modes, and detect
the correct mode number after patching the BIOS automatically, instead
of hardcoding it.

Also move the patching code to a separate file.

Fixes #10570.

Change-Id: I920f448b59ad7373cb8595d92ce3fa52324be67e
Reviewed-on: https://review.haiku-os.org/c/haiku/+/4629
Tested-by: Commit checker robot <no-reply+buildbot@haiku-os.org>
Reviewed-by: waddlesplash <waddlesplash@gmail.com>
This commit is contained in:
Adrien Destugues 2021-10-20 18:13:56 +02:00 committed by waddlesplash
parent 9515cd8c6f
commit 015b40866c
5 changed files with 532 additions and 470 deletions

View File

@ -25,6 +25,26 @@ extern "C" void _sPrintf(const char* format, ...);
#endif
struct nvidia_resolution {
int width;
int height;
};
static const nvidia_resolution kNVidiaAllowedResolutions[] = {
{ 1280, 720 },
{ 1280, 800 },
{ 1360, 768 },
{ 1400, 1050 },
{ 1440, 900 },
{ 1600, 900 },
{ 1600, 1200 },
{ 1680, 1050 },
{ 1920, 1080 },
{ 1920, 1200 },
{ 2048, 1536 },
};
static uint32
get_color_space_for_depth(uint32 depth)
{
@ -54,11 +74,7 @@ is_mode_supported(display_mode* mode)
{
vesa_mode* modes = gInfo->vesa_modes;
bios_type_enum type = gInfo->shared_info->bios_type;
if (type == kIntelBiosType || type == kAtomBiosType1 || type == kAtomBiosType2) {
// We know how to patch the BIOS, so we can set any mode we want
return true;
}
bool colorspaceSupported = false;
for (uint32 i = gInfo->shared_info->vesa_mode_count; i-- > 0;) {
// search mode in VESA mode list
@ -68,6 +84,23 @@ is_mode_supported(display_mode* mode)
&& get_color_space_for_depth(modes[i].bits_per_pixel)
== mode->space)
return true;
if (get_color_space_for_depth(modes[i].bits_per_pixel) == mode->space)
colorspaceSupported = true;
}
bios_type_enum type = gInfo->shared_info->bios_type;
if (type == kIntelBiosType || type == kAtomBiosType1 || type == kAtomBiosType2) {
// We know how to patch the BIOS, so we can set any mode we want
return colorspaceSupported;
}
if (type == kNVidiaBiosType) {
for (size_t i = 0; i < B_COUNT_OF(kNVidiaAllowedResolutions); i++) {
if (mode->virtual_width == kNVidiaAllowedResolutions[i].width
&& mode->virtual_height == kNVidiaAllowedResolutions[i].height)
return colorspaceSupported;
}
}
return false;
@ -161,12 +194,17 @@ vesa_propose_display_mode(display_mode* target, const display_mode* low,
bios_type_enum type = gInfo->shared_info->bios_type;
if (type == kIntelBiosType || type == kAtomBiosType1 || type == kAtomBiosType2) {
// The driver says it knows the BIOS type, and therefore how to patch it to apply custom
// modes. However, it only knows how to do so for 32bit modes.
// TODO: for nVidia there is actually an hardcoded list of possible modes (because no one
// figured out how to generate the required values for an arbitrary mode), so we should
// check against that.
if (target->space == B_RGB32)
return B_OK;
// modes.
return B_OK;
}
if (type == kNVidiaBiosType) {
// For NVidia there is only a limited set of extra resolutions we know how to set
for (size_t i = 0; i < B_COUNT_OF(kNVidiaAllowedResolutions); i++) {
if (target->virtual_width == kNVidiaAllowedResolutions[i].width
&& target->virtual_height == kNVidiaAllowedResolutions[i].height)
return B_OK;
}
}
return B_BAD_VALUE;

View File

@ -10,6 +10,7 @@ UsePrivateKernelHeaders ;
KernelAddon vesa :
device.cpp
driver.cpp
patch.cpp
vesa.cpp
vga.cpp
;

View File

@ -0,0 +1,458 @@
/*
* Copyright 2021, Adrien Destugues, pulkomandy@pulkomandy.tk
* Distributed under the terms of the MIT License.
*/
#include "vesa_private.h"
#include "vesa.h"
#include <SupportDefs.h>
#define _GNU_SOURCE
#include <string.h>
#include "atombios.h"
#include "driver.h"
#include "edid_raw.h"
#include "utility.h"
#include "vesa_info.h"
extern bios_module_info* sBIOSModule;
status_t
vbe_patch_intel_bios(bios_state* state, display_mode& mode)
{
edid1_detailed_timing_raw timing;
timing.pixel_clock = mode.timing.pixel_clock / 10;
timing.h_active = mode.timing.h_display & 0xFF;
timing.h_active_high = (mode.timing.h_display >> 8) & 0xF;
uint16 h_blank = mode.timing.h_total - mode.timing.h_display;
timing.h_blank = h_blank & 0xff;
timing.h_blank_high = (h_blank >> 8) & 0xF;
timing.v_active = mode.timing.v_display & 0xFF;
timing.v_active_high = (mode.timing.v_display >> 8) & 0xF;
uint16 v_blank = mode.timing.v_total - mode.timing.v_display;
timing.v_blank = v_blank & 0xff;
timing.v_blank_high = (v_blank >> 8) & 0xF;
uint16 h_sync_off = mode.timing.h_sync_start - mode.timing.h_display;
timing.h_sync_off = h_sync_off & 0xFF;
timing.h_sync_off_high = (h_sync_off >> 8) & 0x3;
uint16 h_sync_width = mode.timing.h_sync_end - mode.timing.h_sync_start;
timing.h_sync_width = h_sync_width & 0xFF;
timing.h_sync_width_high = (h_sync_width >> 8) & 0x3;
uint16 v_sync_off = mode.timing.v_sync_start - mode.timing.v_display;
timing.v_sync_off = v_sync_off & 0xF;
timing.v_sync_off_high = (v_sync_off >> 4) & 0x3;
uint16 v_sync_width = mode.timing.v_sync_end - mode.timing.v_sync_start;
timing.v_sync_width = v_sync_width & 0xF;
timing.v_sync_width_high = (v_sync_width >> 4) & 0x3;
timing.h_size = 0;
timing.v_size = 0;
timing.h_size_high = 0;
timing.v_size_high = 0;
timing.h_border = 0;
timing.v_border = 0;
timing.interlaced = 0;
timing.stereo = 0;
timing.sync = 3;
timing.misc = 0;
timing.stereo_il = 0;
static const uint8 knownMode[] = { 0x64, 0x19, 0x00, 0x40, 0x41, 0x00, 0x26, 0x30, 0x18,
0x88, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18};
// This is the EDID description for a standard 1024x768 timing. We will find and replace
// all occurences of it in the BIOS with our custom mode.
// Get a pointer to the BIOS
const uintptr_t kBiosBase = 0xc0000;
const size_t kBiosSize = 0x10000;
uint8_t* bios = (uint8_t*)sBIOSModule->virtual_address(state, kBiosBase);
uint8_t* biosEnd = bios + kBiosSize;
int replacementCount = 0;
while (bios < biosEnd) {
bios = (uint8_t*)memmem(bios, biosEnd - bios, knownMode, sizeof(knownMode));
if (bios == NULL)
break;
memcpy(bios, &timing, sizeof(timing));
bios += sizeof(timing);
replacementCount++;
}
dprintf(DEVICE_NAME ": patched custom mode in %d locations\n", replacementCount);
// Did we manage to find a mode descriptor to replace?
if (replacementCount == 0)
return B_NOT_SUPPORTED;
return B_OK;
}
status_t
vbe_patch_nvidia_bios(bios_state* state, display_mode& mode)
{
struct nvidia_mode {
uint32 width;
uint32 height;
uint8 patch0[17];
uint8 patch1[9];
uint8 patch2[13];
uint8 patch3[5];
};
static const nvidia_mode allowedModes[] = {
{1280, 720, {0x16, 0xCB, 0x9F, 0x9F, 0x8F, 0xA7, 0x17, 0xEA, 0xD2, 0xCF, 0xCF, 0xEB, 0x47,
0xE0, 0xC0, 0x00, 0x01},
{0x00, 0x05, 0xD0, 0x02, 0xA0, 0x2C, 0x10, 0x07, 0x05},
{0x7B, 0x01, 0x03, 0x7B, 0x01, 0x08, 0x01, 0x20, 0x80, 0x02, 0xFF, 0xFF, 0x20},
{0x00, 0x05, 0xBA, 0xD0, 0x02}},
{1280, 800, {0x12, 0xCD, 0x9F, 0x9F, 0x91, 0xA9, 0x1A, 0x3A, 0x21, 0x1F, 0x1F, 0x3B, 0x44,
0xFE, 0xC0, 0x00, 0x01},
{0x00, 0x05, 0x20, 0x03, 0xA0, 0x32, 0x10, 0x23, 0x05},
{0x61, 0x01, 0x03, 0x61, 0x01, 0x08, 0x01, 0x20, 0x80, 0x02, 0xFF, 0xFF, 0x20},
{0x00, 0x05, 0xBA, 0x20, 0x03}},
{1360, 768, {0x16, 0xB9, 0xA9, 0x9F, 0x8F, 0xB2, 0x16, 0x14, 0x01, 0xFF, 0xCF, 0xEB, 0x46,
0xEA, 0xC0, 0x00, 0x01},
{0x50, 0x05, 0x00, 0x03, 0xAA, 0x2F, 0x10, 0x07, 0x05},
{0x4D, 0x01, 0x03, 0x4D, 0x01, 0x08, 0x01, 0x20, 0xA8, 0x02, 0xFF, 0xFF, 0x20},
{0x50, 0x05, 0xBA, 0x00, 0x03}},
{1400, 1050, {0x12, 0xE6, 0xAE, 0xAE, 0x8A, 0xBB, 0x8E, 0x3D, 0x1B, 0x19, 0x19, 0x3E, 0x0E,
0x00, 0xC0, 0x24, 0x12},
{0x78, 0x05, 0x1A, 0x04, 0xAF, 0x4A, 0x0E, 0x21, 0x05},
{0x49, 0x01, 0x03, 0x49, 0x01, 0x08, 0x01, 0x20, 0xBC, 0x02, 0xFF, 0xFF, 0x20},
{0x78, 0x05, 0xBA, 0x1A, 0x04}},
{1440, 900, {0x12, 0xE9, 0xB3, 0xB3, 0x8D, 0xBF, 0x92, 0xA3, 0x85, 0x83, 0x83, 0xA4, 0x48,
0xFE, 0xC0, 0x00, 0x00},
{0xA0, 0x05, 0x84, 0x03, 0xB4, 0x38, 0x10, 0x24, 0x05},
{0x65, 0x01, 0x03, 0x65, 0x01, 0x08, 0x01, 0x20, 0xD0, 0x02, 0xFF, 0xFF, 0x20},
{0xA0, 0x05, 0xBA, 0x84, 0x03}},
{1600, 900, {0x1A, 0xD7, 0xC7, 0xC7, 0x9B, 0xCD, 0x11, 0x9C, 0x86, 0x83, 0x83, 0x9D, 0x4B,
0xFE, 0xC0, 0x00, 0x00},
{0x40, 0x06, 0x84, 0x03, 0xC8, 0x38, 0x10, 0x27, 0x05},
{0x67, 0x01, 0x03, 0x67, 0x01, 0x08, 0x01, 0x20, 0x20, 0x03, 0xFF, 0xFF, 0x20},
{0x40, 0x06, 0xBA, 0x84, 0x03}},
{1600, 1200, {0x12, 0x03, 0xC7, 0xC7, 0x87, 0xD1, 0x09, 0xE0, 0xB1, 0xAF, 0xAF, 0xE1, 0x04,
0x00, 0x01, 0x24, 0x13},
{0x40, 0x06, 0xB0, 0x04, 0xC8, 0x4A, 0x10, 0x19, 0x05},
{0x4A, 0x01, 0x03, 0x4A, 0x01, 0x08, 0x01, 0x20, 0x20, 0x03, 0xFF, 0xFF, 0x20},
{0x40, 0x06, 0xBA, 0xB0, 0x04}},
{1680, 1050, {0x12, 0x15, 0xD1, 0xD1, 0x99, 0xE0, 0x17, 0x3D, 0x1B, 0x19, 0x19, 0x3E, 0x0E,
0x00, 0x01, 0x24, 0x13},
{0x90, 0x06, 0x1A, 0x04, 0xD2, 0x41, 0x10, 0x25, 0x05},
{0x69, 0x01, 0x03, 0x69, 0x01, 0x08, 0x01, 0x20, 0x48, 0x03, 0xFF, 0xFF, 0x20},
{0x90, 0x06, 0xBA, 0x1A, 0x04}},
{1920, 1080, {0x16, 0x0E, 0xEF, 0x9F, 0x8F, 0xFD, 0x02, 0x63, 0x3B, 0x37, 0xCF, 0xEB, 0x40,
0x00, 0xC1, 0x24, 0x02},
{0x80, 0x07, 0x38, 0x04, 0xF0, 0x42, 0x10, 0x07, 0x05},
{0x4D, 0x01, 0x03, 0x4D, 0x01, 0x08, 0x01, 0x20, 0xC0, 0x03, 0xFF, 0xFF, 0x20},
{0x80, 0x07, 0xBA, 0x38, 0x04}},
{1920, 1200, {0x12, 0x3F, 0xEF, 0xEF, 0x83, 0x01, 0x1B, 0xD8, 0xB1, 0xAF, 0xAF, 0xD9, 0x04,
0x00, 0x41, 0x25, 0x12},
{0x80, 0x07, 0xB0, 0x04, 0xF0, 0x4B, 0x10, 0x26, 0x05},
{0x7D, 0x01, 0x03, 0x7D, 0x01, 0x08, 0x01, 0x20, 0xC0, 0x03, 0xFF, 0xFF, 0x20},
{0x80, 0x07, 0xBA, 0xB0, 0x04}},
{2048, 1536, {0x12, 0x63, 0xFF, 0xFF, 0x9D, 0x12, 0x0E, 0x34, 0x01, 0x00, 0x00, 0x35, 0x44,
0xE0, 0x41, 0x25, 0x13},
{0x00, 0x08, 0x00, 0x06, 0x00, 0x60, 0x10, 0x22, 0x05},
{0x7A, 0x01, 0x03, 0x52, 0x01, 0x08, 0x01, 0x20, 0x00, 0x04, 0xFF, 0xFF, 0x20},
{0x00, 0x08, 0xBA, 0x00, 0x06}}
};
static const nvidia_mode knownMode = { 0, 0,
{0x34, 0x2d, 0x27, 0x28, 0x90, 0x2b, 0xa0, 0xbf, 0x9c, 0x8f, 0x96, 0xb9, 0x8e, 0x1f, 0x00,
0x00, 0x00},
{0x28, 0x00, 0x19, 0x00, 0x28, 0x18, 0x08, 0x08, 0x05},
{0x82, 0x0f, 0x03, 0x01, 0x00, 0x00, 0x08, 0x04, 0x14, 0x00, 0x00, 0x08, 0x17},
{0x40, 0x06, 0xba, 0xb0, 0x04}
};
size_t i;
for (i = 0; i < B_COUNT_OF(allowedModes); i++) {
if (allowedModes[i].width == mode.timing.h_display
&& allowedModes[i].height == mode.timing.v_display) {
break;
}
}
if (i >= B_COUNT_OF(allowedModes))
return B_BAD_VALUE;
// Get a pointer to the BIOS
const uintptr_t kBiosBase = 0xc0000;
const size_t kBiosSize = 0x10000;
uint8_t* biosBase = (uint8_t*)sBIOSModule->virtual_address(state, kBiosBase);
uint8_t* biosEnd = biosBase + kBiosSize;
int replacementCount = 0;
uint8_t* bios = biosBase;
while (bios < biosEnd) {
bios = (uint8_t*)memmem(bios, biosEnd - bios, knownMode.patch0, sizeof(knownMode.patch0));
if (bios == NULL)
break;
memcpy(bios, allowedModes[i].patch0, sizeof(allowedModes[i].patch0));
bios += sizeof(knownMode.patch0);
replacementCount++;
}
dprintf(DEVICE_NAME ": applied patch0 in %d locations\n", replacementCount);
replacementCount = 0;
bios = biosBase;
while (bios < biosEnd) {
bios = (uint8_t*)memmem(bios, biosEnd - bios, knownMode.patch1, sizeof(knownMode.patch1));
if (bios == NULL)
break;
memcpy(bios, allowedModes[i].patch1, sizeof(allowedModes[i].patch1));
bios += sizeof(knownMode.patch1);
replacementCount++;
}
dprintf(DEVICE_NAME ": applied patch1 in %d locations\n", replacementCount);
replacementCount = 0;
bios = biosBase;
while (bios < biosEnd) {
bios = (uint8_t*)memmem(bios, biosEnd - bios, knownMode.patch2, sizeof(knownMode.patch2));
if (bios == NULL)
break;
memcpy(bios, allowedModes[i].patch2, sizeof(allowedModes[i].patch2));
bios += sizeof(knownMode.patch2);
replacementCount++;
}
dprintf(DEVICE_NAME ": applied patch2 in %d locations\n", replacementCount);
replacementCount = 0;
bios = biosBase;
while (bios < biosEnd) {
bios = (uint8_t*)memmem(bios, biosEnd - bios, knownMode.patch3, sizeof(knownMode.patch3));
if (bios == NULL)
break;
memcpy(bios, allowedModes[i].patch3, sizeof(allowedModes[i].patch3));
bios += sizeof(knownMode.patch3);
replacementCount++;
}
dprintf(DEVICE_NAME ": applied patch3 in %d locations\n", replacementCount);
if ((biosBase[0x34] & 0x8F) == 0x80)
biosBase[0x34] |= 0x01;
return B_OK;
}
status_t
vbe_patch_atom1_bios(vesa_info& info, bios_state* state, display_mode& mode)
{
// Get a pointer to the BIOS
const uintptr_t kBiosBase = 0xc0000;
uint8_t* bios = (uint8_t*)sBIOSModule->virtual_address(state, kBiosBase);
ATOM_MODE_TIMING* timing = (ATOM_MODE_TIMING*)(bios + info.shared_info->mode_table_offset);
dprintf(DEVICE_NAME ": patching ATOM mode timing (overwriting mode %dx%d)\n",
timing->usCRTC_H_Disp, timing->usCRTC_V_Disp);
timing->usCRTC_H_Total = mode.timing.h_total;
timing->usCRTC_H_Disp = mode.timing.h_display;
timing->usCRTC_H_SyncStart = mode.timing.h_sync_start;
timing->usCRTC_H_SyncWidth = mode.timing.h_sync_end - mode.timing.h_sync_start;
timing->usCRTC_V_Total = mode.timing.v_total;
timing->usCRTC_V_Disp = mode.timing.v_display;
timing->usCRTC_V_SyncStart = mode.timing.v_sync_start;
timing->usCRTC_V_SyncWidth = mode.timing.v_sync_end - mode.timing.v_sync_start;
timing->usPixelClock = mode.timing.pixel_clock / 10;
return B_OK;
}
status_t
vbe_patch_atom2_bios(vesa_info& info, bios_state* state, display_mode& mode)
{
// Get a pointer to the BIOS
const uintptr_t kBiosBase = 0xc0000;
uint8_t* bios = (uint8_t*)sBIOSModule->virtual_address(state, kBiosBase);
ATOM_DTD_FORMAT* timing = (ATOM_DTD_FORMAT*)(bios + info.shared_info->mode_table_offset);
dprintf(DEVICE_NAME ": patching ATOM DTD format (overwriting mode %dx%d)\n",
timing->usHActive, timing->usVActive);
timing->usHBlanking_Time = mode.timing.h_total - mode.timing.h_display;
timing->usHActive = mode.timing.h_display;
timing->usHSyncOffset = mode.timing.h_sync_start;
timing->usHSyncWidth = mode.timing.h_sync_end - mode.timing.h_sync_start;
timing->usVBlanking_Time = mode.timing.v_total - mode.timing.v_display;
timing->usVActive = mode.timing.v_display;
timing->usVSyncOffset = mode.timing.v_sync_start;
timing->usVSyncWidth = mode.timing.v_sync_end - mode.timing.v_sync_start;
timing->usPixClk = mode.timing.pixel_clock / 10;
return B_OK;
}
/*! Identify the BIOS type if it's one of the common ones, and locate where the BIOS store its
* allowed video modes table. We can then patch this table to add extra video modes that the
* manufacturer didn't allow.
*/
void
vesa_identify_bios(bios_state* state, vesa_shared_info* sharedInfo)
{
// Get a pointer to the BIOS
const uintptr_t kBiosBase = 0xc0000;
uint8_t* bios = (uint8_t*)sBIOSModule->virtual_address(state, kBiosBase);
const size_t kAtomBiosHeaderOffset = 0x48;
const char kAtomSignature[] = {'A', 'T', 'O', 'M'};
ATOM_ROM_HEADER* atomRomHeader = (ATOM_ROM_HEADER*)(bios + kAtomBiosHeaderOffset);
sharedInfo->bios_type = kUnknownBiosType;
if (*(uint16*)(bios + 0x44) == 0x8086) {
dprintf(DEVICE_NAME ": detected Intel BIOS\n");
// TODO check if we can find the mode table
sharedInfo->bios_type = kIntelBiosType;
} else if (memcmp(atomRomHeader->uaFirmWareSignature, kAtomSignature, 4) == 0) {
dprintf(DEVICE_NAME ": detected ATOM BIOS\n");
ATOM_MASTER_DATA_TABLE* masterDataTable = (ATOM_MASTER_DATA_TABLE*)(bios
+ atomRomHeader->usMasterDataTableOffset);
dprintf(DEVICE_NAME ": list of data tables: %p", &masterDataTable->ListOfDataTables);
ATOM_ANALOG_TV_INFO* standardVesaTable = (ATOM_ANALOG_TV_INFO*)(bios
+ masterDataTable->ListOfDataTables.StandardVESA_Timing);
dprintf(DEVICE_NAME ": std_vesa: %p", standardVesaTable);
sharedInfo->mode_table_offset = (uint8*)&standardVesaTable->aModeTimings - bios;
size_t tableSize = standardVesaTable->sHeader.usStructureSize
- sizeof(ATOM_COMMON_TABLE_HEADER);
if (tableSize % sizeof(ATOM_MODE_TIMING) == 0)
sharedInfo->bios_type = kAtomBiosType2;
else
sharedInfo->bios_type = kAtomBiosType1;
} else if (memmem(bios, 512, "NVID", 4) != NULL) {
dprintf(DEVICE_NAME ": detected nVidia BIOS\n");
// TODO check if we can find the mode table
sharedInfo->bios_type = kNVidiaBiosType;
} else {
dprintf(DEVICE_NAME ": unknown BIOS type, custom video modes will not be available\n");
}
}
/*! Set a custom display mode by live patching the VESA BIOS to insert it in the modeline table.
*
* This is only supported for some video cards, where the format of the mode lines is known.
*/
status_t
vesa_set_custom_display_mode(vesa_info& info, display_mode& mode)
{
int32 modeIndex = -1;
if (info.shared_info->bios_type == kUnknownBiosType)
return B_NOT_SUPPORTED;
// Prepare BIOS environment
bios_state* state;
status_t status = vbe_call_prepare(&state);
if (status != B_OK)
return status;
// Patch bios to inject custom video mode
switch (info.shared_info->bios_type) {
case kIntelBiosType:
status = vbe_patch_intel_bios(state, mode);
// The patch replaces the 1024x768 modes with our custom resolution. We can then use
// mode 0x118 which is the standard VBE2 mode for 1024x768 at 32 bits per pixel, and
// know it will use our new timings.
break;
case kNVidiaBiosType:
status = vbe_patch_nvidia_bios(state, mode);
break;
case kAtomBiosType1:
status = vbe_patch_atom1_bios(info, state, mode);
break;
case kAtomBiosType2:
status = vbe_patch_atom2_bios(info, state, mode);
break;
default:
status = B_NOT_SUPPORTED;
break;
}
if (status != B_OK)
goto out;
// The patching modified some mode, but we don't know which one. So we need to rescan the mode
// list to find the correct one.
struct vbe_mode_info modeInfo;
for (uint32 i = 0; i < info.shared_info->vesa_mode_count; i++) {
status = vbe_get_mode_info(state, info.modes[i].mode, &modeInfo);
if (status != B_OK) {
// Sometimes the patching prevents us from getting the mode info?
dprintf(DEVICE_NAME ": vesa_set_custom_display_mode(): cannot get mode info for %x\n",
info.modes[i].mode);
// Just ignore modes that turn out to be invalid...
continue;
}
if (modeInfo.width == mode.timing.h_display && modeInfo.height == mode.timing.v_display
&& get_color_space_for_depth(info.modes[i].bits_per_pixel) == mode.space) {
modeIndex = info.modes[i].mode;
break;
}
}
if (modeIndex >= 0) {
dprintf(DEVICE_NAME ": custom mode resolution %dx%d succesfully patched at index %"
B_PRIx32 "\n", modeInfo.width, modeInfo.height, modeIndex);
} else {
dprintf(DEVICE_NAME ": video mode patching failed!\n");
goto out;
}
// Set mode
status = vbe_set_mode(state, modeIndex);
if (status != B_OK) {
dprintf(DEVICE_NAME ": vesa_set_custom_display_mode(): cannot set mode\n");
goto out;
}
if (info.modes[modeIndex].bits_per_pixel <= 8)
vbe_set_bits_per_gun(state, info, 8);
// Map new frame buffer if necessary
status = remap_frame_buffer(info, modeInfo.physical_base, modeInfo.width,
modeInfo.height, modeInfo.bits_per_pixel, modeInfo.bytes_per_row,
false);
if (status == B_OK) {
// Update shared frame buffer information
info.shared_info->current_mode.virtual_width = modeInfo.width;
info.shared_info->current_mode.virtual_height = modeInfo.height;
info.shared_info->current_mode.space = get_color_space_for_depth(
modeInfo.bits_per_pixel);
}
out:
vbe_call_finish(state);
return status;
}

View File

@ -7,11 +7,6 @@
#include "vesa_private.h"
#include "vesa.h"
#define _GNU_SOURCE
#include <string.h>
#include <drivers/bios.h>
#include <boot_item.h>
#include <frame_buffer_console.h>
#include <util/kernel_cpp.h>
@ -21,8 +16,6 @@
#include "utility.h"
#include "vesa_info.h"
#include "atombios.h"
#define LINEAR_ADDRESS(segment, offset) \
(((addr_t)(segment) << 4) + (addr_t)(offset))
@ -30,13 +23,13 @@
LINEAR_ADDRESS((addr_t)(segmented) >> 16, (addr_t)(segmented) & 0xffff)
static bios_module_info* sBIOSModule;
bios_module_info* sBIOSModule;
/*! Loads the BIOS module and sets up a state for it. The BIOS module is only
loaded when we need it, as it is quite a large module.
*/
static status_t
status_t
vbe_call_prepare(bios_state** state)
{
status_t status;
@ -59,7 +52,7 @@ vbe_call_prepare(bios_state** state)
}
static void
void
vbe_call_finish(bios_state* state)
{
sBIOSModule->finish(state);
@ -101,7 +94,7 @@ find_graphics_card(addr_t frameBuffer, addr_t& base, size_t& size)
}
static uint32
uint32
get_color_space_for_depth(uint32 depth)
{
switch (depth) {
@ -126,7 +119,7 @@ get_color_space_for_depth(uint32 depth)
}
static status_t
status_t
vbe_get_mode_info(bios_state* state, uint16 mode, struct vbe_mode_info* modeInfo)
{
void* vbeModeInfo = sBIOSModule->allocate_mem(state,
@ -160,7 +153,7 @@ vbe_get_mode_info(bios_state* state, uint16 mode, struct vbe_mode_info* modeInfo
}
static status_t
status_t
vbe_set_mode(bios_state* state, uint16 mode)
{
bios_regs regs = {};
@ -231,7 +224,7 @@ vbe_get_dpms_capabilities(bios_state* state, uint32& vbeMode, uint32& mode)
}
static status_t
status_t
vbe_set_bits_per_gun(bios_state* state, vesa_info& info, uint8 bits)
{
info.bits_per_gun = 6;
@ -290,61 +283,10 @@ vbe_get_vesa_info(bios_state* state, vesa_info& info)
}
/*! Identify the BIOS type if it's one of the common ones, and locate where the BIOS store its
* allowed video modes table. We can then patch this table to add extra video modes that the
* manufacturer didn't allow.
*/
static void
vbe_identify_bios(bios_state* state, vesa_shared_info* sharedInfo)
{
// Get a pointer to the BIOS
const uintptr_t kBiosBase = 0xc0000;
uint8_t* bios = (uint8_t*)sBIOSModule->virtual_address(state, kBiosBase);
const size_t kAtomBiosHeaderOffset = 0x48;
const char kAtomSignature[] = {'A', 'T', 'O', 'M'};
ATOM_ROM_HEADER* atomRomHeader = (ATOM_ROM_HEADER*)(bios + kAtomBiosHeaderOffset);
sharedInfo->bios_type = kUnknownBiosType;
if (*(uint16*)(bios + 0x44) == 0x8086) {
dprintf(DEVICE_NAME ": detected Intel BIOS\n");
// TODO check if we can find the mode table
sharedInfo->bios_type = kIntelBiosType;
} else if (memcmp(atomRomHeader->uaFirmWareSignature, kAtomSignature, 4) == 0) {
dprintf(DEVICE_NAME ": detected ATOM BIOS\n");
ATOM_MASTER_DATA_TABLE* masterDataTable = (ATOM_MASTER_DATA_TABLE*)(bios
+ atomRomHeader->usMasterDataTableOffset);
dprintf(DEVICE_NAME ": list of data tables: %p", &masterDataTable->ListOfDataTables);
ATOM_ANALOG_TV_INFO* standardVesaTable = (ATOM_ANALOG_TV_INFO*)(bios
+ masterDataTable->ListOfDataTables.StandardVESA_Timing);
dprintf(DEVICE_NAME ": std_vesa: %p", standardVesaTable);
sharedInfo->mode_table_offset = (uint8*)&standardVesaTable->aModeTimings - bios;
size_t tableSize = standardVesaTable->sHeader.usStructureSize
- sizeof(ATOM_COMMON_TABLE_HEADER);
if (tableSize % sizeof(ATOM_MODE_TIMING) == 0)
sharedInfo->bios_type = kAtomBiosType2;
else
sharedInfo->bios_type = kAtomBiosType1;
} else if (memmem(bios, 512, "NVID", 4) != NULL) {
dprintf(DEVICE_NAME ": detected nVidia BIOS\n");
// TODO check if we can find the mode table
sharedInfo->bios_type = kNVidiaBiosType;
} else {
dprintf(DEVICE_NAME ": unknown BIOS type, custom video modes will not be available\n");
}
}
/*! Remaps the frame buffer if necessary; if we've already mapped the complete
frame buffer, there is no need to map it again.
*/
static status_t
status_t
remap_frame_buffer(vesa_info& info, addr_t physicalBase, uint32 width,
uint32 height, int8 depth, uint32 bytesPerRow, bool initializing)
{
@ -472,7 +414,7 @@ vesa_init(vesa_info& info)
if (status != B_OK)
return status;
vbe_identify_bios(state, &sharedInfo);
vesa_identify_bios(state, &sharedInfo);
vbe_get_dpms_capabilities(state, info.vbe_dpms_capabilities,
sharedInfo.dpms_capabilities);
@ -545,396 +487,6 @@ out:
}
status_t
vbe_patch_intel_bios(bios_state* state, display_mode& mode)
{
edid1_detailed_timing_raw timing;
timing.pixel_clock = mode.timing.pixel_clock / 10;
timing.h_active = mode.timing.h_display & 0xFF;
timing.h_active_high = (mode.timing.h_display >> 8) & 0xF;
uint16 h_blank = mode.timing.h_total - mode.timing.h_display;
timing.h_blank = h_blank & 0xff;
timing.h_blank_high = (h_blank >> 8) & 0xF;
timing.v_active = mode.timing.v_display & 0xFF;
timing.v_active_high = (mode.timing.v_display >> 8) & 0xF;
uint16 v_blank = mode.timing.v_total - mode.timing.v_display;
timing.v_blank = v_blank & 0xff;
timing.v_blank_high = (v_blank >> 8) & 0xF;
uint16 h_sync_off = mode.timing.h_sync_start - mode.timing.h_display;
timing.h_sync_off = h_sync_off & 0xFF;
timing.h_sync_off_high = (h_sync_off >> 8) & 0x3;
uint16 h_sync_width = mode.timing.h_sync_end - mode.timing.h_sync_start;
timing.h_sync_width = h_sync_width & 0xFF;
timing.h_sync_width_high = (h_sync_width >> 8) & 0x3;
uint16 v_sync_off = mode.timing.v_sync_start - mode.timing.v_display;
timing.v_sync_off = v_sync_off & 0xF;
timing.v_sync_off_high = (v_sync_off >> 4) & 0x3;
uint16 v_sync_width = mode.timing.v_sync_end - mode.timing.v_sync_start;
timing.v_sync_width = v_sync_width & 0xF;
timing.v_sync_width_high = (v_sync_width >> 4) & 0x3;
timing.h_size = 0;
timing.v_size = 0;
timing.h_size_high = 0;
timing.v_size_high = 0;
timing.h_border = 0;
timing.v_border = 0;
timing.interlaced = 0;
timing.stereo = 0;
timing.sync = 3;
timing.misc = 0;
timing.stereo_il = 0;
static const uint8 knownMode[] = { 0x64, 0x19, 0x00, 0x40, 0x41, 0x00, 0x26, 0x30, 0x18,
0x88, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18};
// This is the EDID description for a standard 1024x768 timing. We will find and replace
// all occurences of it in the BIOS with our custom mode.
// Get a pointer to the BIOS
const uintptr_t kBiosBase = 0xc0000;
const size_t kBiosSize = 0x10000;
uint8_t* bios = (uint8_t*)sBIOSModule->virtual_address(state, kBiosBase);
uint8_t* biosEnd = bios + kBiosSize;
int replacementCount = 0;
while (bios < biosEnd) {
bios = (uint8_t*)memmem(bios, biosEnd - bios, knownMode, sizeof(knownMode));
if (bios == NULL)
break;
memcpy(bios, &timing, sizeof(timing));
bios += sizeof(knownMode);
replacementCount++;
}
dprintf(DEVICE_NAME ": patched custom mode in %d locations\n", replacementCount);
// Did we manage to find a mode descriptor to replace?
if (replacementCount == 0)
return B_NOT_SUPPORTED;
return B_OK;
}
status_t
vbe_patch_nvidia_bios(bios_state* state, display_mode& mode)
{
struct nvidia_mode {
uint32 width;
uint32 height;
uint8 patch0[17];
uint8 patch1[9];
uint8 patch2[13];
uint8 patch3[5];
};
static const nvidia_mode allowedModes[] = {
{1280, 720, {0x16, 0xCB, 0x9F, 0x9F, 0x8F, 0xA7, 0x17, 0xEA, 0xD2, 0xCF, 0xCF, 0xEB, 0x47,
0xE0, 0xC0, 0x00, 0x01},
{0x00, 0x05, 0xD0, 0x02, 0xA0, 0x2C, 0x10, 0x07, 0x05},
{0x7B, 0x01, 0x03, 0x7B, 0x01, 0x08, 0x01, 0x20, 0x80, 0x02, 0xFF, 0xFF, 0x20},
{0x00, 0x05, 0xBA, 0xD0, 0x02}},
{1280, 800, {0x12, 0xCD, 0x9F, 0x9F, 0x91, 0xA9, 0x1A, 0x3A, 0x21, 0x1F, 0x1F, 0x3B, 0x44,
0xFE, 0xC0, 0x00, 0x01},
{0x00, 0x05, 0x20, 0x03, 0xA0, 0x32, 0x10, 0x23, 0x05},
{0x61, 0x01, 0x03, 0x61, 0x01, 0x08, 0x01, 0x20, 0x80, 0x02, 0xFF, 0xFF, 0x20},
{0x00, 0x05, 0xBA, 0x20, 0x03}},
{1360, 768, {0x16, 0xB9, 0xA9, 0x9F, 0x8F, 0xB2, 0x16, 0x14, 0x01, 0xFF, 0xCF, 0xEB, 0x46,
0xEA, 0xC0, 0x00, 0x01},
{0x50, 0x05, 0x00, 0x03, 0xAA, 0x2F, 0x10, 0x07, 0x05},
{0x4D, 0x01, 0x03, 0x4D, 0x01, 0x08, 0x01, 0x20, 0xA8, 0x02, 0xFF, 0xFF, 0x20},
{0x50, 0x05, 0xBA, 0x00, 0x03}},
{1400, 1050, {0x12, 0xE6, 0xAE, 0xAE, 0x8A, 0xBB, 0x8E, 0x3D, 0x1B, 0x19, 0x19, 0x3E, 0x0E,
0x00, 0xC0, 0x24, 0x12},
{0x78, 0x05, 0x1A, 0x04, 0xAF, 0x4A, 0x0E, 0x21, 0x05},
{0x49, 0x01, 0x03, 0x49, 0x01, 0x08, 0x01, 0x20, 0xBC, 0x02, 0xFF, 0xFF, 0x20},
{0x78, 0x05, 0xBA, 0x1A, 0x04}},
{1440, 900, {0x12, 0xE9, 0xB3, 0xB3, 0x8D, 0xBF, 0x92, 0xA3, 0x85, 0x83, 0x83, 0xA4, 0x48,
0xFE, 0xC0, 0x00, 0x00},
{0xA0, 0x05, 0x84, 0x03, 0xB4, 0x38, 0x10, 0x24, 0x05},
{0x65, 0x01, 0x03, 0x65, 0x01, 0x08, 0x01, 0x20, 0xD0, 0x02, 0xFF, 0xFF, 0x20},
{0xA0, 0x05, 0xBA, 0x84, 0x03}},
{1600, 900, {0x1A, 0xD7, 0xC7, 0xC7, 0x9B, 0xCD, 0x11, 0x9C, 0x86, 0x83, 0x83, 0x9D, 0x4B,
0xFE, 0xC0, 0x00, 0x00},
{0x40, 0x06, 0x84, 0x03, 0xC8, 0x38, 0x10, 0x27, 0x05},
{0x67, 0x01, 0x03, 0x67, 0x01, 0x08, 0x01, 0x20, 0x20, 0x03, 0xFF, 0xFF, 0x20},
{0x40, 0x06, 0xBA, 0x84, 0x03}},
{1600, 1200, {0x12, 0x03, 0xC7, 0xC7, 0x87, 0xD1, 0x09, 0xE0, 0xB1, 0xAF, 0xAF, 0xE1, 0x04,
0x00, 0x01, 0x24, 0x13},
{0x40, 0x06, 0xB0, 0x04, 0xC8, 0x4A, 0x10, 0x19, 0x05},
{0x4A, 0x01, 0x03, 0x4A, 0x01, 0x08, 0x01, 0x20, 0x20, 0x03, 0xFF, 0xFF, 0x20},
{0x40, 0x06, 0xBA, 0xB0, 0x04}},
{1680, 1050, {0x12, 0x15, 0xD1, 0xD1, 0x99, 0xE0, 0x17, 0x3D, 0x1B, 0x19, 0x19, 0x3E, 0x0E,
0x00, 0x01, 0x24, 0x13},
{0x90, 0x06, 0x1A, 0x04, 0xD2, 0x41, 0x10, 0x25, 0x05},
{0x69, 0x01, 0x03, 0x69, 0x01, 0x08, 0x01, 0x20, 0x48, 0x03, 0xFF, 0xFF, 0x20},
{0x90, 0x06, 0xBA, 0x1A, 0x04}},
{1920, 1080, {0x16, 0x0E, 0xEF, 0x9F, 0x8F, 0xFD, 0x02, 0x63, 0x3B, 0x37, 0xCF, 0xEB, 0x40,
0x00, 0xC1, 0x24, 0x02},
{0x80, 0x07, 0x38, 0x04, 0xF0, 0x42, 0x10, 0x07, 0x05},
{0x4D, 0x01, 0x03, 0x4D, 0x01, 0x08, 0x01, 0x20, 0xC0, 0x03, 0xFF, 0xFF, 0x20},
{0x80, 0x07, 0xBA, 0x38, 0x04}},
{1920, 1200, {0x12, 0x3F, 0xEF, 0xEF, 0x83, 0x01, 0x1B, 0xD8, 0xB1, 0xAF, 0xAF, 0xD9, 0x04,
0x00, 0x41, 0x25, 0x12},
{0x80, 0x07, 0xB0, 0x04, 0xF0, 0x4B, 0x10, 0x26, 0x05},
{0x7D, 0x01, 0x03, 0x7D, 0x01, 0x08, 0x01, 0x20, 0xC0, 0x03, 0xFF, 0xFF, 0x20},
{0x80, 0x07, 0xBA, 0xB0, 0x04}},
{2048, 1536, {0x12, 0x63, 0xFF, 0xFF, 0x9D, 0x12, 0x0E, 0x34, 0x01, 0x00, 0x00, 0x35, 0x44,
0xE0, 0x41, 0x25, 0x13},
{0x00, 0x08, 0x00, 0x06, 0x00, 0x60, 0x10, 0x22, 0x05},
{0x7A, 0x01, 0x03, 0x52, 0x01, 0x08, 0x01, 0x20, 0x00, 0x04, 0xFF, 0xFF, 0x20},
{0x00, 0x08, 0xBA, 0x00, 0x06}}
};
static const nvidia_mode knownMode = { 0, 0,
{0x34, 0x2d, 0x27, 0x28, 0x90, 0x2b, 0xa0, 0xbf, 0x9c, 0x8f, 0x96, 0xb9, 0x8e, 0x1f, 0x00,
0x00, 0x00},
{0x28, 0x00, 0x19, 0x00, 0x28, 0x18, 0x08, 0x08, 0x05},
{0x82, 0x0f, 0x03, 0x01, 0x00, 0x00, 0x08, 0x04, 0x14, 0x00, 0x00, 0x08, 0x17},
{0x40, 0x06, 0xba, 0xb0, 0x04}
};
int i;
for (i = 0; i < B_COUNT_OF(allowedModes); i++) {
if (allowedModes[i].width == mode.timing.h_display
&& allowedModes[i].height == mode.timing.v_display) {
break;
}
}
if (i >= B_COUNT_OF(allowedModes))
return B_BAD_VALUE;
// Get a pointer to the BIOS
const uintptr_t kBiosBase = 0xc0000;
const size_t kBiosSize = 0x10000;
uint8_t* biosBase = (uint8_t*)sBIOSModule->virtual_address(state, kBiosBase);
uint8_t* biosEnd = bios + kBiosSize
int replacementCount = 0;
uint8_t* bios = biosBase;
while (bios < biosEnd) {
bios = (uint8_t*)memmem(bios, biosEnd - bios, knownMode.patch0, sizeof(knownMode.patch0));
if (bios == NULL)
break;
memcpy(bios, allowedModes[i].patch0, sizeof(allowedModes[i].patch0));
bios += sizeof(knownMode.patch0);
replacementCount++;
}
dprintf(DEVICE_NAME ": applied patch0 in %d locations\n", replacementCount);
replacementCount = 0;
bios = biosBase;
while (bios < biosEnd) {
bios = (uint8_t*)memmem(bios, biosEnd - bios, knownMode.patch1, sizeof(knownMode.patch1));
if (bios == NULL)
break;
memcpy(bios, allowedModes[i].patch1, sizeof(allowedModes[i].patch1));
bios += sizeof(knownMode.patch1);
replacementCount++;
}
dprintf(DEVICE_NAME ": applied patch1 in %d locations\n", replacementCount);
replacementCount = 0;
bios = biosBase;
while (bios < biosEnd) {
bios = (uint8_t*)memmem(bios, biosEnd - bios, knownMode.patch2, sizeof(knownMode.patch2));
if (bios == NULL)
break;
memcpy(bios, allowedModes[i].patch2, sizeof(allowedModes[i].patch2));
bios += sizeof(knownMode.patch2);
replacementCount++;
}
dprintf(DEVICE_NAME ": applied patch1 in %d locations\n", replacementCount);
replacementCount = 0;
bios = biosBase;
while (bios < biosEnd) {
bios = (uint8_t*)memmem(bios, biosEnd - bios, knownMode.patch3, sizeof(knownMode.patch3));
if (bios == NULL)
break;
memcpy(bios, allowedModes[i].patch3, sizeof(allowedModes[i].patch3));
bios += sizeof(knownMode.patch3);
replacementCount++;
}
dprintf(DEVICE_NAME ": applied patch1 in %d locations\n", replacementCount);
if ((bios[0x34] & 0x8F) == 0x80)
bios[0x34] |= 0x01;
return B_OK;
}
status_t
vbe_patch_atom1_bios(vesa_info& info, bios_state* state, display_mode& mode)
{
// Get a pointer to the BIOS
const uintptr_t kBiosBase = 0xc0000;
uint8_t* bios = (uint8_t*)sBIOSModule->virtual_address(state, kBiosBase);
ATOM_MODE_TIMING* timing = (ATOM_MODE_TIMING*)(bios + info.shared_info->mode_table_offset);
dprintf(DEVICE_NAME ": patching ATOM mode timing (overwriting mode %dx%d)\n",
timing->usCRTC_H_Disp, timing->usCRTC_V_Disp);
timing->usCRTC_H_Total = mode.timing.h_total;
timing->usCRTC_H_Disp = mode.timing.h_display;
timing->usCRTC_H_SyncStart = mode.timing.h_sync_start;
timing->usCRTC_H_SyncWidth = mode.timing.h_sync_end - mode.timing.h_sync_start;
timing->usCRTC_V_Total = mode.timing.v_total;
timing->usCRTC_V_Disp = mode.timing.v_display;
timing->usCRTC_V_SyncStart = mode.timing.v_sync_start;
timing->usCRTC_V_SyncWidth = mode.timing.v_sync_end - mode.timing.v_sync_start;
timing->usPixelClock = mode.timing.pixel_clock / 10;
return B_OK;
}
status_t
vbe_patch_atom2_bios(vesa_info& info, bios_state* state, display_mode& mode)
{
// Get a pointer to the BIOS
const uintptr_t kBiosBase = 0xc0000;
uint8_t* bios = (uint8_t*)sBIOSModule->virtual_address(state, kBiosBase);
ATOM_DTD_FORMAT* timing = (ATOM_DTD_FORMAT*)(bios + info.shared_info->mode_table_offset);
dprintf(DEVICE_NAME ": patching ATOM DTD format (overwriting mode %dx%d)\n",
timing->usHActive, timing->usVActive);
timing->usHBlanking_Time = mode.timing.h_total - mode.timing.h_display;
timing->usHActive = mode.timing.h_display;
timing->usHSyncOffset = mode.timing.h_sync_start;
timing->usHSyncWidth = mode.timing.h_sync_end - mode.timing.h_sync_start;
timing->usVBlanking_Time = mode.timing.v_total - mode.timing.v_display;
timing->usVActive = mode.timing.v_display;
timing->usVSyncOffset = mode.timing.v_sync_start;
timing->usVSyncWidth = mode.timing.v_sync_end - mode.timing.v_sync_start;
timing->usPixClk = mode.timing.pixel_clock / 10;
return B_OK;
}
status_t
vesa_set_custom_display_mode(vesa_info& info, display_mode& mode)
{
if (info.shared_info->bios_type == kUnknownBiosType)
return B_NOT_SUPPORTED;
// Prepare BIOS environment
bios_state* state;
status_t status = vbe_call_prepare(&state);
if (status != B_OK)
return status;
int32 modeIndex; // Index of the mode that will be patched
// Patch bios to inject custom video mode
switch (info.shared_info->bios_type) {
case kIntelBiosType:
status = vbe_patch_intel_bios(state, mode);
// The patch replaces the 1024x768 modes with our custom resolution. We can then use
// mode 0x118 which is the standard VBE2 mode for 1024x768 at 32 bits per pixel, and
// know it will use our new timings.
// TODO use modes 105 (256 colors), 116 (15 bit) and 117 (16 bit) depending on the
// requested colorspace. They should work too.
// TODO ideally locate the 1024x768 modes from the mode list in info.modes[], instead
// of hardcoding its number, in case the BIOS does not use VBE2 standard numbering.
modeIndex = 0x118;
break;
#if 0
case kNVidiaBiosType:
status = vbe_patch_nvidia_bios(state, mode);
break;
#endif
case kAtomBiosType1:
status = vbe_patch_atom1_bios(info, state, mode);
modeIndex = 0; // TODO how does this work? Is it 100 (first VBE2 mode)?
break;
case kAtomBiosType2:
status = vbe_patch_atom2_bios(info, state, mode);
modeIndex = 0; // TODO how does this work? Is it 100 (first VBE2 mode)?
break;
default:
status = B_NOT_SUPPORTED;
break;
}
if (status != B_OK)
goto out;
// Get mode information
struct vbe_mode_info modeInfo;
for (int i = 0; i < info.shared_info->mode_count; i++) {
status = vbe_get_mode_info(state, info.modes[i].mode, &modeInfo);
if (status != B_OK) {
// Sometimes the patching prevents us from getting the mode info?
dprintf(DEVICE_NAME ": vesa_set_custom_display_mode(): cannot get mode info for %x\n",
info.modes[i].mode);
// Just ignore modes that turn out to be invalid...
continue;
}
if (modeInfo.width == mode.timing.h_display && modeInfo.height == mode.timing.v_display
&& get_color_space_for_depth(info.modes[i].bits_per_pixel) == mode.space) {
modeIndex = info.modes[i].mode;
break;
}
}
if (modeIndex >= 0) {
dprintf(DEVICE_NAME ": custom mode resolution %dx%d succesfully patched at index %"
B_PRIx32 "\n", modeInfo.width, modeInfo.height, modeIndex);
} else {
dprintf(DEVICE_NAME ": video mode patching failed!\n");
goto out;
}
dprintf(DEVICE_NAME ": custom mode resolution %dx%d\n", modeInfo.width, modeInfo.height);
// Set mode
status = vbe_set_mode(state, modeIndex);
if (status != B_OK) {
dprintf(DEVICE_NAME ": vesa_set_custom_display_mode(): cannot set mode\n");
goto out;
}
if (info.modes[modeIndex].bits_per_pixel <= 8)
vbe_set_bits_per_gun(state, info, 8);
// Map new frame buffer if necessary
status = remap_frame_buffer(info, modeInfo.physical_base, modeInfo.width,
modeInfo.height, modeInfo.bits_per_pixel, modeInfo.bytes_per_row,
false);
if (status == B_OK) {
// Update shared frame buffer information
info.shared_info->current_mode.virtual_width = modeInfo.width;
info.shared_info->current_mode.virtual_height = modeInfo.height;
info.shared_info->current_mode.space = get_color_space_for_depth(
modeInfo.bits_per_pixel);
}
out:
vbe_call_finish(state);
return status;
}
status_t
vesa_get_dpms_mode(vesa_info& info, uint32& mode)
{

View File

@ -1,5 +1,6 @@
/*
* Copyright 2005-2009, Axel Dörfler, axeld@pinc-software.de.
* Copyright 2021, Adrien Destugues, pulkomandy@pulkomandy.tk
* Distributed under the terms of the MIT License.
*/
#ifndef VESA_PRIVATE_H
@ -10,6 +11,8 @@
#include <Accelerant.h>
#include <PCI.h>
#include <drivers/bios.h>
#define DEVICE_NAME "vesa"
#define VESA_ACCELERANT_NAME "vesa.accelerant"
@ -42,7 +45,17 @@ extern status_t vesa_set_display_mode(vesa_info& info, uint32 mode);
extern status_t vesa_set_custom_display_mode(vesa_info& info, display_mode& mode);
extern status_t vesa_get_dpms_mode(vesa_info& info, uint32& mode);
extern status_t vesa_set_dpms_mode(vesa_info& info, uint32 mode);
extern status_t vesa_set_indexed_colors(vesa_info& info, uint8 first,
uint8* colors, uint16 count);
extern status_t vesa_set_indexed_colors(vesa_info& info, uint8 first, uint8* colors, uint16 count);
extern void vesa_identify_bios(bios_state* state, vesa_shared_info* sharedInfo);
status_t vbe_call_prepare(bios_state** state);
status_t vbe_get_mode_info(bios_state* state, uint16 mode, struct vbe_mode_info* modeInfo);
uint32 get_color_space_for_depth(uint32 depth);
status_t vbe_set_mode(bios_state* state, uint16 mode);
status_t vbe_set_bits_per_gun(bios_state* state, vesa_info& info, uint8 bits);
status_t remap_frame_buffer(vesa_info& info, addr_t physicalBase, uint32 width,
uint32 height, int8 depth, uint32 bytesPerRow, bool initializing);
void vbe_call_finish(bios_state* state);
#endif /* VESA_PRIVATE_H */