1.1.0版本

This commit is contained in:
xinzhu.yin
2026-04-16 16:51:05 +08:00
commit c157e774e5
333 changed files with 70759 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
from .link_rx import LinkDisplayPortRx
from .link_rx_types import LinkCapabilities, LinkEDPCapabilities
from .link_tx import LinkDisplayPortTx, DpLinkTrainingResult, DPLinkPattern
from .link_tx_config import LinkConfig, SSCConfig
from .link_status_common import DpLinkEncoding
from .link_tx_types import DPLinkPattern, DP128b132bLinkPattern, DPOutLinkMode
from .dp_cable_info import CableCapabilitiesEnum
from .link_rx_aux_controller import RoutedLTConfig

View File

@@ -0,0 +1,19 @@
from UniTAP.libs.lib_tsi import PortIO
from .private_link_rx_types import CableAttributesStruct, PortCableAttributesStruct
from .link_rx_types import CableCapabilitiesEnum
class DpCableInfo:
def __init__(self, port_io: PortIO, ci_name: int):
self.__io = port_io
self.__ci = ci_name
def __read_cable_attributes(self) -> CableAttributesStruct:
return self.__io.get(self.__ci, CableAttributesStruct)[1]
def get_current_port_cable_type(self) -> CableCapabilitiesEnum:
return PortCableAttributesStruct(self.__read_cable_attributes().currentPort).get_cable_caps()
def get_another_port_cable_type(self) -> CableCapabilitiesEnum:
return PortCableAttributesStruct(self.__read_cable_attributes().anotherPort).get_cable_caps()

View File

@@ -0,0 +1,137 @@
from UniTAP.common import ColorInfo
def get_vm_color_format(vsc_sdp_db16: int) -> ColorInfo.ColorFormat:
color_format_value = vsc_sdp_db16 >> 4 & 0xf
if color_format_value == 0:
return ColorInfo.ColorFormat.CF_RGB
elif color_format_value == 1:
return ColorInfo.ColorFormat.CF_YCbCr_444
elif color_format_value == 2:
return ColorInfo.ColorFormat.CF_YCbCr_422
elif color_format_value == 3:
return ColorInfo.ColorFormat.CF_YCbCr_420
elif color_format_value == 4:
return ColorInfo.ColorFormat.CF_Y_ONLY
elif color_format_value == 5:
return ColorInfo.ColorFormat.CF_RAW
else:
return ColorInfo.ColorFormat.CF_UNKNOWN
def get_vm_colorimetry(vsc_sdp_db16: int) -> ColorInfo.Colorimetry:
color_format_value = vsc_sdp_db16 >> 4 & 0xf
colorimetry_value = vsc_sdp_db16 & 0xf
if color_format_value == 0:
if colorimetry_value == 0:
return ColorInfo.Colorimetry.CM_sRGB
elif colorimetry_value == 1:
return ColorInfo.Colorimetry.CM_RGB_WIDE_GAMUT_FIX
elif colorimetry_value == 2:
return ColorInfo.Colorimetry.CM_RGB_WIDE_GAMUT_FLT
elif colorimetry_value == 3:
return ColorInfo.Colorimetry.CM_AdobeRGB
elif colorimetry_value == 4:
return ColorInfo.Colorimetry.CM_DCI_P3
elif colorimetry_value == 5:
return ColorInfo.Colorimetry.CM_CUSTOM_COLOR_PROFILE
elif colorimetry_value == 6:
return ColorInfo.Colorimetry.CM_ITUR_BT2020_RGB
else:
return ColorInfo.Colorimetry.CM_RESERVED
elif color_format_value in [1, 2, 3]:
if colorimetry_value == 0:
return ColorInfo.Colorimetry.CM_ITUR_BT601
elif colorimetry_value == 1:
return ColorInfo.Colorimetry.CM_ITUR_BT709
elif colorimetry_value == 2:
return ColorInfo.Colorimetry.CM_xvYCC601
elif colorimetry_value == 3:
return ColorInfo.Colorimetry.CM_xvYCC709
elif colorimetry_value == 4:
return ColorInfo.Colorimetry.CM_sYCC601
elif colorimetry_value == 5:
return ColorInfo.Colorimetry.CM_opYCC601
elif colorimetry_value == 6:
return ColorInfo.Colorimetry.CM_ITUR_BT2020_YcCbcCrc
elif colorimetry_value == 7:
return ColorInfo.Colorimetry.CM_ITUR_BT2020_YCbCr
else:
return ColorInfo.Colorimetry.CM_RESERVED
elif color_format_value == 4:
if colorimetry_value == 0:
return ColorInfo.Colorimetry.CM_DCI_P3
else:
return ColorInfo.Colorimetry.CM_RESERVED
elif color_format_value == 5:
if colorimetry_value == 0:
return ColorInfo.Colorimetry.CM_CUSTOM_COLOR_PROFILE
else:
return ColorInfo.Colorimetry.CM_RESERVED
else:
return ColorInfo.Colorimetry.CM_RESERVED
def get_vm_bpc(vsc_sdp_db16: int, vsc_sdp_db17: int) -> int:
color_format_value = vsc_sdp_db16 >> 4 & 0xf
bpc_value = vsc_sdp_db17 & 0x7
if color_format_value == 0:
if bpc_value == 0:
return 6
elif bpc_value == 1:
return 8
elif bpc_value == 2:
return 10
elif bpc_value == 3:
return 12
elif bpc_value == 4:
return 16
else:
return 0
elif color_format_value in [1, 2, 3, 4]:
if bpc_value == 1:
return 8
elif bpc_value == 2:
return 10
elif bpc_value == 3:
return 12
elif bpc_value == 4:
return 16
else:
return 0
elif color_format_value == 5:
if bpc_value == 1:
return 6
elif bpc_value == 2:
return 7
elif bpc_value == 3:
return 8
elif bpc_value == 4:
return 10
elif bpc_value == 5:
return 12
elif bpc_value == 6:
return 14
elif bpc_value == 7:
return 16
else:
return 0
else:
return 0
def get_vm_dynamic_range(vsc_sdp_db17: int) -> ColorInfo.DynamicRange:
if vsc_sdp_db17 >> 7 & 1:
return ColorInfo.DynamicRange.DR_CTA
else:
return ColorInfo.DynamicRange.DR_VESA

View File

@@ -0,0 +1,121 @@
from .link_rx_caps import LinkDisplayPortCaps
from .link_rx_status import *
from UniTAP.libs.lib_tsi.tsi_io import PortIO
from .dp_cable_info import DpCableInfo, CableCapabilitiesEnum
from .link_rx_aux_controller import DisplayPortAUXController
from UniTAP.dev.ports.modules.dpcd import DPCDRegisters
class LinkDisplayPortRx:
"""
Class `LinkDisplayPortRx` contains information about DP link.
- Read link status `status`.
- Configure and read link capabilities `capabilities`.
- Make `hpd_pulse`.
- Assert/Deassert HPD state `set_assert_state`.
- Read and write scrambler seed value `scrambler_seed`.
"""
def __init__(self, port_io: PortIO, hw_caps: DPRXHWCaps, dpcd: DPCDRegisters):
self.__io = port_io
self.__HW_CAPS = hw_caps
self.__status = LinkDisplayPortStatusSink(port_io, self.__HW_CAPS)
self.__capabilities = LinkDisplayPortCaps(port_io, self.__HW_CAPS)
self.__cable_info = DpCableInfo(port_io, TSI_DPRX_CABLE_ATTRIBUTES_R)
self.__aux_controller = DisplayPortAUXController(port_io, dpcd)
@property
def status(self) -> LinkDisplayPortStatusSink:
"""
Returns object of class `LinkDisplayPortStatusSink` for working with link status.
Returns:
object of `LinkDisplayPortStatusSink` type
"""
return self.__status
@property
def capabilities(self) -> LinkDisplayPortCaps:
"""
Returns object of class `LinkDisplayPortCaps` for working with link capabilities.
Returns:
object of `LinkDisplayPortStatusSink` type
"""
return self.__capabilities
@property
def aux_controller(self) -> DisplayPortAUXController:
"""
Returns object of class `DisplayPortAUXController` for working with DP AUX Controller.
Returns:
object of `DisplayPortAUXController` type
"""
return self.__aux_controller
def hpd_pulse(self, duration_us: int = 500000):
"""
Start HPD pulse.
Args:
duration_us (int)
"""
self.__io.set(TSI_DPRX_HPD_PULSE_W, duration_us, c_int)
def set_assert_state(self, state: bool):
"""
Assert/Deassert HPD state.
Args:
state (bool)
"""
val = 0x1 if state else 0x0
self.__io.set(TSI_FORCE_HOT_PLUG_STATE_W, val)
@property
def scrambler_seed(self) -> int:
"""
Returns scrambler seed value.
Returns:
object of `int` type
"""
if not self.__HW_CAPS.scrambler_seed:
warnings.warn("Scrambler Seed is not supported.")
return 0
return self.__io.get(TSI_DPRX_SCR_SEED, c_uint32)[1]
@scrambler_seed.setter
def scrambler_seed(self, value: int = 0):
"""
Write new value to scrambler seed
Args:
value (int) - new scrambler seed value
"""
if value < 0 or value > 65535:
raise ValueError(f"Scrambler seed {value} is not available.")
if self.__HW_CAPS.scrambler_seed:
self.__io.set(TSI_DPRX_SCR_SEED, value)
else:
warnings.warn("Scrambler Seed is not supported.")
def cable_rx_type(self) -> CableCapabilitiesEnum:
"""
Get cable type from the RX side.
Returns:
object of `CableCapabilitiesEnum` type
"""
return self.__cable_info.get_current_port_cable_type()
def cable_tx_type(self) -> CableCapabilitiesEnum:
"""
Get cable type from the TX side.
Returns:
object of `CableCapabilitiesEnum` type
"""
return self.__cable_info.get_another_port_cable_type()

View File

@@ -0,0 +1,218 @@
from typing import Optional
from UniTAP.libs.lib_tsi import PortIO
from UniTAP.libs.lib_tsi.tsi_private_types import *
from UniTAP.libs.lib_tsi.tsi_types import *
from .private_link_rx_types import RoutedLTStatusPrivate
from .link_rx_types import RoutedLTStatus, RoutedLTConfig
from UniTAP.dev.ports.modules.dpcd import DPCDRegisters
class DisplayPortAUXController:
"""
Class `DisplayPortAUXController` describes information about DP AUX Controller. Contains following info:
- configure routed LT `exec_routed_lt`
- read routed T status `status`
- request PHY test pattern `request_phy_test_pattern`
"""
def __init__(self, port_io: PortIO, dpcd: DPCDRegisters):
self.__io = port_io
self.__dpcd = dpcd
def enable(self, enable: bool):
"""
Enable or disable Routed LT.
Args:
enable ('bool')
"""
status = self.status()
ctrl = status.dp20_old_lt << 1
if enable:
ctrl |= 0x01
else:
ctrl &= ~0x01
res = self.__io.set(TSI_DPRX_LT_ROUTE_CONTROL_W, ctrl, c_uint32)
if not res:
raise Exception("The feature requires a license key and the required license key is not installed.")
def exec_routed_lt(self, config: RoutedLTConfig, use_ta_request: bool = False):
"""
Execute routed link training with transferred configuration.
Args:
config ('RoutedLTConfig')
use_ta_request (`bool`)
"""
max_rate_dp14 = 0
max_rate_dp20 = 0
if config.is128b132b:
if config.link_bw == 1:
max_rate_dp20 = 1
elif config.link_bw == 2:
max_rate_dp20 = 3
elif config.link_bw == 4:
max_rate_dp20 = 5
max_rate_dp14 = 0x1e
else:
max_rate_dp14 = config.link_bw
res = self.__set_routed_lt_config(config.int_value())
if not res:
raise Exception("The feature requires a license key and the required license key is not installed.")
self.__set_force_cable_status_to_plugged(True)
dpcd_rev = 0x14
dpcd_01_val = max_rate_dp14
dpcd_02_val = config.lane_count | 0xc0
dpcd_03_val = 0x81
dpcd_06_val = 0x03 if config.is128b132b else 0x01
self.__dpcd.write(0x00, dpcd_rev)
self.__dpcd.write(0x01, dpcd_01_val)
self.__dpcd.write(0x02, dpcd_02_val)
self.__dpcd.write(0x03, dpcd_03_val)
self.__dpcd.write(0x06, dpcd_06_val)
self.__dpcd.write(0x0e, 0x80)
self.__dpcd.write(0x2200, dpcd_rev)
self.__dpcd.write(0x2201, dpcd_01_val)
self.__dpcd.write(0x2202, dpcd_02_val)
self.__dpcd.write(0x2203, dpcd_03_val)
self.__dpcd.write(0x2206, dpcd_06_val)
self.__dpcd.write(0x2215, max_rate_dp20)
if config.is128b132b:
self.__dpcd.write(0x21, 0x01)
self.__dpcd.write(0x90, 0xff)
dsc_data = [0x0f, 0x21, 0x03, 0x03, 0xeb, 0x07, 0x01, 0x00, 0x00, 0x1f, 0x0e, 0x11, 0x08, 0x07, 0x00, 0x00]
self.__dpcd.write(0x60, dsc_data)
self.__dpcd.write(0x2217, 0x06)
else:
self.__dpcd.write(0x21, 0x00)
self.__dpcd.write(0x90, 0x00)
dsc_data = [0x00] * 16
self.__dpcd.write(0x60, dsc_data)
self.__dpcd.write(0x2217, 0x00)
self.__dpcd.write(0x100, 0)
self.__dpcd.write(0x101, 0)
self.__dpcd.write(0x103, 0)
self.__dpcd.write(0x104, 0)
self.__dpcd.write(0x105, 0)
self.__dpcd.write(0x106, 0)
self.__dpcd.write(0x107, 0)
self.__dpcd.write(0x108, 1)
self.__dpcd.write(0x206, 0)
self.__dpcd.write(0x207, 0)
if use_ta_request:
self.__dpcd.write(0x219, config.link_bw)
self.__dpcd.write(0x220, config.lane_count)
dpcd_218 = self.__dpcd.read(0x218, 1)
dpcd_218_data = int.from_bytes(dpcd_218.data, byteorder='big', signed=True)
dpcd_218_data |= 0x01
dpcd_218_data &= ~0x30
if config.is128b132b:
dpcd_218_data |= 0x10
self.__dpcd.write(dpcd_218.base, dpcd_218_data)
dpcd_201 = self.__dpcd.read(0x201, 1)
dpcd_201_data = int.from_bytes(dpcd_201.data, byteorder='big', signed=True)
dpcd_201_data |= 0x02
self.__dpcd.write(dpcd_201.base, dpcd_201_data)
self.__generate_short_hpd_pulse(750)
else:
dpcd_201 = self.__dpcd.read(0x201, 1)
dpcd_201_data = int.from_bytes(dpcd_201.data, byteorder='big', signed=True)
dpcd_201_data &= ~2
self.__dpcd.write(dpcd_201.base, dpcd_201_data)
dpcd_218 = self.__dpcd.read(0x218, 1)
dpcd_218_data = int.from_bytes(dpcd_218.data, byteorder='big', signed=True)
dpcd_218_data &= ~0xf
self.__dpcd.write(dpcd_218.base, dpcd_218_data)
self.__generate_short_hpd_pulse(500 * 1000)
def request_phy_test_pattern(self, pattern: int, sq_num: int, config: RoutedLTConfig):
"""
Request PHY test pattern.
Args:
config ('RoutedLTConfig')
pattern (`int`)
sq_num (`int`)
"""
self.__set_force_cable_status_to_plugged(True)
self.__dpcd.write(0x00, 0x14)
self.__dpcd.write(0x218, 0x18 if config.is128b132b else 0x08)
self.__dpcd.write(0x219, config.link_bw)
self.__dpcd.write(0x220, config.lane_count)
self.__dpcd.write(0x248, pattern)
if config.is128b132b and 0x48 <= pattern <= 0x4b:
self.__dpcd.write(0x249, sq_num)
if config.is128b132b:
value = config.ffe | (config.ffe << 4)
else:
value = (config.vs & 3) | ((config.pe & 3) << 2)
value |= value << 4
self.__dpcd.write(0x206, value)
self.__dpcd.write(0x207, value)
self.__dpcd.write(0x260, 0x0)
dpcd_201 = self.__dpcd.read(0x201, 1)
dpcd_201_data = int.from_bytes(dpcd_201.data, byteorder='big', signed=True)
dpcd_201_data |= 0x02
self.__dpcd.write(dpcd_201.base, dpcd_201_data)
self.__generate_short_hpd_pulse(750)
def status(self) -> RoutedLTStatus:
"""
Get Routed link training status.
Returns:
object of `RoutedLTStatus`
"""
routed_lt_status = self.__read_status()
if routed_lt_status is None:
raise Exception("The feature requires a license key and the required license key is not installed.")
return RoutedLTStatus(enabled=routed_lt_status.enabled,
dp20_old_lt=routed_lt_status.dp2OldLt,
state=routed_lt_status.state,
success=routed_lt_status.success,
step=routed_lt_status.step)
def __set_routed_lt_config(self, value: int) -> bool:
res = self.__io.set(TSI_DPRX_LT_ROUTE_CREATE, value, c_uint32)
return res >= 0
def __set_force_cable_status_to_plugged(self, enable: bool):
val = 0x3C if enable else 0
self.__io.set(TSI_DPRX_HPD_FORCE, val, c_int)
def __read_status(self) -> Optional[RoutedLTStatusPrivate]:
res, status = self.__io.get(TSI_DPRX_LT_ROUTE_STATUS_R, RoutedLTStatusPrivate)
if res >= 0:
return status
return None
def __generate_short_hpd_pulse(self, value: int):
self.__io.set(TSI_DPRX_HPD_PULSE_W, value, c_int)

View File

@@ -0,0 +1,332 @@
import warnings
from typing import List, Optional, Union, Type
from .link_rx_types import LinkCapabilities, LinkEDPCapabilities, DisplayPortLinkCaps
from .private_link_rx_types import DPRXHWCaps, DPRXLinkControl
from UniTAP.libs.lib_tsi import PortIO
from UniTAP.libs.lib_tsi.tsi_types import *
class LinkDisplayPortCaps:
"""
Class `LinkDisplayPortCaps` allows settings link capabilities on Sink (RX - receiver) side.
- Set configuration `set`.
- Get current configuration on link `link_caps_status`.
"""
def __init__(self, port_io: PortIO, caps: DPRXHWCaps):
self.__io = port_io
self.__caps = caps
def set(self, capabilities: Union[LinkCapabilities, LinkEDPCapabilities]):
"""
Set new settings on link. Only those values will be written that were specified.
Args:
capabilities (`LinkCapabilities` or `LinkEDPCapabilities`)
"""
if isinstance(capabilities, LinkCapabilities):
self.__set_max_lanes(capabilities.max_lane)
self.__set_max_bitrate(capabilities.bit_rate)
self.__set_force_cable_status_to_plugged(capabilities.force_cable_status_to_plugged)
self.__set_dp_link_control(old_dp2_lt=capabilities.old_dp_2_0_lt)
self.__set_dp_128_132_bitrates(capabilities.dp_128_132_bitrates)
self.__set_bitrate_override(10.0, capabilities.override_10g)
self.__set_dsc(capabilities.dsc)
self.__set_ss_sbm(capabilities.ss_sbm)
self.__set_fec(capabilities.fec)
self.__set_tps3(capabilities.tps3)
self.__set_tps4(capabilities.tps4)
self.__set_mst(capabilities.mst)
self.__set_edp_support(False)
self.__set_mst_sink_count(capabilities.mst_sink_count)
elif isinstance(capabilities, LinkEDPCapabilities):
self.__set_max_lanes(capabilities.max_lane)
self.__set_edp_sel_rates(capabilities.eDp_cur_rate)
self.__set_dp_link_control(edp_aux_preamble=capabilities.eDp_aux_preamble)
self.__set_edp_support(capabilities.eDp_support)
else:
raise TypeError("Incorrect LinkCapabilities format!")
def link_caps_status(self, config_type: Union[Type[DisplayPortLinkCaps], None] = None) -> DisplayPortLinkCaps:
"""
Returns current configuration on link `LinkCapabilities`.
Returns:
object of `LinkCapabilities` type
"""
if config_type is None or issubclass(config_type, LinkCapabilities):
link_status = LinkCapabilities()
link_status.max_lane = self.__get_max_lanes()
link_status.bit_rate = self.__get_max_bitrate()
link_status.force_cable_status_to_plugged = self.__get_force_cable_status_to_plugged()
link_status.old_dp_2_0_lt = self.__get_dp_link_control().old_dp2_lt
link_status.dp_128_132_bitrates = self.__get_dp_128_132_bitrates()
link_status.override_10g = self.__get_bitrate_override(10.0)
link_status.dsc = self.__get_dsc()
link_status.ss_sbm = self.__get_ss_sbm()
link_status.fec = self.__get_fec()
link_status.tps3 = self.__get_tps3()
link_status.tps4 = self.__get_tps4()
link_status.mst = self.__get_mst()
link_status.mst_sink_count = self.__get_mst_sink_count()
return link_status
elif issubclass(config_type, LinkEDPCapabilities):
link_status = LinkEDPCapabilities()
link_status.max_lane = self.__get_max_lanes()
link_status.eDp_cur_rate = self.__get_edp_sel_rates()
link_status.eDp_supported_rates = self.__get_edp_caps_rates()
link_status.eDp_aux_preamble = self.__get_dp_link_control().edp_aux_preamble
link_status.eDp_support = self.__get_edp_support()
return link_status
else:
raise TypeError("Incorrect LinkCapabilities format!")
def __set_max_lanes(self, lane_count: Optional[int]):
if lane_count is None:
return
if lane_count not in [1, 2, 4]:
raise ValueError(f"Incorrect lane count number {lane_count}. Must be from list: 1, 2, 4")
self.__io.set(TSI_DPRX_MAX_LANES, lane_count, c_int)
def __get_max_lanes(self) -> int:
return self.__io.get(TSI_DPRX_MAX_LANES)[1]
def __set_max_bitrate(self, bitrate: Optional[float]):
if bitrate is None:
return
bitrate = round(bitrate / 0.27)
if bitrate not in [6, 10, 20, 25, 30]:
raise ValueError(f"Incorrect bit rate number {bitrate}. Must be from list: 1.62, 2.7, 5.4, 6.75, 8.1")
self.__io.set(TSI_DPRX_MAX_LINK_RATE, bitrate, c_int)
def __get_max_bitrate(self) -> int:
return round(self.__io.get(TSI_DPRX_MAX_LINK_RATE)[1] * 0.27, 2)
def __set_bitrate_override(self, to_override: float, with_override: Optional[float]):
if with_override is None:
return
if not isinstance(to_override, float) or not isinstance(with_override, float):
raise TypeError("DP 128b/132b bitrate override settings must float type!")
list_value = self.__io.get(TSI_DP2RX_CUSTOM_RATE_MAP, c_uint, 3)[1]
if with_override not in [2.5, 2.7, 5.0, 5.4]:
raise ValueError(f"Incorrect bit rate number {with_override}. "
f"Must be from list: 2.5, 2.7, 5.0, 5.4")
list_value[0] = int(with_override * 1000000000 / 200000)
self.__io.set(TSI_DP2RX_CUSTOM_RATE_MAP, list_value, c_uint, 3)
def __get_bitrate_override(self, to_override: float) -> Optional[float]:
if not isinstance(to_override, float):
raise TypeError("DP 128b/132b bitrate override settings must float type!")
override_list = self.__io.get(TSI_DP2RX_CUSTOM_RATE_MAP, c_uint, 3)[1]
if override_list.count(0) == len(override_list):
return None
if to_override == 10.0 and override_list[0] > 0:
return round((override_list[0] * 200000 / 1000000000), 2)
return None
def __set_dp_link_control(self, old_dp2_lt: Optional[bool] = None, edp_aux_preamble: Optional[bool] = None):
ctrl = self.__get_dp_link_control()
if old_dp2_lt is not None:
ctrl.old_dp2_lt = old_dp2_lt
if edp_aux_preamble is not None:
ctrl.edp_aux_preamble = edp_aux_preamble
self.__io.set(TSI_DPRX_LINK_CONTROL, ctrl.value(), c_uint32)
def __get_dp_link_control(self) -> DPRXLinkControl:
return self.__io.get(TSI_DPRX_LINK_CONTROL, DPRXLinkControl)[1]
def __set_dp_128_132_bitrates(self, rates: Optional[List[float]]):
flags = self.__io.get(TSI_DPRX_LINK_FLAGS)[1] & ~0xc0
if rates is None:
return
elif isinstance(rates, list) and len(rates) == 0:
flags &= ~(1 << 4)
self.__io.set(TSI_DPRX_LINK_FLAGS, flags, c_int)
else:
flags |= (1 << 4)
self.__io.set(TSI_DPRX_LINK_FLAGS, flags, c_int)
val = 0
if 10.0 in rates:
val |= 0x01
if 13.5 in rates:
val |= 0x04
if 20.0 in rates:
val |= 0x02
self.__io.set(TSI_DP2RX_LINK_RATE_CAPS, val, c_int)
def __get_dp_128_132_bitrates(self) -> Optional[List[float]]:
val = (self.__io.get(TSI_DPRX_LINK_FLAGS)[1] >> 4) & 0x1
if val:
rates = self.__io.get(TSI_DP2RX_LINK_RATE_CAPS)[1]
list_rates = []
if rates & 0x01:
list_rates.append(10.0)
if rates & 0x04:
list_rates.append(13.5)
if rates & 0x02:
list_rates.append(20.0)
return list_rates
else:
return None
def __set_force_cable_status_to_plugged(self, enable: Optional[bool]):
if enable is None:
return
val = 0x3C if enable else 0
self.__io.set(TSI_DPRX_HPD_FORCE, val, c_int)
def __get_force_cable_status_to_plugged(self) -> bool:
return bool(self.__io.get(TSI_DPRX_HPD_FORCE)[1])
def __set_dsc(self, enable: Optional[bool]):
if enable is None:
return
val = 0x1 if enable else 0x0
if self.__caps.dsc:
self.__io.set(TSI_DPRX_DSC_CONTROL, val, c_int)
def __get_dsc(self) -> Optional[bool]:
if self.__io.get(TSI_DPRX_DSC_STATUS_R)[1] & 0x1:
return bool(self.__io.get(TSI_DPRX_DSC_CONTROL)[1] & 0x1)
def __set_fec(self, enable: Optional[bool]):
if enable is None:
return
val = 0x1 if enable else 0x0
if self.__caps.fec:
self.__io.set(TSI_DPRX_FEC_CONTROL, val, c_int)
def __get_fec(self) -> Optional[bool]:
if self.__caps.fec:
return bool(self.__io.get(TSI_DPRX_FEC_CONTROL)[1] & 0x1)
def __set_mst(self, enable: Optional[bool]):
if enable is None:
return
flags = self.__io.get(TSI_DPRX_LINK_FLAGS)[1] & ~0xc0
if enable:
flags |= 1
else:
flags &= ~1
if self.__caps.mst:
self.__io.set(TSI_DPRX_LINK_FLAGS, flags, c_int)
def __get_mst(self) -> Optional[bool]:
if self.__caps.mst:
return bool(self.__io.get(TSI_DPRX_LINK_FLAGS)[1] & 0x1)
def __set_mst_sink_count(self, count: int):
if count is None:
return
if self.__caps.sink_cnt_config and self.__caps.mst:
if count > self.__caps.mst_stream_count:
warnings.warn(f"Maximum sink count: {self.__caps.mst_stream_count}")
else:
self.__io.set(TSI_DPRX_MST_SINK_COUNT, count, c_uint32)
else:
warnings.warn("MST or Custom Sink Count are not supported")
def __get_mst_sink_count(self) -> int:
if self.__caps.sink_cnt_config and self.__caps.mst:
return int(self.__io.get(TSI_DPRX_MST_SINK_COUNT)[1])
else:
return 0
def __set_tps3(self, enable: Optional[bool]):
if enable is None:
return
flags = self.__io.get(TSI_DPRX_LINK_FLAGS)[1] & ~0xc0
if enable:
flags |= (1 << 1)
else:
flags &= ~(1 << 1)
if self.__caps.fec:
self.__io.set(TSI_DPRX_LINK_FLAGS, flags, c_int)
def __get_tps3(self) -> Optional[bool]:
if self.__caps.fec:
return bool((self.__io.get(TSI_DPRX_LINK_FLAGS)[1] >> 1) & 0x1)
def __set_tps4(self, enable: Optional[bool]):
if enable is None:
return
flags = self.__io.get(TSI_DPRX_LINK_FLAGS)[1] & ~0xc0
if enable:
flags |= (1 << 2)
else:
flags &= ~(1 << 2)
if self.__caps.fec:
self.__io.set(TSI_DPRX_LINK_FLAGS, flags, c_int)
def __get_tps4(self) -> Optional[bool]:
if self.__caps.fec:
return bool((self.__io.get(TSI_DPRX_LINK_FLAGS)[1] >> 2) & 0x1)
def __set_ss_sbm(self, enable: bool):
if enable is None:
return
if self.__caps.dp2_support_rates:
flags = self.__io.get(TSI_DPRX_LINK_FLAGS)[1] & ~0xc0
if enable:
flags |= (1 << 5)
else:
flags &= ~(1 << 5)
self.__io.set(TSI_DPRX_LINK_FLAGS, flags, c_int)
def __get_ss_sbm(self) -> Optional[bool]:
if self.__caps.dp2_support_rates:
return bool((self.__io.get(TSI_DPRX_LINK_FLAGS)[1] >> 5) & 0x1)
def __set_edp_support(self, enable: bool):
if self.__caps.edp:
flags = self.__io.get(TSI_DPRX_LINK_FLAGS)[1] & ~0xc0
if enable:
flags |= (1 << 3)
else:
flags &= ~(1 << 3)
self.__io.set(TSI_DPRX_LINK_FLAGS, flags, c_int)
def __get_edp_support(self) -> Optional[bool]:
if self.__caps.edp:
return bool((self.__io.get(TSI_DPRX_LINK_FLAGS)[1] >> 3) & 0x1)
def __set_edp_sel_rates(self, rates: List[float]):
if self.__caps.edp:
rates.sort()
rates = rates[:8]
to_write_list = [0 for i in range(8)]
j = 0
for i in range(len(rates)):
if rates[i] in self.__get_edp_caps_rates():
to_write_list[j] = int(rates[i] / 0.0002)
j += 1
rates.sort()
self.__io.set(TSI_DPRX_EDP_SEL_RATES, to_write_list, data_type=c_uint32, data_count=8)
def __get_edp_sel_rates(self) -> List[float]:
if self.__caps.edp:
rates = self.__io.get(TSI_DPRX_EDP_SEL_RATES, data_type=c_uint32, data_count=8)[1]
res_rates = []
for rate in rates:
if rate == 0:
return res_rates
res_rates.append(rate * 0.0002)
return res_rates
def __get_edp_caps_rates(self) -> List[float]:
if self.__caps.edp:
rates = self.__io.get(TSI_DPRX_EDP_CAPS_RATES_R, data_type=c_uint32, data_count=32)[1]
new_rates = []
for rate in rates:
if rate > 0:
new_rates.append(rate * 0.0002)
return new_rates

View File

@@ -0,0 +1,407 @@
import time
import warnings
from typing import Optional
from UniTAP.libs.lib_tsi import PortIO
from UniTAP.libs.lib_tsi.tsi_private_types import *
from UniTAP.libs.lib_tsi.tsi_types import *
from UniTAP.utils.function_wrapper import function_scheduler
from .private_link_rx_types import DPRXHWCaps
from .link_status_common import *
from .private_link_status_common import VCPTable, DpMsa, MSAInfo, DpCrc, msa_info_to_video_mode
from typing import Union
class LinkDisplayPortStatusSink:
"""
Class `LinkDisplayPortStatusSink` describes information about DP link status. Contains following info:
- MST stream count `mst_stream_count`.
- Lane count `lane_count`.
- Link encoding `link_encoding`.
- Link rate `link_rate`.
- State of HPD `hpd_asserted`.
- Cable state `cable_state`.
- State of framing `enhanced_framing`.
- State of scrambling `scrambling_enabled`.
- State of DSC `dsc_enabled`
- State of FEC `fec_enabled`.
- State of MST `mst_enabled`.
- State of SSC `ssc_enabled`.
- State of ILA `ila`.
- State of EQ ILA `eq_ila`.
- State of CDS ILA `cds_ila`.
- State of LT fail `lt_fail`.
- State of selected lane `lane`.
- State of VCP `vcp`.
- State of selected stream `stream`.
"""
def __init__(self, port_io: PortIO, caps: DPRXHWCaps):
self.__io = port_io
self.__caps = caps
@property
def mst_stream_count(self) -> int:
"""
Returns current MST count.
Returns:
object of int type
"""
res, value = self.__io.get(TSI_DPRX_MST_STATUS_R, c_uint)
return value >> 8
@property
def lane_count(self) -> int:
"""
Returns current lane count.
Returns:
object of int type
"""
return self.__io.get(TSI_DPRX_LINK_LANE_COUNT_R, c_int)[1]
@property
def link_encoding(self) -> DpLinkEncoding:
"""
Returns current link encoding `DpLinkEncoding`.
Returns:
object of `DpLinkEncoding` type
"""
result, value = self.__io.get(TSI_DPRX_LINK_STATUS_FLAGS_R, c_uint)
if value >> 20 & 1:
return DpLinkEncoding.LE_128b132b
else:
return DpLinkEncoding.LE_8b10b
@property
def link_rate(self) -> float:
"""
Returns current link rate.
Returns:
object of float type
"""
current_link_rate = self.__io.get(TSI_DPRX_LINK_BR_R, c_int)[1]
if self.link_encoding == DpLinkEncoding.LE_8b10b:
return round(current_link_rate * 0.27, 2)
else:
if current_link_rate == 1:
return 10.0
elif current_link_rate == 2:
return 20.0
elif current_link_rate == 4:
return 13.5
else:
return 0.0
@property
def hpd_asserted(self) -> bool:
"""
Returns current state of HDP asserted.
Returns:
object of bool type
"""
return bool(self.__io.get(TSI_DPRX_HPD_STATUS_R, c_int)[1] & 1)
@property
def cable_state(self) -> bool:
"""
Returns current cable state.
Returns:
object of bool type
"""
return bool(self.__io.get(TSI_DPRX_HPD_STATUS_R, c_int)[1] & 4)
@property
def enhanced_framing(self) -> bool:
"""
Returns current enhanced framing state.
Returns:
object of bool type
"""
return bool((self.__io.get(TSI_DPRX_LINK_STATUS_FLAGS_R, c_uint32)[1] >> 17) & 1)
@property
def scrambling_enabled(self) -> bool:
"""
Returns current scrambling enabled state.
Returns:
object of bool type
"""
return bool((self.__io.get(TSI_DPRX_LINK_STATUS_FLAGS_R, c_uint32)[1] >> 18) & 1)
@property
def dsc_enabled(self) -> Union[bool, None]:
"""
Returns current DSC state.
Returns:
object of bool|None type
"""
if self.__caps.dsc or self.__caps.dsc2:
return bool((self.__io.get(TSI_DPRX_DSC_STATUS_R)[1] >> 1) & 1)
else:
return None
@property
def fec_enabled(self) -> Union[bool, None]:
"""
Returns current FEC state.
Returns:
object of bool|None type
"""
if self.__caps.fec or self.__caps.fec2:
return bool(self.__io.get(TSI_DPRX_FEC_STATUS_R)[1] & 1)
else:
return None
@property
def mst_enabled(self) -> Union[bool, None]:
"""
Returns current MST state.
Returns:
object of bool|None type
"""
if self.__caps.mst:
return bool((self.__io.get(TSI_DPRX_LINK_STATUS_FLAGS_R, c_uint32)[1] >> 19) & 1)
else:
return None
@property
def ssc_enabled(self) -> Union[bool, None]:
"""
Returns current SSC state.
Returns:
object of bool|None type
"""
if (self.__io.get(TSI_DPRX_SSC_STATUS_R, c_int)[1] >> 1) & 1:
return bool(self.__io.get(TSI_DPRX_SSC_STATUS_R, c_int)[1] & 1)
else:
return None
@property
def ila(self) -> bool:
"""
Returns current ILA state.
Returns:
object of bool type
"""
return bool((self.__io.get(TSI_DPRX_LINK_STATUS_FLAGS_R, c_uint32)[1] >> 16) & 1)
@property
def eq_ila(self) -> bool:
"""
Returns current EQ ILA state.
Returns:
object of bool type
"""
return bool((self.__io.get(TSI_DPRX_LINK_STATUS_FLAGS_R, c_uint32)[1] >> 30) & 1)
@property
def cds_ila(self) -> bool:
"""
Returns current CDS ILA state.
Returns:
object of bool type
"""
return bool((self.__io.get(TSI_DPRX_LINK_STATUS_FLAGS_R, c_uint32)[1] >> 29) & 1)
@property
def lt_fail(self) -> bool:
"""
Returns current LT fail state.
Returns:
object of bool type
"""
return bool((self.__io.get(TSI_DPRX_LINK_STATUS_FLAGS_R, c_uint32)[1] >> 28) & 1)
def lane(self, lane_number: int) -> LaneStatus:
"""
Returns status of lane `LaneStatus`.
Args:
lane_number (int) - number of selected number
Returns:
object of `LaneStatus` type
"""
if not (0 <= lane_number <= 3):
raise ValueError(f"Incorrect lane number {lane_number}. Available range: 0-{self.lane_count}")
status = self.__io.get(TSI_DPRX_LINK_STATUS_FLAGS_R, c_uint32)[1]
voltage_swing = self.__io.get(TSI_DPRX_LINK_VS_R, c_uint32)[1]
pre_emphasis = self.__io.get(TSI_DPRX_LINK_PE_R, c_uint32)[1]
_, error_counters = self.__io.get(TSI_DPRX_GET_ERROR_COUNTS_R, c_uint32 * 4)
lane_status = LaneStatus()
if lane_number == 0:
lane_status.cr = bool(status & 0x1)
lane_status.sl = bool((status >> 1) & 0x1)
lane_status.eq = bool((status >> 2) & 0x1)
lane_status.error_count = error_counters[0]
elif lane_number == 1:
lane_status.cr = bool((status >> 4) & 0x1)
lane_status.sl = bool((status >> 5) & 0x1)
lane_status.eq = bool((status >> 6) & 0x1)
lane_status.error_count = error_counters[1]
elif lane_number == 2:
lane_status.cr = bool((status >> 8) & 0x1)
lane_status.sl = bool((status >> 9) & 0x1)
lane_status.eq = bool((status >> 10) & 0x1)
lane_status.error_count = error_counters[2]
elif lane_number == 3:
lane_status.cr = bool((status >> 12) & 0x1)
lane_status.sl = bool((status >> 13) & 0x1)
lane_status.eq = bool((status >> 14) & 0x1)
lane_status.error_count = error_counters[3]
if self.__caps.dp2_support_rates and self.link_encoding == DpLinkEncoding.LE_128b132b:
lane_status.voltage_swing = 0
lane_status.pre_emphasis = 0
_, ffe_preset = self.__io.get(TSI_DP2RX_LINK_FFE_PRESET_R, c_uint32)
lane_status.ffe_preset = (ffe_preset >> (8 * lane_number)) & 0xF
else:
lane_status.voltage_swing = (voltage_swing >> (8 * lane_number)) & 0x3
lane_status.pre_emphasis = (pre_emphasis >> (8 * lane_number)) & 0x3
lane_status.ffe_preset = 0
return lane_status
def vcp(self, stream_index: int = 0) -> Optional[VCPStatus]:
"""
Returns VCP status of selected stream `VCPStatus`.
Args:
stream_index (int) - number of selected number
Returns:
object of `VCPStatus` | None type
"""
if self.__caps.mst:
if not stream_index < self.mst_stream_count:
raise ValueError(f"Selected stream {stream_index} is not available. "
f"Available stream number: {self.mst_stream_count}")
res = self.__io.get(TSI_DPRX_VC_TABLE_R, VCPTable, 4)[1]
if self.mst_enabled:
status = VCPStatus()
status.port_number = res[stream_index].port_number
status.stream_id = res[stream_index].stream_id
status.req_pbn = res[stream_index].req_pbn
status.alloc_pbn = res[stream_index].alloc_pbn
status.first_slot = res[stream_index].first_slot
status.slot_num = res[stream_index].slot_num
return status
else:
warnings.warn("MST does not work. Cannot show VCP status.")
return VCPStatus()
def __check_video(self) -> bool:
def is_msa_available(io):
result, msa_info, size = io.get(TSI_DPRX_MSA_INFO_R, DpMsa, 4)
return size > 0
return function_scheduler(is_msa_available, self.__io, interval=5, timeout=10)
def stream(self, stream_index: int) -> StreamStatusDP:
"""
Returns status of selected stream `StreamStatusDP`.
Args:
stream_index (int) - number of selected number
Returns:
object of `StreamStatusDP` type
"""
stream_status = StreamStatusDP()
if not self.__check_video():
warnings.warn("Video is not available.")
return stream_status
time.sleep(1)
result, msa_info, size = self.__io.get(TSI_DPRX_MSA_INFO_R, DpMsa, 4)
if stream_index < size:
msa_info_private = MSAInfo(msa_info[stream_index])
stream_status.video_mode = msa_info_to_video_mode(msa_info_private, self.link_rate * 100000000,
self.link_encoding)
stream_status.mvid = msa_info_private.m_video
stream_status.nvid = msa_info_private.n_video
result = self.__io.get(TSI_DPRX_CRC_LOG_R, DpCrc, self.mst_stream_count)
if result[0] >= TSI_SUCCESS and self.mst_stream_count > 1:
stream_status.crc = [result[1][stream_index].r, result[1][stream_index].g, result[1][stream_index].b]
elif result[0] >= TSI_SUCCESS and size == 1:
stream_status.crc = [result[1].r, result[1].g, result[1].b]
result = self.__io.get(TSI_DPRX_DSC_CRC_R, DpCrc, self.mst_stream_count)
if result[0] >= TSI_SUCCESS and self.mst_stream_count > 1:
stream_status.dsc_crc = [result[1][stream_index].r, result[1][stream_index].g, result[1][stream_index].b]
elif result[0] >= TSI_SUCCESS and size == 1:
stream_status.dsc_crc = [result[1].r, result[1].g, result[1].b]
if self.__caps.dp2_support_rates:
res = self.__io.get(TSI_DPRX_SDP_CRC16_CTRL, c_uint32)
stream_status.sdp_crc16.state = False if res[0] < TSI_SUCCESS else (res[1] & 0x1 == 1)
stream_status.sdp_crc16.errors = self.__io.get(TSI_DPRX_SDP_CRC16_COUNTERS, c_uint8,
self.__caps.mst_stream_count)[1][stream_index]
else:
raise ValueError(f"Selected stream {stream_index} is not available. "
f"Available stream number: {size}")
return stream_status
def reset_sdp_crc16_errors(self):
"""
Reset SDP CRC16 errors.
"""
if not self.__caps.dp2_support_rates:
warnings.warn("SDP CRC16 is not supported. Cannot reset errors.")
return
self.__io.set(TSI_DPRX_SDP_CRC16_COUNTERS, 0)
def __str__(self) -> str:
lane_status_str = ""
stream_info_str = ""
vcp_table_str = ""
for i in range(self.mst_stream_count):
lane_status_str += f"Lane {i}\n{self.lane(i)}\n"
stream_info_str += f"Stream {i}\n{self.stream(i)}\n"
vcp_table_str += f"#{i}\n{self.vcp(i)}"
return f"Lane count: {self.lane_count}\n" \
f"Bit rate: {self.link_rate} Gbps\n" \
f"Enhanced framing mode: {self.enhanced_framing}\n" \
f"MST: {self.mst_enabled}\n" \
f"DSC: {self.dsc_enabled}\n" \
f"Link Encoding: {self.link_encoding.name}\n" \
f"Scrambling: {self.scrambling_enabled}\n" \
f"SSC: {self.ssc_enabled}\n" \
f"FEC: {self.fec_enabled}\n" \
f"ILA: {self.ila}\n" \
f"EQ_ILA: {self.eq_ila}\n" \
f"CDS_ILA: {self.cds_ila}\n" \
f"LT_FAIL: {self.lt_fail}\n\n" \
f"Lane status:\n{lane_status_str}\n" \
f"Stream info:\n{stream_info_str}\n" \
f"VCP status:\n{vcp_table_str}\n"

View File

@@ -0,0 +1,177 @@
from typing import TypeVar
from enum import IntEnum
class LinkCapabilities:
"""
Class `LinkCapabilities` describes capabilities of DP link.
"""
def __init__(self):
self.max_lane = None
self.bit_rate = None
self.dp_128_132_bitrates = None
self.override_10g = None
self.old_dp_2_0_lt = None
self.force_cable_status_to_plugged = None
self.mst = None
self.ss_sbm = None
self.fec = None
self.tps4 = None
self.tps3 = None
self.dsc = None
self.mst_sink_count = None
def __str__(self) -> str:
return f"Max Lanes - {self.max_lane}\n" \
f"Max Bitrate - {self.bit_rate}\n" \
f"DP (128b/132b) Supported Bitrates - {self.dp_128_132_bitrates}\n" \
f"Instead of 10 Gbps, use - {self.override_10g}\n" \
f"Old DP 2.0 LT - {self.old_dp_2_0_lt}\n" \
f"Force Cable Status to Plugged - {self.force_cable_status_to_plugged}\n" \
f"MST - {self.mst} (Sink Count - {self.mst_sink_count})\n" \
f"SS SBM - {self.ss_sbm}\n" \
f"FEC - {self.fec}\n" \
f"TPS4 - {self.tps4}\n" \
f"TPS3 - {self.tps3}\n" \
f"DSC - {self.dsc}\n"
class LinkEDPCapabilities:
"""
Class `LinkEDPCapabilities` describes capabilities of eDP link.
"""
def __init__(self):
self.max_lane = None
self.eDp_cur_rate = None
self.eDp_supported_rates = None
self.eDp_aux_preamble = None
self.eDp_support = None
def __str__(self) -> str:
return f"Max Lanes - {self.max_lane}\n" \
f"eDP rate - {self.eDp_cur_rate}\n" \
f"eDP supported rates - {self.eDp_supported_rates}\n" \
f"eDP AUX preamble - {self.eDp_aux_preamble}\n" \
f"eDp Support - {self.eDp_support}"
DisplayPortLinkCaps = TypeVar("DisplayPortLinkCaps",
LinkEDPCapabilities,
LinkCapabilities)
class CableCapabilitiesEnum(IntEnum):
Unknown = 0,
DP40 = 1
DP54 = 2
DP80 = 3
def __str__(self) -> str:
return self.name
class RoutedLTConfig:
"""
Class `RoutedLTConfig` describes configuration fields for Routed LinkTraining.
"""
def __init__(self):
self.__is128b132b = False
self.__is_old_dp20_lt = False
self.__vs = 0
self.__pe = 0
self.__ffe = 0
self.__link_bw = 0
self.__lane_count = 0
@property
def is128b132b(self) -> bool:
return self.__is128b132b
@is128b132b.setter
def is128b132b(self, value: bool = False):
self.__is128b132b = value
@property
def is_old_dp20_lt(self) -> bool:
return self.__is_old_dp20_lt
@is_old_dp20_lt.setter
def is_old_dp20_lt(self, value: bool = False):
self.__is_old_dp20_lt = value
@property
def vs(self) -> int:
return self.__vs
@vs.setter
def vs(self, value: int):
if not (0 <= value < 3):
raise ValueError(f"VS cannot be less than 0 and more than 3")
self.__vs = value
@property
def pe(self) -> int:
return self.__pe
@pe.setter
def pe(self, value: int):
if not (0 <= value <= 0x3):
raise ValueError(f"PE cannot be less than 0 and more than 0x3")
self.__pe = value
@property
def ffe(self) -> int:
return self.__ffe
@ffe.setter
def ffe(self, value: int):
if not (0 <= value <= 0xF):
raise ValueError(f"FFE cannot be less than 0 and more than 0xF")
self.__ffe = value
@property
def link_bw(self) -> int:
return self.__link_bw
@link_bw.setter
def link_bw(self, value: int):
if not (0 <= value <= 0xFF):
raise ValueError(f"link_bw cannot be less than 0 and more than 0xFF")
self.__link_bw = value
@property
def lane_count(self) -> int:
return self.__lane_count
@lane_count.setter
def lane_count(self, value: int):
if not (0 <= value <= 0xFF):
raise ValueError(f"lane_count cannot be less than 0 and more than 0xFF")
self.__lane_count = value
def int_value(self) -> int:
val = (self.ffe << 8) if self.is128b132b else ((self.vs << 8) | (self.pe << 10))
return self.is128b132b | (self.__is_old_dp20_lt << 1) | val | (self.link_bw << 16) | (self.lane_count << 24)
class RoutedLTStatus:
__StateToStr = {0: "Not started", 1: "In progress", 2: "Finished", 3: "Incorrect"}
def __init__(self, enabled: bool, dp20_old_lt: bool, state: int, success: bool, step: int):
self.enabled = enabled
self.dp20_old_lt = dp20_old_lt
self.state = state
self.success = success
self.step = step
def __str__(self) -> str:
return (f"Is Enabled: {'yes' if self.enabled else 'no'}\n"
f"Is DP 20 old LT: {'yes' if self.dp20_old_lt else 'no'}\n"
f"State: {self.__StateToStr.get(self.state)}\n"
f"Result: {'Successful' if self.success else 'Unsuccessful'}\n"
f"Steps: {self.step}")

View File

@@ -0,0 +1,173 @@
from enum import IntEnum
from UniTAP.common.video_mode import VideoMode
class VCPStatus:
"""
Class `VCPStatus` describes МСЗ status. Contains following information:
- Port number.
- Stream ID.
- Requested PBN.
- Allocated PBN.
- Number of slots allocated for VC.
- Number of first time slot allocated for VC.
"""
def __init__(self):
self.port_number = 0
self.stream_id = 0
self.req_pbn = 0
self.alloc_pbn = 0
self.first_slot = 0
self.slot_num = 0
def __eq__(self, other):
return self.port_number == other.port_number and \
self.stream_id == other.stream_id and \
self.req_pbn == other.req_pbn and \
self.alloc_pbn == other.alloc_pbn and \
self.first_slot == other.first_slot and \
self.slot_num == other.slot_num
def __str__(self) -> str:
return f"Port # - {self.port_number}\n" \
f"SID - {self.stream_id}\n" \
f"REQ PBN - {self.req_pbn}\n" \
f"Alloc PBN - {self.alloc_pbn}\n" \
f"First Slot - {self.first_slot}\n" \
f"Slot num - {self.slot_num}\n"
class DpLinkEncoding(IntEnum):
"""
Class `DpLinkEncoding` contains all possible variants of DP link encoding.
"""
LE_NONE = 0
LE_8b10b = 1
LE_128b132b = 2
def __str__(self):
if self.value == DpLinkEncoding.LE_NONE:
return "None"
elif self.value == DpLinkEncoding.LE_8b10b:
return "8b/10b"
elif self.value == DpLinkEncoding.LE_128b132b:
return "128b/132b"
else:
return "Invalid"
class DpLinkTrainingResult(IntEnum):
"""
Class `DpLinkTrainingResult` contains all possible variants of Link training results.
"""
LTR_NOT_STARTED = 0
LTR_IN_PROGRESS = 1
LTR_FAIL = 2
LTR_SUCCESS = 3
class LaneStatus:
"""
Class `LaneStatus` describes lane status. Contains following information:
- CR state.
- SL state.
- EQ state.
- Voltage swing value.
- Pre Emphasis value.
- FFE preset value.
- Error count.
"""
def __init__(self):
self.cr = False
self.sl = False
self.eq = False
self.voltage_swing = 0
self.pre_emphasis = 0
self.ffe_preset = 0
self.error_count = 0
def __eq__(self, other) -> bool:
return self.cr == other.cr and \
self.sl == other.sl and \
self.eq == other.eq and \
self.voltage_swing == other.voltage_swing and \
self.pre_emphasis == other.pre_emphasis and \
self.ffe_preset == other.ffe_preset and \
self.error_count == other.error_count
def __str__(self) -> str:
return f"CR - {bool(self.cr)}\nSL - {bool(self.sl)}\nEQ - {bool(self.eq)}\n" \
f"Voltage Swing - {self.voltage_swing}\n" \
f"Pre Emphasis - {self.pre_emphasis}\nFFE Preset - {self.ffe_preset}\nError count - {self.error_count}\n"
class SdpCrc16:
"""
Class `SdpCrc16` describes SDP CRC16 errors on the stream. It contains 'State' - enabled or disabled, and errors -
count of the errors on the stream.
"""
def __init__(self):
self.state = False
self.errors = 0
def __eq__(self, other) -> bool:
return self.state == other.state and self.errors == other.errors
def __str__(self) -> str:
return f"State: {'Enabled' if self.state else 'Disabled'}\n" \
f"Errors: {hex(self.errors)}"
class StreamStatus:
"""
Class `StreamStatus` describes stream status. Contains following information:
- Video mode `VideoMode`.
- CRC value of stream.
- DSC CRC value of stream.
- SDP CRC16 error of stream
"""
def __init__(self):
self.video_mode = VideoMode()
self.crc = [0, 0, 0]
self.dsc_crc = [0, 0, 0]
self.sdp_crc16 = SdpCrc16()
def __str__(self) -> str:
return f"Video mode:\n{self.video_mode.__str__()}\n" \
f"CRC: r - {self.crc[0]}, g - {self.crc[1]}, b - {self.crc[2]}\n" \
f"DSC CRC: r - {self.dsc_crc[0]}, g - {self.dsc_crc[1]}, b - {self.dsc_crc[2]}\n"
class StreamStatusDP(StreamStatus):
"""
The `StreamStatusDP` class inherited from the `StreamStatus` class and contains all the functionality.
- MVID
- NVID
- VFREQ `vfreq`
"""
def __init__(self):
super().__init__()
self.mvid = 0
self.nvid = 0
@property
def vfreq(self) -> int:
"""
Return value of VFREQ.
Returns:
object of `int` type
"""
return ((self.mvid & 0xFFFFFF) << 24) | (self.nvid & 0xFFFFFF)
def __str__(self) -> str:
return f"Video mode:\n{self.video_mode.__str__()}\n" \
f"CRC: r - {self.crc[0]}, g - {self.crc[1]}, b - {self.crc[2]}\n" \
f"DSC CRC: r - {self.dsc_crc[0]}, g - {self.dsc_crc[1]}, b - {self.dsc_crc[2]}\n" \
f"MVID / NVID - {self.mvid:06X} / {self.nvid:06X}\n" \
f"VFREQ - {self.vfreq}\n"
DP21_LinkRate = {1: 10.0, 2: 20.0, 4: 13.5}
DP21_LinkRateRev = {10.0: 1, 20.0: 2, 13.5: 4}

View File

@@ -0,0 +1,279 @@
from warnings import warn
from UniTAP.libs.lib_tsi import *
from UniTAP.dev.ports.modules.link.dp.link_status_common import DpLinkTrainingResult
from UniTAP.libs.lib_tsi.tsi_io import PortIO
from UniTAP.utils.function_wrapper import *
from .link_tx_status import LinkDisplayPortStatusSource
from .link_tx_config import LinkDisplayPortConfig
from .link_tx_force_config import LinkDisplayPortForceConfig
from .private_link_tx_types import *
from UniTAP.dev.ports.modules.dpcd import DPCDRegisters
from .link_tx_types import DPLinkPattern, DPOutLinkMode
from .dp_cable_info import DpCableInfo, CableCapabilitiesEnum
class LinkDisplayPortTx:
"""
Class `LinkDisplayPortTx` contains information about DP link.
- Read link status `status`.
- Configure and read link configuration `config`.
- Get maximum stream count `max_stream_count`.
- Do link training `link_training`.
- Get last result of link training `last_lt_result`.
- Read and write scrambler seed value `scrambler_seed`.
- Set `set_override_voltage` and get `get_override_voltage` override voltage.
- Set `set_override_pre_emp` and get `get_override_pre_emp` override pre-emphasis.
- Set and get override FFE presets `override_ffe_presets`.
- Set and get link pattern `link_pattern` 'set_link_pattern'.
- Set and get force link config `LinkDisplayPortForceConfig`.
"""
def __init__(self, port_io: PortIO, dpcd: DPCDRegisters, hw_caps: DPTXHWCaps):
self.__io = port_io
self.__HW_CAPS = hw_caps
self.__status = LinkDisplayPortStatusSource(self.__io, self.__HW_CAPS, dpcd)
self.__config = LinkDisplayPortConfig(self.__io, self.__HW_CAPS)
self.__force_config = LinkDisplayPortForceConfig(self.__io, self.__HW_CAPS)
self.__cable_info = DpCableInfo(port_io, TSI_DPTX_CABLE_ATTRIBUTES_R)
@property
def status(self) -> LinkDisplayPortStatusSource:
"""
Returns object of class `LinkDisplayPortStatusSource` for working with link status.
Returns:
object of `LinkDisplayPortStatusSource` type
"""
return self.__status
@property
def config(self) -> LinkDisplayPortConfig:
"""
Returns object of class `LinkDisplayPortConfig` for working with link configuration.
Returns:
object of `LinkDisplayPortConfig` type
"""
return self.__config
@property
def force_config(self) -> LinkDisplayPortForceConfig:
"""
Returns object of class `LinkDisplayPortForceConfig` for working with link configuration.
Returns:
object of `LinkDisplayPortForceConfig` type
"""
return self.__force_config
@property
def max_stream_count(self):
"""
Returns maximum supported stream count.
Returns:
object of int type
"""
return self.__HW_CAPS.mst_stream_count
def start_link_training(self) -> bool:
"""
Make link training. Returns 'True' state if link training was success, 'False' - if not.
Returns:
object of bool type
"""
def is_link_training_success(link: LinkDisplayPortTx):
return link.last_lt_result() == DpLinkTrainingResult.LTR_SUCCESS
self.__io.set(TSI_W_DPTX_COMMAND, 1, c_uint32)
return function_scheduler(is_link_training_success, self, interval=0.1, timeout=5)
def last_lt_result(self) -> DpLinkTrainingResult:
"""
Returns last result of link training.
Returns:
object of `DpLinkTrainingResult` type
"""
_, value = self.__io.get(TSI_DPTX_LT_RESULT_R, c_uint)
return DpLinkTrainingResult(value)
@property
def scrambler_seed(self) -> int:
"""
Returns scrambler seed value.
Returns:
object of `int` type
"""
if not self.__HW_CAPS.scrambler_seed:
warnings.warn("Scrambler Seed is not supported.")
return 0
return self.__io.get(TSI_DPTX_SCR_SEED, c_uint32)[1]
@scrambler_seed.setter
def scrambler_seed(self, value: int = 0):
"""
Write new value to scrambler seed
Args:
value (int) - new scrambler seed value
"""
if not self.__HW_CAPS.scrambler_seed:
warnings.warn("Scrambler Seed is not supported.")
return
if self.config._read_lt_features().auto_seed is not None and not self.config._read_lt_features().auto_seed:
self.__io.set(TSI_DPTX_SCR_SEED, value, c_uint32)
else:
warnings.warn("AutoSeed has already enabled. Transferred value will not be written")
def get_override_voltage(self, stream_index: int) -> int:
"""
Returns override voltage of selected stream.
Args:
stream_index (int) - number of selected stream
Returns:
object of `int` type
"""
return (self.__get_override_voltage() >> stream_index) & 0x3
def set_override_voltage(self, stream_index: int, value: int):
"""
Returns override voltage of selected stream.
Args:
stream_index (int) - number of selected stream
value (int) - new override voltage value
"""
if stream_index < self.status.mst_stream_count:
raise ValueError(f"Selected stream {stream_index} is not available. "
f"Available stream number: {self.status.mst_stream_count}")
if not (0 <= value <= 3):
raise ValueError(f"Incorrect input value {value}. Available range: 0-{self.status.lane_count}")
old_value = self.__get_override_voltage()
old_value &= ~(3 << (stream_index * 8))
old_value |= (value << (stream_index * 8))
self.__io.set(TSI_DPTX_OVERRIDE_VOLTAGE_SWING, old_value, c_uint32)
def get_override_pre_emp(self, stream_index: int) -> int:
"""
Returns override pre-emphasis of selected stream.
Args:
stream_index (int) - number of selected stream
Returns:
object of `int` type
"""
return (self.__get_override_pre_emp() >> stream_index) & 0x3
def set_override_pre_emp(self, stream_index: int, value: int):
"""
Returns override pre-emphasis of selected stream.
Args:
stream_index (int) - number of selected stream
value (int) - new override pre-emphasis value
"""
if stream_index < self.status.mst_stream_count:
raise ValueError(f"Selected stream {stream_index} is not available. "
f"Available stream number: {self.status.mst_stream_count}")
if not (0 <= value <= 3):
raise ValueError(f"Incorrect input value {value}. Available range: 0-{self.status.lane_count}")
old_value = self.__get_override_pre_emp()
old_value &= ~(3 << (stream_index * 8))
old_value |= (value << (stream_index * 8))
self.__io.set(TSI_DPTX_OVERRIDE_PRE_EMPHASIS, old_value, c_uint32)
@property
def override_ffe_presets(self) -> list:
"""
Returns override FFE presets values.
Returns:
object of `list` type
"""
if not self.__HW_CAPS.dp2_custom_rates:
warnings.warn("FFE presets does not support.")
return []
return self.__io.get(TSI_DP2TX_OUT_FFE, c_uint8, 4)[1]
@override_ffe_presets.setter
def override_ffe_presets(self, value: List[int]):
"""
Returns override FFE presets.
Args:
value list[int] - new values for override FFE presets.
"""
if not self.__HW_CAPS.dp2_custom_rates:
warnings.warn("FFE presets does not support.")
return
new_value = value[0] | (value[0] << 8) | (value[0] << 16) | (value[0] << 32)
self.__io.set(TSI_DP2TX_OUT_FFE, new_value)
def __get_override_voltage(self) -> int:
return self.__io.get(TSI_DPTX_OVERRIDE_VOLTAGE_SWING, c_uint32)[1]
def __get_override_pre_emp(self) -> int:
return self.__io.get(TSI_DPTX_OVERRIDE_PRE_EMPHASIS, c_uint32)[1]
@property
def link_pattern(self) -> DPLinkPattern:
"""
Returns current DP link pattern `DPLinkPattern`.
Returns:
object of `DPLinkPattern` type
"""
return DPLinkPattern(self.__io.get(TSI_DPTX_OUTPUT_PATTERN, c_uint32)[1])
def set_link_pattern(self, pattern: DPLinkPattern, additional_param: int = 1):
"""
Write DP link pattern value `DPLinkPattern`.
Args:
pattern (DPLinkPattern) - new pattern value.
additional_param (int)
"""
if not self.__HW_CAPS.dp2_custom_rates and pattern not in [e.value for e in DPLinkPattern]:
raise ValueError(f"Current pattern {pattern.name} is not supported")
self.__io.set(TSI_DPTX_OUTPUT_PATTERN, pattern.value, c_uint32)
if pattern == DPLinkPattern.LinkSquarePattern:
self.__io.set(TSI_DPTX_SQUARE_PATTERN_NUMBER, additional_param, c_uint32)
def set_force_link_mode(self, link_mode: DPOutLinkMode):
self.__io.set(TSI_DPTX_OUT_LINK_MODE, link_mode.value, c_uint32)
def get_force_link_mode(self) -> DPOutLinkMode:
return DPOutLinkMode(self.__io.get(TSI_DPTX_OUT_LINK_MODE, c_uint32)[1])
def cable_rx_type(self) -> CableCapabilitiesEnum:
"""
Get cable type from the RX side.
Returns:
object of `CableCapabilitiesEnum` type
"""
return self.__cable_info.get_another_port_cable_type()
def cable_tx_type(self) -> CableCapabilitiesEnum:
"""
Get cable type from the TX side.
Returns:
object of `CableCapabilitiesEnum` type
"""
warn("TX cable information is no longer available on the TX port.", DeprecationWarning,2)
return CableCapabilitiesEnum.Unknown

View File

@@ -0,0 +1,360 @@
from typing import Optional, Union, Type
from UniTAP.libs.lib_tsi import PortIO
from UniTAP.libs.lib_tsi.tsi_private_types import *
from .link_tx_types import *
from .private_link_tx_types import *
from .link_status_common import *
class LinkDisplayPortConfig:
"""
Class `LinkDisplayPortConfig` allows settings link configuration on Source (TX - transmitter) side.
- Set configuration `set`.
- Get current configuration on link `get`.
"""
def __init__(self, port_io: PortIO, caps: DPTXHWCaps):
self.__io = port_io
self.__caps = caps
self.__dp2_custom_rate = self.__get_dp2_custom_rates()
def set(self, config: DisplayPortLinkConfig):
"""
Write new configuration on DP link.
Args:
config (`DisplayPortLinkConfig`) - `LinkConfig.DP8b10b`, `LinkConfig.DP128b132b` or `LinkConfig.eDP`.
"""
if isinstance(config, LinkConfig.DP8b10b):
self.__set_lane_count_dp14(config.lane_count)
self.__set_bit_rate_14(config.bit_rate)
self.__set_lt_features(config)
self.__set_config_ssc(config.ssc)
self.__enable_mst(config.mst)
self.__set_mst_channel_count(config.mst_stream_count)
self.__try_fec_after_lt(config.fec)
self.__set_post_lt_features(config)
self.__sdp_control(config)
elif isinstance(config, LinkConfig.DP128b132b):
self.__set_lane_count_dp21(config.lane_count)
self.__set_bit_rate_21(config.bit_rate)
self.__set_lt_features(config)
self.__set_lttpr(config.lttpr)
self.__set_config_ssc(config.ssc)
self.__set_post_lt_features(config)
self.__enable_mst(config.mst)
self.__set_mst_channel_count(config.mst_stream_count)
self.__sdp_control(config)
elif isinstance(config, LinkConfig.eDP):
self.__set_lane_count_dp14(config.lane_count)
self.__set_lt_features(config)
self.__set_edp_sel_rate(config.eDp_cur_rate)
else:
raise TypeError("Incorrect DisplayPortLinkConfig format!")
def get(self, config_type: Union[Type[DisplayPortLinkConfig], None] = None) -> DisplayPortLinkConfig:
"""
Returns current DP source link configuration.
Returns:
object of `DisplayPortLinkConfig` type
"""
if config_type is not None and issubclass(config_type, LinkConfig.eDP):
lt_features = self._read_lt_features()
link_status = LinkConfig.eDP()
link_status.lane_count = self.__get_lane_count_dp14()
link_status.force_edp = lt_features.force_edp
link_status.eDp_cur_rate = self.__get_edp_sel_rate()
link_status.eDp_supported_rates = self.__get_edp_caps_rates()
link_status.eDp_aux_preamble = lt_features.edp_aux_preamble
return link_status
elif config_type is not None and issubclass(config_type, LinkConfig.DP8b10b):
return self.__fill_dp14_config()
elif config_type is not None and issubclass(config_type, LinkConfig.DP128b132b) or \
self.__link_encoding() == DpLinkEncoding.LE_128b132b:
return self.__fill_dp21_config()
elif config_type is None and self.__link_encoding() == DpLinkEncoding.LE_8b10b:
return self.__fill_dp14_config()
elif config_type is None and \
(self.__caps.support_10gbps or self.__caps.support_20gbps or self.__caps.support_13_5gbps) and \
self.__link_encoding() == DpLinkEncoding.LE_128b132b:
return self.__fill_dp21_config()
else:
raise ValueError('Incorrect link encoding type')
def __fill_dp14_config(self) -> LinkConfig.DP8b10b:
lt_features = self._read_lt_features()
post_lt_features = self.__get_post_lt_features()
link_status = LinkConfig.DP8b10b()
link_status.lane_count = self.__get_lane_count_dp14()
link_status.bit_rate = self.__get_bit_rate_14()
link_status.mst = self.__mst_enabled()
link_status.mst_stream_count = self.__mst_stream_count()
link_status.enhanced_framing_mode = lt_features.framing
link_status.auto_seed = lt_features.auto_seed
link_status.ssc = self.__get_config_ssc()
link_status.fec = self.__fec_after_lt_state()
link_status.force_edid_timings_after_lt = post_lt_features.force_edid_timings
link_status.adaptive_sync_auto_enable = post_lt_features.as_auto_enable
link_status.split_sdp = self.__read_sdp_control().dp_split_sdp != 0 if self.__caps.sdp_split else False
return link_status
def __fill_dp21_config(self) -> LinkConfig.DP128b132b:
link_status = LinkConfig.DP128b132b()
link_status.lane_count = self.__get_lane_count_dp21()
if self.__check_custom_bit_rate() != 0:
link_status.bit_rate = self.__check_custom_bit_rate()
else:
link_status.bit_rate = self.__get_bit_rate_21()
lt_features = self._read_lt_features()
post_lt_features = self.__get_post_lt_features()
link_status.force_dp_128_132 = lt_features.force_dp2
link_status.enhanced_framing_mode = lt_features.framing
link_status.max_link_bandwidth_supported = lt_features.max_link_bw_policy
link_status.old_dp2_lt = lt_features.old_dp2_lt
link_status.lttpr = self.__lttpr_active()
link_status.try_dp_128_132 = lt_features.try_dp2
link_status.auto_seed = lt_features.auto_seed
link_status.ssc = self.__get_config_ssc()
link_status.force_edid_timings_after_lt = post_lt_features.force_edid_timings
link_status.adaptive_sync_auto_enable = post_lt_features.as_auto_enable
link_status.mst = self.__mst_enabled()
link_status.mst_stream_count = self.__mst_stream_count()
link_status.crc_16 = self.__read_sdp_control().dp2_crc_sdp != 0 if self.__caps.dp2_sdp_crc else False
return link_status
def _read_lt_features(self) -> LTFeatures:
return self.__io.get(TSI_DPTX_LT_FEATURES, LTFeatures)[1]
def __get_post_lt_features(self) -> PostLtFeatures:
return self.__io.get(TSI_DPTX_POST_LT_FEATURES, PostLtFeatures)[1]
def __set_post_lt_features(self, config: DisplayPortLinkConfig):
post_lt_features = self.__get_post_lt_features()
if config.force_edid_timings_after_lt is not None and self.__caps.edid_parser:
post_lt_features.force_edid_timings = config.force_edid_timings_after_lt
if config.adaptive_sync_auto_enable is not None and self.__caps.adaptive_sync:
post_lt_features.as_auto_enable = config.adaptive_sync_auto_enable
self.__io.set(TSI_DPTX_POST_LT_FEATURES, post_lt_features.value(), c_uint32)
def __get_dp2_custom_rates(self) -> list:
rates = self.__io.get(TSI_DP2TX_CUSTOM_RATE_CAPS_R, c_uint32, 32)[1]
custom_rates = []
for i in range(len(rates)):
custom_rates.append(float(rates[i]) * 200000 / 1000000000)
return custom_rates
def __set_lane_count_dp14(self, lane_count: Optional[int]):
if lane_count is None:
return
if lane_count not in [1, 2, 4]:
raise ValueError(f"Incorrect lane count number {lane_count}. Must be from list: 1, 2, 4")
self.__io.set(TSI_DPTX_LINK_CFG_LANES, lane_count, c_uint32)
def __get_lane_count_dp14(self) -> int:
return self.__io.get(TSI_DPTX_LINK_CFG_LANES)[1]
def __set_lane_count_dp21(self, lane_count: Optional[int]):
if lane_count is None:
return
if lane_count not in [1, 2, 4]:
raise ValueError(f"Incorrect lane count number {lane_count}. Must be from list: 1, 2, 4")
self.__io.set(TSI_DP2TX_LT_SLC, lane_count, c_uint32)
def __get_lane_count_dp21(self) -> int:
return self.__io.get(TSI_DP2TX_LT_SLC)[1]
def __set_bit_rate_14(self, bit_rate: Optional[float]):
if bit_rate is None:
return
if int(round(bit_rate / 0.27)) > self.__caps.max_link_rate:
raise ValueError(f"Following 8b/10b bit rate {bit_rate} is not supported on device. "
f"Max supported link rate {round(self.__caps.max_link_rate * 0.27, 2)}")
self.__io.set(TSI_DPTX_LINK_CFG_BIT_RATE, int(round(bit_rate / 0.27)), c_uint32)
def __get_bit_rate_14(self) -> float:
return round(self.__io.get(TSI_DPTX_LINK_CFG_BIT_RATE)[1] * 0.27, 2)
def __set_bit_rate_21(self, bit_rate: Optional[Union[float, int]]):
if bit_rate is None:
return
if bit_rate in self.__dp2_custom_rate:
if not self.__caps.support_10gbps:
raise ValueError(f"Following bit rate {bit_rate} is not supported on device.")
else:
res = self.__io.get(TSI_DP2TX_CUSTOM_RATE_MAP, c_uint32, 3)[1]
self.__io.set(TSI_DP2TX_CUSTOM_RATE_MAP, (int(bit_rate * 1000000000 / 200000), res[1], res[2]),
c_uint32, 3)
elif (bit_rate == 10.0 and not self.__caps.support_10gbps) or \
(bit_rate == 13.5 and not self.__caps.support_13_5gbps) or \
(bit_rate == 20.0 and not self.__caps.support_20gbps):
raise ValueError(f"Following bit rate {bit_rate} is not supported on device.")
self.__io.set(TSI_DP2TX_LT_SBR, DP21_LinkRateRev.get(bit_rate), c_uint32)
def __get_bit_rate_21(self) -> float:
return DP21_LinkRate.get(self.__io.get(TSI_DP2TX_LT_SBR)[1])
def __support_ssc(self) -> bool:
return (self.__io.get(TSI_DPTX_DOWNSPREAD_STATUS_R, c_uint32)[1] >> 1) & 1 != 0
def __set_config_ssc(self, ssc_conf: Optional[SSCConfig]):
if ssc_conf is None:
return
if not self.__support_ssc():
raise ValueError(f"SSC is not supported on the device.")
self.__io.set(TSI_DPTX_DOWNSPREAD_CONTROL, int(ssc_conf.enabled), c_uint32)
if ssc_conf.enabled:
if not (1 <= ssc_conf.amplitude <= 50):
raise ValueError(f"Incorrect amplitude value {ssc_conf.amplitude}. Must be from range: 0.1 - 0.5")
self.__io.set(TSI_DPTX_DOWNSPREAD_AMP, ssc_conf.amplitude, c_uint32)
if not (30000 <= ssc_conf.frequency <= 63000):
raise ValueError(f"Incorrect frequency value {ssc_conf.frequency}. Must be from range: 30000 - 63000")
self.__io.set(TSI_DPTX_DOWNSPREAD_FREQ, ssc_conf.frequency, c_uint32)
def __get_config_ssc(self) -> SSCConfig:
if not self.__support_ssc():
return None
ssc_conf = SSCConfig()
ssc_conf.enabled = (self.__io.get(TSI_DPTX_DOWNSPREAD_STATUS_R)[1] & 1) != 0
ssc_conf.frequency = self.__io.get(TSI_DPTX_DOWNSPREAD_FREQ)[1]
ssc_conf.amplitude = self.__io.get(TSI_DPTX_DOWNSPREAD_AMP)[1]
return ssc_conf
def __dp2_support_rates(self) -> bool:
return self.__caps.support_10gbps or self.__caps.support_13_5gbps or self.__caps.support_20gbps
def __set_lttpr(self, enable: Optional[bool]):
if enable is None:
return
if not self.__dp2_support_rates():
raise ValueError(f"LTTPR is not supported on the device.")
self.__io.set(TSI_DPTX_LTTPR_CONTROL, int(enable))
def __set_lt_features(self, config: Optional[DisplayPortLinkConfig]):
if config is None:
return
res = self._read_lt_features()
if isinstance(config, LinkConfig.DP8b10b):
if config.enhanced_framing_mode is not None:
res.framing = int(config.enhanced_framing_mode)
if config.auto_seed is not None:
res.auto_seed = int(config.auto_seed)
if self.__caps.support_10gbps or self.__caps.support_20gbps or self.__caps.support_13_5gbps:
res.try_dp2 = 0
res.try_edp = 0
elif isinstance(config, LinkConfig.DP128b132b):
if not self.__dp2_support_rates():
raise ValueError(f"DP 2.1 is not supported on the device.")
if config.try_dp_128_132 is not None:
res.try_dp2 = int(config.try_dp_128_132)
if config.force_dp_128_132 is not None:
res.force_dp2 = int(config.force_dp_128_132)
if config.old_dp2_lt is not None:
res.old_dp2_lt = int(config.old_dp2_lt)
if config.enhanced_framing_mode is not None:
res.framing = int(config.enhanced_framing_mode)
if config.auto_seed is not None:
res.auto_seed = int(config.auto_seed)
if config.max_link_bandwidth_supported is not None:
res.max_link_bw_policy = int(config.max_link_bandwidth_supported)
res.try_edp = 0
elif isinstance(config, LinkConfig.eDP):
if self.__caps.edp:
if config.force_edp is not None:
res.force_edp = config.force_edp
if config.eDp_aux_preamble is not None:
res.edp_aux_preamble = config.eDp_aux_preamble
res.try_edp = 1
elif not self.__caps.edp:
raise ValueError("EDP is not supported.")
else:
raise TypeError("Incorrect DisplayPortLinkConfig format!")
self.__io.set(TSI_DPTX_LT_FEATURES, res.value(), c_uint32)
def __enable_mst(self, enable: Optional[bool]):
if enable is None:
return
self.__io.set(TSI_DPTX_COMMAND_W, 3 if enable else 4, c_uint32)
def __set_mst_channel_count(self, count: Optional[int]):
if count is None:
return
if not (0 <= count <= self.__caps.mst_stream_count):
raise ValueError(f"Incorrect stream count {count}. Available stream count {self.__caps.mst_stream_count}")
self.__io.set(TSI_PG_ENABLED_STREAM_COUNT, count, c_uint32)
def __link_encoding(self) -> DpLinkEncoding:
result, value = self.__io.get(TSI_DPTX_LINK_MODE_R, c_uint)
if value == 0:
return DpLinkEncoding.LE_8b10b
else:
return DpLinkEncoding.LE_128b132b
def __mst_enabled(self) -> bool:
return (self.__io.get(TSI_DPTX_LINK_STATUS_BITS_R)[1] & (1 << 30)) != 0
def __mst_stream_count(self) -> int:
result, mst_status = self.__io.get(TSI_DPTX_MST_STATUS_R, c_uint)
return (mst_status >> 8) & 0xFF
def __lttpr_active(self) -> Optional[bool]:
if self.__link_encoding == DpLinkEncoding.LE_128b132b:
return (self.__io.get(TSI_DPTX_LTTPR_CONTROL, c_uint32)[1] & 1) != 0
else:
return None
def __check_custom_bit_rate(self) -> float:
res = self.__io.get(TSI_DP2TX_CUSTOM_RATE_MAP, c_uint32, 3)[1]
for i in range(3):
if res[i] != 0:
return res[i] * 200000 / 1000000000
return 0
def __try_fec_after_lt(self, enable: bool):
result = self.__io.get(TSI_DPTX_FEC_CTRL, c_int)
val = result[1]
if enable:
val |= 0x2
else:
val &= ~0x2
self.__io.set(TSI_DPTX_FEC_CTRL, val)
def __fec_after_lt_state(self) -> bool:
return (self.__io.get(TSI_DPTX_FEC_CTRL, c_int)[1] & 0x2) != 0
def __sdp_control(self, config: Optional[DisplayPortLinkConfig]):
if config is None:
return
sdp_control = self.__read_sdp_control()
if self.__caps.dp2_sdp_crc and isinstance(config, LinkConfig.DP128b132b) and config.crc_16 is not None:
sdp_control.dp2_crc_sdp = config.crc_16
if self.__caps.sdp_split and isinstance(config, LinkConfig.DP8b10b) and config.split_sdp is not None:
sdp_control.dp_split_sdp = config.split_sdp
self.__write_sdp_control(sdp_control)
def __write_sdp_control(self, sdp_control: SdpControl):
self.__io.set(TSI_DPTX_SDP_CTRL, sdp_control.value())
def __read_sdp_control(self) -> SdpControl:
return self.__io.get(TSI_DPTX_SDP_CTRL, SdpControl)[1]
def __set_edp_sel_rate(self, rate: float):
if self.__caps.edp and rate in self.__get_edp_caps_rates():
self.__io.set(TSI_DPTX_EDP_LT_SBR, int(rate / 0.0002), c_uint32)
elif not self.__caps.edp:
raise ValueError("EDP is not supported.")
def __get_edp_sel_rate(self) -> int:
if self.__caps.edp:
return self.__io.get(TSI_DPTX_EDP_LT_SBR)[1] * 0.0002
def __get_edp_caps_rates(self) -> List[float]:
if self.__caps.edp:
rates = self.__io.get(TSI_DPTX_EXTRA_DP14_RATES, data_type=c_uint32, data_count=8)[1]
new_rates = []
for rate in rates:
if rate > 0:
new_rates.append(rate * 0.27)
return new_rates

View File

@@ -0,0 +1,86 @@
from typing import Optional, Union, Type
from UniTAP.libs.lib_tsi import PortIO
from UniTAP.libs.lib_tsi.tsi_private_types import *
from .link_tx_types import *
from .private_link_tx_types import *
from .link_status_common import *
class LinkDisplayPortForceConfig:
"""
Class `LinkDisplayPortForceConfig` allows settings link configuration on Source (TX - transmitter) side.
- Set configuration `set`.
- Get current configuration on link `get`.
"""
def __init__(self, port_io: PortIO, caps: DPTXHWCaps):
self.__io = port_io
self.__caps = caps
def set(self, config: DisplayPortLinkConfig):
"""
Write new configuration on DP link.
Args:
config (`DisplayPortLinkConfig`).
"""
if isinstance(config, LinkConfig.Force8b10b):
# TODO
pass
elif isinstance(config, LinkConfig.Force128b132b):
self.__force_set_bit_rate(config.bit_rate)
self.__force_set_lane_count(config.lane_count)
self.__force_set_pattern(config.pattern)
else:
raise TypeError("Incorrect LinkConfig Force format!")
def get(self, config_type: Type[DisplayPortLinkConfig]) -> DisplayPortLinkConfig:
"""
Returns current DP source link configuration.
Args:
config_type Type[DisplayPortLinkConfig].
Returns:
object of `DisplayPortLinkConfig` type
"""
if issubclass(config_type, LinkConfig.Force8b10b):
# TODO
return LinkConfig.Force8b10b()
elif issubclass(config_type, LinkConfig.Force128b132b):
link_status = LinkConfig.Force128b132b()
link_status.lane_count = self.__force_get_lane_count()
link_status.bit_rate = self.__force_get_bit_rate()
link_status.pattern = self.__force_get_pattern()
return link_status
else:
raise TypeError("Incorrect type!")
def __force_set_bit_rate(self, bit_rate: Optional[Union[float, int]]):
if bit_rate is None:
return
elif (bit_rate == 10.0 and not self.__caps.support_10gbps) or \
(bit_rate == 13.5 and not self.__caps.support_13_5gbps) or \
(bit_rate == 20.0 and not self.__caps.support_20gbps):
raise ValueError(f"Following bit rate {bit_rate} is not supported on device.")
self.__io.set(TSI_DP2TX_OUT_BR, DP21_LinkRateRev.get(bit_rate), c_uint32)
def __force_get_bit_rate(self) -> float:
return DP21_LinkRate.get(self.__io.get(TSI_DP2TX_OUT_BR)[1])
def __force_set_lane_count(self, lane_count: Optional[int]):
if lane_count is None:
return
if lane_count not in [1, 2, 4]:
raise ValueError(f"Incorrect lane count number {lane_count}. Must be from list: 1, 2, 4")
self.__io.set(TSI_DP2TX_OUT_LC, lane_count, c_uint32)
def __force_get_lane_count(self) -> int:
return self.__io.get(TSI_DP2TX_OUT_LC)[1]
def __force_set_pattern(self, pattern: Optional[DP128b132bLinkPattern]):
if pattern is None:
return
self.__io.set(TSI_DP2TX_OUT_TEST_PATTERN, pattern.value, c_uint32)
def __force_get_pattern(self) -> DP128b132bLinkPattern:
return DP128b132bLinkPattern(self.__io.get(TSI_DP2TX_OUT_TEST_PATTERN, c_uint32)[1])

View File

@@ -0,0 +1,432 @@
import warnings
from typing import Optional
from UniTAP.libs.lib_tsi import PortIO
from UniTAP.libs.lib_tsi.tsi_types import *
from .link_tx_types import *
from .private_link_tx_types import *
from .link_status_common import *
from .private_link_status_common import *
from typing import Union
from UniTAP.dev.ports.modules.dpcd import DPCDRegisters
class LinkDisplayPortStatusSource:
"""
Class `LinkDisplayPortStatusSource` describes information about DP link status. Contains following info:
- MST stream count `mst_stream_count`.
- Link encoding `link_encoding`.
- Link rate `link_rate`.
- Lane count `lane_count`.
- State of HPD `hpd_asserted`.
- State of framing `enhanced_framing`.
- State of scrambling `scrambling_enabled`.
- State of DSC `dsc_enabled`
- State of FEC `fec_enabled`.
- State of MST `mst_enabled`.
- State of SSC `ssc_enabled`.
- State of ILA `ila`.
- State of EQ ILA `eq_ila`.
- State of CDS ILA `cds_ila`.
- State of LT fail `lt_fail`.
- State of selected lane `lane`.
- State of VCP `vcp`.
- State of selected stream `stream`.
- Send ACT command `send_act`.
"""
def __init__(self, port_io: PortIO, caps: DPTXHWCaps, dpcd: DPCDRegisters):
self.__io = port_io
self.__caps = caps
self.__dpcd = dpcd
@property
def mst_stream_count(self) -> int:
"""
Returns current mst stream count.
Returns:
object of int type
"""
result, mst_status = self.__io.get(TSI_DPTX_MST_STATUS_R, c_uint)
return (mst_status >> 8) & 0xFF
@property
def link_encoding(self) -> DpLinkEncoding:
"""
Returns current link encoding `DpLinkEncoding`.
Returns:
object of DpLinkEncoding type
"""
result, value = self.__io.get(TSI_DPTX_LINK_MODE_R, c_uint)
if value == 0:
return DpLinkEncoding.LE_8b10b
else:
return DpLinkEncoding.LE_128b132b
@property
def link_rate(self) -> float:
"""
Returns current link rate.
Returns:
object of float type
"""
if self.link_encoding == DpLinkEncoding.LE_128b132b:
if self.__check_custom_bit_rate() != 0:
return self.__check_custom_bit_rate()
else:
return DP21_LinkRate.get(self.__io.get(TSI_DP2TX_LT_RATE_R, c_uint)[1])
else:
return round(self.__io.get(TSI_DPTX_LINK_RATE_R, c_uint)[1] * 0.27, 2)
@property
def lane_count(self) -> int:
"""
Returns current lane count.
Returns:
object of int type
"""
return self.__io.get(TSI_DPTX_LINK_LANE_COUNT_R, c_int)[1]
@property
def hpd_asserted(self) -> bool:
"""
Returns current state of HPD asserted.
Returns:
object of bool type
"""
return (self.__io.get(TSI_DPTX_HPD_STATUS_R, c_int)[1] & 1) != 0
@property
def available_link_rate(self) -> float:
"""
Returns available link rate.
Returns:
object of float type
"""
if self.link_encoding == DpLinkEncoding.LE_128b132b:
link_rate = self.link_rate * 1000000000 * self.lane_count * ((128 * 383) / (132 * 384))
else:
link_rate = self.link_rate * 1000000000 * self.lane_count
if self.mst_enabled:
if self.fec_enabled:
link_rate = link_rate * 7690 / 10000
else:
link_rate = link_rate * 7875 / 10000
else:
if self.fec_enabled:
link_rate = link_rate * 7813 / 10000
else:
link_rate = link_rate * 8000 / 10000
return link_rate
def lane(self, lane_number: int) -> LaneStatus:
"""
Returns current status of selected lane `LaneStatus`.
Args:
lane_number (int) - number of selected lane
Returns:
object of LaneStatus type
"""
if not (0 <= lane_number <= 3):
raise ValueError(f"Incorrect lane number {lane_number}. Available range: 0-{self.lane_count}")
status = self.__read_link_status()
voltage_swing = self.__io.get(TSI_DPTX_LINK_VOLTAGE_SWING_R, LinkVoltageSwing)[1]
pre_emphasis = self.__io.get(TSI_DPTX_LINK_PRE_EMPHASIS_R, LinkPreEmphasis)[1]
error_counters = self.__dpcd.read(0x210, 8).data
lane_status = LaneStatus()
if lane_number == 0:
lane_status.cr = status.l0_cr
lane_status.sl = status.l0_sl
lane_status.eq = status.l0_eq
lane_status.voltage_swing = voltage_swing.vs_l0
lane_status.pre_emphasis = pre_emphasis.pe_l0
lane_status.error_count = error_counters[0] | ((error_counters[1] & 0x7F) << 8)
elif lane_number == 1:
lane_status.cr = status.l1_cr
lane_status.sl = status.l1_sl
lane_status.eq = status.l1_eq
lane_status.voltage_swing = voltage_swing.vs_l1
lane_status.pre_emphasis = pre_emphasis.pe_l1
lane_status.error_count = error_counters[2] | ((error_counters[3] & 0x7F) << 8)
elif lane_number == 2:
lane_status.cr = status.l2_cr
lane_status.sl = status.l2_sl
lane_status.eq = status.l2_eq
lane_status.voltage_swing = voltage_swing.vs_l2
lane_status.pre_emphasis = pre_emphasis.pe_l2
lane_status.error_count = error_counters[4] | ((error_counters[5] & 0x7F) << 8)
elif lane_number == 3:
lane_status.cr = status.l3_cr
lane_status.sl = status.l3_sl
lane_status.eq = status.l3_eq
lane_status.voltage_swing = voltage_swing.vs_l3
lane_status.pre_emphasis = pre_emphasis.pe_l3
lane_status.error_count = error_counters[6] | ((error_counters[7] & 0x7F) << 8)
if self.link_encoding == DpLinkEncoding.LE_128b132b:
ffe_preset = self.__io.get(TSI_DP2TX_LT_FFE_PRESET_R, c_uint32)[1]
lane_status.ffe_preset = (ffe_preset >> (8 * lane_number)) & 0xFF
else:
lane_status.ffe_preset = 0
return lane_status
@property
def dsc_enabled(self) -> Union[bool, None]:
"""
Returns current state of DSC (enabled or disabled).
None if DSC does not support.
Returns:
object of bool or None type
"""
if self.__caps.dsc or self.__caps.dsc2:
return (self.__io.get(TSI_DPTX_DSC_STATUS_R)[1] & 1) != 0
else:
return None
@property
def mst_enabled(self) -> Union[bool, None]:
"""
Returns current state of MST (enabled or disabled).
None if MST does not support.
Returns:
object of bool or None type
"""
if self.__caps.mst:
return (self.__io.get(TSI_DPTX_MST_STATUS_R, c_uint32)[1] & 1) != 0
else:
return None
@property
def ssc_enabled(self) -> Union[bool, None]:
"""
Returns current state of SSC (enabled or disabled).
None if SSC does not support.
Returns:
object of bool or None type
"""
res = self.__io.get(TSI_DPTX_DOWNSPREAD_STATUS_R)[1]
if ((res >> 1) & 1) != 0:
return (res & 1) != 0
else:
return None
@property
def fec_enabled(self) -> Union[bool, None]:
"""
Returns current state of FEC (enabled or disabled).
None if FEC does not support.
Returns:
object of bool or None type
"""
if self.__caps.fec or self.__caps.fec2:
return (self.__io.get(TSI_DPTX_FEC_STATUS_R)[1] & 1) != 0
else:
return None
@property
def enhanced_framing(self) -> bool:
"""
Returns current state of enhanced framing (enabled or disabled).
Returns:
object of bool type
"""
return self.__read_lt_features().framing
@property
def scrambling_enabled(self) -> bool:
"""
Returns current state of scrambling (enabled or disabled).
Returns:
object of bool type
"""
return bool(self.__caps.scrambler_seed)
@property
def lttpr_active(self) -> Union[bool, None]:
"""
Returns current state of LTTPR (enabled or disabled).
None if LTTPR does not support.
Returns:
object of bool or None type
"""
if self.link_encoding == DpLinkEncoding.LE_128b132b:
return (self.__io.get(TSI_DPTX_LTTPR_STATUS_R, c_uint32)[1] & 1) != 0
else:
return None
@property
def ila(self) -> bool:
"""
Returns current ILA state.
Returns:
object of bool type
"""
return bool(self.__read_link_status().ila)
@property
def eq_ila(self) -> bool:
"""
Returns current EQ ILA state.
Returns:
object of bool type
"""
return bool(self.__read_link_status().eq_ila)
@property
def cds_ila(self) -> bool:
"""
Returns current CDS ILA state.
Returns:
object of bool type
"""
return bool(self.__read_link_status().cds_ila)
@property
def lt_fail(self) -> bool:
"""
Returns current LT fail state.
Returns:
object of bool type
"""
return bool(self.__read_link_status().cds_ila)
def stream(self, stream_index: int) -> StreamStatusDP:
"""
Returns status of selected stream `StreamStatusDP`.
Args:
stream_index (int) - number of selected number
Returns:
object of `StreamStatusDP` type
"""
stream_status = StreamStatusDP()
result, msa_info, size = self.__io.get(TSI_DPTX_MSA_INFO_R, DpMsa, 4)
if stream_index < size:
msa_info_private = MSAInfo(msa_info[stream_index])
stream_status.video_mode = msa_info_to_video_mode(msa_info_private, self.link_rate * 100000000,
self.link_encoding)
stream_status.mvid = msa_info_private.m_video
stream_status.nvid = msa_info_private.n_video
result = self.__io.get(TSI_DPTX_CRC_R, DpCrc, self.mst_stream_count)
if result[0] >= TSI_SUCCESS and self.mst_stream_count > 1:
stream_status.crc = [result[1][stream_index].r, result[1][stream_index].g, result[1][stream_index].b]
elif result[0] >= TSI_SUCCESS and self.mst_stream_count == 1:
stream_status.crc = [result[1].r, result[1].g, result[1].b]
result = self.__io.get(TSI_DPTX_DSC_CRC_R, DpCrc, self.mst_stream_count)
if result[0] >= TSI_SUCCESS and self.mst_stream_count > 1:
stream_status.dsc_crc = [result[1][stream_index].r, result[1][stream_index].g, result[1][stream_index].b]
elif result[0] >= TSI_SUCCESS and self.mst_stream_count == 1:
stream_status.dsc_crc = [result[1].r, result[1].g, result[1].b]
else:
raise ValueError(f"Selected stream {stream_index} is not available. "
f"Available stream number: {size}")
return stream_status
def vcp(self, stream_index: int = 0) -> Optional[VCPStatus]:
"""
Returns VCP status of selected stream `VCPStatus`.
Args:
stream_index (int) - number of selected number
Returns:
object of `VCPStatus` | None type
"""
if self.__caps.mst:
if not stream_index < self.mst_stream_count:
raise ValueError(f"Selected stream {stream_index} is not available. "
f"Available stream number: {self.mst_stream_count}")
res = self.__io.get(TSI_DPTX_VCP_TABLE_R, VCPTable, 4)[1]
if self.mst_enabled:
status = VCPStatus()
status.port_number = res[stream_index].port_number
status.stream_id = res[stream_index].stream_id
status.req_pbn = res[stream_index].req_pbn
status.alloc_pbn = res[stream_index].alloc_pbn
status.first_slot = res[stream_index].first_slot
status.slot_num = res[stream_index].slot_num
return status
else:
warnings.warn("MST not enabled. Cannot show VCP status.")
return VCPStatus()
def send_act(self):
"""
Send ACT command.
"""
self.__io.set(TSI_DPTX_MST_COMMAND_W, 1, c_uint32)
def __read_lt_features(self) -> LTFeatures:
return self.__io.get(TSI_DPTX_LT_FEATURES, LTFeatures)[1]
def __read_link_status(self) -> LinkTxStatus:
return self.__io.get(TSI_DPTX_LINK_STATUS_R, LinkTxStatus)[1]
def __check_custom_bit_rate(self) -> float:
res = self.__io.get(TSI_DP2TX_CUSTOM_RATE_MAP, c_uint32, 3)[1]
for i in range(3):
if res[i] != 0:
return res[i] * 200000 / 1000000000
return 0
def __str__(self) -> str:
lane_status_str = ""
stream_info_str = ""
vcp_table_str = ""
for i in range(self.mst_stream_count):
lane_status_str += f"Lane {i}\n{self.lane(i)}\n"
stream_info_str += f"Stream {i}\n{self.stream(i)}\n"
vcp_table_str += f"#{i}\n{self.vcp(i)}"
return f"Lane count: {self.lane_count}" \
f"Bit rate: {self.link_rate:.3} Gbps\n" \
f"Enhanced framing mode: {self.enhanced_framing}\n" \
f"MST: {self.mst_enabled}\n" \
f"DSC: {self.dsc_enabled}\n" \
f"LTTPR: {self.lttpr_active}\n" \
f"Link Encoding: {self.link_encoding.name}\n" \
f"Scrambling: {self.scrambling_enabled}\n" \
f"SSC: {self.ssc_enabled}\n" \
f"FEC: {self.fec_enabled}\n" \
f"ILA: {self.ila}\n" \
f"EQ_ILA: {self.eq_ila}\n" \
f"CDS_ILA: {self.cds_ila}\n" \
f"LT_FAIL: {self.lt_fail}\n" \
f"Lane status\n{lane_status_str}\n" \
f"Stream info:\n{stream_info_str}\n" \
f"VCP status:\n{vcp_table_str}\n"

View File

@@ -0,0 +1,329 @@
from typing import TypeVar
from enum import IntEnum
from .link_status_common import DpLinkEncoding
class DPLinkPattern(IntEnum):
"""
Class `DPLinkPattern` contains all possible variants of DP link patterns.
Names TrainingPattern1, TrainingPattern2, TrainingPattern3, TrainingPattern4 will be deleted in RC 3.7
"""
ActiveVideo = 0
IdlePattern = 1
TrainingPattern1 = 2
TrainingPattern2 = 3
TrainingPattern3 = 4
TrainingPattern4 = 5
PRBS7 = 6
HBR2 = 7
SER = 8
ForceVideo = 10
ForceIdle = 11
PRBS9 = 12
PRBS11 = 13
PRBS15 = 14
PRBS23 = 15
PRBS31 = 16
LinkSquarePattern = 17
Undefined = 18
TPS1 = TrainingPattern1
TSP2 = TrainingPattern2
TSP3 = TrainingPattern3
TSP4 = TrainingPattern4
class DP128b132bLinkPattern(IntEnum):
"""
Class `DP128b132bLinkPattern` contains all possible output patterns in DP2.0 force link mode.
"""
ActiveVideo = 0
IdlePattern = 1
TPS1 = 2
TPS2 = 3
PRBS9 = 4
PRBS11 = 5
PRBS15 = 6
PRBS23 = 7
PRBS31 = 8
Custom80bit = 9
LinkSquarePattern = 10
PRBS7 = 11
class DPOutLinkMode(IntEnum):
"""
Class `DPOutLinkMode` contains all possible out link modes.
"""
Normal = 0
Force8b10b = 1
Force128b132b = 2
class SSCConfig:
"""
Class `SSCConfig` contains information about SSC configuration parameters.
- Set and get amplitude `amplitude`.
- Set and get frequency `frequency`.
- Enable flag.
"""
def __init__(self):
self.enabled = False
self.amplitude = 0.5
self.frequency = 30000
def __eq__(self, other) -> bool:
return self.enabled == other.enabled and \
self.amplitude == other.amplitude and \
self.frequency == other.frequency
def __str__(self) -> str:
return f"Enabled - {self.enabled}\n" \
f"Amplitude - {self.amplitude}\n" \
f"Frequency - {self.frequency}\n"
class LinkConfig:
"""
Main class contains variants of link configuration:
- DP 1.4 config `DP8b10b`.
- DP 2.1 config `DP128b132b`.
"""
class DP8b10b:
"""
Class `DP8b10b` contains information of possible DP 1.4 configuration. Contains following field:
- lane count
- link bit rate
- MST
- MST stream count
- set enhanced framing mode
- Auto seed
- SSC
- FEC
- Force EDID timings after link training
- Adaptive-Sync auto enable
- Split SDP (if supported)
"""
def __init__(self):
self.lane_count = None
self.bit_rate = None
self.mst = None
self.mst_stream_count = None
self.enhanced_framing_mode = None
self.auto_seed = None
self.ssc = None
self.fec = None
self.force_edid_timings_after_lt = None
self.adaptive_sync_auto_enable = None
self.split_sdp = None
def __eq__(self, other) -> bool:
return self.lane_count == other.lane_count and \
self.bit_rate == other.bit_rate and \
self.mst == other.mst and \
self.mst_stream_count == other.mst_stream_count and \
self.enhanced_framing_mode == other.enhanced_framing_mode and \
self.auto_seed == other.auto_seed and \
self.ssc == other.ssc and \
self.fec == other.fec and \
self.force_edid_timings_after_lt == other.force_edid_timings_after_lt and \
self.adaptive_sync_auto_enable == other.adaptive_sync_auto_enable and \
self.split_sdp == other.split_sdp
def __str__(self) -> str:
return f"Lane Count - {self.lane_count}\n" \
f"Bit rate - {self.bit_rate:.3}\nMST enabled - {self.mst}\n" \
f"MST stream count - {self.mst_stream_count}\n" \
f"Enhanced framing mode - {bool(self.enhanced_framing_mode)}\n" \
f"Auto Seed - {bool(self.auto_seed)}\nSSC info:\n{self.ssc.__str__()}\nFEC: {self.fec}\n" \
f"Force EDID Timings after LT: {self.force_edid_timings_after_lt}\n" \
f"Adaptive Sync auto enable: {self.adaptive_sync_auto_enable}\n" \
f"Split SDP: {self.split_sdp}\n"
class DP128b132b:
"""
Class `DP128b132b` contains information of possible DP 2.1 configuration. Contains following field:
- lane count
- link bit rate
- Force DP 128/132
- Maximum link bandwidth supported
- Set enhanced framing mode
- Auto seed
- SSC
- LLTPR
- Try (enable/disable) DP 128/132
- Old DP2 link training
- Force EDID timings after link training
- Adaptive-Sync auto enable
- Split SDP (if supported)
- Force eDP rates
"""
def __init__(self):
self.lane_count = None
self.bit_rate = None
self.force_dp_128_132 = None
self.enhanced_framing_mode = None
self.max_link_bandwidth_supported = None
self.old_dp2_lt = None
self.lttpr = None
self.try_dp_128_132 = None
self.auto_seed = None
self.ssc = None
self.force_edid_timings_after_lt = None
self.adaptive_sync_auto_enable = None
self.mst = None
self.mst_stream_count = None
self.crc_16 = None
def __eq__(self, other) -> bool:
return self.lane_count == other.lane_count and \
self.bit_rate == other.bit_rate and \
self.force_dp_128_132 == other.force_dp_128_132 and \
self.max_link_bandwidth_supported == other.max_link_bandwidth_supported and \
self.old_dp2_lt == other.old_dp2_lt and \
self.lttpr == other.lttpr and \
self.try_dp_128_132 == other.try_dp_128_132 and \
self.enhanced_framing_mode == other.enhanced_framing_mode and \
self.auto_seed == other.auto_seed and \
self.ssc == other.ssc and \
self.force_edid_timings_after_lt == other.force_edid_timings_after_lt and \
self.adaptive_sync_auto_enable == other.adaptive_sync_auto_enable and \
self.mst == other.mst and \
self.mst_stream_count == other.mst_stream_count and \
self.crc_16 == other.crc_16
def __str__(self) -> str:
return f"Lane Count - {self.lane_count}\n" \
f"Bit rate - {self.bit_rate:.3}\nMST enabled - {self.mst}\n" \
f"MST stream count - {self.mst_stream_count}\n" \
f"Force DP 128b/132b enabled - {self.force_dp_128_132}\n" \
f"Max link bandwidth supported - {self.max_link_bandwidth_supported}\n" \
f"Enhanced framing mode - {self.enhanced_framing_mode}\n" \
f"Try DP 128b/132b enabled - {self.try_dp_128_132}\n"\
f"Old DP 2.1 Link training enabled - {self.old_dp2_lt}\n" \
f"LTTPR enabled - {self.lttpr}\n" \
f"Auto Seed - {self.auto_seed}\nSSC info - {self.ssc.__str__()}\n" \
f"Force EDID Timings after LT: {self.force_edid_timings_after_lt}\n" \
f"Adaptive Sync auto enable: {self.adaptive_sync_auto_enable}\n" \
f"Split SDP: {self.crc_16}\n"
class Force8b10b:
"""
"""
def __init__(self):
self.lane_count = None
self.bit_rate = None
self.pattern = None
def __str__(self) -> str:
return f"Lane Count - {self.lane_count}\n" \
f"Bit rate - {self.bit_rate:.3}\n" \
f"Link Pattern - {self.pattern.name}"
class Force128b132b:
"""
"""
def __init__(self):
self.lane_count = None
self.bit_rate = None
self.pattern = None
def __str__(self) -> str:
return f"Lane Count - {self.lane_count}\n" \
f"Bit rate - {self.bit_rate:.3}\n" \
f"Link Pattern - {self.pattern.name}"
class eDP:
"""
"""
def __init__(self):
self.lane_count = None
self.force_edp = None
self.eDp_cur_rate = None
self.eDp_supported_rates = None
self.eDp_aux_preamble = None
def __str__(self) -> str:
return f"Lane Count - {self.lane_count}\n" \
f"Force eDP: {bool(self.force_edp)}\n" \
f"eDP rate - {self.eDp_cur_rate}\n" \
f"eDP supported rates - {self.eDp_supported_rates}\n" \
f"eDP AUX preamble - {self.eDp_aux_preamble}"
def __eq__(self, other) -> bool:
return self.lane_count == other.lane_count and \
self.force_edp == other.force_edp and \
self.eDp_cur_rate == other.eDp_cur_rate and \
self.eDp_aux_preamble == other.eDp_aux_preamble
class LinkStatus:
"""
Main class describes current link status. Contains following field:
- Lane count
- BIt rate
- State of enhanced framing
- State of MST mode
- State of DSC mode
- LTTPR
- Link encoding `DpLinkEncoding`
- State of scrambling
- State of SSC
- State of FEC
- State of eDP
"""
def __init__(self):
self.lane_count = 0
self.bit_rate = 0
self.enhanced_framing = False
self.mst_enabled = False
self.dsc_enabled = False
self.lttpr = False
self.link_encoding = DpLinkEncoding.LE_NONE
self.scrambling_enabled = False
self.ssc_enabled = False
self.fec_enabled = False
self.force_edp_enabled = False
def __eq__(self, other) -> bool:
return self.lane_count == other.lane_count and \
self.bit_rate == other.bit_rate and \
self.enhanced_framing == other.enhanced_framing and \
self.mst_enabled == other.mst_enabled and \
self.dsc_enabled == other.dsc_enabled and \
self.lttpr == other.lttpr and \
self.link_encoding == other.link_encoding and \
self.scrambling_enabled == other.scrambling_enabled and \
self.ssc_enabled == other.ssc_enabled and \
self.fec_enabled == other.fec_enabled and \
self.force_edp_enabled == other.force_edp_enabled
def __str__(self) -> str:
return f"Lane Count - {self.lane_count}\n" \
f"Bit rate - {self.bit_rate:.3}\n" \
f"Enhanced framing - {self.enhanced_framing}\n" \
f"MST enabled - {self.mst_enabled}\n" \
f"DSC enabled - {self.dsc_enabled}\n" \
f"LTTPR - {self.lttpr}\n" \
f"Link Encoding - {self.link_encoding.name}\n" \
f"Scrambling enabled - {self.link_encoding.name}\n" \
f"SSC enabled - {self.ssc_enabled}\n" \
f"FEC enabled - {self.fec_enabled}\n" \
f"Force eDP enabled: {self.force_edp_enabled}"
# class LinkOverrides:
#
# def __init__(self):
# self.override_voltage = 0
# self.override_pre_emp = 0
# self.override_ffe_presets = [0] * 4
DisplayPortLinkConfig = TypeVar("DisplayPortLinkConfig",
LinkConfig.DP8b10b,
LinkConfig.DP128b132b,
LinkConfig.eDP,
LinkConfig.Force8b10b,
LinkConfig.Force128b132b)

View File

@@ -0,0 +1,122 @@
from ctypes import Structure, c_uint32, c_uint8, c_uint16
from enum import IntEnum
from .link_rx_types import CableCapabilitiesEnum
class DPRXHWCaps(Structure):
_fields_ = [
('mst', c_uint32, 1),
('hdcp_1_x', c_uint32, 1),
('hdcp_2_x', c_uint32, 1),
('fec', c_uint32, 1),
('dsc', c_uint32, 1),
('alpm', c_uint32, 1),
('lanecount3', c_uint32, 1),
('edp', c_uint32, 1),
('mst_stream_count', c_uint32, 4),
('pr', c_uint32, 1),
('psr', c_uint32, 1),
('', c_uint32, 1),
('display_id_capability', c_uint32, 1),
('max_link_rate', c_uint32, 8),
('force_link_congiguration', c_uint32, 1),
('cirdan', c_uint32, 1),
('dp_power', c_uint32, 1),
('aux_swing', c_uint32, 1),
('dp2_custom_rates', c_uint32, 1),
('custom_rate_ex', c_uint32, 1),
('fec2', c_uint32, 1),
('dsc2', c_uint32, 1),
('dp2_support_rates', c_uint32, 8),
('', c_uint32, 24),
('link_pat_log', c_uint32, 1),
('vbid_hw_log', c_uint32, 1),
('msa_hw_log', c_uint32, 1),
('aux_bw_log', c_uint32, 1),
('', c_uint32, 28),
('scrambler_seed', c_uint32, 1),
('sdp_err_counters', c_uint32, 1),
('sink_cnt_config', c_uint32, 1),
('', c_uint32, 29)
]
class DPRXLinkControl(Structure):
_fields_ = [
('tps4_type', c_uint32, 1),
('alpm_aux_wake', c_uint32, 1),
('old_dp2_lt', c_uint32, 1),
('edp_aux_preamble', c_uint32, 1)
]
def value(self) -> int:
return self.tps4_type << 0 | self.alpm_aux_wake << 1 | self.old_dp2_lt << 2 | self.edp_aux_preamble << 3
class CableTypeEnum(IntEnum):
Unknown = 0
Passive = 1
LRD = 2
Retimer = 3
class PortCableAttributesEnum(IntEnum):
DP_CABLE_ATTR_UHBR_10_20_CAPABILITY_POS = 0
DP_CABLE_ATTR_UHBR_10_20_CAPABILITY_MASK = 0x03 << DP_CABLE_ATTR_UHBR_10_20_CAPABILITY_POS
DP_CABLE_ATTR_UHBR_10_20_NOT_CAPABLE = 0x00 << DP_CABLE_ATTR_UHBR_10_20_CAPABILITY_POS
DP_CABLE_ATTR_UHBR_10_CAPABLE = 0x01 << DP_CABLE_ATTR_UHBR_10_20_CAPABILITY_POS
DP_CABLE_ATTR_UHBR_10_20_CAPABLE = 0x02 << DP_CABLE_ATTR_UHBR_10_20_CAPABILITY_POS
DP_CABLE_ATTR_UHBR_13_5_CAPABILITY_POS = 2
DP_CABLE_ATTR_UHBR_13_5_CAPABLE = 0x01 << DP_CABLE_ATTR_UHBR_13_5_CAPABILITY_POS
DP_CABLE_ATTR_CABLE_TYPE_POS = 3
DP_CABLE_ATTR_CABLE_TYPE_MASK = 0x7 << DP_CABLE_ATTR_CABLE_TYPE_POS
class PortCableAttributesStruct(Structure):
_fields_ = [
('data', c_uint8)
]
def isUHBR20Supported(self) -> bool:
return (self.data & PortCableAttributesEnum.DP_CABLE_ATTR_UHBR_10_20_CAPABILITY_MASK) == PortCableAttributesEnum.DP_CABLE_ATTR_UHBR_10_20_CAPABLE
def isUHBR13_5Supported(self) -> bool:
return (self.data & PortCableAttributesEnum.DP_CABLE_ATTR_UHBR_13_5_CAPABLE) != 0
def isUHBR10Supported(self) -> bool:
return ((self.data & PortCableAttributesEnum.DP_CABLE_ATTR_UHBR_10_20_CAPABILITY_MASK) == PortCableAttributesEnum.DP_CABLE_ATTR_UHBR_10_CAPABLE) or self.isUHBR20Supported()
def get_cable_caps(self) -> CableCapabilitiesEnum:
if self.isUHBR20Supported():
return CableCapabilitiesEnum.DP80
elif self.isUHBR13_5Supported():
return CableCapabilitiesEnum.DP54
elif self.isUHBR10Supported():
return CableCapabilitiesEnum.DP40
else:
return CableCapabilitiesEnum.Unknown
def get_cable_type(self) -> CableTypeEnum:
return CableTypeEnum(self.data & PortCableAttributesEnum.DP_CABLE_ATTR_CABLE_TYPE_MASK >> PortCableAttributesEnum.DP_CABLE_ATTR_CABLE_TYPE_POS)
class CableAttributesStruct(Structure):
_fields_ = [
('currentPort', c_uint8),
('anotherPort', c_uint8),
('reserved', c_uint16)
]
class RoutedLTStatusPrivate(Structure):
_fields_ = [
('enabled', c_uint32, 1),
('dp2OldLt', c_uint32, 1),
('state', c_uint32, 2),
('success', c_uint32, 1),
('', c_uint32, 11),
('step', c_uint32, 9),
('', c_uint32, 7)
]

View File

@@ -0,0 +1,157 @@
from ctypes import *
from .dp_utils import *
from .link_status_common import DpLinkEncoding
from UniTAP.common.video_mode import VideoMode
class DpMsa(Structure):
class PixelAttributes(Structure):
_fields_ = [
("n_video_or_freq_low", c_uint32, 24),
("", c_uint32, 8),
("m_video_or_freq_high", c_uint32, 24),
("", c_uint32, 8),
]
_fields_ = [
('attributes', PixelAttributes),
('h_total', c_uint16),
('v_total', c_uint16),
('h_active', c_uint16),
('v_active', c_uint16),
('h_sync', c_int16),
('v_sync', c_int16),
('h_sync_start', c_uint16),
('v_sync_start', c_uint16),
('misc0', c_uint8),
('misc1', c_uint8),
('vbid', c_uint8),
('port_number', c_uint8),
('vsc_sdp_db16', c_uint8),
('vsc_sdp_db17', c_uint8),
('vsc_sdp_db18', c_uint8),
('', c_uint8),
('', c_uint32 * 8)
]
class DpCrc(Structure):
_fields_ = [
('r', c_uint16),
('g', c_uint16),
('b', c_uint16),
('', c_uint16)
]
class VCPTable(Structure):
_fields_ = [
('port_number', c_int),
('stream_id', c_int),
('req_pbn', c_int),
('alloc_pbn', c_int),
('first_slot', c_int),
('slot_num', c_int),
('reserved1', c_int),
('reserved2', c_int),
]
class LinkVoltageSwing(Structure):
_fields_ = [
('vs_l0', c_uint32, 2),
('', c_uint32, 6),
('vs_l1', c_uint32, 2),
('', c_uint32, 6),
('vs_l2', c_uint32, 2),
('', c_uint32, 1),
('vs_l3', c_uint32, 2),
('', c_uint32, 6),
]
class LinkPreEmphasis(Structure):
_fields_ = [
('pe_l0', c_uint32, 2),
('', c_uint32, 6),
('pe_l1', c_uint32, 2),
('', c_uint32, 6),
('pe_l2', c_uint32, 2),
('', c_uint32, 1),
('pe_l3', c_uint32, 2),
('', c_uint32, 6),
]
class MSAInfo:
def __init__(self, dp_msa: DpMsa):
self.n_video = dp_msa.attributes.n_video_or_freq_low
self.m_video = dp_msa.attributes.m_video_or_freq_high
self.video_frequency = (dp_msa.attributes.m_video_or_freq_high << 24) | dp_msa.attributes.n_video_or_freq_low
self.h_total = dp_msa.h_total
self.v_total = dp_msa.v_total
self.h_active = dp_msa.h_active
self.v_active = dp_msa.v_active
self.h_sync = dp_msa.h_sync
self.v_sync = dp_msa.v_sync
self.h_sync_start = dp_msa.h_sync_start
self.v_sync_start = dp_msa.v_sync_start
self.misc0 = dp_msa.misc0
self.misc1 = dp_msa.misc1
self.vbid = dp_msa.vbid
self.port_number = dp_msa.port_number
self.vsc_sdp_db16 = dp_msa.vsc_sdp_db16
self.vsc_sdp_db17 = dp_msa.vsc_sdp_db17
self.vsc_sdp_db18 = dp_msa.vsc_sdp_db18
def is_eq(self, other, enc128b132b: bool) -> bool:
if enc128b132b:
if self.video_frequency != other.video_frequency:
return False
else:
# m_video changes frequently
if self.n_video != other.n_video:
return False
return self.h_total == other.h_total and self.v_total == other.v_total and self.h_active == other.h_active and \
self.v_active == other.v_active and self.h_sync == other.h_sync and self.v_sync == other.v_sync and \
self.h_sync_start == other.h_sync_start and self.v_sync_start == other.v_sync_start and \
self.misc0 == other.misc0 and self.misc1 == other.misc1 and self.vbid == other.vbid and \
self.vsc_sdp_db16 == self.vsc_sdp_db16 and self.vsc_sdp_db17 == self.vsc_sdp_db17 and \
self.vsc_sdp_db18 == self.vsc_sdp_db18
def msa_info_to_video_mode(msa_info: MSAInfo, f_ls_clock: float, link_encoding: DpLinkEncoding):
video_mode = VideoMode()
video_mode.timing.hactive = msa_info.h_active
video_mode.timing.vactive = msa_info.v_active
video_mode.timing.htotal = msa_info.h_total
video_mode.timing.vtotal = msa_info.v_total
video_mode.timing.hstart = msa_info.h_sync_start
video_mode.timing.vstart = msa_info.v_sync_start
video_mode.timing.hswidth = msa_info.h_sync
video_mode.timing.vswidth = msa_info.v_sync
video_mode.color_info.colorimetry = get_vm_colorimetry(msa_info.vsc_sdp_db16)
video_mode.color_info.color_format = get_vm_color_format(msa_info.vsc_sdp_db16)
video_mode.color_info.bpc = get_vm_bpc(msa_info.vsc_sdp_db16, msa_info.vsc_sdp_db17)
video_mode.color_info.dynamic_range = get_vm_dynamic_range(msa_info.vsc_sdp_db17)
frame_rate = 0
if (msa_info.v_total * msa_info.h_total) > 0:
if link_encoding == DpLinkEncoding.LE_8b10b and msa_info.n_video > 0:
f_strm_clock = f_ls_clock * (msa_info.m_video / msa_info.n_video)
frame_rate = 1000 * f_strm_clock / (msa_info.v_total * msa_info.h_total)
elif link_encoding == DpLinkEncoding.LE_128b132b:
frame_rate = 1000 * msa_info.video_frequency / (msa_info.v_total * msa_info.h_total)
else:
pass
video_mode.timing.frame_rate = round(frame_rate)
return video_mode

View File

@@ -0,0 +1,107 @@
from ctypes import Structure, c_uint32
class DPTXHWCaps(Structure):
_fields_ = [
('mst', c_uint32, 1),
('hdcp_1_x', c_uint32, 1),
('hdcp_2_x', c_uint32, 1),
('fec', c_uint32, 1),
('dsc', c_uint32, 1),
('alpm', c_uint32, 1),
('lane_3', c_uint32, 1),
('edp', c_uint32, 1),
('mst_stream_count', c_uint32, 4),
('', c_uint32, 4),
('max_link_rate', c_uint32, 8),
('', c_uint32, 1),
('dp2_custom_rates', c_uint32, 1),
('custom_rates_ext', c_uint32, 1),
('adaptive_sync', c_uint32, 1),
('edid_parser', c_uint32, 1),
('', c_uint32, 1),
('fec2', c_uint32, 1),
('dsc2', c_uint32, 1),
('support_10gbps', c_uint32, 1),
('support_20gbps', c_uint32, 1),
('support_13_5gbps', c_uint32, 1),
('', c_uint32, 29),
('link_pat_log', c_uint32, 1),
('vbid_hw_log', c_uint32, 1),
('msa_hw_log', c_uint32, 1),
('aux_bw_log', c_uint32, 1),
('', c_uint32, 28),
('scrambler_seed', c_uint32, 1),
('dp2_sdp_crc', c_uint32, 1),
('sdp_split', c_uint32, 1),
('dp2_sdp_split', c_uint32, 1),
('', c_uint32, 28),
('supported_dp2_custom_rates', c_uint32 * 32),
]
class LTFeatures(Structure):
_fields_ = [
('skew', c_uint32, 1),
('', c_uint32, 1),
('framing', c_uint32, 1),
('try_3_lane', c_uint32, 1),
('try_edp', c_uint32, 1),
('force_edp', c_uint32, 1),
('try_dp2', c_uint32, 1),
('force_dp2', c_uint32, 1),
('old_dp2_lt', c_uint32, 1),
('auto_seed', c_uint32, 1),
('max_link_bw_policy', c_uint32, 1),
('edp_aux_preamble', c_uint32, 1)
]
def value(self) -> int:
return self.framing << 2 | self.try_edp << 4 | self.force_edp << 5 | self.try_dp2 << 6 | self.force_dp2 << 7 |\
self.old_dp2_lt << 8 | self.auto_seed << 9 | self.max_link_bw_policy << 10 | self.edp_aux_preamble << 11
class LinkTxStatus(Structure):
_fields_ = [
('l0_cr', c_uint32, 1),
('l0_eq', c_uint32, 1),
('l0_sl', c_uint32, 1),
('', c_uint32, 1),
('l1_cr', c_uint32, 1),
('l1_eq', c_uint32, 1),
('l1_sl', c_uint32, 1),
('', c_uint32, 1),
('l2_cr', c_uint32, 1),
('l2_eq', c_uint32, 1),
('l2_sl', c_uint32, 1),
('', c_uint32, 1),
('l3_cr', c_uint32, 1),
('l3_eq', c_uint32, 1),
('l3_sl', c_uint32, 1),
('', c_uint32, 13),
('lt_fail', c_uint32, 1),
('cds_ila', c_uint32, 1),
('eq_ila', c_uint32, 1),
('ila', c_uint32, 1)
]
class PostLtFeatures(Structure):
_fields_ = [
('force_edid_timings', c_uint32, 1),
('as_auto_enable', c_uint32, 1)
]
def value(self) -> int:
return self.force_edid_timings | self.as_auto_enable << 1
class SdpControl(Structure):
_fields_ = [
('dp2_crc_sdp', c_uint32, 1),
('dp_split_sdp', c_uint32, 1),
('dp2_split_sdp', c_uint32, 1)
]
def value(self) -> int:
return self.dp2_crc_sdp | self.dp_split_sdp << 1