use hdmi_hal::scdc::ScdcTransport;
use plumbob::ScdcClient;
use crate::error::ScdcError;
use crate::register::{
CedCount, CedCounters, ClearableUpdateFlags, Config0, FfeLevels, FrlConfig, LtpReq,
LtpRequests, SourceTestConfig, UpdateFlags,
};
use super::Scdc;
impl<T: ScdcTransport> ScdcClient for Scdc<T> {
type Error = ScdcError<T::Error>;
fn read_flt_ready(&mut self) -> Result<bool, Self::Error> {
Ok(Scdc::read_status_flags(self)?.flt_ready)
}
fn read_update_flags(&mut self) -> Result<plumbob::UpdateFlags, Self::Error> {
Scdc::read_update_flags(self).map(Into::into)
}
fn clear_update_flags(
&mut self,
flags: plumbob::ClearableUpdateFlags,
) -> Result<(), Self::Error> {
Scdc::clear_update_flags(self, flags.into())
}
fn read_ltp_requests(&mut self) -> Result<plumbob::LtpRequests, Self::Error> {
Scdc::read_ltp_requests(self).map(Into::into)
}
fn read_source_test_config(&mut self) -> Result<plumbob::SourceTestConfig, Self::Error> {
Scdc::read_source_test_config(self).map(Into::into)
}
fn write_config_0_defaults(&mut self) -> Result<(), Self::Error> {
Scdc::write_config_0(self, Config0::default())
}
fn write_frl_config(&mut self, config: plumbob::FrlConfig) -> Result<(), Self::Error> {
Scdc::write_frl_config(self, config.into())
}
fn read_ced(&mut self) -> Result<plumbob::CedCounters, Self::Error> {
Scdc::read_ced(self).map(Into::into)
}
}
impl From<LtpReq> for plumbob::LtpReq {
fn from(req: LtpReq) -> Self {
match req {
LtpReq::None => Self::None,
LtpReq::AllOnes => Self::AllOnes,
LtpReq::AllZeros => Self::AllZeros,
LtpReq::NyquistClock => Self::NyquistClock,
LtpReq::DdeCompliance => Self::DdeCompliance,
LtpReq::Lfsr0 => Self::Lfsr0,
LtpReq::Lfsr1 => Self::Lfsr1,
LtpReq::Lfsr2 => Self::Lfsr2,
LtpReq::Lfsr3 => Self::Lfsr3,
LtpReq::FfeChange => Self::FfeChange,
LtpReq::RateChange => Self::RateChange,
LtpReq::Reserved(value) => Self::Reserved(value),
}
}
}
impl From<LtpRequests> for plumbob::LtpRequests {
fn from(requests: LtpRequests) -> Self {
Self::new(
requests.lane0.into(),
requests.lane1.into(),
requests.lane2.into(),
requests.lane3.into(),
)
}
}
impl From<UpdateFlags> for plumbob::UpdateFlags {
fn from(flags: UpdateFlags) -> Self {
Self::new(
flags.status_update,
flags.ced_update,
flags.rr_test,
flags.source_test_update,
flags.frl_start,
flags.flt_update,
flags.rsed_update,
)
}
}
impl From<plumbob::ClearableUpdateFlags> for ClearableUpdateFlags {
fn from(flags: plumbob::ClearableUpdateFlags) -> Self {
ClearableUpdateFlags::new(
flags.status_update,
flags.ced_update,
flags.source_test_update,
flags.frl_start,
flags.flt_update,
flags.rsed_update,
)
}
}
impl From<SourceTestConfig> for plumbob::SourceTestConfig {
fn from(config: SourceTestConfig) -> Self {
Self::new(
config.txffe_pre_shoot_only,
config.txffe_de_emphasis_only,
config.txffe_no_ffe,
config.flt_no_timeout,
config.dsc_frl_max,
config.frl_max,
)
}
}
impl From<plumbob::FrlConfig> for FrlConfig {
fn from(config: plumbob::FrlConfig) -> Self {
FrlConfig {
frl_rate: config.frl_rate,
ffe_levels: FfeLevels::new(config.ffe_levels.value()).unwrap_or_default(),
}
}
}
impl From<CedCount> for plumbob::CedCount {
fn from(count: CedCount) -> Self {
Self::new(count.value())
}
}
impl From<CedCounters> for plumbob::CedCounters {
fn from(counters: CedCounters) -> Self {
Self::new(
counters.lane0.map(Into::into),
counters.lane1.map(Into::into),
counters.lane2.map(Into::into),
counters.lane3.map(Into::into),
)
}
}
#[cfg(test)]
mod tests {
use super::super::Scdc;
use super::super::test_transport::TestTransport;
use crate::error::{ProtocolError, ScdcError};
use display_types::HdmiForumFrl;
use plumbob::{
ClearableUpdateFlags, FfeLevels, FrlConfig, LtpReq, LtpRequests, ScdcClient,
SourceTestConfig, UpdateFlags,
};
fn scdc_with(regs: &[(u8, u8)]) -> Scdc<TestTransport> {
let mut t = TestTransport::new();
for &(addr, value) in regs {
t.set(addr, value);
}
Scdc::new(t)
}
#[test]
fn read_flt_ready_reads_status_flags_0_bit_6() {
assert!(ScdcClient::read_flt_ready(&mut scdc_with(&[(0x40, 0x40)])).unwrap());
assert!(!ScdcClient::read_flt_ready(&mut scdc_with(&[(0x40, 0xBF)])).unwrap());
}
#[test]
fn read_update_flags_maps_every_flag() {
let read = |byte| ScdcClient::read_update_flags(&mut scdc_with(&[(0x10, byte)])).unwrap();
let flag = |n: usize| {
let b = |i| i == n;
UpdateFlags::new(b(0), b(1), b(2), b(3), b(4), b(5), b(6))
};
for n in 0..7 {
assert_eq!(read(1 << n), flag(n), "bit {n}");
}
assert_eq!(
read(0x7F),
UpdateFlags::new(true, true, true, true, true, true, true)
);
assert_eq!(read(0x80), UpdateFlags::default());
}
#[test]
fn clear_update_flags_writes_each_given_bit() {
let clear = |flags| {
let mut scdc = Scdc::new(TestTransport::new());
ScdcClient::clear_update_flags(&mut scdc, flags).unwrap();
scdc.into_transport().get(0x10)
};
for (n, bit) in [0, 1, 3, 4, 5, 6].into_iter().enumerate() {
let b = |i| i == n;
let flags = ClearableUpdateFlags::new(b(0), b(1), b(2), b(3), b(4), b(5));
assert_eq!(clear(flags), 1 << bit, "bit {bit}");
}
assert_eq!(clear(ClearableUpdateFlags::default()), 0x00);
}
#[test]
fn read_ltp_requests_maps_each_lane() {
let mut scdc = scdc_with(&[(0x41, 0x65), (0x42, 0x87)]);
assert_eq!(
ScdcClient::read_ltp_requests(&mut scdc).unwrap(),
LtpRequests::new(LtpReq::Lfsr0, LtpReq::Lfsr1, LtpReq::Lfsr2, LtpReq::Lfsr3)
);
}
#[test]
fn read_ltp_requests_maps_every_request_value() {
let cases = [
(0x0, LtpReq::None),
(0x1, LtpReq::AllOnes),
(0x2, LtpReq::AllZeros),
(0x3, LtpReq::NyquistClock),
(0x4, LtpReq::DdeCompliance),
(0x5, LtpReq::Lfsr0),
(0x6, LtpReq::Lfsr1),
(0x7, LtpReq::Lfsr2),
(0x8, LtpReq::Lfsr3),
(0xE, LtpReq::FfeChange),
(0xF, LtpReq::RateChange),
];
for (nibble, expected) in cases {
let requests =
ScdcClient::read_ltp_requests(&mut scdc_with(&[(0x41, nibble)])).unwrap();
assert_eq!(requests.lane0, expected, "{nibble:#x}");
}
}
#[test]
fn read_ltp_requests_passes_undefined_values_on() {
let requests = ScdcClient::read_ltp_requests(&mut scdc_with(&[(0x42, 0x90)])).unwrap();
assert_eq!(requests.lane3, LtpReq::Reserved(0x9));
assert_eq!(requests.lane2, LtpReq::None);
}
#[test]
fn read_source_test_config_maps_every_bit() {
let read =
|byte| ScdcClient::read_source_test_config(&mut scdc_with(&[(0x35, byte)])).unwrap();
for (n, bit) in [1, 2, 3, 5, 6, 7].into_iter().enumerate() {
let b = |i| i == n;
let expected = SourceTestConfig::new(b(0), b(1), b(2), b(3), b(4), b(5));
assert_eq!(read(1 << bit), expected, "bit {bit}");
}
assert_eq!(read(0x11), SourceTestConfig::default());
}
#[test]
fn write_config_0_defaults_clears_config_0() {
let mut scdc = scdc_with(&[(0x30, 0xFF)]);
ScdcClient::write_config_0_defaults(&mut scdc).unwrap();
assert_eq!(scdc.into_transport().get(0x30), 0x00);
}
#[test]
fn write_frl_config_encodes_rate_and_ffe_levels() {
let mut scdc = Scdc::new(TestTransport::new());
ScdcClient::write_frl_config(
&mut scdc,
FrlConfig::new(HdmiForumFrl::Rate12Gbps4Lanes, FfeLevels::new(3).unwrap()),
)
.unwrap();
assert_eq!(scdc.into_transport().get(0x31), 0x36);
}
#[test]
fn write_frl_config_turns_frl_off() {
let mut scdc = scdc_with(&[(0x31, 0x36)]);
ScdcClient::write_frl_config(
&mut scdc,
FrlConfig::new(HdmiForumFrl::NotSupported, FfeLevels::default()),
)
.unwrap();
assert_eq!(scdc.into_transport().get(0x31), 0x00);
}
#[test]
fn write_frl_config_keeps_culverts_rate_check() {
let mut scdc = Scdc::new(TestTransport::new());
let result = ScdcClient::write_frl_config(
&mut scdc,
FrlConfig::new(HdmiForumFrl::Rate6Gbps4Lanes, FfeLevels::MAX),
);
assert!(matches!(
result,
Err(ScdcError::Protocol(ProtocolError::FfeLevelsOutOfRange {
levels: 7,
..
}))
));
}
#[test]
fn read_ced_maps_each_lane() {
let mut scdc = scdc_with(&[
(0x50, 0x34),
(0x51, 0x92),
(0x52, 0x01),
(0x53, 0x00),
(0x54, 0xFF),
(0x55, 0xFF),
(0x57, 0x00),
(0x58, 0x80),
]);
let ced = ScdcClient::read_ced(&mut scdc).unwrap();
assert_eq!(ced.lane0.map(|c| c.value()), Some(0x1234));
assert_eq!(ced.lane1, None);
assert_eq!(ced.lane2.map(|c| c.value()), Some(0x7FFF));
assert_eq!(ced.lane3.map(|c| c.value()), Some(0));
}
#[test]
fn transport_errors_are_returned() {
let fails = || Scdc::new(TestTransport::failing_after(0));
assert!(matches!(
ScdcClient::read_flt_ready(&mut fails()),
Err(ScdcError::Transport(()))
));
assert!(ScdcClient::read_update_flags(&mut fails()).is_err());
assert!(
ScdcClient::clear_update_flags(&mut fails(), ClearableUpdateFlags::default()).is_err()
);
assert!(ScdcClient::read_ltp_requests(&mut fails()).is_err());
assert!(ScdcClient::read_source_test_config(&mut fails()).is_err());
assert!(ScdcClient::write_config_0_defaults(&mut fails()).is_err());
assert!(
ScdcClient::write_frl_config(
&mut fails(),
FrlConfig::new(HdmiForumFrl::Rate6Gbps4Lanes, FfeLevels::default())
)
.is_err()
);
assert!(ScdcClient::read_ced(&mut fails()).is_err());
}
}