Files
RedBear-OS/recipes/drivers/graphics/ihdgd/src/device/ddi.rs
T
vasilito b9874d0941 feat: USB storage read/write proof + full Red Bear OS tree sync
Add redbear-usb-storage-check in-guest binary that validates USB mass
storage read and write I/O: discovers /scheme/disk/ devices, writes a
test pattern to sector 2048, reads it back, verifies match, restores
original content. Updates test-usb-storage-qemu.sh with write-proof
verification step.

Includes all accumulated Red Bear OS work: kernel patches, relibc
patches, driver infrastructure, DRM/GPU, KDE recipes, firmware,
validation tooling, build system hardening, and documentation.
2026-05-03 23:03:24 +01:00

759 lines
28 KiB
Rust

use common::io::{Io, MmioPtr, WriteOnly};
use common::timeout::Timeout;
use embedded_hal::prelude::*;
use std::sync::Arc;
use syscall::error::{Error, Result, EIO};
use crate::device::aux::Aux;
use crate::device::power::PowerWells;
use crate::device::{CallbackGuard, Gmbus};
use super::{GpioPort, MmioRegion};
// IHD-OS-TGL-Vol 2c-12.21 DDI_AUX_CTL
pub const DDI_AUX_CTL_BUSY: u32 = 1 << 31;
pub const DDI_AUX_CTL_DONE: u32 = 1 << 30;
pub const DDI_AUX_CTL_TIMEOUT_ERROR: u32 = 1 << 28;
pub const DDI_AUX_CTL_TIMEOUT_SHIFT: u32 = 26;
pub const DDI_AUX_CTL_TIMEOUT_MASK: u32 = 0b11 << DDI_AUX_CTL_TIMEOUT_SHIFT;
pub const DDI_AUX_CTL_TIMEOUT_4000US: u32 = 0b11 << DDI_AUX_CTL_TIMEOUT_SHIFT;
pub const DDI_AUX_CTL_RECEIVE_ERROR: u32 = 1 << 25;
pub const DDI_AUX_CTL_SIZE_SHIFT: u32 = 20;
pub const DDI_AUX_CTL_SIZE_MASK: u32 = 0b11111 << 20;
pub const DDI_AUX_CTL_IO_SELECT: u32 = 1 << 11;
// IHD-OS-TGL-Vol 2c-12.21 DDI_BUF_CTL
pub const DDI_BUF_CTL_ENABLE: u32 = 1 << 31;
pub const DDI_BUF_CTL_IDLE: u32 = 1 << 7;
// IHD-OS-TGL-Vol 2c-12.21 PORT_CL_DW5
pub const PORT_CL_DW5_SUS_CLOCK_MASK: u32 = 0b11 << 0;
// IHD-OS-TGL-Vol 2c-12.21 PORT_CL_DW10
pub const PORT_CL_DW10_EDP4K2K_MODE_OVRD_EN: u32 = 1 << 3;
pub const PORT_CL_DW10_EDP4K2K_MODE_OVRD_VAL: u32 = 1 << 2;
// IHD-OS-TGL-Vol 2c-12.21 PORT_PCS_DW9
pub const PORT_PCS_DW1_CMNKEEPER_ENABLE: u32 = 1 << 26;
// IHD-OS-TGL-Vol 2c-12.21 PORT_TX_DW2
pub const PORT_TX_DW2_SWING_SEL_UPPER_SHIFT: u32 = 15;
pub const PORT_TX_DW2_SWING_SEL_UPPER_MASK: u32 = 1 << PORT_TX_DW2_SWING_SEL_UPPER_SHIFT;
pub const PORT_TX_DW2_SWING_SEL_LOWER_SHIFT: u32 = 11;
pub const PORT_TX_DW2_SWING_SEL_LOWER_MASK: u32 = 0b111 << PORT_TX_DW2_SWING_SEL_LOWER_SHIFT;
pub const PORT_TX_DW2_RCOMP_SCALAR_SHIFT: u32 = 0;
pub const PORT_TX_DW2_RCOMP_SCALAR_MASK: u32 = 0xFF << PORT_TX_DW2_RCOMP_SCALAR_SHIFT;
// IHD-OS-TGL-Vol 2c-12.21 PORT_TX_DW4
pub const PORT_TX_DW4_SELECT: u32 = 1 << 31;
pub const PORT_TX_DW4_POST_CURSOR_1_SHIFT: u32 = 12;
pub const PORT_TX_DW4_POST_CURSOR_1_MASK: u32 = 0b111111 << PORT_TX_DW4_POST_CURSOR_1_SHIFT;
pub const PORT_TX_DW4_POST_CURSOR_2_SHIFT: u32 = 6;
pub const PORT_TX_DW4_POST_CURSOR_2_MASK: u32 = 0b111111 << PORT_TX_DW4_POST_CURSOR_2_SHIFT;
pub const PORT_TX_DW4_CURSOR_COEFF_SHIFT: u32 = 0;
pub const PORT_TX_DW4_CURSOR_COEFF_MASK: u32 = 0b111111 << PORT_TX_DW4_CURSOR_COEFF_SHIFT;
// IHD-OS-TGL-Vol 2c-12.21 PORT_TX_DW5
pub const PORT_TX_DW5_TRAINING_ENABLE: u32 = 1 << 31;
pub const PORT_TX_DW5_DISABLE_2_TAP_SHIFT: u32 = 29;
pub const PORT_TX_DW5_DISABLE_2_TAP: u32 = 1 << PORT_TX_DW5_DISABLE_2_TAP_SHIFT;
pub const PORT_TX_DW5_DISABLE_3_TAP: u32 = 1 << 29;
pub const PORT_TX_DW5_CURSOR_PROGRAM: u32 = 1 << 26;
pub const PORT_TX_DW5_COEFF_POLARITY: u32 = 1 << 25;
pub const PORT_TX_DW5_SCALING_MODE_SEL_SHIFT: u32 = 18;
pub const PORT_TX_DW5_SCALING_MODE_SEL_MASK: u32 = 0b111 << PORT_TX_DW5_SCALING_MODE_SEL_SHIFT;
pub const PORT_TX_DW5_RTERM_SELECT_SHIFT: u32 = 3;
pub const PORT_TX_DW5_RTERM_SELECT_MASK: u32 = 0b111 << PORT_TX_DW5_RTERM_SELECT_SHIFT;
// IHD-OS-TGL-Vol 2c-12.21 PORT_TX_DW7
pub const PORT_TX_DW7_N_SCALAR_SHIFT: u32 = 24;
#[derive(Clone, Copy, Debug)]
#[repr(usize)]
pub enum PortClReg {
Dw5 = 0x14,
Dw10 = 0x28,
Dw12 = 0x30,
Dw15 = 0x3C,
Dw16 = 0x40,
}
#[derive(Clone, Copy, Debug)]
#[repr(usize)]
pub enum PortCompReg {
Dw0 = 0x100,
Dw1 = 0x104,
Dw3 = 0x10C,
Dw8 = 0x120,
Dw9 = 0x124,
Dw10 = 0x128,
}
#[derive(Clone, Copy, Debug)]
#[repr(usize)]
pub enum PortPcsReg {
Dw1 = 0x04,
Dw9 = 0x24,
}
#[derive(Clone, Copy, Debug)]
#[repr(usize)]
pub enum PortTxReg {
Dw0 = 0x80,
Dw1 = 0x84,
Dw2 = 0x88,
Dw4 = 0x90,
Dw5 = 0x94,
Dw6 = 0x98,
Dw7 = 0x9C,
Dw8 = 0xA0,
}
#[derive(Clone, Copy, Debug)]
#[repr(usize)]
pub enum PortLane {
Aux = 0x300,
Grp = 0x600,
Ln0 = 0x800,
Ln1 = 0x900,
Ln2 = 0xA00,
Ln3 = 0xB00,
}
pub struct Ddi {
pub name: &'static str,
pub index: usize,
pub gttmm: Arc<MmioRegion>,
pub port_base: Option<usize>,
pub aux_ctl: MmioPtr<u32>,
pub aux_datas: [MmioPtr<u32>; 5],
pub buf_ctl: MmioPtr<u32>,
pub dpclka_cfgcr0_clock_shift: Option<u32>,
pub dpclka_cfgcr0_clock_off: Option<u32>,
pub gmbus_pin_pair: Option<u8>,
pub gpio_port: Option<GpioPort>,
pub pwr_well_ctl_aux_request: u32,
pub pwr_well_ctl_aux_state: u32,
pub pwr_well_ctl_ddi_request: u32,
pub pwr_well_ctl_ddi_state: u32,
pub sde_interrupt_hotplug: Option<u32>,
pub transcoder_index: Option<u32>,
}
//TODO: verify offsets and count using DeviceKind?
impl Ddi {
pub fn dump(&self) {
eprint!("Ddi {} {}", self.name, self.index);
eprint!(" buf_ctl {:08X}", self.buf_ctl.read());
let lanes = [PortLane::Ln0, PortLane::Ln1, PortLane::Ln2, PortLane::Ln3];
for reg in [
PortClReg::Dw5,
PortClReg::Dw10,
PortClReg::Dw12,
PortClReg::Dw15,
PortClReg::Dw16,
] {
if let Some(mmio) = self.port_cl(reg) {
eprint!(" CL_{:?} {:08X}", reg, mmio.read());
}
}
for reg in [PortPcsReg::Dw1, PortPcsReg::Dw9] {
for lane in lanes {
if let Some(mmio) = self.port_pcs(reg, lane) {
eprint!(" PCS_{:?}_{:?} {:08X}", reg, lane, mmio.read());
}
}
}
for reg in [
PortTxReg::Dw0,
PortTxReg::Dw1,
PortTxReg::Dw2,
PortTxReg::Dw4,
PortTxReg::Dw5,
PortTxReg::Dw6,
PortTxReg::Dw7,
PortTxReg::Dw8,
] {
for lane in lanes {
if let Some(mmio) = self.port_tx(reg, lane) {
eprint!(" TX_{:?}_{:?} {:08X}", reg, lane, mmio.read());
}
}
}
eprintln!();
}
fn port_reg(&self, offset: usize) -> Option<MmioPtr<u32>> {
//TODO: handle gttmm.mmio error?
unsafe { self.gttmm.mmio(self.port_base? + offset).ok() }
}
pub fn port_cl(&self, reg: PortClReg) -> Option<MmioPtr<u32>> {
self.port_reg(reg as usize)
}
pub fn port_comp(&self, reg: PortCompReg) -> Option<MmioPtr<u32>> {
self.port_reg(reg as usize)
}
//TODO: return WriteOnly if PortLane::Grp?
pub fn port_pcs(&self, reg: PortPcsReg, lane: PortLane) -> Option<MmioPtr<u32>> {
self.port_reg((reg as usize) + (lane as usize))
}
//TODO: return WriteOnly if PortLane::Grp?
pub fn port_tx(&self, reg: PortTxReg, lane: PortLane) -> Option<MmioPtr<u32>> {
self.port_reg((reg as usize) + (lane as usize))
}
pub fn probe_edid(
&mut self,
power_wells: &mut PowerWells,
gttmm: &MmioRegion,
gmbus: &mut Gmbus,
) -> Result<Option<(&'static str, [u8; 128])>, Error> {
if let Some(port_comp_dw0) = self.port_comp(PortCompReg::Dw0) {
log::debug!("PORT_COMP_DW0_{}: {:08X}", self.name, port_comp_dw0.read());
}
let mut aux_read_edid = |ddi: &mut Ddi| -> Result<[u8; 128]> {
//TODO: BLOCK TCCOLD?
//TODO: the request can be shared by multiple DDIs
let pwr_well_ctl_aux_request = ddi.pwr_well_ctl_aux_request;
let pwr_well_ctl_aux_state = ddi.pwr_well_ctl_aux_state;
let mut pwr_well_ctl_aux = unsafe { MmioPtr::new(power_wells.ctl_aux.as_mut_ptr()) };
let _pwr_guard = CallbackGuard::new(
&mut pwr_well_ctl_aux,
|pwr_well_ctl_aux| {
// Enable aux power
pwr_well_ctl_aux.writef(pwr_well_ctl_aux_request, true);
let timeout = Timeout::from_micros(1500);
while !pwr_well_ctl_aux.readf(pwr_well_ctl_aux_state) {
timeout.run().map_err(|()| {
log::debug!("timeout while requesting DDI {} aux power", ddi.name);
Error::new(EIO)
})?;
}
Ok(())
},
|pwr_well_ctl_aux| {
// Disable aux power
pwr_well_ctl_aux.writef(pwr_well_ctl_aux_request, false);
},
)?;
let mut edid_data = [0; 128];
Aux::new(ddi)
.write_read(0x50, &[0x00], &mut edid_data)
.map_err(|_err| Error::new(EIO))?;
Ok(edid_data)
};
let mut gmbus_read_edid = |ddi: &mut Ddi| -> Result<[u8; 128]> {
let Some(pin_pair) = ddi.gmbus_pin_pair else {
return Err(Error::new(EIO));
};
let mut edid_data = [0; 128];
gmbus
.pin_pair(pin_pair)
.write_read(0x50, &[0x00], &mut edid_data)
.map_err(|_err| Error::new(EIO))?;
Ok(edid_data)
};
let gpio_read_edid = |ddi: &mut Ddi| -> Result<[u8; 128]> {
let Some(port) = &ddi.gpio_port else {
return Err(Error::new(EIO));
};
let mut edid_data = [0; 128];
unsafe { port.i2c(gttmm)? }
.write_read(0x50, &[0x00], &mut edid_data)
.map_err(|_err| Error::new(EIO))?;
Ok(edid_data)
};
match aux_read_edid(self) {
Ok(edid_data) => return Ok(Some(("AUX", edid_data))),
Err(err) => {
log::debug!("DDI {} failed to read EDID from AUX: {}", self.name, err);
}
}
match gmbus_read_edid(self) {
Ok(edid_data) => return Ok(Some(("GMBUS", edid_data))),
Err(err) => {
log::debug!("DDI {} failed to read EDID from GMBUS: {}", self.name, err);
}
}
match gpio_read_edid(self) {
Ok(edid_data) => return Ok(Some(("GPIO", edid_data))),
Err(err) => {
log::debug!("DDI {} failed to read EDID from GPIO: {}", self.name, err);
}
}
// Will try again but not fail the driver
Ok(None)
}
pub fn voltage_swing_hdmi(
&mut self,
gttmm: &MmioRegion,
timing: &edid::DetailedTiming,
) -> Result<()> {
struct Setting {
dw2_swing_sel: u32,
dw7_n_scalar: u32,
dw4_cursor_coeff: u32,
dw4_post_cursor_1: u32,
dw5_2_tap_disable: u32,
}
impl Setting {
pub fn new(
dw2_swing_sel: u32,
dw7_n_scalar: u32,
dw4_cursor_coeff: u32,
dw4_post_cursor_1: u32,
dw5_2_tap_disable: u32,
) -> Self {
Self {
dw2_swing_sel,
dw7_n_scalar,
dw4_cursor_coeff,
dw4_post_cursor_1,
dw5_2_tap_disable,
}
}
}
// IHD-OS-TGL-Vol 12-1.22-Rev2.0 "Voltage Swing Programming"
let settings = vec![
// HDMI 450mV, 450mV, 0.0dB
Setting::new(0b1010, 0x60, 0x3F, 0x00, 0b0),
// HDMI 450mV, 650mV, 3.2dB
Setting::new(0b1011, 0x73, 0x36, 0x09, 0b0),
// HDMI 450mV, 850mV, 5.5dB
Setting::new(0b0110, 0x7F, 0x31, 0x0E, 0b0),
// HDMI 650mV, 650mV, 0.0dB
Setting::new(0b1011, 0x73, 0x3F, 0x00, 0b0),
// HDMI 650mV, 850mV, 2.3dB
Setting::new(0b0110, 0x7F, 0x37, 0x08, 0b0),
// HDMI 850mV, 850mV, 0.0dB
Setting::new(0b0110, 0x7F, 0x3F, 0x00, 0b0),
// HDMI 600mV, 850mV, 3.0dB
Setting::new(0b0110, 0x7F, 0x35, 0x0A, 0b0),
];
// Last setting is the default
//TODO: get correct setting index from BIOS
let setting = settings.last().unwrap();
// This allows unwraps on port functions below without panic
if self.port_base.is_none() {
log::error!("HDMI voltage swing procedure only implemented on combo DDI");
return Err(Error::new(EIO));
};
// Clear cmnkeeper_enable for HDMI
{
// It is not possible to read from GRP register, so use LN0 as template
let pcs_dw1_ln0 = self.port_pcs(PortPcsReg::Dw1, PortLane::Ln0).unwrap();
let mut pcs_dw1_grp =
WriteOnly::new(self.port_pcs(PortPcsReg::Dw1, PortLane::Grp).unwrap());
let mut v = pcs_dw1_ln0.read();
v &= !PORT_PCS_DW1_CMNKEEPER_ENABLE;
pcs_dw1_grp.write(v);
}
// Program loadgen select
//TODO: this assumes bit rate <= 6 GHz and 4 lanes enabled
{
let mut tx_dw4_ln0 = self.port_tx(PortTxReg::Dw4, PortLane::Ln0).unwrap();
tx_dw4_ln0.writef(PORT_TX_DW4_SELECT, false);
let mut tx_dw4_ln1 = self.port_tx(PortTxReg::Dw4, PortLane::Ln1).unwrap();
tx_dw4_ln1.writef(PORT_TX_DW4_SELECT, true);
let mut tx_dw4_ln2 = self.port_tx(PortTxReg::Dw4, PortLane::Ln2).unwrap();
tx_dw4_ln2.writef(PORT_TX_DW4_SELECT, true);
let mut tx_dw4_ln3 = self.port_tx(PortTxReg::Dw4, PortLane::Ln3).unwrap();
tx_dw4_ln3.writef(PORT_TX_DW4_SELECT, true);
}
// Set PORT_CL_DW5 sus clock config to 11b
{
let mut cl_dw5 = self.port_cl(PortClReg::Dw5).unwrap();
cl_dw5.writef(PORT_CL_DW5_SUS_CLOCK_MASK, true);
}
// Clear training enable to change swing values
let tx_dw5_ln0 = self.port_tx(PortTxReg::Dw5, PortLane::Ln0).unwrap();
let mut tx_dw5_grp = WriteOnly::new(self.port_tx(PortTxReg::Dw5, PortLane::Grp).unwrap());
{
let mut v = tx_dw5_ln0.read();
v &= !PORT_TX_DW5_TRAINING_ENABLE;
tx_dw5_grp.write(v);
}
// Program swing and de-emphasis
// Disable eDP bits in PORT_CL_DW10
let mut cl_dw10 = self.port_cl(PortClReg::Dw10).unwrap();
cl_dw10.writef(
PORT_CL_DW10_EDP4K2K_MODE_OVRD_EN | PORT_CL_DW10_EDP4K2K_MODE_OVRD_VAL,
false,
);
// For PORT_TX_DW5:
// - Set 2 tap disable from settings
// - Set scaling mode sel to 010b
// - Set rterm select to 110b
// - Set 3 tap disable to 1
// - Set cursor program to 0
// - Set coeff polarity to 0
{
let mut v = tx_dw5_ln0.read();
v &= !(PORT_TX_DW5_DISABLE_2_TAP
| PORT_TX_DW5_CURSOR_PROGRAM
| PORT_TX_DW5_COEFF_POLARITY
| PORT_TX_DW5_SCALING_MODE_SEL_MASK
| PORT_TX_DW5_RTERM_SELECT_MASK);
v |= (setting.dw5_2_tap_disable << PORT_TX_DW5_DISABLE_2_TAP_SHIFT)
| PORT_TX_DW5_DISABLE_3_TAP
| (0b010 << PORT_TX_DW5_SCALING_MODE_SEL_SHIFT)
| (0b110 << PORT_TX_DW5_RTERM_SELECT_SHIFT);
tx_dw5_grp.write(v);
}
// Individual lane settings are used to avoid overwriting lane-specific settings, and because
// group registers cannot be read
let lanes = [PortLane::Ln0, PortLane::Ln1, PortLane::Ln2, PortLane::Ln3];
// For PORT_TX_DW2:
// - Set swing sel from settings
// - Set rcomp scalar to 0x98
for lane in lanes {
let mut tx_dw2 = self.port_tx(PortTxReg::Dw2, lane).unwrap();
let mut v = tx_dw2.read();
v &= !(PORT_TX_DW2_SWING_SEL_UPPER_MASK
| PORT_TX_DW2_SWING_SEL_LOWER_MASK
| PORT_TX_DW2_RCOMP_SCALAR_MASK);
v |= (((setting.dw2_swing_sel >> 3) & 1) << PORT_TX_DW2_SWING_SEL_UPPER_SHIFT)
| ((setting.dw2_swing_sel & 0b111) << PORT_TX_DW2_SWING_SEL_LOWER_SHIFT)
| (0x98 << PORT_TX_DW2_RCOMP_SCALAR_SHIFT);
tx_dw2.write(v);
}
// For PORT_TX_DW4:
// - Set post cursor 1 from settings
// - Set post cursor 2 to 0x0
// - Set cursor coeff from settings
for lane in lanes {
let mut tx_dw4 = self.port_tx(PortTxReg::Dw4, lane).unwrap();
let mut v = tx_dw4.read();
v &= !(PORT_TX_DW4_POST_CURSOR_1_MASK
| PORT_TX_DW4_POST_CURSOR_2_MASK
| PORT_TX_DW4_CURSOR_COEFF_MASK);
v |= (setting.dw4_post_cursor_1 << PORT_TX_DW4_POST_CURSOR_1_SHIFT)
| (setting.dw4_cursor_coeff << PORT_TX_DW4_CURSOR_COEFF_SHIFT);
tx_dw4.write(v);
}
// For PORT_TX_DW7:
// - Set n scalar from settings
for lane in lanes {
let mut tx_dw7 = self.port_tx(PortTxReg::Dw7, lane).unwrap();
// All other bits are spare
tx_dw7.write(setting.dw7_n_scalar << PORT_TX_DW7_N_SCALAR_SHIFT);
}
// Set training enable to trigger update
{
let mut v = tx_dw5_ln0.read();
v |= PORT_TX_DW5_TRAINING_ENABLE;
tx_dw5_grp.write(v);
}
Ok(())
}
pub fn kabylake(gttmm: &Arc<MmioRegion>) -> Result<Vec<Self>> {
let mut ddis = Vec::new();
for (i, name) in [
"A", "B", "C", "D",
//TODO: missing AUX regs? "E",
]
.iter()
.enumerate()
{
ddis.push(Self {
name,
index: i,
port_base: None, //TODO: port regs
gttmm: gttmm.clone(),
// IHD-OS-KBL-Vol 2c-1.17 DDI_AUX_CTL
aux_ctl: unsafe { gttmm.mmio(0x64010 + i * 0x100)? },
// IHD-OS-KBL-Vol 2c-1.17 DDI_AUX_DATA
aux_datas: [
unsafe { gttmm.mmio(0x64014 + i * 0x100)? },
unsafe { gttmm.mmio(0x64018 + i * 0x100)? },
unsafe { gttmm.mmio(0x6401C + i * 0x100)? },
unsafe { gttmm.mmio(0x64020 + i * 0x100)? },
unsafe { gttmm.mmio(0x64024 + i * 0x100)? },
],
// IHD-OS-KBL-Vol 2c-1.17 DDI_BUF_CTL
buf_ctl: unsafe { gttmm.mmio(0x64000 + i * 0x100)? },
// N/A
dpclka_cfgcr0_clock_shift: None,
dpclka_cfgcr0_clock_off: None,
// IHD-OS-KBL-Vol 2c-1.17 GMBUS0
gmbus_pin_pair: match *name {
"B" => Some(0b101),
"C" => Some(0b100),
"D" => Some(0b110),
_ => None,
},
// IHD-OS-KBL-Vol 12-1.17 GMBUS and GPIO
gpio_port: match *name {
"B" => Some(GpioPort::Port4),
"C" => Some(GpioPort::Port3),
"D" => Some(GpioPort::Port5),
_ => None,
},
// IHD-OS-KBL-Vol 2c-1.17 PWR_WELL_CTL
// All auxes go through the same Misc IO request
pwr_well_ctl_aux_request: 1 << 1,
pwr_well_ctl_aux_state: 1 << 0,
pwr_well_ctl_ddi_request: match *name {
"A" | "E" => 1 << 3,
"B" => 1 << 5,
"C" => 1 << 7,
"D" => 1 << 9,
_ => unreachable!(),
},
pwr_well_ctl_ddi_state: match *name {
"A" | "E" => 1 << 2,
"B" => 1 << 4,
"C" => 1 << 6,
"D" => 1 << 8,
_ => unreachable!(),
},
// IHD-OS-KBL-Vol 2c-1.17 SDE_INTERRUPT
sde_interrupt_hotplug: match *name {
"A" => Some(1 << 24),
"B" => Some(1 << 21),
"C" => Some(1 << 22),
"D" => Some(1 << 23),
"E" => Some(1 << 25),
_ => None,
},
// IHD-OS-KBL-Vol 2c-1.17 TRANS_CLK_SEL
transcoder_index: match *name {
"B" => Some(0b010),
"C" => Some(0b011),
"D" => Some(0b100),
"E" => Some(0b101),
_ => None,
},
});
}
Ok(ddis)
}
pub fn tigerlake(gttmm: &Arc<MmioRegion>) -> Result<Vec<Self>> {
let mut ddis = Vec::new();
for (i, name) in [
"A", "B", "C", "USBC1", "USBC2", "USBC3", "USBC4", "USBC5", "USBC6",
]
.iter()
.enumerate()
{
let port_base = match i {
0 => Some(0x162000),
1 => Some(0x6C000),
2 => Some(0x160000),
_ => None,
};
ddis.push(Self {
name,
index: i,
port_base,
gttmm: gttmm.clone(),
// IHD-OS-TGL-Vol 2c-12.21 DDI_AUX_CTL
aux_ctl: unsafe { gttmm.mmio(0x64010 + i * 0x100)? },
// IHD-OS-TGL-Vol 2c-12.21 DDI_AUX_DATA
aux_datas: [
unsafe { gttmm.mmio(0x64014 + i * 0x100)? },
unsafe { gttmm.mmio(0x64018 + i * 0x100)? },
unsafe { gttmm.mmio(0x6401C + i * 0x100)? },
unsafe { gttmm.mmio(0x64020 + i * 0x100)? },
unsafe { gttmm.mmio(0x64024 + i * 0x100)? },
],
// IHD-OS-TGL-Vol 2c-12.21 DDI_BUF_CTL
buf_ctl: unsafe { gttmm.mmio(0x64000 + i * 0x100)? },
// IHD-OS-TGL-Vol 2c-12.21 DPCLKA_CFGCR0
dpclka_cfgcr0_clock_shift: match i {
0 => Some(0),
1 => Some(2),
2 => Some(4),
_ => None,
},
dpclka_cfgcr0_clock_off: match i {
// DDI
0 => Some(1 << 10),
1 => Some(1 << 11),
2 => Some(1 << 24),
// Type C
3 => Some(1 << 12),
4 => Some(1 << 13),
5 => Some(1 << 14),
6 => Some(1 << 21),
7 => Some(1 << 22),
8 => Some(1 << 23),
_ => None,
},
//TODO: link to docs
gmbus_pin_pair: match i {
// DDI pins
0 => Some(1),
1 => Some(2),
2 => Some(3),
// Type C pins
3 => Some(9),
4 => Some(10),
5 => Some(11),
6 => Some(12),
7 => Some(13),
8 => Some(14),
_ => None,
},
// IHD-OS-TGL-Vol 12-1.22-Rev2.0 GMBUS and GPIO
gpio_port: match *name {
"A" => Some(GpioPort::Port1),
"B" => Some(GpioPort::Port2),
"C" => Some(GpioPort::Port3),
"USBC1" => Some(GpioPort::Port9),
"USBC2" => Some(GpioPort::Port10),
"USBC3" => Some(GpioPort::Port11),
"USBC4" => Some(GpioPort::Port12),
"USBC5" => Some(GpioPort::Port13),
"USBC6" => Some(GpioPort::Port14),
_ => None,
},
// IHD-OS-TGL-Vol 2c-12.21 PWR_WELL_CTL_AUX
pwr_well_ctl_aux_request: 2 << (i * 2),
pwr_well_ctl_aux_state: 1 << (i * 2),
// IHD-OS-TGL-Vol 2c-12.21 PWR_WELL_CTL_DDI
pwr_well_ctl_ddi_request: 2 << (i * 2),
pwr_well_ctl_ddi_state: 1 << (i * 2),
// IHD-OS-TGL-Vol 2c-12.21 SDE_INTERRUPT
sde_interrupt_hotplug: match i {
0 => Some(1 << 16),
1 => Some(1 << 17),
2 => Some(1 << 18),
_ => None,
},
// IHD-OS-TGL-Vol 2c-12.21 TRANS_CLK_SEL
transcoder_index: Some((i + 1) as u32),
})
}
Ok(ddis)
}
pub fn alchemist(gttmm: &Arc<MmioRegion>) -> Result<Vec<Self>> {
let mut ddis = Vec::new();
for (i, name) in ["A", "B", "C", "USBC1", "USBC2", "USBC3", "USBC4", "D", "E"]
.iter()
.enumerate()
{
let port_base = match i {
0 => Some(0x162000),
1 => Some(0x6C000),
2 => Some(0x160000),
_ => None,
};
ddis.push(Self {
name,
index: i,
port_base,
gttmm: gttmm.clone(),
// IHD-OS-ACM-Vol 2c-3.23 DDI_AUX_CTL
aux_ctl: unsafe { gttmm.mmio(0x64010 + i * 0x100)? },
// IHD-OS-ACM-Vol 2c-3.23 DDI_AUX_DATA
aux_datas: [
unsafe { gttmm.mmio(0x64014 + i * 0x100)? },
unsafe { gttmm.mmio(0x64018 + i * 0x100)? },
unsafe { gttmm.mmio(0x6401C + i * 0x100)? },
unsafe { gttmm.mmio(0x64020 + i * 0x100)? },
unsafe { gttmm.mmio(0x64024 + i * 0x100)? },
],
// IHD-OS-ACM-Vol 2c-3.23 DDI_BUF_CTL
buf_ctl: unsafe { gttmm.mmio(0x64000 + i * 0x100)? },
// IHD-OS-ACM-Vol 2c-3.23 DPCLKA_CFGCR0
dpclka_cfgcr0_clock_shift: match i {
0 => Some(0),
1 => Some(2),
2 => Some(4),
_ => None,
},
dpclka_cfgcr0_clock_off: match i {
// DDI
0 => Some(1 << 10),
1 => Some(1 << 11),
2 => Some(1 << 24),
// Type C
3 => Some(1 << 12),
4 => Some(1 << 13),
5 => Some(1 << 14),
6 => Some(1 << 21),
7 => Some(1 << 22),
8 => Some(1 << 23),
_ => None,
},
//TODO: link to docs
gmbus_pin_pair: match i {
// DDI pins
0 => Some(1),
1 => Some(2),
2 => Some(3),
// Type C pins
3 => Some(9),
4 => Some(10),
5 => Some(11),
6 => Some(12),
7 => Some(13),
8 => Some(14),
_ => None,
},
// IHD-OS-ACM-Vol 12-3.23 GMBUS and GPIO
gpio_port: match *name {
"A" => Some(GpioPort::Port1),
"B" => Some(GpioPort::Port2),
"C" => Some(GpioPort::Port3),
"D" => Some(GpioPort::Port4),
"USBC1" => Some(GpioPort::Port9),
_ => None,
},
// IHD-OS-ACM-Vol 2c-3.23 PWR_WELL_CTL_AUX
pwr_well_ctl_aux_request: 2 << (i * 2),
pwr_well_ctl_aux_state: 1 << (i * 2),
// IHD-OS-ACM-Vol 2c-3.23 PWR_WELL_CTL_DDI
pwr_well_ctl_ddi_request: 2 << (i * 2),
pwr_well_ctl_ddi_state: 1 << (i * 2),
// IHD-OS-ACM-Vol 2c-3.23 SDE_INTERRUPT
sde_interrupt_hotplug: match i {
0 => Some(1 << 16),
1 => Some(1 << 17),
2 => Some(1 << 18),
_ => None,
},
// IHD-OS-ACM-Vol 2c-3.23 TRANS_CLK_SEL
transcoder_index: Some((i + 1) as u32),
})
}
Ok(ddis)
}
}