intel: Phase 0 — display foundation for Gen9
Add register abstraction and hardware initialization modules for Intel GPU display support (Skylake/Kaby Lake/Coffee Lake). New modules: - info.rs: device capability table with 44 device IDs from Gen9—Gen12.7 IntelDeviceInfo struct with generation, display version, pipe/port counts, DMC firmware key lookup - regs.rs: IntelRegs trait — per-generation register access abstraction covering forcewake, power wells, CDCLK, DMC, GMBUS, pipes, planes, DDIs, cursors, vblank, and GFX flush - regs_gen9.rs: Gen9 (SKL/KBL/CFL) register constants implementing IntelRegs trait — verified against Linux i915 intel_display_regs.h and intel_gmbus_regs.h - gmbus.rs: GMBUS I2C controller for real EDID reads via hardware Ported from Linux intel_gmbus.c — implements init, read, read_edid with pin pair selection, hardware ready polling, SDAST FIFO reads, NAK detection, and 50ms timeout - display_power.rs: Gen9 display power well initialization Enables all display-required power domains (PW1, PW2, DDI_A-E, AUX_A-D) via POWER_WELL_CTL register with status polling - display_dmc.rs: DMC firmware upload to hardware Parses CSS header, uploads payload to MMIO SRAM, enables DMC_CTRL, polls DMC_STATUS for load confirmation - display_cdclk.rs: core display clock programming for Gen9 Supports 337.5/450/540/675 MHz frequencies, required CDCLK calculation from mode pixel clocks Modified: - mod.rs: declare new modules, add IntelDeviceInfo, Gen9Regs, GmbusController, DisplayPower, DmcFirmware, DisplayClock to IntelDriver struct. Constructor initializes all modules in order: forcewake → power wells → DMC firmware → CDCLK → GMBUS → display Linux reference: local/reference/linux-7.1/drivers/gpu/drm/i915/display/ Next: regs_xe2.rs for Xe2/Lunar Lake/Battlemage (display ver 20+)
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use log::{info, warn};
|
||||
use redox_driver_sys::memory::MmioRegion;
|
||||
|
||||
use super::regs::IntelRegs;
|
||||
use crate::driver::Result;
|
||||
use crate::driver::DriverError;
|
||||
use crate::kms::ModeInfo;
|
||||
|
||||
const SKL_CDCLK_337_5: u32 = 0;
|
||||
const SKL_CDCLK_450: u32 = 1;
|
||||
const SKL_CDCLK_540: u32 = 2;
|
||||
const SKL_CDCLK_675: u32 = 3;
|
||||
|
||||
const CDCLK_DECIMAL_MASK: u32 = 0x1FF;
|
||||
const CDCLK_FREQ_SELECT_MASK: u32 = 0x3;
|
||||
const CDCLK_FREQ_SELECT_SHIFT: u32 = 26;
|
||||
const CDCLK_CD2X_DIV_MASK: u32 = 0x3;
|
||||
const CDCLK_CD2X_DIV_SHIFT: u32 = 13;
|
||||
|
||||
const CDCLK_FREQ_DECIMAL_337_5: u32 = 0b0101010;
|
||||
const CDCLK_FREQ_DECIMAL_450: u32 = 0b0011100;
|
||||
const CDCLK_FREQ_DECIMAL_540: u32 = 0b0100010;
|
||||
const CDCLK_FREQ_DECIMAL_675: u32 = 0b0101010;
|
||||
|
||||
pub struct CdclkState {
|
||||
pub frequency_khz: u32,
|
||||
pub voltage_level: u32,
|
||||
}
|
||||
|
||||
pub struct DisplayClock {
|
||||
mmio: Arc<MmioRegion>,
|
||||
regs: &'static dyn IntelRegs,
|
||||
}
|
||||
|
||||
impl DisplayClock {
|
||||
pub fn new(mmio: Arc<MmioRegion>, regs: &'static dyn IntelRegs) -> Self {
|
||||
Self { mmio, regs }
|
||||
}
|
||||
|
||||
pub fn init(&self) -> Result<CdclkState> {
|
||||
let cdclk_ctl = self.regs.cdclk_ctl();
|
||||
let current = self.mmio.read_u32(cdclk_ctl);
|
||||
|
||||
let freq_select = (current >> CDCLK_FREQ_SELECT_SHIFT) & CDCLK_FREQ_SELECT_MASK;
|
||||
let frequency = match freq_select {
|
||||
SKL_CDCLK_337_5 => 337_500,
|
||||
SKL_CDCLK_450 => 450_000,
|
||||
SKL_CDCLK_540 => 540_000,
|
||||
SKL_CDCLK_675 => 675_000,
|
||||
_ => {
|
||||
warn!("redox-drm-intel: unknown CDCLK freq select {}, defaulting to 337.5 MHz", freq_select);
|
||||
337_500
|
||||
}
|
||||
};
|
||||
|
||||
info!("redox-drm-intel: CDCLK initialized at {} kHz", frequency);
|
||||
Ok(CdclkState {
|
||||
frequency_khz: frequency,
|
||||
voltage_level: freq_select,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_frequency(&self, target_khz: u32) -> Result<CdclkState> {
|
||||
let cdclk_ctl = self.regs.cdclk_ctl();
|
||||
|
||||
let (freq_select, decimal, actual_khz) = if target_khz <= 337_500 {
|
||||
(SKL_CDCLK_337_5, CDCLK_FREQ_DECIMAL_337_5, 337_500u32)
|
||||
} else if target_khz <= 450_000 {
|
||||
(SKL_CDCLK_450, CDCLK_FREQ_DECIMAL_450, 450_000)
|
||||
} else if target_khz <= 540_000 {
|
||||
(SKL_CDCLK_540, CDCLK_FREQ_DECIMAL_540, 540_000)
|
||||
} else {
|
||||
(SKL_CDCLK_675, CDCLK_FREQ_DECIMAL_675, 675_000)
|
||||
};
|
||||
|
||||
let mut val = self.mmio.read_u32(cdclk_ctl);
|
||||
val &= !(CDCLK_FREQ_SELECT_MASK << CDCLK_FREQ_SELECT_SHIFT);
|
||||
val &= !(CDCLK_DECIMAL_MASK);
|
||||
val |= (freq_select & 0x01) << CDCLK_FREQ_SELECT_SHIFT;
|
||||
val |= (freq_select >> 1) << 1;
|
||||
val |= decimal & CDCLK_DECIMAL_MASK;
|
||||
|
||||
self.mmio.write_u32(cdclk_ctl, val);
|
||||
|
||||
info!("redox-drm-intel: CDCLK set to {} kHz (requested {} kHz)", actual_khz, target_khz);
|
||||
Ok(CdclkState {
|
||||
frequency_khz: actual_khz,
|
||||
voltage_level: freq_select,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn required_cdclk(modes: &[ModeInfo]) -> u32 {
|
||||
let mut max_pixel_rate: u32 = 0;
|
||||
for mode in modes {
|
||||
let pixel_rate = mode.pixel_clock;
|
||||
if pixel_rate > max_pixel_rate {
|
||||
max_pixel_rate = pixel_rate;
|
||||
}
|
||||
}
|
||||
|
||||
if max_pixel_rate == 0 {
|
||||
return 337_500;
|
||||
}
|
||||
|
||||
if max_pixel_rate <= 337_500 / 2 {
|
||||
337_500
|
||||
} else if max_pixel_rate <= 450_000 / 2 {
|
||||
450_000
|
||||
} else {
|
||||
540_000
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use log::{debug, info, warn};
|
||||
use redox_driver_sys::memory::MmioRegion;
|
||||
|
||||
use super::regs::IntelRegs;
|
||||
use crate::driver::Result;
|
||||
use crate::driver::DriverError;
|
||||
|
||||
const DMC_CTRL_ENABLE: u32 = 1 << 0;
|
||||
const DMC_STATUS_LOADED: u32 = 1 << 0;
|
||||
const DMC_TIMEOUT_MS: u64 = 50;
|
||||
const CSS_HEADER_SIZE: usize = 32;
|
||||
|
||||
pub struct DmcFirmware {
|
||||
mmio: Arc<MmioRegion>,
|
||||
regs: &'static dyn IntelRegs,
|
||||
}
|
||||
|
||||
impl DmcFirmware {
|
||||
pub fn new(mmio: Arc<MmioRegion>, regs: &'static dyn IntelRegs) -> Self {
|
||||
Self { mmio, regs }
|
||||
}
|
||||
|
||||
pub fn upload(&self, firmware: &[u8]) -> Result<()> {
|
||||
if firmware.len() < CSS_HEADER_SIZE {
|
||||
return Err(DriverError::Other(format!(
|
||||
"DMC firmware too small: {} bytes (need at least {})",
|
||||
firmware.len(),
|
||||
CSS_HEADER_SIZE
|
||||
)));
|
||||
}
|
||||
|
||||
let payload_offset = u32::from_le_bytes([
|
||||
firmware[24], firmware[25], firmware[26], firmware[27],
|
||||
]) as usize;
|
||||
|
||||
let payload_size = u32::from_le_bytes([
|
||||
firmware[12], firmware[13], firmware[14], firmware[15],
|
||||
]) as usize;
|
||||
|
||||
let dmc_version = u32::from_le_bytes([
|
||||
firmware[28], firmware[29], firmware[30], firmware[31],
|
||||
]);
|
||||
|
||||
if payload_offset + payload_size > firmware.len() {
|
||||
return Err(DriverError::Other(format!(
|
||||
"DMC payload extends beyond firmware: offset={}, size={}, total={}",
|
||||
payload_offset, payload_size, firmware.len()
|
||||
)));
|
||||
}
|
||||
|
||||
debug!(
|
||||
"redox-drm-intel: DMC firmware v{}, payload {} bytes at offset {}",
|
||||
dmc_version, payload_size, payload_offset
|
||||
);
|
||||
|
||||
let payload = &firmware[payload_offset..payload_offset + payload_size];
|
||||
|
||||
let mmio_start = self.regs.dmc_mmio_start();
|
||||
let mmio_end = self.regs.dmc_mmio_end();
|
||||
let fw_base = self.regs.dmc_fw_base();
|
||||
let sram_base = self.regs.dmc_sram_base();
|
||||
|
||||
self.mmio.write_u32(mmio_start, sram_base as u32);
|
||||
self.mmio.write_u32(mmio_end, (sram_base + payload_size) as u32);
|
||||
|
||||
for (i, chunk) in payload.chunks(4).enumerate() {
|
||||
let mut val: u32 = 0;
|
||||
for (j, &byte) in chunk.iter().enumerate() {
|
||||
val |= (byte as u32) << (j * 8);
|
||||
}
|
||||
self.mmio.write_u32(fw_base + i * 4, val);
|
||||
}
|
||||
|
||||
let ctrl = self.regs.dmc_ctrl();
|
||||
let status_reg = self.regs.dmc_status();
|
||||
self.mmio.write_u32(ctrl, self.mmio.read_u32(ctrl) | DMC_CTRL_ENABLE);
|
||||
|
||||
let deadline = Instant::now() + Duration::from_millis(DMC_TIMEOUT_MS);
|
||||
loop {
|
||||
let status = self.mmio.read_u32(status_reg);
|
||||
if status & DMC_STATUS_LOADED != 0 {
|
||||
info!("redox-drm-intel: DMC firmware loaded successfully");
|
||||
return Ok(());
|
||||
}
|
||||
if Instant::now() > deadline {
|
||||
warn!("redox-drm-intel: DMC firmware load timeout — status: {:#010x}", status);
|
||||
return Ok(());
|
||||
}
|
||||
std::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_loaded(&self) -> bool {
|
||||
let status_reg = self.regs.dmc_status();
|
||||
let status = self.mmio.read_u32(status_reg);
|
||||
status & DMC_STATUS_LOADED != 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use log::{debug, info, warn};
|
||||
use redox_driver_sys::memory::MmioRegion;
|
||||
|
||||
use super::regs::IntelRegs;
|
||||
use crate::driver::Result;
|
||||
use crate::driver::DriverError;
|
||||
|
||||
const POWER_WELL_TIMEOUT_MS: u64 = 20;
|
||||
|
||||
const SKL_PW1_MASK: u32 = 1 << 0;
|
||||
const SKL_PW2_MASK: u32 = 1 << 1;
|
||||
const SKL_DDI_A_IO_MASK: u32 = 1 << 2;
|
||||
const SKL_DDI_B_IO_MASK: u32 = 1 << 3;
|
||||
const SKL_DDI_C_IO_MASK: u32 = 1 << 4;
|
||||
const SKL_DDI_D_IO_MASK: u32 = 1 << 5;
|
||||
const SKL_DDI_E_IO_MASK: u32 = 1 << 6;
|
||||
const SKL_AUX_A_MASK: u32 = 1 << 8;
|
||||
const SKL_AUX_B_MASK: u32 = 1 << 9;
|
||||
const SKL_AUX_C_MASK: u32 = 1 << 10;
|
||||
const SKL_AUX_D_MASK: u32 = 1 << 11;
|
||||
|
||||
const DISPLAY_REQUIRED_WELLS: u32 =
|
||||
SKL_PW1_MASK
|
||||
| SKL_PW2_MASK
|
||||
| SKL_DDI_A_IO_MASK
|
||||
| SKL_DDI_B_IO_MASK
|
||||
| SKL_DDI_C_IO_MASK
|
||||
| SKL_DDI_D_IO_MASK
|
||||
| SKL_DDI_E_IO_MASK
|
||||
| SKL_AUX_A_MASK
|
||||
| SKL_AUX_B_MASK
|
||||
| SKL_AUX_C_MASK
|
||||
| SKL_AUX_D_MASK;
|
||||
|
||||
pub struct DisplayPower {
|
||||
mmio: Arc<MmioRegion>,
|
||||
regs: &'static dyn IntelRegs,
|
||||
}
|
||||
|
||||
impl DisplayPower {
|
||||
pub fn new(mmio: Arc<MmioRegion>, regs: &'static dyn IntelRegs) -> Self {
|
||||
Self { mmio, regs }
|
||||
}
|
||||
|
||||
pub fn init_domains(&self) -> Result<()> {
|
||||
info!("redox-drm-intel: enabling display power wells");
|
||||
|
||||
let ctl = self.regs.power_well_ctl();
|
||||
let current = self.mmio.read_u32(ctl);
|
||||
|
||||
if current & DISPLAY_REQUIRED_WELLS == DISPLAY_REQUIRED_WELLS {
|
||||
debug!("redox-drm-intel: display power wells already enabled ({:#010x})", current);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.mmio.write_u32(ctl, current | DISPLAY_REQUIRED_WELLS);
|
||||
|
||||
let deadline = Instant::now() + Duration::from_millis(POWER_WELL_TIMEOUT_MS);
|
||||
loop {
|
||||
let status = self.mmio.read_u32(ctl);
|
||||
if status & DISPLAY_REQUIRED_WELLS == DISPLAY_REQUIRED_WELLS {
|
||||
info!("redox-drm-intel: display power wells enabled ({:#010x})", status);
|
||||
return Ok(());
|
||||
}
|
||||
if Instant::now() > deadline {
|
||||
warn!("redox-drm-intel: power well enable timeout — current: {:#010x}, expected: {:#010x}",
|
||||
status, DISPLAY_REQUIRED_WELLS);
|
||||
return Err(DriverError::Other(format!(
|
||||
"power well enable timeout: {:#010x} vs {:#010x}",
|
||||
status, DISPLAY_REQUIRED_WELLS
|
||||
)));
|
||||
}
|
||||
std::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_display_ready(&self) -> bool {
|
||||
let ctl = self.regs.power_well_ctl();
|
||||
let status = self.mmio.read_u32(ctl);
|
||||
status & DISPLAY_REQUIRED_WELLS == DISPLAY_REQUIRED_WELLS
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use log::{debug, warn};
|
||||
use redox_driver_sys::memory::MmioRegion;
|
||||
|
||||
use super::regs::IntelRegs;
|
||||
use crate::driver::Result;
|
||||
use crate::driver::DriverError;
|
||||
|
||||
const GMBUS_RATE_100KHZ: u32 = 0;
|
||||
const GMBUS_SLAVE_EDID: u8 = 0xA0;
|
||||
const GMBUS_READ: u32 = 1 << 30;
|
||||
const GMBUS_SW_RDY: u32 = 1 << 31;
|
||||
const GMBUS_CYCLE_WAIT: u32 = 1 << 25;
|
||||
const GMBUS_BYTE_COUNT_SHIFT: u32 = 16;
|
||||
const GMBUS_BYTE_COUNT_MASK: u32 = 0x1FF;
|
||||
const GMBUS_SLAVE_ADDR_SHIFT: u32 = 1;
|
||||
const GMBUS_SLAVE_ADDR_MASK: u32 = 0x7F;
|
||||
const GMBUS_PIN_PAIR_MASK: u32 = 0x07;
|
||||
const GMBUS_PIN_PAIR_SHIFT: u32 = 8;
|
||||
const GMBUS_HW_RDY: u32 = 1 << 11;
|
||||
const GMBUS_SDAST: u32 = 1 << 14;
|
||||
const GMBUS_NAK_INDICATOR: u32 = 1 << 10;
|
||||
const GMBUS_HW_WAIT_PHASE: u32 = 1 << 12;
|
||||
const GMBUS_CLEAR_INTERRUPTS: u32 = 0x1F << 13;
|
||||
|
||||
const GMBUS_TIMEOUT_MS: u64 = 50;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum GmbusPort {
|
||||
DdcB = 0x04,
|
||||
DdcC = 0x05,
|
||||
DdcD = 0x06,
|
||||
Dpc = 0x02,
|
||||
Dpe = 0x03,
|
||||
}
|
||||
|
||||
impl GmbusPort {
|
||||
pub fn from_connector_index(index: u8) -> Self {
|
||||
match index {
|
||||
0 => Self::Dpc,
|
||||
1 => Self::Dpe,
|
||||
2 => Self::DdcC,
|
||||
3 => Self::DdcD,
|
||||
_ => Self::DdcB,
|
||||
}
|
||||
}
|
||||
|
||||
fn pin_pair(&self) -> u32 {
|
||||
*self as u32
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GmbusController {
|
||||
mmio: Arc<MmioRegion>,
|
||||
regs: &'static dyn IntelRegs,
|
||||
}
|
||||
|
||||
impl GmbusController {
|
||||
pub fn new(mmio: Arc<MmioRegion>, regs: &'static dyn IntelRegs) -> Self {
|
||||
Self { mmio, regs }
|
||||
}
|
||||
|
||||
pub fn init(&self) -> Result<()> {
|
||||
debug!("redox-drm-intel: initializing GMBUS controller");
|
||||
let gmbus2 = self.regs.gmbus2();
|
||||
self.mmio.write_u32(gmbus2, GMBUS_CLEAR_INTERRUPTS);
|
||||
debug!("redox-drm-intel: GMBUS initialized");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read(&self, port: GmbusPort, slave_addr: u8, offset: u16, buf: &mut [u8]) -> Result<usize> {
|
||||
let gmbus0 = self.regs.gmbus0();
|
||||
let gmbus1 = self.regs.gmbus1();
|
||||
let gmbus2 = self.regs.gmbus2();
|
||||
let gmbus3 = self.regs.gmbus3();
|
||||
let gmbus5 = self.regs.gmbus5();
|
||||
|
||||
let byte_count = buf.len().min(511);
|
||||
|
||||
self.mmio.write_u32(gmbus2, GMBUS_CLEAR_INTERRUPTS);
|
||||
|
||||
let pin_cfg = (port.pin_pair() << GMBUS_PIN_PAIR_SHIFT) | GMBUS_RATE_100KHZ;
|
||||
self.mmio.write_u32(gmbus0, pin_cfg);
|
||||
|
||||
if offset > 0 {
|
||||
self.mmio.write_u32(gmbus5, offset as u32 & 0xFFFF);
|
||||
}
|
||||
|
||||
let cmd = GMBUS_SW_RDY
|
||||
| GMBUS_CYCLE_WAIT
|
||||
| ((byte_count as u32 & GMBUS_BYTE_COUNT_MASK) << GMBUS_BYTE_COUNT_SHIFT)
|
||||
| (((slave_addr >> 1) as u32 & GMBUS_SLAVE_ADDR_MASK) << GMBUS_SLAVE_ADDR_SHIFT)
|
||||
| GMBUS_READ;
|
||||
|
||||
self.mmio.write_u32(gmbus1, cmd);
|
||||
|
||||
let deadline = Instant::now() + Duration::from_millis(GMBUS_TIMEOUT_MS);
|
||||
|
||||
let mut bytes_read: usize = 0;
|
||||
let mut fifo_offset: usize = 0;
|
||||
while bytes_read < byte_count {
|
||||
let status = self.mmio.read_u32(gmbus2);
|
||||
|
||||
if status & GMBUS_NAK_INDICATOR != 0 {
|
||||
warn!("redox-drm-intel: GMBUS NAK from slave {:#04x}", slave_addr);
|
||||
self.mmio.write_u32(gmbus2, GMBUS_CLEAR_INTERRUPTS);
|
||||
return Err(DriverError::Other(format!(
|
||||
"GMBUS NAK from slave {:#04x}",
|
||||
slave_addr
|
||||
)));
|
||||
}
|
||||
|
||||
if status & GMBUS_SDAST != 0 {
|
||||
let data = self.mmio.read_u32(gmbus3);
|
||||
let available = 4usize.min(byte_count - bytes_read);
|
||||
let data_bytes = data.to_le_bytes();
|
||||
for i in 0..available {
|
||||
buf[bytes_read + i] = data_bytes[i];
|
||||
}
|
||||
bytes_read += available;
|
||||
fifo_offset = (fifo_offset + 1) % 4;
|
||||
continue;
|
||||
}
|
||||
|
||||
if status & GMBUS_HW_RDY != 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
if Instant::now() > deadline {
|
||||
warn!("redox-drm-intel: GMBUS timeout after {}ms, {} bytes read", GMBUS_TIMEOUT_MS, bytes_read);
|
||||
self.mmio.write_u32(gmbus2, GMBUS_CLEAR_INTERRUPTS);
|
||||
if bytes_read > 0 {
|
||||
return Ok(bytes_read);
|
||||
}
|
||||
return Err(DriverError::Other("GMBUS read timeout".into()));
|
||||
}
|
||||
|
||||
std::hint::spin_loop();
|
||||
}
|
||||
|
||||
self.mmio.write_u32(gmbus2, GMBUS_CLEAR_INTERRUPTS);
|
||||
debug!("redox-drm-intel: GMBUS read {} bytes from slave {:#04x}", bytes_read, slave_addr);
|
||||
Ok(bytes_read)
|
||||
}
|
||||
|
||||
pub fn read_edid(&self, port: GmbusPort) -> Result<Vec<u8>> {
|
||||
let mut edid = vec![0u8; 128];
|
||||
let bytes = self.read(port, GMBUS_SLAVE_EDID, 0, &mut edid)?;
|
||||
|
||||
if bytes < 128 {
|
||||
warn!("redox-drm-intel: short EDID read: {} bytes", bytes);
|
||||
return Err(DriverError::Other("short EDID read".into()));
|
||||
}
|
||||
|
||||
if edid[0] != 0x00 || edid[1] != 0xFF || edid[2] != 0xFF || edid[3] != 0xFF
|
||||
|| edid[4] != 0xFF || edid[5] != 0xFF || edid[6] != 0xFF || edid[7] != 0x00
|
||||
{
|
||||
warn!("redox-drm-intel: invalid EDID header: {:02x?}", &edid[..8]);
|
||||
return Err(DriverError::Other("invalid EDID header".into()));
|
||||
}
|
||||
|
||||
let extension_count = edid[126] as usize;
|
||||
debug!("redox-drm-intel: read 128-byte EDID block, {} extensions", extension_count);
|
||||
Ok(edid)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
use log::warn;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum IntelGeneration {
|
||||
Gen4,
|
||||
Gen5,
|
||||
Gen6,
|
||||
Gen7,
|
||||
Gen8,
|
||||
Gen9,
|
||||
Gen9_5,
|
||||
Gen12,
|
||||
Gen12_7,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl IntelGeneration {
|
||||
pub fn display_version(&self) -> u8 {
|
||||
match self {
|
||||
Self::Gen4 => 4,
|
||||
Self::Gen5 => 5,
|
||||
Self::Gen6 => 6,
|
||||
Self::Gen7 => 7,
|
||||
Self::Gen8 => 9,
|
||||
Self::Gen9 => 9,
|
||||
Self::Gen9_5 => 11,
|
||||
Self::Gen12 => 12,
|
||||
Self::Gen12_7 => 14,
|
||||
Self::Unknown => 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gt_version(&self) -> u8 {
|
||||
match self {
|
||||
Self::Gen4 => 4,
|
||||
Self::Gen5 => 5,
|
||||
Self::Gen6 => 6,
|
||||
Self::Gen7 => 7,
|
||||
Self::Gen8 => 8,
|
||||
Self::Gen9 => 9,
|
||||
Self::Gen9_5 => 9,
|
||||
Self::Gen12 => 12,
|
||||
Self::Gen12_7 => 12,
|
||||
Self::Unknown => 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn num_pipes(&self) -> u8 {
|
||||
match self {
|
||||
Self::Gen4 | Self::Gen5 => 2,
|
||||
Self::Gen6 | Self::Gen7 | Self::Gen8 => 3,
|
||||
Self::Gen9 | Self::Gen9_5 | Self::Gen12 | Self::Gen12_7 => 4,
|
||||
Self::Unknown => 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct IntelDeviceInfo {
|
||||
pub generation: IntelGeneration,
|
||||
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_separate_transcoder: bool,
|
||||
pub dmc_fw_key: Option<&'static str>,
|
||||
pub platform_name: &'static str,
|
||||
}
|
||||
|
||||
impl IntelDeviceInfo {
|
||||
pub fn is_gen9_or_later(&self) -> bool {
|
||||
matches!(
|
||||
self.generation,
|
||||
IntelGeneration::Gen9
|
||||
| IntelGeneration::Gen9_5
|
||||
| IntelGeneration::Gen12
|
||||
| IntelGeneration::Gen12_7
|
||||
)
|
||||
}
|
||||
|
||||
pub fn is_gen12_or_later(&self) -> bool {
|
||||
matches!(self.generation, IntelGeneration::Gen12 | IntelGeneration::Gen12_7)
|
||||
}
|
||||
}
|
||||
|
||||
struct DeviceIdEntry {
|
||||
device_id: u16,
|
||||
gen: IntelGeneration,
|
||||
platform_name: &'static str,
|
||||
dmc_fw_key: Option<&'static str>,
|
||||
}
|
||||
|
||||
const DEVICE_ID_TABLE: &[DeviceIdEntry] = &[
|
||||
DeviceIdEntry { device_id: 0x1912, gen: IntelGeneration::Gen9, platform_name: "Skylake DT GT2", dmc_fw_key: Some("SKL") },
|
||||
DeviceIdEntry { device_id: 0x1916, gen: IntelGeneration::Gen9, platform_name: "Skylake ULT GT2", dmc_fw_key: Some("SKL") },
|
||||
DeviceIdEntry { device_id: 0x191B, gen: IntelGeneration::Gen9, platform_name: "Skylake DT GT2", dmc_fw_key: Some("SKL") },
|
||||
DeviceIdEntry { device_id: 0x191D, gen: IntelGeneration::Gen9, platform_name: "Skylake DT GT2", dmc_fw_key: Some("SKL") },
|
||||
DeviceIdEntry { device_id: 0x191E, gen: IntelGeneration::Gen9, platform_name: "Skylake ULX GT2", dmc_fw_key: Some("SKL") },
|
||||
DeviceIdEntry { device_id: 0x1921, gen: IntelGeneration::Gen9, platform_name: "Skylake ULT GT2", dmc_fw_key: Some("SKL") },
|
||||
DeviceIdEntry { device_id: 0x1923, gen: IntelGeneration::Gen9, platform_name: "Skylake ULT GT2", dmc_fw_key: Some("SKL") },
|
||||
DeviceIdEntry { device_id: 0x1926, gen: IntelGeneration::Gen9, platform_name: "Skylake ULT GT3", dmc_fw_key: Some("SKL") },
|
||||
DeviceIdEntry { device_id: 0x1927, gen: IntelGeneration::Gen9, platform_name: "Skylake ULT GT3", dmc_fw_key: Some("SKL") },
|
||||
DeviceIdEntry { device_id: 0x5912, gen: IntelGeneration::Gen9, platform_name: "Kaby Lake DT GT2", dmc_fw_key: Some("KBL") },
|
||||
DeviceIdEntry { device_id: 0x5916, gen: IntelGeneration::Gen9, platform_name: "Kaby Lake ULT GT2", dmc_fw_key: Some("KBL") },
|
||||
DeviceIdEntry { device_id: 0x591B, gen: IntelGeneration::Gen9, platform_name: "Kaby Lake DT GT2", dmc_fw_key: Some("KBL") },
|
||||
DeviceIdEntry { device_id: 0x591D, gen: IntelGeneration::Gen9, platform_name: "Kaby Lake DT GT2", dmc_fw_key: Some("KBL") },
|
||||
DeviceIdEntry { device_id: 0x5921, gen: IntelGeneration::Gen9, platform_name: "Kaby Lake ULT GT2", dmc_fw_key: Some("KBL") },
|
||||
DeviceIdEntry { device_id: 0x5923, gen: IntelGeneration::Gen9, platform_name: "Kaby Lake ULT GT2", dmc_fw_key: Some("KBL") },
|
||||
DeviceIdEntry { device_id: 0x5926, gen: IntelGeneration::Gen9, platform_name: "Kaby Lake ULT GT3", dmc_fw_key: Some("KBL") },
|
||||
DeviceIdEntry { device_id: 0x5927, gen: IntelGeneration::Gen9, platform_name: "Kaby Lake ULT GT3", dmc_fw_key: Some("KBL") },
|
||||
DeviceIdEntry { device_id: 0x3E90, gen: IntelGeneration::Gen9, platform_name: "Coffee Lake DT GT2", dmc_fw_key: Some("CFL") },
|
||||
DeviceIdEntry { device_id: 0x3E91, gen: IntelGeneration::Gen9, platform_name: "Coffee Lake DT GT2", dmc_fw_key: Some("CFL") },
|
||||
DeviceIdEntry { device_id: 0x3E92, gen: IntelGeneration::Gen9, platform_name: "Coffee Lake DT GT2", dmc_fw_key: Some("CFL") },
|
||||
DeviceIdEntry { device_id: 0x3E96, gen: IntelGeneration::Gen9, platform_name: "Coffee Lake DT GT2", dmc_fw_key: Some("CFL") },
|
||||
DeviceIdEntry { device_id: 0x3E98, gen: IntelGeneration::Gen9, platform_name: "Coffee Lake DT GT2", dmc_fw_key: Some("CFL") },
|
||||
DeviceIdEntry { device_id: 0x3E9A, gen: IntelGeneration::Gen9, platform_name: "Coffee Lake DT GT2", dmc_fw_key: Some("CFL") },
|
||||
DeviceIdEntry { device_id: 0x3EA5, gen: IntelGeneration::Gen9, platform_name: "Coffee Lake ULT GT3", dmc_fw_key: Some("CFL") },
|
||||
DeviceIdEntry { device_id: 0x3EA6, gen: IntelGeneration::Gen9, platform_name: "Coffee Lake ULT GT3", dmc_fw_key: Some("CFL") },
|
||||
DeviceIdEntry { device_id: 0x3EA7, gen: IntelGeneration::Gen9, platform_name: "Coffee Lake ULT GT3", dmc_fw_key: Some("CFL") },
|
||||
DeviceIdEntry { device_id: 0x3EA8, gen: IntelGeneration::Gen9, platform_name: "Coffee Lake ULT GT3", dmc_fw_key: Some("CFL") },
|
||||
DeviceIdEntry { device_id: 0x8A56, gen: IntelGeneration::Gen9_5, platform_name: "Ice Lake ULT GT2", dmc_fw_key: Some("ICL") },
|
||||
DeviceIdEntry { device_id: 0x8A52, gen: IntelGeneration::Gen9_5, platform_name: "Ice Lake ULT GT2", dmc_fw_key: Some("ICL") },
|
||||
DeviceIdEntry { device_id: 0x4500, gen: IntelGeneration::Gen9_5, platform_name: "Elkhart Lake", dmc_fw_key: Some("EHL") },
|
||||
DeviceIdEntry { device_id: 0x4571, gen: IntelGeneration::Gen9_5, platform_name: "Elkhart Lake", dmc_fw_key: Some("EHL") },
|
||||
DeviceIdEntry { device_id: 0x9A49, gen: IntelGeneration::Gen12, platform_name: "Tiger Lake ULT GT2", dmc_fw_key: Some("TGL") },
|
||||
DeviceIdEntry { device_id: 0x9A40, gen: IntelGeneration::Gen12, platform_name: "Tiger Lake ULT GT2", dmc_fw_key: Some("TGL") },
|
||||
DeviceIdEntry { device_id: 0x9A78, gen: IntelGeneration::Gen12, platform_name: "Tiger Lake H GT2", dmc_fw_key: Some("TGL") },
|
||||
DeviceIdEntry { device_id: 0x46A6, gen: IntelGeneration::Gen12, platform_name: "Alder Lake-P GT2", dmc_fw_key: Some("ADLP") },
|
||||
DeviceIdEntry { device_id: 0x4626, gen: IntelGeneration::Gen12, platform_name: "Alder Lake-P GT2", dmc_fw_key: Some("ADLP") },
|
||||
DeviceIdEntry { device_id: 0x46A8, gen: IntelGeneration::Gen12, platform_name: "Alder Lake-P GT2", dmc_fw_key: Some("ADLP") },
|
||||
DeviceIdEntry { device_id: 0x4628, gen: IntelGeneration::Gen12, platform_name: "Alder Lake-P GT2", dmc_fw_key: Some("ADLP") },
|
||||
DeviceIdEntry { device_id: 0x46B3, gen: IntelGeneration::Gen12, platform_name: "Alder Lake-P GT2", dmc_fw_key: Some("ADLP") },
|
||||
DeviceIdEntry { device_id: 0x5690, gen: IntelGeneration::Gen12, platform_name: "DG2 Alchemist G10", dmc_fw_key: Some("DG2") },
|
||||
DeviceIdEntry { device_id: 0x5698, gen: IntelGeneration::Gen12, platform_name: "DG2 Alchemist G11", dmc_fw_key: Some("DG2") },
|
||||
DeviceIdEntry { device_id: 0x56A0, gen: IntelGeneration::Gen12, platform_name: "DG2 Alchemist G12", dmc_fw_key: Some("DG2") },
|
||||
DeviceIdEntry { device_id: 0x7D55, gen: IntelGeneration::Gen12_7, platform_name: "Meteor Lake", dmc_fw_key: Some("MTL") },
|
||||
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") },
|
||||
];
|
||||
|
||||
pub fn device_info_from_id(device_id: u16) -> IntelDeviceInfo {
|
||||
for entry in DEVICE_ID_TABLE {
|
||||
if entry.device_id == device_id {
|
||||
let gen = entry.gen;
|
||||
return IntelDeviceInfo {
|
||||
generation: gen,
|
||||
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,
|
||||
dmc_fw_key: entry.dmc_fw_key,
|
||||
platform_name: entry.platform_name,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
warn!("redox-drm: Intel device {:#06x} not in device info table — using Gen9 defaults", device_id);
|
||||
IntelDeviceInfo {
|
||||
generation: IntelGeneration::Gen9,
|
||||
display_version: 9,
|
||||
gt_version: 9,
|
||||
num_pipes: 3,
|
||||
num_ports: 4,
|
||||
has_ddi: true,
|
||||
has_dp_aux: true,
|
||||
has_gmbus: true,
|
||||
has_dmc: true,
|
||||
has_combo_phy: false,
|
||||
has_dbuf_slice: false,
|
||||
has_separate_transcoder: false,
|
||||
dmc_fw_key: None,
|
||||
platform_name: "Unknown (Gen9 default)",
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,12 @@
|
||||
pub mod display;
|
||||
pub mod display_cdclk;
|
||||
pub mod display_dmc;
|
||||
pub mod display_power;
|
||||
pub mod gmbus;
|
||||
pub mod gtt;
|
||||
pub mod info;
|
||||
pub mod regs;
|
||||
pub mod regs_gen9;
|
||||
pub mod ring;
|
||||
|
||||
use std::collections::HashMap;
|
||||
@@ -19,7 +26,14 @@ use crate::kms::encoder::Encoder;
|
||||
use crate::kms::{ConnectorInfo, ConnectorType, ModeInfo};
|
||||
|
||||
use self::display::{DisplayPipe, IntelDisplay};
|
||||
use self::display_cdclk::DisplayClock;
|
||||
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::regs::IntelRegs;
|
||||
use self::regs_gen9::Gen9Regs;
|
||||
use self::ring::{IntelRing, RingType};
|
||||
|
||||
const FORCEWAKE: usize = 0xA18C;
|
||||
@@ -38,7 +52,9 @@ const RING_HEAD_OFFSET: usize = 0x34;
|
||||
|
||||
pub struct IntelDriver {
|
||||
info: PciDeviceInfo,
|
||||
mmio: MmioRegion,
|
||||
device_info: IntelDeviceInfo,
|
||||
mmio: Arc<MmioRegion>,
|
||||
regs: &'static Gen9Regs,
|
||||
irq_handle: Mutex<Option<InterruptHandle>>,
|
||||
display: IntelDisplay,
|
||||
gem: Mutex<GemManager>,
|
||||
@@ -47,6 +63,10 @@ pub struct IntelDriver {
|
||||
encoders: Mutex<Vec<Encoder>>,
|
||||
gtt: Mutex<IntelGtt>,
|
||||
ring: Mutex<IntelRing>,
|
||||
gmbus: GmbusController,
|
||||
display_power: DisplayPower,
|
||||
dmc: DmcFirmware,
|
||||
cdclk: DisplayClock,
|
||||
}
|
||||
|
||||
impl IntelDriver {
|
||||
@@ -97,6 +117,39 @@ impl IntelDriver {
|
||||
|
||||
enable_forcewake(&mmio)?;
|
||||
|
||||
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
|
||||
);
|
||||
|
||||
let regs = &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 display_power = DisplayPower::new(mmio_arc.clone(), regs);
|
||||
display_power.init_domains()?;
|
||||
|
||||
let dmc = DmcFirmware::new(mmio_arc.clone(), regs);
|
||||
if let Some(dmc_key) = device_info.dmc_fw_key {
|
||||
if let Some(fw_data) = firmware.get(dmc_key) {
|
||||
info!("redox-drm-intel: loading DMC firmware for {}", dmc_key);
|
||||
dmc.upload(fw_data)?;
|
||||
} else {
|
||||
warn!("redox-drm-intel: DMC firmware key '{}' not found in cache", dmc_key);
|
||||
}
|
||||
}
|
||||
|
||||
let cdclk = DisplayClock::new(mmio_arc.clone(), regs);
|
||||
let cdclk_state = cdclk.init()?;
|
||||
info!(
|
||||
"redox-drm-intel: CDCLK = {} kHz (voltage level {})",
|
||||
cdclk_state.frequency_khz, cdclk_state.voltage_level
|
||||
);
|
||||
|
||||
let display = IntelDisplay::new(display_mmio)?;
|
||||
let mut gtt = IntelGtt::init(gtt_mmio, gtt_control_mmio)?;
|
||||
let mut ring = IntelRing::create(ring_mmio, RingType::Render)?;
|
||||
@@ -137,7 +190,9 @@ impl IntelDriver {
|
||||
|
||||
Ok(Self {
|
||||
info,
|
||||
mmio,
|
||||
device_info,
|
||||
mmio: mmio_arc,
|
||||
regs,
|
||||
irq_handle: Mutex::new(irq_handle),
|
||||
display,
|
||||
gem: Mutex::new(GemManager::new()),
|
||||
@@ -146,6 +201,10 @@ impl IntelDriver {
|
||||
encoders: Mutex::new(encoders),
|
||||
gtt: Mutex::new(gtt),
|
||||
ring: Mutex::new(ring),
|
||||
gmbus,
|
||||
display_power,
|
||||
dmc,
|
||||
cdclk,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
pub trait IntelRegs {
|
||||
fn forcewake_req(&self) -> usize;
|
||||
fn forcewake_ack(&self) -> usize;
|
||||
|
||||
fn power_well_ctl(&self) -> usize;
|
||||
|
||||
fn cdclk_ctl(&self) -> usize;
|
||||
|
||||
fn dmc_mmio_start(&self) -> usize;
|
||||
fn dmc_mmio_end(&self) -> usize;
|
||||
fn dmc_fw_base(&self) -> usize;
|
||||
fn dmc_ctrl(&self) -> usize;
|
||||
fn dmc_status(&self) -> usize;
|
||||
fn dmc_sram_base(&self) -> usize;
|
||||
|
||||
fn gmbus0(&self) -> usize;
|
||||
fn gmbus1(&self) -> usize;
|
||||
fn gmbus2(&self) -> usize;
|
||||
fn gmbus3(&self) -> usize;
|
||||
fn gmbus4(&self) -> usize;
|
||||
fn gmbus5(&self) -> usize;
|
||||
|
||||
fn pipeconf(&self, pipe: u8) -> usize;
|
||||
fn pipeconf_enable_mask(&self) -> u32;
|
||||
|
||||
fn htotal(&self, pipe: u8) -> usize;
|
||||
fn hblank(&self, pipe: u8) -> usize;
|
||||
fn hsync(&self, pipe: u8) -> usize;
|
||||
fn vtotal(&self, pipe: u8) -> usize;
|
||||
fn vblank(&self, pipe: u8) -> usize;
|
||||
fn vsync(&self, pipe: u8) -> usize;
|
||||
fn pipe_src(&self, pipe: u8) -> usize;
|
||||
fn pipe_stride(&self) -> usize;
|
||||
|
||||
fn dspcntr(&self, pipe: u8) -> usize;
|
||||
fn dspcntr_enable_mask(&self) -> u32;
|
||||
fn dspsurf(&self, pipe: u8) -> usize;
|
||||
fn plane_size(&self, pipe: u8) -> usize;
|
||||
|
||||
fn ddi_buf_ctl(&self, port: u8) -> usize;
|
||||
fn ddi_port_stride(&self) -> usize;
|
||||
|
||||
fn curcntr(&self, pipe: u8) -> usize;
|
||||
fn curpos(&self, pipe: u8) -> usize;
|
||||
fn curbase(&self, pipe: u8) -> usize;
|
||||
|
||||
fn pipeframe_reg(&self, pipe: u8) -> usize;
|
||||
fn pipeframe_count_mask(&self) -> u32;
|
||||
|
||||
fn gfx_flsh_cntl(&self) -> usize;
|
||||
|
||||
fn pp_status(&self) -> usize;
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
use super::regs::IntelRegs;
|
||||
|
||||
pub struct Gen9Regs;
|
||||
|
||||
impl IntelRegs for Gen9Regs {
|
||||
fn forcewake_req(&self) -> usize { 0xA18C }
|
||||
fn forcewake_ack(&self) -> usize { 0xA194 }
|
||||
|
||||
fn power_well_ctl(&self) -> usize { 0x45400 }
|
||||
|
||||
fn cdclk_ctl(&self) -> usize { 0x46000 }
|
||||
|
||||
fn dmc_mmio_start(&self) -> usize { 0x6F000 }
|
||||
fn dmc_mmio_end(&self) -> usize { 0x6F004 }
|
||||
fn dmc_fw_base(&self) -> usize { 0x6F038 }
|
||||
fn dmc_ctrl(&self) -> usize { 0x6F064 }
|
||||
fn dmc_status(&self) -> usize { 0x6F06C }
|
||||
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 }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_gen9_pipeconf() {
|
||||
let regs = Gen9Regs;
|
||||
assert_eq!(regs.pipeconf(0), 0x70008);
|
||||
assert_eq!(regs.pipeconf(1), 0x71008);
|
||||
assert_eq!(regs.pipeconf(2), 0x72008);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gen9_ddi_buf_ctl() {
|
||||
let regs = Gen9Regs;
|
||||
assert_eq!(regs.ddi_buf_ctl(0), 0x64000);
|
||||
assert_eq!(regs.ddi_buf_ctl(1), 0x64100);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gen9_plane_offsets() {
|
||||
let regs = Gen9Regs;
|
||||
assert_eq!(regs.dspcntr(0), 0x70180);
|
||||
assert_eq!(regs.dspsurf(0), 0x7019C);
|
||||
assert_eq!(regs.dspcntr(1), 0x71180);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user