1use hdmi_hal_async::scdc::ScdcTransport;
4use plumbob_async::ScdcClient;
5
6use crate::{FfeLevels, FrlConfig, LtpReq, ScdcError};
7
8use super::Scdc;
9
10impl<T: ScdcTransport> ScdcClient for Scdc<T> {
11 type Error = ScdcError<T::Error>;
12
13 async fn write_frl_config(
14 &mut self,
15 config: plumbob_async::FrlConfig,
16 ) -> Result<(), Self::Error> {
17 self.write_frl_config(FrlConfig {
18 frl_rate: config.rate,
19 dsc_frl_max: config.dsc_frl_max,
20 ffe_levels: ffe_levels(config.ffe_levels),
21 })
22 .await
23 }
24
25 async fn read_training_status(&mut self) -> Result<plumbob_async::TrainingStatus, Self::Error> {
26 let f = self.read_status_flags().await?;
27 Ok(plumbob_async::TrainingStatus {
28 flt_ready: f.flt_ready,
29 frl_start: f.frl_start,
30 ltp_req: ltp_req(f.ltp_req),
31 })
32 }
33
34 async fn read_ced(&mut self) -> Result<plumbob_async::CedCounters, Self::Error> {
35 let c = self.read_ced().await?;
36 Ok(plumbob_async::CedCounters {
37 lane0: c.lane0.map(ced_count),
38 lane1: c.lane1.map(ced_count),
39 lane2: c.lane2.map(ced_count),
40 lane3: c.lane3.map(ced_count),
41 })
42 }
43}
44
45fn ffe_levels(f: plumbob_async::FfeLevels) -> FfeLevels {
46 match f {
47 plumbob_async::FfeLevels::Ffe0 => FfeLevels::Ffe0,
48 plumbob_async::FfeLevels::Ffe1 => FfeLevels::Ffe1,
49 plumbob_async::FfeLevels::Ffe2 => FfeLevels::Ffe2,
50 plumbob_async::FfeLevels::Ffe3 => FfeLevels::Ffe3,
51 plumbob_async::FfeLevels::Ffe4 => FfeLevels::Ffe4,
52 plumbob_async::FfeLevels::Ffe5 => FfeLevels::Ffe5,
53 plumbob_async::FfeLevels::Ffe6 => FfeLevels::Ffe6,
54 plumbob_async::FfeLevels::Ffe7 => FfeLevels::Ffe7,
55 }
56}
57
58fn ltp_req(req: LtpReq) -> plumbob_async::LtpReq {
59 match req {
60 LtpReq::None => plumbob_async::LtpReq::None,
61 LtpReq::Lfsr0 => plumbob_async::LtpReq::Lfsr0,
62 LtpReq::Lfsr1 => plumbob_async::LtpReq::Lfsr1,
63 LtpReq::Lfsr2 => plumbob_async::LtpReq::Lfsr2,
64 LtpReq::Lfsr3 => plumbob_async::LtpReq::Lfsr3,
65 _ => unreachable!("unknown LtpReq variant"),
66 }
67}
68
69fn ced_count(c: crate::CedCount) -> plumbob_async::CedCount {
70 plumbob_async::CedCount::new(c.value())
71}
72
73#[cfg(test)]
74mod tests {
75 use super::super::Scdc;
76 use super::super::test_transport::TestTransport;
77 use display_types::cea861::hdmi_forum::HdmiForumFrl;
78 use plumbob_async::{FfeLevels, FrlConfig, LtpReq, ScdcClient};
79
80 #[pollster::test]
83 async fn write_frl_config_rate_and_ffe() {
84 let mut scdc = Scdc::new(TestTransport::new());
86 ScdcClient::write_frl_config(
87 &mut scdc,
88 FrlConfig {
89 rate: HdmiForumFrl::Rate12Gbps4Lanes,
90 ffe_levels: FfeLevels::Ffe3,
91 dsc_frl_max: false,
92 },
93 )
94 .await
95 .unwrap();
96 assert_eq!(scdc.into_transport().get(0x30), 0x06 | 0x60);
97 }
98
99 #[pollster::test]
100 async fn write_frl_config_dsc_frl_max() {
101 let mut scdc = Scdc::new(TestTransport::new());
102 ScdcClient::write_frl_config(
103 &mut scdc,
104 FrlConfig {
105 rate: HdmiForumFrl::NotSupported,
106 ffe_levels: FfeLevels::Ffe0,
107 dsc_frl_max: true,
108 },
109 )
110 .await
111 .unwrap();
112 assert_eq!(scdc.into_transport().get(0x30), 0x10);
113 }
114
115 #[pollster::test]
116 async fn write_frl_config_all_ffe_levels() {
117 use FfeLevels::*;
118 for (ffe, expected_bits) in [
119 (Ffe0, 0x00u8),
120 (Ffe1, 0x20),
121 (Ffe2, 0x40),
122 (Ffe3, 0x60),
123 (Ffe4, 0x80),
124 (Ffe5, 0xA0),
125 (Ffe6, 0xC0),
126 (Ffe7, 0xE0),
127 ] {
128 let mut scdc = Scdc::new(TestTransport::new());
129 ScdcClient::write_frl_config(
130 &mut scdc,
131 FrlConfig {
132 rate: HdmiForumFrl::NotSupported,
133 ffe_levels: ffe,
134 dsc_frl_max: false,
135 },
136 )
137 .await
138 .unwrap();
139 assert_eq!(
140 scdc.into_transport().get(0x30),
141 expected_bits,
142 "ffe={ffe:?}"
143 );
144 }
145 }
146
147 #[pollster::test]
150 async fn read_training_status_flt_ready() {
151 let mut sim = TestTransport::new();
152 sim.set(0x40, 0x40); let status = Scdc::new(sim).read_training_status().await.unwrap();
154 assert!(status.flt_ready);
155 assert!(!status.frl_start);
156 assert_eq!(status.ltp_req, LtpReq::None);
157 }
158
159 #[pollster::test]
160 async fn read_training_status_frl_start() {
161 let mut sim = TestTransport::new();
162 sim.set(0x41, 0x01); let status = Scdc::new(sim).read_training_status().await.unwrap();
164 assert!(!status.flt_ready);
165 assert!(status.frl_start);
166 }
167
168 #[pollster::test]
169 async fn read_training_status_ltp_req_variants() {
170 for (nibble, expected) in [
171 (0u8, LtpReq::None),
172 (1, LtpReq::Lfsr0),
173 (2, LtpReq::Lfsr1),
174 (3, LtpReq::Lfsr2),
175 (4, LtpReq::Lfsr3),
176 ] {
177 let mut sim = TestTransport::new();
178 sim.set(0x41, nibble << 4);
179 assert_eq!(
180 Scdc::new(sim).read_training_status().await.unwrap().ltp_req,
181 expected,
182 "nibble={nibble}"
183 );
184 }
185 }
186
187 #[pollster::test]
188 async fn read_training_status_unknown_ltp_req_is_error() {
189 let mut sim = TestTransport::new();
190 sim.set(0x41, 5 << 4); assert!(Scdc::new(sim).read_training_status().await.is_err());
192 }
193
194 #[pollster::test]
197 async fn read_ced_lane0_valid() {
198 let mut sim = TestTransport::new();
199 sim.set(0x50, 0x23); sim.set(0x51, 0x81); let ced = Scdc::new(sim).read_ced().await.unwrap();
202 assert_eq!(ced.lane0.map(|c| c.value()), Some(0x0123));
203 }
204
205 #[pollster::test]
206 async fn read_ced_lane_invalid_when_validity_clear() {
207 let mut sim = TestTransport::new();
208 sim.set(0x50, 0xFF);
209 sim.set(0x51, 0x7F); let ced = Scdc::new(sim).read_ced().await.unwrap();
211 assert_eq!(ced.lane0, None);
212 }
213
214 #[pollster::test]
215 async fn read_ced_all_lanes_propagate() {
216 let mut sim = TestTransport::new();
217 for (base, val) in [(0x50u8, 1u8), (0x52, 2), (0x54, 3), (0x56, 4)] {
218 sim.set(base, val);
219 sim.set(base + 1, 0x80);
220 }
221 let ced = Scdc::new(sim).read_ced().await.unwrap();
222 assert_eq!(ced.lane0.map(|c| c.value()), Some(1));
223 assert_eq!(ced.lane1.map(|c| c.value()), Some(2));
224 assert_eq!(ced.lane2.map(|c| c.value()), Some(3));
225 assert_eq!(ced.lane3.map(|c| c.value()), Some(4));
226 }
227
228 #[pollster::test]
231 async fn write_frl_config_transport_error() {
232 assert!(
233 ScdcClient::write_frl_config(
234 &mut Scdc::new(TestTransport::failing_after(0)),
235 FrlConfig {
236 rate: HdmiForumFrl::NotSupported,
237 ffe_levels: FfeLevels::Ffe0,
238 dsc_frl_max: false,
239 }
240 )
241 .await
242 .is_err()
243 );
244 }
245
246 #[pollster::test]
247 async fn read_training_status_transport_error() {
248 assert!(
249 Scdc::new(TestTransport::failing_after(0))
250 .read_training_status()
251 .await
252 .is_err()
253 );
254 }
255
256 #[pollster::test]
257 async fn read_ced_transport_error() {
258 for n in 0..8 {
259 assert!(
260 Scdc::new(TestTransport::failing_after(n))
261 .read_ced()
262 .await
263 .is_err(),
264 "should fail when transport fails after {n} ops"
265 );
266 }
267 }
268}