From 44bcf2b75a9bef76e149be9118d361b179752930 Mon Sep 17 00:00:00 2001 From: Admin Pupkin Date: Fri, 29 May 2026 21:50:07 +0300 Subject: [PATCH] docs: add Intel driver modernization plan Comprehensive 6-phase plan (1,055 lines) for updating the Intel GPU driver from current 1,590-line stub to full Gen9+ support ported from Linux 7.1 i915. Covers register abstraction, GMBUS I2C, DMC firmware, power wells, CDCLK, display pipeline, modesetting, and hardware validation. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- local/docs/INTEL-DRIVER-MODERNIZATION-PLAN.md | 1055 +++++++++++++++++ 1 file changed, 1055 insertions(+) create mode 100644 local/docs/INTEL-DRIVER-MODERNIZATION-PLAN.md diff --git a/local/docs/INTEL-DRIVER-MODERNIZATION-PLAN.md b/local/docs/INTEL-DRIVER-MODERNIZATION-PLAN.md new file mode 100644 index 0000000000..059fe333c8 --- /dev/null +++ b/local/docs/INTEL-DRIVER-MODERNIZATION-PLAN.md @@ -0,0 +1,1055 @@ +# Intel GPU Driver Modernization Plan + +**Created:** 2026-05-29 +**Authority:** This document is the canonical Intel-specific execution plan beneath `local/docs/DRM-MODERNIZATION-EXECUTION-PLAN.md` (Workstream C). It does not replace the DRM plan — it is the detailed Intel backend implementation guide that the DRM plan's Workstream C gates reference. +**Linux reference:** `local/reference/linux-7.1/drivers/gpu/drm/i915/` +**Driver source:** `local/recipes/gpu/redox-drm/source/src/drivers/intel/` + +## 1. Current State + +### 1.1 Code inventory + +| File | Lines | What it does | +|------|-------|--------------| +| `mod.rs` | 693 | `IntelDriver` struct, PCI BAR mapping, forcewake, device init, IRQ dispatch, `GpuDriver` trait impl | +| `display.rs` | 404 | Pipe/connector detection, modesetting, page flip, EDID stub | +| `gtt.rs` | 226 | GGTT page table: allocate, map, unmap, flush | +| `ring.rs` | 267 | Command ring buffer: render, blitter, video enhance | +| **Total** | **1,590** | | + +### 1.2 What works (real code, verified in build) + +- PCI device detection and BAR mapping (BAR0 GGTT, BAR2 MMIO) +- Forcewake register programming (`0xA18C`) +- GGTT page table management: PTE encoding, range allocation with free-list coalescing, multi-page map/unmap, flush via `GFX_FLSH_CNTL` +- Command ring buffer: ring register programming (RBBASE/RBTAIL/RBHEAD/RBCTL), tail/head tracking, `submit_batch` with space wait, MI_FLUSH_DW flush +- Display pipe detection (4 pipes, 6 ports via PIPECONF + DDI_BUF_CTL reads) +- Modesetting: HTOTAL/HBLANK/HSYNC/VTOTAL/VBLANK/VSYNC/PIPE_SRC/PLANE_SIZE/DSPCNTR/PIPECONF/DDI_BUF_CTL register writes +- Page flip via DSPSURF +- Vblank counting via PIPEFRAME +- MSI-X / MSI / legacy IRQ cascade with quirk gating +- Synthetic EDID generation (hardcoded 1920×1080@60) +- EDID block parsing (detailed timing descriptors, pixel clock, vrefresh) +- GEM buffer management (shared core in `gem.rs`) +- DRM ioctl surface (shared core in `scheme.rs`) +- DMC firmware manifest with device-ID-keyed lookup for TGL, ADLP, DG2, MTL, SKL, KBL, CNL, ICL, GLK, RKL +- Device ID tables covering Gen4 through Gen12 (including Arrow Lake, Lunar Lake, Battlemage) + +### 1.3 What does NOT work (hard stubs and missing code) + +| Gap | Severity | Current state | +|-----|----------|---------------| +| EDID I2C/DDC | **BLOCKING** | `read_edid_block()` returns `Err` every time — every connector gets synthetic 1080p | +| DPCD (DisplayPort) | **BLOCKING** | `read_dpcd()` returns empty Vec — no DP link training or capabilities | +| DMC firmware programming | **BLOCKING** | Firmware blobs are preloaded into HashMap but never written to hardware registers | +| Per-gen register offsets | **BLOCKING** | All offsets are Gen8/Gen9 constants — wrong for Gen12+ (Tiger Lake, Alder Lake, Meteor Lake) | +| Display power wells | **BLOCKING** | No power well enable — Gen9+ display registers unreadable without power domain setup | +| CDCLK / PLL | **BLOCKING** | No core display clock or pixel clock PLL programming — no pixels generated | +| DP AUX channel | HIGH | No AUX read/write — no DP EDID, no DPCD, no link training | +| HDMI infoframes | HIGH | No AVI/audio infoframes — HDMI monitors get no mode negotiation | +| Cursor plane | MEDIUM | `CURCNTR`/`CURPOS`/`CURBASE` never programmed | +| Atomic modeset | MEDIUM | Legacy SETCRTC + PAGE_FLIP only — KWin needs atomic | +| Hotplug detection | MEDIUM | Polling DDI_BUF_CTL on every IRQ; no real HPD interrupt handling | +| Watermarks / DBUF | MEDIUM | No display bandwidth programming — risk of underruns | +| Backlight | LOW | No `BLC_PWM_CTL` programming | +| PSR / FBC | LOW | Panel Self Refresh and FrameBuffer Compression not implemented | +| GuC / HuC / GSC | LOW | No GPU scheduling or media firmware | +| GT frequency / RC6 | LOW | No render power state management | + +### 1.4 Register offset problem (critical detail) + +Every register in the driver is a hardcoded Gen8/Gen9-era constant: + +| Register | Current offset | Valid for | Wrong for | +|----------|---------------|-----------|-----------| +| `FORCEWAKE` | `0xA18C` | Gen8/Gen9 | Gen12+ uses `0xA188` or forcewake ack at `0xA188`/`0xA188`+4 | +| `PIPECONF_BASE` | `0x70008` | Gen8–Gen11 | Gen12 uses `0x70008` but transcoder is separate | +| `DSPCNTR_BASE` | `0x70180` | Gen8/Gen9 primary planes | Gen12+ uses `PLANE_CTL` at `0x70180` with different bit layout | +| `DSPSURF_BASE` | `0x7019C` | Gen8/Gen9 | Gen12+ uses `PLANE_SURF` at `0x7019C` but with different semantics | +| `DDI_BUF_CTL_BASE` | `0x64000` | Gen8+ | Mostly correct but port count and lane info changed at Gen12 | +| `HTOTAL_BASE` | `0x60000` | Gen8+ transcoder A | Gen12 separates pipe timings from transcoder timings | +| `PP_STATUS` | `0xC7200` | Gen8/Gen9 | Gen12+ uses different panel power registers | + +**Impact:** The driver can compile for Gen12+ device IDs but will malfunction at runtime because it writes to the wrong register addresses and with the wrong bit layouts. + +### 1.5 Hardware generation coverage + +| Generation | Display ver | Device IDs in table | Driver gate | Register compat | DMC manifest | Real status | +|-----------|-------------|---------------------|-------------|-----------------|--------------|-------------| +| Gen4 (i965/G4x) | 4 | 18 | Blocked | Wrong | No keys | No support | +| Gen5 (Ironlake) | 5 | 2 | Blocked | Wrong | No keys | No support | +| Gen6 (Sandy Bridge) | 6 | 7 | Blocked | Wrong | No keys | No support | +| Gen7 (IVB/HSW/BDW) | 7 | ~68 | Blocked | Wrong | No keys | No support | +| Gen8 (SKL/KBL/CFL) | 9 | ~77 | Allowed | Partially correct | SKL/KBL/CFL keys | Best chance for first validation | +| Gen9 (CNL/ICL/EHL) | 10-11 | ~60 | Allowed | Partially correct | CNL/ICL/GLK/RKL keys | Needs register fixes | +| Gen12 TGL/ADL/DG2 | 12-13 | ~34 | Allowed | Wrong | TGL/ADLP/DG2 keys | Will not work without new registers | +| Gen12 MTL/ARL | 14 | ~10 | Allowed | Wrong | MTL keys | Will not work | +| Gen12 LNL/BMG | 20+ | ~18 | Allowed | Wrong | No keys | i915 does not cover these; Xe driver territory | + +## 2. Linux i915 Reference Architecture + +### 2.1 Scale + +Linux i915 in our reference tree (Linux 7.1): + +| Subsystem | .h files | .c files | Key purpose | +|-----------|----------|----------|-------------| +| `display/` | 195 | 144 | All display: pipes, planes, connectors, encoders, DP, HDMI, DMC, power, CDCLK, PLL | +| `gt/` | 78 | 83 | GPU engines, GTT, command submission, power, workarounds | +| `gt/uc/` | 34 | 23 | GuC, HuC, GSC firmware | +| `gem/` | 21 | 26 | Buffer objects, eviction, tiling, execbuffer | +| Top-level | ~50 | ~50 | PCI, MMIO, IRQ, uncore, PM, debugfs | +| **Total** | **~378** | **~326** | **~700 files** | + +Red Bear's Intel driver is ~1,590 lines. Linux i915 is ~700 files. The gap is not incremental — it is architectural. + +### 2.2 i915 probe sequence (what Red Bear must replicate) + +``` +i915_pci_probe() + → i915_driver_create() // alloc drm_i915_private, init device info + → i915_driver_early_probe() // MMIO map, uncore init, pcode init + → intel_gt_probe_all() // discover GTs and engines + → i915_driver_mmio_probe() // interrupts, GTT init + → i915_driver_hw_probe() // GT, engines, workarounds + → intel_display_driver_probe_noirq() // VBT parse, CDCLK init, power wells, PLL, connectors + → intel_irq_install() // install IRQ handlers + → intel_display_driver_probe_nogem() // encoders, connectors, initial modeset + → i915_gem_init() // GEM memory manager + → intel_display_driver_probe() // planes, framebuffer, fbdev + → i915_driver_register() // DRM device registration +``` + +### 2.3 Critical display init subsequence (Red Bear's missing pieces) + +The Linux `intel_display_driver_probe_noirq()` call chain does the following, none of which Red Bear implements: + +``` +intel_power_domains_init() → enable display power wells (DC off → DC on) +intel_cdclk_init() → program core display clock +intel_dmc_init() → upload DMC firmware to hardware +intel_dpll_init() → initialize shared PLL pool +intel_display_device_info_init() → per-generation capability table +intel_bios_init() → parse VBT for port types, backlight, etc. +intel_gmbus_init() → initialize I2C/GMBUS controllers +intel_panel_init() → backlight, eDP panel power sequencing +intel_psr_init() → Panel Self Refresh setup +``` + +None of these exist in Red Bear. Without them, the display engine is not initialized and register reads may return 0 or garbage. + +### 2.4 GMBUS/DDC I2C (the #1 missing piece) + +Linux path for reading real EDID: + +``` +intel_gmbus.c: + gmbus_setup() → configure GMBUS controller registers + gmbus_xfer() → perform I2C read/write transaction + → GMBUS0: select pin pair (DPC, DPE, etc.) + → GMBUS1: set byte count + slave addr (0xA0 for EDID) + → GMBUS2: status/clear + → GMBUS3: data read FIFO + → GMBUS4: interrupt mask + → GMBUS5: 2-byte index for block reads + do_gmbus_xfer() → hardware transaction with timeout + intel_gmbus_get_adapter() → returns I2C adapter per port + intel_gmbus_read_edid() → reads 128/256-byte EDID via I2C + +Register map (Gen9, Skylake): + GMBUS0 = 0xC5100 — pin pair select + rate + GMBUS1 = 0xC5104 — command: byte count, slave addr, direction + GMBUS2 = 0xC5108 — status: IN_USE, HW_WAIT, SMBALERT, SDAST (data valid) + GMBUS3 = 0xC510C — data FIFO (read 4 bytes at a time) + GMBUS4 = 0xC5110 — interrupt enable/mask + GMBUS5 = 0xC5120 — 2-byte index (for EDID block reads > 1) +``` + +### 2.5 DMC firmware programming + +Linux path: + +``` +intel_dmc.c: + intel_dmc_init() + → parse CSS header (first 32 bytes of firmware blob) + → find matching stepping (ucode version) + → write DMC payload to MMIO: + DMC_FW1_BASE(offset) = payload address low + DMC_FW1_BASE+4 = payload address high + DMC_MMIO_START(offset) = 0x10000 (SRAM base) + DMC_MMIO_END(offset) = 0x10000 + payload_size + → set DMC_CTRL = ENABLE bit + → wait for DMC_STATUS to indicate loaded + +Register map (Gen9): + DMC_MMIO_START = 0x6F000 + DMC_MMIO_END = 0x6F004 + DMC_FW_BASE = 0x6F008 (or per-variant: 0x6F038, 0x6F0B0) + DMC_CTRL = 0x6F064 + DMC_STATUS = 0x6F06C + DMC_SRAM_BASE = 0x10000 (SRAM offset inside MMIO space) +``` + +### 2.6 Display power wells + +Linux path: + +``` +intel_display_power.c: + intel_power_domains_init() + → register power well list per generation + → each well: domains bitmask, ops (enable/disable/is_enabled) + + SKL power wells (Gen9 example): + PW_1 (always on): domains = DISPLAY_CORE, PIPE_A + PW_2: domains = PIPE_B, PIPE_C, PIPE_D, AUX_* + PW_3: domains = DDI_*, TRANS_* + DDI_A_IO, DDI_B_IO, DDI_C_IO, DDI_D_IO, DDI_E_IO + AUX_A, AUX_B, AUX_C, AUX_D + + Each power well enable: + → write POWER_WELL_CTL register (0x45400 for SKL) + → wait for power well to become active (read-back verify) + → on Gen12: also enable DBUF slices via DBUF_CTL_S1/S2 +``` + +Without power wells enabled, DDI_BUF_CTL reads return 0 — connector detection appears to find nothing connected. + +## 3. Target Hardware Generations + +### 3.1 Generational strategy + +| Phase | Target gen | Display ver | Reason | +|-------|-----------|-------------|--------| +| Phase 0 | Gen9 (Skylake, Kaby Lake, Coffee Lake) | 9 | Simplest modern display engine. Widest installed base of laptops. Registers are mostly correct in current code. | +| Phase 1 | Gen9.5 (Ice Lake, Elkhart Lake) | 11 | Adds combo PHY, different DDI programming. Validates register abstraction. | +| Phase 2 | Gen12 (Tiger Lake, Alder Lake-P) | 12-13 | Major display engine rewrite. Separate transcoder from pipe. DBUF slices. | +| Phase 3 | Gen12.7 (Meteor Lake, Arrow Lake) | 14 | Newest i915-supported platform. Uses Xe-LP display. | +| Post-i915 | Battlemage, Lunar Lake, Panther Lake | 20-30 | NOT in i915. Uses Xe driver architecture (`drivers/gpu/drm/xe/`). Future work beyond this plan. | + +### 3.2 Gen9 (Skylake family) — first validation target + +**Why Gen9 first:** +- Most of our register offsets are already correct for Gen9 +- Largest installed base of Intel GPUs in 2024-2026 laptops and desktops +- Well-documented in Linux i915 with clear per-register comments +- No complex combo PHY or separate transcoder (pipe = transcoder) +- DMC firmware is small and well-understood (SKL: 42KB, KBL: 42KB, CFL: 40KB) +- GMBUS registers at standard offsets (`0xC5100`-`0xC5120`) + +**Device IDs for Gen9 validation:** + +| Platform | Substring | Example device IDs | +|----------|-----------|-------------------| +| Skylake DT GT2 | `0x1912`, `0x191B`, `0x191D`, `0x191E`, `0x1921` | Desktop HD 530 | +| Skylake ULT GT2 | `0x1916`, `0x1921`, `0x1923`, `0x1926`, `0x1927` | Laptop HD 520/530 | +| Skylake ULX GT2 | `0x191E` | HD 515 | +| Kaby Lake DT GT2 | `0x5912`, `0x5916`, `0x591B`, `0x591D` | Desktop HD 630 | +| Kaby Lake ULT GT2 | `0x5916`, `0x5921`, `0x5923`, `0x5926`, `0x5927` | Laptop HD 620/630 | +| Coffee Lake DT GT2 | `0x3E90`, `0x3E91`, `0x3E92`, `0x3E93`, `0x3E96`, `0x3E98`, `0x3E9A`, `0x3E9B` | Desktop UHD 630 | +| Coffee Lake ULT GT3 | `0x3EA5`, `0x3EA6`, `0x3EA7`, `0x3EA8` | Laptop Iris Plus | + +## 4. New File Structure + +Current: +``` +src/drivers/intel/ +├── mod.rs (693 lines) +├── display.rs (404 lines) +├── gtt.rs (226 lines) +└── ring.rs (267 lines) +``` + +Target: +``` +src/drivers/intel/ +├── mod.rs Driver struct, init, GpuDriver impl +├── display.rs Display pipe, modesetting, page flip +├── display_power.rs Power well enable/disable per generation [NEW] +├── display_cdclk.rs Core display clock programming [NEW] +├── display_dpll.rs Display PLL (pixel clock generation) [NEW] +├── display_dmc.rs DMC firmware upload to hardware [NEW] +├── gmbus.rs GMBUS I2C controller (EDID/DDC) [NEW] +├── dp_aux.rs DisplayPort AUX channel read/write [NEW] +├── dp_link.rs DP link training [NEW] +├── hdmi.rs HDMI infoframe programming [NEW] +├── hotplug.rs HPD interrupt handling [NEW] +├── cursor.rs Hardware cursor plane [NEW] +├── gtt.rs GGTT page table (existing, mostly complete) +├── ring.rs Command ring buffer (existing, mostly complete) +├── regs.rs Per-generation register tables [NEW] +├── regs_gen9.rs Gen9 (SKL/KBL/CFL) register offsets [NEW] +├── regs_gen11.rs Gen11 (ICL/EHL) register offsets [NEW] +├── regs_gen12.rs Gen12 (TGL/ADL/DG2/MTL) register offsets [NEW] +├── info.rs Device info, capability flags, display version [NEW] +└── vbt.rs Video BIOS Table parser [NEW] +``` + +## 5. Implementation Phases + +### Phase 0 — Display Foundation for Gen9 (4-6 weeks) + +**Goal:** Real EDID reading, DMC firmware programming, and display power well initialization on Gen9 (Skylake / Kaby Lake / Coffee Lake). First real Intel display output on hardware. + +**Exit criteria:** +- `read_edid_block()` returns real EDID from a physical monitor via GMBUS I2C +- DMC firmware is uploaded to hardware and status confirms loaded +- Display power wells are enabled before connector detection +- Connector detection reads real connection status (not synthetic) +- Mode list comes from monitor's EDID, not hardcoded 1080p +- CDCLK is programmed based on display bandwidth requirements +- Bounded modeset proof on real Gen9 hardware via `test-intel-gpu.sh` + +#### 0A: Device info and register abstraction (regs.rs, info.rs) + +**Create `info.rs`** — Device capability table: + +```rust +pub struct IntelDeviceInfo { + pub display_version: u8, + pub gt_version: u8, + pub num_pipes: u8, + pub num_ports: u8, + pub has_ddi: bool, + pub has_dp_aux: bool, + pub has_gmbus: bool, + pub has_dmc: bool, + pub has_combo_phy: bool, + pub has_dbuf_slice: bool, + pub has_transcoder: bool, // Gen12+: pipe != transcoder + pub dmc_fw_key: Option<&'static str>, +} +``` + +Populate from device ID → generation lookup. Key function: + +```rust +pub fn device_info_from_id(device_id: u16) -> IntelDeviceInfo +``` + +This replaces the ad-hoc `is_supported_intel_generation()` gate with a rich capability table. + +**Create `regs.rs`** — Register access abstraction: + +```rust +pub trait IntelRegs { + // Power wells + const POWER_WELL_CTL: usize; + + // Display clock + const CDCLK_CTL: usize; + + // DMC + const DMC_MMIO_START: usize; + const DMC_MMIO_END: usize; + const DMC_FW_BASE: usize; + const DMC_CTRL: usize; + const DMC_STATUS: usize; + + // GMBUS + const GMBUS0: usize; + const GMBUS1: usize; + const GMBUS2: usize; + const GMBUS3: usize; + const GMBUS4: usize; + const GMBUS5: usize; + + // Pipe / transcoder + const PIPECONF(n: u8) -> usize; + const HTOTAL(n: u8) -> usize; + // ... etc + + // Planes + const DSPCNTR(n: u8) -> usize; + const DSPSURF(n: u8) -> usize; + + // Cursor + const CURCNTR(n: u8) -> usize; + const CURPOS(n: u8) -> usize; + const CURBASE(n: u8) -> usize; + + // DDI + const DDI_BUF_CTL(port: u8) -> usize; + + // Forcewake + const FORCEWAKE_REQ: usize; + const FORCEWAKE_ACK: usize; +} +``` + +**Create `regs_gen9.rs`** — Gen9 register constants: + +All offsets from `local/reference/linux-7.1/drivers/gpu/drm/i915/display/intel_display_regs.h` for DISPLAY_VER 9. + +``` +GMBUS0 = 0xC5100 +GMBUS1 = 0xC5104 +GMBUS2 = 0xC5108 +GMBUS3 = 0xC510C +GMBUS4 = 0xC5110 +GMBUS5 = 0xC5120 + +POWER_WELL_CTL = 0x45400 + +CDCLK_CTL = 0x46000 + +DMC_MMIO_START = 0x6F000 +DMC_MMIO_END = 0x6F004 +DMC_FW1_BASE = 0x6F038 +DMC_FW2_BASE = 0x6F0B0 +DMC_CTRL = 0x6F064 +DMC_STATUS = 0x6F06C +DMC_SRAM_BASE = 0x10000 + +PIPECONF(A) = 0x70008 (pipe stride = 0x1000) +HTOTAL(A) = 0x60000 (transcoder stride = 0x1000) +DDI_BUF_CTL(A) = 0x64000 (port stride = 0x100) + +CURCNTR(A) = 0x70080 (pipe stride = 0x1000) +CURPOS(A) = 0x70084 +CURBASE(A) = 0x70088 + +FORCEWAKE_REQ = 0xA18C +FORCEWAKE_ACK = 0xA194 +``` + +**Linux reference files:** +- `i915/i915_reg.h` — master register definitions +- `i915/display/intel_display_regs.h` — display-specific registers +- `i915/display/intel_dmc_regs.h` — DMC registers +- `i915/display/intel_gmbus_regs.h` — GMBUS registers + +**Estimated effort:** ~500 lines Rust across 3 files. + +#### 0B: GMBUS / DDC I2C for real EDID (gmbus.rs) + +**Create `gmbus.rs`** — GMBUS I2C controller: + +Key operations to port from `intel_gmbus.c`: + +```rust +pub struct GmbusController { + mmio: Arc, + regs: &'static dyn IntelRegs, +} + +impl GmbusController { + /// Initialize GMBUS hardware + pub fn init(&self) -> Result<()>; + + /// Read bytes from I2C slave via GMBUS + pub fn read(&self, port: GmbusPort, slave_addr: u8, offset: u8, buf: &mut [u8]) -> Result<()>; + + /// Read full EDID block from monitor (128 or 256 bytes) + pub fn read_edid(&self, port: GmbusPort) -> Result>; +} + +pub enum GmbusPort { + DDC_B = 0x04, // Legacy VGA / DVI + DDC_C = 0x05, // HDMI port C + DDC_D = 0x06, // HDMI port D + // Gen9+ additional ports + DPC = 0x02, // DisplayPort C + DPE = 0x03, // DisplayPort E +} +``` + +Implementation sequence: + +1. **`init()`**: Reset GMBUS controller — clear `GMBUS2` status, set `GMBUS0` pin pair select. +2. **`read()`**: + - Write `GMBUS1`: byte count | (slave_addr << 1) | direction (read=1) + - Wait for `GMBUS2.SDAST` bit (data available in `GMBUS3`) + - Read 4 bytes at a time from `GMBUS3` FIFO + - For multi-byte offset reads (EDID block 1+): write `GMBUS5` with 2-byte index +3. **`read_edid()`**: Call `read()` with slave address `0xA0`, read 128 bytes. Parse byte 126 for extension count; if nonzero, read block 1 via `GMBUS5` index mode. +4. **Error handling**: Timeout after 1ms per byte. Clear `GMBUS2` errors. Hardware reset via `GMBUS0` GPIO mode if stuck. + +**Integration with display.rs:** +- Replace `read_edid_block()` stub with `GmbusController::read_edid()` call +- Port-to-GMBUS mapping: port 0→DPC, port 1→DPC, port 2→DPE, port 3→DPE (Gen9 SKL mapping from Linux) +- Fall back to synthetic EDID only if GMBUS read fails AND DDI_BUF_CTL reports connected + +**Linux reference files:** +- `display/intel_gmbus.c` (~560 lines) — full GMBUS implementation +- `display/intel_gmbus_regs.h` — register definitions + +**Estimated effort:** ~400-500 lines Rust. + +#### 0C: Display power wells (display_power.rs) + +**Create `display_power.rs`** — Power domain management: + +```rust +pub struct DisplayPower { + mmio: Arc, + regs: &'static dyn IntelRegs, +} + +impl DisplayPower { + /// Enable all power domains needed for display operation + pub fn init_domains(&self) -> Result<()>; + + /// Enable a specific power well + pub fn enable_well(&self, well: PowerWell) -> Result<()>; + + /// Check if a power well is active + pub fn is_well_enabled(&self, well: PowerWell) -> bool; + + /// Disable unused power wells (power saving) + pub fn disable_unused(&self, active_pipes: u8) -> Result<()>; +} + +pub enum PowerWell { + PW1, // Always-on domain: display core + PW2, // Pipe B/C/D, AUX channels + DDI_A, // DDI port A (eDP) + DDI_B, // DDI port B (HDMI) + DDI_C, // DDI port C (DP) + DDI_D, // DDI port D (DP) + DDI_E, // DDI port E (HDMI) + AUX_A, // DP AUX channel A + AUX_B, // DP AUX channel B + AUX_C, // DP AUX channel C + AUX_D, // DP AUX channel D +} +``` + +Implementation for Gen9 (SKL): +- `POWER_WELL_CTL` at `0x45400`: each power well is a bit in this register +- Enable: write `1 << well_bit` to `POWER_WELL_CTL`, then read back until the corresponding status bit is set +- Required wells before connector detection: PW1, PW2, all DDI wells, all AUX wells + +**Integration with mod.rs:** +- Call `display_power.init_domains()` in `IntelDriver::new()` before any connector detection +- GMBUS reads require AUX power wells enabled + +**Linux reference files:** +- `display/intel_display_power.c` (~1000 lines) +- `display/intel_display_power_well.c` (~500 lines) +- `display/intel_display_power_map.c` (~200 lines) + +**Estimated effort:** ~400 lines Rust (Gen9 only initially). + +#### 0D: DMC firmware programming (display_dmc.rs) + +**Create `display_dmc.rs`** — DMC firmware upload: + +```rust +pub struct DmcFirmware { + mmio: Arc, + regs: &'static dyn IntelRegs, +} + +impl DmcFirmware { + /// Upload DMC firmware blob to hardware + pub fn upload(&self, firmware: &[u8]) -> Result<()>; + + /// Check if DMC firmware is loaded and running + pub fn is_loaded(&self) -> bool; + + /// Get the firmware version running on hardware + pub fn firmware_version(&self) -> u32; +} +``` + +Implementation: +1. Parse firmware blob: first 32 bytes are CSS header. Extract payload offset, payload size, and ucode version. +2. Find matching stepping: compare firmware ucode version against hardware stepping (from `MCHBAR` or PCI revision). +3. Upload payload: + - Write `DMC_MMIO_START` = `0x10000` (SRAM base) + - Write `DMC_MMIO_END` = `0x10000 + payload_size` + - Write `DMC_FW_BASE` through `DMC_FW_BASE + payload_size` in 4-byte chunks (payload data) +4. Enable: write `DMC_CTRL` with enable bit +5. Wait: poll `DMC_STATUS` for loaded indication, timeout 50ms + +**Integration with mod.rs:** +- `IntelDriver::new()` already receives `firmware: HashMap>` — use the DMC key from the device info table +- Call `dmc.upload(&firmware[dmc_key])` after power wells but before connector detection +- DMC is optional for Gen8/Gen9 (display works without it but power management is broken), required for Gen12+ + +**Linux reference files:** +- `display/intel_dmc.c` (~600 lines) +- `display/intel_dmc_regs.h` (~60 lines) + +**Estimated effort:** ~300 lines Rust. + +#### 0E: Core display clock (display_cdclk.rs) + +**Create `display_cdclk.rs`** — CDCLK programming: + +```rust +pub struct CdclkState { + pub frequency: u32, // Hz + pub voltage_level: u32, // for SKL: 0=337.5MHz, 1=450MHz, 2=540MHz +} + +pub struct DisplayClock { + mmio: Arc, + regs: &'static dyn IntelRegs, +} + +impl DisplayClock { + /// Initialize CDCLK to minimum safe frequency + pub fn init(&self) -> Result; + + /// Set CDCLK frequency for required display bandwidth + pub fn set_frequency(&self, target: u32) -> Result; + + /// Calculate required CDCLK for a given set of display modes + pub fn required_cdclk(modes: &[ModeInfo]) -> u32; +} +``` + +For Gen9 (SKL): +- `CDCLK_CTL` at `0x46000` +- Supported frequencies: 337.5 MHz, 450 MHz, 540 MHz +- Single pipe at 1080p60 needs ~337.5 MHz (pixel rate 148.5 MHz < 337.5/2) +- Bump to 450 MHz for dual pipe or high-resolution modes +- `CDCLK_CTL` bit layout: bits 26:26 = CD_FREQ_SELECT (0=337.5, 1=450, 2=540) + +**Linux reference files:** +- `display/intel_cdclk.c` (~1000 lines) + +**Estimated effort:** ~300 lines Rust (Gen9 only). + +#### 0F: Connect Phase 0 into existing driver + +Modify existing files: + +| File | Change | +|------|--------| +| `mod.rs` | `IntelDriver::new()`: call power well init → DMC upload → CDCLK init before connector detection. Replace hardcoded register offsets with `regs` trait calls. | +| `display.rs` | Replace `read_edid_block()` stub with `GmbusController::read_edid()` call. Use `regs` trait for pipe/plane/DDI register access. Add fallback path: try GMBUS → try DP AUX → fall back to synthetic EDID. | +| `mod.rs` | Add `GmbusController`, `DisplayPower`, `DmcFirmware`, `DisplayClock` as struct fields. | + +**Estimated effort:** ~200 lines of modifications to existing files. + +### Phase 0 Summary + +| Sub-task | New file | Est. lines | Linux reference | Depends on | +|----------|----------|-----------|-----------------|------------| +| 0A | `info.rs`, `regs.rs`, `regs_gen9.rs` | ~500 | `intel_display_regs.h`, `intel_device_info.h` | None | +| 0B | `gmbus.rs` | ~500 | `intel_gmbus.c` | 0A (registers) | +| 0C | `display_power.rs` | ~400 | `intel_display_power.c` | 0A (registers) | +| 0D | `display_dmc.rs` | ~300 | `intel_dmc.c` | 0A (registers) | +| 0E | `display_cdclk.rs` | ~300 | `intel_cdclk.c` | 0A (registers), 0C (power wells) | +| 0F | Modify `mod.rs`, `display.rs` | ~200 | — | All above | +| **Total** | **10 files** | **~2,200** | | | + +**Validation gate:** +```bash +# Must pass on real Gen9 hardware (Skylake/Kaby Lake/Coffee Lake): +./local/scripts/test-intel-gpu.sh + +# In-guest checks: +# 1. connector enumeration returns real connectors (not empty or synthetic) +# 2. EDID is read from physical monitor (not synthetic 1080p) +# 3. mode list contains monitor's native resolution +# 4. bounded modeset produces visible output +# 5. DMC firmware status shows loaded +# 6. power wells show enabled +``` + +### Phase 1 — DisplayPort and HDMI (4-6 weeks) + +**Goal:** Full DP and HDMI output with proper link training, DPCD reads, and infoframe programming. Multi-monitor support. + +**Exit criteria:** +- DP link training succeeds and DP connector shows real modes +- HDMI AVI infoframes are programmed with correct VIC +- DPCD reads return real DP capabilities +- Hotplug detection works via HPD interrupts (not polling) +- Dual-monitor setup works with independent modes per pipe + +#### 1A: DP AUX channel (dp_aux.rs) + +**Create `dp_aux.rs`** — DisplayPort AUX channel I/O: + +```rust +pub struct DpAux { + mmio: Arc, + regs: &'static dyn IntelRegs, +} + +impl DpAux { + /// Read bytes from DP AUX channel + pub fn read(&self, port: u8, offset: u32, buf: &mut [u8]) -> Result<()>; + + /// Write bytes to DP AUX channel + pub fn write(&self, port: u8, offset: u32, data: &[u8]) -> Result<()>; + + /// Read full DPCD capability block (first 16 bytes) + pub fn read_dpcd_caps(&self, port: u8) -> Result; + + /// Read EDID via DP AUX (I2C-over-AUX) + pub fn read_edid(&self, port: u8) -> Result>; +} +``` + +AUX register map (Gen9): +``` +DP_AUX_CTL(port) = 0x64010 + port * 0x100 +DP_AUX_DATA(port) = 0x64014 + port * 0x100 (5 registers for 20-byte message) +``` + +**Linux reference:** `display/intel_dp_aux.c` (~500 lines) +**Estimated effort:** ~500 lines Rust. + +#### 1B: DP link training (dp_link.rs) + +**Create `dp_link.rs`** — DP link establishment: + +```rust +pub struct DpLink { + aux: DpAux, + mmio: Arc, +} + +impl DpLink { + /// Train the DP link: find max lane count, set link rate, train + pub fn train(&self, port: u8) -> Result; + + /// Read current link status from DPCD + pub fn read_status(&self, port: u8) -> Result; +} + +pub struct DpLinkConfig { + pub lane_count: u8, // 1, 2, or 4 + pub link_rate: u32, // 162000, 270000, 540000, 810000 (kHz) + pub spread_spectrum: bool, +} +``` + +**Linux reference:** `display/intel_dp_link_training.c` (~600 lines) +**Estimated effort:** ~600 lines Rust. + +#### 1C: HDMI infoframes (hdmi.rs) + +**Create `hdmi.rs`** — HDMI AVI/audio infoframes: + +```rust +pub struct HdmiInfoframes; + +impl HdmiInfoframes { + /// Program AVI infoframe for a given mode + pub fn program_avi(mmio: &MmioRegion, pipe: u8, mode: &ModeInfo) -> Result<()>; + + /// Program audio infoframe (basic stereo) + pub fn program_audio(mmio: &MmioRegion, pipe: u8) -> Result<()>; + + /// Compute AVI infoframe checksum + pub fn checksum(data: &[u8; 17]) -> u8; +} +``` + +Register map (Gen9): +``` +HSW_TVIDEO_DIP_CTL(pipe) = 0x61180 + pipe * 0x1000 +HSW_TVIDEO_DIP_AVI_DATA(pipe) = 0x61184 + pipe * 0x1000 (5 regs, 4 bytes each) +``` + +**Linux reference:** `display/intel_hdmi.c` (infoframe helpers, ~400 lines relevant) +**Estimated effort:** ~300 lines Rust. + +#### 1D: Hotplug interrupt handling (hotplug.rs) + +**Create `hotplug.rs`** — HPD detection: + +```rust +pub struct HotplugHandler { + mmio: Arc, +} + +impl HotplugHandler { + /// Initialize HPD interrupt mask + pub fn init(&self) -> Result<()>; + + /// Check which ports have pending HPD events + pub fn check_events(&self) -> Vec; + + /// Acknowledge HPD events + pub fn ack(&self, events: &[HotplugEvent]); + + /// Distinguish short pulse (EDID change) from long pulse (connect/disconnect) + pub fn classify_pulse(&self, port: u8) -> HotplugPulse; +} + +pub struct HotplugEvent { + pub port: u8, + pub pulse_type: HotplugPulse, +} + +pub enum HotplugPulse { + Long, // Connect or disconnect + Short, // EDID change or link training request +} +``` + +Register map (Gen9): +``` +SDEISR = 0xC4000 — south display interrupt status +SDEIMR = 0xC4004 — south display interrupt mask +SDEIIR = 0xC4008 — south display interrupt identity +SHOTPLUG_CTL = 0xC4030 — hotplug control +``` + +**Linux reference:** `display/intel_hotplug.c` + `display/intel_hotplug_irq.c` (~600 lines) +**Estimated effort:** ~400 lines Rust. + +#### 1E: Display PLL (display_dpll.rs) + +**Create `display_dpll.rs`** — Pixel clock generation: + +```rust +pub struct DisplayPll { + mmio: Arc, +} + +impl DisplayPll { + /// Initialize shared DPLL pool + pub fn init(&self) -> Result<()>; + + /// Find or allocate a PLL for the given pixel clock + pub fn get_pll_for_clock(&self, pixel_clock_khz: u32) -> Result; + + /// Enable a DPLL and wait for lock + pub fn enable(&self, config: &DpllConfig) -> Result<()>; + + /// Wire a DPLL to a specific CRTC/port + pub fn wire_to_crtc(&self, dpll: u8, pipe: u8) -> Result<()>; +} + +pub struct DpllConfig { + pub id: u8, + pub frequency_khz: u32, + pub pdiv: u32, + pub kdiv: u32, + pub qdiv_mode: u32, +} +``` + +Register map (Gen9 SKL): +``` +LCPLL1_CTL = 0x46010 — LC PLL 1 control +LCPLL2_CTL = 0x46014 — LC PLL 2 control +DPLL0_CTL = 0x46200 — DPLL 0 control +DPLL1_CTL = 0x46204 — DPLL 1 control +DPLL2_CTL = 0x46208 — DPLL 2 control +SKL_DPLL_CTRL1 = 0x46204 — DPLL configuration +``` + +**Linux reference:** `display/intel_dpll_mgr.c` (~2000 lines, Gen9 portion ~500 lines) +**Estimated effort:** ~500 lines Rust (Gen9 SKL DPLLs only). + +### Phase 1 Summary + +| Sub-task | New file | Est. lines | Linux reference | +|----------|----------|-----------|-----------------| +| 1A | `dp_aux.rs` | ~500 | `intel_dp_aux.c` | +| 1B | `dp_link.rs` | ~600 | `intel_dp_link_training.c` | +| 1C | `hdmi.rs` | ~300 | `intel_hdmi.c` | +| 1D | `hotplug.rs` | ~400 | `intel_hotplug.c` | +| 1E | `display_dpll.rs` | ~500 | `intel_dpll_mgr.c` | +| **Total** | **5 files** | **~2,300** | | + +### Phase 2 — Gen12 Display Engine (6-10 weeks) + +**Goal:** Tiger Lake / Alder Lake-P support with Gen12 display engine registers. + +**Exit criteria:** +- Gen12 device IDs produce correct register accesses +- Separate pipe/transcoder programming works +- DBUF slice allocation is correct +- Combo PHY initialization for Ice Lake / Tiger Lake +- Display on real Tiger Lake or Alder Lake hardware + +#### 2A: Gen12 register tables (regs_gen12.rs) + +Major differences from Gen9: + +| Feature | Gen9 | Gen12 | +|---------|------|-------| +| Pipe / transcoder | Same thing | Separate: `PIPECONF` controls pipe, `TRANS_DDI_FUNC_CTL` controls transcoder | +| Plane programming | `DSPCNTR`/`DSPSURF` | `PLANE_CTL`/`PLANE_SURF`/`PLANE_STRIDE`/`PLANE_OFFSET` | +| DBUF | Single DBUF | DBUF slices (S1, S2) must be enabled per pipe | +| Combo PHY | N/A | ICL/TGL: `ICL_PORT_CL1K_DW0` through `ICL_PORT_CL1K_DW30` for each PHY | +| DMC | Single payload | Multiple payloads (DC3DC5, DC3DC6, etc.) | +| CDCLK | 3 fixed frequencies | Bounded range with `BXT_DE_PLL_CTL` | +| DDI | 6 ports max | Up to 8 DDI ports, some are combo (TC) | +| AUX | Per-DDI | Per-DDI but TC ports use `TC_AUX` registers | + +**Create `regs_gen12.rs`** with all Gen12 display register offsets from `intel_display_regs.h` for DISPLAY_VER 12-13. + +**Estimated effort:** ~800 lines (large register table). + +#### 2B: Combo PHY initialization + +For Tiger Lake and Ice Lake, the combo PHYs must be initialized before DDI programming. Port from `display/intel_combo_phy.c`. + +**Estimated effort:** ~400 lines. + +#### 2C: DBUF slice management + +Gen12+ requires display buffer (DBUF) slices to be allocated per pipe. Port from `display/intel_dbuf.c`. + +**Estimated effort:** ~300 lines. + +#### 2D: Transcoder programming + +Gen12 separates pipe from transcoder. New `TRANS_DDI_FUNC_CTL`, `TRANS_HTOTAL`, etc. Port from `display/intel_ddi.c` (transcoder path). + +**Estimated effort:** ~500 lines of changes to `display.rs`. + +### Phase 2 Summary + +| Sub-task | Est. lines | Linux reference | +|----------|-----------|-----------------| +| 2A Gen12 regs | ~800 | `intel_display_regs.h` (DISPLAY_VER 12) | +| 2B Combo PHY | ~400 | `intel_combo_phy.c` | +| 2C DBUF slices | ~300 | `intel_dbuf.c` | +| 2D Transcoder | ~500 | `intel_ddi.c` | +| **Total** | **~2,000** | | + +### Phase 3 — Full KMS Features (4-6 weeks) + +**Goal:** Cursor planes, atomic modesetting, watermarks, backlight. What a compositor needs. + +| Task | New file | Est. lines | Linux reference | +|------|----------|-----------|-----------------| +| Cursor plane | `cursor.rs` | ~200 | `intel_cursor.c` | +| Atomic modeset | Changes to `scheme.rs`, `display.rs` | ~600 | `intel_atomic.c` | +| Watermarks | `display_watermark.rs` | ~500 | `skl_watermark.c` | +| Backlight | `display_backlight.rs` | ~200 | `intel_backlight.c` | +| VBT parser | `vbt.rs` | ~500 | `intel_bios.c` | +| **Total** | **4 new files** | **~2,000** | | + +### Phase 4 — Render Path (12-20 weeks, parallel with Phase 2-3) + +**Goal:** GPU command submission for rendering. Mesa hardware renderer path. + +This phase is architecturally different from display and can proceed in parallel. + +| Task | File | Est. lines | Linux reference | +|------|------|-----------|-----------------| +| Per-gen ring context init | `ring.rs` expansion | ~400 | `intel_lrc.c`, `gen8_engine_cs.c` | +| Execlists submission | `execlists.rs` (new) | ~800 | `intel_execlists_submission.c` | +| Batch buffer protocol | `batch.rs` (new) | ~300 | `gem/i915_gem_execbuffer.c` | +| Fence objects | `fence.rs` (new) | ~400 | `i915_sw_fence.c`, `intel_timeline.c` | +| Mesa winsys | Changes to `scheme.rs` | ~600 | Mesa Redox winsys | +| **Total** | **3 new files** | **~2,500** | | + +### Phase 5 — Power and Optimization (8-12 weeks) + +| Task | Est. lines | Linux reference | +|------|-----------|-----------------| +| PSR (Panel Self Refresh) | ~400 | `intel_psr.c` | +| FBC (Frame Buffer Compression) | ~300 | `intel_fbc.c` | +| GT frequency / RPS / RC6 | ~400 | `intel_rps.c`, `intel_rc6.c` | +| GuC firmware loading | ~600 | `gt/uc/intel_guc.c`, `intel_guc_fw.c` | +| GuC submission | ~1000 | `gt/uc/intel_guc_submission.c` | +| **Total** | **~2,700** | | + +## 6. Effort Summary + +| Phase | Duration | New lines | New files | Blocks on | +|-------|----------|-----------|-----------|-----------| +| 0: Display Foundation (Gen9) | 4-6 weeks | ~2,200 | 10 | Nothing | +| 1: DP/HDMI | 4-6 weeks | ~2,300 | 5 | Phase 0 | +| 2: Gen12 Display | 6-10 weeks | ~2,000 | 2 + modifications | Phase 0 | +| 3: Full KMS | 4-6 weeks | ~2,000 | 4 | Phase 1 | +| 4: Render Path | 12-20 weeks | ~2,500 | 3 | Phase 0 (parallel with 1-3) | +| 5: Power/Optimize | 8-12 weeks | ~2,700 | 0+ | Phase 2 | +| **Total** | **38-60 weeks** | **~13,700** | **~24 new files** | | + +The critical path to first Intel display output on real hardware is **Phase 0** at 4-6 weeks. + +## 7. Validation Strategy + +### Per-phase validation + +| Phase | Validation tool | Success criteria | +|-------|----------------|-----------------| +| 0 | `test-intel-gpu.sh` on Gen9 hardware | Real EDID read, real modes, visible output | +| 1 | DP/HDMI cable hotplug test | DP link training succeeds, HDMI infoframes valid | +| 2 | TGL/ADL-P hardware test | Gen12 modeset with separate transcoder | +| 3 | KWin compositor test | Cursor visible, atomic modeset works | +| 4 | Mesa `glxgears` test | Hardware-rendered output via Intel Mesa driver | +| 5 | Battery life comparison | PSR/FBC measurably reduces power consumption | + +### QEMU validation (bounded, not sufficient for claims) + +QEMU with `qemu64` CPU does not emulate Intel GPU. For Intel DRM testing in QEMU: +- Use `-device virtio-gpu` for shared DRM core validation only +- Real Intel validation requires bare metal or Intel GVT-g passthrough + +### Hardware test matrix + +| Hardware | Gen | Phase 0 | Phase 1 | Phase 2 | Phase 3 | +|----------|-----|---------|---------|---------|---------| +| Skylake HD 530 desktop | 9 | Target | Target | N/A | Target | +| Kaby Lake HD 620 laptop | 9 | Target | Target | N/A | Target | +| Coffee Lake UHD 630 | 9 | Target | Target | N/A | Target | +| Ice Lake Iris Plus | 11 | Secondary | Secondary | Target | Secondary | +| Tiger Lake Xe | 12 | N/A | N/A | Target | Target | +| Alder Lake-P Iris Xe | 12 | N/A | N/A | Target | Target | + +## 8. Anti-Patterns and Constraints + +### Code quality rules + +1. **No stubs.** Every new function must have real register-level implementation. No `todo!()`, no hardcoded return values, no synthetic data when hardware can provide real data. +2. **No `as any` or type suppression.** All MMIO accesses are typed through the register abstraction layer. +3. **No `unwrap()` in new code.** All fallible operations return `Result`. +4. **Bounds-checked MMIO.** Every `read_volatile` / `write_volatile` must be preceded by a bounds check against the MMIO region size. +5. **Per-gen registers.** No hardcoded offsets in implementation code. All offsets come from `regs.rs` trait or `regs_gen*.rs` constants. + +### Porting rules + +1. **Use Linux as reference only.** Port the behavior, not the C code structure. Rust idioms (Result, enums, traits) replace C idioms (error codes, macros, function pointers). +2. **Comment Linux source references.** Every major function should have a comment like `// Ported from intel_gmbus.c:do_gmbus_xfer()` +3. **Test incrementally.** Each sub-task (0A, 0B, etc.) must compile and be testable independently before integration. +4. **Generation-gate everything.** If a register offset differs per generation, it goes in the register table, not as an if/else chain. + +### Patch discipline + +All changes to `redox-drm` source go as P-patches: +- `P11-gmbus-i2c-edid.patch` — Phase 0B +- `P12-display-power-wells.patch` — Phase 0C +- `P13-dmc-firmware-upload.patch` — Phase 0D +- `P14-cdclk-programming.patch` — Phase 0E +- `P15-register-abstraction.patch` — Phase 0A +- etc. + +Each patch must: +1. Be generated via `git diff -U0 -w` from committed source +2. Be validated with `repo validate-patches redox-drm` +3. Build with `CI=1 ./target/release/repo cook redox-drm` + +## 9. Dependencies on Other Red Bear Subsystems + +| Dependency | Status | Impact on Intel work | +|-----------|--------|---------------------| +| `redox-driver-sys` | Build-visible | PCI, memory, IRQ access — sufficient for Phase 0 | +| `linux-kpi` | Build-visible | Not used by Intel Rust driver (not needed — Intel backend is pure Rust) | +| `firmware-loader` | Working daemon | DMC firmware blobs served via `scheme:firmware` — ready | +| `libdrm` | Build-visible with Redox patches | Render node support needed for Phase 4 | +| `Mesa` | Software only (swrast) | Hardware Intel driver needed for Phase 4 — large separate effort | +| `pcid` / `pcid-spawner` | Working | Intel GPU device detection and driver launch — working | +| Kernel `scheme:memory` | Working | BAR mapping and DMA buffer allocation — sufficient | +| Kernel `scheme:irq` | Working | MSI/MSI-X delivery — sufficient | +| Kernel `scheme:proc` | Working | Process management — sufficient | + +## 10. Out of Scope + +The following are explicitly NOT part of this plan: + +- **Battlemage / XE2 / Xe driver** — Linux i915 does not support these platforms. They use the separate `drivers/gpu/drm/xe/` driver. Future work requiring a Xe driver port or abstraction. +- **Lunar Lake, Panther Lake** — Same as above. Xe driver territory. +- **GPU virtualization (GVT-g)** — Not needed for bare-metal desktop. +- **Gen2-Gen7 support** — Legacy hardware. The driver gate correctly blocks these. +- **HDMI/DP audio** — Deferred. Display-only first. +- **Content protection (HDCP, PXP)** — Deferred indefinitely. +- **DSC (Display Stream Compression)** — Deferred to Phase 5+. +- **VRR (Variable Refresh Rate)** — Deferred to Phase 5+.