use display_types::cea861::hdmi_forum::HdmiForumFrl;
pub use hdmi_hal::phy::{EqParams, FrlOutput, LaneEqParams, LanePatterns, LtpPattern, TxFfeLevel};
pub trait HdmiPhy {
type Error;
async fn set_frl_rate(&mut self, rate: HdmiForumFrl) -> Result<(), Self::Error>;
async fn send_ltp(&mut self, patterns: LanePatterns) -> Result<(), Self::Error>;
async fn set_frl_output(&mut self, output: FrlOutput) -> Result<(), Self::Error>;
async fn adjust_equalization(&mut self, params: EqParams) -> Result<(), Self::Error>;
async fn set_scrambling(&mut self, enabled: bool) -> Result<(), Self::Error>;
}
#[cfg(test)]
mod tests {
use super::*;
struct MockPhy {
frl_rate: Option<HdmiForumFrl>,
scrambling: Option<bool>,
eq_calls: u32,
last_eq: Option<EqParams>,
frl_output: Option<FrlOutput>,
last_ltp: Option<LanePatterns>,
}
impl MockPhy {
fn new() -> Self {
Self {
frl_rate: None,
scrambling: None,
eq_calls: 0,
last_eq: None,
frl_output: None,
last_ltp: None,
}
}
}
impl HdmiPhy for MockPhy {
type Error = core::convert::Infallible;
async fn set_frl_rate(&mut self, rate: HdmiForumFrl) -> Result<(), Self::Error> {
self.frl_rate = Some(rate);
Ok(())
}
async fn send_ltp(&mut self, patterns: LanePatterns) -> Result<(), Self::Error> {
self.last_ltp = Some(patterns);
Ok(())
}
async fn set_frl_output(&mut self, output: FrlOutput) -> Result<(), Self::Error> {
self.frl_output = Some(output);
Ok(())
}
async fn adjust_equalization(&mut self, params: EqParams) -> Result<(), Self::Error> {
self.eq_calls += 1;
self.last_eq = Some(params);
Ok(())
}
async fn set_scrambling(&mut self, enabled: bool) -> Result<(), Self::Error> {
self.scrambling = Some(enabled);
Ok(())
}
}
#[pollster::test]
async fn send_ltp_records_patterns() {
let mut phy = MockPhy::new();
let patterns = LanePatterns {
lane0: Some(LtpPattern::Lfsr0),
lane1: Some(LtpPattern::Lfsr1),
lane2: Some(LtpPattern::Lfsr2),
lane3: None,
};
phy.send_ltp(patterns).await.unwrap();
assert_eq!(phy.last_ltp, Some(patterns));
}
#[pollster::test]
async fn send_ltp_updates_on_each_call() {
let mut phy = MockPhy::new();
let first = LanePatterns {
lane0: Some(LtpPattern::Lfsr0),
..LanePatterns::default()
};
let second = LanePatterns {
lane0: Some(LtpPattern::NyquistClock),
..first
};
phy.send_ltp(first).await.unwrap();
phy.send_ltp(second).await.unwrap();
assert_eq!(phy.last_ltp, Some(second));
}
#[pollster::test]
async fn set_frl_rate_records_rate() {
let mut phy = MockPhy::new();
phy.set_frl_rate(HdmiForumFrl::Rate6Gbps4Lanes)
.await
.unwrap();
assert_eq!(phy.frl_rate, Some(HdmiForumFrl::Rate6Gbps4Lanes));
}
#[pollster::test]
async fn set_frl_rate_not_supported_is_valid() {
let mut phy = MockPhy::new();
phy.set_frl_rate(HdmiForumFrl::NotSupported).await.unwrap();
assert_eq!(phy.frl_rate, Some(HdmiForumFrl::NotSupported));
}
#[pollster::test]
async fn set_frl_rate_can_be_updated() {
let mut phy = MockPhy::new();
phy.set_frl_rate(HdmiForumFrl::Rate3Gbps3Lanes)
.await
.unwrap();
phy.set_frl_rate(HdmiForumFrl::Rate12Gbps4Lanes)
.await
.unwrap();
assert_eq!(phy.frl_rate, Some(HdmiForumFrl::Rate12Gbps4Lanes));
}
#[pollster::test]
async fn set_scrambling_enable() {
let mut phy = MockPhy::new();
phy.set_scrambling(true).await.unwrap();
assert_eq!(phy.scrambling, Some(true));
}
#[pollster::test]
async fn set_scrambling_disable() {
let mut phy = MockPhy::new();
phy.set_scrambling(false).await.unwrap();
assert_eq!(phy.scrambling, Some(false));
}
#[pollster::test]
async fn set_scrambling_can_be_toggled() {
let mut phy = MockPhy::new();
phy.set_scrambling(true).await.unwrap();
phy.set_scrambling(false).await.unwrap();
assert_eq!(phy.scrambling, Some(false));
}
#[pollster::test]
async fn set_frl_output_records_mode() {
let mut phy = MockPhy::new();
phy.set_frl_output(FrlOutput::GapOnly).await.unwrap();
assert_eq!(phy.frl_output, Some(FrlOutput::GapOnly));
}
#[pollster::test]
async fn set_frl_output_can_be_switched() {
let mut phy = MockPhy::new();
phy.set_frl_output(FrlOutput::GapOnly).await.unwrap();
phy.set_frl_output(FrlOutput::Active).await.unwrap();
assert_eq!(phy.frl_output, Some(FrlOutput::Active));
}
#[pollster::test]
async fn adjust_equalization_records_per_lane_levels() {
let mut phy = MockPhy::new();
let mut params = EqParams::new();
params.lane1.tx_ffe_level = TxFfeLevel::MAX;
phy.adjust_equalization(params).await.unwrap();
let last = phy.last_eq.unwrap();
assert_eq!(last, params);
assert_eq!(last.lane0.tx_ffe_level.value(), 0);
assert_eq!(last.lane1.tx_ffe_level.value(), 7);
}
#[pollster::test]
async fn adjust_equalization_tracks_call_count() {
let mut phy = MockPhy::new();
phy.adjust_equalization(EqParams::new()).await.unwrap();
phy.adjust_equalization(EqParams::new()).await.unwrap();
assert_eq!(phy.eq_calls, 2);
}
#[test]
fn value_types_are_hdmi_hals_at_both_paths() {
let pattern: crate::phy::LtpPattern = hdmi_hal::phy::LtpPattern::Lfsr0;
let _: crate::LtpPattern = pattern;
let output: crate::phy::FrlOutput = hdmi_hal::phy::FrlOutput::GapOnly;
let _: crate::FrlOutput = output;
let level: crate::phy::TxFfeLevel = crate::TxFfeLevel::MAX;
assert_eq!(level.value(), 7);
let _: crate::phy::LanePatterns = crate::LanePatterns::default();
let _: crate::phy::EqParams = crate::EqParams::default();
let _: crate::phy::LaneEqParams = crate::LaneEqParams::default();
}
}