565 lines
22 KiB
C++
565 lines
22 KiB
C++
/*
|
|
* IntelGPU.cpp
|
|
* Intel integrated graphics (i915) modesetting driver
|
|
* Scans PCI for Intel display controllers, maps MMIO, initializes GTT,
|
|
* and sets up a framebuffer using the firmware's existing display timings.
|
|
* Copyright (c) 2025 Daniel Hammer
|
|
*/
|
|
|
|
#include "IntelGPU.hpp"
|
|
#include <Pci/Pci.hpp>
|
|
#include <Terminal/Terminal.hpp>
|
|
#include <CppLib/Stream.hpp>
|
|
#include <Memory/HHDM.hpp>
|
|
#include <Memory/Paging.hpp>
|
|
#include <Memory/PageFrameAllocator.hpp>
|
|
#include <Io/IoPort.hpp>
|
|
#include <Graphics/Cursor.hpp>
|
|
|
|
using namespace Kt;
|
|
|
|
namespace Drivers::Graphics::IntelGPU {
|
|
|
|
// =========================================================================
|
|
// Driver state
|
|
// =========================================================================
|
|
|
|
static bool g_initialized = false;
|
|
|
|
static GpuInfo g_gpuInfo = {};
|
|
|
|
static volatile uint8_t* g_mmioBase = nullptr;
|
|
|
|
// GTT state
|
|
static volatile void* g_gttBase = nullptr; // Virtual address of GTT entries
|
|
static uint64_t g_gttEntryCount = 0; // Number of GTT entries
|
|
static uint64_t g_scratchPagePhys = 0; // Physical address of scratch page
|
|
static uint8_t g_gpuGen = 0; // Cached generation number
|
|
|
|
// Framebuffer state
|
|
static uint32_t* g_fbBase = nullptr; // HHDM virtual address of first FB page
|
|
static uint64_t g_fbPhysBase = 0; // Physical address of first FB page
|
|
static uint64_t g_fbWidth = 0;
|
|
static uint64_t g_fbHeight = 0;
|
|
static uint64_t g_fbPitch = 0; // Stride in bytes
|
|
static uint64_t g_fbSize = 0; // Total framebuffer size in bytes
|
|
static uint64_t g_fbGttOffset = 0; // GTT offset where FB starts (in bytes)
|
|
|
|
// =========================================================================
|
|
// Register access helpers
|
|
// =========================================================================
|
|
|
|
static void WriteReg(uint32_t reg, uint32_t val) {
|
|
*(volatile uint32_t*)(g_mmioBase + reg) = val;
|
|
}
|
|
|
|
static uint32_t ReadReg(uint32_t reg) {
|
|
return *(volatile uint32_t*)(g_mmioBase + reg);
|
|
}
|
|
|
|
// =========================================================================
|
|
// PCI Detection
|
|
// =========================================================================
|
|
|
|
static bool DetectGpu() {
|
|
auto& devices = Pci::GetDevices();
|
|
const Pci::PciDevice* found = nullptr;
|
|
const DeviceInfo* matchedInfo = nullptr;
|
|
|
|
for (uint64_t i = 0; i < devices.size(); i++) {
|
|
if (devices[i].VendorId != VendorIntel) {
|
|
continue;
|
|
}
|
|
if (devices[i].ClassCode != ClassDisplay) {
|
|
continue;
|
|
}
|
|
|
|
// Found an Intel display controller; try to match device ID
|
|
found = &devices[i];
|
|
|
|
for (int j = 0; j < SupportedDeviceCount; j++) {
|
|
if (devices[i].DeviceId == SupportedDevices[j].deviceId) {
|
|
matchedInfo = &SupportedDevices[j];
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Stop at first Intel display controller
|
|
break;
|
|
}
|
|
|
|
if (found == nullptr) {
|
|
KernelLogStream(WARNING, "IntelGPU") << "No Intel display controller found";
|
|
return false;
|
|
}
|
|
|
|
g_gpuInfo.pciBus = found->Bus;
|
|
g_gpuInfo.pciDevice = found->Device;
|
|
g_gpuInfo.pciFunction = found->Function;
|
|
g_gpuInfo.deviceId = found->DeviceId;
|
|
|
|
if (matchedInfo != nullptr) {
|
|
g_gpuInfo.gen = matchedInfo->gen;
|
|
g_gpuInfo.name = matchedInfo->name;
|
|
|
|
KernelLogStream(OK, "IntelGPU") << "Found " << matchedInfo->name
|
|
<< " (device " << base::hex << (uint64_t)found->DeviceId << ")"
|
|
<< " at PCI " << (uint64_t)found->Bus << ":"
|
|
<< (uint64_t)found->Device << "." << (uint64_t)found->Function;
|
|
} else {
|
|
// Unknown device ID - accept generically but warn
|
|
g_gpuInfo.gen = 7; // Assume gen 7 as a safe default
|
|
g_gpuInfo.name = "Unknown Intel GPU";
|
|
|
|
KernelLogStream(WARNING, "IntelGPU") << "Unknown Intel display controller "
|
|
<< "(device " << base::hex << (uint64_t)found->DeviceId << ")"
|
|
<< " at PCI " << (uint64_t)found->Bus << ":"
|
|
<< (uint64_t)found->Device << "." << (uint64_t)found->Function
|
|
<< " - attempting generic initialization";
|
|
}
|
|
|
|
g_gpuGen = g_gpuInfo.gen;
|
|
return true;
|
|
}
|
|
|
|
// =========================================================================
|
|
// BAR0 MMIO Mapping
|
|
// =========================================================================
|
|
|
|
static bool MapMmio() {
|
|
uint8_t bus = g_gpuInfo.pciBus;
|
|
uint8_t dev = g_gpuInfo.pciDevice;
|
|
uint8_t func = g_gpuInfo.pciFunction;
|
|
|
|
// Read BAR0
|
|
uint32_t bar0Low = Pci::LegacyRead32(bus, dev, func, (uint8_t)PCI_REG_BAR0);
|
|
uint64_t mmioPhys = bar0Low & 0xFFFFFFF0u; // Mask off type/prefetchable bits
|
|
|
|
// Check for 64-bit BAR (bit 2 of BAR type field)
|
|
if (bar0Low & 0x04) {
|
|
uint32_t bar0High = Pci::LegacyRead32(bus, dev, func, (uint8_t)(PCI_REG_BAR0 + 4));
|
|
mmioPhys |= ((uint64_t)bar0High << 32);
|
|
}
|
|
|
|
g_gpuInfo.mmioPhys = mmioPhys;
|
|
g_gpuInfo.mmioSize = 0x200000; // Map 2MB of MMIO space
|
|
|
|
KernelLogStream(INFO, "IntelGPU") << "BAR0 physical: " << base::hex << mmioPhys;
|
|
|
|
// Map 2MB of MMIO space (0x200 pages)
|
|
for (uint64_t offset = 0; offset < 0x200000; offset += 0x1000) {
|
|
Memory::VMM::g_paging->MapMMIO(mmioPhys + offset, Memory::HHDM(mmioPhys + offset));
|
|
}
|
|
|
|
g_mmioBase = (volatile uint8_t*)Memory::HHDM(mmioPhys);
|
|
|
|
KernelLogStream(OK, "IntelGPU") << "MMIO mapped at virtual " << base::hex << (uint64_t)g_mmioBase;
|
|
|
|
// Enable memory space and bus master in PCI command register
|
|
uint16_t pciCmd = Pci::LegacyRead16(bus, dev, func, (uint8_t)PCI_REG_COMMAND);
|
|
pciCmd |= PCI_CMD_MEM_SPACE | PCI_CMD_BUS_MASTER;
|
|
Pci::LegacyWrite16(bus, dev, func, (uint8_t)PCI_REG_COMMAND, pciCmd);
|
|
|
|
KernelLogStream(OK, "IntelGPU") << "PCI memory space and bus mastering enabled";
|
|
|
|
return true;
|
|
}
|
|
|
|
// =========================================================================
|
|
// VGA Disable
|
|
// =========================================================================
|
|
|
|
static void DisableVga() {
|
|
// Check if VGA plane is already disabled (bit 31 of VGACNTRL).
|
|
// On modern Intel iGPUs with eDP/LVDS panels, the firmware typically
|
|
// disables VGA when it sets up GOP. Skip if already done.
|
|
uint32_t vgaCtrl = ReadReg(VGACNTRL);
|
|
if (vgaCtrl & VGACNTRL_DISABLE) {
|
|
KernelLogStream(INFO, "IntelGPU") << "VGA plane already disabled by firmware";
|
|
return;
|
|
}
|
|
|
|
// Step 1: Disable VGA screen via the VGA sequencer I/O ports
|
|
// SR01 bit 5 must be set BEFORE disabling the VGA plane register
|
|
Io::Out8(0x01, 0x3C4);
|
|
uint8_t sr01 = Io::In8(0x3C5);
|
|
sr01 |= (1 << 5);
|
|
Io::Out8(0x01, 0x3C4);
|
|
Io::Out8(sr01, 0x3C5);
|
|
|
|
// Step 2: Set bit 31 of VGACNTRL MMIO register to disable VGA display plane
|
|
vgaCtrl |= VGACNTRL_DISABLE;
|
|
WriteReg(VGACNTRL, vgaCtrl);
|
|
|
|
// Step 3: Read back to flush the write
|
|
(void)ReadReg(VGACNTRL);
|
|
|
|
KernelLogStream(OK, "IntelGPU") << "VGA plane disabled";
|
|
}
|
|
|
|
// =========================================================================
|
|
// Read Current Display State
|
|
// =========================================================================
|
|
|
|
static bool ReadDisplayState() {
|
|
// Read pipe A configuration
|
|
uint32_t pipeConf = ReadReg(PIPEACONF);
|
|
bool pipeEnabled = (pipeConf & PIPECONF_ENABLE) != 0;
|
|
|
|
// Read plane A control
|
|
uint32_t dspaCntr = ReadReg(DSPACNTR);
|
|
bool planeEnabled = (dspaCntr & DISP_ENABLE) != 0;
|
|
|
|
// Read current surface address and stride
|
|
uint32_t dspaSurf = ReadReg(DSPASURF);
|
|
uint32_t dspaStride = ReadReg(DSPASTRIDE);
|
|
|
|
// Read timing registers
|
|
uint32_t htotal = ReadReg(HTOTAL_A);
|
|
uint32_t vtotal = ReadReg(VTOTAL_A);
|
|
uint32_t pipeSrc = ReadReg(PIPEASRC);
|
|
|
|
KernelLogStream(INFO, "IntelGPU") << "Pipe A: "
|
|
<< (pipeEnabled ? "ENABLED" : "DISABLED")
|
|
<< ", Plane A: " << (planeEnabled ? "ENABLED" : "DISABLED");
|
|
KernelLogStream(INFO, "IntelGPU") << "DSPASURF: " << base::hex << (uint64_t)dspaSurf
|
|
<< ", DSPASTRIDE: " << (uint64_t)dspaStride;
|
|
KernelLogStream(INFO, "IntelGPU") << "HTOTAL_A: " << base::hex << (uint64_t)htotal
|
|
<< ", VTOTAL_A: " << (uint64_t)vtotal
|
|
<< ", PIPEASRC: " << (uint64_t)pipeSrc;
|
|
|
|
// Extract resolution from PIPEASRC (preferred) or timing registers
|
|
if (pipeSrc != 0) {
|
|
// PIPEASRC: bits [31:16] = horizontal size - 1, bits [15:0] = vertical size - 1
|
|
g_fbWidth = ((pipeSrc >> 16) & 0xFFFF) + 1;
|
|
g_fbHeight = (pipeSrc & 0xFFFF) + 1;
|
|
} else if (pipeEnabled) {
|
|
// Fallback to timing registers
|
|
g_fbWidth = (htotal & 0xFFF) + 1;
|
|
g_fbHeight = (vtotal & 0xFFF) + 1;
|
|
}
|
|
|
|
// Read stride from hardware.
|
|
// On Gen 9+ (Skylake and later), PLANE_STRIDE (same offset as DSPASTRIDE)
|
|
// stores the stride in 64-byte units, not bytes. Detect this by checking
|
|
// whether the raw value is too small to be a byte stride.
|
|
if (dspaStride != 0) {
|
|
g_fbPitch = dspaStride;
|
|
if (g_fbWidth > 0 && g_fbPitch < g_fbWidth * 4) {
|
|
KernelLogStream(INFO, "IntelGPU") << "DSPASTRIDE=" << base::dec << g_fbPitch
|
|
<< " is in 64-byte units (Gen 9+), converting to bytes";
|
|
g_fbPitch *= 64;
|
|
}
|
|
}
|
|
|
|
// If we still don't have valid dimensions, fall back to the firmware framebuffer
|
|
if (g_fbWidth == 0 || g_fbHeight == 0 || g_fbPitch == 0) {
|
|
g_fbWidth = ::Graphics::Cursor::GetFramebufferWidth();
|
|
g_fbHeight = ::Graphics::Cursor::GetFramebufferHeight();
|
|
g_fbPitch = ::Graphics::Cursor::GetFramebufferPitch();
|
|
|
|
KernelLogStream(INFO, "IntelGPU") << "Using firmware framebuffer dimensions: "
|
|
<< base::dec << g_fbWidth << "x" << g_fbHeight
|
|
<< " pitch=" << g_fbPitch;
|
|
} else {
|
|
KernelLogStream(INFO, "IntelGPU") << "Detected resolution: "
|
|
<< base::dec << g_fbWidth << "x" << g_fbHeight
|
|
<< " pitch=" << g_fbPitch;
|
|
}
|
|
|
|
if (g_fbWidth == 0 || g_fbHeight == 0) {
|
|
KernelLogStream(ERROR, "IntelGPU") << "Could not determine display resolution";
|
|
return false;
|
|
}
|
|
|
|
// Ensure pitch is at least width * 4 (BGRX8888)
|
|
if (g_fbPitch == 0) {
|
|
g_fbPitch = g_fbWidth * 4;
|
|
KernelLogStream(WARNING, "IntelGPU") << "Stride not available, assuming "
|
|
<< base::dec << g_fbPitch << " bytes";
|
|
}
|
|
|
|
g_fbSize = g_fbHeight * g_fbPitch;
|
|
|
|
KernelLogStream(OK, "IntelGPU") << "Display state: "
|
|
<< base::dec << g_fbWidth << "x" << g_fbHeight
|
|
<< ", stride=" << g_fbPitch
|
|
<< ", FB size=" << g_fbSize << " bytes";
|
|
|
|
return true;
|
|
}
|
|
|
|
// =========================================================================
|
|
// GTT Initialization
|
|
// =========================================================================
|
|
|
|
static bool InitializeGtt() {
|
|
uint8_t bus = g_gpuInfo.pciBus;
|
|
uint8_t dev = g_gpuInfo.pciDevice;
|
|
uint8_t func = g_gpuInfo.pciFunction;
|
|
|
|
// Read GMCH_CTL to determine GTT size
|
|
uint16_t gmchCtl = Pci::LegacyRead16(bus, dev, func, (uint8_t)PCI_REG_GMCH_CTL);
|
|
uint8_t gttSizeBits = (gmchCtl >> 8) & 0x3;
|
|
|
|
uint64_t gttSizeBytes = 0;
|
|
|
|
if (g_gpuGen >= 8) {
|
|
// Gen 8+ has a different encoding for GTT size
|
|
switch (gttSizeBits) {
|
|
case 0: gttSizeBytes = 0; break; // No GTT
|
|
case 1: gttSizeBytes = 2 * 1024 * 1024; break; // 2MB
|
|
case 2: gttSizeBytes = 4 * 1024 * 1024; break; // 4MB
|
|
case 3: gttSizeBytes = 8 * 1024 * 1024; break; // 8MB
|
|
default: gttSizeBytes = 2 * 1024 * 1024; break;
|
|
}
|
|
} else {
|
|
// Gen 5-7
|
|
switch (gttSizeBits) {
|
|
case 0: gttSizeBytes = 0; break; // No GTT
|
|
case 1: gttSizeBytes = 1024 * 1024; break; // 1MB
|
|
case 2: gttSizeBytes = 2 * 1024 * 1024; break; // 2MB
|
|
case 3: gttSizeBytes = 2 * 1024 * 1024; break; // Depends on gen, default 2MB
|
|
default: gttSizeBytes = 1024 * 1024; break;
|
|
}
|
|
}
|
|
|
|
if (gttSizeBytes == 0) {
|
|
// If hardware reports no GTT, assume 1MB as a safe fallback
|
|
gttSizeBytes = 1024 * 1024;
|
|
KernelLogStream(WARNING, "IntelGPU") << "GMCH_CTL reports no GTT, assuming 1MB";
|
|
}
|
|
|
|
g_gpuInfo.gttSize = gttSizeBytes;
|
|
|
|
KernelLogStream(INFO, "IntelGPU") << "GMCH_CTL: " << base::hex << (uint64_t)gmchCtl
|
|
<< ", GTT size: " << base::dec << (gttSizeBytes / 1024) << " KB";
|
|
|
|
// The GTT entries reside at BAR0 + 2MB (offset 0x200000)
|
|
// This is correct for most Intel generations
|
|
uint64_t gttPhys = g_gpuInfo.mmioPhys + 0x200000;
|
|
|
|
// Map the GTT region (it may overlap with already-mapped MMIO, but we map
|
|
// additional pages beyond the initial 2MB MMIO mapping)
|
|
uint64_t gttMapSize = gttSizeBytes;
|
|
// Ensure we map at least up to the GTT region end
|
|
for (uint64_t offset = 0; offset < gttMapSize; offset += 0x1000) {
|
|
Memory::VMM::g_paging->MapMMIO(gttPhys + offset, Memory::HHDM(gttPhys + offset));
|
|
}
|
|
|
|
g_gttBase = (volatile void*)Memory::HHDM(gttPhys);
|
|
|
|
// Calculate number of GTT entries
|
|
if (g_gpuGen >= 8) {
|
|
// 64-bit PTEs
|
|
g_gttEntryCount = gttSizeBytes / sizeof(uint64_t);
|
|
} else {
|
|
// 32-bit PTEs
|
|
g_gttEntryCount = gttSizeBytes / sizeof(uint32_t);
|
|
}
|
|
|
|
KernelLogStream(INFO, "IntelGPU") << "GTT at physical " << base::hex << gttPhys
|
|
<< ", " << base::dec << g_gttEntryCount << " entries"
|
|
<< (g_gpuGen >= 8 ? " (64-bit PTEs)" : " (32-bit PTEs)");
|
|
|
|
// Allocate a scratch page (zeroed) for future use
|
|
void* scratchPageVirt = Memory::g_pfa->AllocateZeroed();
|
|
g_scratchPagePhys = Memory::SubHHDM(scratchPageVirt);
|
|
|
|
// Do NOT clear the entire GTT here. The firmware has active GTT mappings
|
|
// that the display engine is currently scanning out from. Clearing them
|
|
// would cause the display to go black (or worse) before we remap.
|
|
// Instead, we only write the entries we need in SetupFramebuffer().
|
|
|
|
KernelLogStream(OK, "IntelGPU") << "GTT ready: " << base::dec << g_gttEntryCount
|
|
<< " entries, scratch page at " << base::hex << g_scratchPagePhys;
|
|
|
|
return true;
|
|
}
|
|
|
|
// =========================================================================
|
|
// Framebuffer Allocation and Setup
|
|
// =========================================================================
|
|
|
|
static bool SetupFramebuffer() {
|
|
// Map the firmware framebuffer's contiguous physical pages through our GTT.
|
|
// This keeps the same physical memory that the firmware set up (contiguous
|
|
// pages, already HHDM-mapped), so both kernel and userspace access continue
|
|
// to work via the original virtual/physical addresses. No copy is needed.
|
|
uint32_t* fwFb = ::Graphics::Cursor::GetFramebufferBase();
|
|
if (fwFb == nullptr) {
|
|
KernelLogStream(ERROR, "IntelGPU") << "No firmware framebuffer available";
|
|
return false;
|
|
}
|
|
|
|
uint64_t fwFbPhys = Memory::SubHHDM((uint64_t)fwFb);
|
|
uint64_t pageCount = (g_fbSize + 0xFFF) / 0x1000;
|
|
|
|
if (pageCount > g_gttEntryCount) {
|
|
KernelLogStream(ERROR, "IntelGPU") << "Framebuffer requires " << base::dec << pageCount
|
|
<< " pages but GTT only has " << g_gttEntryCount << " entries";
|
|
return false;
|
|
}
|
|
|
|
KernelLogStream(INFO, "IntelGPU") << "Mapping " << base::dec << pageCount
|
|
<< " firmware FB pages through GTT (phys base " << base::hex << fwFbPhys << ")";
|
|
|
|
// Program GTT entries to point to the firmware FB's contiguous physical pages
|
|
if (g_gpuGen >= 8) {
|
|
volatile uint64_t* gtt64 = (volatile uint64_t*)g_gttBase;
|
|
for (uint64_t i = 0; i < pageCount; i++) {
|
|
gtt64[i] = MakeGttPte64(fwFbPhys + i * 0x1000);
|
|
}
|
|
// Flush GTT writes
|
|
(void)gtt64[pageCount - 1];
|
|
} else {
|
|
volatile uint32_t* gtt32 = (volatile uint32_t*)g_gttBase;
|
|
for (uint64_t i = 0; i < pageCount; i++) {
|
|
gtt32[i] = MakeGttPte32(fwFbPhys + i * 0x1000);
|
|
}
|
|
// Flush GTT writes
|
|
(void)gtt32[pageCount - 1];
|
|
}
|
|
|
|
// Keep using the same framebuffer memory
|
|
g_fbBase = fwFb;
|
|
g_fbPhysBase = fwFbPhys;
|
|
g_fbGttOffset = 0; // Starting at GTT entry 0 => offset 0
|
|
|
|
KernelLogStream(OK, "IntelGPU") << "Framebuffer mapped through GTT: " << base::dec << pageCount
|
|
<< " pages, phys=" << base::hex << fwFbPhys;
|
|
|
|
return true;
|
|
}
|
|
|
|
static void ProgramDisplayPlane() {
|
|
// Preserve the firmware's DSPACNTR value entirely. The firmware already
|
|
// configured the correct pixel format, pipe assignment, and tiling mode.
|
|
// We only need to point DSPASURF to our GTT-mapped framebuffer.
|
|
uint32_t dspaCntr = ReadReg(DSPACNTR);
|
|
uint32_t fmtBits = (dspaCntr & DISP_FORMAT_MASK) >> DISP_FORMAT_SHIFT;
|
|
uint32_t oldDspaSurf = ReadReg(DSPASURF);
|
|
|
|
KernelLogStream(INFO, "IntelGPU") << "Preserving firmware DSPACNTR: " << base::hex
|
|
<< (uint64_t)dspaCntr << " (format=" << (uint64_t)fmtBits << ")";
|
|
KernelLogStream(INFO, "IntelGPU") << "Firmware DSPASURF was: " << base::hex
|
|
<< (uint64_t)oldDspaSurf;
|
|
|
|
// Ensure the plane is enabled (it should already be)
|
|
if (!(dspaCntr & DISP_ENABLE)) {
|
|
dspaCntr |= DISP_ENABLE;
|
|
WriteReg(DSPACNTR, dspaCntr);
|
|
}
|
|
|
|
// Do NOT write DSPASTRIDE — the firmware already set the correct value
|
|
// in the hardware's native format (bytes on Gen <9, 64-byte units on Gen 9+).
|
|
// Writing our byte-converted g_fbPitch would corrupt it on Gen 9+.
|
|
|
|
// Write the GTT base offset to DSPASURF - this triggers the plane update
|
|
// Since we mapped at GTT entry 0, the offset is 0
|
|
uint32_t surfAddr = (uint32_t)g_fbGttOffset;
|
|
WriteReg(DSPASURF, surfAddr);
|
|
|
|
// Read back to flush
|
|
(void)ReadReg(DSPASURF);
|
|
|
|
KernelLogStream(OK, "IntelGPU") << "Display plane A: DSPASURF="
|
|
<< base::hex << (uint64_t)surfAddr
|
|
<< " (was " << (uint64_t)oldDspaSurf << ")"
|
|
<< ", stride=" << base::dec << g_fbPitch;
|
|
}
|
|
|
|
// =========================================================================
|
|
// Public API
|
|
// =========================================================================
|
|
|
|
void Initialize() {
|
|
KernelLogStream(INFO, "IntelGPU") << "Scanning for Intel integrated graphics...";
|
|
|
|
// Step 1: Detect GPU on PCI bus
|
|
if (!DetectGpu()) {
|
|
return;
|
|
}
|
|
|
|
// Step 2: Map BAR0 MMIO region
|
|
if (!MapMmio()) {
|
|
KernelLogStream(ERROR, "IntelGPU") << "Failed to map MMIO region";
|
|
return;
|
|
}
|
|
|
|
// Step 3: Disable VGA plane
|
|
DisableVga();
|
|
|
|
// Step 4: Read current display state from firmware
|
|
if (!ReadDisplayState()) {
|
|
KernelLogStream(ERROR, "IntelGPU") << "Failed to read display state";
|
|
return;
|
|
}
|
|
|
|
// Step 5: Initialize GTT
|
|
if (!InitializeGtt()) {
|
|
KernelLogStream(ERROR, "IntelGPU") << "Failed to initialize GTT";
|
|
return;
|
|
}
|
|
|
|
// Step 6: Map firmware framebuffer pages through GTT
|
|
if (!SetupFramebuffer()) {
|
|
KernelLogStream(ERROR, "IntelGPU") << "Failed to set up framebuffer";
|
|
return;
|
|
}
|
|
|
|
// Step 7: Program the display plane to use our GTT-mapped framebuffer
|
|
ProgramDisplayPlane();
|
|
|
|
g_initialized = true;
|
|
|
|
// Diagnostic: compare GPU-detected values with firmware/Limine values
|
|
uint64_t fwWidth = ::Graphics::Cursor::GetFramebufferWidth();
|
|
uint64_t fwHeight = ::Graphics::Cursor::GetFramebufferHeight();
|
|
uint64_t fwPitch = ::Graphics::Cursor::GetFramebufferPitch();
|
|
|
|
if (g_fbWidth != fwWidth || g_fbHeight != fwHeight || g_fbPitch != fwPitch) {
|
|
KernelLogStream(WARNING, "IntelGPU") << "GPU dimensions differ from firmware!";
|
|
KernelLogStream(WARNING, "IntelGPU") << " GPU: "
|
|
<< base::dec << g_fbWidth << "x" << g_fbHeight
|
|
<< " pitch=" << g_fbPitch;
|
|
KernelLogStream(WARNING, "IntelGPU") << " Firmware: "
|
|
<< base::dec << fwWidth << "x" << fwHeight
|
|
<< " pitch=" << fwPitch;
|
|
}
|
|
|
|
KernelLogStream(OK, "IntelGPU") << "Initialization complete: "
|
|
<< base::dec << g_fbWidth << "x" << g_fbHeight
|
|
<< " @ " << base::hex << (uint64_t)g_fbBase;
|
|
}
|
|
|
|
bool IsInitialized() {
|
|
return g_initialized;
|
|
}
|
|
|
|
const GpuInfo* GetGpuInfo() {
|
|
return &g_gpuInfo;
|
|
}
|
|
|
|
uint32_t* GetFramebufferBase() {
|
|
return g_fbBase;
|
|
}
|
|
|
|
uint64_t GetFramebufferPhysBase() {
|
|
return g_fbPhysBase;
|
|
}
|
|
|
|
uint64_t GetWidth() {
|
|
return g_fbWidth;
|
|
}
|
|
|
|
uint64_t GetHeight() {
|
|
return g_fbHeight;
|
|
}
|
|
|
|
uint64_t GetPitch() {
|
|
return g_fbPitch;
|
|
}
|
|
|
|
};
|