intel: Xe2/Arrow Lake support — regs_xe2.rs, device IDs, generation selection

Add Arrow Lake-P Arc Pro 130T/140T (0x7d51) and other Xe2 device IDs.
Create Xe2 register table with correct forcewake (0xa188/0xdfc),
DMC (0x80000 base), and DBUF/D2D registers.

- info.rs: add GenXe2 generation (display ver 20, gt ver 20),
  4 Arrow Lake device IDs, update generation gating for Xe2
  (has_combo_phy, has_dbuf_slice, has_separate_transcoder = true;
  has_gmbus = false — Xe2 uses DP AUX for EDID)
- regs_xe2.rs: Xe2Regs implementing IntelRegs trait with Xe2-specific
  forcewake, DMC offsets. Xe2LpdRegs struct for Xe2LPD display
  registers (DE_CAP, DFSM, DBUF_CTL, D2D_LINK_CTL)
- mod.rs: dynamic register table selection based on generation
  (GenXe2 → Xe2Regs, default → Gen9Regs). GMBUS controller moved
  to Option — initialized only for non-Xe2 platforms.
  Import IntelGeneration for generation dispatch.

Linux reference: xe_pci.c (INTEL_ARL_IDS), xe_gt_regs.h,
  intel_display_regs.h (XE2LPD_* defines)

Compiled: library modules clean (pre-existing daemon errors unrelated)
This commit is contained in:
2026-05-30 07:22:48 +03:00
parent fd773c46d9
commit f07fd649af
3 changed files with 164 additions and 17 deletions
@@ -11,6 +11,7 @@ pub enum IntelGeneration {
Gen9_5,
Gen12,
Gen12_7,
GenXe2,
Unknown,
}
@@ -26,6 +27,7 @@ impl IntelGeneration {
Self::Gen9_5 => 11,
Self::Gen12 => 12,
Self::Gen12_7 => 14,
Self::GenXe2 => 20,
Self::Unknown => 0,
}
}
@@ -41,6 +43,7 @@ impl IntelGeneration {
Self::Gen9_5 => 9,
Self::Gen12 => 12,
Self::Gen12_7 => 12,
Self::GenXe2 => 20,
Self::Unknown => 0,
}
}
@@ -50,6 +53,7 @@ impl IntelGeneration {
Self::Gen4 | Self::Gen5 => 2,
Self::Gen6 | Self::Gen7 | Self::Gen8 => 3,
Self::Gen9 | Self::Gen9_5 | Self::Gen12 | Self::Gen12_7 => 4,
Self::GenXe2 => 4,
Self::Unknown => 1,
}
}
@@ -81,11 +85,12 @@ impl IntelDeviceInfo {
| IntelGeneration::Gen9_5
| IntelGeneration::Gen12
| IntelGeneration::Gen12_7
| IntelGeneration::GenXe2
)
}
pub fn is_gen12_or_later(&self) -> bool {
matches!(self.generation, IntelGeneration::Gen12 | IntelGeneration::Gen12_7)
matches!(self.generation, IntelGeneration::Gen12 | IntelGeneration::Gen12_7 | IntelGeneration::GenXe2)
}
}
@@ -143,6 +148,10 @@ const DEVICE_ID_TABLE: &[DeviceIdEntry] = &[
DeviceIdEntry { device_id: 0x7D60, gen: IntelGeneration::Gen12_7, platform_name: "Meteor Lake", dmc_fw_key: Some("MTL") },
DeviceIdEntry { device_id: 0x7D45, gen: IntelGeneration::Gen12_7, platform_name: "Meteor Lake", dmc_fw_key: Some("MTL") },
DeviceIdEntry { device_id: 0x7D67, gen: IntelGeneration::Gen12_7, platform_name: "Meteor Lake", dmc_fw_key: Some("MTL") },
DeviceIdEntry { device_id: 0x7D41, gen: IntelGeneration::GenXe2, platform_name: "Arrow Lake-U", dmc_fw_key: Some("ARL") },
DeviceIdEntry { device_id: 0x7D51, gen: IntelGeneration::GenXe2, platform_name: "Arrow Lake-P Arc Pro 130T/140T", dmc_fw_key: Some("ARL") },
DeviceIdEntry { device_id: 0x7DD1, gen: IntelGeneration::GenXe2, platform_name: "Arrow Lake-P", dmc_fw_key: Some("ARL") },
DeviceIdEntry { device_id: 0xB640, gen: IntelGeneration::GenXe2, platform_name: "Arrow Lake-H", dmc_fw_key: Some("ARL") },
];
pub fn device_info_from_id(device_id: u16) -> IntelDeviceInfo {
@@ -154,14 +163,14 @@ pub fn device_info_from_id(device_id: u16) -> IntelDeviceInfo {
display_version: gen.display_version(),
gt_version: gen.gt_version(),
num_pipes: gen.num_pipes(),
num_ports: if gen == IntelGeneration::Gen12 || gen == IntelGeneration::Gen12_7 { 6 } else { 4 },
has_ddi: matches!(gen, IntelGeneration::Gen9 | IntelGeneration::Gen9_5 | IntelGeneration::Gen12 | IntelGeneration::Gen12_7),
has_dp_aux: matches!(gen, IntelGeneration::Gen9 | IntelGeneration::Gen9_5 | IntelGeneration::Gen12 | IntelGeneration::Gen12_7),
has_gmbus: matches!(gen, IntelGeneration::Gen9 | IntelGeneration::Gen9_5 | IntelGeneration::Gen12 | IntelGeneration::Gen12_7),
has_dmc: matches!(gen, IntelGeneration::Gen9 | IntelGeneration::Gen9_5 | IntelGeneration::Gen12 | IntelGeneration::Gen12_7),
has_combo_phy: matches!(gen, IntelGeneration::Gen9_5 | IntelGeneration::Gen12 | IntelGeneration::Gen12_7),
has_dbuf_slice: gen == IntelGeneration::Gen12 || gen == IntelGeneration::Gen12_7,
has_separate_transcoder: gen == IntelGeneration::Gen12 || gen == IntelGeneration::Gen12_7,
num_ports: if gen == IntelGeneration::Gen12 || gen == IntelGeneration::Gen12_7 || gen == IntelGeneration::GenXe2 { 6 } else { 4 },
has_ddi: matches!(gen, IntelGeneration::Gen9 | IntelGeneration::Gen9_5 | IntelGeneration::Gen12 | IntelGeneration::Gen12_7 | IntelGeneration::GenXe2),
has_dp_aux: matches!(gen, IntelGeneration::Gen9 | IntelGeneration::Gen9_5 | IntelGeneration::Gen12 | IntelGeneration::Gen12_7 | IntelGeneration::GenXe2),
has_gmbus: matches!(gen, IntelGeneration::Gen9 | IntelGeneration::Gen9_5),
has_dmc: matches!(gen, IntelGeneration::Gen9 | IntelGeneration::Gen9_5 | IntelGeneration::Gen12 | IntelGeneration::Gen12_7 | IntelGeneration::GenXe2),
has_combo_phy: matches!(gen, IntelGeneration::Gen9_5 | IntelGeneration::Gen12 | IntelGeneration::Gen12_7 | IntelGeneration::GenXe2),
has_dbuf_slice: gen == IntelGeneration::Gen12 || gen == IntelGeneration::Gen12_7 || gen == IntelGeneration::GenXe2,
has_separate_transcoder: gen == IntelGeneration::Gen12 || gen == IntelGeneration::Gen12_7 || gen == IntelGeneration::GenXe2,
dmc_fw_key: entry.dmc_fw_key,
platform_name: entry.platform_name,
};
@@ -7,6 +7,7 @@ pub mod gtt;
pub mod info;
pub mod regs;
pub mod regs_gen9;
pub mod regs_xe2;
pub mod ring;
use std::collections::HashMap;
@@ -31,9 +32,10 @@ use self::display_dmc::DmcFirmware;
use self::display_power::DisplayPower;
use self::gmbus::GmbusController;
use self::gtt::IntelGtt;
use self::info::{IntelDeviceInfo, device_info_from_id};
use self::info::{IntelDeviceInfo, IntelGeneration, device_info_from_id};
use self::regs::IntelRegs;
use self::regs_gen9::Gen9Regs;
use self::regs_xe2::Xe2Regs;
use self::ring::{IntelRing, RingType};
const FORCEWAKE: usize = 0xA18C;
@@ -54,7 +56,7 @@ pub struct IntelDriver {
info: PciDeviceInfo,
device_info: IntelDeviceInfo,
mmio: Arc<MmioRegion>,
regs: &'static Gen9Regs,
regs: &'static dyn IntelRegs,
irq_handle: Mutex<Option<InterruptHandle>>,
display: IntelDisplay,
gem: Mutex<GemManager>,
@@ -63,7 +65,7 @@ pub struct IntelDriver {
encoders: Mutex<Vec<Encoder>>,
gtt: Mutex<IntelGtt>,
ring: Mutex<IntelRing>,
gmbus: GmbusController,
gmbus: Option<GmbusController>,
display_power: DisplayPower,
dmc: DmcFirmware,
cdclk: DisplayClock,
@@ -119,16 +121,25 @@ impl IntelDriver {
let device_info = device_info_from_id(info.device_id);
info!(
"redox-drm: Intel {} detected (device {:#06x}, display ver {})",
device_info.platform_name, info.device_id, device_info.display_version
"redox-drm: Intel {} detected (device {:#06x}, display ver {}, gen {:?})",
device_info.platform_name, info.device_id, device_info.display_version, device_info.generation
);
let regs = &Gen9Regs;
let regs: &'static dyn IntelRegs = match device_info.generation {
IntelGeneration::GenXe2 => &Xe2Regs,
_ => &Gen9Regs,
};
let mmio_arc = Arc::new(mmio);
let display_mmio_arc = Arc::new(display_mmio);
let gmbus = GmbusController::new(display_mmio_arc.clone(), regs);
gmbus.init()?;
let gmbus = if device_info.has_gmbus {
let ctrl = GmbusController::new(display_mmio_arc.clone(), regs);
ctrl.init()?;
Some(ctrl)
} else {
info!("redox-drm-intel: Xe2 platform — skipping GMBUS (uses DP AUX for EDID)");
None
};
let display_power = DisplayPower::new(mmio_arc.clone(), regs);
display_power.init_domains()?;
@@ -0,0 +1,127 @@
use super::regs::IntelRegs;
pub struct Xe2Regs;
impl IntelRegs for Xe2Regs {
fn forcewake_req(&self) -> usize { 0xa188 }
fn forcewake_ack(&self) -> usize { 0xdfc }
fn power_well_ctl(&self) -> usize { 0x45400 }
fn cdclk_ctl(&self) -> usize { 0x46000 }
fn dmc_mmio_start(&self) -> usize { 0x80000 }
fn dmc_mmio_end(&self) -> usize { 0x8FFFF }
fn dmc_fw_base(&self) -> usize { 0x80008 }
fn dmc_ctrl(&self) -> usize { 0x80020 }
fn dmc_status(&self) -> usize { 0x80024 }
fn dmc_sram_base(&self) -> usize { 0x10000 }
fn gmbus0(&self) -> usize { 0xC5100 }
fn gmbus1(&self) -> usize { 0xC5104 }
fn gmbus2(&self) -> usize { 0xC5108 }
fn gmbus3(&self) -> usize { 0xC510C }
fn gmbus4(&self) -> usize { 0xC5110 }
fn gmbus5(&self) -> usize { 0xC5120 }
fn pipeconf(&self, pipe: u8) -> usize {
0x70008 + (pipe as usize) * 0x1000
}
fn pipeconf_enable_mask(&self) -> u32 { 1 << 31 }
fn htotal(&self, pipe: u8) -> usize {
0x60000 + (pipe as usize) * 0x1000
}
fn hblank(&self, pipe: u8) -> usize {
0x60004 + (pipe as usize) * 0x1000
}
fn hsync(&self, pipe: u8) -> usize {
0x60008 + (pipe as usize) * 0x1000
}
fn vtotal(&self, pipe: u8) -> usize {
0x6000C + (pipe as usize) * 0x1000
}
fn vblank(&self, pipe: u8) -> usize {
0x60010 + (pipe as usize) * 0x1000
}
fn vsync(&self, pipe: u8) -> usize {
0x60014 + (pipe as usize) * 0x1000
}
fn pipe_src(&self, pipe: u8) -> usize {
0x6001C + (pipe as usize) * 0x1000
}
fn pipe_stride(&self) -> usize { 0x1000 }
fn dspcntr(&self, pipe: u8) -> usize {
0x70180 + (pipe as usize) * 0x1000
}
fn dspcntr_enable_mask(&self) -> u32 { 1 << 31 }
fn dspsurf(&self, pipe: u8) -> usize {
0x7019C + (pipe as usize) * 0x1000
}
fn plane_size(&self, pipe: u8) -> usize {
0x70190 + (pipe as usize) * 0x1000
}
fn ddi_buf_ctl(&self, port: u8) -> usize {
0x64000 + (port as usize) * 0x100
}
fn ddi_port_stride(&self) -> usize { 0x100 }
fn curcntr(&self, pipe: u8) -> usize {
0x70080 + (pipe as usize) * 0x1000
}
fn curpos(&self, pipe: u8) -> usize {
0x70084 + (pipe as usize) * 0x1000
}
fn curbase(&self, pipe: u8) -> usize {
0x70088 + (pipe as usize) * 0x1000
}
fn pipeframe_reg(&self, pipe: u8) -> usize {
0x70040 + (pipe as usize) * 0x1000
}
fn pipeframe_count_mask(&self) -> u32 { 0x00FFFFFF }
fn gfx_flsh_cntl(&self) -> usize { 0x101008 }
fn pp_status(&self) -> usize { 0xC7200 }
}
pub struct Xe2LpdRegs {
pub de_cap: usize,
pub dfsm: usize,
pub dbuf_ctl: usize,
pub d2d_link_ctl: usize,
pub aux_ctl: usize,
}
impl Xe2LpdRegs {
pub const fn new() -> Self {
Self {
de_cap: 0x41100,
dfsm: 0x51000,
dbuf_ctl: 0x45008,
d2d_link_ctl: 0x64200,
aux_ctl: 0x64010,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_xe2_forcewake() {
let regs = Xe2Regs;
assert_eq!(regs.forcewake_req(), 0xa188);
assert_ne!(regs.forcewake_req(), 0xA18C);
}
#[test]
fn test_xe2_dmc() {
let regs = Xe2Regs;
assert_eq!(regs.dmc_mmio_start(), 0x80000);
}
}