1use hdmi_hal_async::scdc::ScdcTransport;
2
3use crate::ScdcError;
4use crate::{Config0, FrlConfig, LtpRequests, SourceTestConfig, StatusFlags};
5use culvert::codec;
6
7use super::Scdc;
8
9impl<T: ScdcTransport> Scdc<T> {
10 pub async fn write_frl_config(&mut self, config: FrlConfig) -> Result<(), ScdcError<T::Error>> {
18 let byte = codec::encode_config_1(config).map_err(ScdcError::Protocol)?;
19 self.transport
20 .write(codec::CONFIG_1, byte)
21 .await
22 .map_err(ScdcError::Transport)
23 }
24
25 pub async fn write_config_0(&mut self, config: Config0) -> Result<(), ScdcError<T::Error>> {
27 self.transport
28 .write(codec::CONFIG_0, codec::encode_config_0(config))
29 .await
30 .map_err(ScdcError::Transport)
31 }
32
33 pub async fn read_source_test_config(
36 &mut self,
37 ) -> Result<SourceTestConfig, ScdcError<T::Error>> {
38 let byte = self
39 .transport
40 .read(codec::SOURCE_TEST_CONFIG)
41 .await
42 .map_err(ScdcError::Transport)?;
43 Ok(codec::decode_source_test_config(byte))
44 }
45
46 pub async fn read_status_flags(&mut self) -> Result<StatusFlags, ScdcError<T::Error>> {
49 let flags0 = self
50 .transport
51 .read(codec::STATUS_FLAGS_0)
52 .await
53 .map_err(ScdcError::Transport)?;
54 Ok(codec::decode_status_flags(flags0))
55 }
56
57 pub async fn read_ltp_requests(&mut self) -> Result<LtpRequests, ScdcError<T::Error>> {
63 let flags1 = self
64 .transport
65 .read(codec::STATUS_FLAGS_1)
66 .await
67 .map_err(ScdcError::Transport)?;
68 let flags2 = self
69 .transport
70 .read(codec::STATUS_FLAGS_2)
71 .await
72 .map_err(ScdcError::Transport)?;
73 Ok(codec::decode_ltp_requests(flags1, flags2))
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use super::super::Scdc;
80 use super::super::test_transport::{Op, TestTransport};
81 use crate::{Config0, FfeLevels, FrlConfig, LtpReq, SourceTestConfig, StatusFlags};
82 use crate::{ProtocolError, ScdcError};
83 use display_types::HdmiForumFrl;
84
85 #[pollster::test]
86 async fn frl_config_rate_field() {
87 let mut scdc = Scdc::new(TestTransport::new());
88 scdc.write_frl_config(FrlConfig::new(
89 HdmiForumFrl::Rate12Gbps4Lanes, FfeLevels::default(),
91 ))
92 .await
93 .unwrap();
94 let t = scdc.into_transport();
95 assert_eq!(t.get(0x31), 0x06);
96 assert_eq!(t.get(0x30), 0x00); }
98
99 #[pollster::test]
100 async fn frl_config_ffe_levels_field() {
101 let mut scdc = Scdc::new(TestTransport::new());
102 scdc.write_frl_config(FrlConfig::new(
103 HdmiForumFrl::Rate12Gbps4Lanes,
104 FfeLevels::new(3).unwrap(), ))
106 .await
107 .unwrap();
108 assert_eq!(scdc.into_transport().get(0x31), 0x36);
109 }
110
111 #[pollster::test]
112 async fn frl_config_rejects_ffe_levels_above_rate_maximum() {
113 let mut scdc = Scdc::new(TestTransport::new());
114 let result = scdc
115 .write_frl_config(FrlConfig::new(
116 HdmiForumFrl::Rate12Gbps4Lanes,
117 FfeLevels::new(4).unwrap(),
118 ))
119 .await;
120 assert!(matches!(
121 result,
122 Err(ScdcError::Protocol(ProtocolError::FfeLevelsOutOfRange {
123 rate: HdmiForumFrl::Rate12Gbps4Lanes,
124 levels: 4
125 }))
126 ));
127 assert_eq!(scdc.into_transport().get(0x31), 0x00);
129 }
130
131 #[pollster::test]
132 async fn status_flags_all_zero() {
133 let f = Scdc::new(TestTransport::new())
134 .read_status_flags()
135 .await
136 .unwrap();
137 assert_eq!(
138 f,
139 StatusFlags::new(false, false, false, false, false, false, false)
140 );
141 }
142
143 #[pollster::test]
144 async fn status_flags_individual_bits() {
145 let read = |byte: u8| {
146 let mut sim = TestTransport::new();
147 sim.set(0x40, byte);
148 pollster::block_on(Scdc::new(sim).read_status_flags()).unwrap()
149 };
150 assert!(read(0x01).clock_detected);
151 assert!(read(0x02).ch0_locked);
152 assert!(read(0x04).ch1_locked);
153 assert!(read(0x08).ch2_locked);
154 assert!(read(0x10).ln3_locked);
155 assert!(read(0x40).flt_ready);
156 assert!(read(0x80).dsc_decode_fail);
157 assert_eq!(read(0x20), read(0x00));
159 }
160
161 #[pollster::test]
162 async fn status_flags_reads_only_status_flags_0() {
163 let mut sim = TestTransport::new();
164 sim.set(0x41, 0xFF);
165 sim.set(0x42, 0xFF);
166 let f = Scdc::new(sim).read_status_flags().await.unwrap();
167 assert!(!f.flt_ready && !f.clock_detected);
168 }
169
170 #[pollster::test]
171 async fn ltp_requests_per_lane() {
172 let mut sim = TestTransport::new();
173 sim.set(0x41, 0x65); sim.set(0x42, 0xF1); let r = Scdc::new(sim).read_ltp_requests().await.unwrap();
176 assert_eq!(r.lane0, LtpReq::Lfsr0);
177 assert_eq!(r.lane1, LtpReq::Lfsr1);
178 assert_eq!(r.lane2, LtpReq::AllOnes);
179 assert_eq!(r.lane3, LtpReq::RateChange);
180 assert!(!r.all_trained());
181 }
182
183 #[pollster::test]
184 async fn ltp_requests_all_defined_values() {
185 for (nibble, req) in [
186 (0x0u8, LtpReq::None),
187 (0x1, LtpReq::AllOnes),
188 (0x2, LtpReq::AllZeros),
189 (0x3, LtpReq::NyquistClock),
190 (0x4, LtpReq::DdeCompliance),
191 (0x5, LtpReq::Lfsr0),
192 (0x6, LtpReq::Lfsr1),
193 (0x7, LtpReq::Lfsr2),
194 (0x8, LtpReq::Lfsr3),
195 (0xE, LtpReq::FfeChange),
196 (0xF, LtpReq::RateChange),
197 ] {
198 let mut sim = TestTransport::new();
199 sim.set(0x42, nibble << 4); assert_eq!(Scdc::new(sim).read_ltp_requests().await.unwrap().lane3, req);
201 }
202 }
203
204 #[pollster::test]
205 async fn ltp_requests_undefined_values_are_reserved() {
206 for nibble in 0x9u8..=0xD {
207 let mut sim = TestTransport::new();
208 sim.set(0x41, nibble); let requests = Scdc::new(sim).read_ltp_requests().await.unwrap();
210 assert_eq!(requests.lane0, LtpReq::Reserved(nibble));
211 assert_eq!(requests.lane1, LtpReq::None);
212 }
213 }
214
215 #[pollster::test]
216 async fn ltp_requests_all_trained_when_zero() {
217 assert!(
218 Scdc::new(TestTransport::new())
219 .read_ltp_requests()
220 .await
221 .unwrap()
222 .all_trained()
223 );
224 }
225
226 #[pollster::test]
227 async fn config_0_bits() {
228 for (config, expected) in [
229 (Config0::default(), 0x00u8),
230 (
231 Config0 {
232 rr_enable: true,
233 flt_no_retrain: false,
234 },
235 0x01,
236 ),
237 (
238 Config0 {
239 rr_enable: false,
240 flt_no_retrain: true,
241 },
242 0x02,
243 ),
244 (
245 Config0 {
246 rr_enable: true,
247 flt_no_retrain: true,
248 },
249 0x03,
250 ),
251 ] {
252 let mut scdc = Scdc::new(TestTransport::new());
253 scdc.write_config_0(config).await.unwrap();
254 assert_eq!(scdc.into_transport().get(0x30), expected, "{config:?}");
255 }
256 }
257
258 #[pollster::test]
259 async fn source_test_config_bits() {
260 let read = |byte: u8| {
261 let mut sim = TestTransport::new();
262 sim.set(0x35, byte);
263 pollster::block_on(Scdc::new(sim).read_source_test_config()).unwrap()
264 };
265 let fields = |c: SourceTestConfig| {
266 [
267 c.txffe_pre_shoot_only,
268 c.txffe_de_emphasis_only,
269 c.txffe_no_ffe,
270 c.flt_no_timeout,
271 c.dsc_frl_max,
272 c.frl_max,
273 ]
274 };
275 for (i, bit) in [1u8, 2, 3, 5, 6, 7].into_iter().enumerate() {
276 let mut expected = [false; 6];
277 expected[i] = true;
278 assert_eq!(fields(read(1 << bit)), expected, "bit {bit}");
279 }
280 assert_eq!(read(0x11), read(0x00));
282 assert_eq!(
283 read(0x00),
284 SourceTestConfig::new(false, false, false, false, false, false)
285 );
286 }
287
288 #[pollster::test]
289 async fn read_status_flags_reads_status_flags_0_once() {
290 let mut scdc = Scdc::new(TestTransport::new());
291 scdc.read_status_flags().await.unwrap();
292 assert_eq!(
293 scdc.into_transport().log()[..2],
294 [Some(Op::Read(0x40)), None]
295 );
296 }
297
298 #[pollster::test]
299 async fn read_ltp_requests_reads_status_flags_1_then_2() {
300 let mut scdc = Scdc::new(TestTransport::new());
301 scdc.read_ltp_requests().await.unwrap();
302 assert_eq!(
303 scdc.into_transport().log()[..3],
304 [Some(Op::Read(0x41)), Some(Op::Read(0x42)), None]
305 );
306 }
307
308 #[pollster::test]
309 async fn transport_error_propagates() {
310 assert!(
311 Scdc::new(TestTransport::failing_after(0))
312 .write_frl_config(FrlConfig::new(
313 HdmiForumFrl::NotSupported,
314 FfeLevels::default(),
315 ))
316 .await
317 .is_err_and(|error| matches!(error, ScdcError::Transport(())))
318 );
319 assert!(
320 Scdc::new(TestTransport::failing_after(0))
321 .read_status_flags()
322 .await
323 .is_err_and(|error| matches!(error, ScdcError::Transport(())))
324 );
325 assert!(
326 Scdc::new(TestTransport::failing_after(0))
327 .write_config_0(Config0::default())
328 .await
329 .is_err_and(|error| matches!(error, ScdcError::Transport(())))
330 );
331 assert!(
332 Scdc::new(TestTransport::failing_after(0))
333 .read_source_test_config()
334 .await
335 .is_err_and(|error| matches!(error, ScdcError::Transport(())))
336 );
337 for n in 0..2 {
339 assert!(
340 Scdc::new(TestTransport::failing_after(n))
341 .read_ltp_requests()
342 .await
343 .is_err_and(|error| matches!(error, ScdcError::Transport(())))
344 );
345 }
346 }
347}