use display_types::cea861::hdmi_forum::HdmiForumFrl;
use hdmi_hal::phy::LtpPattern;
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LtpReq {
None,
AllOnes,
AllZeros,
NyquistClock,
DdeCompliance,
Lfsr0,
Lfsr1,
Lfsr2,
Lfsr3,
FfeChange,
RateChange,
Reserved(u8),
}
impl LtpReq {
pub const fn from_value(value: u8) -> Option<Self> {
Some(match value {
0x0 => Self::None,
0x1 => Self::AllOnes,
0x2 => Self::AllZeros,
0x3 => Self::NyquistClock,
0x4 => Self::DdeCompliance,
0x5 => Self::Lfsr0,
0x6 => Self::Lfsr1,
0x7 => Self::Lfsr2,
0x8 => Self::Lfsr3,
0x9..=0xD => Self::Reserved(value),
0xE => Self::FfeChange,
0xF => Self::RateChange,
_ => return None,
})
}
pub const fn value(self) -> u8 {
match self {
Self::None => 0x0,
Self::AllOnes => 0x1,
Self::AllZeros => 0x2,
Self::NyquistClock => 0x3,
Self::DdeCompliance => 0x4,
Self::Lfsr0 => 0x5,
Self::Lfsr1 => 0x6,
Self::Lfsr2 => 0x7,
Self::Lfsr3 => 0x8,
Self::FfeChange => 0xE,
Self::RateChange => 0xF,
Self::Reserved(value) => value,
}
}
pub const fn pattern(self) -> Option<LtpPattern> {
match self {
Self::None | Self::FfeChange | Self::RateChange | Self::Reserved(_) => None,
Self::AllOnes => Some(LtpPattern::AllOnes),
Self::AllZeros => Some(LtpPattern::AllZeros),
Self::NyquistClock => Some(LtpPattern::NyquistClock),
Self::DdeCompliance => Some(LtpPattern::DdeCompliance),
Self::Lfsr0 => Some(LtpPattern::Lfsr0),
Self::Lfsr1 => Some(LtpPattern::Lfsr1),
Self::Lfsr2 => Some(LtpPattern::Lfsr2),
Self::Lfsr3 => Some(LtpPattern::Lfsr3),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LtpRequests {
pub lane0: LtpReq,
pub lane1: LtpReq,
pub lane2: LtpReq,
pub lane3: LtpReq,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct FfeLevels(u8);
impl FfeLevels {
pub const MAX: Self = Self(7);
pub const fn new(level: u8) -> Option<Self> {
if level <= Self::MAX.0 {
Some(Self(level))
} else {
None
}
}
pub const fn value(self) -> u8 {
self.0
}
pub fn limited_to(self, rate: HdmiForumFrl) -> Self {
let top_3_level_rate = HdmiForumFrl::Rate12Gbps4Lanes;
let max = if rate <= top_3_level_rate { 3 } else { 7 };
Self(self.0.min(max))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FrlConfig {
pub rate: HdmiForumFrl,
pub ffe_levels: FfeLevels,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct UpdateFlags {
pub source_test_update: bool,
pub frl_start: bool,
pub flt_update: bool,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct SourceTestConfig {
pub flt_no_timeout: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CedCount(u16);
impl CedCount {
pub fn new(raw: u16) -> Self {
Self(raw & 0x7FFF)
}
pub fn value(self) -> u16 {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CedCounters {
pub lane0: Option<CedCount>,
pub lane1: Option<CedCount>,
pub lane2: Option<CedCount>,
pub lane3: Option<CedCount>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ltp_req_from_value_gives_one_request_per_value() {
for value in 0u8..=0xF {
let request = LtpReq::from_value(value).unwrap();
assert_eq!(request.value(), value, "value {value:#x}");
assert_eq!(
matches!(request, LtpReq::Reserved(_)),
(0x9..=0xD).contains(&value),
"value {value:#x}"
);
}
assert_eq!(LtpReq::from_value(0x10), None);
assert_eq!(LtpReq::from_value(0xFF), None);
}
#[test]
fn ltp_req_values_match_status_flags_encoding() {
assert_eq!(LtpReq::None.value(), 0x0);
assert_eq!(LtpReq::AllOnes.value(), 0x1);
assert_eq!(LtpReq::AllZeros.value(), 0x2);
assert_eq!(LtpReq::NyquistClock.value(), 0x3);
assert_eq!(LtpReq::DdeCompliance.value(), 0x4);
assert_eq!(LtpReq::Lfsr0.value(), 0x5);
assert_eq!(LtpReq::Lfsr1.value(), 0x6);
assert_eq!(LtpReq::Lfsr2.value(), 0x7);
assert_eq!(LtpReq::Lfsr3.value(), 0x8);
assert_eq!(LtpReq::FfeChange.value(), 0xE);
assert_eq!(LtpReq::RateChange.value(), 0xF);
assert_eq!(LtpReq::Reserved(0xB).value(), 0xB);
}
#[test]
fn ltp_req_patterns_match_by_value() {
assert_eq!(LtpReq::AllOnes.pattern(), Some(LtpPattern::AllOnes));
assert_eq!(LtpReq::AllZeros.pattern(), Some(LtpPattern::AllZeros));
assert_eq!(
LtpReq::NyquistClock.pattern(),
Some(LtpPattern::NyquistClock)
);
assert_eq!(
LtpReq::DdeCompliance.pattern(),
Some(LtpPattern::DdeCompliance)
);
assert_eq!(LtpReq::Lfsr0.pattern(), Some(LtpPattern::Lfsr0));
assert_eq!(LtpReq::Lfsr1.pattern(), Some(LtpPattern::Lfsr1));
assert_eq!(LtpReq::Lfsr2.pattern(), Some(LtpPattern::Lfsr2));
assert_eq!(LtpReq::Lfsr3.pattern(), Some(LtpPattern::Lfsr3));
}
#[test]
fn ltp_req_patterns_share_their_value_with_ltp_pattern() {
for req in [
LtpReq::AllOnes,
LtpReq::AllZeros,
LtpReq::NyquistClock,
LtpReq::DdeCompliance,
LtpReq::Lfsr0,
LtpReq::Lfsr1,
LtpReq::Lfsr2,
LtpReq::Lfsr3,
] {
assert_eq!(req.pattern().map(LtpPattern::value), Some(req.value()));
}
}
#[test]
fn ltp_req_signals_have_no_pattern() {
assert_eq!(LtpReq::None.pattern(), None);
assert_eq!(LtpReq::FfeChange.pattern(), None);
assert_eq!(LtpReq::RateChange.pattern(), None);
assert_eq!(LtpReq::Reserved(0x9).pattern(), None);
}
#[test]
fn ffe_levels_accepts_0_to_7() {
for level in 0..=7 {
assert_eq!(FfeLevels::new(level).map(FfeLevels::value), Some(level));
}
}
#[test]
fn ffe_levels_rejects_above_7() {
assert_eq!(FfeLevels::new(8), None);
assert_eq!(FfeLevels::new(u8::MAX), None);
}
#[test]
fn ffe_levels_max_and_default() {
assert_eq!(FfeLevels::MAX.value(), 7);
assert_eq!(FfeLevels::default().value(), 0);
}
#[test]
fn ffe_levels_limited_to_3_up_to_12_gbps() {
for rate in [
HdmiForumFrl::Rate3Gbps3Lanes,
HdmiForumFrl::Rate6Gbps3Lanes,
HdmiForumFrl::Rate6Gbps4Lanes,
HdmiForumFrl::Rate8Gbps4Lanes,
HdmiForumFrl::Rate10Gbps4Lanes,
HdmiForumFrl::Rate12Gbps4Lanes,
] {
assert_eq!(FfeLevels::MAX.limited_to(rate).value(), 3);
}
}
#[test]
fn ffe_levels_below_the_limit_are_unchanged() {
let two = FfeLevels::new(2).unwrap();
assert_eq!(two.limited_to(HdmiForumFrl::Rate12Gbps4Lanes), two);
assert_eq!(
FfeLevels::default().limited_to(HdmiForumFrl::Rate3Gbps3Lanes),
FfeLevels::default()
);
}
#[test]
fn update_flags_default_is_all_clear() {
let flags = UpdateFlags::default();
assert!(!flags.source_test_update);
assert!(!flags.frl_start);
assert!(!flags.flt_update);
}
#[test]
fn source_test_config_default_keeps_timeouts() {
assert!(!SourceTestConfig::default().flt_no_timeout);
}
#[test]
fn ltp_requests_compare_per_lane() {
let a = LtpRequests {
lane0: LtpReq::Lfsr0,
lane1: LtpReq::Lfsr1,
lane2: LtpReq::Lfsr2,
lane3: LtpReq::Lfsr3,
};
assert_eq!(a, a);
assert_ne!(
a,
LtpRequests {
lane3: LtpReq::None,
..a
}
);
}
#[test]
fn ced_count_masks_validity_bit() {
let c = CedCount::new(0xFFFF);
assert_eq!(c.value(), 0x7FFF);
}
#[test]
fn ced_count_preserves_15_bit_value() {
let c = CedCount::new(0x0123);
assert_eq!(c.value(), 0x0123);
}
#[test]
fn ced_count_zero() {
assert_eq!(CedCount::new(0).value(), 0);
}
#[test]
fn ced_count_clone_eq() {
let a = CedCount::new(42);
assert_eq!(a, a.clone());
assert_ne!(CedCount::new(1), CedCount::new(2));
}
}