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:
parent
9515cd8c6f
commit
015b40866c
@ -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,14 +194,19 @@ 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)
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ UsePrivateKernelHeaders ;
|
||||
KernelAddon vesa :
|
||||
device.cpp
|
||||
driver.cpp
|
||||
patch.cpp
|
||||
vesa.cpp
|
||||
vga.cpp
|
||||
;
|
||||
|
458
src/add-ons/kernel/drivers/graphics/vesa/patch.cpp
Normal file
458
src/add-ons/kernel/drivers/graphics/vesa/patch.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user