feat: userspace overhaul, Intel GPU driver, and more
This commit is contained in:
@@ -0,0 +1,564 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
};
|
||||
@@ -0,0 +1,420 @@
|
||||
/*
|
||||
* IntelGPU.hpp
|
||||
* Intel integrated graphics (i915) modesetting driver
|
||||
* Supports Gen 5 (Ironlake) through Gen 12 (Tiger Lake / Alder Lake)
|
||||
* Copyright (c) 2025 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
namespace Drivers::Graphics::IntelGPU {
|
||||
|
||||
// =========================================================================
|
||||
// PCI identification
|
||||
// =========================================================================
|
||||
|
||||
static constexpr uint16_t VendorIntel = 0x8086;
|
||||
static constexpr uint8_t ClassDisplay = 0x03;
|
||||
static constexpr uint8_t SubclassVGA = 0x00;
|
||||
|
||||
// PCI config space offsets
|
||||
static constexpr uint16_t PCI_REG_BAR0 = 0x10;
|
||||
static constexpr uint16_t PCI_REG_BAR2 = 0x18;
|
||||
static constexpr uint16_t PCI_REG_COMMAND = 0x04;
|
||||
static constexpr uint16_t PCI_CMD_MEM_SPACE = (1 << 1);
|
||||
static constexpr uint16_t PCI_CMD_BUS_MASTER = (1 << 2);
|
||||
|
||||
// Graphics control register (PCI config offset 0x50 on SNB+)
|
||||
static constexpr uint16_t PCI_REG_GMCH_CTL = 0x50;
|
||||
|
||||
// Supported Intel GPU device IDs (representative subset)
|
||||
struct DeviceInfo {
|
||||
uint16_t deviceId;
|
||||
uint8_t gen; // Intel graphics generation (5-12)
|
||||
const char* name;
|
||||
};
|
||||
|
||||
static constexpr DeviceInfo SupportedDevices[] = {
|
||||
// Gen 5 - Ironlake
|
||||
{0x0042, 5, "Ironlake Desktop"},
|
||||
{0x0046, 5, "Ironlake Mobile"},
|
||||
|
||||
// Gen 6 - Sandy Bridge
|
||||
{0x0102, 6, "Sandy Bridge GT1 Desktop"},
|
||||
{0x0112, 6, "Sandy Bridge GT2 Desktop"},
|
||||
{0x0122, 6, "Sandy Bridge GT2 Desktop"},
|
||||
{0x0106, 6, "Sandy Bridge GT1 Mobile"},
|
||||
{0x0116, 6, "Sandy Bridge GT2 Mobile"},
|
||||
{0x0126, 6, "Sandy Bridge GT2 Mobile"},
|
||||
{0x010A, 6, "Sandy Bridge GT1 Server"},
|
||||
|
||||
// Gen 7 - Ivy Bridge
|
||||
{0x0152, 7, "Ivy Bridge GT1 Desktop"},
|
||||
{0x0162, 7, "Ivy Bridge GT2 Desktop"},
|
||||
{0x0156, 7, "Ivy Bridge GT1 Mobile"},
|
||||
{0x0166, 7, "Ivy Bridge GT2 Mobile"},
|
||||
{0x015A, 7, "Ivy Bridge GT1 Server"},
|
||||
{0x016A, 7, "Ivy Bridge GT2 Server"},
|
||||
|
||||
// Gen 7.5 - Haswell
|
||||
{0x0402, 7, "Haswell GT1 Desktop"},
|
||||
{0x0412, 7, "Haswell GT2 Desktop"},
|
||||
{0x0422, 7, "Haswell GT3 Desktop"},
|
||||
{0x0406, 7, "Haswell GT1 Mobile"},
|
||||
{0x0416, 7, "Haswell GT2 Mobile"},
|
||||
{0x0426, 7, "Haswell GT3 Mobile"},
|
||||
{0x0A06, 7, "Haswell ULT GT1"},
|
||||
{0x0A16, 7, "Haswell ULT GT2"},
|
||||
{0x0A26, 7, "Haswell ULT GT3"},
|
||||
{0x0D12, 7, "Haswell CRW GT2"},
|
||||
{0x0D22, 7, "Haswell CRW GT3"},
|
||||
|
||||
// Gen 8 - Broadwell
|
||||
{0x1602, 8, "Broadwell GT1"},
|
||||
{0x1612, 8, "Broadwell GT2"},
|
||||
{0x1616, 8, "Broadwell GT2 Mobile"},
|
||||
{0x1622, 8, "Broadwell GT3"},
|
||||
{0x1626, 8, "Broadwell GT3 Mobile"},
|
||||
{0x162A, 8, "Broadwell GT3 Server"},
|
||||
|
||||
// Gen 9 - Skylake
|
||||
{0x1902, 9, "Skylake GT1 Desktop"},
|
||||
{0x1906, 9, "Skylake GT1 Mobile"},
|
||||
{0x1912, 9, "Skylake GT2 Desktop"},
|
||||
{0x1916, 9, "Skylake GT2 Mobile"},
|
||||
{0x191E, 9, "Skylake GT2 Mobile"},
|
||||
{0x1926, 9, "Skylake GT3 Mobile"},
|
||||
{0x1932, 9, "Skylake GT4 Desktop"},
|
||||
|
||||
// Gen 9.5 - Kaby Lake / Coffee Lake
|
||||
{0x5902, 9, "Kaby Lake GT1 Desktop"},
|
||||
{0x5912, 9, "Kaby Lake GT2 Desktop"},
|
||||
{0x5916, 9, "Kaby Lake GT2 Mobile"},
|
||||
{0x5926, 9, "Kaby Lake GT3 Mobile"},
|
||||
{0x3E90, 9, "Coffee Lake GT1 Desktop"},
|
||||
{0x3E92, 9, "Coffee Lake GT2 Desktop"},
|
||||
{0x3EA0, 9, "Coffee Lake GT3"},
|
||||
{0x3E91, 9, "Coffee Lake GT2 Desktop"},
|
||||
{0x3E98, 9, "Coffee Lake GT2 Desktop"},
|
||||
{0x9B41, 9, "Comet Lake GT2"},
|
||||
{0x9BA5, 9, "Comet Lake GT2 Mobile"},
|
||||
|
||||
// Gen 11 - Ice Lake
|
||||
{0x8A52, 11, "Ice Lake GT2"},
|
||||
{0x8A56, 11, "Ice Lake GT2 Mobile"},
|
||||
{0x8A5A, 11, "Ice Lake GT1.5"},
|
||||
{0x8A5C, 11, "Ice Lake GT1"},
|
||||
|
||||
// Gen 12 - Tiger Lake
|
||||
{0x9A49, 12, "Tiger Lake GT2"},
|
||||
{0x9A78, 12, "Tiger Lake GT2"},
|
||||
{0x9A40, 12, "Tiger Lake GT2"},
|
||||
|
||||
// Gen 12 - Alder Lake
|
||||
{0x4626, 12, "Alder Lake GT2"},
|
||||
{0x4680, 12, "Alder Lake-S GT1"},
|
||||
{0x4692, 12, "Alder Lake-S GT1"},
|
||||
{0x46A6, 12, "Alder Lake-P GT2"},
|
||||
};
|
||||
|
||||
static constexpr int SupportedDeviceCount = sizeof(SupportedDevices) / sizeof(SupportedDevices[0]);
|
||||
|
||||
// =========================================================================
|
||||
// MMIO register offsets (relative to BAR0)
|
||||
// =========================================================================
|
||||
|
||||
// --- VGA control ---
|
||||
static constexpr uint32_t VGACNTRL = 0x71400;
|
||||
static constexpr uint32_t VGACNTRL_DISABLE = (1u << 31);
|
||||
|
||||
// --- DPLL (Display PLL) ---
|
||||
static constexpr uint32_t DPLL_A = 0x06014;
|
||||
static constexpr uint32_t DPLL_B = 0x06018;
|
||||
static constexpr uint32_t FPA0 = 0x06040;
|
||||
static constexpr uint32_t FPA1 = 0x06044;
|
||||
static constexpr uint32_t FPB0 = 0x06048;
|
||||
static constexpr uint32_t FPB1 = 0x0604C;
|
||||
|
||||
// DPLL control bits
|
||||
static constexpr uint32_t DPLL_VCO_ENABLE = (1u << 31);
|
||||
static constexpr uint32_t DPLL_VGA_MODE_DIS = (1u << 28);
|
||||
static constexpr uint32_t DPLL_MODE_DAC_SDVO = (1u << 26);
|
||||
static constexpr uint32_t DPLL_MODE_LVDS = (2u << 26);
|
||||
|
||||
// FP register fields
|
||||
static constexpr uint32_t FP_N_DIV_SHIFT = 16;
|
||||
static constexpr uint32_t FP_N_DIV_MASK = 0x3F0000;
|
||||
static constexpr uint32_t FP_M1_DIV_SHIFT = 8;
|
||||
static constexpr uint32_t FP_M1_DIV_MASK = 0x003F00;
|
||||
static constexpr uint32_t FP_M2_DIV_SHIFT = 0;
|
||||
static constexpr uint32_t FP_M2_DIV_MASK = 0x00003F;
|
||||
|
||||
// --- Display timing registers (Pipe A) ---
|
||||
static constexpr uint32_t HTOTAL_A = 0x60000;
|
||||
static constexpr uint32_t HBLANK_A = 0x60004;
|
||||
static constexpr uint32_t HSYNC_A = 0x60008;
|
||||
static constexpr uint32_t VTOTAL_A = 0x6000C;
|
||||
static constexpr uint32_t VBLANK_A = 0x60010;
|
||||
static constexpr uint32_t VSYNC_A = 0x60014;
|
||||
static constexpr uint32_t PIPEASRC = 0x6001C;
|
||||
|
||||
// --- Display timing registers (Pipe B) ---
|
||||
static constexpr uint32_t HTOTAL_B = 0x61000;
|
||||
static constexpr uint32_t HBLANK_B = 0x61004;
|
||||
static constexpr uint32_t HSYNC_B = 0x61008;
|
||||
static constexpr uint32_t VTOTAL_B = 0x6100C;
|
||||
static constexpr uint32_t VBLANK_B = 0x61010;
|
||||
static constexpr uint32_t VSYNC_B = 0x61014;
|
||||
static constexpr uint32_t PIPEBSRC = 0x6101C;
|
||||
|
||||
// --- Pipe configuration ---
|
||||
static constexpr uint32_t PIPEACONF = 0x70008;
|
||||
static constexpr uint32_t PIPEBCONF = 0x71008;
|
||||
|
||||
static constexpr uint32_t PIPECONF_ENABLE = (1u << 31);
|
||||
static constexpr uint32_t PIPECONF_STATE = (1u << 30);
|
||||
static constexpr uint32_t PIPECONF_8BPC = (0u << 5);
|
||||
static constexpr uint32_t PIPECONF_10BPC = (1u << 5);
|
||||
static constexpr uint32_t PIPECONF_6BPC = (2u << 5);
|
||||
static constexpr uint32_t PIPECONF_12BPC = (3u << 5);
|
||||
|
||||
// --- Display plane control (Plane A, pre-Skylake i9xx-style) ---
|
||||
static constexpr uint32_t DSPACNTR = 0x70180;
|
||||
static constexpr uint32_t DSPALINOFF = 0x70184;
|
||||
static constexpr uint32_t DSPASTRIDE = 0x70188;
|
||||
static constexpr uint32_t DSPAPOS = 0x7018C;
|
||||
static constexpr uint32_t DSPASIZE = 0x70190;
|
||||
static constexpr uint32_t DSPASURF = 0x7019C;
|
||||
static constexpr uint32_t DSPATILEOFF = 0x701A4;
|
||||
|
||||
// --- Display plane control (Plane B) ---
|
||||
static constexpr uint32_t DSPBCNTR = 0x71180;
|
||||
static constexpr uint32_t DSPBLINOFF = 0x71184;
|
||||
static constexpr uint32_t DSPBSTRIDE = 0x71188;
|
||||
static constexpr uint32_t DSPBPOS = 0x7118C;
|
||||
static constexpr uint32_t DSPBSIZE = 0x71190;
|
||||
static constexpr uint32_t DSPBSURF = 0x7119C;
|
||||
static constexpr uint32_t DSPBTILEOFF = 0x711A4;
|
||||
|
||||
// DSPCNTR bits
|
||||
static constexpr uint32_t DISP_ENABLE = (1u << 31);
|
||||
static constexpr uint32_t DISP_GAMMA_ENABLE = (1u << 30);
|
||||
static constexpr uint32_t DISP_FORMAT_SHIFT = 26;
|
||||
static constexpr uint32_t DISP_FORMAT_MASK = (0xFu << 26);
|
||||
static constexpr uint32_t DISP_FORMAT_BGRX8888 = (0x6u << 26); // 32bpp BGRX (no alpha)
|
||||
static constexpr uint32_t DISP_FORMAT_BGRA8888 = (0x7u << 26); // 32bpp BGRA (with alpha)
|
||||
static constexpr uint32_t DISP_FORMAT_RGBX8888 = (0xEu << 26); // 32bpp RGBX
|
||||
static constexpr uint32_t DISP_FORMAT_BGR565 = (0x5u << 26); // 16bpp BGR 5:6:5
|
||||
static constexpr uint32_t DISP_FORMAT_BGRX1010102 = (0xAu << 26);
|
||||
static constexpr uint32_t DISP_PIPE_B_SELECT = (1u << 24);
|
||||
static constexpr uint32_t DISP_TILED = (1u << 10);
|
||||
|
||||
// --- Cursor plane (Pipe A) ---
|
||||
static constexpr uint32_t CURACNTR = 0x70080;
|
||||
static constexpr uint32_t CURABASE = 0x70084;
|
||||
static constexpr uint32_t CURAPOS = 0x70088;
|
||||
|
||||
// --- Output connectors ---
|
||||
static constexpr uint32_t ADPA = 0x61100; // Analog Display Port (VGA/CRT)
|
||||
static constexpr uint32_t DVOB = 0x61140; // DVO-B
|
||||
static constexpr uint32_t DVOC = 0x61160; // DVO-C
|
||||
static constexpr uint32_t LVDS = 0x61180; // LVDS panel
|
||||
static constexpr uint32_t DP_B = 0x64100; // DisplayPort B
|
||||
static constexpr uint32_t DP_C = 0x64200; // DisplayPort C
|
||||
static constexpr uint32_t DP_D = 0x64300; // DisplayPort D
|
||||
static constexpr uint32_t HDMI_B = 0x61140; // HDMI-B (same as DVOB on some gens)
|
||||
static constexpr uint32_t HDMI_C = 0x61160; // HDMI-C (same as DVOC on some gens)
|
||||
|
||||
// ADPA bits
|
||||
static constexpr uint32_t ADPA_DAC_ENABLE = (1u << 31);
|
||||
static constexpr uint32_t ADPA_PIPE_B_SELECT = (1u << 30);
|
||||
static constexpr uint32_t ADPA_HSYNC_ACTIVE_LOW = (1u << 3);
|
||||
static constexpr uint32_t ADPA_VSYNC_ACTIVE_LOW = (1u << 4);
|
||||
|
||||
// LVDS bits
|
||||
static constexpr uint32_t LVDS_PORT_ENABLE = (1u << 31);
|
||||
static constexpr uint32_t LVDS_PIPE_B_SELECT = (1u << 30);
|
||||
|
||||
// --- GMBUS (I2C for EDID) ---
|
||||
static constexpr uint32_t GMBUS0 = 0x5100; // Clock/port select
|
||||
static constexpr uint32_t GMBUS1 = 0x5104; // Command/status
|
||||
static constexpr uint32_t GMBUS2 = 0x5108; // Status
|
||||
static constexpr uint32_t GMBUS3 = 0x510C; // Data buffer
|
||||
static constexpr uint32_t GMBUS4 = 0x5110; // Interrupt mask
|
||||
static constexpr uint32_t GMBUS5 = 0x5120; // 2-byte index
|
||||
|
||||
// --- Hardware status page ---
|
||||
static constexpr uint32_t HWS_PGA = 0x02080;
|
||||
|
||||
// --- Fence registers (tiling) ---
|
||||
static constexpr uint32_t FENCE_REG_BASE = 0x02000; // Gen 2-3
|
||||
static constexpr uint32_t FENCE_REG_965_BASE = 0x03000; // Gen 4+
|
||||
|
||||
// =========================================================================
|
||||
// GTT (Graphics Translation Table)
|
||||
// =========================================================================
|
||||
|
||||
// GTT PTE format for Gen 6/7 (Sandy Bridge through Haswell) - 32-bit entries
|
||||
// Bits [31:12] = physical page address [31:12]
|
||||
// Bits [10:4] = physical page address [38:32]
|
||||
// Bits [3:1] = cacheability
|
||||
// Bit [0] = valid
|
||||
static constexpr uint32_t GTT_PTE_VALID = (1u << 0);
|
||||
static constexpr uint32_t GTT_PTE_WB_LLC = (3u << 1); // Write-back LLC cache
|
||||
static constexpr uint32_t GTT_PTE_UNCACHED = (0u << 1);
|
||||
|
||||
// Gen 8+ uses 64-bit GTT PTEs
|
||||
static constexpr uint64_t GTT_PTE64_VALID = (1ULL << 0);
|
||||
|
||||
// Helper: Build a Gen 6/7 GTT PTE from a physical address
|
||||
static inline uint32_t MakeGttPte32(uint64_t physAddr) {
|
||||
uint32_t pte = (uint32_t)(physAddr & 0xFFFFF000u); // bits [31:12]
|
||||
pte |= (uint32_t)((physAddr >> 28) & 0x7F0u); // bits [38:32] -> PTE [10:4]
|
||||
pte |= GTT_PTE_VALID;
|
||||
return pte;
|
||||
}
|
||||
|
||||
// Helper: Build a Gen 8+ GTT PTE from a physical address
|
||||
static inline uint64_t MakeGttPte64(uint64_t physAddr) {
|
||||
return (physAddr & ~0xFFFULL) | GTT_PTE64_VALID;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// DPLL clock calculation structures
|
||||
// =========================================================================
|
||||
|
||||
struct DpllParams {
|
||||
uint32_t n;
|
||||
uint32_t m1;
|
||||
uint32_t m2;
|
||||
uint32_t p1;
|
||||
uint32_t p2;
|
||||
};
|
||||
|
||||
// DPLL parameter limits (Gen 6 / Sandy Bridge)
|
||||
struct DpllLimits {
|
||||
uint32_t nMin, nMax;
|
||||
uint32_t m1Min, m1Max;
|
||||
uint32_t m2Min, m2Max;
|
||||
uint32_t p1Min, p1Max;
|
||||
uint32_t p2Slow, p2Fast; // P2 values for slow/fast pixel clocks
|
||||
uint32_t p2Threshold; // kHz threshold between slow/fast P2
|
||||
uint32_t vcoMin, vcoMax; // VCO range in kHz
|
||||
uint32_t refClock; // Reference clock in kHz
|
||||
};
|
||||
|
||||
// Sandy Bridge / Ivy Bridge DAC/SDVO limits
|
||||
static constexpr DpllLimits SNB_DAC_LIMITS = {
|
||||
.nMin = 1, .nMax = 5,
|
||||
.m1Min = 12, .m1Max = 22,
|
||||
.m2Min = 5, .m2Max = 9,
|
||||
.p1Min = 1, .p1Max = 8,
|
||||
.p2Slow = 10, .p2Fast = 5,
|
||||
.p2Threshold = 225000,
|
||||
.vcoMin = 1750000, .vcoMax = 3500000,
|
||||
.refClock = 120000,
|
||||
};
|
||||
|
||||
// Sandy Bridge / Ivy Bridge LVDS limits
|
||||
static constexpr DpllLimits SNB_LVDS_LIMITS = {
|
||||
.nMin = 1, .nMax = 3,
|
||||
.m1Min = 12, .m1Max = 22,
|
||||
.m2Min = 5, .m2Max = 9,
|
||||
.p1Min = 1, .p1Max = 8,
|
||||
.p2Slow = 14, .p2Fast = 7,
|
||||
.p2Threshold = 225000,
|
||||
.vcoMin = 1750000, .vcoMax = 3500000,
|
||||
.refClock = 120000,
|
||||
};
|
||||
|
||||
// =========================================================================
|
||||
// Display mode timing
|
||||
// =========================================================================
|
||||
|
||||
struct DisplayMode {
|
||||
uint32_t hdisplay; // Horizontal active pixels
|
||||
uint32_t hsyncStart;
|
||||
uint32_t hsyncEnd;
|
||||
uint32_t htotal;
|
||||
uint32_t vdisplay; // Vertical active lines
|
||||
uint32_t vsyncStart;
|
||||
uint32_t vsyncEnd;
|
||||
uint32_t vtotal;
|
||||
uint32_t pixelClock; // in kHz
|
||||
bool hsyncPositive;
|
||||
bool vsyncPositive;
|
||||
};
|
||||
|
||||
// Common mode timings (CVT/DMT standard)
|
||||
static constexpr DisplayMode MODE_1920x1080_60 = {
|
||||
.hdisplay = 1920, .hsyncStart = 2008, .hsyncEnd = 2052, .htotal = 2200,
|
||||
.vdisplay = 1080, .vsyncStart = 1084, .vsyncEnd = 1089, .vtotal = 1125,
|
||||
.pixelClock = 148500,
|
||||
.hsyncPositive = true, .vsyncPositive = true,
|
||||
};
|
||||
|
||||
static constexpr DisplayMode MODE_1280x720_60 = {
|
||||
.hdisplay = 1280, .hsyncStart = 1390, .hsyncEnd = 1430, .htotal = 1650,
|
||||
.vdisplay = 720, .vsyncStart = 725, .vsyncEnd = 730, .vtotal = 750,
|
||||
.pixelClock = 74250,
|
||||
.hsyncPositive = true, .vsyncPositive = true,
|
||||
};
|
||||
|
||||
static constexpr DisplayMode MODE_1024x768_60 = {
|
||||
.hdisplay = 1024, .hsyncStart = 1048, .hsyncEnd = 1184, .htotal = 1344,
|
||||
.vdisplay = 768, .vsyncStart = 771, .vsyncEnd = 777, .vtotal = 806,
|
||||
.pixelClock = 65000,
|
||||
.hsyncPositive = false, .vsyncPositive = false,
|
||||
};
|
||||
|
||||
static constexpr DisplayMode MODE_800x600_60 = {
|
||||
.hdisplay = 800, .hsyncStart = 840, .hsyncEnd = 968, .htotal = 1056,
|
||||
.vdisplay = 600, .vsyncStart = 601, .vsyncEnd = 605, .vtotal = 628,
|
||||
.pixelClock = 40000,
|
||||
.hsyncPositive = true, .vsyncPositive = true,
|
||||
};
|
||||
|
||||
// =========================================================================
|
||||
// Detected GPU information
|
||||
// =========================================================================
|
||||
|
||||
struct GpuInfo {
|
||||
uint16_t deviceId;
|
||||
uint8_t gen;
|
||||
const char* name;
|
||||
uint8_t pciBus;
|
||||
uint8_t pciDevice;
|
||||
uint8_t pciFunction;
|
||||
uint64_t mmioPhys; // BAR0 physical address
|
||||
uint64_t mmioSize; // BAR0 region size
|
||||
uint64_t gmadrPhys; // BAR2 physical (aperture)
|
||||
uint64_t gttSize; // GTT size in bytes
|
||||
uint32_t stolenMb; // Stolen memory in MB
|
||||
};
|
||||
|
||||
// =========================================================================
|
||||
// Public API
|
||||
// =========================================================================
|
||||
|
||||
// Initialize the Intel GPU driver (scans PCI, maps MMIO, sets up GTT + FB)
|
||||
void Initialize();
|
||||
|
||||
// Check if an Intel GPU was found and initialized
|
||||
bool IsInitialized();
|
||||
|
||||
// Get detected GPU information
|
||||
const GpuInfo* GetGpuInfo();
|
||||
|
||||
// Framebuffer access (returns HHDM virtual address)
|
||||
uint32_t* GetFramebufferBase();
|
||||
|
||||
// Framebuffer physical base (for userspace mapping)
|
||||
uint64_t GetFramebufferPhysBase();
|
||||
|
||||
// Framebuffer dimensions
|
||||
uint64_t GetWidth();
|
||||
uint64_t GetHeight();
|
||||
uint64_t GetPitch();
|
||||
|
||||
};
|
||||
Reference in New Issue
Block a user