diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/intel/audio_eld.rs b/local/recipes/gpu/redox-drm/source/src/drivers/intel/audio_eld.rs new file mode 100644 index 0000000000..756b04dcfa --- /dev/null +++ b/local/recipes/gpu/redox-drm/source/src/drivers/intel/audio_eld.rs @@ -0,0 +1,114 @@ +use std::collections::HashMap; + +pub struct EldData { + pub eld: [u8; 128], + pub eld_size: usize, + pub speaker_allocation: u8, + pub sad_count: u8, + pub baseline_len: u8, +} + +impl EldData { + pub fn new(edid: &[u8]) -> Self { + let mut eld = [0u8; 128]; + let mut size = 0usize; + + if edid.len() >= 256 { + let mut offset = 128u16; + while offset < (edid.len() as u16).min(256) && offset + 3 <= edid.len() as u16 { + let tag = edid[offset as usize] >> 5; + let len = (edid[offset as usize] & 0x1F) as u16; + + if tag == 0x02 { + let data = &edid[offset as usize + 1..]; + let mut dtd_offset = 0u16; + while dtd_offset < len { + let dtd_tag = data[dtd_offset as usize] >> 5; + let dtd_len = (data[dtd_offset as usize] & 0x1F) as u16; + if dtd_tag == 0x07 && dtd_len >= 3 { + let oui = ((data[dtd_offset as usize + 1] as u32) << 16) + | ((data[dtd_offset as usize + 2] as u32) << 8) + | (data[dtd_offset as usize + 3] as u32); + if oui == 0x000C03 { + let sad_start = dtd_offset + 4; + let sad_end = (sad_start + dtd_len - 3).min(128); + let copy_len = sad_end - sad_start; + eld[..copy_len].copy_from_slice( + &data[sad_start as usize..sad_start as usize + copy_len]); + size = copy_len; + } + } + dtd_offset += dtd_len + 1; + } + break; + } + offset += len + 1; + } + } + + Self { + eld, eld_size: size, + speaker_allocation: 0x01, + sad_count: 0, + baseline_len: 8, + } + } + + pub fn speaker_allocation_str(&self) -> &str { + match self.speaker_allocation { + 0x01 => "FL/FR", + 0x02 => "LFE", + 0x04 => "FC", + 0x09 => "FL/FR + LFE", + 0x0B => "FL/FR + LFE + FC", + 0x13 => "FL/FR + LFE + FC + SL/SR", + 0x1F => "FL/FR + LFE + FC + SL/SR + RLC/RRC", + _ => "Stereo", + } + } + + pub fn compute_n_cts(pixel_clock: u32, sample_rate: u32) -> (u32, u32) { + match sample_rate { + 32000 => { + let n = 4096u32; + let cts = pixel_clock * n / (128 * sample_rate); + (n, cts) + } + 44100 => { + let n = 6272u32; + let cts = pixel_clock * n / (128 * sample_rate); + (n, cts) + } + 48000 => { + let n = 6144u32; + let cts = pixel_clock * n / (128 * sample_rate); + (n, cts) + } + 88200 => { + let n = 12544u32; + let cts = pixel_clock * n / (128 * sample_rate); + (n, cts) + } + 96000 => { + let n = 12288u32; + let cts = pixel_clock * n / (128 * sample_rate); + (n, cts) + } + 176400 => { + let n = 25088u32; + let cts = pixel_clock * n / (128 * sample_rate); + (n, cts) + } + 192000 => { + let n = 24576u32; + let cts = pixel_clock * n / (128 * sample_rate); + (n, cts) + } + _ => (6144, pixel_clock * 6144 / (128 * 48000)), + } + } + + pub fn is_valid(&self) -> bool { + self.eld_size > 0 && self.eld[0] != 0 + } +} diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/intel/gpu_reset.rs b/local/recipes/gpu/redox-drm/source/src/drivers/intel/gpu_reset.rs new file mode 100644 index 0000000000..8c805c7bb4 --- /dev/null +++ b/local/recipes/gpu/redox-drm/source/src/drivers/intel/gpu_reset.rs @@ -0,0 +1,123 @@ +use std::sync::Arc; +use std::time::{Duration, Instant}; + +use log::{debug, error, info, warn}; +use redox_driver_sys::memory::MmioRegion; + +use crate::driver::{DriverError, Result}; + +const GEN6_GDRST: usize = 0x941C; +const GEN6_GRDOM_RENDER: u32 = 1 << 1; +const GEN6_GRDOM_MEDIA: u32 = 1 << 2; +const GEN6_GRDOM_BLT: u32 = 1 << 3; +const GEN6_GRDOM_VECS: u32 = 1 << 4; +const GEN6_GRDOM_FULL: u32 = 1 << 0; +const GEN6_GRDOM_GUC: u32 = 1 << 5; + +const RING_RESET_CTL: usize = 0xD0; +const RESET_CTL_REQUEST_RESET: u32 = 1 << 0; +const RESET_CTL_READY_TO_RESET: u32 = 1 << 1; + +const RESET_TIMEOUT_MS: u64 = 50; +const RECOVERY_TIMEOUT_MS: u64 = 200; + +pub struct GpuResetManager { + mmio: Arc, + render_ring_base: usize, + reset_count: u32, + recovery_successful: u32, +} + +impl GpuResetManager { + pub fn new(mmio: Arc, render_ring_base: usize) -> Self { + Self { + mmio, render_ring_base, + reset_count: 0, + recovery_successful: 0, + } + } + + pub fn reset_engine(&mut self, ring_base: usize) -> Result<()> { + self.reset_count += 1; + info!("redox-drm-intel: engine reset requested for ring {:#06x} (attempt {})", + ring_base, self.reset_count); + + let reset_ctl = ring_base + RING_RESET_CTL; + self.mmio.write32(reset_ctl, RESET_CTL_REQUEST_RESET); + + let deadline = Instant::now() + Duration::from_millis(RESET_TIMEOUT_MS); + loop { + let status = self.mmio.read32(reset_ctl); + if status & RESET_CTL_READY_TO_RESET != 0 { break; } + if Instant::now() > deadline { + error!("redox-drm-intel: engine reset timeout for ring {:#06x}", ring_base); + return Err(DriverError::Initialization("engine reset timeout")); + } + std::hint::spin_loop(); + } + + self.mmio.write32(reset_ctl, 0); + + let deadline = Instant::now() + Duration::from_millis(RECOVERY_TIMEOUT_MS); + loop { + let status = self.mmio.read32(reset_ctl); + if status & RESET_CTL_READY_TO_RESET == 0 { break; } + if Instant::now() > deadline { + warn!("redox-drm-intel: engine reset recovery timeout for ring {:#06x}", ring_base); + return Ok(()); + } + std::hint::spin_loop(); + } + + self.recovery_successful += 1; + info!("redox-drm-intel: engine reset completed for ring {:#06x}", ring_base); + Ok(()) + } + + pub fn reset_render(&mut self) -> Result<()> { + self.reset_engine(self.render_ring_base) + } + + pub fn reset_gpu(&mut self) -> Result<()> { + self.reset_count += 1; + error!("redox-drm-intel: GLOBAL GPU RESET (attempt {})", self.reset_count); + + let val = GEN6_GRDOM_FULL | GEN6_GRDOM_RENDER + | GEN6_GRDOM_MEDIA | GEN6_GRDOM_BLT | GEN6_GRDOM_VECS + | GEN6_GRDOM_GUC; + + self.mmio.write32(GEN6_GDRST, val); + + let deadline = Instant::now() + Duration::from_millis(RECOVERY_TIMEOUT_MS); + loop { + let status = self.mmio.read32(GEN6_GDRST); + if status & val == 0 { break; } + if Instant::now() > deadline { + error!("redox-drm-intel: global GPU reset timeout — status {:#x}", status); + return Err(DriverError::Initialization("global GPU reset timeout")); + } + std::hint::spin_loop(); + } + + self.recovery_successful += 1; + info!("redox-drm-intel: global GPU reset completed"); + Ok(()) + } + + pub fn force_reset_domain(&mut self, domain: u32) -> Result<()> { + self.mmio.write32(GEN6_GDRST, domain); + + let deadline = Instant::now() + Duration::from_millis(RESET_TIMEOUT_MS); + loop { + if self.mmio.read32(GEN6_GDRST) & domain == 0 { break; } + if Instant::now() > deadline { + return Err(DriverError::Initialization("reset domain timeout")); + } + std::hint::spin_loop(); + } + Ok(()) + } + + pub fn reset_count(&self) -> u32 { self.reset_count } + pub fn recovery_count(&self) -> u32 { self.recovery_successful } +} diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/intel/rps_rc6.rs b/local/recipes/gpu/redox-drm/source/src/drivers/intel/rps_rc6.rs new file mode 100644 index 0000000000..d678d7a39c --- /dev/null +++ b/local/recipes/gpu/redox-drm/source/src/drivers/intel/rps_rc6.rs @@ -0,0 +1,163 @@ +use std::sync::Arc; +use std::time::Instant; + +use log::{debug, info}; +use redox_driver_sys::memory::MmioRegion; + +use super::info::IntelDeviceInfo; +use crate::driver::Result; + +const GEN6_RPNSWREQ: usize = 0xA008; +const GEN6_RP_CONTROL: usize = 0xA024; +const GEN6_RC_CONTROL: usize = 0xA090; +const GEN6_RC_STATE: usize = 0xA094; +const GEN6_RP_INTERRUPT_LIMITS: usize = 0xA014; +const GEN6_RP_DOWN_TIMEOUT: usize = 0xA018; +const GEN6_RP_UP_THRESHOLD: usize = 0xA02C; +const GEN6_RP_DOWN_THRESHOLD: usize = 0xA030; +const GEN6_RP_DOWN_EI: usize = 0xA050; +const GEN6_RP_UP_EI: usize = 0xA054; + +const RPNSWREQ_FREQ_MASK: u32 = 0xFF; +const RC_CTL_RC6_ENABLE: u32 = 1 << 0; +const RC_CTL_HW_ENABLE: u32 = 1 << 31; + +const RPS_UP_THRESHOLD_DEFAULT: u32 = 85; +const RPS_DOWN_THRESHOLD_DEFAULT: u32 = 65; +const RPS_DOWN_TIMEOUT_MS: u32 = 50; +const RPS_EI_INTERVAL_US: u32 = 1000; + +const FREQ_TABLE_GEN9: &[(u32, u32)] = &[ + (100, 1), (150, 2), (200, 3), (250, 4), (300, 5), + (350, 6), (400, 7), (450, 8), (500, 9), (550, 10), + (600, 11), (650, 12), (700, 13), (750, 14), (800, 15), + (850, 16), (900, 17), (950, 18), (1000, 19), (1050, 20), +]; + +const FREQ_TABLE_GEN12: &[(u32, u32)] = &[ + (100, 2), (150, 3), (200, 4), (250, 5), (300, 6), + (400, 8), (500, 10), (600, 12), (700, 14), (800, 16), + (900, 18), (1000, 20), (1100, 22), (1200, 24), (1300, 26), + (1400, 28), (1500, 30), +]; + +pub struct RpsRc6Manager { + mmio: Arc, + device_info: IntelDeviceInfo, + rps_enabled: bool, + rc6_enabled: bool, + current_freq_idx: usize, + min_freq_idx: usize, + max_freq_idx: usize, + last_up_eval: Option, + last_down_eval: Option, + up_threshold: u32, + down_threshold: u32, +} + +impl RpsRc6Manager { + pub fn new(mmio: Arc, device_info: &IntelDeviceInfo) -> Self { + Self { + mmio, device_info: device_info.clone(), + rps_enabled: false, rc6_enabled: false, + current_freq_idx: 0, min_freq_idx: 0, max_freq_idx: 0, + last_up_eval: None, last_down_eval: None, + up_threshold: RPS_UP_THRESHOLD_DEFAULT, + down_threshold: RPS_DOWN_THRESHOLD_DEFAULT, + } + } + + pub fn init(&mut self) -> Result<()> { + let table = self.freq_table(); + self.min_freq_idx = 0; + self.max_freq_idx = table.len() - 1; + self.current_freq_idx = self.max_freq_idx; + self.set_frequency(table[self.current_freq_idx].1)?; + self.rps_enabled = true; + + self.mmio.write32(GEN6_RP_INTERRUPT_LIMITS, + (table[self.max_freq_idx].1 << 24) | (table[self.min_freq_idx].1 << 16)); + self.mmio.write32(GEN6_RP_DOWN_TIMEOUT, RPS_DOWN_TIMEOUT_MS); + self.mmio.write32(GEN6_RP_UP_THRESHOLD, self.up_threshold); + self.mmio.write32(GEN6_RP_DOWN_THRESHOLD, self.down_threshold); + self.mmio.write32(GEN6_RP_DOWN_EI, RPS_EI_INTERVAL_US); + self.mmio.write32(GEN6_RP_UP_EI, RPS_EI_INTERVAL_US); + + info!("redox-drm-intel: RPS initialized ({} MHz, range {}-{} MHz)", + table[self.current_freq_idx].0, + table[self.min_freq_idx].0, + table[self.max_freq_idx].0); + Ok(()) + } + + pub fn enable_rc6(&mut self) -> Result<()> { + let mut ctl = self.mmio.read32(GEN6_RC_CONTROL); + ctl |= RC_CTL_RC6_ENABLE | RC_CTL_HW_ENABLE; + self.mmio.write32(GEN6_RC_CONTROL, ctl); + self.rc6_enabled = true; + info!("redox-drm-intel: RC6 enabled (HW-managed)"); + Ok(()) + } + + pub fn disable_rc6(&mut self) -> Result<()> { + let mut ctl = self.mmio.read32(GEN6_RC_CONTROL); + ctl &= !(RC_CTL_RC6_ENABLE | RC_CTL_HW_ENABLE); + self.mmio.write32(GEN6_RC_CONTROL, ctl); + self.rc6_enabled = false; + Ok(()) + } + + pub fn rps_up(&mut self) -> Result<()> { + if !self.rps_enabled { return Ok(()); } + if self.current_freq_idx >= self.max_freq_idx { return Ok(()); } + let now = Instant::now(); + if let Some(last) = self.last_up_eval { + if now.duration_since(last).as_micros() < RPS_EI_INTERVAL_US as u128 { return Ok(()); } + } + self.current_freq_idx += 1; + let table = self.freq_table(); + self.set_frequency(table[self.current_freq_idx].1)?; + self.last_up_eval = Some(now); + debug!("redox-drm-intel: RPS ↑ {} MHz", table[self.current_freq_idx].0); + Ok(()) + } + + pub fn rps_down(&mut self) -> Result<()> { + if !self.rps_enabled { return Ok(()); } + if self.current_freq_idx <= self.min_freq_idx { return Ok(()); } + let now = Instant::now(); + if let Some(last) = self.last_down_eval { + if now.duration_since(last).as_millis() < RPS_DOWN_TIMEOUT_MS as u128 { return Ok(()); } + } + self.current_freq_idx -= 1; + let table = self.freq_table(); + self.set_frequency(table[self.current_freq_idx].1)?; + self.last_down_eval = Some(now); + debug!("redox-drm-intel: RPS ↓ {} MHz", table[self.current_freq_idx].0); + Ok(()) + } + + fn set_frequency(&self, freq_val: u32) -> Result<()> { + let mut rpns = self.mmio.read32(GEN6_RPNSWREQ); + rpns &= !(RPNSWREQ_FREQ_MASK); + rpns |= freq_val & RPNSWREQ_FREQ_MASK; + self.mmio.write32(GEN6_RPNSWREQ, rpns); + Ok(()) + } + + pub fn current_frequency_mhz(&self) -> u32 { + self.freq_table().get(self.current_freq_idx) + .map(|&(mhz, _)| mhz).unwrap_or(0) + } + + fn freq_table(&self) -> &[(u32, u32)] { + if self.device_info.is_gen12_or_later() { + FREQ_TABLE_GEN12 + } else { + FREQ_TABLE_GEN9 + } + } + + pub fn rps_enabled(&self) -> bool { self.rps_enabled } + pub fn rc6_enabled(&self) -> bool { self.rc6_enabled } +}