1use display_types::cea861::hdmi_forum::HdmiForumFrl;
2
3#[non_exhaustive]
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11#[repr(u8)]
12pub enum LtpPattern {
13 AllOnes = 1,
15 AllZeros = 2,
17 NyquistClock = 3,
19 DdeCompliance = 4,
21 Lfsr0 = 5,
23 Lfsr1 = 6,
25 Lfsr2 = 7,
27 Lfsr3 = 8,
29}
30
31impl LtpPattern {
32 pub const fn value(self) -> u8 {
34 self as u8
35 }
36}
37
38#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
45pub struct LanePatterns {
46 pub lane0: Option<LtpPattern>,
48 pub lane1: Option<LtpPattern>,
50 pub lane2: Option<LtpPattern>,
52 pub lane3: Option<LtpPattern>,
54}
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
67pub enum FrlOutput {
68 GapOnly,
71 Active,
74}
75
76#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
82pub struct TxFfeLevel(u8);
83
84impl TxFfeLevel {
85 pub const MAX: Self = Self(7);
87
88 pub const fn new(level: u8) -> Option<Self> {
90 if level <= Self::MAX.0 {
91 Some(Self(level))
92 } else {
93 None
94 }
95 }
96
97 pub const fn value(self) -> u8 {
99 self.0
100 }
101}
102
103#[non_exhaustive]
105#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
106pub struct LaneEqParams {
107 pub tx_ffe_level: TxFfeLevel,
109}
110
111#[non_exhaustive]
134#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
135pub struct TxFfeRestrictions {
136 pub pre_shoot_only: bool,
138 pub de_emphasis_only: bool,
140 pub no_ffe: bool,
142}
143
144impl TxFfeRestrictions {
145 pub const fn new(pre_shoot_only: bool, de_emphasis_only: bool, no_ffe: bool) -> Self {
147 Self {
148 pre_shoot_only,
149 de_emphasis_only,
150 no_ffe,
151 }
152 }
153}
154
155#[non_exhaustive]
161#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
162pub struct EqParams {
163 pub lane0: LaneEqParams,
165 pub lane1: LaneEqParams,
167 pub lane2: LaneEqParams,
169 pub lane3: Option<LaneEqParams>,
171 pub tx_ffe_restrictions: TxFfeRestrictions,
173}
174
175impl EqParams {
176 pub fn new() -> Self {
178 Self::default()
179 }
180}
181
182pub trait HdmiPhy {
192 type Error;
194
195 fn set_frl_rate(&mut self, rate: HdmiForumFrl) -> Result<(), Self::Error>;
202
203 fn send_ltp(&mut self, patterns: LanePatterns) -> Result<(), Self::Error>;
205
206 fn set_frl_output(&mut self, output: FrlOutput) -> Result<(), Self::Error>;
209
210 fn adjust_equalization(&mut self, params: EqParams) -> Result<(), Self::Error>;
214
215 fn set_scrambling(&mut self, enabled: bool) -> Result<(), Self::Error>;
217}
218
219#[cfg(test)]
220mod tests {
221 use super::*;
222 use display_types::cea861::hdmi_forum::HdmiForumFrl;
223
224 struct MockPhy {
229 frl_rate: Option<HdmiForumFrl>,
230 scrambling: Option<bool>,
231 eq_calls: u32,
232 last_eq: Option<EqParams>,
233 frl_output: Option<FrlOutput>,
234 last_ltp: Option<LanePatterns>,
235 }
236
237 impl MockPhy {
238 fn new() -> Self {
239 Self {
240 frl_rate: None,
241 scrambling: None,
242 eq_calls: 0,
243 last_eq: None,
244 frl_output: None,
245 last_ltp: None,
246 }
247 }
248 }
249
250 impl HdmiPhy for MockPhy {
251 type Error = core::convert::Infallible;
252
253 fn send_ltp(&mut self, patterns: LanePatterns) -> Result<(), Self::Error> {
254 self.last_ltp = Some(patterns);
255 Ok(())
256 }
257
258 fn set_frl_output(&mut self, output: FrlOutput) -> Result<(), Self::Error> {
259 self.frl_output = Some(output);
260 Ok(())
261 }
262
263 fn set_frl_rate(&mut self, rate: HdmiForumFrl) -> Result<(), Self::Error> {
264 self.frl_rate = Some(rate);
265 Ok(())
266 }
267
268 fn adjust_equalization(&mut self, params: EqParams) -> Result<(), Self::Error> {
269 self.eq_calls += 1;
270 self.last_eq = Some(params);
271 Ok(())
272 }
273
274 fn set_scrambling(&mut self, enabled: bool) -> Result<(), Self::Error> {
275 self.scrambling = Some(enabled);
276 Ok(())
277 }
278 }
279
280 #[test]
281 fn ltp_pattern_values_match_scdc_encoding() {
282 assert_eq!(LtpPattern::AllOnes.value(), 1);
283 assert_eq!(LtpPattern::AllZeros.value(), 2);
284 assert_eq!(LtpPattern::NyquistClock.value(), 3);
285 assert_eq!(LtpPattern::DdeCompliance.value(), 4);
286 assert_eq!(LtpPattern::Lfsr0.value(), 5);
287 assert_eq!(LtpPattern::Lfsr1.value(), 6);
288 assert_eq!(LtpPattern::Lfsr2.value(), 7);
289 assert_eq!(LtpPattern::Lfsr3.value(), 8);
290 }
291
292 #[test]
293 fn ltp_pattern_clone_eq() {
294 let a = LtpPattern::Lfsr1;
295 assert_eq!(a, a);
296 assert_ne!(LtpPattern::Lfsr0, LtpPattern::Lfsr1);
297 }
298
299 #[test]
300 fn lane_patterns_default_is_no_pattern() {
301 let p = LanePatterns::default();
302 assert_eq!(p.lane0, None);
303 assert_eq!(p.lane1, None);
304 assert_eq!(p.lane2, None);
305 assert_eq!(p.lane3, None);
306 }
307
308 #[test]
309 fn send_ltp_records_patterns() {
310 let mut phy = MockPhy::new();
311 let patterns = LanePatterns {
312 lane0: Some(LtpPattern::Lfsr0),
313 lane1: Some(LtpPattern::Lfsr1),
314 lane2: Some(LtpPattern::Lfsr2),
315 lane3: None,
316 };
317 phy.send_ltp(patterns).unwrap();
318 assert_eq!(phy.last_ltp, Some(patterns));
319 }
320
321 #[test]
322 fn send_ltp_updates_on_each_call() {
323 let mut phy = MockPhy::new();
324 let first = LanePatterns {
325 lane0: Some(LtpPattern::Lfsr0),
326 ..LanePatterns::default()
327 };
328 let second = LanePatterns {
329 lane0: Some(LtpPattern::NyquistClock),
330 ..first
331 };
332 phy.send_ltp(first).unwrap();
333 phy.send_ltp(second).unwrap();
334 assert_eq!(phy.last_ltp, Some(second));
335 }
336
337 #[test]
338 fn eq_params_constructors_are_equivalent() {
339 let _a = EqParams::new();
340 let _b = EqParams::default();
341 }
342
343 #[test]
344 fn set_frl_rate_records_rate() {
345 let mut phy = MockPhy::new();
346 phy.set_frl_rate(HdmiForumFrl::Rate6Gbps4Lanes).unwrap();
347 assert_eq!(phy.frl_rate, Some(HdmiForumFrl::Rate6Gbps4Lanes));
348 }
349
350 #[test]
351 fn set_frl_rate_not_supported_is_valid() {
352 let mut phy = MockPhy::new();
353 phy.set_frl_rate(HdmiForumFrl::NotSupported).unwrap();
354 assert_eq!(phy.frl_rate, Some(HdmiForumFrl::NotSupported));
355 }
356
357 #[test]
358 fn set_frl_rate_can_be_updated() {
359 let mut phy = MockPhy::new();
360 phy.set_frl_rate(HdmiForumFrl::Rate3Gbps3Lanes).unwrap();
361 phy.set_frl_rate(HdmiForumFrl::Rate12Gbps4Lanes).unwrap();
362 assert_eq!(phy.frl_rate, Some(HdmiForumFrl::Rate12Gbps4Lanes));
363 }
364
365 #[test]
366 fn set_scrambling_enable() {
367 let mut phy = MockPhy::new();
368 phy.set_scrambling(true).unwrap();
369 assert_eq!(phy.scrambling, Some(true));
370 }
371
372 #[test]
373 fn set_scrambling_disable() {
374 let mut phy = MockPhy::new();
375 phy.set_scrambling(false).unwrap();
376 assert_eq!(phy.scrambling, Some(false));
377 }
378
379 #[test]
380 fn set_scrambling_can_be_toggled() {
381 let mut phy = MockPhy::new();
382 phy.set_scrambling(true).unwrap();
383 phy.set_scrambling(false).unwrap();
384 assert_eq!(phy.scrambling, Some(false));
385 }
386
387 #[test]
388 fn set_frl_output_records_mode() {
389 let mut phy = MockPhy::new();
390 phy.set_frl_output(FrlOutput::GapOnly).unwrap();
391 assert_eq!(phy.frl_output, Some(FrlOutput::GapOnly));
392 }
393
394 #[test]
395 fn set_frl_output_can_be_switched() {
396 let mut phy = MockPhy::new();
397 phy.set_frl_output(FrlOutput::GapOnly).unwrap();
398 phy.set_frl_output(FrlOutput::Active).unwrap();
399 assert_eq!(phy.frl_output, Some(FrlOutput::Active));
400 }
401
402 #[test]
403 fn value_types_can_be_hashed() {
404 extern crate std;
405 use std::collections::HashSet;
406 let patterns: HashSet<LanePatterns> = [LanePatterns::default(), LanePatterns::default()]
407 .into_iter()
408 .collect();
409 assert_eq!(patterns.len(), 1);
410 let outputs: HashSet<FrlOutput> = [FrlOutput::GapOnly, FrlOutput::Active].into();
411 assert_eq!(outputs.len(), 2);
412 let levels: HashSet<TxFfeLevel> = [TxFfeLevel::MAX, TxFfeLevel::default()].into();
413 assert_eq!(levels.len(), 2);
414 let eq: HashSet<EqParams> = [EqParams::new(), EqParams::new()].into();
415 assert_eq!(eq.len(), 1);
416 let ltp: HashSet<LtpPattern> = [LtpPattern::Lfsr0, LtpPattern::Lfsr0].into();
417 assert_eq!(ltp.len(), 1);
418 }
419
420 #[test]
421 fn tx_ffe_level_accepts_0_to_7() {
422 for level in 0..=7 {
423 assert_eq!(TxFfeLevel::new(level).map(TxFfeLevel::value), Some(level));
424 }
425 }
426
427 #[test]
428 fn tx_ffe_level_rejects_above_7() {
429 assert_eq!(TxFfeLevel::new(8), None);
430 assert_eq!(TxFfeLevel::new(u8::MAX), None);
431 }
432
433 #[test]
434 fn tx_ffe_level_max_and_default() {
435 assert_eq!(TxFfeLevel::MAX.value(), 7);
436 assert_eq!(TxFfeLevel::default().value(), 0);
437 assert!(TxFfeLevel::default() < TxFfeLevel::MAX);
438 }
439
440 #[test]
441 fn lane_eq_params_default_tx_ffe_level_is_0() {
442 assert_eq!(LaneEqParams::default().tx_ffe_level, TxFfeLevel::default());
443 }
444
445 #[test]
446 fn tx_ffe_restrictions_new_takes_the_bits_in_register_order() {
447 let r = TxFfeRestrictions::new(true, false, false);
448 assert!(r.pre_shoot_only && !r.de_emphasis_only && !r.no_ffe);
449 let r = TxFfeRestrictions::new(false, true, false);
450 assert!(!r.pre_shoot_only && r.de_emphasis_only && !r.no_ffe);
451 let r = TxFfeRestrictions::new(false, false, true);
452 assert!(!r.pre_shoot_only && !r.de_emphasis_only && r.no_ffe);
453 let both = TxFfeRestrictions::new(true, true, false);
455 assert!(both.pre_shoot_only && both.de_emphasis_only);
456 }
457
458 #[test]
459 fn eq_params_restrict_nothing_by_default() {
460 assert_eq!(
461 TxFfeRestrictions::default(),
462 TxFfeRestrictions::new(false, false, false)
463 );
464 assert_eq!(
465 EqParams::new().tx_ffe_restrictions,
466 TxFfeRestrictions::default()
467 );
468 }
469
470 #[test]
471 fn adjust_equalization_records_per_lane_levels() {
472 let mut phy = MockPhy::new();
473 let mut params = EqParams::new();
474 params.lane1.tx_ffe_level = TxFfeLevel::MAX;
475 phy.adjust_equalization(params).unwrap();
476 let last = phy.last_eq.unwrap();
477 assert_eq!(last, params);
478 assert_eq!(last.lane0.tx_ffe_level.value(), 0);
479 assert_eq!(last.lane1.tx_ffe_level.value(), 7);
480 }
481
482 #[test]
483 fn adjust_equalization_tracks_call_count() {
484 let mut phy = MockPhy::new();
485 phy.adjust_equalization(EqParams::new()).unwrap();
486 phy.adjust_equalization(EqParams::new()).unwrap();
487 assert_eq!(phy.eq_calls, 2);
488 }
489}