use display_types::HdmiForumFrl;
pub use display_types::HdmiForumFrl as FrlRate;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TmdsConfig {
pub scrambling_enable: bool,
pub high_tmds_clock_ratio: bool,
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ScramblerStatus {
pub scrambling_active: bool,
}
impl ScramblerStatus {
pub fn new(scrambling_active: bool) -> Self {
Self { scrambling_active }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct FfeLevels(u8);
impl FfeLevels {
pub const fn new(levels: u8) -> Option<Self> {
if levels <= 7 {
Some(Self(levels))
} else {
None
}
}
pub fn max_for(rate: HdmiForumFrl) -> Self {
let faster_than_12g = (rate as u8 > HdmiForumFrl::Rate12Gbps4Lanes as u8) as u8;
Self(3 + 4 * faster_than_12g)
}
pub const fn value(self) -> u8 {
self.0
}
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FrlConfig {
pub frl_rate: HdmiForumFrl,
pub ffe_levels: FfeLevels,
}
impl FrlConfig {
pub const fn new(frl_rate: HdmiForumFrl, ffe_levels: FfeLevels) -> Self {
Self {
frl_rate,
ffe_levels,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Config0 {
pub rr_enable: bool,
pub flt_no_retrain: bool,
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct SourceTestConfig {
pub txffe_pre_shoot_only: bool,
pub txffe_de_emphasis_only: bool,
pub txffe_no_ffe: bool,
pub flt_no_timeout: bool,
pub dsc_frl_max: bool,
pub frl_max: bool,
}
impl SourceTestConfig {
pub const fn new(
txffe_pre_shoot_only: bool,
txffe_de_emphasis_only: bool,
txffe_no_ffe: bool,
flt_no_timeout: bool,
dsc_frl_max: bool,
frl_max: bool,
) -> Self {
Self {
txffe_pre_shoot_only,
txffe_de_emphasis_only,
txffe_no_ffe,
flt_no_timeout,
dsc_frl_max,
frl_max,
}
}
}
#[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(crate) const fn from_nibble(nibble: u8) -> Self {
Self::from_value(nibble & 0x0F).unwrap()
}
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,
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LtpRequests {
pub lane0: LtpReq,
pub lane1: LtpReq,
pub lane2: LtpReq,
pub lane3: LtpReq,
}
impl LtpRequests {
pub const fn new(lane0: LtpReq, lane1: LtpReq, lane2: LtpReq, lane3: LtpReq) -> Self {
Self {
lane0,
lane1,
lane2,
lane3,
}
}
pub fn all_trained(&self) -> bool {
[self.lane0, self.lane1, self.lane2, self.lane3]
.iter()
.all(|r| *r == LtpReq::None)
}
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct StatusFlags {
pub clock_detected: bool,
pub ch0_locked: bool,
pub ch1_locked: bool,
pub ch2_locked: bool,
pub ln3_locked: bool,
pub flt_ready: bool,
pub dsc_decode_fail: bool,
}
impl StatusFlags {
pub fn new(
clock_detected: bool,
ch0_locked: bool,
ch1_locked: bool,
ch2_locked: bool,
ln3_locked: bool,
flt_ready: bool,
dsc_decode_fail: bool,
) -> Self {
Self {
clock_detected,
ch0_locked,
ch1_locked,
ch2_locked,
ln3_locked,
flt_ready,
dsc_decode_fail,
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct UpdateFlags {
pub status_update: bool,
pub ced_update: bool,
pub rr_test: bool,
pub source_test_update: bool,
pub frl_start: bool,
pub flt_update: bool,
pub rsed_update: bool,
}
impl UpdateFlags {
pub const fn new(
status_update: bool,
ced_update: bool,
rr_test: bool,
source_test_update: bool,
frl_start: bool,
flt_update: bool,
rsed_update: bool,
) -> Self {
Self {
status_update,
ced_update,
rr_test,
source_test_update,
frl_start,
flt_update,
rsed_update,
}
}
pub const fn clearable(self) -> ClearableUpdateFlags {
ClearableUpdateFlags {
status_update: self.status_update,
ced_update: self.ced_update,
source_test_update: self.source_test_update,
frl_start: self.frl_start,
flt_update: self.flt_update,
rsed_update: self.rsed_update,
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct ClearableUpdateFlags {
pub status_update: bool,
pub ced_update: bool,
pub source_test_update: bool,
pub frl_start: bool,
pub flt_update: bool,
pub rsed_update: bool,
}
impl ClearableUpdateFlags {
pub const fn new(
status_update: bool,
ced_update: bool,
source_test_update: bool,
frl_start: bool,
flt_update: bool,
rsed_update: bool,
) -> Self {
Self {
status_update,
ced_update,
source_test_update,
frl_start,
flt_update,
rsed_update,
}
}
}
#[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 RsCorrectionCount(u16);
impl RsCorrectionCount {
pub fn new(raw: u16) -> Self {
Self(raw & 0x7FFF)
}
pub fn value(self) -> u16 {
self.0
}
}
#[non_exhaustive]
#[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>,
}
impl CedCounters {
pub const fn new(
lane0: Option<CedCount>,
lane1: Option<CedCount>,
lane2: Option<CedCount>,
lane3: Option<CedCount>,
) -> Self {
Self {
lane0,
lane1,
lane2,
lane3,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ced_count_masks_validity_bit() {
assert_eq!(CedCount::new(0xFFFF).value(), 0x7FFF);
assert_eq!(CedCount::new(0x8000).value(), 0x0000);
}
#[test]
fn ced_count_preserves_15_bit_value() {
assert_eq!(CedCount::new(0x0000).value(), 0x0000);
assert_eq!(CedCount::new(0x0001).value(), 0x0001);
assert_eq!(CedCount::new(0x7FFF).value(), 0x7FFF);
}
#[test]
fn status_flags_new_field_order() {
let all = |f: StatusFlags| {
[
f.clock_detected,
f.ch0_locked,
f.ch1_locked,
f.ch2_locked,
f.ln3_locked,
f.flt_ready,
f.dsc_decode_fail,
]
};
for i in 0..7 {
let mut args = [false; 7];
args[i] = true;
let f = StatusFlags::new(
args[0], args[1], args[2], args[3], args[4], args[5], args[6],
);
assert_eq!(all(f), args, "parameter {i}");
}
}
#[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_from_nibble_decodes_every_value() {
for n in 0u8..=0xF {
let req = LtpReq::from_nibble(n);
assert_eq!(req.value(), n, "nibble {n:#x}");
assert_eq!(
matches!(req, LtpReq::Reserved(_)),
(0x9..=0xD).contains(&n),
"nibble {n:#x}"
);
}
assert_eq!(LtpReq::from_nibble(0xF3), LtpReq::NyquistClock);
}
#[test]
fn ltp_requests_all_trained() {
assert!(
LtpRequests::new(LtpReq::None, LtpReq::None, LtpReq::None, LtpReq::None).all_trained()
);
assert!(
!LtpRequests::new(LtpReq::None, LtpReq::None, LtpReq::None, LtpReq::Lfsr0)
.all_trained()
);
}
#[test]
fn ced_counters_new_field_order() {
let a = CedCount::new(1);
let b = CedCount::new(2);
let c = CedCount::new(3);
let d = CedCount::new(4);
let counters = CedCounters::new(Some(a), Some(b), Some(c), Some(d));
assert_eq!(counters.lane0.unwrap().value(), 1);
assert_eq!(counters.lane1.unwrap().value(), 2);
assert_eq!(counters.lane2.unwrap().value(), 3);
assert_eq!(counters.lane3.unwrap().value(), 4);
}
#[test]
fn ced_counters_new_lane3_none() {
let counters = CedCounters::new(
Some(CedCount::new(0)),
Some(CedCount::new(0)),
Some(CedCount::new(0)),
None,
);
assert!(counters.lane3.is_none());
}
#[test]
fn scrambler_status_new() {
assert!(ScramblerStatus::new(true).scrambling_active);
assert!(!ScramblerStatus::new(false).scrambling_active);
}
#[test]
fn update_flags_new_field_order() {
let all = |f: UpdateFlags| {
[
f.status_update,
f.ced_update,
f.rr_test,
f.source_test_update,
f.frl_start,
f.flt_update,
f.rsed_update,
]
};
for bit in 0..7 {
let mut args = [false; 7];
args[bit] = true;
let f = UpdateFlags::new(
args[0], args[1], args[2], args[3], args[4], args[5], args[6],
);
assert_eq!(all(f), args, "parameter {bit}");
}
}
#[test]
fn clearable_update_flags_new_is_in_bit_order() {
let all = |f: ClearableUpdateFlags| {
[
f.status_update,
f.ced_update,
f.source_test_update,
f.frl_start,
f.flt_update,
f.rsed_update,
]
};
for bit in 0..6 {
let mut args = [false; 6];
args[bit] = true;
let f = ClearableUpdateFlags::new(args[0], args[1], args[2], args[3], args[4], args[5]);
assert_eq!(all(f), args, "parameter {bit}");
}
}
#[test]
fn clearable_keeps_every_flag_but_rr_test() {
let read = UpdateFlags::new(true, true, true, true, true, true, true);
assert_eq!(
read.clearable(),
ClearableUpdateFlags::new(true, true, true, true, true, true)
);
let rr_test_only = UpdateFlags::new(false, false, true, false, false, false, false);
assert_eq!(rr_test_only.clearable(), ClearableUpdateFlags::default());
}
}