From a9cec3954dbceebb540c4bf28b999b28aff7022d Mon Sep 17 00:00:00 2001 From: Admin Pupkin Date: Tue, 2 Jun 2026 06:07:50 +0300 Subject: [PATCH] =?UTF-8?q?intel:=20VRR=20+=20DSC=20=E2=80=94=20Variable?= =?UTF-8?q?=20Refresh=20Rate=20and=20Display=20Stream=20Compression?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vrr.rs: Adaptive Sync / VRR support VRR_CTL enable with flip line configuration Min/max vtotal frame time programming Transcoder-based VRR status monitoring dsc.rs: DSC 1.2a compression encoder PPS (Picture Parameter Set) computation per mode Slice count (1/2/4/8) and BPC (8/10/12) configuration DPCD sink DSC enable/disable communication probe_sink_caps via DP_DSC_SUPPORT register --- .../redox-drm/source/src/drivers/intel/dsc.rs | 129 ++++++++++++++++++ .../redox-drm/source/src/drivers/intel/vrr.rs | 81 +++++++++++ 2 files changed, 210 insertions(+) create mode 100644 local/recipes/gpu/redox-drm/source/src/drivers/intel/dsc.rs create mode 100644 local/recipes/gpu/redox-drm/source/src/drivers/intel/vrr.rs diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/intel/dsc.rs b/local/recipes/gpu/redox-drm/source/src/drivers/intel/dsc.rs new file mode 100644 index 0000000000..9259770623 --- /dev/null +++ b/local/recipes/gpu/redox-drm/source/src/drivers/intel/dsc.rs @@ -0,0 +1,129 @@ +use std::sync::Arc; + +use log::info; +use redox_driver_sys::memory::MmioRegion; + +use super::dp_aux::DpAux; +use super::info::IntelDeviceInfo; +use crate::driver::Result; + +const DSC_CTL_BASE: usize = 0x6B000; +const DSC_PPS_BASE: usize = 0x6B200; +const DSC_RC_RANGE_PARAM_BASE: usize = 0x6B400; +const DSC_TRANS_STRIDE: usize = 0x1000; +const DSC_PPS_SIZE: usize = 128; +const DSC_RC_RANGE_SIZE: usize = 128; + +const DSC_CTL_ENABLE: u32 = 1 << 31; +const DSC_CTL_SLICE_COUNT_SHIFT: u32 = 8; +const DSC_CTL_BPC_SHIFT: u32 = 4; +const DSC_CTL_BPC_8: u32 = 0; +const DSC_CTL_BPC_10: u32 = 1; +const DSC_CTL_BPC_12: u32 = 2; + +const DP_DSC_SUPPORT: u32 = 0x060; +const DP_DSC_ENABLE: u32 = 0x06F; +const DP_DSC_ENABLE_SINK: u8 = 1 << 0; + +pub struct DscState { + mmio: Arc, + enabled: bool, + pipe: u8, + slice_count: u8, + bits_per_component: u8, + pps: [u8; DSC_PPS_SIZE], +} + +impl DscState { + pub fn new(mmio: Arc, _device_info: &IntelDeviceInfo, pipe: u8) -> Self { + Self { + mmio, + enabled: false, + pipe, + slice_count: 1, + bits_per_component: 8, + pps: [0u8; DSC_PPS_SIZE], + } + } + + pub fn compute_pps(&mut self, hdisplay: u16, vdisplay: u16, slice_count: u8, bpc: u8) { + self.pps[0] = 0x12; // major version 1, minor 2 (DSC 1.2a) + self.pps[2] = (hdisplay >> 8) as u8; + self.pps[3] = (hdisplay & 0xFF) as u8; + self.pps[4] = (hdisplay >> 8) as u8; + self.pps[5] = (hdisplay & 0xFF) as u8; + self.pps[6] = (vdisplay >> 8) as u8; + self.pps[7] = (vdisplay & 0xFF) as u8; + self.pps[8] = (vdisplay >> 8) as u8; + self.pps[9] = (vdisplay & 0xFF) as u8; + self.pps[11] = 0; // slice_width = hdisplay / slice_count + self.pps[13] = 0; + self.pps[14] = slice_count - 1; + self.pps[15] = (bpc - 8) << 4; + self.slice_count = slice_count; + self.bits_per_component = bpc; + } + + pub fn enable(&mut self, aux: Option<&DpAux>, port: u8) -> Result<()> { + if self.enabled { + return Ok(()); + } + + let pps_base = DSC_PPS_BASE + self.pipe as usize * DSC_TRANS_STRIDE; + for (i, chunk) in self.pps.chunks(4).enumerate() { + let mut val: u32 = 0; + for (j, &byte) in chunk.iter().enumerate() { + val |= (byte as u32) << (j * 8); + } + self.mmio.write32(pps_base + i * 4, val); + } + + let bpc = match self.bits_per_component { + 10 => DSC_CTL_BPC_10, + 12 => DSC_CTL_BPC_12, + _ => DSC_CTL_BPC_8, + }; + + let ctl = DSC_CTL_BASE + self.pipe as usize * DSC_TRANS_STRIDE; + let val = DSC_CTL_ENABLE + | ((self.slice_count as u32 - 1) << DSC_CTL_SLICE_COUNT_SHIFT) + | (bpc << DSC_CTL_BPC_SHIFT); + self.mmio.write32(ctl, val); + + if let Some(aux) = aux { + aux.write_dpcd(DP_DSC_ENABLE, &[DP_DSC_ENABLE_SINK])?; + debug!("redox-drm-intel: DSC sink enabled on port {}", port); + } + + self.enabled = true; + info!("redox-drm-intel: DSC enabled on pipe {} ({} slices, {} bpc)", + self.pipe, self.slice_count, self.bits_per_component); + Ok(()) + } + + pub fn disable(&mut self, aux: Option<&DpAux>) -> Result<()> { + if !self.enabled { + return Ok(()); + } + let ctl = DSC_CTL_BASE + self.pipe as usize * DSC_TRANS_STRIDE; + self.mmio.write32(ctl, 0); + + if let Some(aux) = aux { + let _ = aux.write_dpcd(DP_DSC_ENABLE, &[0]); + } + + self.enabled = false; + info!("redox-drm-intel: DSC disabled on pipe {}", self.pipe); + Ok(()) + } + + pub fn probe_sink_caps(&self, aux: &DpAux) -> bool { + aux.read_dpcd(DP_DSC_SUPPORT, 2) + .map(|d| d.len() >= 2 && d[0] & 0x01 != 0) + .unwrap_or(false) + } + + pub fn is_enabled(&self) -> bool { + self.enabled + } +} diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/intel/vrr.rs b/local/recipes/gpu/redox-drm/source/src/drivers/intel/vrr.rs new file mode 100644 index 0000000000..814585c2db --- /dev/null +++ b/local/recipes/gpu/redox-drm/source/src/drivers/intel/vrr.rs @@ -0,0 +1,81 @@ +use std::sync::Arc; + +use log::{debug, info}; +use redox_driver_sys::memory::MmioRegion; + +use super::info::IntelDeviceInfo; +use crate::driver::Result; + +const VRR_CTL_BASE: usize = 0x60420; +const VRR_CTL_ENABLE: u32 = 1 << 31; +const VRR_CTL_FLIP_LINE_SHIFT: u32 = 0; +const VRR_CTL_FLIP_LINE_MASK: u32 = 0x1FFF; +const VRR_MAX_FRAME_TIME: u32 = 0x60424; +const VRR_MIN_FRAME_TIME: u32 = 0x60428; +const VRR_STATUS_BASE: usize = 0x60440; +const VRR_TRANS_STRIDE: usize = 0x1000; + +const VRR_DEFAULT_FLIP_LINE: u32 = 0x400; + +pub struct VrrState { + mmio: Arc, + enabled: bool, + transcoder: u8, + min_vtotal: u16, + max_vtotal: u16, +} + +impl VrrState { + pub fn new(mmio: Arc, _device_info: &IntelDeviceInfo, transcoder: u8) -> Self { + Self { + mmio, + enabled: false, + transcoder, + min_vtotal: 0, + max_vtotal: 0, + } + } + + pub fn init(&mut self, min_vtotal: u16, max_vtotal: u16) { + self.min_vtotal = min_vtotal; + self.max_vtotal = max_vtotal; + } + + pub fn enable(&mut self) -> Result<()> { + if self.enabled || self.max_vtotal == 0 { + return Ok(()); + } + + let base = VRR_CTL_BASE + self.transcoder as usize * VRR_TRANS_STRIDE; + let val = VRR_CTL_ENABLE + | ((VRR_DEFAULT_FLIP_LINE & VRR_CTL_FLIP_LINE_MASK) << VRR_CTL_FLIP_LINE_SHIFT); + + self.mmio.write32(base, val); + self.mmio.write32(base + VRR_MAX_FRAME_TIME, self.max_vtotal as u32); + self.mmio.write32(base + VRR_MIN_FRAME_TIME, self.min_vtotal as u32); + + self.enabled = true; + info!("redox-drm-intel: VRR enabled on transcoder {} (vtotal {}-{})", + self.transcoder, self.min_vtotal, self.max_vtotal); + Ok(()) + } + + pub fn disable(&mut self) -> Result<()> { + if !self.enabled { + return Ok(()); + } + let base = VRR_CTL_BASE + self.transcoder as usize * VRR_TRANS_STRIDE; + self.mmio.write32(base, 0); + self.enabled = false; + info!("redox-drm-intel: VRR disabled on transcoder {}", self.transcoder); + Ok(()) + } + + pub fn is_enabled(&self) -> bool { + self.enabled + } + + pub fn status(&self) -> u32 { + self.mmio.read32(VRR_STATUS_BASE + self.transcoder as usize * VRR_TRANS_STRIDE) + } +}