blob: c70cd5742173dda51c2d726f0adfe7ece1693bb8 [file] [log] [blame]
# 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.
"""DisplayPort, HDMI, and CRT receiver modules."""
import logging
import time
import chameleon_common # pylint: disable=W0611
from chameleond.utils import common
from chameleond.utils import i2c
class RxError(Exception):
"""Exception raised when any error on receiver."""
pass
class DpRx(i2c.I2cSlave):
"""A class to control ITE IT6506 DisplayPort Receiver."""
SLAVE_ADDRESSES = (0x58, 0x59)
_DELAY_VIDEO_MODE_PROBE = 1.0
_TIMEOUT_VIDEO_STABLE_PROBE = 5
_AUDIO_RESET_DELAY = 0.001
_VIDEO_RESET_DELAY = 0.001
_REG_PCLK_COUNT_LOW = 0x10
_REG_PCLK_COUNT_HIGH = 0x11
_REG_INPUT_STATUS = 0x11
_BIT_VIDEO_STABLE = 1 << 4
_REG_FUNC_RESET = 0xEA
_REG_PLL_RESET = 0xB3 # This is at bank 1
_BIT_RESET_VIDEO = 0x02
_REG_HACTIVE_H = 0x9C
_REG_HACTIVE_L = 0x9B
_REG_VACTIVE_H = 0xA2
_REG_VACTIVE_L = 0xA1
_REG_VIDEO_FLAG = 0xa9
_BIT_INTERLACED = 1 << 2
def Initialize(self, dual_pixel_mode):
"""Runs the initialization sequence for the chip."""
logging.info('Initialize DisplayPort RX chip.')
if dual_pixel_mode:
self.SetDualPixelMode()
else:
self.SetSinglePixelMode()
# TODO(waihong): Declare constants for the registers and values.
self._SwitchBank(0)
self.Set(0x01, 0xe3) # make interrupt output polarity active low to
# match hdmi
self.Set(0xe0, 0xd2) # video fifo gain???
self.Set(0xc5, 0xcc) # [3:0] CR Wait Time x 100us???
# This makes 2560x1600 work
self._SwitchBank(1)
self.Set(0xee, 0xa5) # the driving strength for video
# (ITE firmware uses 0xc8)
self.Set(0x03, 0xb8) # ?
# Initialize the audio path.
self._SwitchBank(1)
self.Set(0xee, 0xa6) # max driving strength for audio
self.Set(0x04, 0xb3) # reset audio pll
self._SwitchBank(0)
self.Set(0x04, 0xea) # reset audio module
time.sleep(self._AUDIO_RESET_DELAY)
self._SwitchBank(1)
self.Set(0x00, 0xb3)
self._SwitchBank(0)
self.Set(0x00, 0xea)
def SetDualPixelMode(self):
"""Uses dual pixel mode which occupes 2 video paths in FPGA."""
self._SwitchBank(0)
self.Set(0x6c, 0xed) # power up dual pixel mode path
self.Set(0x06, 0xef) # tune dual pixel dp timing
self._SwitchBank(1)
self.Set(0x11, 0xa2) # bit 0 reset FIFO
self.Set(0x10, 0xa2) # bit 4 enables dual pixel mode
self._SwitchBank(0)
def SetSinglePixelMode(self):
"""Uses single pixel mode which occupes 1 video path in FPGA."""
self._SwitchBank(0)
self.Set(0xec, 0xed) # power down double pixel mode path
self.Set(0x07, 0xef) # tune single pixel dp timing
self._SwitchBank(1)
self.Set(0x00, 0xa2) # bit 4 disables dual pixel mode
self._SwitchBank(0)
def IsCablePowered(self):
"""Returns if the cable is powered or not."""
return bool(self.Get(0xc8) & (1 << 3))
def IsVideoInputStable(self):
"""Returns whether the video input is stable."""
input_status = self.Get(self._REG_INPUT_STATUS)
return bool(input_status & self._BIT_VIDEO_STABLE)
def WaitVideoInputStable(self, timeout=None):
"""Waits the video input stable or timeout.
Returns:
True if the video input is stable before timeout; otherwise, False.
"""
if timeout is None:
timeout = self._TIMEOUT_VIDEO_STABLE_PROBE
try:
common.WaitForCondition(
self.IsVideoInputStable, True, self._DELAY_VIDEO_MODE_PROBE,
timeout)
return True
except common.TimeoutError:
return False
def GetPixelClock(self):
"""Returns the pixel clock of the input signal in MHz."""
# PCLK = 27MHz * 1024 / PCLK_COUNT
pclk_count = (self.Get(self._REG_PCLK_COUNT_LOW) +
((self.Get(self._REG_PCLK_COUNT_HIGH) & 0x0F) << 8))
if pclk_count:
return 27 * 1024 / pclk_count
else:
# report None if no reading obtained from rx
return None
def _SwitchBank(self, bank):
"""Switch register bank."""
assert bank == 0 or bank == 1
self.Set(0x02 + bank, 0x05)
def IsInterlaced(self):
"""Returns True if the input video is in interlaced mode."""
video_flag = self.Get(self._REG_VIDEO_FLAG)
return bool(video_flag & self._BIT_INTERLACED)
def GetFieldResolution(self):
"""Gets the resolution of a field."""
hactive_h = self.Get(self._REG_HACTIVE_H)
hactive_l = self.Get(self._REG_HACTIVE_L)
vactive_h = self.Get(self._REG_VACTIVE_H)
vactive_l = self.Get(self._REG_VACTIVE_L)
width = hactive_h << 8 | hactive_l
height = vactive_h << 8 | vactive_l
return (width, height)
def GetFrameResolution(self):
"""Gets the resolution of a frame."""
field_per_frame = 2 if self.IsInterlaced() else 1
(width, height) = self.GetFieldResolution()
return (width, height * field_per_frame)
class HdmiRx(i2c.I2cSlave):
"""A class to control ITE IT6803 HDMI Receiver."""
SLAVE_ADDRESSES = (0x48, )
_DELAY_VIDEO_MODE_PROBE = 0.1
_TIMEOUT_VIDEO_STABLE_PROBE = 10
_AUDIO_RESET_DELAY = 0.001
_REG_P0_INTERRUPT = 0x05
_BIT_P0_RX_CLK_STABLE_CHG = 1 << 2
_BIT_P0_RX_CLK_ON_CHG = 1 << 1
_REG_INTERNAL_STATUS = 0x0a
_BIT_P0_PWR5V_DET = 1 << 0
_REG_AUDIO_VIDEO_RESET = 0x10
_BIT_REG_AUDIO_RESET = 1 << 1
_REG_P0_RESET = 0x11
_BIT_P0_SWRST = 1 << 0
_REG_P0_HDCP_CONTROL = 0x2d
_BIT_P0_HDCP_ENABLE = 1 << 2
_REG_EDID_SLAVE_ADDR = 0x87
_BIT_ENABLE_EDID_ACCESS = 1
_REG_IO_MAP = 0x8c
_BIT_VDIO3_ENABLE = 1 << 3
_BIT_SP_OUT_MODE = 1 << 0
_REG_HDCP_STATUS = 0x93
_BIT_P0_HDCP_ON = 1 << 0
_REG_EDID_CONFIG = 0xc0
_BIT_DISABLE_SHADOW_P0 = 1 << 0
# Registers for checksum
_REG_P0_B0_SUM = 0xc4 # Port 0, block 0
_REG_P0_B1_SUM = 0xc5 # Port 0, block 1
_REG_VIDEO_MODE = 0x99
_BIT_VIDEO_STABLE = 1 << 3
_BIT_INTERLACED = 1 << 1
_REG_PIXEL_CLOCK_DIV = 0x9A
_REG_CLK_CONFIG = 0x54
_MASK_RCLK_SELECT = 0x03
_REG_HACTIVE_H = 0x9f
_REG_HACTIVE_L = 0x9e
_REG_VACTIVE_H = 0xa4
_REG_VACTIVE_L = 0xa5
_DELAY_SOFTWARE_RESET = 0.3
def __init__(self, i2c_bus, slave):
"""Constructs a HdmiRx object.
Args:
i2c_bus: The I2cBus object.
slave: The number of slave address.
"""
super(HdmiRx, self).__init__(i2c_bus, slave)
self._pclk_base = None
def Initialize(self, dual_pixel_mode):
"""Runs the initialization sequence for the chip."""
logging.info('Initialize HDMI RX chip.')
if dual_pixel_mode:
self.SetDualPixelMode()
else:
self.SetSinglePixelMode()
# TODO(waihong): Declare constants for the registers and values.
self.Set(0x3f, 0x63) # enable interrupt IO output
self.Set(0x33, 0x58) # set driving strength to max (video)
self.Set(0x33, 0x59) # set driving strength to max (audio)
self.SetColorSpaceConvertion()
# OCLK_MHz = (MHL14|MHL13|MHL12) * 10 / 10^6 = 43.3 (hardware dependent)
# RCLK_MHz = OCLK_MHz / (1 << (REG054[1:0] + 1))
# PCLK_MHz = RCLK_MHz * 255 / pclk_div
# where pclk_div is from REG09A and depends on input signal
# Here we pre-calculate _pclk_base = RCLK_MHz * 255 for PCLK_MHz calculation
# in GetPixelClock().
rclk_select = self.Get(self._REG_CLK_CONFIG) & self._MASK_RCLK_SELECT
rclk = 43.3 / (1 << (rclk_select + 1))
self._pclk_base = rclk * 255
def SetDualPixelMode(self):
"""Uses dual pixel mode which occupes 2 video paths in FPGA."""
self.Set(0x02, 0x05) # bank 0
self.Set(0x0f, 0x0d) # enable PHFCLK
self.Set(0x03, 0x8b) # enable dual pixel mode
self.Set(0x08, 0x8c) # enable QA IO
self.Set(0x01, 0x8b) # dual pixel fifo normal operation
self.Set(0xb1, 0x50) # tune hdmi dual pixel timing
def SetSinglePixelMode(self):
"""Uses single pixel mode which occupes 1 video path in FPGA."""
self.Set(0x07, 0x0d) # disable PHFCLK
self.Set(0x80, 0x8b) # disable dual pixel mode
self.Set(0x09, 0x8c) # enable QA IO, single pixel mode 1
self.Set(0xb3, 0x50) # tune hdmi single pixel timing
def SetColorSpaceConvertion(self):
"""Sets the registers for YUV color space convertion."""
self.Set(0x01, 0x0f)
self.Set(0x04, 0x70)
self.Set(0x00, 0x71)
self.Set(0xa7, 0x72)
self.Set(0x4f, 0x73)
self.Set(0x09, 0x74)
self.Set(0xba, 0x75)
self.Set(0x3b, 0x76)
self.Set(0x4b, 0x77)
self.Set(0x3e, 0x78)
self.Set(0x4f, 0x79)
self.Set(0x09, 0x7a)
self.Set(0x57, 0x7b)
self.Set(0x0e, 0x7c)
self.Set(0x02, 0x7d)
self.Set(0x00, 0x7e)
self.Set(0x4f, 0x7f)
self.Set(0x09, 0x80)
self.Set(0xfe, 0x81)
self.Set(0x3f, 0x82)
self.Set(0xe8, 0x83)
self.Set(0x10, 0x84)
self.Set(0x00, 0x0f)
self.Set(0x01, 0x11) # Port 0 all logic reset
self.Set(0x00, 0x11)
def IsCablePowered(self):
"""Returns if the cable is powered or not."""
return bool(self.Get(self._REG_INTERNAL_STATUS) & self._BIT_P0_PWR5V_DET)
def SetEdidSlave(self, slave):
"""Sets the slave address for the EDID which is stored in the internal RAM.
Args:
slave: The slave address for the EDID.
"""
old_value = self.Get(self._REG_EDID_SLAVE_ADDR)
# Bit 7:1 is the slave address; bit 0 is to enable EDID access.
# Keep the original state of the EDID accessibility.
new_value = (slave << 1) | (old_value & self._BIT_ENABLE_EDID_ACCESS)
self.Set(new_value, self._REG_EDID_SLAVE_ADDR)
def EnableEdidAccess(self):
"""Enables the access of the EDID RAM content."""
self.SetMask(self._REG_EDID_SLAVE_ADDR, self._BIT_ENABLE_EDID_ACCESS)
def DisableEdidAccess(self):
"""Disables the access of the EDID RAM content."""
self.ClearMask(self._REG_EDID_SLAVE_ADDR, self._BIT_ENABLE_EDID_ACCESS)
def IsEdidEnabled(self):
"""Returns True if the receiver is enabled to respond EDID request."""
return not self.Get(self._REG_EDID_CONFIG) & self._BIT_DISABLE_SHADOW_P0
def EnableEdid(self):
"""Enables the receiver to monitor DDC and respond EDID."""
self.ClearMask(self._REG_EDID_CONFIG, self._BIT_DISABLE_SHADOW_P0)
def DisableEdid(self):
"""Disables the receiver to monitor DDC and respond EDID."""
self.SetMask(self._REG_EDID_CONFIG, self._BIT_DISABLE_SHADOW_P0)
def UpdateEdidChecksum(self, block_num, checksum):
"""Updates the checksum of the EDID block.
Args:
block_num: 0 for Block 0; 1 for Block 1.
checksum: The checksum value.
"""
if block_num == 0:
self.Set(checksum, self._REG_P0_B0_SUM)
elif block_num == 1:
self.Set(checksum, self._REG_P0_B1_SUM)
def IsVideoInputStable(self):
"""Returns whether the video input is stable."""
video_mode = self.Get(self._REG_VIDEO_MODE)
return bool(video_mode & self._BIT_VIDEO_STABLE)
def WaitVideoInputStable(self, timeout=None):
"""Waits the video input stable or timeout.
Returns:
True if the video input is stable before timeout; otherwise, False.
"""
if timeout is None:
timeout = self._TIMEOUT_VIDEO_STABLE_PROBE
try:
common.WaitForCondition(
self.IsVideoInputStable, True, self._DELAY_VIDEO_MODE_PROBE,
timeout)
return True
except common.TimeoutError:
return False
def GetPixelClock(self):
"""Returns the pixel clock of the input signal in MHz."""
pclk_div = self.Get(self._REG_PIXEL_CLOCK_DIV)
return self._pclk_base / pclk_div
def IsResetNeeded(self):
"""Returns if the RX needs reset by checking the interrupt values."""
# TODO(waihong): Handle all the interrupts.
# Now we only handle 2 interrupts, i.e. clock-stable and clock-on changes,
# by forcing receiver reset.
interrupt = self.Get(self._REG_P0_INTERRUPT)
self._ClearInterrupt()
return bool(interrupt & (self._BIT_P0_RX_CLK_STABLE_CHG |
self._BIT_P0_RX_CLK_ON_CHG))
def _ClearInterrupt(self):
"""Clears the interrupt."""
interrupt = self.Get(self._REG_P0_INTERRUPT)
# W1C register
self.Set(interrupt, self._REG_P0_INTERRUPT)
def Reset(self):
"""Resets the receiver."""
logging.info('Reset the receiver.')
self.SetAndClear(self._REG_P0_RESET, self._BIT_P0_SWRST,
self._DELAY_SOFTWARE_RESET)
# Some interrupts are triggered on reset. Clear them.
self._ClearInterrupt()
def IsInterlaced(self):
"""Returns True if the input video is in interlaced mode."""
video_mode = self.Get(self._REG_VIDEO_MODE)
return bool(video_mode & self._BIT_INTERLACED)
def GetFieldResolution(self):
"""Gets the resolution of a field."""
hactive_h = self.Get(self._REG_HACTIVE_H)
hactive_l = self.Get(self._REG_HACTIVE_L)
vactive_h = self.Get(self._REG_VACTIVE_H)
vactive_l = self.Get(self._REG_VACTIVE_L)
width = (hactive_h & 0x3f) << 8 | hactive_l
height = (vactive_h & 0xf0) << 4 | vactive_l
return (width, height)
def GetFrameResolution(self):
"""Gets the resolution of a frame."""
field_per_frame = 2 if self.IsInterlaced() else 1
(width, height) = self.GetFieldResolution()
return (width, height * field_per_frame)
def SetContentProtection(self, enabled):
"""Sets the content protection state on the receiver.
Args:
enabled: True to enable; False to disable.
"""
if enabled:
self.SetMask(self._REG_P0_HDCP_CONTROL, self._BIT_P0_HDCP_ENABLE)
else:
self.ClearMask(self._REG_P0_HDCP_CONTROL, self._BIT_P0_HDCP_ENABLE)
def IsContentProtectionEnabled(self):
"""Returns True if the content protection is enabled on the receiver.
Returns:
True if the content protection is enabled; otherwise, False.
"""
return bool(self.Get(self._REG_P0_HDCP_CONTROL) & self._BIT_P0_HDCP_ENABLE)
def IsVideoInputEncrypted(self):
"""Returns True if the received video signal is encrypted.
Returns:
True if the video input is encrypted; otherwise, False.
"""
return bool(self.Get(self._REG_HDCP_STATUS) & self._BIT_P0_HDCP_ON)
def ResetAudioLogic(self):
"""Resets audio logic.
For some ChromeOS boards, receiver judges HDMI audio data stop as an error.
In error state, receiver does not dump data anymore. Reset audio logic so
receiver can dump new data.
"""
self.SetMask(self._REG_AUDIO_VIDEO_RESET, self._BIT_REG_AUDIO_RESET)
time.sleep(self._AUDIO_RESET_DELAY)
self.ClearMask(self._REG_AUDIO_VIDEO_RESET, self._BIT_REG_AUDIO_RESET)
class VgaRx(i2c.I2cSlave):
"""A class to control ITE CAT9883C CRT Receiver."""
SLAVE_ADDRESSES = (0x4c, )
_DELAY_CHECKING_STABLE_PROBE = 0.1
_TIMEOUT_CHECKING_STABLE = 5
_REG_SYNC_DETECT = 0x14
_BIT_HSYNC_DETECTED = 1 << 7
_BIT_TV_MODE = 1 << 6
_BIT_VSYNC_DETECTED = 1 << 4
_BITS_SYNC_MASK = _BIT_HSYNC_DETECTED | _BIT_VSYNC_DETECTED
_REG_HSYNC_COUNTER_REFRESH = 0x8f
_BITS_HSYNC_COUNTER_REFRESH = 0x68
_REG_HSYNC_COUNTER_H = 0xac
_REG_HSYNC_COUNTER_L = 0xab
_VGA_MODES = {
'PC_576px50': [0x35, 0xf0, 0x68, 0x80, 0x20, 0x10, 0xf0, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x40, 0x6f, 0xb8,
0x19, 0x00, 0x00],
'PC_480px60': [0x35, 0x90, 0x28, 0x38, 0x20, 0x10, 0xf0, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x40, 0x6f, 0xb8,
0x19, 0x00, 0x00],
'PC_720px60': [0x67, 0x10, 0xa0, 0x38, 0x40, 0x40, 0xf0, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x44, 0x6f, 0xb8,
0x19, 0x00, 0x00],
'PC_1080ix60': [0x89, 0x70, 0xa0, 0x38, 0x40, 0x40, 0xf0, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x44, 0x6f, 0xb8,
0x19, 0x00, 0x00],
'PC_640x480x60': [0x31, 0xf0, 0x30, 0x88, 0x10, 0x10, 0xf0, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x42, 0x6e, 0xb8,
0x19, 0x00, 0x00],
'PC_640x480x72': [0x33, 0xf0, 0x70, 0x88, 0x10, 0x10, 0xf0, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x42, 0x6e, 0xb8,
0x19, 0x00, 0x00],
'PC_640x480x75': [0x34, 0x70, 0x70, 0x88, 0x10, 0x10, 0xf0, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x42, 0x6e, 0xb8,
0x19, 0x00, 0x00],
'PC_640x480x85': [0x33, 0xf0, 0x70, 0x88, 0x10, 0x10, 0xf0, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x42, 0x6e, 0xb8,
0x19, 0x00, 0x00],
'PC_800x600x56': [0x3f, 0xf0, 0x70, 0x38, 0x10, 0x20, 0xf0, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x42, 0x6e, 0xb8,
0x19, 0x00, 0x00],
'PC_800x600x60': [0x41, 0xf0, 0x60, 0x38, 0x10, 0x20, 0xf0, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x42, 0x6e, 0xb8,
0x19, 0x00, 0x00],
'PC_800x600x72': [0x40, 0xf0, 0x70, 0x38, 0x10, 0x20, 0xf0, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x42, 0x6e, 0xb8,
0x19, 0x00, 0x00],
'PC_800x600x75': [0x41, 0xf0, 0x70, 0x38, 0x10, 0x20, 0xf0, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x42, 0x6e, 0xb8,
0x19, 0x00, 0x00],
'PC_800x600x85': [0x41, 0x70, 0x70, 0x38, 0x10, 0x20, 0xf0, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x42, 0x6e, 0xb8,
0x19, 0x00, 0x00],
'PC_1024x768x60': [0x53, 0xf0, 0xa8, 0x38, 0x10, 0x40, 0xf0, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x42, 0x6e, 0xb8,
0x19, 0x00, 0x00],
'PC_1024x768x70': [0x52, 0xf0, 0xa8, 0x38, 0x10, 0x40, 0xf0, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x42, 0x6e, 0xb8,
0x19, 0x00, 0x00],
'PC_1024x768x75': [0x51, 0xf0, 0xa8, 0x38, 0x10, 0x40, 0xf0, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x42, 0x6e, 0xb8,
0x19, 0x00, 0x00],
'PC_1024x768x80': [0x53, 0x70, 0xa8, 0x38, 0x10, 0x40, 0xf0, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x42, 0x6e, 0xb8,
0x19, 0x00, 0x00],
'PC_1024x768x85': [0x55, 0xf0, 0xa8, 0x38, 0x10, 0x40, 0xf0, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x42, 0x6e, 0xb8,
0x19, 0x00, 0x00],
'PC_1280x1024x60': [0x69, 0x70, 0xa8, 0x38, 0x10, 0x40, 0xf0, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x42, 0x6e, 0xb8,
0x19, 0x00, 0x00],
'PC_1280x1024x75': [0x69, 0x70, 0xf0, 0x38, 0x10, 0x40, 0xf0, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x42, 0x6e, 0xb8,
0x19, 0x00, 0x00],
'PC_1280x1024x85': [0x6b, 0xf0, 0xa8, 0x38, 0x10, 0x40, 0xf0, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x42, 0x6e, 0xb8,
0x19, 0x00, 0x00],
'PC_1600x1200x60': [0x86, 0xf0, 0xe8, 0x40, 0x10, 0x80, 0xf0, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x42, 0x6e, 0xb8,
0x19, 0x00, 0x00],
'PC_1360x768x60': [0x6f, 0xf0, 0x90, 0x10, 0x10, 0x40, 0xf0, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x40, 0x6f, 0xb8,
0x19, 0x00, 0x00],
'PC_1680x1050x60': [0x8b, 0xf0, 0xf0, 0xa8, 0x40, 0x40, 0xf0, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x40, 0x6f, 0xb8,
0x19, 0x00, 0x00],
'PC_1440x900x60': [0x76, 0xf0, 0xa8, 0x00, 0x20, 0x20, 0xf0, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x40, 0x6f, 0xb8,
0x19, 0x00, 0x00],
'PC_1280x800x60': [0x68, 0xf0, 0xa8, 0x00, 0x10, 0x40, 0xf0, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x40, 0x6f, 0xb8,
0x19, 0x00, 0x00],
'PC_1280x960x60': [0x70, 0x70, 0xb0, 0x00, 0x10, 0x40, 0xf0, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x40, 0x6f, 0xb8,
0x19, 0x00, 0x00],
'PC_1920x1080x60': [0x89, 0x70, 0xf0, 0x80, 0x30, 0x20, 0xf0, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x40, 0x6f, 0xb8,
0x19, 0x00, 0x00],
'PC_1920x1200xReduce': [0x81, 0xf0, 0xf0, 0x80, 0x06, 0x10, 0xf0, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x40, 0x6f, 0xb8,
0x19, 0x00, 0x00]
}
_VGA_MODES_DETECT = [
(0x1a0, 0x240, 'PC_480px60'),
(0x240, 0x2d0, 'PC_800x600x60'),
(0x2d0, 0x310, 'PC_720px60'),
(0x310, 0x31d, 'PC_1360x768x60'),
(0x31e, 0x331, 'PC_1024x768x60'),
(0x332, 0x352, 'PC_1280x800x60'),
(0x3a0, 0x3a7, 'PC_1440x900x60'),
(0x3ad, 0x400, 'PC_1280x960x60'),
(0x420, 0x437, 'PC_1280x1024x60'),
(0x440, 0x459, 'PC_1680x1050x60'),
(0x459, 0x490, 'PC_1920x1080x60'),
(0x4d0, 0x4d7, 'PC_1920x1200xReduce'),
(0x4da, 0x510, 'PC_1600x1200x60'),
]
def Initialize(self, unused_dual_pixel_mode):
"""Runs the initialization sequence for the chip."""
logging.info('Initialize CRT RX chip.')
# TODO(waihong): Declare constants for the registers and values.
self.Set(0x69, 0x01)
self.Set(0xd0, 0x02)
self.Set(0x88, 0x03)
self.Set(0xf0, 0x07)
self.Set(0x68, 0x8f)
self.Set(0x29, 0x86)
self.Set(0x80, 0x8d)
self.Set(0x00, 0x84)
self.Set(0x69, 0x87)
self.Set(0x30, 0x91)
self.Set(0x22, 0x96)
self.Set(0x19, 0x98)
self.Set(0x0C, 0x84)
self.Set(0x08, 0x99)
self.Set(0x0f, 0x86) # Tweak: max driving strength
def _IsTvMode(self):
"""Returns True if the current mode is a TV mode."""
return bool(self.Get(self._REG_SYNC_DETECT) & self._BIT_TV_MODE)
def DetectMode(self):
"""Returns the current VGA mode."""
self.Set(self._BITS_HSYNC_COUNTER_REFRESH, self._REG_HSYNC_COUNTER_REFRESH)
hsync_counter = (((self.Get(self._REG_HSYNC_COUNTER_H) << 4) & 0xf00) +
self.Get(self._REG_HSYNC_COUNTER_L))
mode = None
if not self._IsTvMode():
# For simplification, this detection logic only works on 60Hz reflesh
# rate.
for min, max, detect_mode in self._VGA_MODES_DETECT:
if min <= hsync_counter < max:
mode = detect_mode
if not mode:
raise RxError('Failed to detect the VGA mode, #hsync: %#x' %
hsync_counter)
else:
raise RxError('Detected TV mode which is not supported yet.')
logging.info('Detected VGA mode: %s (#hsync: %#x)', mode, hsync_counter)
return mode
def SetMode(self, mode):
"""Sets the VGA mode.
Args:
mode: A string of the mode name.
"""
if mode not in self._VGA_MODES:
raise RxError('Set to an unsupported mode: %s' % mode)
setting = self._VGA_MODES[mode]
for index, value in enumerate(setting):
reg = index + 1
self.Set(value, reg)
def IsValidVGAMode(self):
"""Return True if the VGA mode is valid."""
self.Set(self._BITS_HSYNC_COUNTER_REFRESH, self._REG_HSYNC_COUNTER_REFRESH)
hsync_counter = (((self.Get(self._REG_HSYNC_COUNTER_H) << 4) & 0xf00) +
self.Get(self._REG_HSYNC_COUNTER_L))
for min, max, _ in self._VGA_MODES_DETECT:
if min <= hsync_counter < max:
return True
return False
def IsSyncDetected(self):
"""Returns True if Hsync or Vsync is detected."""
return bool(self.Get(self._REG_SYNC_DETECT) & self._BITS_SYNC_MASK)
def WaitVideoInputStable(self, timeout=None):
"""Waits the video input stable or timeout.
Returns:
True if the video input is stable before timeout; otherwise, False.
"""
if timeout is None:
timeout = self._TIMEOUT_CHECKING_STABLE
try:
# Check if H-Sync/V-Sync recevied from the source.
common.WaitForCondition(
self.IsSyncDetected and self.IsValidVGAMode, True,
self._DELAY_CHECKING_STABLE_PROBE, timeout)
except common.TimeoutError:
return False
return True
def IsInterlaced(self):
"""Returns True if the input video is in interlaced mode."""
# TODO(waihong): Support checking interlaced.
return False