1use display_types::cea861::hdmi_forum::HdmiForumFrl;
2use hdmi_hal::phy::LtpPattern;
3
4#[non_exhaustive]
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum LtpReq {
15 None,
17 AllOnes,
19 AllZeros,
21 NyquistClock,
23 DdeCompliance,
25 Lfsr0,
27 Lfsr1,
29 Lfsr2,
31 Lfsr3,
33 FfeChange,
35 RateChange,
37 Reserved(u8),
44}
45
46impl LtpReq {
47 pub const fn from_value(value: u8) -> Option<Self> {
50 Some(match value {
51 0x0 => Self::None,
52 0x1 => Self::AllOnes,
53 0x2 => Self::AllZeros,
54 0x3 => Self::NyquistClock,
55 0x4 => Self::DdeCompliance,
56 0x5 => Self::Lfsr0,
57 0x6 => Self::Lfsr1,
58 0x7 => Self::Lfsr2,
59 0x8 => Self::Lfsr3,
60 0x9..=0xD => Self::Reserved(value),
61 0xE => Self::FfeChange,
62 0xF => Self::RateChange,
63 _ => return None,
64 })
65 }
66
67 pub const fn value(self) -> u8 {
69 match self {
70 Self::None => 0x0,
71 Self::AllOnes => 0x1,
72 Self::AllZeros => 0x2,
73 Self::NyquistClock => 0x3,
74 Self::DdeCompliance => 0x4,
75 Self::Lfsr0 => 0x5,
76 Self::Lfsr1 => 0x6,
77 Self::Lfsr2 => 0x7,
78 Self::Lfsr3 => 0x8,
79 Self::FfeChange => 0xE,
80 Self::RateChange => 0xF,
81 Self::Reserved(value) => value,
82 }
83 }
84
85 pub const fn pattern(self) -> Option<LtpPattern> {
88 match self {
89 Self::None | Self::FfeChange | Self::RateChange | Self::Reserved(_) => None,
90 Self::AllOnes => Some(LtpPattern::AllOnes),
91 Self::AllZeros => Some(LtpPattern::AllZeros),
92 Self::NyquistClock => Some(LtpPattern::NyquistClock),
93 Self::DdeCompliance => Some(LtpPattern::DdeCompliance),
94 Self::Lfsr0 => Some(LtpPattern::Lfsr0),
95 Self::Lfsr1 => Some(LtpPattern::Lfsr1),
96 Self::Lfsr2 => Some(LtpPattern::Lfsr2),
97 Self::Lfsr3 => Some(LtpPattern::Lfsr3),
98 }
99 }
100}
101
102#[non_exhaustive]
107#[derive(Debug, Clone, Copy, PartialEq, Eq)]
108pub struct LtpRequests {
109 pub lane0: LtpReq,
111 pub lane1: LtpReq,
113 pub lane2: LtpReq,
115 pub lane3: LtpReq,
119}
120
121impl LtpRequests {
122 pub const fn new(lane0: LtpReq, lane1: LtpReq, lane2: LtpReq, lane3: LtpReq) -> Self {
124 Self {
125 lane0,
126 lane1,
127 lane2,
128 lane3,
129 }
130 }
131
132 pub fn all_trained(&self) -> bool {
136 [self.lane0, self.lane1, self.lane2, self.lane3]
137 .iter()
138 .all(|r| *r == LtpReq::None)
139 }
140}
141
142#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)]
147pub struct FfeLevels(u8);
148
149impl FfeLevels {
150 pub const MAX: Self = Self(7);
152
153 pub const fn new(level: u8) -> Option<Self> {
155 if level <= Self::MAX.0 {
156 Some(Self(level))
157 } else {
158 None
159 }
160 }
161
162 pub const fn value(self) -> u8 {
164 self.0
165 }
166
167 pub fn limited_to(self, rate: HdmiForumFrl) -> Self {
170 let top_3_level_rate = HdmiForumFrl::Rate12Gbps4Lanes;
173 let max = if rate <= top_3_level_rate { 3 } else { 7 };
174 Self(self.0.min(max))
175 }
176}
177
178#[non_exhaustive]
183#[derive(Debug, Clone, Copy, PartialEq, Eq)]
184pub struct FrlConfig {
185 pub frl_rate: HdmiForumFrl,
187 pub ffe_levels: FfeLevels,
190}
191
192impl FrlConfig {
193 pub const fn new(frl_rate: HdmiForumFrl, ffe_levels: FfeLevels) -> Self {
195 Self {
196 frl_rate,
197 ffe_levels,
198 }
199 }
200}
201
202#[non_exhaustive]
210#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
211pub struct UpdateFlags {
212 pub status_update: bool,
214 pub ced_update: bool,
216 pub rr_test: bool,
219 pub source_test_update: bool,
221 pub frl_start: bool,
223 pub flt_update: bool,
225 pub rsed_update: bool,
227}
228
229impl UpdateFlags {
230 pub const fn new(
232 status_update: bool,
233 ced_update: bool,
234 rr_test: bool,
235 source_test_update: bool,
236 frl_start: bool,
237 flt_update: bool,
238 rsed_update: bool,
239 ) -> Self {
240 Self {
241 status_update,
242 ced_update,
243 rr_test,
244 source_test_update,
245 frl_start,
246 flt_update,
247 rsed_update,
248 }
249 }
250
251 pub const fn clearable(self) -> ClearableUpdateFlags {
253 ClearableUpdateFlags {
254 status_update: self.status_update,
255 ced_update: self.ced_update,
256 source_test_update: self.source_test_update,
257 frl_start: self.frl_start,
258 flt_update: self.flt_update,
259 rsed_update: self.rsed_update,
260 }
261 }
262}
263
264#[non_exhaustive]
272#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
273pub struct ClearableUpdateFlags {
274 pub status_update: bool,
276 pub ced_update: bool,
278 pub source_test_update: bool,
280 pub frl_start: bool,
282 pub flt_update: bool,
284 pub rsed_update: bool,
286}
287
288impl ClearableUpdateFlags {
289 pub const fn new(
291 status_update: bool,
292 ced_update: bool,
293 source_test_update: bool,
294 frl_start: bool,
295 flt_update: bool,
296 rsed_update: bool,
297 ) -> Self {
298 Self {
299 status_update,
300 ced_update,
301 source_test_update,
302 frl_start,
303 flt_update,
304 rsed_update,
305 }
306 }
307}
308
309#[non_exhaustive]
322#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
323pub struct SourceTestConfig {
324 pub txffe_pre_shoot_only: bool,
326 pub txffe_de_emphasis_only: bool,
328 pub txffe_no_ffe: bool,
330 pub flt_no_timeout: bool,
333 pub dsc_frl_max: bool,
337 pub frl_max: bool,
341}
342
343impl SourceTestConfig {
344 pub const fn new(
346 txffe_pre_shoot_only: bool,
347 txffe_de_emphasis_only: bool,
348 txffe_no_ffe: bool,
349 flt_no_timeout: bool,
350 dsc_frl_max: bool,
351 frl_max: bool,
352 ) -> Self {
353 Self {
354 txffe_pre_shoot_only,
355 txffe_de_emphasis_only,
356 txffe_no_ffe,
357 flt_no_timeout,
358 dsc_frl_max,
359 frl_max,
360 }
361 }
362}
363
364#[derive(Debug, Clone, Copy, PartialEq, Eq)]
369pub struct CedCount(u16);
370
371impl CedCount {
372 pub fn new(raw: u16) -> Self {
374 Self(raw & 0x7FFF)
375 }
376
377 pub fn value(self) -> u16 {
379 self.0
380 }
381}
382
383#[non_exhaustive]
390#[derive(Debug, Clone, Copy, PartialEq, Eq)]
391pub struct CedCounters {
392 pub lane0: Option<CedCount>,
394 pub lane1: Option<CedCount>,
396 pub lane2: Option<CedCount>,
398 pub lane3: Option<CedCount>,
401}
402
403impl CedCounters {
404 pub const fn new(
406 lane0: Option<CedCount>,
407 lane1: Option<CedCount>,
408 lane2: Option<CedCount>,
409 lane3: Option<CedCount>,
410 ) -> Self {
411 Self {
412 lane0,
413 lane1,
414 lane2,
415 lane3,
416 }
417 }
418}
419
420#[cfg(test)]
421mod tests {
422 use super::*;
423
424 #[test]
427 fn ltp_req_from_value_gives_one_request_per_value() {
428 for value in 0u8..=0xF {
429 let request = LtpReq::from_value(value).unwrap();
430 assert_eq!(request.value(), value, "value {value:#x}");
431 assert_eq!(
432 matches!(request, LtpReq::Reserved(_)),
433 (0x9..=0xD).contains(&value),
434 "value {value:#x}"
435 );
436 }
437 assert_eq!(LtpReq::from_value(0x10), None);
438 assert_eq!(LtpReq::from_value(0xFF), None);
439 }
440
441 #[test]
442 fn ltp_req_values_match_status_flags_encoding() {
443 assert_eq!(LtpReq::None.value(), 0x0);
444 assert_eq!(LtpReq::AllOnes.value(), 0x1);
445 assert_eq!(LtpReq::AllZeros.value(), 0x2);
446 assert_eq!(LtpReq::NyquistClock.value(), 0x3);
447 assert_eq!(LtpReq::DdeCompliance.value(), 0x4);
448 assert_eq!(LtpReq::Lfsr0.value(), 0x5);
449 assert_eq!(LtpReq::Lfsr1.value(), 0x6);
450 assert_eq!(LtpReq::Lfsr2.value(), 0x7);
451 assert_eq!(LtpReq::Lfsr3.value(), 0x8);
452 assert_eq!(LtpReq::FfeChange.value(), 0xE);
453 assert_eq!(LtpReq::RateChange.value(), 0xF);
454 assert_eq!(LtpReq::Reserved(0xB).value(), 0xB);
455 }
456
457 #[test]
458 fn ltp_req_patterns_match_by_value() {
459 assert_eq!(LtpReq::AllOnes.pattern(), Some(LtpPattern::AllOnes));
460 assert_eq!(LtpReq::AllZeros.pattern(), Some(LtpPattern::AllZeros));
461 assert_eq!(
462 LtpReq::NyquistClock.pattern(),
463 Some(LtpPattern::NyquistClock)
464 );
465 assert_eq!(
466 LtpReq::DdeCompliance.pattern(),
467 Some(LtpPattern::DdeCompliance)
468 );
469 assert_eq!(LtpReq::Lfsr0.pattern(), Some(LtpPattern::Lfsr0));
470 assert_eq!(LtpReq::Lfsr1.pattern(), Some(LtpPattern::Lfsr1));
471 assert_eq!(LtpReq::Lfsr2.pattern(), Some(LtpPattern::Lfsr2));
472 assert_eq!(LtpReq::Lfsr3.pattern(), Some(LtpPattern::Lfsr3));
473 }
474
475 #[test]
476 fn ltp_req_patterns_share_their_value_with_ltp_pattern() {
477 for req in [
478 LtpReq::AllOnes,
479 LtpReq::AllZeros,
480 LtpReq::NyquistClock,
481 LtpReq::DdeCompliance,
482 LtpReq::Lfsr0,
483 LtpReq::Lfsr1,
484 LtpReq::Lfsr2,
485 LtpReq::Lfsr3,
486 ] {
487 assert_eq!(req.pattern().map(LtpPattern::value), Some(req.value()));
488 }
489 }
490
491 #[test]
492 fn ltp_req_signals_have_no_pattern() {
493 assert_eq!(LtpReq::None.pattern(), None);
494 assert_eq!(LtpReq::FfeChange.pattern(), None);
495 assert_eq!(LtpReq::RateChange.pattern(), None);
496 assert_eq!(LtpReq::Reserved(0x9).pattern(), None);
497 }
498
499 #[test]
502 fn ffe_levels_accepts_0_to_7() {
503 for level in 0..=7 {
504 assert_eq!(FfeLevels::new(level).map(FfeLevels::value), Some(level));
505 }
506 }
507
508 #[test]
509 fn ffe_levels_rejects_above_7() {
510 assert_eq!(FfeLevels::new(8), None);
511 assert_eq!(FfeLevels::new(u8::MAX), None);
512 }
513
514 #[test]
515 fn ffe_levels_max_and_default() {
516 assert_eq!(FfeLevels::MAX.value(), 7);
517 assert_eq!(FfeLevels::default().value(), 0);
518 }
519
520 #[test]
521 fn ffe_levels_limited_to_3_up_to_12_gbps() {
522 for rate in [
523 HdmiForumFrl::Rate3Gbps3Lanes,
524 HdmiForumFrl::Rate6Gbps3Lanes,
525 HdmiForumFrl::Rate6Gbps4Lanes,
526 HdmiForumFrl::Rate8Gbps4Lanes,
527 HdmiForumFrl::Rate10Gbps4Lanes,
528 HdmiForumFrl::Rate12Gbps4Lanes,
529 ] {
530 assert_eq!(FfeLevels::MAX.limited_to(rate).value(), 3);
531 }
532 }
533
534 #[test]
535 fn ffe_levels_below_the_limit_are_unchanged() {
536 let two = FfeLevels::new(2).unwrap();
537 assert_eq!(two.limited_to(HdmiForumFrl::Rate12Gbps4Lanes), two);
538 assert_eq!(
539 FfeLevels::default().limited_to(HdmiForumFrl::Rate3Gbps3Lanes),
540 FfeLevels::default()
541 );
542 }
543
544 #[test]
547 fn update_flags_default_is_all_clear() {
548 let flags = UpdateFlags::default();
549 assert!(!flags.source_test_update);
550 assert!(!flags.frl_start);
551 assert!(!flags.flt_update);
552 }
553
554 #[test]
555 fn source_test_config_default_keeps_timeouts() {
556 assert!(!SourceTestConfig::default().flt_no_timeout);
557 }
558
559 #[test]
560 fn ltp_requests_compare_per_lane() {
561 let a = LtpRequests {
562 lane0: LtpReq::Lfsr0,
563 lane1: LtpReq::Lfsr1,
564 lane2: LtpReq::Lfsr2,
565 lane3: LtpReq::Lfsr3,
566 };
567 assert_eq!(a, a);
568 assert_ne!(
569 a,
570 LtpRequests {
571 lane3: LtpReq::None,
572 ..a
573 }
574 );
575 }
576
577 #[test]
580 fn ced_count_masks_validity_bit() {
581 let c = CedCount::new(0xFFFF);
583 assert_eq!(c.value(), 0x7FFF);
584 }
585
586 #[test]
587 fn ced_count_preserves_15_bit_value() {
588 let c = CedCount::new(0x0123);
589 assert_eq!(c.value(), 0x0123);
590 }
591
592 #[test]
593 fn ced_count_zero() {
594 assert_eq!(CedCount::new(0).value(), 0);
595 }
596
597 #[test]
598 fn ced_count_clone_eq() {
599 let a = CedCount::new(42);
600 assert_eq!(a, a.clone());
601 assert_ne!(CedCount::new(1), CedCount::new(2));
602 }
603
604 #[test]
607 fn update_flags_new_takes_the_flags_in_bit_order() {
608 let bit = |n: usize| {
609 let b = |i| i == n;
610 UpdateFlags::new(b(0), b(1), b(2), b(3), b(4), b(5), b(6))
611 };
612 let set = |f: UpdateFlags| {
613 [
614 f.status_update,
615 f.ced_update,
616 f.rr_test,
617 f.source_test_update,
618 f.frl_start,
619 f.flt_update,
620 f.rsed_update,
621 ]
622 };
623 for n in 0..7 {
624 let expected: [bool; 7] = core::array::from_fn(|i| i == n);
625 assert_eq!(set(bit(n)), expected, "bit {n}");
626 }
627 assert_eq!(UpdateFlags::default(), bit(7));
628 }
629
630 #[test]
631 fn clearable_keeps_every_flag_but_rr_test() {
632 let all = UpdateFlags::new(true, true, true, true, true, true, true);
633 assert_eq!(
634 all.clearable(),
635 ClearableUpdateFlags::new(true, true, true, true, true, true)
636 );
637 let rr_test_only = UpdateFlags::new(false, false, true, false, false, false, false);
638 assert_eq!(rr_test_only.clearable(), ClearableUpdateFlags::default());
639 }
640
641 #[test]
642 fn source_test_config_new_takes_the_bits_in_bit_order() {
643 let set = |c: SourceTestConfig| {
644 [
645 c.txffe_pre_shoot_only,
646 c.txffe_de_emphasis_only,
647 c.txffe_no_ffe,
648 c.flt_no_timeout,
649 c.dsc_frl_max,
650 c.frl_max,
651 ]
652 };
653 for n in 0..6 {
654 let b = |i| i == n;
655 let expected: [bool; 6] = core::array::from_fn(|i| i == n);
656 assert_eq!(
657 set(SourceTestConfig::new(b(0), b(1), b(2), b(3), b(4), b(5))),
658 expected,
659 "field {n}"
660 );
661 }
662 assert_eq!(set(SourceTestConfig::default()), [false; 6]);
663 }
664
665 #[test]
666 fn clearable_update_flags_new_takes_the_flags_in_bit_order() {
667 let c = ClearableUpdateFlags::new(true, false, true, false, true, false);
668 assert!(c.status_update && c.source_test_update && c.flt_update);
669 assert!(!c.ced_update && !c.frl_start && !c.rsed_update);
670 }
671
672 #[test]
675 fn ltp_requests_new_takes_the_lanes_in_order() {
676 let r = LtpRequests::new(LtpReq::Lfsr0, LtpReq::Lfsr1, LtpReq::Lfsr2, LtpReq::Lfsr3);
677 assert_eq!(
678 [r.lane0, r.lane1, r.lane2, r.lane3],
679 [LtpReq::Lfsr0, LtpReq::Lfsr1, LtpReq::Lfsr2, LtpReq::Lfsr3]
680 );
681 }
682
683 #[test]
684 fn all_trained_checks_all_four_lanes() {
685 let none = LtpReq::None;
686 assert!(LtpRequests::new(none, none, none, none).all_trained());
687 assert!(!LtpRequests::new(LtpReq::Lfsr0, none, none, none).all_trained());
688 assert!(!LtpRequests::new(none, none, none, LtpReq::Reserved(0x9)).all_trained());
690 }
691
692 #[test]
693 fn ced_counters_new_takes_the_lanes_in_order() {
694 let c = CedCounters::new(
695 Some(CedCount::new(0)),
696 Some(CedCount::new(1)),
697 Some(CedCount::new(2)),
698 None,
699 );
700 assert_eq!(c.lane0, Some(CedCount::new(0)));
701 assert_eq!(c.lane1, Some(CedCount::new(1)));
702 assert_eq!(c.lane2, Some(CedCount::new(2)));
703 assert_eq!(c.lane3, None);
704 }
705
706 #[test]
707 fn frl_config_new_takes_the_rate_and_levels() {
708 let c = FrlConfig::new(HdmiForumFrl::Rate6Gbps4Lanes, FfeLevels::new(2).unwrap());
709 assert_eq!(c.frl_rate, HdmiForumFrl::Rate6Gbps4Lanes);
710 assert_eq!(c.ffe_levels, FfeLevels::new(2).unwrap());
711 }
712}