intel: hotplug handler + cursor plane (Phase 1 + Phase 3)

Add hotplug.rs — HPD interrupt handling for monitor connect/disconnect.
- PORT_HOTPLUG_EN/STAT at 0x61110/0x61114 per-port HPD detection
- GEN11_DE_HPD_ISR/IMR/IIR/IER at 0x44470-0x4447C for Xe2
- GEN8_DE_PORT_ISR/IMR for Gen9 legacy path
- init() enables HPD on all 6 ports, check_events() reads ISR
- Distinguishes long pulse (connect/disconnect) from short pulse (EDID change)

Add cursor.rs — hardware cursor plane.
- CURCNTR/CURPOS/CURBASE via IntelRegs trait (multi-generation)
- enable() with 64x64 ARGB8888 + gamma, disable(), set_position()
- update() atomically sets surface + position for tear-free cursor

Wire both into IntelDriver constructor and struct.
Linux reference: intel_cursor.c, intel_hotplug.c
This commit is contained in:
2026-05-30 08:49:41 +03:00
parent 2f18b35122
commit a932ae1ca1
3 changed files with 200 additions and 0 deletions
@@ -0,0 +1,69 @@
use std::sync::Arc;
use log::debug;
use redox_driver_sys::memory::MmioRegion;
use super::regs::IntelRegs;
use crate::driver::Result;
pub struct CursorPlane {
mmio: Arc<MmioRegion>,
regs: &'static dyn IntelRegs,
}
impl CursorPlane {
pub fn new(mmio: Arc<MmioRegion>, regs: &'static dyn IntelRegs) -> Self {
Self { mmio, regs }
}
pub fn set_position(&self, pipe: u8, x: u16, y: u16) -> Result<()> {
let pos_reg = self.regs.curpos(pipe);
let pos_val = ((y as u32 & 0xFFF) << 16) | (x as u32 & 0xFFF);
self.mmio.write_u32(pos_reg, pos_val);
debug!("redox-drm-intel: cursor pipe {} position ({}, {})", pipe, x, y);
Ok(())
}
pub fn set_surface(&self, pipe: u8, gtt_offset: u32) -> Result<()> {
let base_reg = self.regs.curbase(pipe);
self.mmio.write_u32(base_reg, gtt_offset);
debug!("redox-drm-intel: cursor pipe {} surface at {:#010x}", pipe, gtt_offset);
Ok(())
}
pub fn enable(&self, pipe: u8) -> Result<()> {
let cntr = self.regs.curcntr(pipe);
let mode_128x128: u32 = 0 << 28;
let mode_64x64: u32 = 1 << 28;
let mode_argb8888: u32 = 1 << 24;
let gamma_enable: u32 = 1 << 16;
let pipe_select: u32 = (pipe as u32) << 28;
let val = mode_64x64 | mode_argb8888 | gamma_enable;
self.mmio.write_u32(cntr, val | pipe_select);
debug!("redox-drm-intel: cursor enabled on pipe {}", pipe);
Ok(())
}
pub fn disable(&self, pipe: u8) -> Result<()> {
let cntr = self.regs.curcntr(pipe);
self.mmio.write_u32(cntr, 0);
debug!("redox-drm-intel: cursor disabled on pipe {}", pipe);
Ok(())
}
pub fn is_visible(&self, pipe: u8) -> bool {
let cntr = self.regs.curcntr(pipe);
let val = self.mmio.read_u32(cntr);
val & 0x80000000 != 0
}
pub fn update(&self, pipe: u8, gtt_offset: u32, x: u16, y: u16) -> Result<()> {
let base_reg = self.regs.curbase(pipe);
self.mmio.write_u32(base_reg, gtt_offset);
let pos_reg = self.regs.curpos(pipe);
let pos_val = ((y as u32 & 0xFFF) << 16) | (x as u32 & 0xFFF);
self.mmio.write_u32(pos_reg, pos_val);
Ok(())
}
}
@@ -0,0 +1,118 @@
use std::sync::Arc;
use log::{debug, info};
use redox_driver_sys::memory::MmioRegion;
use super::info::IntelDeviceInfo;
use crate::driver::Result;
const PORT_HOTPLUG_EN: usize = 0x61110;
const PORT_HOTPLUG_STAT: usize = 0x61114;
const GEN11_DE_HPD_ISR: usize = 0x44470;
const GEN11_DE_HPD_IMR: usize = 0x44474;
const GEN11_DE_HPD_IIR: usize = 0x44478;
const GEN11_DE_HPD_IER: usize = 0x4447C;
const GEN8_DE_PORT_ISR: usize = 0x44400;
const GEN8_DE_PORT_IMR: usize = 0x44404;
const HPD_PORT_A: u8 = 0;
const HPD_PORT_B: u8 = 1;
const HPD_PORT_C: u8 = 2;
const HPD_PORT_D: u8 = 3;
const HPD_PORT_E: u8 = 4;
const HPD_PORT_F: u8 = 5;
const MAX_PORTS: u8 = 6;
const HPD_LONG_DETECT: u32 = 1 << 2;
const HPD_SHORT_DETECT: u32 = 1 << 1;
#[derive(Debug, Clone, Copy)]
pub struct HotplugEvent {
pub port: u8,
pub long_pulse: bool,
pub short_pulse: bool,
}
pub struct HotplugHandler {
mmio: Arc<MmioRegion>,
is_xe2: bool,
}
impl HotplugHandler {
pub fn new(mmio: Arc<MmioRegion>, device_info: &IntelDeviceInfo) -> Self {
let is_xe2 = device_info.generation == super::info::IntelGeneration::GenXe2;
Self { mmio, is_xe2 }
}
pub fn init(&self) -> Result<()> {
info!("redox-drm-intel: enabling hotplug detection");
for port in 0..MAX_PORTS {
self.enable_port_hpd(port)?;
}
if self.is_xe2 {
self.mmio.write_u32(GEN11_DE_HPD_IER, 0x3F);
self.mmio.write_u32(GEN11_DE_HPD_IMR, 0);
debug!("redox-drm-intel: Xe2 HPD interrupts enabled");
} else {
let imr = self.mmio.read_u32(GEN8_DE_PORT_IMR);
self.mmio.write_u32(GEN8_DE_PORT_IMR, imr & !0xFC00);
}
Ok(())
}
fn enable_port_hpd(&self, port: u8) -> Result<()> {
let en_reg = PORT_HOTPLUG_EN;
let current = self.mmio.read_u32(en_reg);
let hpd_bit = 1u32 << (port as u32 + 16);
if current & hpd_bit == 0 {
self.mmio.write_u32(en_reg, current | hpd_bit);
}
Ok(())
}
pub fn check_events(&self) -> Vec<HotplugEvent> {
let mut events = Vec::new();
let isr = if self.is_xe2 {
self.mmio.read_u32(GEN11_DE_HPD_ISR)
} else {
self.mmio.read_u32(GEN8_DE_PORT_ISR)
};
for port in 0..MAX_PORTS {
let port_bit = 1u32 << (port as u32 + 3);
if isr & port_bit != 0 {
let stat = self.mmio.read_u32(PORT_HOTPLUG_STAT);
let port_stat = (stat >> (port as u32 * 4)) & 0xF;
events.push(HotplugEvent {
port,
long_pulse: port_stat & HPD_LONG_DETECT != 0,
short_pulse: port_stat & HPD_SHORT_DETECT != 0,
});
}
}
if !events.is_empty() {
debug!("redox-drm-intel: {} hotplug event(s)", events.len());
}
events
}
pub fn ack_events(&self, events: &[HotplugEvent]) {
let iir = if self.is_xe2 { GEN11_DE_HPD_IIR } else { GEN8_DE_PORT_ISR + 8 };
for event in events {
let port_bit = 1u32 << (event.port as u32 + 3);
self.mmio.write_u32(iir, port_bit);
}
}
pub fn is_connected(&self, port: u8) -> bool {
let stat = self.mmio.read_u32(PORT_HOTPLUG_STAT);
let port_stat = (stat >> (port as u32 * 4)) & 0xF;
port_stat & HPD_LONG_DETECT != 0
}
}
@@ -1,3 +1,4 @@
pub mod cursor;
pub mod display;
pub mod display_cdclk;
pub mod display_combo_phy;
@@ -7,6 +8,7 @@ pub mod display_power;
pub mod dp_aux;
pub mod gmbus;
pub mod gtt;
pub mod hotplug;
pub mod info;
pub mod regs;
pub mod regs_gen9;
@@ -29,6 +31,7 @@ use crate::kms::crtc::Crtc;
use crate::kms::encoder::Encoder;
use crate::kms::{ConnectorInfo, ConnectorType, ModeInfo};
use self::cursor::CursorPlane;
use self::display::{DisplayPipe, IntelDisplay};
use self::display_cdclk::DisplayClock;
use self::display_combo_phy::ComboPhy;
@@ -38,6 +41,7 @@ use self::display_power::DisplayPower;
use self::dp_aux::DpAux;
use self::gmbus::GmbusController;
use self::gtt::IntelGtt;
use self::hotplug::HotplugHandler;
use self::info::{IntelDeviceInfo, IntelGeneration, device_info_from_id};
use self::regs::IntelRegs;
use self::regs_gen9::Gen9Regs;
@@ -78,6 +82,8 @@ pub struct IntelDriver {
dpll: DisplayPll,
dmc: DmcFirmware,
cdclk: DisplayClock,
cursor: CursorPlane,
hotplug: HotplugHandler,
}
impl IntelDriver {
@@ -200,6 +206,11 @@ impl IntelDriver {
} else {
None
};
let cursor = CursorPlane::new(mmio_arc.clone(), regs);
let hotplug = HotplugHandler::new(mmio_arc.clone(), &device_info);
hotplug.init()?;
let (connectors, encoders) = detect_display_topology(&display, edid_source)?;
let crtcs = build_crtcs(&display)?;
@@ -252,6 +263,8 @@ impl IntelDriver {
display_power,
dmc,
cdclk,
cursor,
hotplug,
})
}
}