use display_types::cea861::hdmi_forum::HdmiForumFrl;
use hdmi_hal_async::phy::HdmiPhy;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use crate::client::ScdcClient;
use crate::{FrlConfig, LtpReq, TrainingConfig, TrainingError, TrainingOutcome};
#[cfg(feature = "alloc")]
use crate::{TrainingEvent, TrainingTrace};
pub struct FrlTrainer<C, P> {
scdc: C,
phy: P,
}
impl<C: ScdcClient, P: HdmiPhy> FrlTrainer<C, P> {
pub fn new(scdc: C, phy: P) -> Self {
Self { scdc, phy }
}
pub fn into_parts(self) -> (C, P) {
(self.scdc, self.phy)
}
pub async fn train_at_rate(
&mut self,
rate: HdmiForumFrl,
config: &TrainingConfig,
) -> Result<TrainingOutcome, TrainingError<C::Error, P::Error>> {
self.scdc
.write_frl_config(FrlConfig {
rate,
ffe_levels: config.ffe_levels,
dsc_frl_max: config.dsc_frl_max,
})
.await
.map_err(TrainingError::Scdc)?;
self.phy
.set_frl_rate(rate)
.await
.map_err(TrainingError::Phy)?;
let mut i = 0u32;
loop {
let status = self
.scdc
.read_training_status()
.await
.map_err(TrainingError::Scdc)?;
if status.flt_ready {
break;
}
i += 1;
if i >= config.flt_ready_timeout {
return Ok(TrainingOutcome::FallbackRequired);
}
}
let mut i = 0u32;
loop {
let status = self
.scdc
.read_training_status()
.await
.map_err(TrainingError::Scdc)?;
if status.frl_start {
break;
}
i += 1;
if i >= config.frl_start_timeout {
return Ok(TrainingOutcome::FallbackRequired);
}
}
let mut i = 0u32;
loop {
let status = self
.scdc
.read_training_status()
.await
.map_err(TrainingError::Scdc)?;
if status.ltp_req == LtpReq::None {
return Ok(TrainingOutcome::Success {
achieved_rate: rate,
});
}
let _ced = self.scdc.read_ced().await.map_err(TrainingError::Scdc)?;
self.phy
.send_ltp(status.ltp_req.into())
.await
.map_err(TrainingError::Phy)?;
i += 1;
if i >= config.ltp_timeout {
return Ok(TrainingOutcome::FallbackRequired);
}
}
}
#[cfg(feature = "alloc")]
pub async fn train_at_rate_traced(
&mut self,
rate: HdmiForumFrl,
config: &TrainingConfig,
) -> Result<(TrainingOutcome, TrainingTrace), TrainingError<C::Error, P::Error>> {
let mut events: Vec<TrainingEvent> = Vec::new();
self.scdc
.write_frl_config(FrlConfig {
rate,
ffe_levels: config.ffe_levels,
dsc_frl_max: config.dsc_frl_max,
})
.await
.map_err(TrainingError::Scdc)?;
self.phy
.set_frl_rate(rate)
.await
.map_err(TrainingError::Phy)?;
events.push(TrainingEvent::RateConfigured {
rate,
ffe_levels: config.ffe_levels,
});
let mut i = 0u32;
let timed_out = loop {
let status = self
.scdc
.read_training_status()
.await
.map_err(TrainingError::Scdc)?;
if status.flt_ready {
events.push(TrainingEvent::FltReadyReceived {
after_iterations: i,
});
break false;
}
i += 1;
if i >= config.flt_ready_timeout {
events.push(TrainingEvent::FltReadyTimeout {
iterations_elapsed: i,
});
break true;
}
};
if timed_out {
return Ok((
TrainingOutcome::FallbackRequired,
TrainingTrace::new(rate, *config, events),
));
}
let mut i = 0u32;
let timed_out = loop {
let status = self
.scdc
.read_training_status()
.await
.map_err(TrainingError::Scdc)?;
if status.frl_start {
events.push(TrainingEvent::FrlStartReceived {
after_iterations: i,
});
break false;
}
i += 1;
if i >= config.frl_start_timeout {
events.push(TrainingEvent::FrlStartTimeout {
iterations_elapsed: i,
});
break true;
}
};
if timed_out {
return Ok((
TrainingOutcome::FallbackRequired,
TrainingTrace::new(rate, *config, events),
));
}
let mut i = 0u32;
let mut last_ltp: Option<LtpReq> = None;
let outcome = loop {
let status = self
.scdc
.read_training_status()
.await
.map_err(TrainingError::Scdc)?;
if status.ltp_req == LtpReq::None {
events.push(TrainingEvent::AllLanesSatisfied {
after_iterations: i,
});
break TrainingOutcome::Success {
achieved_rate: rate,
};
}
if Some(status.ltp_req) != last_ltp {
events.push(TrainingEvent::LtpPatternRequested {
pattern: status.ltp_req,
});
last_ltp = Some(status.ltp_req);
}
let _ced = self.scdc.read_ced().await.map_err(TrainingError::Scdc)?;
self.phy
.send_ltp(status.ltp_req.into())
.await
.map_err(TrainingError::Phy)?;
i += 1;
if i >= config.ltp_timeout {
events.push(TrainingEvent::LtpLoopTimeout {
iterations_elapsed: i,
});
break TrainingOutcome::FallbackRequired;
}
};
Ok((outcome, TrainingTrace::new(rate, *config, events)))
}
}
#[cfg(test)]
mod sim;
#[cfg(test)]
mod tests;