feat: power down dGPUs on hybrid graphics laptops

This commit is contained in:
daniel committed 2026-10-01 13:09:26 +02:00
1 parent 7f12e1a150
commit 24f88ec4c6
23 files changed
+4325 -1130

No files matched your search

+93 -1
View File
@@ -11,6 +11,7 @@
#include <Memory/Paging.hpp>
#include <Io/IoPort.hpp>
#include <CppLib/Spinlock.hpp>
#include <Timekeeping/ApicTimer.hpp>
using namespace Kt;
@@ -246,6 +247,54 @@ namespace Pci {
return LegacyRead8(bus, device, function, (uint8_t)offset);
}
static bool EcamCovers(uint8_t bus) {
return g_useEcam && bus >= g_ecamStartBus && bus <= g_ecamEndBus;
}
uint32_t ConfigRead(uint8_t bus, uint8_t device, uint8_t function, uint16_t offset, uint8_t width) {
if (device > 31 || function > 7 || offset > 0xFFF || (offset & (width - 1)))
return 0xFFFFFFFF;
if (EcamCovers(bus)) {
switch (width) {
case 1: return EcamRead8(g_ecamBase, bus, device, function, offset);
case 2: return EcamRead16(g_ecamBase, bus, device, function, offset);
case 4: return EcamRead32(g_ecamBase, bus, device, function, offset);
default: return 0xFFFFFFFF;
}
}
if (offset > 0xFF) return 0xFFFFFFFF;
switch (width) {
case 1: return LegacyRead8(bus, device, function, (uint8_t)offset);
case 2: return LegacyRead16(bus, device, function, (uint8_t)offset);
case 4: return LegacyRead32(bus, device, function, (uint8_t)offset);
default: return 0xFFFFFFFF;
}
}
bool ConfigWrite(uint8_t bus, uint8_t device, uint8_t function, uint16_t offset, uint8_t width, uint32_t value) {
if (device > 31 || function > 7 || offset > 0xFFF || (offset & (width - 1)))
return false;
if (EcamCovers(bus)) {
switch (width) {
case 1: EcamWrite8(g_ecamBase, bus, device, function, offset, (uint8_t)value); return true;
case 2: EcamWrite16(g_ecamBase, bus, device, function, offset, (uint16_t)value); return true;
case 4: EcamWrite32(g_ecamBase, bus, device, function, offset, value); return true;
default: return false;
}
}
if (offset > 0xFF) return false;
switch (width) {
case 1: LegacyWrite8(bus, device, function, (uint8_t)offset, (uint8_t)value); return true;
case 2: LegacyWrite16(bus, device, function, (uint8_t)offset, (uint16_t)value); return true;
case 4: LegacyWrite32(bus, device, function, (uint8_t)offset, value); return true;
default: return false;
}
}
// -------------------------------------------------------------------------
// PCI capability list traversal
// -------------------------------------------------------------------------
@@ -493,6 +542,46 @@ namespace Pci {
LegacyWrite16(bus, device, function, (uint8_t)PCI_REG_COMMAND, cmd);
}
bool IsBridge(const PciDevice& dev) {
return (dev.HeaderType & 0x7F) == 0x01;
}
// PMCSR lives at PM capability + 4; its low two bits are the D-state.
static constexpr uint16_t PmcsrOffset = 4;
int GetPowerState(uint8_t bus, uint8_t device, uint8_t function) {
if (ReadConfig16(bus, device, function, RegVendorId) == 0xFFFF) return -1;
uint8_t pm = FindCapability(bus, device, function, PCI_CAP_PM);
if (pm == 0) return -1;
return (int)(ReadConfig16(bus, device, function, pm + PmcsrOffset) & 0x3);
}
bool SetPowerState(uint8_t bus, uint8_t device, uint8_t function, int state) {
if (state < 0 || state > 3) return false;
uint8_t pm = FindCapability(bus, device, function, PCI_CAP_PM);
if (pm == 0) return false;
// D1/D2 are optional; PMC bits 25/26 advertise them.
uint32_t pmc = ConfigRead(bus, device, function, pm, 4);
if (state == 1 && !(pmc & (1u << 25))) return false;
if (state == 2 && !(pmc & (1u << 26))) return false;
uint16_t pmcsr = (uint16_t)ConfigRead(bus, device, function, pm + PmcsrOffset, 2);
int current = pmcsr & 0x3;
if (current == state) return true;
// Keep PME_Status (bit 15, write-1-to-clear) from being cleared by
// accident; only the state field changes.
pmcsr = (uint16_t)((pmcsr & ~0x8003u) | (uint16_t)state);
ConfigWrite(bus, device, function, pm + PmcsrOffset, 2, pmcsr);
// PCI PM 1.2 table 5-6: 10 ms into or out of D3hot, 200 us for D2.
if (state == 3 || current == 3) Timekeeping::BusyWaitMs(10);
else if (state == 2 || current == 2) Timekeeping::BusyWaitUs(200);
return GetPowerState(bus, device, function) == state;
}
// -------------------------------------------------------------------------
// PCI driver matching
// -------------------------------------------------------------------------
@@ -523,7 +612,9 @@ namespace Pci {
void ProbeAll(const PciDriverDesc* table, uint16_t count, ProbePhase phase) {
for (uint64_t d = 0; d < g_devices.size(); d++) {
const PciDevice& dev = g_devices[d];
PciDevice& dev = g_devices[d];
if (dev.Claimed || dev.Reserved)
continue;
for (uint16_t i = 0; i < count; i++) {
if (table[i].Phase != phase)
@@ -537,6 +628,7 @@ namespace Pci {
<< "." << (uint64_t)dev.Function;
if (table[i].Probe(dev)) {
dev.Claimed = true;
KernelLogStream(OK, "PCI") << table[i].Name << " claimed device "
<< base::hex << (uint64_t)dev.Bus << ":"
<< (uint64_t)dev.Device << "." << (uint64_t)dev.Function;