280 lines
9.8 KiB
Python
280 lines
9.8 KiB
Python
|
|
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
|