323 lines
10 KiB
Python
323 lines
10 KiB
Python
import warnings
|
|
from typing import List
|
|
|
|
from UniTAP.common import Timing
|
|
|
|
from UniTAP.libs.lib_tsi.tsi_types import TSI_EDID_SELECT_STREAM, TSI_EDID_TE_INPUT, TSI_EDID_TE_OUTPUT, \
|
|
TSI_EDID_TE_OUTPUT_REMOTE_R, TSI_DID_TE_OUTPUT, TSI_DID_TE_INPUT, TSI_DID_SELECT_STREAM, \
|
|
TSI_DID_TE_OUTPUT_REMOTE_R, TSI_DPRX_DISPLAYID_CTRL, TSI_DPTX_DISPLAYID_CTRL, TSI_EDID_DUT_TIMINGS_COUNT, \
|
|
TSI_EDID_DUT_TIMINGS_DATA
|
|
from UniTAP.libs.lib_tsi.tsi_io import PortIO
|
|
from UniTAP.dev.ports.modules.device_constants import MAX_EDID_SIZE
|
|
from ctypes import c_ubyte, c_uint32
|
|
from .edid_utils import save_file, load_file, EdidFileType
|
|
from .edid_parser import EdidParser
|
|
from .edid_types import DisplayIDReadMode
|
|
from .edid_private_types import ParsedTimingStruct
|
|
|
|
|
|
class Edid:
|
|
|
|
"""
|
|
Main class for working with EDID.
|
|
Allows reading and saving EDID. This functionality is used by child classes `EdidSource` and `EdidSink`.
|
|
You cannot use a class `Edid` object directly.
|
|
"""
|
|
|
|
def __init__(self, port_io: PortIO, control_ci: int, max_stream_count: int, select_stream_ci: int):
|
|
self._io = port_io
|
|
self._control_ci = control_ci
|
|
self.__select_stream_ci = select_stream_ci
|
|
self._max_stream_count = max_stream_count
|
|
self.__parser = EdidParser()
|
|
|
|
def read_i2c(self) -> bytearray:
|
|
"""
|
|
|
|
Allows reading from DUT `EdidSource` or TE `EdidSink` side EDID block(s) over connecting signal cable.
|
|
|
|
Returns:
|
|
object of bytearray
|
|
"""
|
|
result, edid_data, size = self._io.get(self._control_ci, c_ubyte, MAX_EDID_SIZE)
|
|
if result > 0:
|
|
return bytearray(edid_data[:result])
|
|
else:
|
|
return bytearray()
|
|
|
|
def save_edid(self, path: str, file_type: EdidFileType, data: bytearray):
|
|
"""
|
|
|
|
Save received EDID data into file.
|
|
Supported formats:
|
|
- BIN.
|
|
- HEX.
|
|
|
|
Args:
|
|
path (str) - full path to file
|
|
file_type (`EdidFileType`) - one of the Supported formats.
|
|
data (bytearray) - EDID data for saving
|
|
"""
|
|
if len(data) > 0:
|
|
save_file(data, path, file_type)
|
|
else:
|
|
warnings.warn("EDID is empty.")
|
|
|
|
def read_timings(self) -> List[Timing]:
|
|
"""
|
|
|
|
"""
|
|
timings_count = self._io.get(TSI_EDID_DUT_TIMINGS_COUNT, c_uint32)[1]
|
|
|
|
if timings_count == 0:
|
|
return []
|
|
|
|
timings = self._io.get(TSI_EDID_DUT_TIMINGS_DATA, ParsedTimingStruct, timings_count)[1]
|
|
parsed_timings = []
|
|
|
|
for i in range(timings_count):
|
|
timing = Timing()
|
|
timing.htotal = timings[i].h_total
|
|
timing.vtotal = timings[i].v_total
|
|
timing.hactive = timings[i].h_active
|
|
timing.vactive = timings[i].v_active
|
|
timing.hstart = timings[i].h_start
|
|
timing.vstart = timings[i].v_start
|
|
timing.hswidth = timings[i].h_sync
|
|
timing.vswidth = timings[i].v_sync
|
|
# timing.id = timings[i].id
|
|
|
|
# timing.aspect_ratio = timings[i].aspect_ratio
|
|
timing.frame_rate = timings[i].frame_rate_millihz
|
|
# timing.standard = timings[i].standard
|
|
# timing.reduce_blanking = timings[i].reduce_blanking
|
|
parsed_timings.append(timing)
|
|
|
|
return parsed_timings
|
|
|
|
def _select_stream(self, stream):
|
|
"""
|
|
Set Virtual Sink's EDID index.
|
|
stream = 0 - UCD local EDID
|
|
stream > 0 - Virtual Sink's EDID
|
|
|
|
Args:
|
|
stream (int) - Virtual Sink's EDID index.
|
|
"""
|
|
if not (0 <= stream < self._max_stream_count):
|
|
warnings.warn(f"Stream must be in range 0 - {self._max_stream_count}. Current value: {stream}")
|
|
return
|
|
self._io.set(self.__select_stream_ci, stream, c_uint32)
|
|
|
|
@property
|
|
def _parser(self) -> EdidParser:
|
|
return self.__parser
|
|
|
|
|
|
class EdidSource(Edid):
|
|
|
|
"""
|
|
Class `EdidSource` inherited from class `Edid`.
|
|
Allows read EDID from remote devices `read_sbm`, save received EDID data to file `save_edid` and
|
|
read EDID from DUT `read_i2c`
|
|
"""
|
|
|
|
def __init__(self, port_io: PortIO, max_stream_count: int):
|
|
super().__init__(port_io, TSI_EDID_TE_OUTPUT, max_stream_count, TSI_EDID_SELECT_STREAM)
|
|
|
|
def read_sbm(self, stream: int):
|
|
"""
|
|
|
|
Allows reading remote EDID block(s) of remote device(s) attached to DUT over connecting signal cable.
|
|
stream = 0 - UCD local EDID
|
|
stream > 0 - Virtual Sink's EDID
|
|
|
|
Args:
|
|
stream (int) - Virtual Sink index.
|
|
|
|
"""
|
|
self._select_stream(stream)
|
|
result, edid_data, size = self._io.get(TSI_EDID_TE_OUTPUT_REMOTE_R, c_ubyte, MAX_EDID_SIZE)
|
|
if result > 0:
|
|
return bytearray(edid_data[:result])
|
|
else:
|
|
return bytearray()
|
|
|
|
|
|
class EdidSink(Edid):
|
|
|
|
"""
|
|
Class `EdidSink` inherited from class `Edid`.
|
|
Allows writing EDID to device `write_edid`, load EDID data from file `load_edid`,
|
|
save received EDID data to file `save_edid` and read EDID from TE `read_i2c`.
|
|
"""
|
|
|
|
def __init__(self, port_io: PortIO, max_stream_count: int):
|
|
super().__init__(port_io, TSI_EDID_TE_INPUT, max_stream_count, TSI_EDID_SELECT_STREAM)
|
|
|
|
def write_edid(self, data: bytearray, stream: int = 0):
|
|
"""
|
|
|
|
Write transferred EDID to device.
|
|
|
|
Args:
|
|
data (bytearray) - EDID data for writing
|
|
stream (int) - Virtual Sink's EDID index
|
|
"""
|
|
self._select_stream(stream)
|
|
self._io.set(self._control_ci, data, c_ubyte, len(data))
|
|
|
|
def load_edid(self, path: str, load_on_device: bool, stream: int = 0) -> bytearray:
|
|
"""
|
|
|
|
Read EDID data from file. If needed to write data to device, select load_on_device = True.
|
|
Supported formats:
|
|
- BIN.
|
|
- HEX.
|
|
|
|
Args:
|
|
path (str) - full path to file.
|
|
load_on_device (bool) - write loaded data to device or not.
|
|
stream (int) - Virtual Sink's EDID index
|
|
|
|
Returns:
|
|
object of bytearray
|
|
"""
|
|
data = load_file(path)
|
|
if len(data) > 0:
|
|
if load_on_device:
|
|
self.write_edid(data, stream)
|
|
return data
|
|
else:
|
|
warnings.warn("File is empty.")
|
|
return bytearray()
|
|
|
|
def read_sbm(self, stream: int):
|
|
"""
|
|
|
|
Allows reading remote EDID block(s) of remote device(s) attached to DUT over connecting signal cable.
|
|
stream = 0 - UCD local EDID
|
|
stream > 0 - Virtual Sink's EDID
|
|
|
|
Args:
|
|
stream (int) - Virtual Sink index.
|
|
|
|
"""
|
|
self._select_stream(stream)
|
|
return self.read_i2c()
|
|
|
|
|
|
class DisplayIdSource(Edid):
|
|
|
|
"""
|
|
Class `DisplayIdSource` inherited from class `Edid`.
|
|
Allows read DisplayId from remote devices `read_sbm`, save received DisplayId data to file `save_edid` and
|
|
read DisplayId from DUT `read_i2c`
|
|
"""
|
|
|
|
def __init__(self, port_io: PortIO, max_stream_count: int):
|
|
super().__init__(port_io, TSI_DID_TE_OUTPUT, max_stream_count, TSI_DID_SELECT_STREAM)
|
|
|
|
def read_sbm(self, stream: int):
|
|
"""
|
|
|
|
Allows reading remote DisplayId block(s) of remote device(s) attached to DUT over connecting signal cable.
|
|
stream = 0 - UCD local EDID
|
|
stream > 0 - Virtual Sink's EDID
|
|
|
|
Args:
|
|
stream (int) - Virtual Sink index.
|
|
|
|
"""
|
|
self._select_stream(stream)
|
|
result, data, size = self._io.get(TSI_DID_TE_OUTPUT_REMOTE_R, c_ubyte, MAX_EDID_SIZE)
|
|
if result > 0:
|
|
return bytearray(data[:result])
|
|
else:
|
|
return bytearray()
|
|
|
|
def set_display_id_mode(self, mode: DisplayIDReadMode):
|
|
"""
|
|
Set DisplayID read mode
|
|
|
|
Args:
|
|
mode (`DisplayIDReadMode`)
|
|
"""
|
|
self._io.set(TSI_DPTX_DISPLAYID_CTRL, mode.value, c_uint32)
|
|
|
|
def get_display_id_mode(self) -> DisplayIDReadMode:
|
|
"""
|
|
Returns DisplayID read mode.
|
|
|
|
Returns:
|
|
object of `DisplayIDReadMode` type.
|
|
"""
|
|
result = self._io.get(TSI_DPTX_DISPLAYID_CTRL, c_uint32)[1]
|
|
return DisplayIDReadMode(result)
|
|
|
|
|
|
class DisplayIdSink(Edid):
|
|
|
|
"""
|
|
Class `DisplayIdSink` inherited from class `Edid`.
|
|
Allows writing DisplayId to device `write_display_id`, load DisplayId data from file `load_display_id`,
|
|
save received DisplayId data to file `save_edid` and read DisplayId from TE `read_i2c`.
|
|
"""
|
|
|
|
def __init__(self, port_io: PortIO, max_stream_count: int):
|
|
super().__init__(port_io, TSI_DID_TE_INPUT, max_stream_count, TSI_DID_SELECT_STREAM)
|
|
|
|
def is_enabled(self) -> bool:
|
|
"""
|
|
Returns status of DisplayId, is enabled or not.
|
|
|
|
Returns:
|
|
object of `bool` type.
|
|
"""
|
|
result = self._io.get(TSI_DPRX_DISPLAYID_CTRL, c_uint32)
|
|
status_display_id = ((result[1] & 0x1) != 0)
|
|
return bool(status_display_id)
|
|
|
|
def enable(self, enable: bool):
|
|
"""
|
|
Enable/Disable DisplayId.
|
|
|
|
Args:
|
|
enable (bool) - enable (True) or disable (False)
|
|
"""
|
|
val = 0x1 if enable else 0x0
|
|
self._io.set(TSI_DPRX_DISPLAYID_CTRL, val)
|
|
|
|
def write_display_id(self, data: bytearray):
|
|
|
|
"""
|
|
Write transferred EDID to device.
|
|
|
|
Args:
|
|
data (bytearray) - EDID data for writing
|
|
"""
|
|
self._io.set(self._control_ci, data, c_ubyte, len(data))
|
|
|
|
def load_display_id(self, path: str, load_on_device: bool) -> bytearray:
|
|
|
|
"""
|
|
Read EDID data from file. If needed to write data to device, select load_on_device = True.
|
|
Supported formats:
|
|
- BIN.
|
|
- HEX.
|
|
|
|
Args:
|
|
path (str) - full path to file.
|
|
load_on_device (bool) - write loaded data to device or not.
|
|
|
|
Returns:
|
|
object of bytearray
|
|
"""
|
|
data = load_file(path)
|
|
if len(data) > 0:
|
|
if load_on_device:
|
|
self.write_display_id(data)
|
|
return data
|
|
else:
|
|
warnings.warn("File is empty.")
|
|
return bytearray() |