From 7f8f93146dc9794e10a6172754f41548dc6ed42f Mon Sep 17 00:00:00 2001 From: Admin Pupkin Date: Tue, 2 Jun 2026 05:06:16 +0300 Subject: [PATCH] =?UTF-8?q?intel:=20FBC,=20DRRS,=20PSR2,=20Gen4-7=20regs?= =?UTF-8?q?=20=E2=80=94=20comprehensive=20subsystems?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fbc.rs: Frame Buffer Compression FBC_CTL enable/compression/fence programming (Gen7+) FBC_STATUS compressed buffer tracking nuke() for frontbuffer invalidation on render drrs.rs: Display Refresh Rate Switching Transitions high→low refresh rate on idle timeout DRRS_CTL with idle frame count configuration mark_active() for compositor interaction psr2.rs: Panel Self Refresh v2 Selective update via sink DPCD capability probe PSR2_MAN_TRK_CTL for partial frame update tracking DP_PSR2_SUPPORT/EN_CFG2 sink communication regs_gen4_7.rs: Pre-Gen9 FDI register definitions Gen4-7 pipe/plane/DDI registers (PIPEACONF, DSPACNTR) GMBUS at 0x5100 base (pre-PCH offset) No DDI_BUF_CTL — these platforms use FDI display mod.rs: Wired FbcState, DrrsState alongside existing PsrState --- .../source/src/drivers/intel/drrs.rs | 111 ++++++++++++++++ .../redox-drm/source/src/drivers/intel/fbc.rs | 109 ++++++++++++++++ .../redox-drm/source/src/drivers/intel/mod.rs | 12 ++ .../source/src/drivers/intel/psr2.rs | 123 ++++++++++++++++++ .../source/src/drivers/intel/regs_gen4_7.rs | 55 ++++++++ 5 files changed, 410 insertions(+) create mode 100644 local/recipes/gpu/redox-drm/source/src/drivers/intel/drrs.rs create mode 100644 local/recipes/gpu/redox-drm/source/src/drivers/intel/fbc.rs create mode 100644 local/recipes/gpu/redox-drm/source/src/drivers/intel/psr2.rs create mode 100644 local/recipes/gpu/redox-drm/source/src/drivers/intel/regs_gen4_7.rs diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/intel/drrs.rs b/local/recipes/gpu/redox-drm/source/src/drivers/intel/drrs.rs new file mode 100644 index 0000000000..2a0e40cd28 --- /dev/null +++ b/local/recipes/gpu/redox-drm/source/src/drivers/intel/drrs.rs @@ -0,0 +1,111 @@ +use std::sync::Arc; +use std::time::{Duration, Instant}; + +use log::{debug, info}; +use redox_driver_sys::memory::MmioRegion; + +use super::info::IntelDeviceInfo; +use crate::driver::Result; +use crate::kms::ModeInfo; + +const DRRS_CTL_BASE: usize = 0x46100; +const DRRS_CTL_ENABLE: u32 = 1 << 31; +const DRRS_CTL_IDLE_FRAMES_SHIFT: u32 = 8; +const DRRS_CTL_IDLE_FRAMES_MASK: u32 = 0xFF; + +const DRRS_STATUS_BASE: usize = 0x46104; +const DRRS_STATUS_ACTIVE_LOW_RR: u32 = 1 << 31; +const DRRS_STATUS_CURRENT_LOW_RR: u32 = 1 << 0; + +const DRRS_IDLE_TIMEOUT_MS: u64 = 1000; +const DRRS_IDLE_FRAMES_DEFAULT: u32 = 4; + +pub struct DrrsState { + mmio: Arc, + enabled: bool, + high_rr_mode: ModeInfo, + low_rr_mode: Option, + last_activity: Instant, + pipe: u8, +} + +impl DrrsState { + pub fn new(mmio: Arc, _device_info: &IntelDeviceInfo, pipe: u8) -> Self { + Self { + mmio, + enabled: false, + high_rr_mode: ModeInfo::default_1080p(), + low_rr_mode: None, + last_activity: Instant::now(), + pipe, + } + } + + pub fn init(&mut self, high_mode: &ModeInfo) -> Result<()> { + self.high_rr_mode = high_mode.clone(); + + let low_vrefresh = 48u32; + if high_mode.vrefresh > low_vrefresh + 10 { + let mut low = high_mode.clone(); + low.vrefresh = low_vrefresh; + low.clock = (low.clock as u64 * low_vrefresh as u64 + / high_mode.vrefresh as u64) as u32; + self.low_rr_mode = Some(low); + } + + Ok(()) + } + + pub fn enable(&mut self) -> Result<()> { + if self.enabled { + return Ok(()); + } + let ctl = DRRS_CTL_BASE + self.pipe as usize * 0x1000; + let val = DRRS_CTL_ENABLE + | ((DRRS_IDLE_FRAMES_DEFAULT & DRRS_CTL_IDLE_FRAMES_MASK) + << DRRS_CTL_IDLE_FRAMES_SHIFT); + self.mmio.write32(ctl, val); + self.enabled = true; + self.last_activity = Instant::now(); + info!("redox-drm-intel: DRRS enabled on pipe {}", self.pipe); + Ok(()) + } + + pub fn disable(&mut self) -> Result<()> { + if !self.enabled { + return Ok(()); + } + let ctl = DRRS_CTL_BASE + self.pipe as usize * 0x1000; + self.mmio.write32(ctl, 0); + self.enabled = false; + info!("redox-drm-intel: DRRS disabled on pipe {}", self.pipe); + Ok(()) + } + + pub fn mark_active(&mut self) { + self.last_activity = Instant::now(); + } + + pub fn is_low_rr_active(&self) -> bool { + if !self.enabled { + return false; + } + let status = DRRS_STATUS_BASE + self.pipe as usize * 0x1000; + let val = self.mmio.read32(status); + val & DRRS_STATUS_CURRENT_LOW_RR != 0 + } + + pub fn should_enter_low_rr(&self) -> bool { + self.enabled + && self.low_rr_mode.is_some() + && self.last_activity.elapsed() > Duration::from_millis(DRRS_IDLE_TIMEOUT_MS) + } + + pub fn low_rr_mode(&self) -> Option<&ModeInfo> { + self.low_rr_mode.as_ref() + } + + pub fn is_enabled(&self) -> bool { + self.enabled + } +} diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/intel/fbc.rs b/local/recipes/gpu/redox-drm/source/src/drivers/intel/fbc.rs new file mode 100644 index 0000000000..073b6fe13e --- /dev/null +++ b/local/recipes/gpu/redox-drm/source/src/drivers/intel/fbc.rs @@ -0,0 +1,109 @@ +use std::sync::Arc; + +use log::{debug, info}; +use redox_driver_sys::memory::MmioRegion; + +use super::info::IntelDeviceInfo; +use crate::driver::Result; +use crate::kms::ModeInfo; + +const FBC_CTL_BASE: usize = 0x43200; +const FBC_CTL_ENABLE: u32 = 1 << 31; +const FBC_CTL_COMPRESSION_EN: u32 = 1 << 30; +const FBC_CTL_INTERVAL_SHIFT: u32 = 22; +const FBC_CTL_INTERVAL_MASK: u32 = 0x3; +const FBC_CTL_FENCE_EN: u32 = 1 << 21; + +const FBC_STATUS_BASE: usize = 0x43214; +const FBC_STATUS_COMPRESSING: u32 = 1 << 31; +const FBC_STATUS_COMPRESSED: u32 = 1 << 0; + +const FBC_FENCE_BASE: usize = 0x43300; + +const FBC_LL_BASE: usize = 0x43400; +const FBC_STRIDE_OFFSET: usize = 0x4; +const FBC_CFB_SIZE: usize = 0x8; + +pub struct FbcState { + mmio: Arc, + enabled: bool, + compressed: bool, + compressed_size: u32, +} + +impl FbcState { + pub fn new(mmio: Arc, _device_info: &IntelDeviceInfo) -> Self { + Self { + mmio, + enabled: false, + compressed: false, + compressed_size: 0, + } + } + + pub fn enable(&mut self, fb_gtt_addr: u64, stride: u32, mode: &ModeInfo) -> Result<()> { + if self.enabled { + return Ok(()); + } + + let fb_lines = mode.vdisplay as u32; + self.compressed_size = fb_lines.saturating_mul(stride); + + let ctl_reg = FBC_CTL_BASE; + let mut ctl = self.mmio.read32(ctl_reg); + ctl &= !FBC_CTL_ENABLE; + self.mmio.write32(ctl_reg, ctl); + + let ll_reg = FBC_LL_BASE; + self.mmio.write32(ll_reg, fb_gtt_addr as u32); + self.mmio.write32(ll_reg + 4, (fb_gtt_addr >> 32) as u32); + + self.mmio.write32(FBC_LL_BASE + FBC_STRIDE_OFFSET, stride); + + self.mmio.write32(FBC_FENCE_BASE, 0); + + ctl = self.mmio.read32(ctl_reg); + ctl |= FBC_CTL_ENABLE | FBC_CTL_COMPRESSION_EN | FBC_CTL_FENCE_EN; + ctl &= !(FBC_CTL_INTERVAL_MASK << FBC_CTL_INTERVAL_SHIFT); + ctl |= 1 << FBC_CTL_INTERVAL_SHIFT; + self.mmio.write32(ctl_reg, ctl); + + let status = self.mmio.read32(FBC_STATUS_BASE); + if status & FBC_STATUS_COMPRESSING != 0 || status & FBC_STATUS_COMPRESSED != 0 { + self.enabled = true; + info!( + "redox-drm-intel: FBC enabled — {}x{} stride={} cfb={}KB status={:#x}", + mode.hdisplay, mode.vdisplay, stride, + self.compressed_size / 1024, status + ); + } else { + debug!("redox-drm-intel: FBC enable requested, status={:#x}", status); + self.enabled = true; + } + Ok(()) + } + + pub fn disable(&mut self) -> Result<()> { + if !self.enabled { + return Ok(()); + } + let ctl = self.mmio.read32(FBC_CTL_BASE); + self.mmio.write32(FBC_CTL_BASE, ctl & !FBC_CTL_ENABLE); + self.mmio.write32(FBC_STATUS_BASE, FBC_STATUS_COMPRESSED | FBC_STATUS_COMPRESSING); + self.enabled = false; + info!("redox-drm-intel: FBC disabled"); + Ok(()) + } + + pub fn nuke(&self) { + if !self.enabled { + return; + } + self.mmio.write32(FBC_STATUS_BASE, FBC_STATUS_COMPRESSED); + debug!("redox-drm-intel: FBC nuke (invalidate compressed framebuffer)"); + } + + pub fn is_enabled(&self) -> bool { + self.enabled + } +} diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/intel/mod.rs b/local/recipes/gpu/redox-drm/source/src/drivers/intel/mod.rs index cf0e6a4268..b9423274d4 100644 --- a/local/recipes/gpu/redox-drm/source/src/drivers/intel/mod.rs +++ b/local/recipes/gpu/redox-drm/source/src/drivers/intel/mod.rs @@ -15,7 +15,9 @@ pub mod display_watermark; pub mod dp_aux; pub mod dp_link; pub mod execbuffer; +pub mod drrs; pub mod execlists; +pub mod fbc; pub mod fence; pub mod gamma; pub mod gmbus; @@ -30,7 +32,9 @@ pub mod info; pub mod lmem; pub mod mocs; pub mod panel_pps; +pub mod psr2; pub mod regs; +pub mod regs_gen4_7; pub mod regs_gen9; pub mod regs_gen12; pub mod regs_xe2; @@ -82,6 +86,8 @@ use self::regs_xe2::Xe2Regs; use self::backlight::Backlight; use self::context::ContextManager; use self::display_psr::PsrState; +use self::drrs::DrrsState; +use self::fbc::FbcState; use self::hangcheck::GpuHangDetector; use self::panel_pps::PanelPowerSequencer; use self::ring::{IntelRing, RingType}; @@ -124,6 +130,8 @@ pub struct IntelDriver { backlight: Mutex, context_manager: Mutex, psr: Mutex, + drrs: Mutex, + fbc: Mutex, hang_detector: Mutex, panel_pps: Mutex, syncobj_mgr: Mutex, @@ -369,6 +377,8 @@ impl IntelDriver { let context_manager = Mutex::new(ContextManager::new()); let psr = Mutex::new(PsrState::new(mmio_arc.clone(), 0)); + let drrs = Mutex::new(DrrsState::new(mmio_arc.clone(), &device_info, 0)); + let fbc = Mutex::new(FbcState::new(mmio_arc.clone(), &device_info)); let hang_detector = Mutex::new(GpuHangDetector::new(mmio_arc.clone(), RENDER_RING_BASE)); @@ -443,6 +453,8 @@ impl IntelDriver { backlight: Mutex::new(backlight), context_manager, psr, + drrs, + fbc, hang_detector, panel_pps, syncobj_mgr, diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/intel/psr2.rs b/local/recipes/gpu/redox-drm/source/src/drivers/intel/psr2.rs new file mode 100644 index 0000000000..e02c6c0b89 --- /dev/null +++ b/local/recipes/gpu/redox-drm/source/src/drivers/intel/psr2.rs @@ -0,0 +1,123 @@ +use std::sync::Arc; + +use log::{debug, info}; +use redox_driver_sys::memory::MmioRegion; + +use super::dp_aux::DpAux; +use super::info::IntelDeviceInfo; +use crate::driver::Result; + +const PSR2_CTL_BASE: usize = 0x60800; +const PSR2_CTL_ENABLE: u32 = 1 << 31; +const PSR2_CTL_FRAME_SYNC: u32 = 1 << 14; +const PSR2_CTL_TP2_TP3_TIME_SHIFT: u32 = 8; +const PSR2_CTL_TP2_TP3_MASK: u32 = 0x3; +const PSR2_CTL_IDLE_FRAMES_SHIFT: u32 = 0; +const PSR2_CTL_IDLE_FRAMES_MASK: u32 = 0x7; + +const PSR2_MAN_TRK_CTL_BASE: usize = 0x60810; +const PSR2_MAN_TRK_CTL_ENABLE: u32 = 1 << 31; +const PSR2_MAN_TRK_CTL_SF_PARTIAL_FRAME_UPDATE: u32 = 1 << 2; + +const DP_PSR2_SUPPORT: u32 = 0x070; +const DP_PSR2_CAPABILITY: u32 = 0x071; +const DP_PSR2_ENABLE_SINK: u8 = 1 << 0; +const DP_PSR2_Y_COORD_REQUIRED: u8 = 1 << 3; +const DP_PSR2_SU_REGION_SCANLINE_CAP: u8 = 1 << 4; + +const DP_PSR_EN_CFG2: u32 = 0x0171; +const DP_PSR2_SELECTIVE_UPDATE: u8 = 1 << 0; + +pub struct Psr2State { + mmio: Arc, + enabled: bool, + selective_update: bool, + transcoder: u8, +} + +impl Psr2State { + pub fn new(mmio: Arc, _device_info: &IntelDeviceInfo, transcoder: u8) -> Self { + Self { + mmio, + enabled: false, + selective_update: false, + transcoder, + } + } + + pub fn probe_sink_caps(&self, aux: &DpAux) -> Result { + match aux.read_dpcd(DP_PSR2_SUPPORT, 2) { + Ok(data) if data.len() >= 2 => { + let supported = data[0] & 0x01 != 0; + if supported { + info!("redox-drm-intel: PSR2 supported by sink"); + } + Ok(supported) + } + _ => Ok(false), + } + } + + pub fn enable(&mut self, aux: Option<&DpAux>, port: u8) -> Result<()> { + if self.enabled { + return Ok(()); + } + + let sink_caps = aux.and_then(|a| a.read_dpcd(DP_PSR2_CAPABILITY, 2).ok()); + let y_coord_required = sink_caps.as_ref() + .map_or(false, |d| d.len() >= 2 && d[0] & DP_PSR2_Y_COORD_REQUIRED != 0); + let su_scanline = sink_caps.as_ref() + .map_or(false, |d| d.len() >= 2 && d[0] & DP_PSR2_SU_REGION_SCANLINE_CAP != 0); + + if let Some(aux) = aux { + aux.write_dpcd(DP_PSR_EN_CFG2, &[DP_PSR2_SELECTIVE_UPDATE])?; + aux.write_dpcd(DP_PSR2_SUPPORT, &[DP_PSR2_ENABLE_SINK])?; + debug!("redox-drm-intel: PSR2 sink enabled on port {} (y_coord={}, su_scanline={})", + port, y_coord_required, su_scanline); + } + + let ctl = PSR2_CTL_BASE + self.transcoder as usize * 0x1000; + let val = PSR2_CTL_ENABLE + | PSR2_CTL_FRAME_SYNC + | (1 & PSR2_CTL_TP2_TP3_MASK) << PSR2_CTL_TP2_TP3_TIME_SHIFT + | (4 & PSR2_CTL_IDLE_FRAMES_MASK) << PSR2_CTL_IDLE_FRAMES_SHIFT; + self.mmio.write32(ctl, val); + + self.enabled = true; + self.selective_update = su_scanline; + info!("redox-drm-intel: PSR2 enabled on transcoder {} (selective_update={})", + self.transcoder, self.selective_update); + Ok(()) + } + + pub fn disable(&mut self, aux: Option<&DpAux>) -> Result<()> { + if !self.enabled { + return Ok(()); + } + let ctl = PSR2_CTL_BASE + self.transcoder as usize * 0x1000; + self.mmio.write32(ctl, 0); + + if let Some(aux) = aux { + aux.write_dpcd(DP_PSR_EN_CFG2, &[0])?; + aux.write_dpcd(DP_PSR2_SUPPORT, &[0])?; + } + + self.enabled = false; + info!("redox-drm-intel: PSR2 disabled on transcoder {}", self.transcoder); + Ok(()) + } + + pub fn flush(&self) { + if !self.enabled { + return; + } + let man_trk = PSR2_MAN_TRK_CTL_BASE + self.transcoder as usize * 0x1000; + self.mmio.write32(man_trk, + PSR2_MAN_TRK_CTL_ENABLE | PSR2_MAN_TRK_CTL_SF_PARTIAL_FRAME_UPDATE); + debug!("redox-drm-intel: PSR2 manual tracking flush"); + } + + pub fn is_enabled(&self) -> bool { + self.enabled + } +} diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/intel/regs_gen4_7.rs b/local/recipes/gpu/redox-drm/source/src/drivers/intel/regs_gen4_7.rs new file mode 100644 index 0000000000..4cbe671f80 --- /dev/null +++ b/local/recipes/gpu/redox-drm/source/src/drivers/intel/regs_gen4_7.rs @@ -0,0 +1,55 @@ +use super::regs::IntelRegs; + +pub struct Gen4_7Regs; + +impl IntelRegs for Gen4_7Regs { + fn forcewake_req(&self) -> usize { 0xA188 } + fn forcewake_ack(&self) -> usize { 0x130040 } + fn power_well_ctl(&self) -> usize { 0 } + fn cdclk_ctl(&self) -> usize { 0x46200 } + fn dmc_mmio_start(&self) -> usize { 0 } + fn dmc_mmio_end(&self) -> usize { 0 } + fn dmc_fw_base(&self) -> usize { 0 } + fn dmc_ctrl(&self) -> usize { 0 } + fn dmc_status(&self) -> usize { 0 } + fn dmc_sram_base(&self) -> usize { 0 } + fn gmbus0(&self) -> usize { 0x5100 } + fn gmbus1(&self) -> usize { 0x5120 } + fn gmbus2(&self) -> usize { 0x5140 } + fn gmbus3(&self) -> usize { 0x5160 } + fn gmbus4(&self) -> usize { 0x5180 } + fn gmbus5(&self) -> usize { 0x51A0 } + 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 ps_ctrl(&self, _pipe: u8) -> usize { 0 } + fn ps_win_pos(&self, _pipe: u8) -> usize { 0 } + fn ps_win_size(&self, _pipe: u8) -> usize { 0 } + fn ddi_buf_ctl(&self, _port: u8) -> usize { 0 } + fn ddi_port_stride(&self) -> usize { 0x100 } + fn curcntr(&self, pipe: u8) -> usize { 0x70080 + pipe as usize * 0x1000 } + fn curbase(&self, pipe: u8) -> usize { 0x70084 + pipe as usize * 0x1000 } + fn curpos(&self, pipe: u8) -> usize { 0x70088 + pipe as usize * 0x1000 } + fn gfx_flsh_cntl(&self) -> usize { 0x101008 } + fn pipestat(&self, pipe: u8) -> usize { 0x70024 + pipe as usize * 0x1000 } + fn pipestat_vblank_mask(&self) -> u32 { 1 << 0 } + fn pipestat_vsync_mask(&self) -> u32 { 1 << 4 } + fn pp_status(&self) -> usize { 0xC7200 } + fn pipeframe_reg(&self, pipe: u8) -> usize { 0x70040 + pipe as usize * 0x1000 } + fn pipeframe_count_mask(&self) -> u32 { 0xFFFFFF } + fn de_iir(&self) -> usize { 0x44008 } + fn de_imr(&self) -> usize { 0x4400C } + fn de_ier(&self) -> usize { 0x44010 } + fn d2d_link_ctl(&self, _port: u8) -> usize { 0 } +}