Files
xinzhu.yin c157e774e5 1.1.0版本
2026-04-16 16:51:05 +08:00

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()