1.1.0版本
This commit is contained in:
2
UniTAP/dev/ports/modules/link/__init__.py
Normal file
2
UniTAP/dev/ports/modules/link/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
from .dp import *
|
||||
from .hdmi import *
|
||||
8
UniTAP/dev/ports/modules/link/dp/__init__.py
Normal file
8
UniTAP/dev/ports/modules/link/dp/__init__.py
Normal 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
|
||||
19
UniTAP/dev/ports/modules/link/dp/dp_cable_info.py
Normal file
19
UniTAP/dev/ports/modules/link/dp/dp_cable_info.py
Normal 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()
|
||||
137
UniTAP/dev/ports/modules/link/dp/dp_utils.py
Normal file
137
UniTAP/dev/ports/modules/link/dp/dp_utils.py
Normal 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
|
||||
121
UniTAP/dev/ports/modules/link/dp/link_rx.py
Normal file
121
UniTAP/dev/ports/modules/link/dp/link_rx.py
Normal 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()
|
||||
218
UniTAP/dev/ports/modules/link/dp/link_rx_aux_controller.py
Normal file
218
UniTAP/dev/ports/modules/link/dp/link_rx_aux_controller.py
Normal 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)
|
||||
332
UniTAP/dev/ports/modules/link/dp/link_rx_caps.py
Normal file
332
UniTAP/dev/ports/modules/link/dp/link_rx_caps.py
Normal 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
|
||||
407
UniTAP/dev/ports/modules/link/dp/link_rx_status.py
Normal file
407
UniTAP/dev/ports/modules/link/dp/link_rx_status.py
Normal 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"
|
||||
177
UniTAP/dev/ports/modules/link/dp/link_rx_types.py
Normal file
177
UniTAP/dev/ports/modules/link/dp/link_rx_types.py
Normal 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}")
|
||||
|
||||
|
||||
173
UniTAP/dev/ports/modules/link/dp/link_status_common.py
Normal file
173
UniTAP/dev/ports/modules/link/dp/link_status_common.py
Normal 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}
|
||||
279
UniTAP/dev/ports/modules/link/dp/link_tx.py
Normal file
279
UniTAP/dev/ports/modules/link/dp/link_tx.py
Normal 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
|
||||
360
UniTAP/dev/ports/modules/link/dp/link_tx_config.py
Normal file
360
UniTAP/dev/ports/modules/link/dp/link_tx_config.py
Normal 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
|
||||
86
UniTAP/dev/ports/modules/link/dp/link_tx_force_config.py
Normal file
86
UniTAP/dev/ports/modules/link/dp/link_tx_force_config.py
Normal 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])
|
||||
432
UniTAP/dev/ports/modules/link/dp/link_tx_status.py
Normal file
432
UniTAP/dev/ports/modules/link/dp/link_tx_status.py
Normal 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"
|
||||
329
UniTAP/dev/ports/modules/link/dp/link_tx_types.py
Normal file
329
UniTAP/dev/ports/modules/link/dp/link_tx_types.py
Normal 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)
|
||||
122
UniTAP/dev/ports/modules/link/dp/private_link_rx_types.py
Normal file
122
UniTAP/dev/ports/modules/link/dp/private_link_rx_types.py
Normal 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)
|
||||
]
|
||||
157
UniTAP/dev/ports/modules/link/dp/private_link_status_common.py
Normal file
157
UniTAP/dev/ports/modules/link/dp/private_link_status_common.py
Normal 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
|
||||
107
UniTAP/dev/ports/modules/link/dp/private_link_tx_types.py
Normal file
107
UniTAP/dev/ports/modules/link/dp/private_link_tx_types.py
Normal 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
|
||||
1
UniTAP/dev/ports/modules/link/hdmi/__init__.py
Normal file
1
UniTAP/dev/ports/modules/link/hdmi/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .types import FrlMode, FrlCaps, ClockRate, HdmiModeTx, HdmiModeRx, LinkMode
|
||||
134
UniTAP/dev/ports/modules/link/hdmi/arc_rx.py
Normal file
134
UniTAP/dev/ports/modules/link/hdmi/arc_rx.py
Normal file
@@ -0,0 +1,134 @@
|
||||
from UniTAP.libs.lib_tsi.tsi_io import PortIO
|
||||
from enum import IntEnum
|
||||
from UniTAP.libs.lib_tsi.tsi_types import TSI_ARC_CONTROL_W, TSI_HDRX_ARC_STATUS_R
|
||||
from ctypes import c_uint32
|
||||
|
||||
|
||||
class ArcLoopbackAudioSource(IntEnum):
|
||||
"""
|
||||
Class `ArcLoopbackAudioSource` contains all possible variants of ARC loopback audio source type.
|
||||
"""
|
||||
Unknown = -1
|
||||
TPG = 0
|
||||
HDMI = 1
|
||||
DVI = 2
|
||||
DP = 3
|
||||
SPDIF = 4
|
||||
|
||||
|
||||
class ArcRx:
|
||||
"""
|
||||
Class `ArcRx` contains information about caps and states of Audio return channel.
|
||||
- Support ARC `supported`.
|
||||
- Loopback support TGP `loopback_supported_tpg`.
|
||||
- Loopback support HDMI `loopback_supported_hdmi`.
|
||||
- Loopback support DVI `loopback_supported_dvi`.
|
||||
- Loopback support DP `loopback_supported_dp`.
|
||||
- Loopback support SPDIF `loopback_supported_spdif`.
|
||||
- Current state (enabled/disabled) - `enabled`.
|
||||
- Control of ARC Source `arc_source`.
|
||||
- Control of single mode `single_mode`.
|
||||
|
||||
"""
|
||||
def __init__(self, port_io: PortIO):
|
||||
self.__io = port_io
|
||||
self.__supported = False
|
||||
self.__loopback_supported_tpg = False
|
||||
self.__loopback_supported_hdmi = False
|
||||
self.__loopback_supported_dvi = False
|
||||
self.__loopback_supported_dp = False
|
||||
self.__loopback_supported_spdif = False
|
||||
self.__enabled = False
|
||||
self.__arc_source = ArcLoopbackAudioSource.HDMI
|
||||
self.__arc_source_value = 2
|
||||
self.__single_mode = True
|
||||
|
||||
def __read_arc_status(self) -> int:
|
||||
return self.__io.get(TSI_HDRX_ARC_STATUS_R, c_uint32)[1]
|
||||
|
||||
def __write_arc(self, value: int):
|
||||
self.__io.set(TSI_ARC_CONTROL_W, value, c_uint32)
|
||||
|
||||
@property
|
||||
def supported(self) -> bool:
|
||||
self.__supported = (self.__read_arc_status() & 0x1) != 0
|
||||
return self.__supported
|
||||
|
||||
@property
|
||||
def loopback_supported_tpg(self) -> bool:
|
||||
self.__loopback_supported_tpg = ((self.__read_arc_status() >> 1) & 0x1) != 0
|
||||
return self.__loopback_supported_tpg
|
||||
|
||||
@property
|
||||
def loopback_supported_hdmi(self) -> bool:
|
||||
self.__loopback_supported_hdmi = ((self.__read_arc_status() >> 2) & 0x1) != 0
|
||||
return self.__loopback_supported_hdmi
|
||||
|
||||
@property
|
||||
def loopback_supported_dvi(self) -> bool:
|
||||
self.__loopback_supported_dvi = ((self.__read_arc_status() >> 8) & 0x1) != 0
|
||||
return self.__loopback_supported_dvi
|
||||
|
||||
@property
|
||||
def loopback_supported_dp(self) -> bool:
|
||||
self.__loopback_supported_dp = ((self.__read_arc_status() >> 9) & 0x1) != 0
|
||||
return self.__loopback_supported_dp
|
||||
|
||||
@property
|
||||
def loopback_supported_spdif(self) -> bool:
|
||||
self.__loopback_supported_spdif = ((self.__read_arc_status() >> 10) & 0x1) != 0
|
||||
return self.__loopback_supported_spdif
|
||||
|
||||
@property
|
||||
def enabled(self) -> bool:
|
||||
self.__enabled = ((self.__read_arc_status() >> 31) & 0x1) != 0
|
||||
return self.__enabled
|
||||
|
||||
@property
|
||||
def arc_source(self) -> ArcLoopbackAudioSource:
|
||||
return self.__arc_source
|
||||
|
||||
@property
|
||||
def single_mode(self) -> bool:
|
||||
return self.__single_mode
|
||||
|
||||
@single_mode.setter
|
||||
def single_mode(self, single_mode: bool):
|
||||
value = self.__arc_source_value | ((1 << 16) if single_mode else 0)
|
||||
self.__write_arc(value)
|
||||
self.__single_mode = single_mode
|
||||
|
||||
@arc_source.setter
|
||||
def arc_source(self, arc_source: ArcLoopbackAudioSource):
|
||||
|
||||
self.__arc_source_value = 0
|
||||
|
||||
if arc_source == ArcLoopbackAudioSource.HDMI:
|
||||
self.__arc_source_value |= 2
|
||||
self.__arc_source_value |= (0 << 8)
|
||||
elif arc_source == ArcLoopbackAudioSource.TPG:
|
||||
self.__arc_source_value |= 1
|
||||
elif arc_source == ArcLoopbackAudioSource.DVI:
|
||||
self.__arc_source_value |= 2
|
||||
self.__arc_source_value |= (1 << 8)
|
||||
elif arc_source == ArcLoopbackAudioSource.DP:
|
||||
self.__arc_source_value |= 2
|
||||
self.__arc_source_value |= (2 << 8)
|
||||
elif arc_source == ArcLoopbackAudioSource.SPDIF:
|
||||
self.__arc_source_value |= 2
|
||||
self.__arc_source_value |= (3 << 8)
|
||||
|
||||
value = self.__arc_source_value | ((1 << 16) if self.__single_mode else 0)
|
||||
self.__write_arc(value)
|
||||
self.__arc_source = arc_source
|
||||
|
||||
def __str__(self):
|
||||
return f"Audio return channel supported: {self.supported}\n" \
|
||||
f"Loopback supported TPG: {self.loopback_supported_tpg}\n" \
|
||||
f"Loopback supported HDMI: {self.loopback_supported_hdmi}\n" \
|
||||
f"Loopback supported DVI: {self.loopback_supported_dvi}\n" \
|
||||
f"Loopback supported DP: {self.loopback_supported_dp}\n" \
|
||||
f"Loopback supported SPDIF: {self.loopback_supported_spdif}\n" \
|
||||
f"Enabled: {self.enabled}\n" \
|
||||
f"ARC Source: {self.arc_source.name}\n" \
|
||||
f"Single mode: {self.single_mode}\n"
|
||||
45
UniTAP/dev/ports/modules/link/hdmi/capabilities.py
Normal file
45
UniTAP/dev/ports/modules/link/hdmi/capabilities.py
Normal file
@@ -0,0 +1,45 @@
|
||||
from UniTAP.libs.lib_tsi.tsi_io import PortIO
|
||||
from ctypes import c_uint32
|
||||
|
||||
|
||||
class HdmiCapabilities:
|
||||
|
||||
def __init__(self, ci_control: int, port_io: PortIO):
|
||||
self.__io = port_io
|
||||
|
||||
caps = self.__io.get(ci_control, c_uint32)[1]
|
||||
self.__cap_tmds = caps & 0x1
|
||||
self.__cap_frl = (caps >> 1) & 0x1
|
||||
self.__cap_arc = (caps >> 2) & 0x1
|
||||
self.__cap_hdcp = (caps >> 3) & 0x1
|
||||
self.__cap_behavior_14 = (caps >> 4) & 0x1
|
||||
self.__cap_behavior_20 = (caps >> 5) & 0x1
|
||||
self.__cap_behavior_21 = (caps >> 6) & 0x1
|
||||
|
||||
@property
|
||||
def support_tmds(self) -> bool:
|
||||
return self.__cap_tmds
|
||||
|
||||
@property
|
||||
def support_frl(self) -> bool:
|
||||
return self.__cap_tmds
|
||||
|
||||
@property
|
||||
def support_arc(self) -> bool:
|
||||
return self.__cap_tmds
|
||||
|
||||
@property
|
||||
def support_hdcp(self) -> bool:
|
||||
return self.__cap_tmds
|
||||
|
||||
@property
|
||||
def support_hdmi_mode_14(self) -> bool:
|
||||
return self.__cap_tmds
|
||||
|
||||
@property
|
||||
def support_hdmi_mode_20(self) -> bool:
|
||||
return self.__cap_tmds
|
||||
|
||||
@property
|
||||
def support_hdmi_mode_21(self) -> bool:
|
||||
return self.__cap_tmds
|
||||
139
UniTAP/dev/ports/modules/link/hdmi/frl_caps_rx.py
Normal file
139
UniTAP/dev/ports/modules/link/hdmi/frl_caps_rx.py
Normal file
@@ -0,0 +1,139 @@
|
||||
from UniTAP.libs.lib_tsi.tsi_io import PortIO
|
||||
from .types import FrlMode, LtpLanesPattern, FrlCaps, LtpPattern, _update_frl_values, _update_ltp_pattern_values
|
||||
from UniTAP.libs.lib_tsi.tsi_types import TSI_HDRX_FRL_CAPABILITY, TSI_HDRX_FRL_PATTERN, TSI_HDRX_LINK_STATUS_R
|
||||
from ctypes import c_uint32
|
||||
|
||||
|
||||
class FrlControlRx:
|
||||
"""
|
||||
Class `FrlControlRx` contains information about FRL on Sink (RX - receiver) side.
|
||||
allows working with:
|
||||
- Set and get FRL mode `frl_mode`.
|
||||
- Set and get FRL capabilities `frl_caps`.
|
||||
- Set and get LTP requested `ltp_request` and additional `ltp_additional`.
|
||||
"""
|
||||
def __init__(self, port_io: PortIO):
|
||||
self.__io = port_io
|
||||
self.__frl_caps = FrlCaps()
|
||||
self.__ltp_request = LtpLanesPattern()
|
||||
self.__ltp_additional = LtpLanesPattern()
|
||||
|
||||
def __read_caps(self) -> int:
|
||||
return self.__io.get(TSI_HDRX_FRL_CAPABILITY, c_uint32)[1]
|
||||
|
||||
def __write_caps(self, value: int):
|
||||
self.__io.set(TSI_HDRX_FRL_CAPABILITY, value, c_uint32)
|
||||
|
||||
def __read_frl_pattern(self) -> int:
|
||||
return self.__io.get(TSI_HDRX_FRL_PATTERN, c_uint32)[1]
|
||||
|
||||
def __write_frl_pattern(self, value: int):
|
||||
self.__io.set(TSI_HDRX_FRL_PATTERN, value, c_uint32)
|
||||
|
||||
@property
|
||||
def frl_mode(self) -> FrlMode:
|
||||
"""
|
||||
Returns current FRL mode.
|
||||
|
||||
Returns:
|
||||
object of `FrlMode` type
|
||||
"""
|
||||
return FrlMode(self.__read_caps() & 0xF)
|
||||
|
||||
@frl_mode.setter
|
||||
def frl_mode(self, frl_mode: FrlMode):
|
||||
"""
|
||||
Set new FRL mode.
|
||||
|
||||
Args:
|
||||
frl_mode (`FrlMode`)
|
||||
"""
|
||||
new_value = frl_mode.value
|
||||
new_value |= self.__read_caps() & ~0xF
|
||||
self.__write_caps(new_value)
|
||||
|
||||
@property
|
||||
def frl_caps(self) -> FrlCaps:
|
||||
"""
|
||||
Returns current FRL capabilities.
|
||||
|
||||
Returns:
|
||||
object of `FrlCaps` type
|
||||
"""
|
||||
_update_frl_values(self.__frl_caps, self.__read_caps())
|
||||
return self.__frl_caps
|
||||
|
||||
@frl_caps.setter
|
||||
def frl_caps(self, frl_caps: FrlCaps):
|
||||
"""
|
||||
Set new FRL capabilities.
|
||||
|
||||
Args:
|
||||
frl_caps (`FrlCaps`)
|
||||
"""
|
||||
new_value = frl_caps.value()
|
||||
new_value |= self.__read_caps() & ~0xF
|
||||
self.__write_caps(new_value)
|
||||
self.__frl_caps = frl_caps
|
||||
|
||||
@property
|
||||
def ltp_request(self) -> LtpLanesPattern:
|
||||
"""
|
||||
Returns current LTP lanes pattern. Current Pattern that is being checking during Link Training.
|
||||
Each lane can request different pattern.
|
||||
|
||||
Returns:
|
||||
object of `LtpLanesPattern` type
|
||||
"""
|
||||
_update_ltp_pattern_values(self.__ltp_request, self.__read_frl_pattern())
|
||||
return self.__ltp_request
|
||||
|
||||
@ltp_request.setter
|
||||
def ltp_request(self, ltp_request: LtpLanesPattern):
|
||||
"""
|
||||
Set new LTP lanes pattern.
|
||||
|
||||
Args:
|
||||
ltp_request (`LtpLanesPattern`)
|
||||
"""
|
||||
new_value = ltp_request.value()
|
||||
new_value |= self.__read_frl_pattern()
|
||||
self.__write_frl_pattern(new_value)
|
||||
self.__ltp_request = ltp_request
|
||||
|
||||
@property
|
||||
def ltp_additional(self) -> LtpLanesPattern:
|
||||
"""
|
||||
Returns current additional LTP lanes pattern. Current Pattern that is being checking during Link Training.
|
||||
Each lane can request different pattern.
|
||||
|
||||
Returns:
|
||||
object of `LtpLanesPattern` type
|
||||
"""
|
||||
_update_ltp_pattern_values(self.__ltp_additional, self.__read_frl_pattern() >> 0xFF)
|
||||
return self.__ltp_additional
|
||||
|
||||
@ltp_additional.setter
|
||||
def ltp_additional(self, ltp_request: LtpLanesPattern):
|
||||
"""
|
||||
Set new additional LTP lanes pattern.
|
||||
|
||||
Args:
|
||||
ltp_request (`LtpLanesPattern`)
|
||||
"""
|
||||
new_value = ltp_request.value() << 0xFF
|
||||
new_value |= self.__read_frl_pattern()
|
||||
self.__write_frl_pattern(new_value)
|
||||
self.__ltp_additional = ltp_request
|
||||
|
||||
def re_train(self):
|
||||
"""
|
||||
DO re train.
|
||||
"""
|
||||
self.__io.set(TSI_HDRX_LINK_STATUS_R, 0x1c0000, c_uint32)
|
||||
|
||||
def __str__(self):
|
||||
return f"FRL Mode: {self.frl_mode.name}\n" \
|
||||
f"FRL Caps:\n{self.frl_caps.__str__()}" \
|
||||
f"LTP Lanes Pattern:\n{self.ltp_request.__str__()}" \
|
||||
f"LTP Lanes Pattern Additional:\n{self.ltp_additional.__str__()}\n"
|
||||
366
UniTAP/dev/ports/modules/link/hdmi/frl_control_tx.py
Normal file
366
UniTAP/dev/ports/modules/link/hdmi/frl_control_tx.py
Normal file
@@ -0,0 +1,366 @@
|
||||
from UniTAP.libs.lib_tsi.tsi_io import PortIO
|
||||
from .types import FrlMode, LtpLanesPattern, FrlCaps, _update_frl_values, _update_ltp_pattern_values
|
||||
from UniTAP.libs.lib_tsi.tsi_types import TSI_HDTX_FRL_CAPABILITY, TSI_HDTX_FRL_PATTERN_R, TSI_HDTX_FRL_TIMERS, \
|
||||
TSI_HDTX_SINK_FEATURE_W, TSI_HDTX_FRL_STATUS_R
|
||||
from ctypes import c_uint32
|
||||
|
||||
|
||||
class FfeMax:
|
||||
"""
|
||||
Class `FfeMax` allows working with FFE on HDMI.
|
||||
Possible to configure:
|
||||
- Set and get Value for mode 3 lanes and 3 Gbps `mode_3lanes_3gbps`.
|
||||
- Set and get Value for mode 3 lanes and 6 Gbps `mode_3lanes_6gbps`.
|
||||
- Set and get Value for mode 4 lanes and 6 Gbps `mode_4lanes_6gbps`.
|
||||
- Set and get Value for mode 4 lanes and 8 Gbps `mode_4lanes_8gbps`.
|
||||
- Set and get Value for mode 4 lanes and 10 Gbps `mode_4lanes_10gbps`.
|
||||
- Set and get Value for mode 4 lanes and 12 Gbps `mode_4lanes_12gbps`.
|
||||
"""
|
||||
def __init__(self, mode_3lanes_3gbps: int = 0, mode_3lanes_6gbps: int = 0, mode_4lanes_6gbps: int = 0,
|
||||
mode_4lanes_8gbps: int = 0, mode_4lanes_10gbps: int = 0, mode_4lanes_12gbps: int = 0):
|
||||
self.__mode_3lanes_3gbps = mode_3lanes_3gbps
|
||||
self.__mode_3lanes_6gbps = mode_3lanes_6gbps
|
||||
self.__mode_4lanes_6gbps = mode_4lanes_6gbps
|
||||
self.__mode_4lanes_8gbps = mode_4lanes_8gbps
|
||||
self.__mode_4lanes_10gbps = mode_4lanes_10gbps
|
||||
self.__mode_4lanes_12gbps = mode_4lanes_12gbps
|
||||
|
||||
@property
|
||||
def mode_3lanes_3gbps(self) -> int:
|
||||
"""
|
||||
Returns current value for mode 3 lanes and 3 Gbps.
|
||||
|
||||
Returns:
|
||||
object of int type
|
||||
"""
|
||||
return self.__mode_3lanes_3gbps
|
||||
|
||||
@mode_3lanes_3gbps.setter
|
||||
def mode_3lanes_3gbps(self, ffe_value: int):
|
||||
"""
|
||||
Set new FFE value for mode 3 lanes and 3 Gbps.
|
||||
|
||||
Args:
|
||||
ffe_value (int)
|
||||
"""
|
||||
if not(0 <= ffe_value <= 3):
|
||||
raise ValueError(f"FFE value must be in range 0-3. Current value = {ffe_value}")
|
||||
self.__mode_3lanes_3gbps = ffe_value
|
||||
|
||||
@property
|
||||
def mode_3lanes_6gbps(self) -> int:
|
||||
"""
|
||||
Returns current value for mode 3 lanes and 6 Gbps.
|
||||
|
||||
Returns:
|
||||
object of int type
|
||||
"""
|
||||
return self.__mode_3lanes_6gbps
|
||||
|
||||
@mode_3lanes_6gbps.setter
|
||||
def mode_3lanes_6gbps(self, ffe_value: int):
|
||||
"""
|
||||
Set new FFE value for mode 3 lanes and 6 Gbps.
|
||||
|
||||
Args:
|
||||
ffe_value (int)
|
||||
"""
|
||||
if not(0 <= ffe_value <= 3):
|
||||
raise ValueError(f"FFE value must be in range 0-3. Current value = {ffe_value}")
|
||||
self.__mode_3lanes_6gbps = ffe_value
|
||||
|
||||
@property
|
||||
def mode_4lanes_6gbps(self) -> int:
|
||||
"""
|
||||
Returns current value for mode 6 lanes and 6 Gbps.
|
||||
|
||||
Returns:
|
||||
object of int type
|
||||
"""
|
||||
return self.__mode_4lanes_6gbps
|
||||
|
||||
@mode_4lanes_6gbps.setter
|
||||
def mode_4lanes_6gbps(self, ffe_value: int):
|
||||
"""
|
||||
Set new FFE value for mode 4 lanes and 6 Gbps.
|
||||
|
||||
Args:
|
||||
ffe_value (int)
|
||||
"""
|
||||
if not (0 <= ffe_value <= 3):
|
||||
raise ValueError(f"FFE value must be in range 0-3. Current value = {ffe_value}")
|
||||
self.__mode_4lanes_6gbps = ffe_value
|
||||
|
||||
@property
|
||||
def mode_4lanes_8gbps(self) -> int:
|
||||
"""
|
||||
Returns current value for mode 6 lanes and 8 Gbps.
|
||||
|
||||
Returns:
|
||||
object of int type
|
||||
"""
|
||||
return self.__mode_4lanes_8gbps
|
||||
|
||||
@mode_4lanes_8gbps.setter
|
||||
def mode_4lanes_8gbps(self, ffe_value: int):
|
||||
"""
|
||||
Set new FFE value for mode 4 lanes and 8 Gbps.
|
||||
|
||||
Args:
|
||||
ffe_value (int)
|
||||
"""
|
||||
if not (0 <= ffe_value <= 3):
|
||||
raise ValueError(f"FFE value must be in range 0-3. Current value = {ffe_value}")
|
||||
self.__mode_4lanes_8gbps = ffe_value
|
||||
|
||||
@property
|
||||
def mode_4lanes_10gbps(self) -> int:
|
||||
"""
|
||||
Returns current value for mode 6 lanes and 10 Gbps.
|
||||
|
||||
Returns:
|
||||
object of int type
|
||||
"""
|
||||
return self.__mode_4lanes_10gbps
|
||||
|
||||
@mode_4lanes_10gbps.setter
|
||||
def mode_4lanes_10gbps(self, ffe_value: int):
|
||||
"""
|
||||
Set new FFE value for mode 4 lanes and 10 Gbps.
|
||||
|
||||
Args:
|
||||
ffe_value (int)
|
||||
"""
|
||||
if not (0 <= ffe_value <= 3):
|
||||
raise ValueError(f"FFE value must be in range 0-3. Current value = {ffe_value}")
|
||||
self.__mode_4lanes_10gbps = ffe_value
|
||||
|
||||
@property
|
||||
def mode_4lanes_12gbps(self) -> int:
|
||||
"""
|
||||
Returns current value for mode 6 lanes and 12 Gbps.
|
||||
|
||||
Returns:
|
||||
object of int type
|
||||
"""
|
||||
return self.__mode_4lanes_12gbps
|
||||
|
||||
@mode_4lanes_12gbps.setter
|
||||
def mode_4lanes_12gbps(self, ffe_value: int):
|
||||
"""
|
||||
Set new FFE value for mode 4 lanes and 12 Gbps.
|
||||
|
||||
Args:
|
||||
ffe_value (int)
|
||||
"""
|
||||
if not (0 <= ffe_value <= 3):
|
||||
raise ValueError(f"FFE value must be in range 0-3. Current value = {ffe_value}")
|
||||
self.__mode_4lanes_12gbps = ffe_value
|
||||
|
||||
def value(self) -> int:
|
||||
"""
|
||||
Returns current combined value from all modes.
|
||||
|
||||
Returns:
|
||||
object of int type
|
||||
"""
|
||||
return ((self.mode_3lanes_3gbps & 0x3) << 5) | ((self.mode_3lanes_6gbps & 0x3) << 7) |\
|
||||
((self.mode_4lanes_6gbps & 0x3) << 9) | ((self.mode_4lanes_8gbps & 0x3) << 11) | \
|
||||
((self.mode_4lanes_10gbps & 0x3) << 13) | ((self.mode_4lanes_12gbps & 0x3) << 15)
|
||||
|
||||
def __str__(self):
|
||||
return f"3 lanes 3 Gbps - {self.mode_3lanes_3gbps}\n" \
|
||||
f"3 lanes 6 Gbps - {self.mode_3lanes_6gbps}\n" \
|
||||
f"4 lanes 6 Gbps - {self.mode_4lanes_6gbps}\n" \
|
||||
f"4 lanes 8 Gbps - {self.mode_4lanes_8gbps}\n" \
|
||||
f"4 lanes 10 Gbps - {self.mode_4lanes_10gbps}\n" \
|
||||
f"4 lanes 12 Gbps - {self.mode_4lanes_12gbps}\n"
|
||||
|
||||
|
||||
class FrlControlTx:
|
||||
"""
|
||||
Class `FrlControlTx` contains information about FRL on Source (TX - transmitter) side.
|
||||
allows working with:
|
||||
- Set and get FRL mode `frl_mode`.
|
||||
- Set and get FRL capabilities `frl_caps`.
|
||||
- Set and get FFE max `ffe_max`.
|
||||
- Set and get LTP requested `ltp_pattern` and additional `ltp_additional_pattern`.
|
||||
- Set link training timeout `lt_timeout` and link training poll timeout `lt_poll_timeout`.
|
||||
"""
|
||||
def __init__(self, port_io: PortIO):
|
||||
self.__io = port_io
|
||||
self.__ffe_max = FfeMax(0)
|
||||
self.__frl_caps = FrlCaps()
|
||||
self.__lt_timeout = 0
|
||||
self.__lt_poll_timeout = 0
|
||||
self.__ltp_request = LtpLanesPattern()
|
||||
self.__ltp_additional = LtpLanesPattern()
|
||||
|
||||
def __read_caps(self) -> int:
|
||||
return self.__io.get(TSI_HDTX_FRL_CAPABILITY, c_uint32)[1]
|
||||
|
||||
def __write_caps(self, value: int):
|
||||
self.__io.set(TSI_HDTX_FRL_CAPABILITY, value, c_uint32)
|
||||
|
||||
def __read_frl_timers(self) -> int:
|
||||
return self.__io.get(TSI_HDTX_FRL_TIMERS, c_uint32)[1]
|
||||
|
||||
def __write_frl_timers(self, value: int):
|
||||
self.__io.set(TSI_HDTX_FRL_TIMERS, value, c_uint32)
|
||||
|
||||
def __read_frl_patterns(self) -> int:
|
||||
return self.__io.get(TSI_HDTX_FRL_PATTERN_R, c_uint32)[1]
|
||||
|
||||
@property
|
||||
def ltp_pattern(self) -> LtpLanesPattern:
|
||||
"""
|
||||
Returns current LTP lanes pattern. Current Pattern that is being checking during Link Training.
|
||||
Each lane can request different pattern.
|
||||
|
||||
Returns:
|
||||
object of `LtpLanesPattern` type
|
||||
"""
|
||||
_update_ltp_pattern_values(self.__ltp_request, self.__read_frl_patterns())
|
||||
return self.__ltp_request
|
||||
|
||||
@property
|
||||
def ltp_additional_pattern(self) -> LtpLanesPattern:
|
||||
"""
|
||||
Returns current additional LTP lanes pattern. Current Pattern that is being checking during Link Training.
|
||||
Each lane can request different pattern.
|
||||
|
||||
Returns:
|
||||
object of `LtpLanesPattern` type
|
||||
"""
|
||||
_update_ltp_pattern_values(self.__ltp_additional, self.__read_frl_patterns() >> 16)
|
||||
return self.__ltp_additional
|
||||
|
||||
@property
|
||||
def frl_caps(self) -> FrlCaps:
|
||||
"""
|
||||
Returns current FRL capabilities.
|
||||
|
||||
Returns:
|
||||
object of `FrlCaps` type
|
||||
"""
|
||||
_update_frl_values(self.__frl_caps, self.__io.get(TSI_HDTX_FRL_STATUS_R, c_uint32)[1])
|
||||
return self.__frl_caps
|
||||
|
||||
@property
|
||||
def frl_mode(self) -> FrlMode:
|
||||
"""
|
||||
Returns current FRL mode.
|
||||
|
||||
Returns:
|
||||
object of `FrlMode` type
|
||||
"""
|
||||
return FrlMode(self.__read_caps() & 0x7)
|
||||
|
||||
@frl_mode.setter
|
||||
def frl_mode(self, frl_mode: FrlMode):
|
||||
"""
|
||||
Set new FRL mode.
|
||||
|
||||
Args:
|
||||
frl_mode (FrlMode)
|
||||
"""
|
||||
new_value = frl_mode.value
|
||||
new_value |= self.__read_caps() & ~0x7
|
||||
self.__write_caps(new_value)
|
||||
|
||||
@property
|
||||
def ffe_max(self) -> FfeMax:
|
||||
"""
|
||||
Returns current FFE MAX values.
|
||||
|
||||
Returns:
|
||||
object of `FfeMax` type
|
||||
"""
|
||||
ffe_values = self.__read_caps()
|
||||
self.__update_values(self.__ffe_max, ffe_values)
|
||||
return self.__ffe_max
|
||||
|
||||
@ffe_max.setter
|
||||
def ffe_max(self, ffe_max: FfeMax):
|
||||
"""
|
||||
Set new FFE max values.
|
||||
|
||||
Args:
|
||||
ffe_max (FfeMax)
|
||||
"""
|
||||
new_value = ffe_max.value()
|
||||
new_value |= (self.__read_caps() >> 5) & ~0xFFF
|
||||
self.__write_caps(new_value)
|
||||
self.__ffe_max = ffe_max
|
||||
|
||||
@property
|
||||
def lt_timeout(self) -> int:
|
||||
"""
|
||||
Returns current value of link training timeout.
|
||||
|
||||
Returns:
|
||||
object of int type
|
||||
"""
|
||||
self.__lt_timeout = self.__read_frl_timers() & 0xFFFF
|
||||
return self.__lt_timeout
|
||||
|
||||
@lt_timeout.setter
|
||||
def lt_timeout(self, lt_timeout: int):
|
||||
"""
|
||||
Set new link training timeout.
|
||||
|
||||
Args:
|
||||
lt_timeout (int)
|
||||
"""
|
||||
new_value = lt_timeout
|
||||
new_value |= self.__read_frl_timers() & ~0xFFFF
|
||||
self.__write_frl_timers(new_value)
|
||||
self.__lt_timeout = lt_timeout
|
||||
|
||||
@property
|
||||
def lt_poll_timeout(self) -> int:
|
||||
"""
|
||||
Returns current value of link training poll timeout.
|
||||
|
||||
Returns:
|
||||
object of int type
|
||||
"""
|
||||
self.__lt_timeout = self.__read_frl_timers() & 0xFFFF
|
||||
return self.__lt_timeout
|
||||
|
||||
@lt_poll_timeout.setter
|
||||
def lt_poll_timeout(self, lt_poll_timeout: int):
|
||||
"""
|
||||
Set new link training poll timeout.
|
||||
|
||||
Args:
|
||||
lt_poll_timeout (int)
|
||||
"""
|
||||
new_value = lt_poll_timeout
|
||||
new_value |= self.__read_frl_timers() & ~(0xFFFF << 16)
|
||||
self.__write_frl_timers(new_value)
|
||||
self.__lt_poll_timeout = lt_poll_timeout
|
||||
|
||||
def link_training(self):
|
||||
"""
|
||||
Do link training.
|
||||
"""
|
||||
self.__io.set(TSI_HDTX_SINK_FEATURE_W, 5, c_uint32)
|
||||
|
||||
@staticmethod
|
||||
def __update_values(ffe_max: FfeMax, value: int):
|
||||
ffe_max.mode_3lanes_3gbps = (value >> 5) & 0x3
|
||||
ffe_max.mode_3lanes_6gbps = (value >> 7) & 0x3
|
||||
ffe_max.mode_4lanes_6gbps = (value >> 9) & 0x3
|
||||
ffe_max.mode_4lanes_8gbps = (value >> 11) & 0x3
|
||||
ffe_max.mode_4lanes_10gbps = (value >> 13) & 0x3
|
||||
ffe_max.mode_4lanes_12gbps = (value >> 15) & 0x3
|
||||
|
||||
def __str__(self):
|
||||
return f"FRL Mode: {self.frl_mode.name}\n" \
|
||||
f"FRL Caps:\n{self.frl_caps.__str__()}" \
|
||||
f"LTP Lanes Pattern:\n{self.ltp_pattern.__str__()}" \
|
||||
f"LTP Lanes Pattern Additional:\n{self.ltp_additional_pattern.__str__()}" \
|
||||
f"LT timeout: {self.lt_timeout}\n" \
|
||||
f"LT poll timeout: {self.lt_poll_timeout}\n" \
|
||||
f"FFE Max:\n{self.ffe_max.__str__()}\n"
|
||||
53
UniTAP/dev/ports/modules/link/hdmi/hdmi_utils.py
Normal file
53
UniTAP/dev/ports/modules/link/hdmi/hdmi_utils.py
Normal file
@@ -0,0 +1,53 @@
|
||||
from UniTAP.common import VideoMode, ColorInfo, Timing
|
||||
|
||||
|
||||
def get_vm_color_format(color_mode: int) -> ColorInfo.ColorFormat:
|
||||
|
||||
if color_mode == 0:
|
||||
return ColorInfo.ColorFormat.CF_NONE
|
||||
elif color_mode == 1:
|
||||
return ColorInfo.ColorFormat.CF_UNKNOWN
|
||||
elif color_mode == 2:
|
||||
return ColorInfo.ColorFormat.CF_RGB
|
||||
elif color_mode == 3:
|
||||
return ColorInfo.ColorFormat.CF_YCbCr_422
|
||||
elif color_mode == 4:
|
||||
return ColorInfo.ColorFormat.CF_YCbCr_444
|
||||
elif color_mode == 5:
|
||||
return ColorInfo.ColorFormat.CF_YCbCr_420
|
||||
else:
|
||||
return ColorInfo.ColorFormat.CF_IDO_DEFINED
|
||||
|
||||
|
||||
def get_vm_colorimetry(colorimetry: int) -> ColorInfo.Colorimetry:
|
||||
|
||||
if colorimetry == 0:
|
||||
return ColorInfo.Colorimetry.CM_RESERVED
|
||||
elif colorimetry == 1:
|
||||
return ColorInfo.Colorimetry.CM_NONE
|
||||
elif colorimetry == 2:
|
||||
return ColorInfo.Colorimetry.CM_sRGB
|
||||
elif colorimetry == 3:
|
||||
return ColorInfo.Colorimetry.CM_SMPTE_170M
|
||||
elif colorimetry == 4:
|
||||
return ColorInfo.Colorimetry.CM_ITUR_BT601
|
||||
elif colorimetry == 5:
|
||||
return ColorInfo.Colorimetry.CM_ITUR_BT709
|
||||
elif colorimetry == 6:
|
||||
return ColorInfo.Colorimetry.CM_xvYCC601
|
||||
elif colorimetry == 7:
|
||||
return ColorInfo.Colorimetry.CM_xvYCC709
|
||||
elif colorimetry == 8:
|
||||
return ColorInfo.Colorimetry.CM_sYCC601
|
||||
elif colorimetry == 9:
|
||||
return ColorInfo.Colorimetry.CM_AdobeYCC601
|
||||
elif colorimetry == 10:
|
||||
return ColorInfo.Colorimetry.CM_AdobeRGB
|
||||
elif colorimetry == 11:
|
||||
return ColorInfo.Colorimetry.CM_ITUR_BT2020_YcCbcCrc
|
||||
elif colorimetry == 12:
|
||||
return ColorInfo.Colorimetry.CM_ITUR_BT2020_YCbCr
|
||||
elif colorimetry == 13:
|
||||
return ColorInfo.Colorimetry.CM_ITUR_BT2020_RGB
|
||||
else:
|
||||
return ColorInfo.Colorimetry.CM_RESERVED
|
||||
171
UniTAP/dev/ports/modules/link/hdmi/link.py
Normal file
171
UniTAP/dev/ports/modules/link/hdmi/link.py
Normal file
@@ -0,0 +1,171 @@
|
||||
import warnings
|
||||
|
||||
from UniTAP.libs.lib_tsi.tsi_io import PortIO
|
||||
from .arc_rx import ArcRx
|
||||
from .tmds_rx import TmdsRx
|
||||
from .tmds_tx import TmdsTx
|
||||
from .status_rx import StatusRx, HdmiModeRx
|
||||
from .status_tx import StatusTx, HdmiModeTx
|
||||
from .capabilities import HdmiCapabilities
|
||||
from .frl_control_tx import FrlControlTx
|
||||
from .frl_caps_rx import FrlControlRx
|
||||
from UniTAP.libs.lib_tsi.tsi_types import TSI_HDTX_CAPABILITY_R, TSI_HDRX_CAPABILITY_R
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class HdmiLinkTx:
|
||||
"""
|
||||
Main class describes HDMI link on Source (TX - transmitter) side. Contains following objects for working with link:
|
||||
- TMDS `tmds`.
|
||||
- FRL `frl`.
|
||||
- Status `status`.
|
||||
"""
|
||||
def __init__(self, port_io: PortIO):
|
||||
self.__io = port_io
|
||||
self.__tmds = TmdsTx(self.__io)
|
||||
self.__status = StatusTx(self.__io)
|
||||
self.__frl = FrlControlTx(self.__io)
|
||||
self.__caps = HdmiCapabilities(TSI_HDTX_CAPABILITY_R, self.__io)
|
||||
|
||||
@property
|
||||
def tmds(self) -> Optional[TmdsTx]:
|
||||
"""
|
||||
Returns object of class `TmdsTx` for working with TMDS.
|
||||
None if TMDS does not support on the device.
|
||||
|
||||
Returns:
|
||||
object of `TmdsTx`|None type
|
||||
"""
|
||||
if self.__status.hdmi_mode == HdmiModeTx.HDMI_2_1:
|
||||
warnings.warn("Current device mode is HDMI 2.1, please, use FRL controls or change HDMI mode.")
|
||||
return None
|
||||
if not self.__caps.support_tmds:
|
||||
warnings.warn("Current device does not support TMDS.")
|
||||
return None
|
||||
|
||||
return self.__tmds
|
||||
|
||||
@property
|
||||
def frl(self) -> Optional[FrlControlTx]:
|
||||
"""
|
||||
Returns object of class `FrlControlTx` for working with FRL.
|
||||
None if FRL does not support on the device.
|
||||
|
||||
Returns:
|
||||
object of `FrlControlTx`|None type
|
||||
"""
|
||||
if self.__status.hdmi_mode != HdmiModeTx.HDMI_2_1:
|
||||
warnings.warn("Current device mode is not HDMI 2.1, please, use TMDS controls or change HDMI mode.")
|
||||
return None
|
||||
if not self.__caps.support_frl:
|
||||
warnings.warn("Current device does not support FRl.")
|
||||
return None
|
||||
|
||||
return self.__frl
|
||||
|
||||
@property
|
||||
def status(self) -> StatusTx:
|
||||
"""
|
||||
Returns object of class `StatusTx` for working with link status.
|
||||
|
||||
Returns:
|
||||
object of `StatusTx` type
|
||||
"""
|
||||
return self.__status
|
||||
|
||||
def __str__(self):
|
||||
__str = f"Status:\n{self.status.__str__()}\n"
|
||||
if self.__status.hdmi_mode == HdmiModeTx.HDMI_2_1 and self.__caps.support_frl:
|
||||
__str += f"FRL:\n{self.frl.__str__()}\n"
|
||||
if self.__status.hdmi_mode != HdmiModeTx.HDMI_2_1 and self.__caps.support_tmds:
|
||||
__str += f"TMDS:\n{self.tmds.__str__()}\n"
|
||||
return __str
|
||||
|
||||
|
||||
class HdmiLinkRx:
|
||||
"""
|
||||
Main class describes HDMI link on Sink (RX - receiver) side. Contains following objects for working with link:
|
||||
- TMDS `tmds`.
|
||||
- FRL `frl`.
|
||||
- ARC `arc`.
|
||||
- Status `status`.
|
||||
"""
|
||||
def __init__(self, port_io: PortIO):
|
||||
self.__io = port_io
|
||||
self.__tmds = TmdsRx(self.__io)
|
||||
self.__status = StatusRx(self.__io)
|
||||
self.__frl = FrlControlRx(self.__io)
|
||||
self.__arc = ArcRx(self.__io)
|
||||
self.__caps = HdmiCapabilities(TSI_HDRX_CAPABILITY_R, self.__io)
|
||||
|
||||
@property
|
||||
def tmds(self) -> Optional[TmdsRx]:
|
||||
"""
|
||||
Returns object of class `TmdsRx` for working with TMDS.
|
||||
None if TMDS does not support on the device.
|
||||
|
||||
Returns:
|
||||
object of `TmdsRx`|None type
|
||||
"""
|
||||
if self.__status.hdmi_mode == HdmiModeTx.HDMI_2_1:
|
||||
warnings.warn("Current device mode is HDMI 2.1, please, use FRL controls or change HDMI mode.")
|
||||
return None
|
||||
if not self.__caps.support_tmds:
|
||||
warnings.warn("Current device does not support TMDS.")
|
||||
return None
|
||||
|
||||
return self.__tmds
|
||||
|
||||
@property
|
||||
def frl(self) -> Optional[FrlControlRx]:
|
||||
"""
|
||||
Returns object of class `FrlControlRx` for working with FRL.
|
||||
None if FRL does not support on the device.
|
||||
|
||||
Returns:
|
||||
object of `FrlControlRx`|None type
|
||||
"""
|
||||
if self.__status.hdmi_mode != HdmiModeRx.HDMI_2_1:
|
||||
warnings.warn("Current device mode is not HDMI 2.1, please, use TMDS controls or change HDMI mode.")
|
||||
return None
|
||||
if not self.__caps.support_frl:
|
||||
warnings.warn("Current device does not support FRl.")
|
||||
return None
|
||||
|
||||
return self.__frl
|
||||
|
||||
@property
|
||||
def arc(self) -> Optional[ArcRx]:
|
||||
"""
|
||||
Returns object of class `ArcRx` for working with ARC.
|
||||
None if ARC does not support on the device.
|
||||
|
||||
Returns:
|
||||
object of `ArcRx`|None type
|
||||
"""
|
||||
if not self.__caps.support_arc:
|
||||
warnings.warn("Current device does not support ARC.")
|
||||
return None
|
||||
|
||||
return self.__arc
|
||||
|
||||
@property
|
||||
def status(self) -> StatusRx:
|
||||
"""
|
||||
Returns object of class `StatusRx` for working with link status.
|
||||
|
||||
Returns:
|
||||
object of `StatusRx` type
|
||||
"""
|
||||
return self.__status
|
||||
|
||||
def __str__(self):
|
||||
__str = f"Status:\n{self.status.__str__()}\n"
|
||||
if self.__status.hdmi_mode == HdmiModeTx.HDMI_2_1 and self.__caps.support_frl:
|
||||
__str += f"FRL:\n{self.frl.__str__()}\n"
|
||||
if self.__status.hdmi_mode != HdmiModeTx.HDMI_2_1 and self.__caps.support_tmds:
|
||||
__str += f"TMDS:\n{self.tmds.__str__()}\n"
|
||||
if self.__caps.support_arc:
|
||||
__str += f"ARC:\n{self.arc.__str__()}\n"
|
||||
__str += f"Stream info:\n{self.status.stream(0).__str__()}\n"
|
||||
return __str
|
||||
52
UniTAP/dev/ports/modules/link/hdmi/private_types.py
Normal file
52
UniTAP/dev/ports/modules/link/hdmi/private_types.py
Normal file
@@ -0,0 +1,52 @@
|
||||
from ctypes import Structure, c_uint16, c_uint32
|
||||
|
||||
|
||||
class HdmiCrc(Structure):
|
||||
_fields_ = [
|
||||
('r', c_uint16),
|
||||
('g', c_uint16),
|
||||
('b', c_uint16)
|
||||
]
|
||||
|
||||
|
||||
class HdmiVideoMode(Structure):
|
||||
_fields_ = [
|
||||
('h_total', c_uint32),
|
||||
('v_total', c_uint32),
|
||||
('h_active', c_uint32),
|
||||
('v_active', c_uint32),
|
||||
('f_rate', c_uint32),
|
||||
('color_mode', c_uint32),
|
||||
('colorimetry', c_uint32),
|
||||
('bpp', c_uint32),
|
||||
('h_start', c_uint32),
|
||||
('v_start', c_uint32),
|
||||
('h_sync_width', c_uint32),
|
||||
('v_sync_width', c_uint32),
|
||||
('bpc', c_uint32)
|
||||
]
|
||||
|
||||
|
||||
class HdmiVideoModeInfo:
|
||||
|
||||
def __init__(self, hdmi_vm: HdmiVideoMode):
|
||||
self.h_total = hdmi_vm.h_total
|
||||
self.v_total = hdmi_vm.v_total
|
||||
self.h_active = hdmi_vm.h_active
|
||||
self.v_active = hdmi_vm.v_active
|
||||
self.h_start = hdmi_vm.h_start
|
||||
self.v_start = hdmi_vm.v_start
|
||||
self.h_sync_width = hdmi_vm.h_sync_width
|
||||
self.v_sync_width = hdmi_vm.v_sync_width
|
||||
self.frame_rate = hdmi_vm.f_rate
|
||||
self.bpp = hdmi_vm.bpp
|
||||
self.color_mode = hdmi_vm.color_mode
|
||||
self.colorimetry = hdmi_vm.colorimetry
|
||||
self.bpc = hdmi_vm.bpc
|
||||
|
||||
def is_eq(self, other) -> bool:
|
||||
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_start == other.h_start and self.v_start == other.v_start and \
|
||||
self.h_sync_width == other.h_sync_start and self.v_sync_width == other.v_sync_start and \
|
||||
self.frame_rate == other.frame_rate and self.bpp == other.bpp and self.color_mode == other.color_mode \
|
||||
and self.colorimetry == self.colorimetry and self.bpc == self.bpc
|
||||
151
UniTAP/dev/ports/modules/link/hdmi/status_rx.py
Normal file
151
UniTAP/dev/ports/modules/link/hdmi/status_rx.py
Normal file
@@ -0,0 +1,151 @@
|
||||
import time
|
||||
|
||||
from UniTAP.libs.lib_tsi.tsi_io import PortIO
|
||||
from .types import HdmiModeRx, FrlMode, _update_error_counters_rx
|
||||
from UniTAP.libs.lib_tsi.tsi_types import TSI_HDRX_BEHAVIOR, TSI_HDRX_LANES_ERR_COUNTERS_R, TSI_HDRX_HPD_STATUS_R,\
|
||||
TSI_HDRX_FRL_CAPABILITY, TSI_HDRX_LINK_STATUS_R, TSI_HDRX_HPD_CONTROL_W, TSI_HDRX_VIDEO_MODE_R, \
|
||||
TSI_VIDCAP_SIGNAL_CRC_R, TSI_SUCCESS
|
||||
from ctypes import c_uint8, c_uint32, c_uint64
|
||||
from UniTAP.utils.function_wrapper import function_scheduler
|
||||
from .types import _hdmi_vm_info_to_video_mode, HdmiVideoModeInfo
|
||||
from .private_types import HdmiCrc, HdmiVideoMode
|
||||
from ..dp.link_status_common import StreamStatus
|
||||
|
||||
|
||||
class StatusRx:
|
||||
"""
|
||||
Class `StatusRx` describes information about HDMI link status on Sink (RX - receiver) side. Contains following info:
|
||||
- HDMI mode `hdmi_mode`.
|
||||
- Link Error counters `error_counters`.
|
||||
- Channel lock `channel_lock`.
|
||||
- HPD status `hpd_status` and HPD assert `set_assert_state`.
|
||||
- Stream info `stream`.
|
||||
"""
|
||||
def __init__(self, port_io: PortIO):
|
||||
self.__io = port_io
|
||||
|
||||
def __frl_status(self) -> FrlMode:
|
||||
return FrlMode(self.__io.get(TSI_HDRX_FRL_CAPABILITY, c_uint32)[1] & 0xF)
|
||||
|
||||
@property
|
||||
def hdmi_mode(self) -> HdmiModeRx:
|
||||
"""
|
||||
Returns current HDMI mode.
|
||||
|
||||
Returns:
|
||||
object of `HdmiModeRx` type
|
||||
"""
|
||||
return HdmiModeRx(self.__io.get(TSI_HDRX_BEHAVIOR, c_uint32)[1])
|
||||
|
||||
@hdmi_mode.setter
|
||||
def hdmi_mode(self, hdmi_mode: HdmiModeRx):
|
||||
"""
|
||||
Set new HDMI mode.
|
||||
|
||||
Args:
|
||||
hdmi_mode (HdmiModeRx)
|
||||
"""
|
||||
self.__io.set(TSI_HDRX_BEHAVIOR, hdmi_mode.value, c_uint32)
|
||||
|
||||
@property
|
||||
def error_counters(self) -> list:
|
||||
"""
|
||||
Returns values of current errors on link.
|
||||
|
||||
Returns:
|
||||
object of list type
|
||||
"""
|
||||
return _update_error_counters_rx(self.__io.get(TSI_HDRX_LANES_ERR_COUNTERS_R, c_uint64)[1])
|
||||
|
||||
def set_assert_state(self, asserted: bool = True):
|
||||
"""
|
||||
Assert/Deassert HPD state.
|
||||
|
||||
Args:
|
||||
asserted (bool)
|
||||
"""
|
||||
self.__io.set(TSI_HDRX_HPD_CONTROL_W, int(asserted), c_uint32)
|
||||
|
||||
@property
|
||||
def channel_lock(self) -> list:
|
||||
"""
|
||||
Returns channel lock states of current link.
|
||||
|
||||
Returns:
|
||||
object of list type
|
||||
"""
|
||||
return self.__update_channel_lock(self.__io.get(TSI_HDRX_LINK_STATUS_R, c_uint32)[1], self.hdmi_mode)
|
||||
|
||||
@staticmethod
|
||||
def __update_channel_lock(value: int, mode: HdmiModeRx) -> list:
|
||||
if mode != HdmiModeRx.HDMI_2_1:
|
||||
lane_0 = (value & (1 << 4)) != 0
|
||||
lane_1 = (value & (1 << 5)) != 0
|
||||
lane_2 = (value & (1 << 6)) != 0
|
||||
lane_3 = False
|
||||
|
||||
else:
|
||||
lane_0 = (value & (1 << 13)) != 0
|
||||
lane_1 = (value & (1 << 14)) != 0
|
||||
lane_2 = (value & (1 << 15)) != 0
|
||||
lane_3 = (value & (1 << 16)) != 0
|
||||
return [lane_0, lane_1, lane_2, lane_3]
|
||||
|
||||
@property
|
||||
def hpd_status(self) -> bool:
|
||||
"""
|
||||
Returns True if HDP is enabled, False - if not.
|
||||
|
||||
Returns:
|
||||
object of bool type
|
||||
"""
|
||||
return (self.__io.get(TSI_HDRX_HPD_STATUS_R, c_uint32)[1] & 0x1) != 0
|
||||
|
||||
def __check_video(self) -> bool:
|
||||
def is_msa_available(io):
|
||||
result, msa_info, size = io.get(TSI_HDRX_VIDEO_MODE_R, HdmiVideoMode, 4)
|
||||
return result > 0
|
||||
|
||||
return function_scheduler(is_msa_available, self.__io, interval=5, timeout=10)
|
||||
|
||||
def stream(self, stream_index: int = 0) -> StreamStatus:
|
||||
"""
|
||||
Returns status of selected stream `StreamStatus`.
|
||||
|
||||
Args:
|
||||
stream_index (int) - number of selected number
|
||||
|
||||
Returns:
|
||||
object of `StreamStatus` type
|
||||
"""
|
||||
stream_status = StreamStatus()
|
||||
|
||||
if not self.__check_video():
|
||||
raise ValueError("Video is not available.")
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
result, hdmi_video_mode_info, size = self.__io.get(TSI_HDRX_VIDEO_MODE_R, HdmiVideoMode, 4)
|
||||
|
||||
# TODO - Size temporary will be equal 1
|
||||
size = 1
|
||||
|
||||
if 0 <= stream_index < size:
|
||||
stream_status.video_mode = _hdmi_vm_info_to_video_mode(HdmiVideoModeInfo(hdmi_video_mode_info
|
||||
[stream_index]))
|
||||
result = self.__io.get(TSI_VIDCAP_SIGNAL_CRC_R, data_type=c_uint8, data_count=6)
|
||||
if result[0] >= TSI_SUCCESS:
|
||||
hdmi_crc = HdmiCrc.from_buffer(bytearray(result[1]))
|
||||
stream_status.crc = [hdmi_crc.r, hdmi_crc.g, hdmi_crc.b]
|
||||
|
||||
stream_status.dsc_crc = [0, 0, 0]
|
||||
else:
|
||||
raise ValueError(f"Selected stream {stream_index} is not available. "
|
||||
f"Available stream number: {size}")
|
||||
|
||||
return stream_status
|
||||
|
||||
def __str__(self):
|
||||
return f"HDMI Mode: {self.hdmi_mode.name}\n" \
|
||||
f"Channel Lock:\n{self.channel_lock}\n" \
|
||||
f"Error Counters:\n{self.error_counters.__str__()}\n"
|
||||
140
UniTAP/dev/ports/modules/link/hdmi/status_tx.py
Normal file
140
UniTAP/dev/ports/modules/link/hdmi/status_tx.py
Normal file
@@ -0,0 +1,140 @@
|
||||
from UniTAP.libs.lib_tsi.tsi_io import PortIO
|
||||
from .types import HdmiModeTx, FrlMode, _update_error_counters_tx
|
||||
from UniTAP.libs.lib_tsi.tsi_types import TSI_HDTX_FRL_STATUS_R, TSI_HDTX_CONTROL_W, TSI_HDTX_STATUS_R, \
|
||||
TSI_HDTX_SINK_STATUS_R, TSI_HDTX_LANES_ERR_COUNTERS_R, TSI_HDTX_HPD_STATUS_R
|
||||
from ctypes import c_uint32, c_uint64
|
||||
|
||||
|
||||
class StatusTx:
|
||||
"""
|
||||
Class `StatusTx` describes information about HDMI link status on Source (TX - transmitter) side.
|
||||
Contains following info:
|
||||
- Set and get HDMI mode `hdmi_mode`.
|
||||
- Link Error counters `error_counters`.
|
||||
- Video status `video_status`.
|
||||
- Channel lock `channel_lock`.
|
||||
- HPD status `hpd_status`.
|
||||
"""
|
||||
def __init__(self, port_io: PortIO):
|
||||
self.__io = port_io
|
||||
|
||||
def __read_status(self) -> int:
|
||||
return self.__io.get(TSI_HDTX_STATUS_R, c_uint32)[1]
|
||||
|
||||
def __frl_status(self) -> FrlMode:
|
||||
return FrlMode(self.__io.get(TSI_HDTX_FRL_STATUS_R, c_uint32)[1] & 0xF)
|
||||
|
||||
@property
|
||||
def video_status(self) -> bool:
|
||||
"""
|
||||
Returns current video status (video enable or not).
|
||||
|
||||
Returns:
|
||||
object of bool type
|
||||
"""
|
||||
return (self.__read_status() & 0x2) != 0
|
||||
|
||||
@property
|
||||
def error_counters(self) -> list:
|
||||
"""
|
||||
Returns values of current errors on link.
|
||||
|
||||
Returns:
|
||||
object of list type
|
||||
"""
|
||||
return _update_error_counters_tx(self.__io.get(TSI_HDTX_LANES_ERR_COUNTERS_R, c_uint64)[1])
|
||||
|
||||
@property
|
||||
def channel_lock(self) -> list:
|
||||
"""
|
||||
Returns channel lock states of current link.
|
||||
|
||||
Returns:
|
||||
object of list type
|
||||
"""
|
||||
if self.__frl_status().Mode_Disable or self.hdmi_mode in [HdmiModeTx.HDMI_1_4, HdmiModeTx.HDMI_2_0]:
|
||||
return self.__update_channel_lock(self.__io.get(TSI_HDTX_SINK_STATUS_R, c_uint32)[1], self.hdmi_mode)
|
||||
else:
|
||||
return self.__update_channel_lock(self.__io.get(TSI_HDTX_FRL_STATUS_R, c_uint32)[1], self.hdmi_mode)
|
||||
|
||||
@property
|
||||
def hpd_status(self) -> bool:
|
||||
"""
|
||||
Returns True if HDP is enabled, False - if not.
|
||||
|
||||
Returns:
|
||||
object of bool type
|
||||
"""
|
||||
return (self.__io.get(TSI_HDTX_HPD_STATUS_R, c_uint32)[1] & 0x1) != 0
|
||||
|
||||
@property
|
||||
def hdmi_mode(self) -> HdmiModeTx:
|
||||
"""
|
||||
Returns current HDMI mode.
|
||||
|
||||
Returns:
|
||||
object of `HdmiModeTx` type
|
||||
"""
|
||||
return HdmiModeTx((self.__read_status() >> 2) & 0x3)
|
||||
|
||||
@hdmi_mode.setter
|
||||
def hdmi_mode(self, hdmi_mode: HdmiModeTx):
|
||||
"""
|
||||
Set new HDMI mode.
|
||||
|
||||
Args:
|
||||
hdmi_mode (HdmiModeTx)
|
||||
"""
|
||||
self.__io.set(TSI_HDTX_CONTROL_W, hdmi_mode.value << 2, c_uint32)
|
||||
|
||||
@property
|
||||
def available_link_rate(self) -> float:
|
||||
"""
|
||||
|
||||
Returns available link rate.
|
||||
|
||||
Returns:
|
||||
object of float type
|
||||
"""
|
||||
|
||||
gbps = 1000000000
|
||||
frl_status = self.__frl_status()
|
||||
if frl_status == FrlMode.Mode_Disable:
|
||||
link_rate = 3 * 6 * gbps
|
||||
elif frl_status == FrlMode.Mode_3lanes_3gbps:
|
||||
link_rate = 3 * 3 * gbps
|
||||
elif frl_status == FrlMode.Mode_3lanes_6gbps:
|
||||
link_rate = 3 * 6 * gbps
|
||||
elif frl_status == FrlMode.Mode_4lanes_6gbps:
|
||||
link_rate = 4 * 6 * gbps
|
||||
elif frl_status == FrlMode.Mode_4lanes_8gbps:
|
||||
link_rate = 4 * 8 * gbps
|
||||
elif frl_status == FrlMode.Mode_4lanes_10gbps:
|
||||
link_rate = 4 * 10 * gbps
|
||||
elif frl_status == FrlMode.Mode_4lanes_12gbps:
|
||||
link_rate = 4 * 12 * gbps
|
||||
else:
|
||||
link_rate = 0
|
||||
|
||||
return link_rate
|
||||
|
||||
@staticmethod
|
||||
def __update_channel_lock(value, mode: HdmiModeTx) -> list:
|
||||
if mode != HdmiModeTx.HDMI_2_1:
|
||||
lane_0 = (value & (1 << 6)) != 0
|
||||
lane_1 = (value & (1 << 7)) != 0
|
||||
lane_2 = (value & (1 << 8)) != 0
|
||||
lane_3 = False
|
||||
|
||||
else:
|
||||
lane_0 = (((value >> 25) & 0xF) >> 0) & 0x1 != 0
|
||||
lane_1 = (((value >> 25) & 0xF) >> 1) & 0x1 != 0
|
||||
lane_2 = (((value >> 25) & 0xF) >> 2) & 0x1 != 0
|
||||
lane_3 = (((value >> 25) & 0xF) >> 3) & 0x1 != 0
|
||||
|
||||
return [lane_0, lane_1, lane_2, lane_3]
|
||||
|
||||
def __str__(self):
|
||||
return f"HDMI Mode: {self.hdmi_mode.name}\n" \
|
||||
f"Channel Lock:\n{self.channel_lock}\n" \
|
||||
f"Error Counters:\n{self.error_counters.__str__()}\n"
|
||||
57
UniTAP/dev/ports/modules/link/hdmi/tmds_rx.py
Normal file
57
UniTAP/dev/ports/modules/link/hdmi/tmds_rx.py
Normal file
@@ -0,0 +1,57 @@
|
||||
from UniTAP.libs.lib_tsi.tsi_io import PortIO
|
||||
from .types import LinkMode
|
||||
from UniTAP.libs.lib_tsi.tsi_types import TSI_HDRX_LINK_STATUS_R
|
||||
from ctypes import c_uint32
|
||||
|
||||
|
||||
class TmdsRx:
|
||||
"""
|
||||
CLass `TmdsRx` allows working with TMDS on Sink (RX - receiver) side.
|
||||
- Get link mode `link_mode`.
|
||||
- Get clock rate `clock_rate`.
|
||||
- Get input stream lock state `input_stream_lock`.
|
||||
"""
|
||||
def __init__(self, port_io: PortIO):
|
||||
self.__io = port_io
|
||||
self.__link_mode = LinkMode.Unknown
|
||||
self.__input_stream_lock = False
|
||||
|
||||
def __read_link_status(self) -> int:
|
||||
return self.__io.get(TSI_HDRX_LINK_STATUS_R, c_uint32)[1]
|
||||
|
||||
@property
|
||||
def clock_rate(self) -> int:
|
||||
"""
|
||||
Returns current clock rate.
|
||||
|
||||
Returns:
|
||||
object of int type
|
||||
"""
|
||||
return 6 if self.__read_link_status() & 0x2 else 3
|
||||
|
||||
@property
|
||||
def link_mode(self) -> LinkMode:
|
||||
"""
|
||||
Returns current link mode.
|
||||
|
||||
Returns:
|
||||
object of `LinkMode` type
|
||||
"""
|
||||
self.__link_mode = LinkMode(self.__read_link_status() & 0x8)
|
||||
return self.__link_mode
|
||||
|
||||
@property
|
||||
def input_stream_lock(self) -> bool:
|
||||
"""
|
||||
Returns current state of input stream lock.
|
||||
|
||||
Returns:
|
||||
object of bool type
|
||||
"""
|
||||
self.__input_stream_lock = (self.__read_link_status() & 0x4) != 0
|
||||
return self.__input_stream_lock
|
||||
|
||||
def __str__(self):
|
||||
return f"Clock rate: {self.clock_rate}G\n" \
|
||||
f"Link mode: {self.link_mode.name}\n" \
|
||||
f"Input stream lock: {'Enable' if self.input_stream_lock else 'Disable'}"
|
||||
99
UniTAP/dev/ports/modules/link/hdmi/tmds_tx.py
Normal file
99
UniTAP/dev/ports/modules/link/hdmi/tmds_tx.py
Normal file
@@ -0,0 +1,99 @@
|
||||
import warnings
|
||||
|
||||
from UniTAP.libs.lib_tsi.tsi_io import PortIO
|
||||
from .types import ClockRate, LinkMode, ScramblerState
|
||||
from UniTAP.libs.lib_tsi.tsi_types import TSI_HDTX_SINK_FEATURE_W, TSI_HDTX_STATUS_R, TSI_HDTX_SINK_STATUS_R, \
|
||||
TSI_HDTX_CONTROL_W
|
||||
from ctypes import c_uint32
|
||||
|
||||
|
||||
class TmdsTx:
|
||||
"""
|
||||
CLass `TmdsRx` allows working with TMDS on Source (TX - transmitter) side.
|
||||
- Get link mode `link_mode`.
|
||||
- Set and get clock rate `clock_rate`.
|
||||
- Set and get scrambler `scrambler`.
|
||||
"""
|
||||
def __init__(self, port_io: PortIO):
|
||||
self.__io = port_io
|
||||
self.__clock_rate = ClockRate.Unknown
|
||||
self.__link_mode = LinkMode.Unknown
|
||||
self.__scrambler = ScramblerState.Unknown
|
||||
|
||||
def __read_sink_status(self) -> int:
|
||||
return self.__io.get(TSI_HDTX_SINK_STATUS_R, c_uint32)[1]
|
||||
|
||||
def __read_hdtx_status(self) -> int:
|
||||
return self.__io.get(TSI_HDTX_STATUS_R, c_uint32)[1]
|
||||
|
||||
def __write_sink_feature(self, value: int):
|
||||
self.__io.set(TSI_HDTX_SINK_FEATURE_W, value, c_uint32)
|
||||
|
||||
@property
|
||||
def clock_rate(self) -> int:
|
||||
"""
|
||||
Returns current clock rate.
|
||||
|
||||
Returns:
|
||||
object of int type
|
||||
"""
|
||||
return 6 if self.__read_sink_status() & 0x2 else 3
|
||||
|
||||
@clock_rate.setter
|
||||
def clock_rate(self, clock_rate: int):
|
||||
"""
|
||||
Set new clock rate value. Available variants: 3, 6.
|
||||
|
||||
Args:
|
||||
clock_rate (int)
|
||||
"""
|
||||
if clock_rate not in [3, 6]:
|
||||
warnings.warn("Incorrect value. Must be from available values: 3, 6")
|
||||
return
|
||||
self.__write_sink_feature(3 if clock_rate == 3 else 4)
|
||||
|
||||
@property
|
||||
def link_mode(self) -> LinkMode:
|
||||
"""
|
||||
Returns current link mode.
|
||||
|
||||
Returns:
|
||||
object of `LinkMode` type
|
||||
"""
|
||||
self.__link_mode = LinkMode(self.__read_hdtx_status() & 0x1)
|
||||
return self.__link_mode
|
||||
|
||||
@link_mode.setter
|
||||
def link_mode(self, link_mode: LinkMode):
|
||||
"""
|
||||
Set new link mode.
|
||||
|
||||
Args:
|
||||
link_mode (LinkMode)
|
||||
"""
|
||||
self.__io.set(TSI_HDTX_CONTROL_W, link_mode.value, c_uint32)
|
||||
|
||||
@property
|
||||
def scrambler(self) -> bool:
|
||||
"""
|
||||
Returns current scrambler state.
|
||||
|
||||
Returns:
|
||||
object of bool type
|
||||
"""
|
||||
return self.__read_sink_status() & 0x1 != 0
|
||||
|
||||
@scrambler.setter
|
||||
def scrambler(self, scrambler: bool):
|
||||
"""
|
||||
Enable/disable scrambler.
|
||||
|
||||
Args:
|
||||
scrambler (bool)
|
||||
"""
|
||||
self.__write_sink_feature(1 if scrambler else 2)
|
||||
|
||||
def __str__(self):
|
||||
return f"Clock rate: {self.clock_rate}G\n" \
|
||||
f"Link mode: {self.link_mode.name}\n" \
|
||||
f"Scrambler: {'Enable' if self.scrambler else 'Disable'}\n"
|
||||
372
UniTAP/dev/ports/modules/link/hdmi/types.py
Normal file
372
UniTAP/dev/ports/modules/link/hdmi/types.py
Normal file
@@ -0,0 +1,372 @@
|
||||
from enum import IntEnum
|
||||
from .hdmi_utils import *
|
||||
from .private_types import HdmiVideoModeInfo
|
||||
|
||||
|
||||
class FrlMode(IntEnum):
|
||||
"""
|
||||
Class `FrlMode` contains all possible variants of FRL modes.
|
||||
"""
|
||||
Mode_Unknown = -1
|
||||
Mode_Disable = 0
|
||||
Mode_3lanes_3gbps = 1
|
||||
Mode_3lanes_6gbps = 2
|
||||
Mode_4lanes_6gbps = 3
|
||||
Mode_4lanes_8gbps = 4
|
||||
Mode_4lanes_10gbps = 5
|
||||
Mode_4lanes_12gbps = 6
|
||||
|
||||
|
||||
class LtpPattern(IntEnum):
|
||||
"""
|
||||
Class `LtpPattern` contains all possible variants of LTP pattern types.
|
||||
"""
|
||||
Unknown = -1
|
||||
NoLinkPattern = 0
|
||||
All_1s = 1
|
||||
All_0s = 2
|
||||
NyquistClockPattern = 3
|
||||
SourceTxFFECompliance = 4
|
||||
LFSR0 = 5
|
||||
LFSR1 = 6
|
||||
LFSR2 = 7
|
||||
LFSR3 = 8
|
||||
|
||||
|
||||
class ScramblerState(IntEnum):
|
||||
"""
|
||||
Class `ScramblerState` contains all possible variants of scrambler states.
|
||||
"""
|
||||
Unknown = -1
|
||||
Disable = 0
|
||||
Enable = 1
|
||||
|
||||
|
||||
class LinkMode(IntEnum):
|
||||
"""
|
||||
Class `LinkMode` contains all possible variants of Link modes.
|
||||
"""
|
||||
Unknown = -1
|
||||
HDMI = 0
|
||||
DVI = 1
|
||||
|
||||
|
||||
class ClockRate(IntEnum):
|
||||
"""
|
||||
Class `ClockRate` contains all possible variants of clock rate.
|
||||
"""
|
||||
Unknown = -1
|
||||
Rate3G = 0
|
||||
Rate6G = 1
|
||||
|
||||
|
||||
class HdmiModeTx(IntEnum):
|
||||
"""
|
||||
Class `HdmiModeTx` contains all possible variants of HDMI modes on TX side.
|
||||
"""
|
||||
Unknown = -1
|
||||
HDMI_2_0 = 0
|
||||
HDMI_1_4 = 1
|
||||
HDMI_2_1 = 2
|
||||
|
||||
|
||||
class HdmiModeRx(IntEnum):
|
||||
"""
|
||||
Class `HdmiModeRx` contains all possible variants of HDMI modes on RX side.
|
||||
"""
|
||||
Unknown = -1
|
||||
HDMI_1_4 = 0
|
||||
HDMI_2_0 = 1
|
||||
HDMI_2_1 = 2
|
||||
|
||||
|
||||
class LtpLanesPattern:
|
||||
"""
|
||||
Class `LtpLanesPattern` describes LTP pattern values on all lanes.
|
||||
- Lane 0 `lane0`.
|
||||
- Lane 1 `lane1`.
|
||||
- Lane 2 `lane2`.
|
||||
- Lane 3 `lane3`.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.__lane0 = LtpPattern.Unknown
|
||||
self.__lane1 = LtpPattern.Unknown
|
||||
self.__lane2 = LtpPattern.Unknown
|
||||
self.__lane3 = LtpPattern.Unknown
|
||||
|
||||
@property
|
||||
def lane0(self) -> LtpPattern:
|
||||
"""
|
||||
Returns `LtpPattern` value of line 0.
|
||||
|
||||
Returns:
|
||||
object of `LtpPattern` type
|
||||
"""
|
||||
return self.__lane0
|
||||
|
||||
@lane0.setter
|
||||
def lane0(self, lane0: LtpPattern):
|
||||
"""
|
||||
Set new value `LtpPattern` to lane 0.
|
||||
|
||||
Args:
|
||||
lane0 (LtpPattern)
|
||||
"""
|
||||
self.__lane0 = lane0
|
||||
|
||||
@property
|
||||
def lane1(self) -> LtpPattern:
|
||||
"""
|
||||
Returns `LtpPattern` value of line 1.
|
||||
|
||||
Returns:
|
||||
object of `LtpPattern` type
|
||||
"""
|
||||
return self.__lane1
|
||||
|
||||
@lane1.setter
|
||||
def lane1(self, lane1: LtpPattern):
|
||||
"""
|
||||
Set new value `LtpPattern` to lane 1.
|
||||
|
||||
Args:
|
||||
lane1 (LtpPattern)
|
||||
"""
|
||||
self.__lane1 = lane1
|
||||
|
||||
@property
|
||||
def lane2(self) -> LtpPattern:
|
||||
"""
|
||||
Returns `LtpPattern` value of line 2.
|
||||
|
||||
Returns:
|
||||
object of `LtpPattern` type
|
||||
"""
|
||||
return self.__lane2
|
||||
|
||||
@lane2.setter
|
||||
def lane2(self, lane2: LtpPattern):
|
||||
"""
|
||||
Set new value `LtpPattern` to lane 1.
|
||||
|
||||
Args:
|
||||
lane2 (LtpPattern)
|
||||
"""
|
||||
self.__lane2 = lane2
|
||||
|
||||
@property
|
||||
def lane3(self) -> LtpPattern:
|
||||
"""
|
||||
Returns `LtpPattern` value of line 3.
|
||||
|
||||
Returns:
|
||||
object of `LtpPattern` type
|
||||
"""
|
||||
return self.__lane3
|
||||
|
||||
@lane3.setter
|
||||
def lane3(self, lane3: LtpPattern):
|
||||
"""
|
||||
Set new value `LtpPattern` to lane 3.
|
||||
|
||||
Args:
|
||||
lane3 (LtpPattern)
|
||||
"""
|
||||
self.__lane3 = lane3
|
||||
|
||||
def value(self) -> int:
|
||||
"""
|
||||
Returns combines value of all lines.
|
||||
|
||||
Returns:
|
||||
object of int type
|
||||
"""
|
||||
return self.lane0.value | (self.lane1.value << 4) | (self.lane2.value << 8) | (self.lane3.value << 12)
|
||||
|
||||
def __str__(self):
|
||||
return f"Lane 0: {self.lane0}\n" \
|
||||
f"Lane 1: {self.lane1}\n" \
|
||||
f"Lane 2: {self.lane2}\n" \
|
||||
f"Lane 3: {self.lane3}\n"
|
||||
|
||||
|
||||
class FrlCaps:
|
||||
"""
|
||||
Class `FrlCaps` describes FRL capabilities.
|
||||
- FRL start `frl_start`.
|
||||
- FLT no timeout `flt_no_timeout`.
|
||||
- FLT ready `flt_ready`
|
||||
- FRL max `frl_max`.
|
||||
- Check patterns state `check_patterns`.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.__frl_start = False
|
||||
self.__flt_no_timeout = False
|
||||
self.__flt_ready = False
|
||||
self.__frl_max = False
|
||||
self.__check_patterns = False
|
||||
|
||||
@property
|
||||
def frl_start(self) -> bool:
|
||||
"""
|
||||
Returns state of FRL start.
|
||||
|
||||
Returns:
|
||||
object of bool type
|
||||
"""
|
||||
return self.__frl_start
|
||||
|
||||
@frl_start.setter
|
||||
def frl_start(self, frl_start: bool):
|
||||
"""
|
||||
Set new state to FRL start.
|
||||
|
||||
Args:
|
||||
frl_start (bool)
|
||||
"""
|
||||
self.__frl_start = frl_start
|
||||
|
||||
@property
|
||||
def flt_no_timeout(self) -> bool:
|
||||
"""
|
||||
Returns state of FLT no timeout.
|
||||
|
||||
Returns:
|
||||
object of bool type
|
||||
"""
|
||||
return self.__flt_no_timeout
|
||||
|
||||
@flt_no_timeout.setter
|
||||
def flt_no_timeout(self, flt_no_timeout: bool):
|
||||
"""
|
||||
Set new state to FLT no timeout.
|
||||
|
||||
Args:
|
||||
flt_no_timeout (bool)
|
||||
"""
|
||||
self.__flt_no_timeout = flt_no_timeout
|
||||
|
||||
@property
|
||||
def flt_ready(self) -> bool:
|
||||
"""
|
||||
Returns state of FLT ready.
|
||||
|
||||
Returns:
|
||||
object of bool type
|
||||
"""
|
||||
return self.__flt_ready
|
||||
|
||||
@flt_ready.setter
|
||||
def flt_ready(self, flt_ready: bool):
|
||||
"""
|
||||
Set new state to FLT ready.
|
||||
|
||||
Args:
|
||||
flt_ready (bool)
|
||||
"""
|
||||
self.__flt_ready = flt_ready
|
||||
|
||||
@property
|
||||
def frl_max(self) -> bool:
|
||||
"""
|
||||
Returns state of FLT max.
|
||||
|
||||
Returns:
|
||||
object of bool type
|
||||
"""
|
||||
return self.__frl_max
|
||||
|
||||
@frl_max.setter
|
||||
def frl_max(self, frl_max: bool):
|
||||
"""
|
||||
Set new state to FLT max.
|
||||
|
||||
Args:
|
||||
frl_max (bool)
|
||||
"""
|
||||
self.__frl_max = frl_max
|
||||
|
||||
@property
|
||||
def check_patterns(self) -> bool:
|
||||
"""
|
||||
Returns state of check patterns.
|
||||
|
||||
Returns:
|
||||
object of bool type
|
||||
"""
|
||||
return self.__check_patterns
|
||||
|
||||
@check_patterns.setter
|
||||
def check_patterns(self, check_patterns: bool):
|
||||
"""
|
||||
Set new state to check patterns.
|
||||
|
||||
Args:
|
||||
check_patterns (bool)
|
||||
"""
|
||||
self.__check_patterns = check_patterns
|
||||
|
||||
def value(self) -> int:
|
||||
"""
|
||||
Returns combined value of all flags.
|
||||
|
||||
Returns:
|
||||
object of int type
|
||||
"""
|
||||
return (self.frl_start << 4) | (self.flt_ready << 5) | (self.flt_no_timeout << 6) | (self.frl_max << 7) | \
|
||||
(self.check_patterns << 8)
|
||||
|
||||
def __str__(self):
|
||||
return f"FRL Start: {self.__frl_start}\n" \
|
||||
f"FRL No Timeout: {self.__flt_no_timeout}\n" \
|
||||
f"FRL Max: {self.__frl_max}\n" \
|
||||
f"Check patterns: {self.__check_patterns}\n"
|
||||
|
||||
|
||||
def _hdmi_vm_info_to_video_mode(hdmi_vm_info: HdmiVideoModeInfo):
|
||||
video_mode = VideoMode()
|
||||
|
||||
video_mode.timing.hactive = hdmi_vm_info.h_active
|
||||
video_mode.timing.vactive = hdmi_vm_info.v_active
|
||||
video_mode.timing.htotal = hdmi_vm_info.h_total
|
||||
video_mode.timing.vtotal = hdmi_vm_info.v_total
|
||||
video_mode.timing.hstart = hdmi_vm_info.h_start
|
||||
video_mode.timing.vstart = hdmi_vm_info.v_start
|
||||
video_mode.timing.hswidth = hdmi_vm_info.h_sync_width
|
||||
video_mode.timing.vswidth = hdmi_vm_info.v_sync_width
|
||||
|
||||
video_mode.color_info.colorimetry = get_vm_colorimetry(hdmi_vm_info.colorimetry)
|
||||
video_mode.color_info.color_format = get_vm_color_format(hdmi_vm_info.color_mode)
|
||||
video_mode.color_info.bpc = hdmi_vm_info.bpc
|
||||
video_mode.color_info.dynamic_range = ColorInfo.DynamicRange.DR_VESA
|
||||
|
||||
video_mode.timing.frame_rate = hdmi_vm_info.frame_rate
|
||||
|
||||
return video_mode
|
||||
|
||||
|
||||
def _update_frl_values(frl_caps: FrlCaps, value: int):
|
||||
frl_caps.frl_start = bool(value >> 4 & 0x1)
|
||||
frl_caps.flt_ready = bool(value >> 5 & 0x1)
|
||||
frl_caps.flt_no_timeout = bool(value >> 6 & 0x1)
|
||||
frl_caps.frl_max = bool(value >> 7 & 0x1)
|
||||
frl_caps.check_patterns = bool(value >> 8 & 0x1)
|
||||
|
||||
|
||||
def _update_ltp_pattern_values(pattern: LtpLanesPattern, value: int):
|
||||
pattern.lane0 = LtpPattern(value & 0xF)
|
||||
pattern.lane1 = LtpPattern((value >> 4) & 0xF)
|
||||
pattern.lane2 = LtpPattern((value >> 8) & 0xF)
|
||||
pattern.lane3 = LtpPattern((value >> 12) & 0xF)
|
||||
|
||||
|
||||
def _update_error_counters_rx(value: int) -> list:
|
||||
lane0 = value & 0x7FFF if (value >> 15) & 0x1 else 0
|
||||
lane1 = ((value >> 16) & 0xFFFF) if (value >> 31) & 0x1 else 0
|
||||
lane2 = ((value >> 32) & 0xFFFF) if (value >> 47) & 0x1 else 0
|
||||
lane3 = ((value >> 48) & 0xFFFF) if (value >> 63) & 0x1 else 0
|
||||
return [lane0, lane1, lane2, lane3]
|
||||
|
||||
|
||||
def _update_error_counters_tx(value: int) -> list:
|
||||
return [value & 0xFFFF, (value >> 16) & 0xFFFF, (value >> 32) & 0xFFFF, (value >> 48) & 0xFFFF]
|
||||
Reference in New Issue
Block a user