1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
use crate;
/// Typed SCDC interface required by the link training state machine, one method per
/// register operation it performs.
///
/// Defined here so the state machine has no dependency on any specific SCDC
/// implementation. SCDC crates implement this trait; plumbob calls it.
///
/// # Implementer responsibilities
///
/// plumbob treats every `Ok` return as correct and does not re-validate it.
/// Implementers are responsible for:
///
/// - **Correct register mapping.** Returned values must reflect the sink's registers at
/// the time of the read. An undefined request value (0x9–0xD) in `Status_Flags_1/2`
/// is returned as [`LtpReq::Reserved`](crate::LtpReq::Reserved) with its raw value,
/// not as an error: whether it matters depends on the lanes in use, so plumbob decides
/// (it leaves the lane as it was and reports a `TrainingWarning`).
///
/// - **Bus-level error handling.** Any I²C/DDC timeout, NACK or protocol error must
/// surface as `Err(Self::Error)`. Returning a zeroed or default `Ok` value in place of
/// an error corrupts the state machine: a zeroed [`LtpRequests`] reads as every lane
/// passing training.
///
/// - **Polling cadence.** plumbob polls [`read_flt_ready`](Self::read_flt_ready) in
/// LTS:2 and [`read_update_flags`](Self::read_update_flags) in LTS:3 and LTS:P in a
/// tight loop bounded by the poll limits in `TrainingConfig`. The implementer controls
/// how long each call takes and therefore how much wall time a poll represents; the
/// default limits assume one poll every 2 ms. Sleeping or yielding inside these
/// methods is the correct place to enforce that interval.
///
/// - **Stopping early.** The polled methods are also where a wall-clock deadline, a
/// cancel flag or any other reason to stop belongs. Return one error of your own from a
/// polled read, then let every later call through: training stops, LTS:L returns the
/// link to TMDS, and `train` returns
/// [`TrainingError::Scdc`](crate::TrainingError::Scdc) with that error and
/// `TmdsExit::Exited`. LTS:L's own `read_update_flags` is a single read, not a poll, but
/// it is the same method, so never fail a read made after a
/// [`write_frl_config`](Self::write_frl_config) with `HdmiForumFrl::NotSupported`
/// (LTS:L's first SCDC write; a later write with an FRL rate starts training again): a
/// deadline that passes while the attempt is already ending then lets it end with its
/// own result. The exit is `TmdsExit::Skipped` with
/// `TrainingConfig::exit_to_tmds_on_error` off, and `TmdsExit::HeldUnderTest` (the link
/// left as it is) while the sink sets `FLT_no_timeout`.