blob: 134b7c0814e03261c48eff6039a23ffdae61b02e [file] [log] [blame]
# Lint as: python2, python3
# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""EDID module to abstract different EDID behaviors."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import time
from . import chameleon_common # pylint: disable=W0611
from chameleond.utils import i2c
from chameleond.utils import ids
from chameleond.utils import chameleon_io as io
from chameleond.utils import rx
from six.moves import map
from six.moves import range
class I2cRam(i2c.I2cSlave):
"""A Class to abstract the behavior of memory with I2C interface."""
SLAVE_ADDRESSES = (0x49, 0x50)
_WRITE_DELAY = 'Unknown' # A subclass should override it.
def Write(self, data):
"""Writes the given data to the F-RAM.
Args:
data: A byte-array of content to write.
"""
for i in range(0, len(data), 8):
self.Set(data[i:i+8], i)
time.sleep(self._WRITE_DELAY)
def Read(self, size):
"""Reads the F-RAM content.
Args:
size: The total size to read.
Returns:
A string of data of the F-RAM content.
"""
return self.Get(0, size)
class FRam(I2cRam):
"""A Class to abstract the behavior of F-RAM."""
SLAVE_ADDRESSES = (0x50, )
# FM24CL04B F-RAM doesn't need this delay.
_WRITE_DELAY = 0
class HdmiEdidRam(I2cRam):
"""A Class to abstract the behavior of interal RAM on HDMI RX."""
SLAVE_ADDRESSES = (0x49, )
# Collabora tested that it should be 0.01 seconds.
_WRITE_DELAY = 0.01
class DpEdid(object):
"""Class to abstract the EDID of DisplayPort.
The EDID of DisplayPort is stored in a F-RAM behind DisplayPort receiver.
The receiver responses an EDID request by reading the F-RAM content and
converting it to AUX signals.
When programming the F-RAM, it switches the F-RAM from behind the receiver
to the main I2C bus, such that ARM can see and program it.
"""
_EDID_SIZE = 256
_EDID_SRAM_MUXES = {
ids.DP1: io.MuxIo.MASK_DP1_EDID_SRAM_MUX,
ids.DP2: io.MuxIo.MASK_DP2_EDID_SRAM_MUX
}
def __init__(self, input_id, main_i2c_bus):
"""Constructs a DpEdid object.
Args:
input_id: The ID of the input.
main_i2c_bus: The main I2cBus object.
"""
self._input_id = input_id
self._mux_io = main_i2c_bus.GetSlave(io.MuxIo.SLAVE_ADDRESSES[0])
self._fram = main_i2c_bus.GetSlave(FRam.SLAVE_ADDRESSES[0])
self._on_main_before_access = None
def _SwitchRamToMain(self):
"""Switches the F-RAM to the main I2C bus."""
self._mux_io.SetOutputMask(self._EDID_SRAM_MUXES[self._input_id])
def _SwitchRamToRx(self):
"""Switches the F-RAM to the I2C bus behind receiver for EDID."""
self._mux_io.ClearOutputMask(self._EDID_SRAM_MUXES[self._input_id])
def _IsRamOnMain(self):
"""Returns True if the F-RAM is on the main I2C bus; otherwise, False."""
return bool(self._mux_io.GetOutput() &
self._EDID_SRAM_MUXES[self._input_id])
def Disable(self):
"""Disables the EDID response."""
self._SwitchRamToMain()
def Enable(self):
"""Enables the EDID response."""
self._SwitchRamToRx()
def _BeginAccess(self):
"""Performs the sequence before EDID access."""
# Switch F-RAM to the main I2C main before access.
self._on_main_before_access = self._IsRamOnMain()
if not self._on_main_before_access:
self._SwitchRamToMain()
def _EndAccess(self):
"""Performs the sequence after EDID access."""
if not self._on_main_before_access:
self._SwitchRamToRx()
def WriteEdid(self, data):
"""Writes the EDID content.
Args:
data: The EDID control to write.
"""
self._BeginAccess()
try:
self._fram.Write(data)
finally:
self._EndAccess()
def ReadEdid(self):
"""Reads the EDID content.
Returns:
A byte array of EDID data.
"""
self._BeginAccess()
try:
edid = self._fram.Read(self._EDID_SIZE)
finally:
self._EndAccess()
return edid
class HdmiEdid(object):
"""Class to abstract the EDID of HDMI.
The EDID of HDMI is stored in an internal RAM of the HDMI receiver.
By configuring the receiver, the internal RAM acts as a standard EEPROM,
such that we can program and read its content.
"""
_EDID_SIZE = 256
def __init__(self, main_i2c_bus):
"""Constructs a HdmiEdid object.
Args:
main_i2c_bus: The main I2cBus object.
"""
self._rx = main_i2c_bus.GetSlave(rx.HdmiRx.SLAVE_ADDRESSES[0])
self._edidram = main_i2c_bus.GetSlave(HdmiEdidRam.SLAVE_ADDRESSES[0])
self._enabled_before_access = None
def _BeginAccess(self):
"""Performs the sequence before EDID access."""
# Disable the EDID response during the update.
self._enabled_before_access = self._rx.IsEdidEnabled()
if self._enabled_before_access:
self._rx.DisableEdid()
self._rx.SetEdidSlave(HdmiEdidRam.SLAVE_ADDRESSES[0])
self._rx.EnableEdidAccess()
def _EndAccess(self):
"""Performs the sequence after EDID access."""
self._rx.DisableEdidAccess()
if self._enabled_before_access:
self._rx.EnableEdid()
def _ValidateEdid(self, data):
"""Validates the EDID on the HDMI receiver.
It updates the checksum to the receiver and makes the EDID validated.
Args:
data: The EDID control to write.
"""
for block in (0, 1):
# Skip the last byte, i.e. checksum.
checksum = ((-sum(map(ord, data[128 * block:128 * (block + 1) - 1])))
& 0xff)
self._rx.UpdateEdidChecksum(block, checksum)
def Disable(self):
"""Disables the EDID response."""
self._rx.DisableEdid()
def Enable(self):
"""Enables the EDID response."""
self._rx.EnableEdid()
def WriteEdid(self, data):
"""Writes the EDID content.
Args:
data: The EDID control to write.
"""
self._BeginAccess()
try:
self._edidram.Write(data)
self._ValidateEdid(data)
finally:
self._EndAccess()
def ReadEdid(self):
"""Reads the EDID content.
Returns:
A byte array of EDID data.
"""
self._BeginAccess()
try:
edid = self._edidram.Read(self._EDID_SIZE)
finally:
self._EndAccess()
return edid
class VgaEdid(object):
"""Class to abstract the EDID of VGA.
The request of EDID of VGA is reponsed by the FPGA. There is a memory space
in FPGA which stores the EDID content.
"""
def __init__(self, fpga_ctrl):
"""Constructs a HdmiEdid object.
Args:
fpga_ctrl: The FpgaController object.
"""
self._vga_edid = fpga_ctrl.vga_edid
def Disable(self):
"""Disables the EDID response."""
self._vga_edid.Disable()
def Enable(self):
"""Enables the EDID response."""
self._vga_edid.Enable()
def WriteEdid(self, data):
"""Writes the EDID content.
Args:
data: The EDID control to write.
"""
self._vga_edid.WriteEdid(data)
def ReadEdid(self):
"""Reads the EDID content.
Returns:
A byte array of EDID data.
"""
return self._vga_edid.ReadEdid()