drivers/graphics/ihdgd: Move edid probing into ddi.rs

This commit is contained in:
bjorn3
2025-12-22 11:48:47 +01:00
parent ff3cb3ce64
commit a4a0fbd198
2 changed files with 116 additions and 109 deletions
+107
View File
@@ -1,7 +1,14 @@
use common::io::{Io, MmioPtr, WriteOnly};
use common::timeout::Timeout;
use embedded_hal::prelude::*;
use std::sync::Arc;
use std::time::Duration;
use syscall::error::{Error, Result, EIO};
use crate::device::aux::Aux;
use crate::device::power::PowerWells;
use crate::device::{CallbackGuard, Gmbus, HalTimer};
use super::{GpioPort, MmioRegion};
// IHD-OS-TGL-Vol 2c-12.21 DDI_AUX_CTL
@@ -200,6 +207,106 @@ impl Ddi {
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];
let i2c_freq = 100_000.0;
bitbang_hal::i2c::I2cBB::new(
unsafe { port.clock(gttmm)? },
unsafe { port.data(gttmm)? },
HalTimer::new(Duration::from_secs_f64(1.0 / i2c_freq)),
)
.write_read(0x50, &[0x00], &mut edid_data)
.map_err(|_err| Error::new(EIO))?;
Ok(edid_data)
};
let (source, edid_data) = match aux_read_edid(self) {
Ok(edid_data) => ("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) => ("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) => ("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
return Ok(None);
}
}
}
}
}
};
Ok(Some((source, edid_data)))
}
pub fn voltage_swing_hdmi(
&mut self,
gttmm: &MmioRegion,
+9 -109
View File
@@ -2,14 +2,12 @@ use common::{
io::{Io, MmioPtr},
timeout::Timeout,
};
use embedded_hal::prelude::*;
use pcid_interface::{PciFunction, PciFunctionHandle};
use range_alloc::RangeAllocator;
use std::{collections::VecDeque, fmt, mem, sync::Arc, time::Duration};
use std::{collections::VecDeque, fmt, mem, sync::Arc};
use syscall::error::{Error, Result, EIO, ENODEV, ERANGE};
mod aux;
use self::aux::*;
mod bios;
use self::bios::*;
mod ddi;
@@ -552,102 +550,10 @@ impl Device {
// Enable DDI power well
self.power_wells.enable_well_by_ddi(ddi.name)?;
//TODO: init port if needed
if let Some(port_comp_dw0) = ddi.port_comp(PortCompReg::Dw0) {
log::debug!("PORT_COMP_DW0_{}: {:08X}", ddi.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(self.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];
self.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];
let i2c_freq = 100_000.0;
bitbang_hal::i2c::I2cBB::new(
unsafe { port.clock(&self.gttmm)? },
unsafe { port.data(&self.gttmm)? },
HalTimer::new(Duration::from_secs_f64(1.0 / i2c_freq)),
)
.write_read(0x50, &[0x00], &mut edid_data)
.map_err(|_err| Error::new(EIO))?;
Ok(edid_data)
};
let (source, edid_data) = match aux_read_edid(ddi) {
Ok(edid_data) => ("AUX", edid_data),
Err(err) => {
log::debug!("DDI {} failed to read EDID from AUX: {}", ddi.name, err);
match gmbus_read_edid(ddi) {
Ok(edid_data) => ("GMBUS", edid_data),
Err(err) => {
log::debug!("DDI {} failed to read EDID from GMBUS: {}", ddi.name, err);
match gpio_read_edid(ddi) {
Ok(edid_data) => ("GPIO", edid_data),
Err(err) => {
log::debug!(
"DDI {} failed to read EDID from GPIO: {}",
ddi.name,
err
);
// Will try again but not fail the driver
return Ok(false);
}
}
}
}
}
let Some((source, edid_data)) =
ddi.probe_edid(&mut self.power_wells, &self.gttmm, &mut self.gmbus)?
else {
return Ok(false);
};
let edid = match edid::parse(&edid_data).to_full_result() {
@@ -667,16 +573,10 @@ impl Device {
}
};
let mut timing_opt = None;
for desc in edid.descriptors.iter() {
match desc {
edid::Descriptor::DetailedTiming(timing) => {
timing_opt = Some(timing);
break;
}
_ => {}
}
}
let timing_opt = edid.descriptors.iter().find_map(|desc| match desc {
edid::Descriptor::DetailedTiming(timing) => Some(timing),
_ => None,
});
let Some(timing) = timing_opt else {
log::warn!(
"DDI {} EDID from {} missing detailed timing",