intel: audio/ELD, RPS/RC6, GPU reset — final subsystems
audio_eld.rs: EDID-Like Data + N/CTS computation EldData parser from CEA-861 EDID extension block HDMI OUI (0x000C03) Short Audio Descriptor extraction Speaker allocation string mapping (stereo → 7.1) compute_n_cts for 7 sample rates (32/44.1/48/88.2/96/176.4/192 kHz) rps_rc6.rs: full Render Power State + RC6 management Gen9 freq table: 20 entries (100-1050 MHz) Gen12 freq table: 17 entries (100-1500 MHz) RPS up/down with evaluation interval + timeout gating RC6 enable/disable with HW-managed transitions Interrupt limits + up/down threshold programming gpu_reset.rs: per-engine + global GPU reset recovery reset_engine with RESET_CTL request → ready → clear sequence reset_render convenience wrapper reset_gpu with GEN6_GDRST full domain register Per-domain force_reset for render/media/blitter/vecs/GUC Reset count + recovery success counter tracking
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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<MmioRegion>,
|
||||
render_ring_base: usize,
|
||||
reset_count: u32,
|
||||
recovery_successful: u32,
|
||||
}
|
||||
|
||||
impl GpuResetManager {
|
||||
pub fn new(mmio: Arc<MmioRegion>, 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 }
|
||||
}
|
||||
@@ -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<MmioRegion>,
|
||||
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<Instant>,
|
||||
last_down_eval: Option<Instant>,
|
||||
up_threshold: u32,
|
||||
down_threshold: u32,
|
||||
}
|
||||
|
||||
impl RpsRc6Manager {
|
||||
pub fn new(mmio: Arc<MmioRegion>, 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 }
|
||||
}
|
||||
Reference in New Issue
Block a user