Skip to main content

culvert_async/client/
scrambling.rs

1use hdmi_hal_async::scdc::ScdcTransport;
2
3use crate::{ScdcError, ScramblerStatus, TmdsConfig};
4
5use super::Scdc;
6use super::address;
7
8impl<T: ScdcTransport> Scdc<T> {
9    /// Writes scrambling configuration to `TMDS_Config` (0x20).
10    ///
11    /// Sets `Scrambling_Enable` (bit 0) and `TMDS_Bit_Clock_Ratio` (bit 1).
12    pub async fn write_tmds_config(
13        &mut self,
14        config: TmdsConfig,
15    ) -> Result<(), ScdcError<T::Error>> {
16        let byte = (config.scrambling_enable as u8) | ((config.high_tmds_clock_ratio as u8) << 1);
17        self.transport
18            .write(address::TMDS_CONFIG, byte)
19            .await
20            .map_err(ScdcError::Transport)
21    }
22
23    /// Reads scrambler acknowledgement from `Scrambler_Status` (0x21).
24    ///
25    /// Returns [`ScramblerStatus::scrambling_active`] set when the sink confirms
26    /// that TMDS scrambling is active (bit 0).
27    pub async fn read_scrambler_status(&mut self) -> Result<ScramblerStatus, ScdcError<T::Error>> {
28        let byte = self
29            .transport
30            .read(address::SCRAMBLER_STATUS)
31            .await
32            .map_err(ScdcError::Transport)?;
33        Ok(ScramblerStatus::new(byte & 0x01 != 0))
34    }
35}
36
37#[cfg(test)]
38mod tests {
39    use super::super::Scdc;
40    use super::super::test_transport::TestTransport;
41    use crate::{ScramblerStatus, TmdsConfig};
42
43    #[pollster::test]
44    async fn tmds_config_scrambling_only() {
45        let mut scdc = Scdc::new(TestTransport::new());
46        scdc.write_tmds_config(TmdsConfig {
47            scrambling_enable: true,
48            high_tmds_clock_ratio: false,
49        })
50        .await
51        .unwrap();
52        assert_eq!(scdc.into_transport().get(0x20), 0x01);
53    }
54
55    #[pollster::test]
56    async fn tmds_config_clock_ratio_only() {
57        let mut scdc = Scdc::new(TestTransport::new());
58        scdc.write_tmds_config(TmdsConfig {
59            scrambling_enable: false,
60            high_tmds_clock_ratio: true,
61        })
62        .await
63        .unwrap();
64        assert_eq!(scdc.into_transport().get(0x20), 0x02);
65    }
66
67    #[pollster::test]
68    async fn tmds_config_both_clear() {
69        let mut scdc = Scdc::new(TestTransport::new());
70        scdc.write_tmds_config(TmdsConfig {
71            scrambling_enable: false,
72            high_tmds_clock_ratio: false,
73        })
74        .await
75        .unwrap();
76        assert_eq!(scdc.into_transport().get(0x20), 0x00);
77    }
78
79    #[pollster::test]
80    async fn scrambler_status_bit0_set() {
81        let mut sim = TestTransport::new();
82        sim.set(0x21, 0xFF); // all bits set; only bit 0 is defined
83        assert_eq!(
84            Scdc::new(sim).read_scrambler_status().await.unwrap(),
85            ScramblerStatus::new(true)
86        );
87    }
88
89    #[pollster::test]
90    async fn scrambler_status_bit0_clear() {
91        let mut sim = TestTransport::new();
92        sim.set(0x21, 0xFE); // bit 0 clear, other bits set (reserved)
93        assert_eq!(
94            Scdc::new(sim).read_scrambler_status().await.unwrap(),
95            ScramblerStatus::new(false)
96        );
97    }
98
99    #[pollster::test]
100    async fn transport_error_propagates() {
101        assert!(
102            Scdc::new(TestTransport::failing_after(0))
103                .write_tmds_config(TmdsConfig {
104                    scrambling_enable: false,
105                    high_tmds_clock_ratio: false
106                })
107                .await
108                .is_err()
109        );
110        assert!(
111            Scdc::new(TestTransport::failing_after(0))
112                .read_scrambler_status()
113                .await
114                .is_err()
115        );
116    }
117}